Browse Source

Update DPF

Signed-off-by: falkTX <falktx@falktx.com>
tags/v2.5.0
falkTX 2 years ago
parent
commit
c012c31be2
94 changed files with 21825 additions and 11658 deletions
  1. +51
    -29
      data/copy-dpf-stuff
  2. +9
    -11
      source/Makefile.mk
  3. +54
    -16
      source/modules/dgl/Application.hpp
  4. +104
    -113
      source/modules/dgl/Base.hpp
  5. +18
    -8
      source/modules/dgl/Color.hpp
  6. +174
    -0
      source/modules/dgl/EventHandlers.hpp
  7. +128
    -39
      source/modules/dgl/Geometry.hpp
  8. +12
    -123
      source/modules/dgl/Image.hpp
  9. +156
    -0
      source/modules/dgl/ImageBase.hpp
  10. +257
    -0
      source/modules/dgl/ImageBaseWidgets.hpp
  11. +15
    -245
      source/modules/dgl/ImageWidgets.hpp
  12. +29
    -8
      source/modules/dgl/Makefile
  13. +80
    -21
      source/modules/dgl/NanoVG.hpp
  14. +308
    -0
      source/modules/dgl/OpenGL.hpp
  15. +48
    -18
      source/modules/dgl/StandaloneWindow.hpp
  16. +179
    -0
      source/modules/dgl/SubWidget.hpp
  17. +146
    -0
      source/modules/dgl/TopLevelWidget.hpp
  18. +198
    -130
      source/modules/dgl/Widget.hpp
  19. +447
    -77
      source/modules/dgl/Window.hpp
  20. +42
    -35
      source/modules/dgl/src/Application.cpp
  21. +172
    -0
      source/modules/dgl/src/ApplicationPrivateData.cpp
  22. +83
    -38
      source/modules/dgl/src/ApplicationPrivateData.hpp
  23. +68
    -54
      source/modules/dgl/src/Color.cpp
  24. +0
    -124
      source/modules/dgl/src/Common.hpp
  25. +632
    -0
      source/modules/dgl/src/EventHandlers.cpp
  26. +241
    -260
      source/modules/dgl/src/Geometry.cpp
  27. +0
    -194
      source/modules/dgl/src/Image.cpp
  28. +132
    -0
      source/modules/dgl/src/ImageBase.cpp
  29. +928
    -0
      source/modules/dgl/src/ImageBaseWidgets.cpp
  30. +0
    -1077
      source/modules/dgl/src/ImageWidgets.cpp
  31. +202
    -89
      source/modules/dgl/src/NanoVG.cpp
  32. +796
    -0
      source/modules/dgl/src/OpenGL.cpp
  33. +189
    -0
      source/modules/dgl/src/SubWidget.cpp
  34. +45
    -0
      source/modules/dgl/src/SubWidgetPrivateData.cpp
  35. +50
    -0
      source/modules/dgl/src/SubWidgetPrivateData.hpp
  36. +151
    -0
      source/modules/dgl/src/TopLevelWidget.cpp
  37. +134
    -0
      source/modules/dgl/src/TopLevelWidgetPrivateData.cpp
  38. +50
    -0
      source/modules/dgl/src/TopLevelWidgetPrivateData.hpp
  39. +43
    -94
      source/modules/dgl/src/Widget.cpp
  40. +229
    -0
      source/modules/dgl/src/WidgetPrivateData.cpp
  41. +21
    -97
      source/modules/dgl/src/WidgetPrivateData.hpp
  42. +321
    -1230
      source/modules/dgl/src/Window.cpp
  43. +1079
    -0
      source/modules/dgl/src/WindowPrivateData.cpp
  44. +199
    -0
      source/modules/dgl/src/WindowPrivateData.hpp
  45. +312
    -187
      source/modules/dgl/src/nanovg/fontstash.h
  46. +467
    -200
      source/modules/dgl/src/nanovg/nanovg.c
  47. +114
    -26
      source/modules/dgl/src/nanovg/nanovg.h
  48. +336
    -97
      source/modules/dgl/src/nanovg/nanovg_gl.h
  49. +29
    -7
      source/modules/dgl/src/nanovg/nanovg_gl_utils.h
  50. +2835
    -869
      source/modules/dgl/src/nanovg/stb_image.h
  51. +5011
    -2081
      source/modules/dgl/src/nanovg/stb_truetype.h
  52. +1
    -0
      source/modules/dgl/src/pugl-upstream
  53. +675
    -0
      source/modules/dgl/src/pugl.cpp
  54. +154
    -0
      source/modules/dgl/src/pugl.hpp
  55. +1
    -0
      source/modules/dgl/src/pugl.mm
  56. +0
    -121
      source/modules/dgl/src/pugl/common.h
  57. +0
    -41
      source/modules/dgl/src/pugl/event.h
  58. +0
    -32
      source/modules/dgl/src/pugl/gl.h
  59. +0
    -32
      source/modules/dgl/src/pugl/glu.h
  60. +0
    -385
      source/modules/dgl/src/pugl/pugl.h
  61. +0
    -352
      source/modules/dgl/src/pugl/pugl_internal.h
  62. +0
    -576
      source/modules/dgl/src/pugl/pugl_osx.m
  63. +0
    -489
      source/modules/dgl/src/pugl/pugl_win.cpp
  64. +0
    -636
      source/modules/dgl/src/pugl/pugl_x11.c
  65. +0
    -570
      source/modules/distrho/DistrhoInfo.hpp
  66. +382
    -59
      source/modules/distrho/DistrhoPlugin.hpp
  67. +20
    -4
      source/modules/distrho/DistrhoPluginMain.cpp
  68. +215
    -0
      source/modules/distrho/DistrhoPluginUtils.hpp
  69. +175
    -32
      source/modules/distrho/DistrhoUI.hpp
  70. +15
    -2
      source/modules/distrho/DistrhoUIMain.cpp
  71. +137
    -58
      source/modules/distrho/DistrhoUtils.hpp
  72. +484
    -78
      source/modules/distrho/extra/ExternalWindow.hpp
  73. +21
    -3
      source/modules/distrho/extra/LeakDetector.hpp
  74. +20
    -14
      source/modules/distrho/extra/Mutex.hpp
  75. +22
    -1
      source/modules/distrho/extra/ScopedPointer.hpp
  76. +3
    -0
      source/modules/distrho/extra/Sleep.hpp
  77. +215
    -70
      source/modules/distrho/extra/String.hpp
  78. +65
    -22
      source/modules/distrho/extra/Thread.hpp
  79. +119
    -30
      source/modules/distrho/src/DistrhoDefines.h
  80. +115
    -15
      source/modules/distrho/src/DistrhoPlugin.cpp
  81. +30
    -16
      source/modules/distrho/src/DistrhoPluginCarla.cpp
  82. +39
    -5
      source/modules/distrho/src/DistrhoPluginChecks.h
  83. +334
    -33
      source/modules/distrho/src/DistrhoPluginInternal.hpp
  84. +289
    -45
      source/modules/distrho/src/DistrhoUI.cpp
  85. +254
    -313
      source/modules/distrho/src/DistrhoUIInternal.hpp
  86. +500
    -0
      source/modules/distrho/src/DistrhoUIPrivateData.hpp
  87. +140
    -0
      source/modules/distrho/src/DistrhoUtils.cpp
  88. +0
    -1
      source/modules/distrho/src/dssi
  89. +0
    -1
      source/modules/distrho/src/ladspa
  90. +0
    -1
      source/modules/distrho/src/lv2
  91. +0
    -1
      source/modules/distrho/src/vestige
  92. +4
    -3
      source/plugin/carla-vst-export.cpp
  93. +2
    -2
      source/plugin/carla-vst.hpp
  94. +70
    -18
      source/plugin/ui_launcher.cpp

+ 51
- 29
data/copy-dpf-stuff View File

@@ -2,39 +2,61 @@

set -e

cp -v /home/falktx/FOSS/GIT-mine/DISTRHO/DPF/dgl/*.hpp /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/dgl/
cp -r -v /home/falktx/FOSS/GIT-mine/DISTRHO/DPF/dgl/src /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/dgl/
cp -r -v /home/falktx/FOSS/GIT-mine/DISTRHO/DPF/distrho/* /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/distrho/ || true
cp -v /Shared/Personal/FOSS/GIT/DISTRHO/DPF/dgl/*.hpp /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/dgl/
cp -r -v /Shared/Personal/FOSS/GIT/DISTRHO/DPF/dgl/src/*.cpp /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/dgl/src/
cp -r -v /Shared/Personal/FOSS/GIT/DISTRHO/DPF/dgl/src/*.hpp /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/dgl/src/
cp -r -v /Shared/Personal/FOSS/GIT/DISTRHO/DPF/distrho/*.cpp /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/
cp -r -v /Shared/Personal/FOSS/GIT/DISTRHO/DPF/distrho/*.hpp /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/
cp -r -v /Shared/Personal/FOSS/GIT/DISTRHO/DPF/distrho/*.h /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/

rm -r /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/dgl/src/resources
rm -r /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/dgl/src/sofd
rm /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/dgl/src/Resources.*
cp -r -v /Shared/Personal/FOSS/GIT/DISTRHO/DPF/dgl/src/pugl-upstream/include /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/dgl/src/pugl-upstream/
cp -r -v /Shared/Personal/FOSS/GIT/DISTRHO/DPF/dgl/src/pugl-upstream/src /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/dgl/src/pugl-upstream/
cp -r -v /Shared/Personal/FOSS/GIT/DISTRHO/DPF/dgl/src/pugl-upstream/AUTHORS /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/dgl/src/pugl-upstream/
cp -r -v /Shared/Personal/FOSS/GIT/DISTRHO/DPF/dgl/src/pugl-upstream/COPYING /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/dgl/src/pugl-upstream/

rm /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/distrho/src/DistrhoPlugin{Jack,LADSPA+DSSI,LV2,LV2export,VST}.cpp
rm /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/distrho/src/DistrhoUI{DSSI,LV2}.cpp
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/dgl/Cairo.hpp
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/dgl/Vulkan.hpp
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/dgl/src/Cairo.cpp
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/dgl/src/Vulkan.cpp
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/dgl/src/Resources.{cpp,hpp}

# cp -v /home/falktx/FOSS/GIT-mine/DISTRHO/DISTRHO_mini-series/plugins/3BandEQ/{*.cpp,*.hpp,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/3bandeq/
# cp -v /home/falktx/FOSS/GIT-mine/DISTRHO/DISTRHO_mini-series/plugins/3BandSplitter/{*.cpp,*.hpp,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/3bandsplitter/
# cp -v /home/falktx/FOSS/GIT-mine/DISTRHO/DISTRHO_mini-series/plugins/PingPongPan/{*.cpp,*.hpp,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/pingpongpan/
# rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/*.cpp
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/*.mm
rm -r /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/src/jackbridge
rm -r /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/src/travesty
rm -r /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/src/vst
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/DistrhoInfo.hpp
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/src/DistrhoPlugin{JACK,LADSPA+DSSI,LV2,LV2export,VST2,VST3}.cpp
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/src/DistrhoPluginVST3.hpp
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/src/DistrhoUI{DSSI,LV2,VST3}.cpp
rm -r /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/extra/sofd
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/extra/FileBrowserDialog.{cpp,hpp}
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/extra/LibraryUtils.hpp
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/extra/RingBuffer.hpp
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/extra/ScopedSafeLocale.hpp

# cp -v /Shared/Personal/FOSS/GIT/DISTRHO/DISTRHO_mini-series/plugins/3BandEQ/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/3bandeq/
# cp -v /Shared/Personal/FOSS/GIT/DISTRHO/DISTRHO_mini-series/plugins/3BandSplitter/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/3bandsplitter/
# cp -v /Shared/Personal/FOSS/GIT/DISTRHO/DISTRHO_mini-series/plugins/PingPongPan/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/pingpongpan/
#
# cp -v /home/falktx/FOSS/GIT-mine/DISTRHO/DISTRHO_nekobi/plugins/Nekobi/{*.cpp,*.hpp,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/nekobi/
# cp -v /home/falktx/FOSS/GIT-mine/DISTRHO/DISTRHO_nekobi/plugins/Nekobi/nekobee-src/{*.c,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/nekobi/nekobee-src/
# cp -v /Shared/Personal/FOSS/GIT/DISTRHO/DISTRHO_nekobi/plugins/Nekobi/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/nekobi/
# cp -v /Shared/Personal/FOSS/GIT/DISTRHO/DISTRHO_nekobi/plugins/Nekobi/nekobee-src/{*.c,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/nekobi/nekobee-src/
#
# cp -v /home/falktx/FOSS/GIT-mine/DISTRHO/DISTRHO_prom/plugins/ProM/{*.cpp,*.hpp,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/prom/
# cp -v /Shared/Personal/FOSS/GIT/DISTRHO/DISTRHO_prom/plugins/ProM/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/prom/
#
# cp -v /home/falktx/FOSS/GIT-mine/DISTRHO/JuicePlugins/plugins/GrooveJuice/{*.cpp,*.hpp,*.hxx,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/groovejuice/
# cp -v /home/falktx/FOSS/GIT-mine/DISTRHO/JuicePlugins/plugins/PowerJuice/{*.cpp,*.hpp,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/powerjuice/
# # cp -v /home/falktx/FOSS/GIT-mine/DISTRHO/JuicePlugins/plugins/PowerJuiceX2/{*.cpp,*.hpp,*.hxx,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/powerjuicex2/
# cp -v /home/falktx/FOSS/GIT-mine/DISTRHO/JuicePlugins/plugins/SegmentJuice/{*.cpp,*.hpp,*.hxx,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/segmentjuice/
# # cp -v /home/falktx/FOSS/GIT-mine/DISTRHO/JuicePlugins/plugins/StutterJuice/{*.cpp,*.hpp,*.hxx,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/stutterjuice/
# # cp -v /home/falktx/FOSS/GIT-mine/DISTRHO/JuicePlugins/plugins/TriggerJuice/{*.cpp,*.hpp,*.hxx,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/triggerjuice/
# cp -v /home/falktx/FOSS/GIT-mine/DISTRHO/JuicePlugins/plugins/VectorJuice/{*.cpp,*.hpp,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/vectorjuice/
# cp -v /home/falktx/FOSS/GIT-mine/DISTRHO/JuicePlugins/plugins/WobbleJuice/{*.cpp,*.hpp,*.hxx,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/wobblejuice/
# cp -v /Shared/Personal/FOSS/GIT/DISTRHO/JuicePlugins/plugins/GrooveJuice/{*.cpp,*.hpp,*.hxx,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/groovejuice/
# cp -v /Shared/Personal/FOSS/GIT/DISTRHO/JuicePlugins/plugins/PowerJuice/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/powerjuice/
# # cp -v /Shared/Personal/FOSS/GIT/DISTRHO/JuicePlugins/plugins/PowerJuiceX2/{*.cpp,*.hpp,*.hxx,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/powerjuicex2/
# cp -v /Shared/Personal/FOSS/GIT/DISTRHO/JuicePlugins/plugins/SegmentJuice/{*.cpp,*.hpp,*.hxx,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/segmentjuice/
# # cp -v /Shared/Personal/FOSS/GIT/DISTRHO/JuicePlugins/plugins/StutterJuice/{*.cpp,*.hpp,*.hxx,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/stutterjuice/
# # cp -v /Shared/Personal/FOSS/GIT/DISTRHO/JuicePlugins/plugins/TriggerJuice/{*.cpp,*.hpp,*.hxx,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/triggerjuice/
# cp -v /Shared/Personal/FOSS/GIT/DISTRHO/JuicePlugins/plugins/VectorJuice/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/vectorjuice/
# cp -v /Shared/Personal/FOSS/GIT/DISTRHO/JuicePlugins/plugins/WobbleJuice/{*.cpp,*.hpp,*.hxx,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/wobblejuice/
#
# cp -v /home/falktx/FOSS/GIT-mine/zam-plugins-DPF/plugins/ZamComp/{*.cpp,*.hpp,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/zamcomp/
# cp -v /home/falktx/FOSS/GIT-mine/zam-plugins-DPF/plugins/ZamCompX2/{*.cpp,*.hpp,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/zamcompx2/
# cp -v /home/falktx/FOSS/GIT-mine/zam-plugins-DPF/plugins/ZamEQ2/{*.cpp,*.hpp,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/zameq2/
# cp -v /home/falktx/FOSS/GIT-mine/zam-plugins-DPF/plugins/ZamSynth/{*.cpp,*.hpp,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/zamsynth/
# cp -v /home/falktx/FOSS/GIT-mine/zam-plugins-DPF/plugins/ZamTube/{*.cpp,*.hpp,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/zamtube/
# cp -v /home/falktx/FOSS/GIT-mine/zam-plugins-DPF/plugins/ZaMultiComp/{*.cpp,*.hpp,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/zamulticomp/
# cp -v /home/falktx/FOSS/GIT-mine/zam-plugins-DPF/plugins/ZaMultiCompX2/{*.cpp,*.hpp,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/zamulticompx2/
# cp -v /Shared/Personal/FOSS/GIT/zam-plugins-DPF/plugins/ZamComp/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/zamcomp/
# cp -v /Shared/Personal/FOSS/GIT/zam-plugins-DPF/plugins/ZamCompX2/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/zamcompx2/
# cp -v /Shared/Personal/FOSS/GIT/zam-plugins-DPF/plugins/ZamEQ2/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/zameq2/
# cp -v /Shared/Personal/FOSS/GIT/zam-plugins-DPF/plugins/ZamSynth/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/zamsynth/
# cp -v /Shared/Personal/FOSS/GIT/zam-plugins-DPF/plugins/ZamTube/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/zamtube/
# cp -v /Shared/Personal/FOSS/GIT/zam-plugins-DPF/plugins/ZaMultiComp/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/zamulticomp/
# cp -v /Shared/Personal/FOSS/GIT/zam-plugins-DPF/plugins/ZaMultiCompX2/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/zamulticompx2/

+ 9
- 11
source/Makefile.mk View File

@@ -178,7 +178,11 @@ endif

ifeq ($(HAVE_DGL),true)
BASE_FLAGS += -DHAVE_DGL
BASE_FLAGS += -DDGL_NAMESPACE=CarlaDGL -DDGL_FILE_BROWSER_DISABLED -DDGL_NO_SHARED_RESOURCES
BASE_FLAGS += -DDGL_NAMESPACE=CarlaDGL
BASE_FLAGS += -DDGL_OPENGL
BASE_FLAGS += -DDGL_FILE_BROWSER_DISABLED
BASE_FLAGS += -DDGL_NO_SHARED_RESOURCES
BASE_FLAGS += -DDONT_SET_USING_DGL_NAMESPACE
endif

ifeq ($(HAVE_FLUIDSYNTH),true)
@@ -218,6 +222,10 @@ endif

ifeq ($(USING_JUCE),true)
BASE_FLAGS += -DUSING_JUCE
BASE_FLAGS += -DJUCE_APP_CONFIG_HEADER='"AppConfig.h"'
ifeq ($(WIN32),true)
BASE_FLAGS += -D_WIN32_WINNT=0x0600
endif
endif

ifeq ($(USING_JUCE_AUDIO_DEVICES),true)
@@ -232,16 +240,6 @@ ifeq ($(STATIC_PLUGIN_TARGET),true)
BASE_FLAGS += -DSTATIC_PLUGIN_TARGET
endif

# ---------------------------------------------------------------------------------------------------------------------
# Custom build flags for JUCE

ifeq ($(USING_JUCE),true)
BUILD_CXX_FLAGS += -DJUCE_APP_CONFIG_HEADER='"AppConfig.h"'
ifeq ($(WIN32),true)
BASE_FLAGS += -D_WIN32_WINNT=0x0600
endif
endif

# ---------------------------------------------------------------------------------------------------------------------
# Allow custom namespace



+ 54
- 16
source/modules/dgl/Application.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -21,30 +21,26 @@

START_NAMESPACE_DGL

// -----------------------------------------------------------------------
// Forward class names

class Window;

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

/**
Base DGL Application class.

One application instance is required for creating a window.
There's no single/global application instance in DGL, and multiple
windows can share the same app instance.
There's no single/global application instance in DGL, and multiple windows can share the same app instance.

In standalone mode an application will automatically quit its event-loop when all its windows are closed.

In standalone mode an application will automatically quit its
event-loop when all its windows are closed.
Unless stated otherwise, functions within this class are not thread-safe.
*/
class Application
class DISTRHO_API Application
{
public:
/**
Constructor.
*/
Application();
// NOTE: the default value is not yet passed, so we catch where we use this
Application(bool isStandalone = true);

/**
Destructor.
@@ -62,29 +58,71 @@ public:
idle() is called at regular intervals.
@note This function is meant for standalones only, *never* call this from plugins.
*/
void exec(unsigned int idleTime = 10);
void exec(uint idleTimeInMs = 30);

/**
Quit the application.
This stops the event-loop and closes all Windows.
This function is thread-safe.
*/
void quit();

/**
Check if the application is about to quit.
Returning true means there's no event-loop running at the moment (or it's just about to stop).
This function is thread-safe.
*/
bool isQuitting() const noexcept;

/**
Check if the application is standalone, otherwise running as a module or plugin.
This function is thread-safe.
*/
bool isStandalone() const noexcept;

/**
Return the time in seconds.

This is a monotonically increasing clock with high resolution.@n
The returned time is only useful to compare against other times returned by this function,
its absolute value has no meaning.
*/
double getTime() const;

/**
Add a callback function to be triggered on every idle cycle.
You can add more than one, and remove them at anytime with removeIdleCallback().
Idle callbacks trigger right after OS event handling and Window idle events (within the same cycle).
There are no guarantees in terms of timing, use Window::addIdleCallback for time-relative callbacks.
*/
void addIdleCallback(IdleCallback* callback);

/**
Remove an idle callback previously added via addIdleCallback().
*/
void removeIdleCallback(IdleCallback* callback);

/**
Set the class name of the application.

This is a stable identifier for the application, used as the window class/instance name on X11 and Windows.
It is not displayed to the user, but can be used in scripts and by window managers,
so it should be the same for every instance of the application, but different from other applications.

Plugins created with DPF have their class name automatically set based on DGL_NAMESPACE and plugin name.
*/
bool isQuiting() const noexcept;
void setClassName(const char* name);

private:
struct PrivateData;
PrivateData* const pData;
friend class PluginApplication;
friend class Window;

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Application)
};

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL



+ 104
- 113
source/modules/dgl/Base.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -20,7 +20,7 @@
#include "../distrho/extra/LeakDetector.hpp"
#include "../distrho/extra/ScopedPointer.hpp"

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// Define namespace

#ifndef DGL_NAMESPACE
@@ -31,124 +31,48 @@
#define END_NAMESPACE_DGL }
#define USE_NAMESPACE_DGL using namespace DGL_NAMESPACE;

#ifdef DISTRHO_OS_WINDOWS
// -----------------------------------------------------------------------
// Fix OpenGL includes for Windows, based on glfw code

#ifndef APIENTRY
# define APIENTRY __stdcall
# define DGL_APIENTRY_DEFINED
#endif // APIENTRY

/* We need WINGDIAPI defined */
#ifndef WINGDIAPI
# if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__POCC__)
# define WINGDIAPI __declspec(dllimport)
# elif defined(__LCC__)
# define WINGDIAPI __stdcall
# else
# define WINGDIAPI extern
# endif
# define DGL_WINGDIAPI_DEFINED
#endif // WINGDIAPI

/* Some <GL/glu.h> files also need CALLBACK defined */
#ifndef CALLBACK
# if defined(_MSC_VER)
# if (defined(_M_MRX000) || defined(_M_IX86) || defined(_M_ALPHA) || defined(_M_PPC)) && !defined(MIDL_PASS)
# define CALLBACK __stdcall
# else
# define CALLBACK
# endif
# else
# define CALLBACK __stdcall
# endif
# define DGL_CALLBACK_DEFINED
#endif // CALLBACK

/* Most GL/glu.h variants on Windows need wchar_t */
#include <cstddef>

#endif // DISTRHO_OS_WINDOWS

// -----------------------------------------------------------------------
// OpenGL includes

#ifdef DISTRHO_OS_MAC
# include <OpenGL/gl.h>
#else
# ifndef DISTRHO_OS_WINDOWS
# define GL_GLEXT_PROTOTYPES
# endif
# include <GL/gl.h>
# include <GL/glext.h>
#endif

// -----------------------------------------------------------------------
// Missing OpenGL defines

#if defined(GL_BGR_EXT) && ! defined(GL_BGR)
# define GL_BGR GL_BGR_EXT
#endif

#if defined(GL_BGRA_EXT) && ! defined(GL_BGRA)
# define GL_BGRA GL_BGRA_EXT
#endif

#ifndef GL_CLAMP_TO_BORDER
# define GL_CLAMP_TO_BORDER 0x812D
#endif

#ifdef DISTRHO_OS_WINDOWS
// -----------------------------------------------------------------------
// Fix OpenGL includes for Windows, based on glfw code

#ifdef DGL_APIENTRY_DEFINED
# undef APIENTRY
# undef DGL_APIENTRY_DEFINED
#endif

#ifdef DGL_WINGDIAPI_DEFINED
# undef WINGDIAPI
# undef DGL_WINGDIAPI_DEFINED
#endif

#ifdef DGL_CALLBACK_DEFINED
# undef CALLBACK
# undef DGL_CALLBACK_DEFINED
#endif

#endif // DISTRHO_OS_WINDOWS

START_NAMESPACE_DGL

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// Base DGL enums

/**
Convenience symbols for ASCII control characters.
*/
enum Char {
kCharBackspace = 0x08,
kCharEscape = 0x1B,
kCharDelete = 0x7F
};

/**
Keyboard modifier flags.
*/
enum Modifier {
kModifierShift = 1 << 0, /**< Shift key */
kModifierControl = 1 << 1, /**< Control key */
kModifierAlt = 1 << 2, /**< Alt/Option key */
kModifierSuper = 1 << 3 /**< Mod4/Command/Windows key */
kModifierShift = 1u << 0u, ///< Shift key
kModifierControl = 1u << 1u, ///< Control key
kModifierAlt = 1u << 2u, ///< Alt/Option key
kModifierSuper = 1u << 3u ///< Mod4/Command/Windows key
};

/**
Special (non-Unicode) keyboard keys.
Keyboard key codepoints.

All keys are identified by a Unicode code point in Widget::KeyboardEvent::key.
This enumeration defines constants for special keys that do not have a standard
code point, and some convenience constants for control characters.
Note that all keys are handled in the same way, this enumeration is just for
convenience when writing hard-coded key bindings.

Keys that do not have a standard code point use values in the Private Use
Area in the Basic Multilingual Plane (`U+E000` to `U+F8FF`).
Applications must take care to not interpret these values beyond key detection,
the mapping used here is arbitrary and specific to DPF.
*/
enum Key {
kKeyF1 = 1,
// Convenience symbols for ASCII control characters
kKeyBackspace = 0x08,
kKeyEscape = 0x1B,
kKeyDelete = 0x7F,

// Backwards compatibility with old DPF
kCharBackspace DISTRHO_DEPRECATED_BY("kKeyBackspace") = kKeyBackspace,
kCharEscape DISTRHO_DEPRECATED_BY("kKeyEscape") = kKeyEscape,
kCharDelete DISTRHO_DEPRECATED_BY("kKeyDelete") = kKeyDelete,

// Unicode Private Use Area
kKeyF1 = 0xE000,
kKeyF2,
kKeyF3,
kKeyF4,
@@ -170,25 +94,92 @@ enum Key {
kKeyEnd,
kKeyInsert,
kKeyShift,
kKeyShiftL = kKeyShift,
kKeyShiftR,
kKeyControl,
kKeyControlL = kKeyControl,
kKeyControlR,
kKeyAlt,
kKeySuper
kKeyAltL = kKeyAlt,
kKeyAltR,
kKeySuper,
kKeySuperL = kKeySuper,
kKeySuperR,
kKeyMenu,
kKeyCapsLock,
kKeyScrollLock,
kKeyNumLock,
kKeyPrintScreen,
kKeyPause
};

/**
Common flags for all events.
*/
enum EventFlag {
kFlagSendEvent = 1, ///< Event is synthetic
kFlagIsHint = 2 ///< Event is a hint (not direct user input)
};

/**
Reason for a crossing event.
*/
enum CrossingMode {
kCrossingNormal, ///< Crossing due to pointer motion
kCrossingGrab, ///< Crossing due to a grab
kCrossingUngrab ///< Crossing due to a grab release
};

// -----------------------------------------------------------------------
/**
A mouse cursor type.

This is a portable subset of mouse cursors that exist on X11, MacOS, and Windows.
*/
enum MouseCursor {
kMouseCursorArrow, ///< Default pointing arrow
kMouseCursorCaret, ///< Caret (I-Beam) for text entry
kMouseCursorCrosshair, ///< Cross-hair
kMouseCursorHand, ///< Hand with a pointing finger
kMouseCursorNotAllowed, ///< Operation not allowed
kMouseCursorLeftRight, ///< Left/right arrow for horizontal resize
kMouseCursorUpDown, ///< Up/down arrow for vertical resize
kMouseCursorDiagonal, ///< Top-left to bottom-right arrow for diagonal resize
kMouseCursorAntiDiagonal ///< Bottom-left to top-right arrow for diagonal resize
};

/**
Scroll direction.

Describes the direction of a scroll event along with whether the scroll is a "smooth" scroll.
The discrete directions are for devices like mouse wheels with constrained axes,
while a smooth scroll is for those with arbitrary scroll direction freedom, like some touchpads.
*/
enum ScrollDirection {
kScrollUp, ///< Scroll up
kScrollDown, ///< Scroll down
kScrollLeft, ///< Scroll left
kScrollRight, ///< Scroll right
kScrollSmooth ///< Smooth scroll in any direction
};

// --------------------------------------------------------------------------------------------------------------------
// Base DGL classes

/**
Graphics context, definition depends on build type.
*/
struct GraphicsContext {};

/**
Idle callback.
*/
class IdleCallback
struct IdleCallback
{
public:
virtual ~IdleCallback() {}
virtual void idleCallback() = 0;
};

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL

@@ -198,6 +189,6 @@ END_NAMESPACE_DGL
using namespace DGL_NAMESPACE;
#endif

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

#endif // DGL_BASE_HPP_INCLUDED

+ 18
- 8
source/modules/dgl/Color.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -23,7 +23,7 @@ struct NVGcolor;

START_NAMESPACE_DGL

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

/**
A color made from red, green, blue and alpha floating-point values in [0..1] range.
@@ -44,13 +44,13 @@ struct Color {

/**
Create a color from red, green, blue and alpha numeric values.
Values must be in [0..255] range.
All values except alpha must be in [0..255] range, with alpha in [0..1] range.
*/
Color(int red, int green, int blue, int alpha = 255) noexcept;
Color(int red, int green, int blue, float alpha = 1.0f) noexcept;

/**
Create a color from red, green, blue and alpha floating-point values.
Values must in [0..1] range.
All values must in [0..1] range.
*/
Color(float red, float green, float blue, float alpha = 1.0f) noexcept;

@@ -65,6 +65,11 @@ struct Color {
*/
Color(const Color& color1, const Color& color2, float u) noexcept;

/**
Create a new color based on this one but with a different alpha value.
*/
Color withAlpha(float alpha) noexcept;

/**
Create a color specified by hue, saturation and lightness.
Values must in [0..1] range.
@@ -74,7 +79,7 @@ struct Color {
/**
Create a color from a HTML string like "#333" or "#112233".
*/
static Color fromHTML(const char* rgb, float alpha = 1.0f);
static Color fromHTML(const char* rgb, float alpha = 1.0f) noexcept;

/**
Linearly interpolate this color against another.
@@ -83,7 +88,7 @@ struct Color {

/**
Check if this color matches another.
@note Comparison is forced within 8-bit color values.
@note Comparison is done within 8-bit color space.
*/
bool isEqual(const Color& color, bool withAlpha = true) noexcept;
bool isNotEqual(const Color& color, bool withAlpha = true) noexcept;
@@ -95,6 +100,11 @@ struct Color {
*/
void fixBounds() noexcept;

/**
Set this color for use in the next drawing operation for the provided context.
*/
void setFor(const GraphicsContext& context, bool includeAlpha = false);

/**
@internal
Needed for NanoVG compatibility.
@@ -103,7 +113,7 @@ struct Color {
operator NVGcolor() const noexcept;
};

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL



+ 174
- 0
source/modules/dgl/EventHandlers.hpp View File

@@ -0,0 +1,174 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef DGL_EVENT_HANDLERS_HPP_INCLUDED
#define DGL_EVENT_HANDLERS_HPP_INCLUDED

#include "Widget.hpp"

START_NAMESPACE_DGL

/* NOTE none of these classes get assigned to a widget automatically
Manual plugging into Widget events is needed, like so:

```
bool onMouse(const MouseEvent& ev) override
{
return ButtonEventHandler::mouseEvent(ev);
}
```
*/

// --------------------------------------------------------------------------------------------------------------------

class ButtonEventHandler
{
public:
enum State {
kButtonStateDefault = 0x0,
kButtonStateHover = 0x1,
kButtonStateActive = 0x2,
kButtonStateActiveHover = kButtonStateActive|kButtonStateHover
};

class Callback
{
public:
virtual ~Callback() {}
virtual void buttonClicked(SubWidget* widget, int button) = 0;
};

explicit ButtonEventHandler(SubWidget* self);
virtual ~ButtonEventHandler();

bool isActive() noexcept;
void setActive(bool active, bool sendCallback) noexcept;

bool isChecked() const noexcept;
void setChecked(bool checked, bool sendCallback) noexcept;

bool isCheckable() const noexcept;
void setCheckable(bool checkable) noexcept;

Point<double> getLastClickPosition() const noexcept;
Point<double> getLastMotionPosition() const noexcept;

void setCallback(Callback* callback) noexcept;

bool mouseEvent(const Widget::MouseEvent& ev);
bool motionEvent(const Widget::MotionEvent& ev);

protected:
State getState() const noexcept;
void clearState() noexcept;

virtual void stateChanged(State state, State oldState);

void setInternalCallback(Callback* callback) noexcept;
void triggerUserCallback(SubWidget* widget, int button);

private:
struct PrivateData;
PrivateData* const pData;

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ButtonEventHandler)
};

// --------------------------------------------------------------------------------------------------------------------

class KnobEventHandler
{
public:
enum Orientation {
Horizontal,
Vertical
};

// NOTE hover not implemented yet
enum State {
kKnobStateDefault = 0x0,
kKnobStateHover = 0x1,
kKnobStateDragging = 0x2,
kKnobStateDraggingHover = kKnobStateDragging|kKnobStateHover
};

class Callback
{
public:
virtual ~Callback() {}
virtual void knobDragStarted(SubWidget* widget) = 0;
virtual void knobDragFinished(SubWidget* widget) = 0;
virtual void knobValueChanged(SubWidget* widget, float value) = 0;
};

explicit KnobEventHandler(SubWidget* self);
explicit KnobEventHandler(SubWidget* self, const KnobEventHandler& other);
KnobEventHandler& operator=(const KnobEventHandler& other);
virtual ~KnobEventHandler();

// returns raw value, is assumed to be scaled if using log
float getValue() const noexcept;

// NOTE: value is assumed to be scaled if using log
virtual bool setValue(float value, bool sendCallback = false) noexcept;

// returns 0-1 ranged value, already with log scale as needed
float getNormalizedValue() const noexcept;

// NOTE: value is assumed to be scaled if using log
void setDefault(float def) noexcept;

// NOTE: value is assumed to be scaled if using log
void setRange(float min, float max) noexcept;

void setStep(float step) noexcept;

void setUsingLogScale(bool yesNo) noexcept;

Orientation getOrientation() const noexcept;
void setOrientation(const Orientation orientation) noexcept;

void setCallback(Callback* callback) noexcept;

bool mouseEvent(const Widget::MouseEvent& ev);
bool motionEvent(const Widget::MotionEvent& ev);
bool scrollEvent(const Widget::ScrollEvent& ev);

protected:
State getState() const noexcept;

private:
struct PrivateData;
PrivateData* const pData;

/* not for use */
#ifdef DISTRHO_PROPER_CPP11_SUPPORT
KnobEventHandler(KnobEventHandler& other) = delete;
KnobEventHandler(const KnobEventHandler& other) = delete;
#else
KnobEventHandler(KnobEventHandler& other);
KnobEventHandler(const KnobEventHandler& other);
#endif

DISTRHO_LEAK_DETECTOR(KnobEventHandler)
};

// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL

#endif // DGL_EVENT_HANDLERS_HPP_INCLUDED


+ 128
- 39
source/modules/dgl/Geometry.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -21,7 +21,7 @@

START_NAMESPACE_DGL

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// Forward class names

template<typename> class Line;
@@ -29,7 +29,7 @@ template<typename> class Circle;
template<typename> class Triangle;
template<typename> class Rectangle;

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

/**
DGL Point class.
@@ -114,14 +114,14 @@ public:
bool operator!=(const Point<T>& pos) const noexcept;

private:
T fX, fY;
T x, y;
template<typename> friend class Line;
template<typename> friend class Circle;
template<typename> friend class Triangle;
template<typename> friend class Rectangle;
};

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

/**
DGL Size class.
@@ -195,7 +195,7 @@ public:

/**
Return true if size is not null (0x0).
A non-null size is still invalid if its width or height is negative.
A non-null size is still invalid if its width or height are negative.
*/
bool isNotNull() const noexcept;

@@ -210,6 +210,8 @@ public:
*/
bool isInvalid() const noexcept;

Size<int> toInt() const noexcept;

Size<T> operator+(const Size<T>& size) noexcept;
Size<T> operator-(const Size<T>& size) noexcept;
Size<T>& operator=(const Size<T>& size) noexcept;
@@ -217,6 +219,8 @@ public:
Size<T>& operator-=(const Size<T>& size) noexcept;
Size<T>& operator*=(double m) noexcept;
Size<T>& operator/=(double d) noexcept;
Size<T> operator*(double m) const noexcept;
Size<T> operator/(double m) const noexcept;
bool operator==(const Size<T>& size) const noexcept;
bool operator!=(const Size<T>& size) const noexcept;

@@ -346,11 +350,6 @@ public:
*/
void moveBy(const Point<T>& pos) noexcept;

/**
Draw this line using the current OpenGL state.
*/
void draw();

/**
Return true if line is null (start and end pos are equal).
*/
@@ -361,12 +360,28 @@ public:
*/
bool isNotNull() const noexcept;

#ifndef DPF_TEST_POINT_CPP
/**
Draw this line using the provided graphics context, optionally specifying line width.
*/
void draw(const GraphicsContext& context, T width = 1);
#endif

Line<T>& operator=(const Line<T>& line) noexcept;
bool operator==(const Line<T>& line) const noexcept;
bool operator!=(const Line<T>& line) const noexcept;

#ifndef DPF_TEST_POINT_CPP
/**
Draw this line using the current OpenGL state.@n
DEPRECATED Please use draw(const GraphicsContext&) instead.
*/
DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)")
void draw();
#endif

private:
Point<T> fPosStart, fPosEnd;
Point<T> posStart, posEnd;
};

// -----------------------------------------------------------------------
@@ -461,19 +476,35 @@ public:
void setNumSegments(const uint num);

/**
Draw this circle using the current OpenGL state.
Draw this circle using the provided graphics context.
*/
void draw();
void draw(const GraphicsContext& context);

/**
Draw lines (outline of this circle) using the current OpenGL state.
Draw lines (outline of this circle) using the provided graphics context, optionally specifying line width.
*/
void drawOutline();
void drawOutline(const GraphicsContext& context, T lineWidth = 1);

Circle<T>& operator=(const Circle<T>& cir) noexcept;
bool operator==(const Circle<T>& cir) const noexcept;
bool operator!=(const Circle<T>& cir) const noexcept;

#ifndef DPF_TEST_POINT_CPP
/**
Draw this circle using the current OpenGL state.@n
DEPRECATED Please use draw(const GraphicsContext&) instead.
*/
DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)")
void draw();

/**
Draw lines (outline of this circle) using the current OpenGL state.@n
DEPRECATED Please use drawOutline(const GraphicsContext&,T) instead.
*/
DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)")
void drawOutline();
#endif

private:
Point<T> fPos;
float fSize;
@@ -481,8 +512,6 @@ private:

// cached values
float fTheta, fCos, fSin;

void _draw(const bool outline);
};

// -----------------------------------------------------------------------
@@ -516,16 +545,6 @@ public:
*/
Triangle(const Triangle<T>& tri) noexcept;

/**
Draw this triangle using the current OpenGL state.
*/
void draw();

/**
Draw lines (outline of this triangle) using the current OpenGL state.
*/
void drawOutline();

/**
Return true if triangle is null (all its points are equal).
An null triangle is also invalid.
@@ -549,14 +568,38 @@ public:
*/
bool isInvalid() const noexcept;

/**
Draw this triangle using the provided graphics context.
*/
void draw(const GraphicsContext& context);

/**
Draw lines (outline of this triangle) using the provided graphics context, optionally specifying line width.
*/
void drawOutline(const GraphicsContext& context, T lineWidth = 1);

Triangle<T>& operator=(const Triangle<T>& tri) noexcept;
bool operator==(const Triangle<T>& tri) const noexcept;
bool operator!=(const Triangle<T>& tri) const noexcept;

private:
Point<T> fPos1, fPos2, fPos3;
#ifndef DPF_TEST_POINT_CPP
/**
Draw this triangle using the current OpenGL state.@n
DEPRECATED Please use draw(const GraphicsContext&) instead.
*/
DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)")
void draw();

void _draw(const bool outline);
/**
Draw lines (outline of this triangle) using the current OpenGL state.@n
DEPRECATED Please use drawOutline(const GraphicsContext&,T) instead.
*/
DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)")
void drawOutline();
#endif

private:
Point<T> pos1, pos2, pos3;
};

// -----------------------------------------------------------------------
@@ -710,6 +753,17 @@ public:
*/
bool contains(const Point<T>& pos) const noexcept;

/**
Check if this rectangle contains the point @a pos affected by a custom scale.
*/
bool containsAfterScaling(const Point<T>& pos, double scaling) const noexcept;

/**
Check if this rectangle contains the point @a pos of another type.
*/
template<typename T2>
bool contains(const Point<T2>& pos) const noexcept;

/**
Check if this rectangle contains X.
*/
@@ -721,14 +775,37 @@ public:
bool containsY(const T& y) const noexcept;

/**
Draw this rectangle using the current OpenGL state.
Return true if size is null (0x0).
An null size is also invalid.
*/
bool isNull() const noexcept;

/**
Return true if size is not null (0x0).
A non-null size is still invalid if its width or height are negative.
*/
void draw();
bool isNotNull() const noexcept;

/**
Draw lines (outline of this rectangle) using the current OpenGL state.
Return true if size is valid (width and height are higher than zero).
*/
void drawOutline();
bool isValid() const noexcept;

/**
Return true if size is invalid (width or height are lower or equal to zero).
An invalid size might not be null under some circumstances.
*/
bool isInvalid() const noexcept;

/**
Draw this rectangle using the provided graphics context.
*/
void draw(const GraphicsContext& context);

/**
Draw lines (outline of this rectangle) using the provided graphics context, optionally specifying line width.
*/
void drawOutline(const GraphicsContext& context, T lineWidth = 1);

Rectangle<T>& operator=(const Rectangle<T>& rect) noexcept;
Rectangle<T>& operator*=(double m) noexcept;
@@ -736,11 +813,23 @@ public:
bool operator==(const Rectangle<T>& size) const noexcept;
bool operator!=(const Rectangle<T>& size) const noexcept;

private:
Point<T> fPos;
Size<T> fSize;
/**
Draw this rectangle using the current OpenGL state.@n
DEPRECATED Please use draw(const GraphicsContext&) instead.
*/
DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)")
void draw();

void _draw(const bool outline);
/**
Draw lines (outline of this rectangle) using the current OpenGL state.@n
DEPRECATED Please use drawOutline(const GraphicsContext&,T) instead.
*/
DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)")
void drawOutline();

private:
Point<T> pos;
Size<T> size;
};

// -----------------------------------------------------------------------


+ 12
- 123
source/modules/dgl/Image.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -17,131 +17,20 @@
#ifndef DGL_IMAGE_HPP_INCLUDED
#define DGL_IMAGE_HPP_INCLUDED

#include "Geometry.hpp"
#ifdef DGL_CAIRO
#include "Cairo.hpp"
#else
#include "OpenGL.hpp"
#endif

START_NAMESPACE_DGL

// -----------------------------------------------------------------------

/**
Base DGL Image class.

This is an Image class that handles raw image data in pixels.
You can init the image data on the contructor or later on by calling loadFromMemory().

To generate raw data useful for this class see the utils/png2rgba.py script.
Be careful when using a PNG without alpha channel, for those the format is 'GL_BGR'
instead of the default 'GL_BGRA'.

Images are drawn on screen via 2D textures.
*/
class Image
{
public:
/**
Constructor for a null Image.
*/
Image();

/**
Constructor using raw image data.
@note @a rawData must remain valid for the lifetime of this Image.
*/
Image(const char* const rawData, const uint width, const uint height, const GLenum format = GL_BGRA, const GLenum type = GL_UNSIGNED_BYTE);

/**
Constructor using raw image data.
@note @a rawData must remain valid for the lifetime of this Image.
*/
Image(const char* const rawData, const Size<uint>& size, const GLenum format = GL_BGRA, const GLenum type = GL_UNSIGNED_BYTE);

/**
Constructor using another image data.
*/
Image(const Image& image);

/**
Destructor.
*/
~Image();

/**
Load image data from memory.
@note @a rawData must remain valid for the lifetime of this Image.
*/
void loadFromMemory(const char* const rawData, const uint width, const uint height, const GLenum format = GL_BGRA, const GLenum type = GL_UNSIGNED_BYTE) noexcept;

/**
Load image data from memory.
@note @a rawData must remain valid for the lifetime of this Image.
*/
void loadFromMemory(const char* const rawData, const Size<uint>& size, const GLenum format = GL_BGRA, const GLenum type = GL_UNSIGNED_BYTE) noexcept;

/**
Check if this image is valid.
*/
bool isValid() const noexcept;

/**
Get width.
*/
uint getWidth() const noexcept;

/**
Get height.
*/
uint getHeight() const noexcept;

/**
Get size.
*/
const Size<uint>& getSize() const noexcept;

/**
Get the raw image data.
*/
const char* getRawData() const noexcept;

/**
Get the image format.
*/
GLenum getFormat() const noexcept;

/**
Get the image type.
*/
GLenum getType() const noexcept;

/**
Draw this image at (0, 0) point.
*/
void draw();

/**
Draw this image at (x, y) point.
*/
void drawAt(const int x, const int y);

/**
Draw this image at position @a pos.
*/
void drawAt(const Point<int>& pos);

Image& operator=(const Image& image) noexcept;
bool operator==(const Image& image) const noexcept;
bool operator!=(const Image& image) const noexcept;

private:
const char* fRawData;
Size<uint> fSize;
GLenum fFormat;
GLenum fType;
GLuint fTextureId;
bool fIsReady;
};

// -----------------------------------------------------------------------
#ifdef DGL_CAIRO
typedef CairoImage Image;
#else
typedef OpenGLImage Image;
#endif

END_NAMESPACE_DGL

#endif // DGL_IMAGE_HPP_INCLUDED
#endif

+ 156
- 0
source/modules/dgl/ImageBase.hpp View File

@@ -0,0 +1,156 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef DGL_IMAGE_BASE_HPP_INCLUDED
#define DGL_IMAGE_BASE_HPP_INCLUDED

#include "Geometry.hpp"

START_NAMESPACE_DGL

// --------------------------------------------------------------------------------------------------------------------

enum ImageFormat {
kImageFormatNull,
kImageFormatGrayscale,
kImageFormatBGR,
kImageFormatBGRA,
kImageFormatRGB,
kImageFormatRGBA,
};

/**
Base DGL Image class.

This is an Image class that handles raw image data in pixels.
It is an abstract class that provides the common methods to build on top.
Cairo and OpenGL Image classes are based upon this one.

@see CairoImage, OpenGLImage
*/
class ImageBase
{
protected:
/**
Constructor for a null Image.
*/
ImageBase();

/**
Constructor using raw image data.
@note @a rawData must remain valid for the lifetime of this Image.
*/
ImageBase(const char* rawData, uint width, uint height, ImageFormat format);

/**
Constructor using raw image data.
@note @a rawData must remain valid for the lifetime of this Image.
*/
ImageBase(const char* rawData, const Size<uint>& size, ImageFormat format);

/**
Constructor using another image data.
*/
ImageBase(const ImageBase& image);

public:
/**
Destructor.
*/
virtual ~ImageBase();

/**
Check if this image is valid.
*/
bool isValid() const noexcept;

/**
Check if this image is not valid.
*/
bool isInvalid() const noexcept;

/**
Get width.
*/
uint getWidth() const noexcept;

/**
Get height.
*/
uint getHeight() const noexcept;

/**
Get size.
*/
const Size<uint>& getSize() const noexcept;

/**
Get the raw image data.
*/
const char* getRawData() const noexcept;

/**
Get the image format.
*/
ImageFormat getFormat() const noexcept;

/**
Load image data from memory.
@note @a rawData must remain valid for the lifetime of this Image.
*/
void loadFromMemory(const char* rawData, uint width, uint height, ImageFormat format = kImageFormatBGRA) noexcept;

/**
Load image data from memory.
@note @a rawData must remain valid for the lifetime of this Image.
*/
virtual void loadFromMemory(const char* rawData,
const Size<uint>& size,
ImageFormat format = kImageFormatBGRA) noexcept;

/**
Draw this image at (0, 0) point using the current OpenGL context.
*/
void draw(const GraphicsContext& context);

/**
Draw this image at (x, y) point using the current OpenGL context.
*/
void drawAt(const GraphicsContext& context, int x, int y);

/**
Draw this image at position @a pos using the current OpenGL context.
*/
virtual void drawAt(const GraphicsContext& context, const Point<int>& pos) = 0;

/**
TODO document this.
*/
ImageBase& operator=(const ImageBase& image) noexcept;
bool operator==(const ImageBase& image) const noexcept;
bool operator!=(const ImageBase& image) const noexcept;

protected:
const char* rawData;
Size<uint> size;
ImageFormat format;
};

// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL

#endif // DGL_IMAGE_HPP_INCLUDED

+ 257
- 0
source/modules/dgl/ImageBaseWidgets.hpp View File

@@ -0,0 +1,257 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef DGL_IMAGE_BASE_WIDGETS_HPP_INCLUDED
#define DGL_IMAGE_BASE_WIDGETS_HPP_INCLUDED

#include "EventHandlers.hpp"
#include "StandaloneWindow.hpp"
#include "SubWidget.hpp"

START_NAMESPACE_DGL

// --------------------------------------------------------------------------------------------------------------------

/**
DGL Image About Window class.

This is a Window attached (transient) to another Window that simply shows an Image as its content.
It is typically used for "about this project" style pop-up Windows.

Pressing 'Esc' or clicking anywhere on the window will automatically close it.

@see CairoImageAboutWindow, OpenGLImageAboutWindow, Window::runAsModal(bool)
*/
template <class ImageType>
class ImageBaseAboutWindow : public StandaloneWindow
{
public:
/**
Constructor taking an existing Window as the parent transient window and an optional image.
If @a image is valid, the about window size will match the image size.
*/
explicit ImageBaseAboutWindow(Window& transientParentWindow, const ImageType& image = ImageType());

/**
Constructor taking a top-level-widget's Window as the parent transient window and an optional image.
If @a image is valid, the about window size will match the image size.
*/
explicit ImageBaseAboutWindow(TopLevelWidget* topLevelWidget, const ImageType& image = ImageType());

/**
Set a new image to use as background for this window.
Window size will adjust to match the image size.
*/
void setImage(const ImageType& image);

protected:
void onDisplay() override;
bool onKeyboard(const KeyboardEvent&) override;
bool onMouse(const MouseEvent&) override;

private:
ImageType img;

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageBaseAboutWindow)
};

// --------------------------------------------------------------------------------------------------------------------

/**
DGL Image Button class.

This is a typical button, where the drawing comes from a pregenerated set of images.
The button can be under "normal", "hover" and "down" states, with one separate image possible for each.

The event logic for this button comes from the ButtonEventHandler class.

@see CairoImageButton, OpenGLImageButton
*/
template <class ImageType>
class ImageBaseButton : public SubWidget,
public ButtonEventHandler
{
public:
class Callback
{
public:
virtual ~Callback() {}
virtual void imageButtonClicked(ImageBaseButton* imageButton, int button) = 0;
};

explicit ImageBaseButton(Widget* parentWidget, const ImageType& image);
explicit ImageBaseButton(Widget* parentWidget, const ImageType& imageNormal, const ImageType& imageDown);
explicit ImageBaseButton(Widget* parentWidget, const ImageType& imageNormal, const ImageType& imageHover, const ImageType& imageDown);

~ImageBaseButton() override;

void setCallback(Callback* callback) noexcept;

protected:
void onDisplay() override;
bool onMouse(const MouseEvent&) override;
bool onMotion(const MotionEvent&) override;

private:
struct PrivateData;
PrivateData* const pData;

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageBaseButton)
};

// --------------------------------------------------------------------------------------------------------------------

/**
DGL Image Knob class.

This is a typical knob/dial, where the drawing comes from a pregenerated image "filmstrip".
The knob's "filmstrip" image can be either horizontal or vertical,
with the number of steps automatically based on the largest value (ie, horizontal if width>height, vertical if height>width).
There are no different images for "hover" or "down" states.

The event logic for this knob comes from the KnobEventHandler class.

@see CairoImageKnob, OpenGLImageKnob
*/
template <class ImageType>
class ImageBaseKnob : public SubWidget,
public KnobEventHandler
{
public:
class Callback
{
public:
virtual ~Callback() {}
virtual void imageKnobDragStarted(ImageBaseKnob* imageKnob) = 0;
virtual void imageKnobDragFinished(ImageBaseKnob* imageKnob) = 0;
virtual void imageKnobValueChanged(ImageBaseKnob* imageKnob, float value) = 0;
};

explicit ImageBaseKnob(Widget* parentWidget, const ImageType& image, Orientation orientation = Vertical) noexcept;
explicit ImageBaseKnob(const ImageBaseKnob& imageKnob);
ImageBaseKnob& operator=(const ImageBaseKnob& imageKnob);
~ImageBaseKnob() override;

void setCallback(Callback* callback) noexcept;
void setImageLayerCount(uint count) noexcept;
void setRotationAngle(int angle);
bool setValue(float value, bool sendCallback = false) noexcept override;

protected:
void onDisplay() override;
bool onMouse(const MouseEvent&) override;
bool onMotion(const MotionEvent&) override;
bool onScroll(const ScrollEvent&) override;

private:
struct PrivateData;
PrivateData* const pData;

DISTRHO_LEAK_DETECTOR(ImageBaseKnob)
};

// --------------------------------------------------------------------------------------------------------------------

// note set range and step before setting the value

template <class ImageType>
class ImageBaseSlider : public SubWidget
{
public:
class Callback
{
public:
virtual ~Callback() {}
virtual void imageSliderDragStarted(ImageBaseSlider* imageSlider) = 0;
virtual void imageSliderDragFinished(ImageBaseSlider* imageSlider) = 0;
virtual void imageSliderValueChanged(ImageBaseSlider* imageSlider, float value) = 0;
};

explicit ImageBaseSlider(Widget* parentWidget, const ImageType& image) noexcept;
~ImageBaseSlider() override;

float getValue() const noexcept;
void setValue(float value, bool sendCallback = false) noexcept;
void setDefault(float def) noexcept;

void setStartPos(const Point<int>& startPos) noexcept;
void setStartPos(int x, int y) noexcept;
void setEndPos(const Point<int>& endPos) noexcept;
void setEndPos(int x, int y) noexcept;

void setInverted(bool inverted) noexcept;
void setRange(float min, float max) noexcept;
void setStep(float step) noexcept;

void setCallback(Callback* callback) noexcept;

protected:
void onDisplay() override;
bool onMouse(const MouseEvent&) override;
bool onMotion(const MotionEvent&) override;

private:
struct PrivateData;
PrivateData* const pData;

// these should not be used
void setAbsoluteX(int) const noexcept {}
void setAbsoluteY(int) const noexcept {}
void setAbsolutePos(int, int) const noexcept {}
void setAbsolutePos(const Point<int>&) const noexcept {}

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageBaseSlider)
};

// --------------------------------------------------------------------------------------------------------------------

template <class ImageType>
class ImageBaseSwitch : public SubWidget
{
public:
class Callback
{
public:
virtual ~Callback() {}
virtual void imageSwitchClicked(ImageBaseSwitch* imageSwitch, bool down) = 0;
};

explicit ImageBaseSwitch(Widget* parentWidget, const ImageType& imageNormal, const ImageType& imageDown) noexcept;
explicit ImageBaseSwitch(const ImageBaseSwitch& imageSwitch) noexcept;
ImageBaseSwitch& operator=(const ImageBaseSwitch& imageSwitch) noexcept;
~ImageBaseSwitch() override;

bool isDown() const noexcept;
void setDown(bool down) noexcept;

void setCallback(Callback* callback) noexcept;

protected:
void onDisplay() override;
bool onMouse(const MouseEvent&) override;

private:
struct PrivateData;
PrivateData* const pData;

DISTRHO_LEAK_DETECTOR(ImageBaseSwitch)
};

// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL

#endif // DGL_IMAGE_BASE_WIDGETS_HPP_INCLUDED

+ 15
- 245
source/modules/dgl/ImageWidgets.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -18,253 +18,23 @@
#define DGL_IMAGE_WIDGETS_HPP_INCLUDED

#include "Image.hpp"
#include "Widget.hpp"
#include "Window.hpp"
#include "ImageBaseWidgets.hpp"

START_NAMESPACE_DGL

// -----------------------------------------------------------------------

class ImageAboutWindow : public Window,
public Widget
{
public:
explicit ImageAboutWindow(Window& parent, const Image& image = Image());
explicit ImageAboutWindow(Widget* widget, const Image& image = Image());

void setImage(const Image& image);

protected:
void onDisplay() override;
bool onKeyboard(const KeyboardEvent&) override;
bool onMouse(const MouseEvent&) override;
void onReshape(uint width, uint height) override;

private:
Image fImgBackground;

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageAboutWindow)
};

// -----------------------------------------------------------------------

class ImageButton : public Widget
{
public:
class Callback
{
public:
virtual ~Callback() {}
virtual void imageButtonClicked(ImageButton* imageButton, int button) = 0;
};

explicit ImageButton(Window& parent, const Image& image);
explicit ImageButton(Window& parent, const Image& imageNormal, const Image& imageDown);
explicit ImageButton(Window& parent, const Image& imageNormal, const Image& imageHover, const Image& imageDown);

explicit ImageButton(Widget* widget, const Image& image);
explicit ImageButton(Widget* widget, const Image& imageNormal, const Image& imageDown);
explicit ImageButton(Widget* widget, const Image& imageNormal, const Image& imageHover, const Image& imageDown);

~ImageButton() override;

void setCallback(Callback* callback) noexcept;

protected:
void onDisplay() override;
bool onMouse(const MouseEvent&) override;
bool onMotion(const MotionEvent&) override;

private:
struct PrivateData;
PrivateData* const pData;

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageButton)
};

// -----------------------------------------------------------------------

class ImageKnob : public Widget
{
public:
enum Orientation {
Horizontal,
Vertical
};

class Callback
{
public:
virtual ~Callback() {}
virtual void imageKnobDragStarted(ImageKnob* imageKnob) = 0;
virtual void imageKnobDragFinished(ImageKnob* imageKnob) = 0;
virtual void imageKnobValueChanged(ImageKnob* imageKnob, float value) = 0;
};

explicit ImageKnob(Window& parent, const Image& image, Orientation orientation = Vertical) noexcept;
explicit ImageKnob(Widget* widget, const Image& image, Orientation orientation = Vertical) noexcept;
explicit ImageKnob(const ImageKnob& imageKnob);
ImageKnob& operator=(const ImageKnob& imageKnob);
~ImageKnob() override;

float getValue() const noexcept;

void setDefault(float def) noexcept;
void setRange(float min, float max) noexcept;
void setStep(float step) noexcept;
void setValue(float value, bool sendCallback = false) noexcept;
void setUsingLogScale(bool yesNo) noexcept;

void setCallback(Callback* callback) noexcept;
void setOrientation(Orientation orientation) noexcept;
void setRotationAngle(int angle);

void setImageLayerCount(uint count) noexcept;

protected:
void onDisplay() override;
bool onMouse(const MouseEvent&) override;
bool onMotion(const MotionEvent&) override;
bool onScroll(const ScrollEvent&) override;

private:
Image fImage;
float fMinimum;
float fMaximum;
float fStep;
float fValue;
float fValueDef;
float fValueTmp;
bool fUsingDefault;
bool fUsingLog;
Orientation fOrientation;

int fRotationAngle;
bool fDragging;
int fLastX;
int fLastY;

Callback* fCallback;

bool fIsImgVertical;
uint fImgLayerWidth;
uint fImgLayerHeight;
uint fImgLayerCount;
bool fIsReady;
GLuint fTextureId;

float _logscale(float value) const;
float _invlogscale(float value) const;

DISTRHO_LEAK_DETECTOR(ImageKnob)
};

// -----------------------------------------------------------------------

// note set range and step before setting the value

class ImageSlider : public Widget
{
public:
class Callback
{
public:
virtual ~Callback() {}
virtual void imageSliderDragStarted(ImageSlider* imageSlider) = 0;
virtual void imageSliderDragFinished(ImageSlider* imageSlider) = 0;
virtual void imageSliderValueChanged(ImageSlider* imageSlider, float value) = 0;
};

explicit ImageSlider(Window& parent, const Image& image) noexcept;
explicit ImageSlider(Widget* widget, const Image& image) noexcept;

float getValue() const noexcept;
void setValue(float value, bool sendCallback = false) noexcept;

void setStartPos(const Point<int>& startPos) noexcept;
void setStartPos(int x, int y) noexcept;
void setEndPos(const Point<int>& endPos) noexcept;
void setEndPos(int x, int y) noexcept;

void setInverted(bool inverted) noexcept;
void setRange(float min, float max) noexcept;
void setStep(float step) noexcept;

void setCallback(Callback* callback) noexcept;

protected:
void onDisplay() override;
bool onMouse(const MouseEvent&) override;
bool onMotion(const MotionEvent&) override;

private:
Image fImage;
float fMinimum;
float fMaximum;
float fStep;
float fValue;
float fValueTmp;

bool fDragging;
bool fInverted;
bool fValueIsSet;
int fStartedX;
int fStartedY;

Callback* fCallback;

Point<int> fStartPos;
Point<int> fEndPos;
Rectangle<int> fSliderArea;

void _recheckArea() noexcept;

// these should not be used
void setAbsoluteX(int) const noexcept {}
void setAbsoluteY(int) const noexcept {}
void setAbsolutePos(int, int) const noexcept {}
void setAbsolutePos(const Point<int>&) const noexcept {}

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageSlider)
};

// -----------------------------------------------------------------------

class ImageSwitch : public Widget
{
public:
class Callback
{
public:
virtual ~Callback() {}
virtual void imageSwitchClicked(ImageSwitch* imageButton, bool down) = 0;
};

explicit ImageSwitch(Window& parent, const Image& imageNormal, const Image& imageDown) noexcept;
explicit ImageSwitch(Widget* widget, const Image& imageNormal, const Image& imageDown) noexcept;
explicit ImageSwitch(const ImageSwitch& imageSwitch) noexcept;
ImageSwitch& operator=(const ImageSwitch& imageSwitch) noexcept;

bool isDown() const noexcept;
void setDown(bool down) noexcept;

void setCallback(Callback* callback) noexcept;

protected:
void onDisplay() override;
bool onMouse(const MouseEvent&) override;

private:
Image fImageNormal;
Image fImageDown;
bool fIsDown;

Callback* fCallback;

DISTRHO_LEAK_DETECTOR(ImageSwitch)
};

// -----------------------------------------------------------------------
#ifdef DGL_CAIRO
typedef CairoImageAboutWindow ImageAboutWindow;
typedef CairoImageButton ImageButton;
typedef CairoImageKnob ImageKnob;
typedef CairoImageSlider ImageSlider;
typedef CairoImageSwitch ImageSwitch;
#else
typedef OpenGLImageAboutWindow ImageAboutWindow;
typedef OpenGLImageButton ImageButton;
typedef OpenGLImageKnob ImageKnob;
typedef OpenGLImageSlider ImageSlider;
typedef OpenGLImageSwitch ImageSwitch;
#endif

END_NAMESPACE_DGL



+ 29
- 8
source/modules/dgl/Makefile View File

@@ -11,6 +11,7 @@ include ../Makefile.mk
# ---------------------------------------------------------------------------------------------------------------------

BUILD_CXX_FLAGS += $(DGL_FLAGS) -Isrc
BUILD_CXX_FLAGS += -Isrc/pugl-upstream/include

ifneq ($(MACOS),true)
WINE_FLAGS = -I/usr/include/wine/wine/windows
@@ -22,27 +23,47 @@ endif

OBJS = \
$(OBJDIR)/Application.cpp.o \
$(OBJDIR)/ApplicationPrivateData.cpp.o \
$(OBJDIR)/Color.cpp.o \
$(OBJDIR)/EventHandlers.cpp.o \
$(OBJDIR)/Geometry.cpp.o \
$(OBJDIR)/Image.cpp.o \
$(OBJDIR)/ImageWidgets.cpp.o \
$(OBJDIR)/ImageBase.cpp.o \
$(OBJDIR)/ImageBaseWidgets.cpp.o \
$(OBJDIR)/NanoVG.cpp.o \
$(OBJDIR)/Widget.cpp.o
$(OBJDIR)/OpenGL.cpp.o \
$(OBJDIR)/SubWidget.cpp.o \
$(OBJDIR)/SubWidgetPrivateData.cpp.o \
$(OBJDIR)/TopLevelWidget.cpp.o \
$(OBJDIR)/TopLevelWidgetPrivateData.cpp.o \
$(OBJDIR)/Widget.cpp.o \
$(OBJDIR)/WidgetPrivateData.cpp.o \
$(OBJDIR)/Window.cpp.o \
$(OBJDIR)/WindowPrivateData.cpp.o

ifeq ($(MACOS),true)
OBJS += $(OBJDIR)/Window.mm.o
OBJS += $(OBJDIR)/pugl.mm.o
else
OBJS += $(OBJDIR)/Window.cpp.o
OBJS += $(OBJDIR)/pugl.cpp.o
endif

OBJS_wine = \
$(OBJDIR)/pugl.cpp-wine.o \
$(OBJDIR)/Application.cpp-wine.o \
$(OBJDIR)/ApplicationPrivateData.cpp-wine.o \
$(OBJDIR)/Color.cpp-wine.o \
$(OBJDIR)/EventHandlers.cpp-wine.o \
$(OBJDIR)/Geometry.cpp-wine.o \
$(OBJDIR)/Image.cpp-wine.o \
$(OBJDIR)/ImageWidgets.cpp-wine.o \
$(OBJDIR)/ImageBase.cpp-wine.o \
$(OBJDIR)/ImageBaseWidgets.cpp-wine.o \
$(OBJDIR)/OpenGL.cpp-wine.o \
$(OBJDIR)/SubWidget.cpp-wine.o \
$(OBJDIR)/SubWidgetPrivateData.cpp-wine.o \
$(OBJDIR)/TopLevelWidget.cpp-wine.o \
$(OBJDIR)/TopLevelWidgetPrivateData.cpp-wine.o \
$(OBJDIR)/Widget.cpp-wine.o \
$(OBJDIR)/Window.cpp-wine.o
$(OBJDIR)/WidgetPrivateData.cpp-wine.o \
$(OBJDIR)/Window.cpp-wine.o \
$(OBJDIR)/WindowPrivateData.cpp-wine.o

# ---------------------------------------------------------------------------------------------------------------------



+ 80
- 21
source/modules/dgl/NanoVG.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -18,7 +18,15 @@
#define DGL_NANO_WIDGET_HPP_INCLUDED

#include "Color.hpp"
#include "Widget.hpp"
#include "OpenGL.hpp"
#include "SubWidget.hpp"
#include "TopLevelWidget.hpp"
#include "StandaloneWindow.hpp"

#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable:4661) /* instantiated template classes whose methods are defined elsewhere */
#endif

#ifndef DGL_NO_SHARED_RESOURCES
# define NANOVG_DEJAVU_SANS_TTF "__dpf_dejavusans_ttf__"
@@ -32,9 +40,17 @@ START_NAMESPACE_DGL
// -----------------------------------------------------------------------
// Forward class names

class BlendishWidget;
class NanoVG;

// -----------------------------------------------------------------------
// Helper methods

/**
Create a NanoVG context using the DPF-provided NanoVG library.
On Windows this will load a few extra OpenGL functions required for NanoVG to work.
*/
NVGcontext* nvgCreateGL(int flags);

// -----------------------------------------------------------------------
// NanoImage

@@ -304,7 +320,9 @@ public:
/**
Constructor reusing a NanoVG context, used for subwidgets.
*/
/*
NanoVG(NanoWidget* groupWidget);
*/

/**
Destructor.
@@ -435,6 +453,11 @@ public:
*/
void globalAlpha(float alpha);

/**
Sets the color tint applied to all rendered shapes.
*/
void globalTint(Color tint);

/* --------------------------------------------------------------------
* Transforms */

@@ -578,12 +601,26 @@ public:
NanoImage::Handle createImageFromMemory(uchar* data, uint dataSize, int imageFlags);

/**
Creates image from specified image data.
Creates image from specified raw format image data.
*/
NanoImage::Handle createImageFromRawMemory(uint w, uint h, const uchar* data,
ImageFlags imageFlags, ImageFormat format);

/**
Creates image from specified raw format image data.
Overloaded function for convenience.
@see ImageFlags
*/
NanoImage::Handle createImageFromRawMemory(uint w, uint h, const uchar* data,
int imageFlags, ImageFormat format);

/**
Creates image from specified RGBA image data.
*/
NanoImage::Handle createImageFromRGBA(uint w, uint h, const uchar* data, ImageFlags imageFlags);

/**
Creates image from specified image data.
Creates image from specified RGBA image data.
Overloaded function for convenience.
@see ImageFlags
*/
@@ -850,14 +887,13 @@ public:
/**
Load DPF's internal shared resources for this NanoVG class.
*/
virtual void loadSharedResources();
virtual bool loadSharedResources();
#endif

private:
NVGcontext* const fContext;
bool fInFrame;
bool fIsSubWidget;
friend class BlendishWidget;

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(NanoVG)
};
@@ -872,30 +908,39 @@ private:
The drawing function onDisplay() is implemented internally but a
new onNanoDisplay() needs to be overridden instead.
*/
class NanoWidget : public Widget,
public NanoVG
template <class BaseWidget>
class NanoBaseWidget : public BaseWidget,
public NanoVG
{
public:
/**
Constructor.
Constructor for a NanoSubWidget.
@see CreateFlags
*/
explicit NanoBaseWidget(Widget* parentGroupWidget, int flags = CREATE_ANTIALIAS);

/**
Constructor for a NanoTopLevelWidget.
@see CreateFlags
*/
explicit NanoWidget(Window& parent, int flags = CREATE_ANTIALIAS);
explicit NanoBaseWidget(Window& windowToMapTo, int flags = CREATE_ANTIALIAS);

/**
Constructor for a subwidget.
Constructor for a NanoStandaloneWindow without transient parent window.
@see CreateFlags
*/
explicit NanoWidget(Widget* groupWidget, int flags = CREATE_ANTIALIAS);
explicit NanoBaseWidget(Application& app, int flags = CREATE_ANTIALIAS);

/**
Constructor for a subwidget, reusing a NanoVG context.
Constructor for a NanoStandaloneWindow with transient parent window.
@see CreateFlags
*/
explicit NanoWidget(NanoWidget* groupWidget);
explicit NanoBaseWidget(Application& app, Window& transientParentWindow, int flags = CREATE_ANTIALIAS);

/**
Destructor.
*/
virtual ~NanoWidget();
virtual ~NanoBaseWidget() {}

protected:
/**
@@ -905,14 +950,17 @@ protected:
virtual void onNanoDisplay() = 0;

private:
struct PrivateData;
PrivateData* const nData;

/**
Widget display function.
Implemented internally to wrap begin/endFrame() automatically.
*/
void onDisplay() override;
inline void onDisplay() override
{
// NOTE maybe should use BaseWidget::getWindow().getScaleFactor() as 3rd arg ?
NanoVG::beginFrame(BaseWidget::getWidth(), BaseWidget::getHeight());
onNanoDisplay();
NanoVG::endFrame();
}

// these should not be used
void beginFrame(uint,uint) {}
@@ -921,11 +969,22 @@ private:
void cancelFrame() {}
void endFrame() {}

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(NanoWidget)
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(NanoBaseWidget)
};

typedef NanoBaseWidget<SubWidget> NanoSubWidget;
typedef NanoBaseWidget<TopLevelWidget> NanoTopLevelWidget;
typedef NanoBaseWidget<StandaloneWindow> NanoStandaloneWindow;

DISTRHO_DEPRECATED_BY("NanoSubWidget")
typedef NanoSubWidget NanoWidget;

// -----------------------------------------------------------------------

END_NAMESPACE_DGL

#ifdef _MSC_VER
# pragma warning(pop)
#endif

#endif // DGL_NANO_WIDGET_HPP_INCLUDED

+ 308
- 0
source/modules/dgl/OpenGL.hpp View File

@@ -0,0 +1,308 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef DGL_OPENGL_HPP_INCLUDED
#define DGL_OPENGL_HPP_INCLUDED

#include "ImageBase.hpp"
#include "ImageBaseWidgets.hpp"

// -----------------------------------------------------------------------
// Fix OpenGL includes for Windows, based on glfw code (part 1)

#undef DGL_CALLBACK_DEFINED
#undef DGL_WINGDIAPI_DEFINED

#ifdef DISTRHO_OS_WINDOWS

#ifndef APIENTRY
# define APIENTRY __stdcall
#endif // APIENTRY

/* We need WINGDIAPI defined */
#ifndef WINGDIAPI
# if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__POCC__)
# define WINGDIAPI __declspec(dllimport)
# elif defined(__LCC__)
# define WINGDIAPI __stdcall
# else
# define WINGDIAPI extern
# endif
# define DGL_WINGDIAPI_DEFINED
#endif // WINGDIAPI

/* Some <GL/glu.h> files also need CALLBACK defined */
#ifndef CALLBACK
# if defined(_MSC_VER)
# if (defined(_M_MRX000) || defined(_M_IX86) || defined(_M_ALPHA) || defined(_M_PPC)) && !defined(MIDL_PASS)
# define CALLBACK __stdcall
# else
# define CALLBACK
# endif
# else
# define CALLBACK __stdcall
# endif
# define DGL_CALLBACK_DEFINED
#endif // CALLBACK

/* Most GL/glu.h variants on Windows need wchar_t */
#include <cstddef>

#endif // DISTRHO_OS_WINDOWS

// -----------------------------------------------------------------------
// OpenGL includes

#ifdef DISTRHO_OS_MAC
# ifdef DGL_USE_OPENGL3
# include <OpenGL/gl3.h>
# include <OpenGL/gl3ext.h>
# else
# include <OpenGL/gl.h>
# endif
#else
# ifndef DISTRHO_OS_WINDOWS
# define GL_GLEXT_PROTOTYPES
# endif
# ifndef __GLEW_H__
# include <GL/gl.h>
# include <GL/glext.h>
# endif
#endif

// -----------------------------------------------------------------------
// Missing OpenGL defines

#if defined(GL_BGR_EXT) && !defined(GL_BGR)
# define GL_BGR GL_BGR_EXT
#endif

#if defined(GL_BGRA_EXT) && !defined(GL_BGRA)
# define GL_BGRA GL_BGRA_EXT
#endif

#ifndef GL_CLAMP_TO_BORDER
# define GL_CLAMP_TO_BORDER 0x812D
#endif

// -----------------------------------------------------------------------
// Fix OpenGL includes for Windows, based on glfw code (part 2)

#ifdef DGL_CALLBACK_DEFINED
# undef CALLBACK
# undef DGL_CALLBACK_DEFINED
#endif

#ifdef DGL_WINGDIAPI_DEFINED
# undef WINGDIAPI
# undef DGL_WINGDIAPI_DEFINED
#endif

START_NAMESPACE_DGL

// -----------------------------------------------------------------------

/**
OpenGL Graphics context.
*/
struct OpenGLGraphicsContext : GraphicsContext
{
};

// -----------------------------------------------------------------------

static inline
ImageFormat asDISTRHOImageFormat(const GLenum format)
{
switch (format)
{
#ifdef DGL_USE_OPENGL3
case GL_RED:
#else
case GL_LUMINANCE:
#endif
return kImageFormatGrayscale;
case GL_BGR:
return kImageFormatBGR;
case GL_BGRA:
return kImageFormatBGRA;
case GL_RGB:
return kImageFormatRGB;
case GL_RGBA:
return kImageFormatRGBA;
}

return kImageFormatNull;
}

static inline
GLenum asOpenGLImageFormat(const ImageFormat format)
{
switch (format)
{
case kImageFormatNull:
break;
case kImageFormatGrayscale:
#ifdef DGL_USE_OPENGL3
return GL_RED;
#else
return GL_LUMINANCE;
#endif
case kImageFormatBGR:
return GL_BGR;
case kImageFormatBGRA:
return GL_BGRA;
case kImageFormatRGB:
return GL_RGB;
case kImageFormatRGBA:
return GL_RGBA;
}

return 0x0;
}

// -----------------------------------------------------------------------

/**
OpenGL Image class.

This is an Image class that handles raw image data in pixels.
You can init the image data on the contructor or later on by calling loadFromMemory().

To generate raw data useful for this class see the utils/png2rgba.py script.
Be careful when using a PNG without alpha channel, for those the format is 'GL_BGR'
instead of the default 'GL_BGRA'.

Images are drawn on screen via 2D textures.
*/
class OpenGLImage : public ImageBase
{
public:
/**
Constructor for a null Image.
*/
OpenGLImage();

/**
Constructor using raw image data.
@note @a rawData must remain valid for the lifetime of this Image.
*/
OpenGLImage(const char* rawData, uint width, uint height, ImageFormat format = kImageFormatBGRA);

/**
Constructor using raw image data.
@note @a rawData must remain valid for the lifetime of this Image.
*/
OpenGLImage(const char* rawData, const Size<uint>& size, ImageFormat format = kImageFormatBGRA);

/**
Constructor using another image data.
*/
OpenGLImage(const OpenGLImage& image);

/**
Destructor.
*/
~OpenGLImage() override;

/**
Load image data from memory.
@note @a rawData must remain valid for the lifetime of this Image.
*/
void loadFromMemory(const char* rawData,
const Size<uint>& size,
ImageFormat format = kImageFormatBGRA) noexcept override;

/**
Draw this image at position @a pos using the graphics context @a context.
*/
void drawAt(const GraphicsContext& context, const Point<int>& pos) override;

/**
TODO document this.
*/
OpenGLImage& operator=(const OpenGLImage& image) noexcept;

// FIXME this should not be needed
inline void loadFromMemory(const char* rdata, uint w, uint h, ImageFormat fmt = kImageFormatBGRA)
{ loadFromMemory(rdata, Size<uint>(w, h), fmt); };
inline void draw(const GraphicsContext& context)
{ drawAt(context, Point<int>(0, 0)); };
inline void drawAt(const GraphicsContext& context, int x, int y)
{ drawAt(context, Point<int>(x, y)); };

/**
Constructor using raw image data, specifying an OpenGL image format.
@note @a rawData must remain valid for the lifetime of this Image.
DEPRECATED This constructor uses OpenGL image format instead of DISTRHO one.
*/
DISTRHO_DEPRECATED_BY("OpenGLImage(const char*, uint, uint, ImageFormat)")
explicit OpenGLImage(const char* rawData, uint width, uint height, GLenum glFormat);

/**
Constructor using raw image data, specifying an OpenGL image format.
@note @a rawData must remain valid for the lifetime of this Image.
DEPRECATED This constructor uses OpenGL image format instead of DISTRHO one.
*/
DISTRHO_DEPRECATED_BY("OpenGLImage(const char*, const Size<uint>&, ImageFormat)")
explicit OpenGLImage(const char* rawData, const Size<uint>& size, GLenum glFormat);

/**
Draw this image at (0, 0) point using the current OpenGL context.
DEPRECATED This function does not take into consideration the current graphics context and only works in OpenGL.
*/
DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)")
void draw();

/**
Draw this image at (x, y) point using the current OpenGL context.
DEPRECATED This function does not take into consideration the current graphics context and only works in OpenGL.
*/
DISTRHO_DEPRECATED_BY("drawAt(const GraphicsContext&, int, int)")
void drawAt(int x, int y);

/**
Draw this image at position @a pos using the current OpenGL context.
DEPRECATED This function does not take into consideration the current graphics context and only works in OpenGL.
*/
DISTRHO_DEPRECATED_BY("drawAt(const GraphicsContext&, const Point<int>&)")
void drawAt(const Point<int>& pos);

/**
Get the image type.
DEPRECATED Type is always assumed to be GL_UNSIGNED_BYTE.
*/
DISTRHO_DEPRECATED
GLenum getType() const noexcept { return GL_UNSIGNED_BYTE; }

private:
GLuint textureId;
bool setupCalled;
};

// -----------------------------------------------------------------------

typedef ImageBaseAboutWindow<OpenGLImage> OpenGLImageAboutWindow;
typedef ImageBaseButton<OpenGLImage> OpenGLImageButton;
typedef ImageBaseKnob<OpenGLImage> OpenGLImageKnob;
typedef ImageBaseSlider<OpenGLImage> OpenGLImageSlider;
typedef ImageBaseSwitch<OpenGLImage> OpenGLImageSwitch;

// -----------------------------------------------------------------------

END_NAMESPACE_DGL

#endif

+ 48
- 18
source/modules/dgl/StandaloneWindow.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -17,39 +17,69 @@
#ifndef DGL_STANDALONE_WINDOW_HPP_INCLUDED
#define DGL_STANDALONE_WINDOW_HPP_INCLUDED

#include "Application.hpp"
#include "Widget.hpp"
#include "TopLevelWidget.hpp"
#include "Window.hpp"

START_NAMESPACE_DGL

// -----------------------------------------------------------------------

class StandaloneWindow : public Application,
public Window
class StandaloneWindow : public Window,
public TopLevelWidget
{
public:
/**
Constructor.
Constructor without parent.
*/
StandaloneWindow();
StandaloneWindow(Application& app)
: Window(app),
TopLevelWidget((Window&)*this),
sgc((Window&)*this) {}

/**
Show window and execute application.
Constructor with a transient parent window, typically used to run as modal.
*/
void exec();
StandaloneWindow(Application& app, Window& transientParentWindow)
: Window(app, transientParentWindow),
TopLevelWidget((Window&)*this),
sgc((Window&)*this, transientParentWindow) {}

private:
Widget* fWidget;

/** @internal */
void onReshape(uint width, uint height) override;
/**
Clear current graphics context.
Must be called at the end of your StandaloneWindow constructor.
*/
void done()
{
sgc.done();
}

/** @internal */
void _addWidget(Widget* widget) override;
/**
Overloaded functions to ensure they apply to the Window class.
*/
bool isVisible() const noexcept { return Window::isVisible(); }
void setVisible(bool yesNo) { Window::setVisible(yesNo); }
void hide() { Window::hide(); }
void show() { Window::show(); }
uint getWidth() const noexcept { return Window::getWidth(); }
uint getHeight() const noexcept { return Window::getHeight(); }
const Size<uint> getSize() const noexcept { return Window::getSize(); }
void repaint() noexcept { Window::repaint(); }
void setWidth(uint width) { Window::setWidth(width); }
void setHeight(uint height) { Window::setHeight(height); }
void setSize(uint width, uint height) { Window::setSize(width, height); }
void setSize(const Size<uint>& size) { Window::setSize(size); }
bool addIdleCallback(IdleCallback* callback, uint timerFrequencyInMs = 0)
{ return Window::addIdleCallback(callback, timerFrequencyInMs); }
bool removeIdleCallback(IdleCallback* callback) { return Window::removeIdleCallback(callback); }
Application& getApp() const noexcept { return Window::getApp(); }
const GraphicsContext& getGraphicsContext() const noexcept { return Window::getGraphicsContext(); }
double getScaleFactor() const noexcept { return Window::getScaleFactor(); }
void setGeometryConstraints(uint minimumWidth, uint minimumHeight,
bool keepAspectRatio = false, bool automaticallyScale = false)
{ Window::setGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio, automaticallyScale); }

/** @internal */
void _removeWidget(Widget* widget) override;
private:
ScopedGraphicsContext sgc;

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(StandaloneWindow)
};


+ 179
- 0
source/modules/dgl/SubWidget.hpp View File

@@ -0,0 +1,179 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef DGL_SUBWIDGET_HPP_INCLUDED
#define DGL_SUBWIDGET_HPP_INCLUDED

#include "Widget.hpp"

START_NAMESPACE_DGL

// --------------------------------------------------------------------------------------------------------------------

/**
Sub-Widget class.

This class is the main entry point for creating any reusable widgets from within DGL.
It can be freely positioned from within a parent widget, thus being named subwidget.

Many subwidgets can share the same parent, and subwidgets themselves can also have its own subwidgets.
It is subwidgets all the way down.

TODO check absolute vs relative position and see what makes more sense.

@see CairoSubWidget
*/
class SubWidget : public Widget
{
public:
/**
Constructor.
*/
explicit SubWidget(Widget* parentWidget);

/**
Destructor.
*/
virtual ~SubWidget();

/**
Check if this widget contains the point defined by @a x and @a y.
*/
// TODO rename as containsRelativePos
template<typename T>
bool contains(T x, T y) const noexcept;

/**
Check if this widget contains the point @a pos.
*/
// TODO rename as containsRelativePos
template<typename T>
bool contains(const Point<T>& pos) const noexcept;

/**
Get absolute X.
*/
int getAbsoluteX() const noexcept;

/**
Get absolute Y.
*/
int getAbsoluteY() const noexcept;

/**
Get absolute position.
*/
Point<int> getAbsolutePos() const noexcept;

/**
Get absolute area of this subwidget.
This is the same as `Rectangle<int>(getAbsolutePos(), getSize());`
@see getConstrainedAbsoluteArea()
*/
Rectangle<int> getAbsoluteArea() const noexcept;

/**
Get absolute area of this subwidget, with special consideration for not allowing negative values.
@see getAbsoluteArea()
*/
Rectangle<uint> getConstrainedAbsoluteArea() const noexcept;

/**
Set absolute X.
*/
void setAbsoluteX(int x) noexcept;

/**
Set absolute Y.
*/
void setAbsoluteY(int y) noexcept;

/**
Set absolute position using @a x and @a y values.
*/
void setAbsolutePos(int x, int y) noexcept;

/**
Set absolute position.
*/
void setAbsolutePos(const Point<int>& pos) noexcept;

/**
Get the margin currently in use for widget coordinates.
By default this value is (0,0).
*/
Point<int> getMargin() const noexcept;

/**
Set a margin to be used for widget coordinates using @a x and @a y values.
*/
void setMargin(int x, int y) noexcept;

/**
Set a margin to be used for widget coordinates.
*/
void setMargin(const Point<int>& offset) noexcept;

/**
Get parent Widget, as passed in the constructor.
*/
Widget* getParentWidget() const noexcept;

/**
Request repaint of this subwidget's area to the window this widget belongs to.
*/
void repaint() noexcept override;

/**
Bring this widget to the "front" of the parent widget.
Makes the widget behave as if it was the last to be registered on the parent widget, thus being "in front".
*/
virtual void toFront();

/**
Indicate that this subwidget will draw out of bounds, and thus needs the entire viewport available for drawing.
*/
void setNeedsFullViewportDrawing(bool needsFullViewportForDrawing = true);

/**
Indicate that this subwidget will always draw at its own internal size and needs scaling to fit target size.
*/
void setNeedsViewportScaling(bool needsViewportScaling = true, double autoScaleFactor = 0.0);

/**
Indicate that this subwidget should not be drawn on screen, typically because it is managed by something else.
*/
void setSkipDrawing(bool skipDrawing = true);

protected:
/**
A function called when the subwidget's absolute position is changed.
*/
virtual void onPositionChanged(const PositionChangedEvent&);

private:
struct PrivateData;
PrivateData* const pData;
friend class Widget;
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SubWidget)
};

// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL

#endif // DGL_SUBWIDGET_HPP_INCLUDED


+ 146
- 0
source/modules/dgl/TopLevelWidget.hpp View File

@@ -0,0 +1,146 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef DGL_TOP_LEVEL_WIDGET_HPP_INCLUDED
#define DGL_TOP_LEVEL_WIDGET_HPP_INCLUDED

#include "Widget.hpp"

#ifdef DISTRHO_DEFINES_H_INCLUDED
START_NAMESPACE_DISTRHO
class UI;
END_NAMESPACE_DISTRHO
#endif

START_NAMESPACE_DGL

class Window;

// -----------------------------------------------------------------------

/**
Top-Level Widget class.

This is the only Widget class that is allowed to be used directly on a Window.

This widget takes the full size of the Window it is mapped to.
Sub-widgets can be added on top of this top-level widget, by creating them with this class as parent.
Doing so allows for custom position and sizes.

This class is used as the type for DPF Plugin UIs.
So anything that a plugin UI might need that does not belong in a simple Widget will go here.
*/
class TopLevelWidget : public Widget
{
public:
/**
Constructor.
*/
explicit TopLevelWidget(Window& windowToMapTo);

/**
Destructor.
*/
virtual ~TopLevelWidget();

/**
Get the application associated with this top-level widget's window.
*/
Application& getApp() const noexcept;

/**
Get the window associated with this top-level widget.
*/
Window& getWindow() const noexcept;

/**
Set width of this widget's window.
@note This will not change the widget's size right away, but be pending on the OS resizing the window
*/
void setWidth(uint width);

/**
Set height of this widget's window.
@note This will not change the widget's size right away, but be pending on the OS resizing the window
*/
void setHeight(uint height);

/**
Set size of this widget's window, using @a width and @a height values.
@note This will not change the widget's size right away, but be pending on the OS resizing the window
*/
void setSize(uint width, uint height);

/**
Set size of this widget's window.
@note This will not change the widget's size right away, but be pending on the OS resizing the window
*/
void setSize(const Size<uint>& size);

/**
TODO document this.
*/
void repaint() noexcept override;

/**
TODO document this.
*/
void repaint(const Rectangle<uint>& rect) noexcept;

// TODO group stuff after here, convenience functions present in Window class
bool setClipboard(const char* mimeType, const void* data, size_t dataSize);
const void* getClipboard(const char*& mimeType, size_t& dataSize);
bool setCursor(MouseCursor cursor);
bool addIdleCallback(IdleCallback* callback, uint timerFrequencyInMs = 0);
bool removeIdleCallback(IdleCallback* callback);
double getScaleFactor() const noexcept;
void setGeometryConstraints(uint minimumWidth,
uint minimumHeight,
bool keepAspectRatio = false,
bool automaticallyScale = false,
bool resizeNowIfAutoScaling = true);

DISTRHO_DEPRECATED_BY("getApp()")
Application& getParentApp() const noexcept { return getApp(); }

DISTRHO_DEPRECATED_BY("getWindow()")
Window& getParentWindow() const noexcept { return getWindow(); }

protected:
bool onKeyboard(const KeyboardEvent&) override;
bool onCharacterInput(const CharacterInputEvent&) override;
bool onMouse(const MouseEvent&) override;
bool onMotion(const MotionEvent&) override;
bool onScroll(const ScrollEvent&) override;

private:
struct PrivateData;
PrivateData* const pData;
friend class Window;
#ifdef DISTRHO_DEFINES_H_INCLUDED
friend class DISTRHO_NAMESPACE::UI;
#endif
/** @internal */
virtual void requestSizeChange(uint width, uint height);

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(TopLevelWidget)
};

// -----------------------------------------------------------------------

END_NAMESPACE_DGL

#endif // DGL_TOP_LEVEL_WIDGET_HPP_INCLUDED

+ 198
- 130
source/modules/dgl/Widget.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -19,91 +19,102 @@

#include "Geometry.hpp"

#include <vector>
START_NAMESPACE_DGL

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// Forward class names

#ifdef DISTRHO_DEFINES_H_INCLUDED
START_NAMESPACE_DISTRHO
class UI;
END_NAMESPACE_DISTRHO
#endif

START_NAMESPACE_DGL

class Application;
class ImageSlider;
class NanoWidget;
class SubWidget;
class TopLevelWidget;
class Window;
class StandaloneWindow;

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

/**
Base DGL Widget class.

This is the base Widget class, from which all widgets are built.

All widgets have a parent Window where they'll be drawn.
This parent is never changed during the widget lifetime.
All widgets have a parent widget where they'll be drawn, this can be the top-level widget or a group widget.
This parent is never changed during a widget's lifetime.

Widgets receive events in relative coordinates.
(0, 0) means its top-left position.
Widgets receive events in relative coordinates. (0, 0) means its top-left position.

Windows paint widgets in the order they are constructed.
Early widgets are drawn first, at the bottom, then newer ones on top.
Events are sent in the inverse order so that the top-most widget gets
The top-level widget will draw subwidgets in the order they are constructed.
Early subwidgets are drawn first, at the bottom, then newer ones on top.
Events are sent in the inverse order so that the top-most widgets get
a chance to catch the event and stop its propagation.

All widget event callbacks do nothing by default.
All widget event callbacks do nothing by default and onDisplay MUST be reimplemented by subclasses.

@note It is not possible to subclass this Widget class directly, you must use SubWidget or TopLevelWidget instead.
*/
class Widget
{
public:
/**
Base event data.
@a mod The currently active keyboard modifiers, @see Modifier.
@a time The timestamp (if any).
These are the fields present on all Widget events.

@a mod Currently active keyboard modifiers, @see Modifier.
@a mod Event flags, @see EventFlag.
@a time Event timestamp (if any).
*/
struct BaseEvent {
uint mod;
uint32_t time;
uint mod;
uint flags;
uint time;

/** Constuctor */
BaseEvent() noexcept : mod(0x0), time(0) {}
/** Constructor */
BaseEvent() noexcept : mod(0x0), flags(0x0), time(0) {}
/** Destuctor */
virtual ~BaseEvent() noexcept {}
};

/**
Keyboard event.
@a press True if the key was pressed, false if released.
@a key Unicode point of the key pressed.

This event represents low-level key presses and releases.
This can be used for "direct" keyboard handing like key bindings, but must not be interpreted as text input.

Keys are represented portably as Unicode code points, using the "natural" code point for the key.
The @a key field is the code for the pressed key, without any modifiers applied.
For example, a press or release of the 'A' key will have `key` 97 ('a')
regardless of whether shift or control are being held.

Alternatively, the raw @a keycode can be used to work directly with physical keys,
but note that this value is not portable and differs between platforms and hardware.

@a press True if the key was pressed, false if released.
@a key Unicode point of the key pressed.
@a keycode Raw keycode.
@see onKeyboard
*/
struct KeyboardEvent : BaseEvent {
bool press;
uint key;
uint keycode;

/** Constuctor */
/** Constructor */
KeyboardEvent() noexcept
: BaseEvent(),
press(false),
key(0) {}
key(0),
keycode(0) {}
};

/**
Special keyboard event.
@a press True if the key was pressed, false if released.
@a key The key pressed.
@see onSpecial
DEPRECATED This used to be part of DPF due to pugl, but now deprecated and simply non-functional.
All events go through KeyboardEvent or CharacterInputEvent, use those instead.
*/
struct SpecialEvent : BaseEvent {
struct DISTRHO_DEPRECATED_BY("KeyboardEvent") SpecialEvent : BaseEvent {
bool press;
Key key;

/** Constuctor */
/** Constructor */
SpecialEvent() noexcept
: BaseEvent(),
press(false),
@@ -111,54 +122,107 @@ public:
};

/**
Mouse event.
@a button The button number (1 = left, 2 = middle, 3 = right).
@a press True if the button was pressed, false if released.
@a pos The widget-relative coordinates of the pointer.
Character input event.

This event represents text input, usually as the result of a key press.
The text is given both as a Unicode character code and a UTF-8 string.

Note that this event is generated by the platform's input system,
so there is not necessarily a direct correspondence between text events and physical key presses.
For example, with some input methods a sequence of several key presses will generate a single character.

@a keycode Raw key code.
@a character Unicode character code.
@a string UTF-8 string.
@see onCharacterInput
*/
struct CharacterInputEvent : BaseEvent {
uint keycode;
uint character;
char string[8];

/** Constructor */
CharacterInputEvent() noexcept
: BaseEvent(),
keycode(0),
character(0),
#ifdef DISTRHO_PROPER_CPP11_SUPPORT
string{'\0','\0','\0','\0','\0','\0','\0','\0'} {}
#else
string() { std::memset(string, 0, sizeof(string)); }
#endif
};

/**
Mouse press or release event.

@a button The button number starting from 1 (1 = left, 2 = middle, 3 = right).
@a press True if the button was pressed, false if released.
@a pos The widget-relative coordinates of the pointer.
@a absolutePos The absolute coordinates of the pointer.
@see onMouse
*/
struct MouseEvent : BaseEvent {
int button;
uint button;
bool press;
Point<int> pos;
Point<double> pos;
Point<double> absolutePos;

/** Constuctor */
/** Constructor */
MouseEvent() noexcept
: BaseEvent(),
button(0),
press(false),
pos(0, 0) {}
pos(0.0, 0.0),
absolutePos(0.0, 0.0) {}
};

/**
Mouse motion event.
@a pos The widget-relative coordinates of the pointer.

@a pos The widget-relative coordinates of the pointer.
@a absolutePos The absolute coordinates of the pointer.
@see onMotion
*/
struct MotionEvent : BaseEvent {
Point<int> pos;
Point<double> pos;
Point<double> absolutePos;

/** Constuctor */
/** Constructor */
MotionEvent() noexcept
: BaseEvent(),
pos(0, 0) {}
pos(0.0, 0.0),
absolutePos(0.0, 0.0) {}
};

/**
Mouse scroll event.
@a pos The widget-relative coordinates of the pointer.
@a delta The scroll distance.

The scroll distance is expressed in "lines",
an arbitrary unit that corresponds to a single tick of a detented mouse wheel.
For example, `delta.y` = 1.0 scrolls 1 line up.
Some systems and devices support finer resolution and/or higher values for fast scrolls,
so programs should handle any value gracefully.

@a pos The widget-relative coordinates of the pointer.
@a absolutePos The absolute coordinates of the pointer.
@a delta The scroll distance.
@a direction The direction of the scroll or "smooth".
@see onScroll
*/
struct ScrollEvent : BaseEvent {
Point<int> pos;
Point<float> delta;
Point<double> pos;
Point<double> absolutePos;
Point<double> delta;
ScrollDirection direction;

/** Constuctor */
/** Constructor */
ScrollEvent() noexcept
: BaseEvent(),
pos(0, 0),
delta(0.0f, 0.0f) {}
pos(0.0, 0.0),
absolutePos(0.0, 0.0),
delta(0.0, 0.0),
direction(kScrollSmooth) {}
};

/**
@@ -171,22 +235,40 @@ public:
Size<uint> size;
Size<uint> oldSize;

/** Constuctor */
/** Constructor */
ResizeEvent() noexcept
: size(0, 0),
oldSize(0, 0) {}
};

/**
Constructor.
Widget position changed event.
@a pos The new absolute position of the widget.
@a oldPos The previous absolute position of the widget.
@see onPositionChanged
*/
explicit Widget(Window& parent);
struct PositionChangedEvent {
Point<int> pos;
Point<int> oldPos;

/** Constructor */
PositionChangedEvent() noexcept
: pos(0, 0),
oldPos(0, 0) {}
};

private:
/**
Constructor for a subwidget.
Private constructor, reserved for TopLevelWidget class.
*/
explicit Widget(Widget* groupWidget);
explicit Widget(TopLevelWidget* topLevelWidget);

/**
Private constructor, reserved for SubWidget class.
*/
explicit Widget(Widget* widgetToGroupTo);

public:
/**
Destructor.
*/
@@ -199,9 +281,9 @@ public:
bool isVisible() const noexcept;

/**
Set widget visible (or not) according to @a yesNo.
Set widget visible (or not) according to @a visible.
*/
void setVisible(bool yesNo);
void setVisible(bool visible);

/**
Show widget.
@@ -228,7 +310,7 @@ public:
/**
Get size.
*/
const Size<uint>& getSize() const noexcept;
const Size<uint> getSize() const noexcept;

/**
Set width.
@@ -251,81 +333,58 @@ public:
void setSize(const Size<uint>& size) noexcept;

/**
Get absolute X.
*/
int getAbsoluteX() const noexcept;

/**
Get absolute Y.
*/
int getAbsoluteY() const noexcept;

/**
Get absolute position.
*/
const Point<int>& getAbsolutePos() const noexcept;

/**
Set absolute X.
*/
void setAbsoluteX(int x) noexcept;

/**
Set absolute Y.
*/
void setAbsoluteY(int y) noexcept;

/**
Set absolute position using @a x and @a y values.
Get the Id associated with this widget.
@see setId
*/
void setAbsolutePos(int x, int y) noexcept;
uint getId() const noexcept;

/**
Set absolute position.
Set an Id to be associated with this widget.
@see getId
*/
void setAbsolutePos(const Point<int>& pos) noexcept;
void setId(uint id) noexcept;

/**
Get this widget's window application.
Same as calling getParentWindow().getApp().
Get the application associated with this widget's window.
This is the same as calling `getTopLevelWidget()->getApp()`.
*/
Application& getParentApp() const noexcept;
Application& getApp() const noexcept;

/**
Get parent window, as passed in the constructor.
Get the window associated with this widget.
This is the same as calling `getTopLevelWidget()->getWindow()`.
*/
Window& getParentWindow() const noexcept;
Window& getWindow() const noexcept;

/**
Check if this widget contains the point defined by @a x and @a y.
Get the graphics context associated with this widget's window.
GraphicsContext is an empty struct and needs to be casted into a different type in order to be usable,
for example GraphicsContext.
@see CairoSubWidget, CairoTopLevelWidget
*/
bool contains(int x, int y) const noexcept;
const GraphicsContext& getGraphicsContext() const noexcept;

/**
Check if this widget contains the point @a pos.
Get top-level widget, as passed directly in the constructor
or going up the chain of group widgets until it finds the top-level one.
*/
bool contains(const Point<int>& pos) const noexcept;
TopLevelWidget* getTopLevelWidget() const noexcept;

/**
Tell this widget's window to repaint itself.
Request repaint of this widget's area to the window this widget belongs to.
On the raw Widget class this function does nothing.
*/
void repaint() noexcept;
virtual void repaint() noexcept;

/**
Get the Id associated with this widget.
@see setId
*/
uint getId() const noexcept;
DISTRHO_DEPRECATED_BY("getApp()")
Application& getParentApp() const noexcept { return getApp(); }

/**
Set an Id to be associated with this widget.
@see getId
*/
void setId(uint id) noexcept;
DISTRHO_DEPRECATED_BY("getWindow()")
Window& getParentWindow() const noexcept { return getWindow(); }

protected:
/**
A function called to draw the view contents with OpenGL.
A function called to draw the widget contents.
*/
virtual void onDisplay() = 0;

@@ -336,10 +395,10 @@ protected:
virtual bool onKeyboard(const KeyboardEvent&);

/**
A function called when a special key is pressed or released.
A function called when an UTF-8 character is received.
@return True to stop event propagation, false otherwise.
*/
virtual bool onSpecial(const SpecialEvent&);
virtual bool onCharacterInput(const CharacterInputEvent&);

/**
A function called when a mouse button is pressed or released.
@@ -364,25 +423,34 @@ protected:
*/
virtual void onResize(const ResizeEvent&);

/**
A function called when a special key is pressed or released.
DEPRECATED use onKeyboard or onCharacterInput
*/
#if defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 460
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
virtual bool onSpecial(const SpecialEvent&) { return false; }
#if defined(__clang__)
# pragma clang diagnostic pop
#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 460
# pragma GCC diagnostic pop
#endif

private:
struct PrivateData;
PrivateData* const pData;

/** @internal */
explicit Widget(Widget* groupWidget, bool addToSubWidgets);

friend class ImageSlider;
friend class NanoWidget;
friend class Window;
friend class StandaloneWindow;
#ifdef DISTRHO_DEFINES_H_INCLUDED
friend class DISTRHO_NAMESPACE::UI;
#endif
friend class SubWidget;
friend class TopLevelWidget;

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Widget)
};

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL



+ 447
- 77
source/modules/dgl/Window.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -19,128 +19,498 @@

#include "Geometry.hpp"

#ifdef DISTRHO_DEFINES_H_INCLUDED
START_NAMESPACE_DISTRHO
class UIExporter;
END_NAMESPACE_DISTRHO
#ifndef DGL_FILE_BROWSER_DISABLED
# include "../distrho/extra/FileBrowserDialog.hpp"
#endif

START_NAMESPACE_DGL

class Application;
class PluginWindow;
class TopLevelWidget;

// -----------------------------------------------------------------------

class Application;
class Widget;
class StandaloneWindow;
/**
DGL Window class.

This is the where all OS-related events initially happen, before being propagated to any widgets.

A Window MUST have an Application instance tied to it.
It is not possible to swap Application instances from within the lifetime of a Window.
But it is possible to completely change the Widgets that a Window contains during its lifetime.

Typically the event handling functions as following:
Application -> Window -> Top-Level-Widget -> SubWidgets

class Window
Please note that, unlike many other graphical toolkits out there,
DGL makes a clear distinction between a Window and a Widget.
You cannot directly draw in a Window, you need to create a Widget for that.

Also, a Window MUST have a single top-level Widget.
The Window will take care of global screen positioning and resizing, everything else is sent for widgets to handle.

...
*/
class DISTRHO_API Window
{
struct PrivateData;

public:
#ifndef DGL_FILE_BROWSER_DISABLED
typedef DISTRHO_NAMESPACE::FileBrowserHandle FileBrowserHandle;
typedef DISTRHO_NAMESPACE::FileBrowserOptions FileBrowserOptions;
#endif

/**
File browser options.
*/
struct FileBrowserOptions {
const char* startDir;
const char* title;
uint width;
uint height;

/**
File browser buttons.

0 means hidden.
1 means visible and unchecked.
2 means visible and checked.
*/
struct Buttons {
uint listAllFiles;
uint showHidden;
uint showPlaces;

/** Constuctor for default values */
Buttons()
: listAllFiles(2),
showHidden(1),
showPlaces(1) {}
} buttons;

/** Constuctor for default values */
FileBrowserOptions()
: startDir(nullptr),
title(nullptr),
width(0),
height(0),
buttons() {}
Window graphics context as a scoped struct.
This class gives graphics context drawing time to a window's widgets.
Typically used for allowing OpenGL drawing operations during a window + widget constructor.

Unless you are subclassing the Window or StandaloneWindow classes, you do not need to care.
In such cases you will need to use this struct as a way to get a valid OpenGL context.
For example in a standalone application:
```
int main()
{
Application app;
Window win(app);
ScopedPointer<MyCustomTopLevelWidget> widget;
{
const Window::ScopedGraphicsContext sgc(win);
widget = new MyCustomTopLevelWidget(win);
}
app.exec();
return 0;
}
```

This struct is necessary because we cannot automatically make the window leave the OpenGL context in custom code.
And we must always cleanly enter and leave the OpenGL context.
So in order to avoid messing up the global host context, this class is used around widget creation.
*/
struct ScopedGraphicsContext
{
/** Constructor that will make the @a window graphics context the current one */
explicit ScopedGraphicsContext(Window& window);

/** Overloaded constructor, gives back context to its transient parent when done */
explicit ScopedGraphicsContext(Window& window, Window& transientParentWindow);

/** Desstructor for clearing current context, if not done yet */
~ScopedGraphicsContext();

/** Early context clearing, useful for standalone windows not created by you. */
void done();

DISTRHO_DECLARE_NON_COPYABLE(ScopedGraphicsContext)
DISTRHO_PREVENT_HEAP_ALLOCATION

private:
Window& window;
Window::PrivateData* ppData;
bool active;
};
#endif // DGL_FILE_BROWSER_DISABLED

/**
Constructor for a regular, standalone window.
*/
explicit Window(Application& app);
explicit Window(Application& app, Window& parent);
explicit Window(Application& app, intptr_t parentId);

/**
Constructor for a modal window, by having another window as its transient parent.
The Application instance must be the same between the 2 windows.
*/
explicit Window(Application& app, Window& transientParentWindow);

/**
Constructor for an embed Window without known size,
typically used in modules or plugins that run inside another host.
*/
explicit Window(Application& app,
uintptr_t parentWindowHandle,
double scaleFactor,
bool resizable);

/**
Constructor for an embed Window with known size,
typically used in modules or plugins that run inside another host.
*/
explicit Window(Application& app,
uintptr_t parentWindowHandle,
uint width,
uint height,
double scaleFactor,
bool resizable);

/**
Destructor.
*/
virtual ~Window();

/**
Whether this Window is embed into another (usually not DGL-controlled) Window.
*/
bool isEmbed() const noexcept;

/**
Check if this window is visible / mapped.
Invisible windows do not receive events except resize.
@see setVisible(bool)
*/
bool isVisible() const noexcept;

/**
Set window visible (or not) according to @a visible.
Only valid for standalones, embed windows are always visible.
@see isVisible(), hide(), show()
*/
void setVisible(bool visible);

/**
Show window.
This is the same as calling setVisible(true).
@see isVisible(), setVisible(bool)
*/
void show();

/**
Hide window.
This is the same as calling setVisible(false).
@see isVisible(), setVisible(bool)
*/
void hide();

/**
Hide window and notify application of a window close event.
The application event-loop will stop when all windows have been closed.
For standalone windows only, has no effect if window is embed.
@see isEmbed()

@note It is possible to hide the window while not stopping the event-loop.
A closed window is always hidden, but the reverse is not always true.
*/
void close();
void exec(bool lockWait = false);

void focus();
void repaint() noexcept;
/**
Check if this window is resizable (by the user or window manager).
@see setResizable
*/
bool isResizable() const noexcept;

#ifndef DGL_FILE_BROWSER_DISABLED
bool openFileBrowser(const FileBrowserOptions& options);
#endif
/**
Set window as resizable (by the user or window manager).
It is always possible to resize a window programmatically, which is not the same as the user being allowed to it.
@note This function does nothing for plugins, where the resizable state is set via macro.
@see DISTRHO_UI_USER_RESIZABLE
*/
void setResizable(bool resizable);

bool isVisible() const noexcept;
void setVisible(bool yesNo);
/**
Get X offset, typically 0.
*/
int getOffsetX() const noexcept;

bool isResizable() const noexcept;
void setResizable(bool yesNo);
/**
Get Y offset, typically 0.
*/
int getOffsetY() const noexcept;

/**
Get offset.
*/
Point<int> getOffset() const noexcept;

/**
Set X offset.
*/
void setOffsetX(int x);

/**
Set Y offset.
*/
void setOffsetY(int y);

/**
Set offset using @a x and @a y values.
*/
void setOffset(int x, int y);

/**
Set offset.
*/
void setOffset(const Point<int>& offset);

/**
Get width.
*/
uint getWidth() const noexcept;

/**
Get height.
*/
uint getHeight() const noexcept;

/**
Get size.
*/
Size<uint> getSize() const noexcept;

/**
Set width.
*/
void setWidth(uint width);

/**
Set height.
*/
void setHeight(uint height);

/**
Set size using @a width and @a height values.
*/
void setSize(uint width, uint height);
void setSize(Size<uint> size);

/**
Set size.
*/
void setSize(const Size<uint>& size);

/**
Get the title of the window previously set with setTitle().
*/
const char* getTitle() const noexcept;

/**
Set the title of the window, typically displayed in the title bar or in window switchers.

This only makes sense for non-embedded windows.
*/
void setTitle(const char* title);

void setTransientWinId(uintptr_t winId);
/**
Check if key repeat events are ignored.
*/
bool isIgnoringKeyRepeat() const noexcept;

/**
Set to ignore (or not) key repeat events according to @a ignore.
*/
void setIgnoringKeyRepeat(bool ignore) noexcept;

/**
Set the clipboard contents.

This sets the system clipboard contents,
which can be retrieved with getClipboard() or pasted into other applications.

If using a string, the use of a null terminator is required (and must be part of dataSize).@n
The MIME type of the data "text/plain" is assumed if null is used.
*/
bool setClipboard(const char* mimeType, const void* data, size_t dataSize);

/**
Get the clipboard contents.

This gets the system clipboard contents,
which may have been set with setClipboard() or copied from another application.

returns the clipboard contents, or null.
*/
const void* getClipboard(const char*& mimeType, size_t& dataSize);

/**
Set the mouse cursor.

This changes the system cursor that is displayed when the pointer is inside the window.
May fail if setting the cursor is not supported on this system,
for example if compiled on X11 without Xcursor support.
*/
bool setCursor(MouseCursor cursor);

/**
Add a callback function to be triggered on every idle cycle or on a specific timer frequency.
You can add more than one, and remove them at anytime with removeIdleCallback().
This can be used to perform some action at a regular interval with relatively low frequency.

If providing a timer frequency, there are a few things to note:
1. There is a platform-specific limit to the number of supported timers, and overhead associated with each,
so you should create only a few timers and perform several tasks in one if necessary.
2. This timer frequency is not guaranteed to have a resolution better than 10ms
(the maximum timer resolution on Windows) and may be rounded up if it is too short.
On X11 and MacOS, a resolution of about 1ms can usually be relied on.
*/
bool addIdleCallback(IdleCallback* callback, uint timerFrequencyInMs = 0);

/**
Remove an idle callback previously added via addIdleCallback().
*/
bool removeIdleCallback(IdleCallback* callback);

/**
Get the application associated with this window.
*/
Application& getApp() const noexcept;
intptr_t getWindowId() const noexcept;

void addIdleCallback(IdleCallback* const callback);
void removeIdleCallback(IdleCallback* const callback);
/**
Get the graphics context associated with this window.
GraphicsContext is an empty struct and needs to be casted into a different type in order to be usable,
for example GraphicsContext.
@see CairoSubWidget, CairoTopLevelWidget
*/
const GraphicsContext& getGraphicsContext() const noexcept;

/**
Get the "native" window handle.
Returned value depends on the platform:
- HaikuOS: This is a pointer to a `BView`.
- MacOS: This is a pointer to an `NSView*`.
- Windows: This is a `HWND`.
- Everything else: This is an [X11] `Window`.
*/
uintptr_t getNativeWindowHandle() const noexcept;

/**
Get the scale factor requested for this window.
This is purely informational, and up to developers to choose what to do with it.

If you do not want to deal with this yourself,
consider using setGeometryConstraints() where you can specify to automatically scale the window contents.
@see setGeometryConstraints
*/
double getScaleFactor() const noexcept;

/**
Grab the keyboard input focus.
*/
void focus();

#ifndef DGL_FILE_BROWSER_DISABLED
/**
Open a file browser dialog with this window as transient parent.
A few options can be specified to setup the dialog.

If a path is selected, onFileSelected() will be called with the user chosen path.
If the user cancels or does not pick a file, onFileSelected() will be called with nullptr as filename.

This function does not block the event loop.
*/
bool openFileBrowser(const FileBrowserOptions& options = FileBrowserOptions());
#endif

/**
Request repaint of this window, for the entire area.
*/
void repaint() noexcept;

/**
Request partial repaint of this window, with bounds according to @a rect.
*/
void repaint(const Rectangle<uint>& rect) noexcept;

/**
Render this window's content into a picture file, specified by @a filename.
Window must be visible and on screen.
Written picture format is PPM.
*/
void renderToPicture(const char* filename);

/**
Run this window as a modal, blocking input events from the parent.
Only valid for windows that have been created with another window as parent (as passed in the constructor).
Can optionally block-wait, but such option is only available if the application is running as standalone.
*/
void runAsModal(bool blockWait = false);

/**
Get the geometry constraints set for the Window.
@see setGeometryConstraints
*/
Size<uint> getGeometryConstraints(bool& keepAspectRatio);

/**
Set geometry constraints for the Window when resized by the user, and optionally scale contents automatically.
*/
void setGeometryConstraints(uint minimumWidth,
uint minimumHeight,
bool keepAspectRatio = false,
bool automaticallyScale = false,
bool resizeNowIfAutoScaling = true);

/** DEPRECATED Use isIgnoringKeyRepeat(). */
DISTRHO_DEPRECATED_BY("isIgnoringKeyRepeat()")
inline bool getIgnoringKeyRepeat() const noexcept { return isIgnoringKeyRepeat(); }

/** DEPRECATED Use getScaleFactor(). */
DISTRHO_DEPRECATED_BY("getScaleFactor()")
inline double getScaling() const noexcept { return getScaleFactor(); }

/** DEPRECATED Use runAsModal(bool). */
DISTRHO_DEPRECATED_BY("runAsModal(bool)")
inline void exec(bool blockWait = false) { runAsModal(blockWait); }

protected:
virtual void onDisplayBefore();
virtual void onDisplayAfter();
/**
A function called when the window is attempted to be closed.
Returning true closes the window, which is the default behaviour.
Override this method and return false to prevent the window from being closed by the user.

This method is not used for embed windows, and not even made available in DISTRHO_NAMESPACE::UI.
For embed windows, closing is handled by the host/parent process and we have no control over it.
As such, a close action on embed windows will always succeed and cannot be cancelled.

NOTE: This currently does not work under macOS.
*/
virtual bool onClose();

/**
A function called when the window gains or loses the keyboard focus.
The default implementation does nothing.
*/
virtual void onFocus(bool focus, CrossingMode mode);

/**
A function called when the window is resized.
If there is a top-level widget associated with this window, its size will be set right after this function.
The default implementation sets up drawing context where necessary.
*/
virtual void onReshape(uint width, uint height);
virtual void onClose();

/**
A function called when scale factor requested for this window changes.
The default implementation does nothing.
WARNING function needs a proper name
*/
virtual void onScaleFactorChanged(double scaleFactor);

#ifndef DGL_FILE_BROWSER_DISABLED
virtual void fileBrowserSelected(const char* filename);
/**
A function called when a path is selected by the user, as triggered by openFileBrowser().
This action happens after the user confirms the action, so the file browser dialog will be closed at this point.
The default implementation does nothing.
*/
virtual void onFileSelected(const char* filename);

/** DEPRECATED Use onFileSelected(). */
DISTRHO_DEPRECATED_BY("onFileSelected(const char*)")
inline virtual void fileBrowserSelected(const char* filename) { return onFileSelected(filename); }
#endif

private:
struct PrivateData;
PrivateData* const pData;
friend class Application;
friend class Widget;
friend class StandaloneWindow;
#ifdef DISTRHO_DEFINES_H_INCLUDED
friend class DISTRHO_NAMESPACE::UIExporter;
#endif

virtual void _addWidget(Widget* const widget);
virtual void _removeWidget(Widget* const widget);
void _idle();
friend class PluginWindow;
friend class TopLevelWidget;

bool handlePluginKeyboard(const bool press, const uint key);
bool handlePluginSpecial(const bool press, const Key key);
/** @internal */
explicit Window(Application& app,
uintptr_t parentWindowHandle,
uint width,
uint height,
double scaleFactor,
bool resizable,
bool isVST3,
bool doPostInit);

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Window)
};


+ 42
- 35
source/modules/dgl/src/Application.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -14,19 +14,14 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifdef __WINE__
# include "winsock2.h"
#endif

#include "ApplicationPrivateData.hpp"
#include "../Window.hpp"

START_NAMESPACE_DGL

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

Application::Application()
: pData(new PrivateData()) {}
Application::Application(const bool isStandalone)
: pData(new PrivateData(isStandalone)) {}

Application::~Application()
{
@@ -35,44 +30,56 @@ Application::~Application()

void Application::idle()
{
for (std::list<Window*>::iterator it = pData->windows.begin(), ite = pData->windows.end(); it != ite; ++it)
{
Window* const window(*it);
window->_idle();
}

for (std::list<IdleCallback*>::iterator it = pData->idleCallbacks.begin(), ite = pData->idleCallbacks.end(); it != ite; ++it)
{
IdleCallback* const idleCallback(*it);
idleCallback->idleCallback();
}
pData->idle(0);
}

void Application::exec(unsigned int idleTime)
void Application::exec(const uint idleTimeInMs)
{
for (; pData->doLoop;)
{
idle();
d_msleep(idleTime);
}
DISTRHO_SAFE_ASSERT_RETURN(pData->isStandalone,);

while (! pData->isQuitting)
pData->idle(idleTimeInMs);
}

void Application::quit()
{
pData->doLoop = false;
pData->quit();
}

bool Application::isQuitting() const noexcept
{
return pData->isQuitting || pData->isQuittingInNextCycle;
}

bool Application::isStandalone() const noexcept
{
return pData->isStandalone;
}

double Application::getTime() const
{
return pData->getTime();
}

void Application::addIdleCallback(IdleCallback* const callback)
{
DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,)

pData->idleCallbacks.push_back(callback);
}

void Application::removeIdleCallback(IdleCallback* const callback)
{
DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,)

for (std::list<Window*>::reverse_iterator rit = pData->windows.rbegin(), rite = pData->windows.rend(); rit != rite; ++rit)
{
Window* const window(*rit);
window->close();
}
pData->idleCallbacks.remove(callback);
}

bool Application::isQuiting() const noexcept
void Application::setClassName(const char* const name)
{
return !pData->doLoop;
pData->setClassName(name);
}

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL

+ 172
- 0
source/modules/dgl/src/ApplicationPrivateData.cpp View File

@@ -0,0 +1,172 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "ApplicationPrivateData.hpp"
#include "../Window.hpp"

#include "pugl.hpp"

#include <ctime>

START_NAMESPACE_DGL

typedef std::list<DGL_NAMESPACE::Window*>::reverse_iterator WindowListReverseIterator;

static d_ThreadHandle getCurrentThreadHandle() noexcept
{
#ifdef DISTRHO_OS_WINDOWS
return GetCurrentThread();
#else
return pthread_self();
#endif
}

static bool isThisTheMainThread(const d_ThreadHandle mainThreadHandle) noexcept
{
#ifdef DISTRHO_OS_WINDOWS
return GetCurrentThread() == mainThreadHandle; // IsGUIThread ?
#else
return pthread_equal(getCurrentThreadHandle(), mainThreadHandle) != 0;
#endif
}

// --------------------------------------------------------------------------------------------------------------------

Application::PrivateData::PrivateData(const bool standalone)
: world(puglNewWorld(standalone ? PUGL_PROGRAM : PUGL_MODULE,
standalone ? PUGL_WORLD_THREADS : 0x0)),
isStandalone(standalone),
isQuitting(false),
isQuittingInNextCycle(false),
isStarting(true),
visibleWindows(0),
mainThreadHandle(getCurrentThreadHandle()),
windows(),
idleCallbacks()
{
DISTRHO_SAFE_ASSERT_RETURN(world != nullptr,);

puglSetWorldHandle(world, this);
puglSetClassName(world, DISTRHO_MACRO_AS_STRING(DGL_NAMESPACE));

#ifdef DISTRHO_OS_MAC
if (standalone)
puglMacOSActivateApp();
#endif
}

Application::PrivateData::~PrivateData()
{
DISTRHO_SAFE_ASSERT(isStarting || isQuitting);
DISTRHO_SAFE_ASSERT(visibleWindows == 0);

windows.clear();
idleCallbacks.clear();

if (world != nullptr)
puglFreeWorld(world);
}

// --------------------------------------------------------------------------------------------------------------------

void Application::PrivateData::oneWindowShown() noexcept
{
if (++visibleWindows == 1)
{
isQuitting = false;
isStarting = false;
}
}

void Application::PrivateData::oneWindowClosed() noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(visibleWindows != 0,);

if (--visibleWindows == 0)
isQuitting = true;
}

// --------------------------------------------------------------------------------------------------------------------

void Application::PrivateData::idle(const uint timeoutInMs)
{
if (isQuittingInNextCycle)
{
quit();
isQuittingInNextCycle = false;
}

if (world != nullptr)
{
const double timeoutInSeconds = timeoutInMs != 0
? static_cast<double>(timeoutInMs) / 1000.0
: 0.0;

puglUpdate(world, timeoutInSeconds);
}

triggerIdleCallbacks();
}

void Application::PrivateData::triggerIdleCallbacks()
{
for (std::list<IdleCallback*>::iterator it = idleCallbacks.begin(), ite = idleCallbacks.end(); it != ite; ++it)
{
IdleCallback* const idleCallback(*it);
idleCallback->idleCallback();
}
}

void Application::PrivateData::quit()
{
if (! isThisTheMainThread(mainThreadHandle))
{
if (! isQuittingInNextCycle)
{
isQuittingInNextCycle = true;
return;
}
}

isQuitting = true;

#ifndef DPF_TEST_APPLICATION_CPP
for (WindowListReverseIterator rit = windows.rbegin(), rite = windows.rend(); rit != rite; ++rit)
{
DGL_NAMESPACE::Window* const window(*rit);
window->close();
}
#endif
}

double Application::PrivateData::getTime() const
{
DISTRHO_SAFE_ASSERT_RETURN(world != nullptr, 0.0);

return puglGetTime(world);
}

void Application::PrivateData::setClassName(const char* const name)
{
DISTRHO_SAFE_ASSERT_RETURN(world != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0',);

puglSetClassName(world, name);
}

// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL

+ 83
- 38
source/modules/dgl/src/ApplicationPrivateData.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -18,53 +18,98 @@
#define DGL_APP_PRIVATE_DATA_HPP_INCLUDED

#include "../Application.hpp"
#include "../../distrho/extra/Sleep.hpp"

#include <list>

#ifdef DISTRHO_OS_WINDOWS
# ifndef NOMINMAX
# define NOMINMAX
# endif
# include <winsock2.h>
# include <windows.h>
typedef HANDLE d_ThreadHandle;
#else
# include <pthread.h>
typedef pthread_t d_ThreadHandle;
#endif

#ifdef DISTRHO_OS_MAC
typedef struct PuglWorldImpl PuglWorld;
#endif

START_NAMESPACE_DGL

// -----------------------------------------------------------------------
class Window;

#ifndef DISTRHO_OS_MAC
typedef struct PuglWorldImpl PuglWorld;
#endif

// --------------------------------------------------------------------------------------------------------------------

struct Application::PrivateData {
bool doLoop;
/** Pugl world instance. */
PuglWorld* const world;

/** Whether the application is running as standalone, otherwise it is part of a plugin. */
const bool isStandalone;

/** Whether the applicating is about to quit, or already stopped. Defaults to false. */
bool isQuitting;

/** Helper for safely close everything from main thread. */
bool isQuittingInNextCycle;

/** Whether the applicating is starting up, that is, no windows have been made visible yet. Defaults to true. */
bool isStarting;

/** Counter of visible windows, only used in standalone mode.
If 0->1, application is starting. If 1->0, application is quitting/stopping. */
uint visibleWindows;
std::list<Window*> windows;
std::list<IdleCallback*> idleCallbacks;

PrivateData()
: doLoop(true),
visibleWindows(0),
windows(),
idleCallbacks() {}

~PrivateData()
{
DISTRHO_SAFE_ASSERT(! doLoop);
DISTRHO_SAFE_ASSERT(visibleWindows == 0);

windows.clear();
idleCallbacks.clear();
}

void oneShown() noexcept
{
if (++visibleWindows == 1)
doLoop = true;
}

void oneHidden() noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(visibleWindows > 0,);

if (--visibleWindows == 0)
doLoop = false;
}

DISTRHO_DECLARE_NON_COPY_STRUCT(PrivateData)

/** Handle that identifies the main thread. Used to check if calls belong to current thread or not. */
d_ThreadHandle mainThreadHandle;

/** List of windows for this application. Only used during `close`. */
std::list<DGL_NAMESPACE::Window*> windows;

/** List of idle callbacks for this application. */
std::list<DGL_NAMESPACE::IdleCallback*> idleCallbacks;

/** Constructor and destructor */
explicit PrivateData(bool standalone);
~PrivateData();

/** Flag one window as shown, which increments @a visibleWindows.
Sets @a isQuitting and @a isStarting as false if this is the first window.
For standalone mode only. */
void oneWindowShown() noexcept;

/** Flag one window as closed, which decrements @a visibleWindows.
Sets @a isQuitting as true if this is the last window.
For standalone mode only. */
void oneWindowClosed() noexcept;

/** Run Pugl world update for @a timeoutInMs, and then each idle callback in order of registration. */
void idle(uint timeoutInMs);

/** Run each idle callback without updating pugl world. */
void triggerIdleCallbacks();

/** Set flag indicating application is quitting, and close all windows in reverse order of registration.
For standalone mode only. */
void quit();

/** Get time via pugl */
double getTime() const;

/** Set pugl world class name. */
void setClassName(const char* name);

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData)
};

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL



+ 68
- 54
source/modules/dgl/src/Color.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -16,12 +16,23 @@

#include "../Color.hpp"

#include "nanovg/nanovg.h"

START_NAMESPACE_DGL

// -----------------------------------------------------------------------

static float computeHue(float h, float m1, float m2)
{
if (h < 0) h += 1;
if (h > 1) h -= 1;
if (h < 1.0f/6.0f)
return m1 + (m2 - m1) * h * 6.0f;
if (h < 3.0f/6.0f)
return m2;
if (h < 4.0f/6.0f)
return m1 + (m2 - m1) * (2.0f/3.0f - h) * 6.0f;
return m1;
}

static void fixRange(float& value)
{
/**/ if (value < 0.0f)
@@ -46,27 +57,27 @@ static uchar getFixedRange2(const float& value)
return 0;
if (value2 >= 255.0f)
return 255;
return static_cast<uchar>(value2);
return static_cast<uchar>(value2 + 0.5f);
}

// -----------------------------------------------------------------------

Color::Color() noexcept
: red(1.0f),
green(1.0f),
blue(1.0f),
: red(0.0f),
green(0.0f),
blue(0.0f),
alpha(1.0f) {}

Color::Color(int r, int g, int b, int a) noexcept
Color::Color(const int r, const int g, const int b, const float a) noexcept
: red(static_cast<float>(r)/255.0f),
green(static_cast<float>(g)/255.0f),
blue(static_cast<float>(b)/255.0f),
alpha(static_cast<float>(a)/255.0f)
alpha(a)
{
fixBounds();
}

Color::Color(float r, float g, float b, float a) noexcept
Color::Color(const float r, const float g, const float b, const float a) noexcept
: red(r),
green(g),
blue(b),
@@ -94,7 +105,7 @@ Color& Color::operator=(const Color& color) noexcept
return *this;
}

Color::Color(const Color& color1, const Color& color2, float u) noexcept
Color::Color(const Color& color1, const Color& color2, const float u) noexcept
: red(color1.red),
green(color1.green),
blue(color1.blue),
@@ -103,70 +114,91 @@ Color::Color(const Color& color1, const Color& color2, float u) noexcept
interpolate(color2, u);
}

Color Color::withAlpha(const float alpha2) noexcept
{
Color color(*this);
color.alpha = alpha2;
return color;
}

Color Color::fromHSL(float hue, float saturation, float lightness, float alpha)
{
return nvgHSLA(hue, saturation, lightness, static_cast<uchar>(getFixedRange(alpha)*255.0f));
float m1, m2;
Color col;
hue = fmodf(hue, 1.0f);
if (hue < 0.0f) hue += 1.0f;
fixRange(saturation);
fixRange(lightness);
m2 = lightness <= 0.5f ? (lightness * (1 + saturation)) : (lightness + saturation - lightness * saturation);
m1 = 2 * lightness - m2;
col.red = computeHue(hue + 1.0f/3.0f, m1, m2);
col.green = computeHue(hue, m1, m2);
col.blue = computeHue(hue - 1.0f/3.0f, m1, m2);
col.alpha = alpha;
col.fixBounds();
return col;
}

Color Color::fromHTML(const char* rgb, float alpha)
Color Color::fromHTML(const char* rgb, const float alpha) noexcept
{
Color fallback;
DISTRHO_SAFE_ASSERT_RETURN(rgb != nullptr && rgb[0] != '\0', fallback);

if (rgb[0] == '#') ++rgb;
if (rgb[0] == '#')
++rgb;
DISTRHO_SAFE_ASSERT_RETURN(rgb[0] != '\0', fallback);

std::size_t rgblen(std::strlen(rgb));
std::size_t rgblen = std::strlen(rgb);
DISTRHO_SAFE_ASSERT_RETURN(rgblen == 3 || rgblen == 6, fallback);

char rgbtmp[3] = { '\0', '\0', '\0' };
char rgbtmp[5] = { '0', 'x', '\0', '\0', '\0' };
int r, g, b;

if (rgblen == 3)
{
rgbtmp[0] = rgb[0];
r = static_cast<int>(std::strtol(rgbtmp, nullptr, 16));
rgbtmp[2] = rgb[0];
r = static_cast<int>(std::strtol(rgbtmp, nullptr, 16)) * 17;

rgbtmp[0] = rgb[1];
g = static_cast<int>(std::strtol(rgbtmp, nullptr, 16));
rgbtmp[2] = rgb[1];
g = static_cast<int>(std::strtol(rgbtmp, nullptr, 16)) * 17;

rgbtmp[0] = rgb[2];
b = static_cast<int>(std::strtol(rgbtmp, nullptr, 16));
rgbtmp[2] = rgb[2];
b = static_cast<int>(std::strtol(rgbtmp, nullptr, 16)) * 17;
}
else
{
rgbtmp[0] = rgb[0];
rgbtmp[1] = rgb[1];
rgbtmp[2] = rgb[0];
rgbtmp[3] = rgb[1];
r = static_cast<int>(std::strtol(rgbtmp, nullptr, 16));

rgbtmp[0] = rgb[2];
rgbtmp[1] = rgb[3];
rgbtmp[2] = rgb[2];
rgbtmp[3] = rgb[3];
g = static_cast<int>(std::strtol(rgbtmp, nullptr, 16));

rgbtmp[0] = rgb[4];
rgbtmp[1] = rgb[5];
rgbtmp[2] = rgb[4];
rgbtmp[3] = rgb[5];
b = static_cast<int>(std::strtol(rgbtmp, nullptr, 16));
}

return Color(r, g, b, static_cast<int>(getFixedRange(alpha)*255.0f));
return Color(r, g, b, alpha);
}

void Color::interpolate(const Color& other, float u) noexcept
{
fixRange(u);
const float oneMinusU(1.0f - u);
const float oneMinusU = 1.0f - u;

red = red * oneMinusU + other.red * u;
green = green * oneMinusU + other.green * u;
blue = blue * oneMinusU + other.blue * u;
alpha = alpha * oneMinusU + other.alpha * u;
red = (red * oneMinusU) + (other.red * u);
green = (green * oneMinusU) + (other.green * u);
blue = (blue * oneMinusU) + (other.blue * u);
alpha = (alpha * oneMinusU) + (other.alpha * u);

fixBounds();
}

// -----------------------------------------------------------------------

bool Color::isEqual(const Color& color, bool withAlpha) noexcept
bool Color::isEqual(const Color& color, const bool withAlpha) noexcept
{
const uchar r1 = getFixedRange2(rgba[0]);
const uchar g1 = getFixedRange2(rgba[1]);
@@ -184,7 +216,7 @@ bool Color::isEqual(const Color& color, bool withAlpha) noexcept
return (r1 == r2 && g1 == g2 && b1 == b2);
}

bool Color::isNotEqual(const Color& color, bool withAlpha) noexcept
bool Color::isNotEqual(const Color& color, const bool withAlpha) noexcept
{
const uchar r1 = getFixedRange2(rgba[0]);
const uchar g1 = getFixedRange2(rgba[1]);
@@ -224,22 +256,4 @@ void Color::fixBounds() noexcept

// -----------------------------------------------------------------------

Color::Color(const NVGcolor& c) noexcept
: red(c.r), green(c.g), blue(c.b), alpha(c.a)
{
fixBounds();
}

Color::operator NVGcolor() const noexcept
{
NVGcolor nc;
nc.r = red;
nc.g = green;
nc.b = blue;
nc.a = alpha;
return nc;
}

// -----------------------------------------------------------------------

END_NAMESPACE_DGL

+ 0
- 124
source/modules/dgl/src/Common.hpp View File

@@ -1,124 +0,0 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef DGL_COMMON_HPP_INCLUDED
#define DGL_COMMON_HPP_INCLUDED

#include "../ImageWidgets.hpp"

START_NAMESPACE_DGL

// -----------------------------------------------------------------------

struct ButtonImpl {
enum State {
kStateNormal = 0,
kStateHover,
kStateDown
};

int button;
int state;
Widget* self;

ImageButton::Callback* callback_img;

ButtonImpl(Widget* const s) noexcept
: button(-1),
state(kStateNormal),
self(s),
callback_img(nullptr) {}

bool onMouse(const Widget::MouseEvent& ev)
{
// button was released, handle it now
if (button != -1 && ! ev.press)
{
DISTRHO_SAFE_ASSERT(state == kStateDown);

// release button
const int button2 = button;
button = -1;

// cursor was moved outside the button bounds, ignore click
if (! self->contains(ev.pos))
{
state = kStateNormal;
self->repaint();
return true;
}

// still on bounds, register click
state = kStateHover;
self->repaint();

if (callback_img != nullptr)
callback_img->imageButtonClicked((ImageButton*)self, button2);

return true;
}

// button was pressed, wait for release
if (ev.press && self->contains(ev.pos))
{
button = ev.button;
state = kStateDown;
self->repaint();
return true;
}

return false;
}

bool onMotion(const Widget::MotionEvent& ev)
{
// keep pressed
if (button != -1)
return true;

if (self->contains(ev.pos))
{
// check if entering hover
if (state == kStateNormal)
{
state = kStateHover;
self->repaint();
return true;
}
}
else
{
// check if exiting hover
if (state == kStateHover)
{
state = kStateNormal;
self->repaint();
return true;
}
}

return false;
}

DISTRHO_PREVENT_HEAP_ALLOCATION
DISTRHO_DECLARE_NON_COPY_STRUCT(ButtonImpl)
};

// -----------------------------------------------------------------------

END_NAMESPACE_DGL

#endif // DGL_APP_PRIVATE_DATA_HPP_INCLUDED

+ 632
- 0
source/modules/dgl/src/EventHandlers.cpp View File

@@ -0,0 +1,632 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "../EventHandlers.hpp"
#include "../SubWidget.hpp"

START_NAMESPACE_DGL

// --------------------------------------------------------------------------------------------------------------------

struct ButtonEventHandler::PrivateData {
ButtonEventHandler* const self;
SubWidget* const widget;
ButtonEventHandler::Callback* internalCallback;
ButtonEventHandler::Callback* userCallback;

int button;
int state;
bool checkable;
bool checked;

Point<double> lastClickPos;
Point<double> lastMotionPos;

PrivateData(ButtonEventHandler* const s, SubWidget* const w)
: self(s),
widget(w),
internalCallback(nullptr),
userCallback(nullptr),
button(-1),
state(kButtonStateDefault),
checkable(false),
checked(false),
lastClickPos(0, 0),
lastMotionPos(0, 0) {}

bool mouseEvent(const Widget::MouseEvent& ev)
{
lastClickPos = ev.pos;

// button was released, handle it now
if (button != -1 && ! ev.press)
{
DISTRHO_SAFE_ASSERT(state & kButtonStateActive);

// release button
const int button2 = button;
button = -1;

const int state2 = state;
state &= ~kButtonStateActive;

self->stateChanged(static_cast<State>(state), static_cast<State>(state2));
widget->repaint();

// cursor was moved outside the button bounds, ignore click
if (! widget->contains(ev.pos))
return true;

// still on bounds, register click
if (checkable)
checked = !checked;

if (internalCallback != nullptr)
internalCallback->buttonClicked(widget, button2);
else if (userCallback != nullptr)
userCallback->buttonClicked(widget, button2);

return true;
}

// button was pressed, wait for release
if (ev.press && widget->contains(ev.pos))
{
const int state2 = state;
button = static_cast<int>(ev.button);
state |= kButtonStateActive;
self->stateChanged(static_cast<State>(state), static_cast<State>(state2));
widget->repaint();
return true;
}

return false;
}

bool motionEvent(const Widget::MotionEvent& ev)
{
// keep pressed
if (button != -1)
{
lastMotionPos = ev.pos;
return true;
}

bool ret = false;

if (widget->contains(ev.pos))
{
// check if entering hover
if ((state & kButtonStateHover) == 0x0)
{
const int state2 = state;
state |= kButtonStateHover;
ret = widget->contains(lastMotionPos);
self->stateChanged(static_cast<State>(state), static_cast<State>(state2));
widget->repaint();
}
}
else
{
// check if exiting hover
if (state & kButtonStateHover)
{
const int state2 = state;
state &= ~kButtonStateHover;
ret = widget->contains(lastMotionPos);
self->stateChanged(static_cast<State>(state), static_cast<State>(state2));
widget->repaint();
}
}

lastMotionPos = ev.pos;
return ret;
}

void setActive(const bool active2, const bool sendCallback) noexcept
{
const bool active = state & kButtonStateActive;
if (active == active2)
return;

state |= kButtonStateActive;
widget->repaint();

if (sendCallback)
{
if (internalCallback != nullptr)
internalCallback->buttonClicked(widget, -1);
else if (userCallback != nullptr)
userCallback->buttonClicked(widget, -1);
}
}

void setChecked(const bool checked2, const bool sendCallback) noexcept
{
if (checked == checked2)
return;

checked = checked2;
widget->repaint();

if (sendCallback)
{
if (internalCallback != nullptr)
internalCallback->buttonClicked(widget, -1);
else if (userCallback != nullptr)
userCallback->buttonClicked(widget, -1);
}
}

DISTRHO_DECLARE_NON_COPYABLE(PrivateData)
};

// --------------------------------------------------------------------------------------------------------------------

ButtonEventHandler::ButtonEventHandler(SubWidget* const self)
: pData(new PrivateData(this, self)) {}

ButtonEventHandler::~ButtonEventHandler()
{
delete pData;
}

bool ButtonEventHandler::isActive() noexcept
{
return pData->state & kButtonStateActive;
}

void ButtonEventHandler::setActive(const bool active, const bool sendCallback) noexcept
{
pData->setActive(active, sendCallback);
}

bool ButtonEventHandler::isChecked() const noexcept
{
return pData->checked;
}

void ButtonEventHandler::setChecked(const bool checked, const bool sendCallback) noexcept
{
pData->setChecked(checked, sendCallback);
}

bool ButtonEventHandler::isCheckable() const noexcept
{
return pData->checkable;
}

void ButtonEventHandler::setCheckable(const bool checkable) noexcept
{
if (pData->checkable == checkable)
return;

pData->checkable = checkable;
}

Point<double> ButtonEventHandler::getLastClickPosition() const noexcept
{
return pData->lastClickPos;
}

Point<double> ButtonEventHandler::getLastMotionPosition() const noexcept
{
return pData->lastMotionPos;
}

void ButtonEventHandler::setCallback(Callback* const callback) noexcept
{
pData->userCallback = callback;
}

bool ButtonEventHandler::mouseEvent(const Widget::MouseEvent& ev)
{
return pData->mouseEvent(ev);
}

bool ButtonEventHandler::motionEvent(const Widget::MotionEvent& ev)
{
return pData->motionEvent(ev);
}

ButtonEventHandler::State ButtonEventHandler::getState() const noexcept
{
return static_cast<State>(pData->state);
}

void ButtonEventHandler::clearState() noexcept
{
pData->state = kButtonStateDefault;
}

void ButtonEventHandler::stateChanged(State, State)
{
}

void ButtonEventHandler::setInternalCallback(Callback* const callback) noexcept
{
pData->internalCallback = callback;
}

void ButtonEventHandler::triggerUserCallback(SubWidget* const widget, const int button)
{
if (pData->userCallback != nullptr)
pData->userCallback->buttonClicked(widget, button);
}

// --------------------------------------------------------------------------------------------------------------------

struct KnobEventHandler::PrivateData {
KnobEventHandler* const self;
SubWidget* const widget;
KnobEventHandler::Callback* callback;

float minimum;
float maximum;
float step;
float value;
float valueDef;
float valueTmp;
bool usingDefault;
bool usingLog;
Orientation orientation;
int state;

double lastX;
double lastY;

PrivateData(KnobEventHandler* const s, SubWidget* const w)
: self(s),
widget(w),
callback(nullptr),
minimum(0.0f),
maximum(1.0f),
step(0.0f),
value(0.5f),
valueDef(value),
valueTmp(value),
usingDefault(false),
usingLog(false),
orientation(Vertical),
state(kKnobStateDefault),
lastX(0.0),
lastY(0.0) {}

PrivateData(KnobEventHandler* const s, SubWidget* const w, PrivateData* const other)
: self(s),
widget(w),
callback(other->callback),
minimum(other->minimum),
maximum(other->maximum),
step(other->step),
value(other->value),
valueDef(other->valueDef),
valueTmp(value),
usingDefault(other->usingDefault),
usingLog(other->usingLog),
orientation(other->orientation),
state(kKnobStateDefault),
lastX(0.0),
lastY(0.0) {}

void assignFrom(PrivateData* const other)
{
callback = other->callback;
minimum = other->minimum;
maximum = other->maximum;
step = other->step;
value = other->value;
valueDef = other->valueDef;
valueTmp = value;
usingDefault = other->usingDefault;
usingLog = other->usingLog;
orientation = other->orientation;
state = kKnobStateDefault;
lastX = 0.0;
lastY = 0.0;
}

inline float logscale(const float v) const
{
const float b = std::log(maximum/minimum)/(maximum-minimum);
const float a = maximum/std::exp(maximum*b);
return a * std::exp(b*v);
}

inline float invlogscale(const float v) const
{
const float b = std::log(maximum/minimum)/(maximum-minimum);
const float a = maximum/std::exp(maximum*b);
return std::log(v/a)/b;
}

bool mouseEvent(const Widget::MouseEvent& ev)
{
if (ev.button != 1)
return false;

if (ev.press)
{
if (! widget->contains(ev.pos))
return false;

if ((ev.mod & kModifierShift) != 0 && usingDefault)
{
setValue(valueDef, true);
valueTmp = value;
return true;
}

state |= kKnobStateDragging;
lastX = ev.pos.getX();
lastY = ev.pos.getY();
widget->repaint();

if (callback != nullptr)
callback->knobDragStarted(widget);

return true;
}
else if (state & kKnobStateDragging)
{
state &= ~kKnobStateDragging;
widget->repaint();

if (callback != nullptr)
callback->knobDragFinished(widget);

return true;
}

return false;
}

bool motionEvent(const Widget::MotionEvent& ev)
{
if ((state & kKnobStateDragging) == 0x0)
return false;

bool doVal = false;
float d, value2 = 0.0f;

if (orientation == Horizontal)
{
if (const double movX = ev.pos.getX() - lastX)
{
d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f;
value2 = (usingLog ? invlogscale(valueTmp) : valueTmp) + (float(maximum - minimum) / d * float(movX));
doVal = true;
}
}
else if (orientation == Vertical)
{
if (const double movY = lastY - ev.pos.getY())
{
d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f;
value2 = (usingLog ? invlogscale(valueTmp) : valueTmp) + (float(maximum - minimum) / d * float(movY));
doVal = true;
}
}

if (! doVal)
return false;

if (usingLog)
value2 = logscale(value2);

if (value2 < minimum)
{
valueTmp = value2 = minimum;
}
else if (value2 > maximum)
{
valueTmp = value2 = maximum;
}
else
{
valueTmp = value2;

if (d_isNotZero(step))
{
const float rest = std::fmod(value2, step);
value2 -= rest + (rest > step/2.0f ? step : 0.0f);
}
}

setValue(value2, true);

lastX = ev.pos.getX();
lastY = ev.pos.getY();

return true;
}

bool scrollEvent(const Widget::ScrollEvent& ev)
{
if (! widget->contains(ev.pos))
return false;

const float dir = (ev.delta.getY() > 0.f) ? 1.f : -1.f;
const float d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f;
float value2 = (usingLog ? invlogscale(valueTmp) : valueTmp)
+ ((maximum - minimum) / d * 10.f * dir);

if (usingLog)
value2 = logscale(value2);

if (value2 < minimum)
{
valueTmp = value2 = minimum;
}
else if (value2 > maximum)
{
valueTmp = value2 = maximum;
}
else
{
valueTmp = value2;

if (d_isNotZero(step))
{
const float rest = std::fmod(value2, step);
value2 = value2 - rest + (rest > step/2.0f ? step : 0.0f);
}
}

setValue(value2, true);
return true;
}

float getNormalizedValue() const noexcept
{
const float diff = maximum - minimum;
return ((usingLog ? invlogscale(value) : value) - minimum) / diff;
}

void setRange(const float min, const float max) noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(max > min,);

if (value < min)
{
valueTmp = value = min;
widget->repaint();
}
else if (value > max)
{
valueTmp = value = max;
widget->repaint();
}

minimum = min;
maximum = max;
}

bool setValue(const float value2, const bool sendCallback)
{
if (d_isEqual(value, value2))
return false;

valueTmp = value = value2;
widget->repaint();

if (sendCallback && callback != nullptr)
{
try {
callback->knobValueChanged(widget, value);
} DISTRHO_SAFE_EXCEPTION("KnobEventHandler::setValue");
}

return true;
}
};

// --------------------------------------------------------------------------------------------------------------------

KnobEventHandler::KnobEventHandler(SubWidget* const self)
: pData(new PrivateData(this, self)) {}

KnobEventHandler::KnobEventHandler(SubWidget* const self, const KnobEventHandler& other)
: pData(new PrivateData(this, self, other.pData)) {}

KnobEventHandler& KnobEventHandler::operator=(const KnobEventHandler& other)
{
pData->assignFrom(other.pData);
return *this;
}

KnobEventHandler::~KnobEventHandler()
{
delete pData;
}

float KnobEventHandler::getValue() const noexcept
{
return pData->value;
}

bool KnobEventHandler::setValue(const float value, const bool sendCallback) noexcept
{
return pData->setValue(value, sendCallback);
}

float KnobEventHandler::getNormalizedValue() const noexcept
{
return pData->getNormalizedValue();
}

void KnobEventHandler::setDefault(const float def) noexcept
{
pData->valueDef = def;
pData->usingDefault = true;
}

void KnobEventHandler::setRange(const float min, const float max) noexcept
{
pData->setRange(min, max);
}

void KnobEventHandler::setStep(const float step) noexcept
{
pData->step = step;
}

void KnobEventHandler::setUsingLogScale(const bool yesNo) noexcept
{
pData->usingLog = yesNo;
}

KnobEventHandler::Orientation KnobEventHandler::getOrientation() const noexcept
{
return pData->orientation;
}

void KnobEventHandler::setOrientation(const Orientation orientation) noexcept
{
if (pData->orientation == orientation)
return;

pData->orientation = orientation;
}

void KnobEventHandler::setCallback(Callback* const callback) noexcept
{
pData->callback = callback;
}

bool KnobEventHandler::mouseEvent(const Widget::MouseEvent& ev)
{
return pData->mouseEvent(ev);
}

bool KnobEventHandler::motionEvent(const Widget::MotionEvent& ev)
{
return pData->motionEvent(ev);
}

bool KnobEventHandler::scrollEvent(const Widget::ScrollEvent& ev)
{
return pData->scrollEvent(ev);
}

KnobEventHandler::State KnobEventHandler::getState() const noexcept
{
return static_cast<State>(pData->state);
}

// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL

+ 241
- 260
source/modules/dgl/src/Geometry.cpp
File diff suppressed because it is too large
View File


+ 0
- 194
source/modules/dgl/src/Image.cpp View File

@@ -1,194 +0,0 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "../Image.hpp"

START_NAMESPACE_DGL

// -----------------------------------------------------------------------

Image::Image()
: fRawData(nullptr),
fSize(0, 0),
fFormat(0),
fType(0),
fTextureId(0),
fIsReady(false)
{
glGenTextures(1, &fTextureId);
}

Image::Image(const char* const rawData, const uint width, const uint height, const GLenum format, const GLenum type)
: fRawData(rawData),
fSize(width, height),
fFormat(format),
fType(type),
fTextureId(0),
fIsReady(false)
{
glGenTextures(1, &fTextureId);
}

Image::Image(const char* const rawData, const Size<uint>& size, const GLenum format, const GLenum type)
: fRawData(rawData),
fSize(size),
fFormat(format),
fType(type),
fTextureId(0),
fIsReady(false)
{
glGenTextures(1, &fTextureId);
}

Image::Image(const Image& image)
: fRawData(image.fRawData),
fSize(image.fSize),
fFormat(image.fFormat),
fType(image.fType),
fTextureId(0),
fIsReady(false)
{
glGenTextures(1, &fTextureId);
}

Image::~Image()
{
if (fTextureId != 0)
{
#ifndef DISTRHO_OS_MAC // FIXME
glDeleteTextures(1, &fTextureId);
#endif
fTextureId = 0;
}
}

void Image::loadFromMemory(const char* const rawData, const uint width, const uint height, const GLenum format, const GLenum type) noexcept
{
loadFromMemory(rawData, Size<uint>(width, height), format, type);
}

void Image::loadFromMemory(const char* const rawData, const Size<uint>& size, const GLenum format, const GLenum type) noexcept
{
fRawData = rawData;
fSize = size;
fFormat = format;
fType = type;
fIsReady = false;
}

bool Image::isValid() const noexcept
{
return (fRawData != nullptr && fSize.getWidth() > 0 && fSize.getHeight() > 0);
}

uint Image::getWidth() const noexcept
{
return fSize.getWidth();
}

uint Image::getHeight() const noexcept
{
return fSize.getHeight();
}

const Size<uint>& Image::getSize() const noexcept
{
return fSize;
}

const char* Image::getRawData() const noexcept
{
return fRawData;
}

GLenum Image::getFormat() const noexcept
{
return fFormat;
}

GLenum Image::getType() const noexcept
{
return fType;
}

void Image::draw()
{
drawAt(0, 0);
}

void Image::drawAt(const int x, const int y)
{
drawAt(Point<int>(x, y));
}

void Image::drawAt(const Point<int>& pos)
{
if (fTextureId == 0 || ! isValid())
return;

glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, fTextureId);

if (! fIsReady)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);

static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans);

glPixelStorei(GL_PACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
static_cast<GLsizei>(fSize.getWidth()), static_cast<GLsizei>(fSize.getHeight()), 0,
fFormat, fType, fRawData);

fIsReady = true;
}

Rectangle<int>(pos, static_cast<int>(fSize.getWidth()), static_cast<int>(fSize.getHeight())).draw();

glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}

// -----------------------------------------------------------------------

Image& Image::operator=(const Image& image) noexcept
{
fRawData = image.fRawData;
fSize = image.fSize;
fFormat = image.fFormat;
fType = image.fType;
fIsReady = false;
return *this;
}

bool Image::operator==(const Image& image) const noexcept
{
return (fRawData == image.fRawData && fSize == image.fSize);
}

bool Image::operator!=(const Image& image) const noexcept
{
return !operator==(image);
}

// -----------------------------------------------------------------------

END_NAMESPACE_DGL

+ 132
- 0
source/modules/dgl/src/ImageBase.cpp View File

@@ -0,0 +1,132 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "../ImageBase.hpp"

START_NAMESPACE_DGL

// --------------------------------------------------------------------------------------------------------------------
// protected constructors

ImageBase::ImageBase()
: rawData(nullptr),
size(0, 0),
format(kImageFormatNull) {}

ImageBase::ImageBase(const char* const rdata, const uint width, const uint height, const ImageFormat fmt)
: rawData(rdata),
size(width, height),
format(fmt) {}

ImageBase::ImageBase(const char* const rdata, const Size<uint>& s, const ImageFormat fmt)
: rawData(rdata),
size(s),
format(fmt) {}

ImageBase::ImageBase(const ImageBase& image)
: rawData(image.rawData),
size(image.size),
format(image.format) {}

// --------------------------------------------------------------------------------------------------------------------
// public methods

ImageBase::~ImageBase() {}

bool ImageBase::isValid() const noexcept
{
return (rawData != nullptr && size.isValid());
}

bool ImageBase::isInvalid() const noexcept
{
return (rawData == nullptr || size.isInvalid());
}

uint ImageBase::getWidth() const noexcept
{
return size.getWidth();
}

uint ImageBase::getHeight() const noexcept
{
return size.getHeight();
}

const Size<uint>& ImageBase::getSize() const noexcept
{
return size;
}

const char* ImageBase::getRawData() const noexcept
{
return rawData;
}

ImageFormat ImageBase::getFormat() const noexcept
{
return format;
}

void ImageBase::loadFromMemory(const char* const rdata,
const uint width,
const uint height,
const ImageFormat fmt) noexcept
{
loadFromMemory(rdata, Size<uint>(width, height), fmt);
}

void ImageBase::loadFromMemory(const char* const rdata, const Size<uint>& s, const ImageFormat fmt) noexcept
{
rawData = rdata;
size = s;
format = fmt;
}

void ImageBase::draw(const GraphicsContext& context)
{
drawAt(context, Point<int>(0, 0));
}

void ImageBase::drawAt(const GraphicsContext& context, const int x, const int y)
{
drawAt(context, Point<int>(x, y));
}

// --------------------------------------------------------------------------------------------------------------------
// public operators

ImageBase& ImageBase::operator=(const ImageBase& image) noexcept
{
rawData = image.rawData;
size = image.size;
format = image.format;
return *this;
}

bool ImageBase::operator==(const ImageBase& image) const noexcept
{
return (rawData == image.rawData && size == image.size && format == image.format);
}

bool ImageBase::operator!=(const ImageBase& image) const noexcept
{
return !operator==(image);
}

// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL

+ 928
- 0
source/modules/dgl/src/ImageBaseWidgets.cpp View File

@@ -0,0 +1,928 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "../ImageBaseWidgets.hpp"
#include "../Color.hpp"

START_NAMESPACE_DGL

// --------------------------------------------------------------------------------------------------------------------

template <class ImageType>
ImageBaseAboutWindow<ImageType>::ImageBaseAboutWindow(Window& transientParentWindow, const ImageType& image)
: StandaloneWindow(transientParentWindow.getApp(), transientParentWindow),
img(image)
{
setResizable(false);
setTitle("About");

if (image.isValid())
{
setSize(image.getSize());
setGeometryConstraints(image.getWidth(), image.getHeight(), true, true);
}

done();
}

template <class ImageType>
ImageBaseAboutWindow<ImageType>::ImageBaseAboutWindow(TopLevelWidget* const topLevelWidget, const ImageType& image)
: StandaloneWindow(topLevelWidget->getApp(), topLevelWidget->getWindow()),
img(image)
{
setResizable(false);
setTitle("About");

if (image.isValid())
{
setSize(image.getSize());
setGeometryConstraints(image.getWidth(), image.getHeight(), true, true);
}

done();
}

template <class ImageType>
void ImageBaseAboutWindow<ImageType>::setImage(const ImageType& image)
{
if (img == image)
return;

img = image;

if (image.isInvalid())
return;

setSize(image.getSize());
setGeometryConstraints(image.getWidth(), image.getHeight(), true, true);
}

template <class ImageType>
void ImageBaseAboutWindow<ImageType>::onDisplay()
{
img.draw(getGraphicsContext());
}

template <class ImageType>
bool ImageBaseAboutWindow<ImageType>::onKeyboard(const KeyboardEvent& ev)
{
if (ev.press && ev.key == kKeyEscape)
{
close();
return true;
}

return false;
}

template <class ImageType>
bool ImageBaseAboutWindow<ImageType>::onMouse(const MouseEvent& ev)
{
if (ev.press)
{
close();
return true;
}

return false;
}

// --------------------------------------------------------------------------------------------------------------------

template <class ImageType>
struct ImageBaseButton<ImageType>::PrivateData : public ButtonEventHandler::Callback {
ImageBaseButton<ImageType>::Callback* callback;
ImageType imageNormal;
ImageType imageHover;
ImageType imageDown;

PrivateData(const ImageType& normal, const ImageType& hover, const ImageType& down)
: callback(nullptr),
imageNormal(normal),
imageHover(hover),
imageDown(down) {}

void buttonClicked(SubWidget* widget, int button) override
{
if (callback != nullptr)
if (ImageBaseButton* const imageButton = dynamic_cast<ImageBaseButton*>(widget))
callback->imageButtonClicked(imageButton, button);
}

DISTRHO_DECLARE_NON_COPYABLE(PrivateData)
};

// --------------------------------------------------------------------------------------------------------------------

template <class ImageType>
ImageBaseButton<ImageType>::ImageBaseButton(Widget* const parentWidget, const ImageType& image)
: SubWidget(parentWidget),
ButtonEventHandler(this),
pData(new PrivateData(image, image, image))
{
ButtonEventHandler::setCallback(pData);
setSize(image.getSize());
}

template <class ImageType>
ImageBaseButton<ImageType>::ImageBaseButton(Widget* const parentWidget, const ImageType& imageNormal, const ImageType& imageDown)
: SubWidget(parentWidget),
ButtonEventHandler(this),
pData(new PrivateData(imageNormal, imageNormal, imageDown))
{
DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize());

ButtonEventHandler::setCallback(pData);
setSize(imageNormal.getSize());
}

template <class ImageType>
ImageBaseButton<ImageType>::ImageBaseButton(Widget* const parentWidget, const ImageType& imageNormal, const ImageType& imageHover, const ImageType& imageDown)
: SubWidget(parentWidget),
ButtonEventHandler(this),
pData(new PrivateData(imageNormal, imageHover, imageDown))
{
DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageHover.getSize() && imageHover.getSize() == imageDown.getSize());

ButtonEventHandler::setCallback(pData);
setSize(imageNormal.getSize());
}

template <class ImageType>
ImageBaseButton<ImageType>::~ImageBaseButton()
{
delete pData;
}

template <class ImageType>
void ImageBaseButton<ImageType>::setCallback(Callback* callback) noexcept
{
pData->callback = callback;
}

template <class ImageType>
void ImageBaseButton<ImageType>::onDisplay()
{
const GraphicsContext& context(getGraphicsContext());

const State state = ButtonEventHandler::getState();

if (state & kButtonStateActive)
pData->imageDown.draw(context);
else if (state & kButtonStateHover)
pData->imageHover.draw(context);
else
pData->imageNormal.draw(context);
}

template <class ImageType>
bool ImageBaseButton<ImageType>::onMouse(const MouseEvent& ev)
{
if (SubWidget::onMouse(ev))
return true;
return ButtonEventHandler::mouseEvent(ev);
}

template <class ImageType>
bool ImageBaseButton<ImageType>::onMotion(const MotionEvent& ev)
{
if (SubWidget::onMotion(ev))
return true;
return ButtonEventHandler::motionEvent(ev);
}

// --------------------------------------------------------------------------------------------------------------------

template <class ImageType>
struct ImageBaseKnob<ImageType>::PrivateData : public KnobEventHandler::Callback {
ImageBaseKnob<ImageType>::Callback* callback;
ImageType image;

int rotationAngle;

bool alwaysRepaint;
bool isImgVertical;
uint imgLayerWidth;
uint imgLayerHeight;
uint imgLayerCount;
bool isReady;

union {
uint glTextureId;
void* cairoSurface;
};

explicit PrivateData(const ImageType& img)
: callback(nullptr),
image(img),
rotationAngle(0),
alwaysRepaint(false),
isImgVertical(img.getHeight() > img.getWidth()),
imgLayerWidth(isImgVertical ? img.getWidth() : img.getHeight()),
imgLayerHeight(imgLayerWidth),
imgLayerCount(isImgVertical ? img.getHeight()/imgLayerHeight : img.getWidth()/imgLayerWidth),
isReady(false)
{
init();
}

explicit PrivateData(PrivateData* const other)
: callback(other->callback),
image(other->image),
rotationAngle(other->rotationAngle),
alwaysRepaint(other->alwaysRepaint),
isImgVertical(other->isImgVertical),
imgLayerWidth(other->imgLayerWidth),
imgLayerHeight(other->imgLayerHeight),
imgLayerCount(other->imgLayerCount),
isReady(false)
{
init();
}

void assignFrom(PrivateData* const other)
{
cleanup();
image = other->image;
rotationAngle = other->rotationAngle;
callback = other->callback;
alwaysRepaint = other->alwaysRepaint;
isImgVertical = other->isImgVertical;
imgLayerWidth = other->imgLayerWidth;
imgLayerHeight = other->imgLayerHeight;
imgLayerCount = other->imgLayerCount;
isReady = false;
init();
}

~PrivateData()
{
cleanup();
}

void knobDragStarted(SubWidget* const widget) override
{
if (callback != nullptr)
if (ImageBaseKnob* const imageKnob = dynamic_cast<ImageBaseKnob*>(widget))
callback->imageKnobDragStarted(imageKnob);
}

void knobDragFinished(SubWidget* const widget) override
{
if (callback != nullptr)
if (ImageBaseKnob* const imageKnob = dynamic_cast<ImageBaseKnob*>(widget))
callback->imageKnobDragFinished(imageKnob);
}

void knobValueChanged(SubWidget* const widget, const float value) override
{
if (rotationAngle == 0 || alwaysRepaint)
isReady = false;

if (callback != nullptr)
if (ImageBaseKnob* const imageKnob = dynamic_cast<ImageBaseKnob*>(widget))
callback->imageKnobValueChanged(imageKnob, value);
}

// implemented independently per graphics backend
void init();
void cleanup();

DISTRHO_DECLARE_NON_COPYABLE(PrivateData)
};

// --------------------------------------------------------------------------------------------------------------------

template <class ImageType>
ImageBaseKnob<ImageType>::ImageBaseKnob(Widget* const parentWidget,
const ImageType& image,
const Orientation orientation) noexcept
: SubWidget(parentWidget),
KnobEventHandler(this),
pData(new PrivateData(image))
{
KnobEventHandler::setCallback(pData);
setOrientation(orientation);
setSize(pData->imgLayerWidth, pData->imgLayerHeight);
}

template <class ImageType>
ImageBaseKnob<ImageType>::ImageBaseKnob(const ImageBaseKnob<ImageType>& imageKnob)
: SubWidget(imageKnob.getParentWidget()),
KnobEventHandler(this, imageKnob),
pData(new PrivateData(imageKnob.pData))
{
KnobEventHandler::setCallback(pData);
setOrientation(imageKnob.getOrientation());
setSize(pData->imgLayerWidth, pData->imgLayerHeight);
}

template <class ImageType>
ImageBaseKnob<ImageType>& ImageBaseKnob<ImageType>::operator=(const ImageBaseKnob<ImageType>& imageKnob)
{
KnobEventHandler::operator=(imageKnob);
pData->assignFrom(imageKnob.pData);
setSize(pData->imgLayerWidth, pData->imgLayerHeight);
return *this;
}

template <class ImageType>
ImageBaseKnob<ImageType>::~ImageBaseKnob()
{
delete pData;
}

template <class ImageType>
void ImageBaseKnob<ImageType>::setCallback(Callback* callback) noexcept
{
pData->callback = callback;
}

template <class ImageType>
void ImageBaseKnob<ImageType>::setImageLayerCount(uint count) noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(count > 1,);

pData->imgLayerCount = count;

if (pData->isImgVertical)
pData->imgLayerHeight = pData->image.getHeight()/count;
else
pData->imgLayerWidth = pData->image.getWidth()/count;

setSize(pData->imgLayerWidth, pData->imgLayerHeight);
}

template <class ImageType>
void ImageBaseKnob<ImageType>::setRotationAngle(int angle)
{
if (pData->rotationAngle == angle)
return;

pData->rotationAngle = angle;
pData->isReady = false;
}

template <class ImageType>
bool ImageBaseKnob<ImageType>::setValue(float value, bool sendCallback) noexcept
{
if (KnobEventHandler::setValue(value, sendCallback))
{
if (pData->rotationAngle == 0 || pData->alwaysRepaint)
pData->isReady = false;

return true;
}

return false;
}

template <class ImageType>
bool ImageBaseKnob<ImageType>::onMouse(const MouseEvent& ev)
{
if (SubWidget::onMouse(ev))
return true;
return KnobEventHandler::mouseEvent(ev);
}

template <class ImageType>
bool ImageBaseKnob<ImageType>::onMotion(const MotionEvent& ev)
{
if (SubWidget::onMotion(ev))
return true;
return KnobEventHandler::motionEvent(ev);
}

template <class ImageType>
bool ImageBaseKnob<ImageType>::onScroll(const ScrollEvent& ev)
{
if (SubWidget::onScroll(ev))
return true;
return KnobEventHandler::scrollEvent(ev);
}

// --------------------------------------------------------------------------------------------------------------------

template <class ImageType>
struct ImageBaseSlider<ImageType>::PrivateData {
ImageType image;
float minimum;
float maximum;
float step;
float value;
float valueDef;
float valueTmp;
bool usingDefault;

bool dragging;
bool inverted;
bool valueIsSet;
double startedX;
double startedY;

Callback* callback;

Point<int> startPos;
Point<int> endPos;
Rectangle<double> sliderArea;

PrivateData(const ImageType& img)
: image(img),
minimum(0.0f),
maximum(1.0f),
step(0.0f),
value(0.5f),
valueDef(value),
valueTmp(value),
usingDefault(false),
dragging(false),
inverted(false),
valueIsSet(false),
startedX(0.0),
startedY(0.0),
callback(nullptr),
startPos(),
endPos(),
sliderArea() {}

void recheckArea() noexcept
{
if (startPos.getY() == endPos.getY())
{
// horizontal
sliderArea = Rectangle<double>(startPos.getX(),
startPos.getY(),
endPos.getX() + static_cast<int>(image.getWidth()) - startPos.getX(),
static_cast<int>(image.getHeight()));
}
else
{
// vertical
sliderArea = Rectangle<double>(startPos.getX(),
startPos.getY(),
static_cast<int>(image.getWidth()),
endPos.getY() + static_cast<int>(image.getHeight()) - startPos.getY());
}
}

DISTRHO_DECLARE_NON_COPYABLE(PrivateData)
};

// --------------------------------------------------------------------------------------------------------------------

template <class ImageType>
ImageBaseSlider<ImageType>::ImageBaseSlider(Widget* const parentWidget, const ImageType& image) noexcept
: SubWidget(parentWidget),
pData(new PrivateData(image))
{
setNeedsFullViewportDrawing();
}

template <class ImageType>
ImageBaseSlider<ImageType>::~ImageBaseSlider()
{
delete pData;
}

template <class ImageType>
float ImageBaseSlider<ImageType>::getValue() const noexcept
{
return pData->value;
}

template <class ImageType>
void ImageBaseSlider<ImageType>::setValue(float value, bool sendCallback) noexcept
{
if (! pData->valueIsSet)
pData->valueIsSet = true;

if (d_isEqual(pData->value, value))
return;

pData->value = value;

if (d_isZero(pData->step))
pData->valueTmp = value;

repaint();

if (sendCallback && pData->callback != nullptr)
{
try {
pData->callback->imageSliderValueChanged(this, pData->value);
} DISTRHO_SAFE_EXCEPTION("ImageBaseSlider::setValue");
}
}

template <class ImageType>
void ImageBaseSlider<ImageType>::setStartPos(const Point<int>& startPos) noexcept
{
pData->startPos = startPos;
pData->recheckArea();
}

template <class ImageType>
void ImageBaseSlider<ImageType>::setStartPos(int x, int y) noexcept
{
setStartPos(Point<int>(x, y));
}

template <class ImageType>
void ImageBaseSlider<ImageType>::setEndPos(const Point<int>& endPos) noexcept
{
pData->endPos = endPos;
pData->recheckArea();
}

template <class ImageType>
void ImageBaseSlider<ImageType>::setEndPos(int x, int y) noexcept
{
setEndPos(Point<int>(x, y));
}

template <class ImageType>
void ImageBaseSlider<ImageType>::setInverted(bool inverted) noexcept
{
if (pData->inverted == inverted)
return;

pData->inverted = inverted;
repaint();
}

template <class ImageType>
void ImageBaseSlider<ImageType>::setDefault(float value) noexcept
{
pData->valueDef = value;
pData->usingDefault = true;
}

template <class ImageType>
void ImageBaseSlider<ImageType>::setRange(float min, float max) noexcept
{
pData->minimum = min;
pData->maximum = max;

if (pData->value < min)
{
pData->value = min;
repaint();

if (pData->callback != nullptr && pData->valueIsSet)
{
try {
pData->callback->imageSliderValueChanged(this, pData->value);
} DISTRHO_SAFE_EXCEPTION("ImageBaseSlider::setRange < min");
}
}
else if (pData->value > max)
{
pData->value = max;
repaint();

if (pData->callback != nullptr && pData->valueIsSet)
{
try {
pData->callback->imageSliderValueChanged(this, pData->value);
} DISTRHO_SAFE_EXCEPTION("ImageBaseSlider::setRange > max");
}
}
}

template <class ImageType>
void ImageBaseSlider<ImageType>::setStep(float step) noexcept
{
pData->step = step;
}

template <class ImageType>
void ImageBaseSlider<ImageType>::setCallback(Callback* callback) noexcept
{
pData->callback = callback;
}

template <class ImageType>
void ImageBaseSlider<ImageType>::onDisplay()
{
const GraphicsContext& context(getGraphicsContext());

#if 0 // DEBUG, paints slider area
Color(1.0f, 1.0f, 1.0f, 0.5f).setFor(context, true);
Rectangle<int>(pData->sliderArea.getX(),
pData->sliderArea.getY(),
pData->sliderArea.getX()+pData->sliderArea.getWidth(),
pData->sliderArea.getY()+pData->sliderArea.getHeight()).draw(context);
Color(1.0f, 1.0f, 1.0f, 1.0f).setFor(context, true);
#endif

const float normValue = (pData->value - pData->minimum) / (pData->maximum - pData->minimum);

int x, y;

if (pData->startPos.getY() == pData->endPos.getY())
{
// horizontal
if (pData->inverted)
x = pData->endPos.getX() - static_cast<int>(normValue*static_cast<float>(pData->endPos.getX()-pData->startPos.getX()));
else
x = pData->startPos.getX() + static_cast<int>(normValue*static_cast<float>(pData->endPos.getX()-pData->startPos.getX()));

y = pData->startPos.getY();
}
else
{
// vertical
x = pData->startPos.getX();

if (pData->inverted)
y = pData->endPos.getY() - static_cast<int>(normValue*static_cast<float>(pData->endPos.getY()-pData->startPos.getY()));
else
y = pData->startPos.getY() + static_cast<int>(normValue*static_cast<float>(pData->endPos.getY()-pData->startPos.getY()));
}

pData->image.drawAt(context, x, y);
}

template <class ImageType>
bool ImageBaseSlider<ImageType>::onMouse(const MouseEvent& ev)
{
if (ev.button != 1)
return false;

if (ev.press)
{
if (! pData->sliderArea.contains(ev.pos))
return false;

if ((ev.mod & kModifierShift) != 0 && pData->usingDefault)
{
setValue(pData->valueDef, true);
pData->valueTmp = pData->value;
return true;
}

float vper;
const double x = ev.pos.getX();
const double y = ev.pos.getY();

if (pData->startPos.getY() == pData->endPos.getY())
{
// horizontal
vper = float(x - pData->sliderArea.getX()) / float(pData->sliderArea.getWidth());
}
else
{
// vertical
vper = float(y - pData->sliderArea.getY()) / float(pData->sliderArea.getHeight());
}

float value;

if (pData->inverted)
value = pData->maximum - vper * (pData->maximum - pData->minimum);
else
value = pData->minimum + vper * (pData->maximum - pData->minimum);

if (value < pData->minimum)
{
pData->valueTmp = value = pData->minimum;
}
else if (value > pData->maximum)
{
pData->valueTmp = value = pData->maximum;
}
else if (d_isNotZero(pData->step))
{
pData->valueTmp = value;
const float rest = std::fmod(value, pData->step);
value = value - rest + (rest > pData->step/2.0f ? pData->step : 0.0f);
}

pData->dragging = true;
pData->startedX = x;
pData->startedY = y;

if (pData->callback != nullptr)
pData->callback->imageSliderDragStarted(this);

setValue(value, true);

return true;
}
else if (pData->dragging)
{
if (pData->callback != nullptr)
pData->callback->imageSliderDragFinished(this);

pData->dragging = false;
return true;
}

return false;
}

template <class ImageType>
bool ImageBaseSlider<ImageType>::onMotion(const MotionEvent& ev)
{
if (! pData->dragging)
return false;

const bool horizontal = pData->startPos.getY() == pData->endPos.getY();
const double x = ev.pos.getX();
const double y = ev.pos.getY();

if ((horizontal && pData->sliderArea.containsX(x)) || (pData->sliderArea.containsY(y) && ! horizontal))
{
float vper;

if (horizontal)
{
// horizontal
vper = float(x - pData->sliderArea.getX()) / float(pData->sliderArea.getWidth());
}
else
{
// vertical
vper = float(y - pData->sliderArea.getY()) / float(pData->sliderArea.getHeight());
}

float value;

if (pData->inverted)
value = pData->maximum - vper * (pData->maximum - pData->minimum);
else
value = pData->minimum + vper * (pData->maximum - pData->minimum);

if (value < pData->minimum)
{
pData->valueTmp = value = pData->minimum;
}
else if (value > pData->maximum)
{
pData->valueTmp = value = pData->maximum;
}
else if (d_isNotZero(pData->step))
{
pData->valueTmp = value;
const float rest = std::fmod(value, pData->step);
value = value - rest + (rest > pData->step/2.0f ? pData->step : 0.0f);
}

setValue(value, true);
}
else if (horizontal)
{
if (x < pData->sliderArea.getX())
setValue(pData->inverted ? pData->maximum : pData->minimum, true);
else
setValue(pData->inverted ? pData->minimum : pData->maximum, true);
}
else
{
if (y < pData->sliderArea.getY())
setValue(pData->inverted ? pData->maximum : pData->minimum, true);
else
setValue(pData->inverted ? pData->minimum : pData->maximum, true);
}

return true;
}

// --------------------------------------------------------------------------------------------------------------------

template <class ImageType>
struct ImageBaseSwitch<ImageType>::PrivateData {
ImageType imageNormal;
ImageType imageDown;
bool isDown;
Callback* callback;

PrivateData(const ImageType& normal, const ImageType& down)
: imageNormal(normal),
imageDown(down),
isDown(false),
callback(nullptr)
{
DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize());
}

PrivateData(PrivateData* const other)
: imageNormal(other->imageNormal),
imageDown(other->imageDown),
isDown(other->isDown),
callback(other->callback)
{
DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize());
}

void assignFrom(PrivateData* const other)
{
imageNormal = other->imageNormal;
imageDown = other->imageDown;
isDown = other->isDown;
callback = other->callback;
DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize());
}

DISTRHO_DECLARE_NON_COPYABLE(PrivateData)
};

// --------------------------------------------------------------------------------------------------------------------

template <class ImageType>
ImageBaseSwitch<ImageType>::ImageBaseSwitch(Widget* const parentWidget, const ImageType& imageNormal, const ImageType& imageDown) noexcept
: SubWidget(parentWidget),
pData(new PrivateData(imageNormal, imageDown))
{
setSize(imageNormal.getSize());
}

template <class ImageType>
ImageBaseSwitch<ImageType>::ImageBaseSwitch(const ImageBaseSwitch<ImageType>& imageSwitch) noexcept
: SubWidget(imageSwitch.getParentWidget()),
pData(new PrivateData(imageSwitch.pData))
{
setSize(pData->imageNormal.getSize());
}

template <class ImageType>
ImageBaseSwitch<ImageType>& ImageBaseSwitch<ImageType>::operator=(const ImageBaseSwitch<ImageType>& imageSwitch) noexcept
{
pData->assignFrom(imageSwitch.pData);
setSize(pData->imageNormal.getSize());
return *this;
}

template <class ImageType>
ImageBaseSwitch<ImageType>::~ImageBaseSwitch()
{
delete pData;
}

template <class ImageType>
bool ImageBaseSwitch<ImageType>::isDown() const noexcept
{
return pData->isDown;
}

template <class ImageType>
void ImageBaseSwitch<ImageType>::setDown(const bool down) noexcept
{
if (pData->isDown == down)
return;

pData->isDown = down;
repaint();
}

template <class ImageType>
void ImageBaseSwitch<ImageType>::setCallback(Callback* const callback) noexcept
{
pData->callback = callback;
}

template <class ImageType>
void ImageBaseSwitch<ImageType>::onDisplay()
{
const GraphicsContext& context(getGraphicsContext());

if (pData->isDown)
pData->imageDown.draw(context);
else
pData->imageNormal.draw(context);
}

template <class ImageType>
bool ImageBaseSwitch<ImageType>::onMouse(const MouseEvent& ev)
{
if (ev.press && contains(ev.pos))
{
pData->isDown = !pData->isDown;

repaint();

if (pData->callback != nullptr)
pData->callback->imageSwitchClicked(this, pData->isDown);

return true;
}

return false;
}

// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL

+ 0
- 1077
source/modules/dgl/src/ImageWidgets.cpp
File diff suppressed because it is too large
View File


+ 202
- 89
source/modules/dgl/src/NanoVG.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -15,7 +15,7 @@
*/

#include "../NanoVG.hpp"
#include "WidgetPrivateData.hpp"
#include "SubWidgetPrivateData.hpp"

#ifndef DGL_NO_SHARED_RESOURCES
# include "Resources.hpp"
@@ -53,6 +53,28 @@ DGL_EXT(PFNGLUNIFORM2FVPROC, glUniform2fv)
DGL_EXT(PFNGLUNIFORM4FVPROC, glUniform4fv)
DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram)
DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer)
DGL_EXT(PFNGLBLENDFUNCSEPARATEPROC, glBlendFuncSeparate)
# ifdef DGL_USE_NANOVG_FBO
DGL_EXT(PFNGLBINDFRAMEBUFFERPROC, glBindFramebuffer)
DGL_EXT(PFNGLBINDRENDERBUFFERPROC, glBindRenderbuffer)
DGL_EXT(PFNGLCHECKFRAMEBUFFERSTATUSPROC, glCheckFramebufferStatus)
DGL_EXT(PFNGLDELETEFRAMEBUFFERSPROC, glDeleteFramebuffers)
DGL_EXT(PFNGLDELETERENDERBUFFERSPROC, glDeleteRenderbuffers)
DGL_EXT(PFNGLFRAMEBUFFERTEXTURE2DPROC, glFramebufferTexture2D)
DGL_EXT(PFNGLFRAMEBUFFERRENDERBUFFERPROC, glFramebufferRenderbuffer)
DGL_EXT(PFNGLGENFRAMEBUFFERSPROC, glGenFramebuffers)
DGL_EXT(PFNGLGENRENDERBUFFERSPROC, glGenRenderbuffers)
DGL_EXT(PFNGLRENDERBUFFERSTORAGEPROC, glRenderbufferStorage)
# endif
# ifdef DGL_USE_OPENGL3
DGL_EXT(PFNGLBINDBUFFERRANGEPROC, glBindBufferRange)
DGL_EXT(PFNGLBINDVERTEXARRAYPROC, glBindVertexArray)
DGL_EXT(PFNGLDELETEVERTEXARRAYSPROC, glDeleteVertexArrays)
DGL_EXT(PFNGLGENERATEMIPMAPPROC, glGenerateMipmap)
DGL_EXT(PFNGLGETUNIFORMBLOCKINDEXPROC, glGetUniformBlockIndex)
DGL_EXT(PFNGLGENVERTEXARRAYSPROC, glGenVertexArrays)
DGL_EXT(PFNGLUNIFORMBLOCKBINDINGPROC, glUniformBlockBinding)
# endif
# undef DGL_EXT
#endif

@@ -60,32 +82,61 @@ DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer)
// Include NanoVG OpenGL implementation

//#define STB_IMAGE_STATIC
#define NANOVG_GL2_IMPLEMENTATION
#ifdef DGL_USE_OPENGL3
# define NANOVG_GL3_IMPLEMENTATION
#else
# define NANOVG_GL2_IMPLEMENTATION
#endif

#if defined(DISTRHO_OS_MAC) && defined(NANOVG_GL2_IMPLEMENTATION)
# define glBindVertexArray glBindVertexArrayAPPLE
# define glDeleteVertexArrays glDeleteVertexArraysAPPLE
# define glGenVertexArrays glGenVertexArraysAPPLE
#endif

#include "nanovg/nanovg_gl.h"

#ifdef DGL_USE_NANOVG_FBO
# define NANOVG_FBO_VALID 1
# include "nanovg/nanovg_gl_utils.h"
#endif

#if defined(NANOVG_GL2)
# define nvgCreateGL nvgCreateGL2
# define nvgCreateGLfn nvgCreateGL2
# define nvgDeleteGL nvgDeleteGL2
# define nvglCreateImageFromHandle nvglCreateImageFromHandleGL2
# define nvglImageHandle nvglImageHandleGL2
#elif defined(NANOVG_GL3)
# define nvgCreateGL nvgCreateGL3
# define nvgCreateGLfn nvgCreateGL3
# define nvgDeleteGL nvgDeleteGL3
# define nvglCreateImageFromHandle nvglCreateImageFromHandleGL3
# define nvglImageHandle nvglImageHandleGL3
#elif defined(NANOVG_GLES2)
# define nvgCreateGL nvgCreateGLES2
# define nvgCreateGLfn nvgCreateGLES2
# define nvgDeleteGL nvgDeleteGLES2
# define nvglCreateImageFromHandle nvglCreateImageFromHandleGLES2
# define nvglImageHandle nvglImageHandleGLES2
#elif defined(NANOVG_GLES3)
# define nvgCreateGL nvgCreateGLES3
# define nvgCreateGLfn nvgCreateGLES3
# define nvgDeleteGL nvgDeleteGLES3
# define nvglCreateImageFromHandle nvglCreateImageFromHandleGLES3
# define nvglImageHandle nvglImageHandleGLES3
#endif

static NVGcontext* nvgCreateGL_helper(int flags)
// -----------------------------------------------------------------------

START_NAMESPACE_DGL

NVGcontext* nvgCreateGL(int flags)
{
#if defined(DISTRHO_OS_WINDOWS)
# if defined(__GNUC__) && (__GNUC__ >= 9)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wcast-function-type"
# endif
static bool needsInit = true;
if (needsInit)
{
needsInit = false;
# define DGL_EXT(PROC, func) \
func = (PROC) wglGetProcAddress ( #func ); \
if (needsInit) func = (PROC) wglGetProcAddress ( #func ); \
DISTRHO_SAFE_ASSERT_RETURN(func != nullptr, nullptr);
DGL_EXT(PFNGLACTIVETEXTUREPROC, glActiveTexture)
DGL_EXT(PFNGLATTACHSHADERPROC, glAttachShader)
@@ -114,15 +165,55 @@ DGL_EXT(PFNGLUNIFORM2FVPROC, glUniform2fv)
DGL_EXT(PFNGLUNIFORM4FVPROC, glUniform4fv)
DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram)
DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer)
DGL_EXT(PFNGLBLENDFUNCSEPARATEPROC, glBlendFuncSeparate)
# ifdef DGL_USE_NANOVG_FBO
DGL_EXT(PFNGLBINDFRAMEBUFFERPROC, glBindFramebuffer)
DGL_EXT(PFNGLBINDRENDERBUFFERPROC, glBindRenderbuffer)
DGL_EXT(PFNGLCHECKFRAMEBUFFERSTATUSPROC, glCheckFramebufferStatus)
DGL_EXT(PFNGLDELETEFRAMEBUFFERSPROC, glDeleteFramebuffers)
DGL_EXT(PFNGLDELETERENDERBUFFERSPROC, glDeleteRenderbuffers)
DGL_EXT(PFNGLFRAMEBUFFERTEXTURE2DPROC, glFramebufferTexture2D)
DGL_EXT(PFNGLFRAMEBUFFERRENDERBUFFERPROC, glFramebufferRenderbuffer)
DGL_EXT(PFNGLGENFRAMEBUFFERSPROC, glGenFramebuffers)
DGL_EXT(PFNGLGENRENDERBUFFERSPROC, glGenRenderbuffers)
DGL_EXT(PFNGLRENDERBUFFERSTORAGEPROC, glRenderbufferStorage)
# endif
# ifdef DGL_USE_OPENGL3
DGL_EXT(PFNGLBINDBUFFERRANGEPROC, glBindBufferRange)
DGL_EXT(PFNGLBINDVERTEXARRAYPROC, glBindVertexArray)
DGL_EXT(PFNGLDELETEVERTEXARRAYSPROC, glDeleteVertexArrays)
DGL_EXT(PFNGLGENERATEMIPMAPPROC, glGenerateMipmap)
DGL_EXT(PFNGLGETUNIFORMBLOCKINDEXPROC, glGetUniformBlockIndex)
DGL_EXT(PFNGLGENVERTEXARRAYSPROC, glGenVertexArrays)
DGL_EXT(PFNGLUNIFORMBLOCKBINDINGPROC, glUniformBlockBinding)
# endif
# undef DGL_EXT
}
needsInit = false;
# if defined(__GNUC__) && (__GNUC__ >= 9)
# pragma GCC diagnostic pop
# endif
#endif
return nvgCreateGL(flags);
return nvgCreateGLfn(flags);
}

// -----------------------------------------------------------------------
// DGL Color class conversion

START_NAMESPACE_DGL
Color::Color(const NVGcolor& c) noexcept
: red(c.r), green(c.g), blue(c.b), alpha(c.a)
{
fixBounds();
}

Color::operator NVGcolor() const noexcept
{
NVGcolor nc;
nc.r = red;
nc.g = green;
nc.b = blue;
nc.a = alpha;
return nc;
}

// -----------------------------------------------------------------------
// NanoImage
@@ -153,6 +244,7 @@ NanoImage& NanoImage::operator=(const Handle& handle)

fHandle.context = handle.context;
fHandle.imageId = handle.imageId;
_updateSize();

return *this;
}
@@ -220,15 +312,10 @@ NanoVG::Paint::operator NVGpaint() const noexcept
// NanoVG

NanoVG::NanoVG(int flags)
: fContext(nvgCreateGL_helper(flags)),
: fContext(nvgCreateGL(flags)),
fInFrame(false),
fIsSubWidget(false) {}

NanoVG::NanoVG(NanoWidget* groupWidget)
: fContext(groupWidget->fContext),
fInFrame(false),
fIsSubWidget(true) {}

NanoVG::~NanoVG()
{
DISTRHO_SAFE_ASSERT(! fInFrame);
@@ -241,26 +328,28 @@ NanoVG::~NanoVG()

void NanoVG::beginFrame(const uint width, const uint height, const float scaleFactor)
{
if (fContext == nullptr) return;
DISTRHO_SAFE_ASSERT_RETURN(scaleFactor > 0.0f,);
DISTRHO_SAFE_ASSERT_RETURN(! fInFrame,);

fInFrame = true;
nvgBeginFrame(fContext, static_cast<int>(width), static_cast<int>(height), scaleFactor);

if (fContext != nullptr)
nvgBeginFrame(fContext, static_cast<int>(width), static_cast<int>(height), scaleFactor);
}

void NanoVG::beginFrame(Widget* const widget)
{
DISTRHO_SAFE_ASSERT_RETURN(widget != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(! fInFrame,);

fInFrame = true;

if (fContext == nullptr)
return;

Window& window(widget->getParentWindow());
nvgBeginFrame(fContext, static_cast<int>(window.getWidth()), static_cast<int>(window.getHeight()), 1.0f);
if (TopLevelWidget* const tlw = widget->getTopLevelWidget())
nvgBeginFrame(fContext,
static_cast<int>(tlw->getWidth()),
static_cast<int>(tlw->getHeight()),
tlw->getScaleFactor());
}

void NanoVG::cancelFrame()
@@ -424,6 +513,12 @@ void NanoVG::globalAlpha(float alpha)
nvgGlobalAlpha(fContext, alpha);
}

void NanoVG::globalTint(Color tint)
{
if (fContext != nullptr)
nvgGlobalTint(fContext, tint);
}

// -----------------------------------------------------------------------
// Transforms

@@ -447,10 +542,8 @@ void NanoVG::translate(float x, float y)

void NanoVG::rotate(float angle)
{
if (fContext == nullptr) return;
DISTRHO_SAFE_ASSERT_RETURN(angle > 0.0f,);

nvgRotate(fContext, angle);
if (fContext != nullptr)
nvgRotate(fContext, angle);
}

void NanoVG::skewX(float angle)
@@ -574,6 +667,45 @@ NanoImage::Handle NanoVG::createImageFromMemory(uchar* data, uint dataSize, int
return NanoImage::Handle(fContext, nvgCreateImageMem(fContext, imageFlags, data,static_cast<int>(dataSize)));
}

NanoImage::Handle NanoVG::createImageFromRawMemory(uint w, uint h, const uchar* data,
ImageFlags imageFlags, ImageFormat format)
{
return createImageFromRawMemory(w, h, data, static_cast<int>(imageFlags), format);
}

NanoImage::Handle NanoVG::createImageFromRawMemory(uint w, uint h, const uchar* data,
int imageFlags, ImageFormat format)
{
if (fContext == nullptr) return NanoImage::Handle();
DISTRHO_SAFE_ASSERT_RETURN(data != nullptr, NanoImage::Handle());

NVGtexture nvgformat;
switch (format)
{
case kImageFormatGrayscale:
nvgformat = NVG_TEXTURE_ALPHA;
break;
case kImageFormatBGR:
nvgformat = NVG_TEXTURE_BGR;
break;
case kImageFormatBGRA:
nvgformat = NVG_TEXTURE_BGRA;
break;
case kImageFormatRGB:
nvgformat = NVG_TEXTURE_RGB;
break;
case kImageFormatRGBA:
nvgformat = NVG_TEXTURE_RGBA;
break;
default:
return NanoImage::Handle();
}

return NanoImage::Handle(fContext, nvgCreateImageRaw(fContext,
static_cast<int>(w),
static_cast<int>(h), imageFlags, nvgformat, data));
}

NanoImage::Handle NanoVG::createImageFromRGBA(uint w, uint h, const uchar* data, ImageFlags imageFlags)
{
return createImageFromRGBA(w, h, data, static_cast<int>(imageFlags));
@@ -589,12 +721,14 @@ NanoImage::Handle NanoVG::createImageFromRGBA(uint w, uint h, const uchar* data,
static_cast<int>(h), imageFlags, data));
}

NanoImage::Handle NanoVG::createImageFromTextureHandle(GLuint textureId, uint w, uint h, ImageFlags imageFlags, bool deleteTexture)
NanoImage::Handle NanoVG::createImageFromTextureHandle(GLuint textureId, uint w, uint h,
ImageFlags imageFlags, bool deleteTexture)
{
return createImageFromTextureHandle(textureId, w, h, static_cast<int>(imageFlags), deleteTexture);
}

NanoImage::Handle NanoVG::createImageFromTextureHandle(GLuint textureId, uint w, uint h, int imageFlags, bool deleteTexture)
NanoImage::Handle NanoVG::createImageFromTextureHandle(GLuint textureId, uint w, uint h,
int imageFlags, bool deleteTexture)
{
if (fContext == nullptr) return NanoImage::Handle();
DISTRHO_SAFE_ASSERT_RETURN(textureId != 0, NanoImage::Handle());
@@ -758,26 +892,26 @@ void NanoVG::stroke()

NanoVG::FontId NanoVG::createFontFromFile(const char* name, const char* filename)
{
if (fContext == nullptr) return -1;
DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', -1);
DISTRHO_SAFE_ASSERT_RETURN(filename != nullptr && filename[0] != '\0', -1);
DISTRHO_SAFE_ASSERT_RETURN(fContext != nullptr, -1);

return nvgCreateFont(fContext, name, filename);
}

NanoVG::FontId NanoVG::createFontFromMemory(const char* name, const uchar* data, uint dataSize, bool freeData)
{
if (fContext == nullptr) return -1;
DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', -1);
DISTRHO_SAFE_ASSERT_RETURN(data != nullptr, -1);
DISTRHO_SAFE_ASSERT_RETURN(fContext != nullptr, -1);

return nvgCreateFontMem(fContext, name, const_cast<uchar*>(data), static_cast<int>(dataSize), freeData);
}

NanoVG::FontId NanoVG::findFont(const char* name)
{
if (fContext == nullptr) return -1;
DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', -1);
DISTRHO_SAFE_ASSERT_RETURN(fContext != nullptr, -1);

return nvgFindFont(fContext, name);
}
@@ -899,77 +1033,56 @@ int NanoVG::textBreakLines(const char* string, const char* end, float breakRowWi
}

#ifndef DGL_NO_SHARED_RESOURCES
void NanoVG::loadSharedResources()
bool NanoVG::loadSharedResources()
{
if (fContext == nullptr) return false;

if (nvgFindFont(fContext, NANOVG_DEJAVU_SANS_TTF) >= 0)
return;
return true;

using namespace dpf_resources;

nvgCreateFontMem(fContext, NANOVG_DEJAVU_SANS_TTF, (const uchar*)dejavusans_ttf, dejavusans_ttf_size, 0);
return nvgCreateFontMem(fContext, NANOVG_DEJAVU_SANS_TTF, (uchar*)dejavusans_ttf, dejavusans_ttf_size, 0) >= 0;
}
#endif

// -----------------------------------------------------------------------
// NanoSubWidget

struct NanoWidget::PrivateData {
NanoWidget* const self;
std::vector<NanoWidget*> subWidgets;

PrivateData(NanoWidget* const s)
: self(s),
subWidgets() {}

~PrivateData()
{
subWidgets.clear();
}
};

NanoWidget::NanoWidget(Window& parent, int flags)
: Widget(parent),
NanoVG(flags),
nData(new PrivateData(this))
template <>
NanoBaseWidget<SubWidget>::NanoBaseWidget(Widget* const parent, int flags)
: SubWidget(parent),
NanoVG(flags)
{
pData->needsScaling = true;
setNeedsViewportScaling();
}

NanoWidget::NanoWidget(Widget* groupWidget, int flags)
: Widget(groupWidget, true),
NanoVG(flags),
nData(new PrivateData(this))
{
pData->needsScaling = true;
}
template class NanoBaseWidget<SubWidget>;

NanoWidget::NanoWidget(NanoWidget* groupWidget)
: Widget(groupWidget, false),
NanoVG(groupWidget),
nData(new PrivateData(this))
{
pData->needsScaling = true;
pData->skipDisplay = true;
groupWidget->nData->subWidgets.push_back(this);
}
// -----------------------------------------------------------------------
// NanoTopLevelWidget

NanoWidget::~NanoWidget()
{
delete nData;
}
template <>
NanoBaseWidget<TopLevelWidget>::NanoBaseWidget(Window& windowToMapTo, int flags)
: TopLevelWidget(windowToMapTo),
NanoVG(flags) {}

void NanoWidget::onDisplay()
{
NanoVG::beginFrame(getWidth(), getHeight());
onNanoDisplay();
template class NanoBaseWidget<TopLevelWidget>;

for (std::vector<NanoWidget*>::iterator it = nData->subWidgets.begin(); it != nData->subWidgets.end(); ++it)
{
NanoWidget* const widget(*it);
widget->onNanoDisplay();
}
// -----------------------------------------------------------------------
// NanoStandaloneWindow

NanoVG::endFrame();
}
template <>
NanoBaseWidget<StandaloneWindow>::NanoBaseWidget(Application& app, int flags)
: StandaloneWindow(app),
NanoVG(flags) {}

template <>
NanoBaseWidget<StandaloneWindow>::NanoBaseWidget(Application& app, Window& parentWindow, int flags)
: StandaloneWindow(app, parentWindow),
NanoVG(flags) {}

template class NanoBaseWidget<StandaloneWindow>;

// -----------------------------------------------------------------------



+ 796
- 0
source/modules/dgl/src/OpenGL.cpp View File

@@ -0,0 +1,796 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifdef _MSC_VER
// instantiated template classes whose methods are defined elsewhere
# pragma warning(disable:4661)
#endif

#include "../OpenGL.hpp"
#include "../Color.hpp"
#include "../ImageWidgets.hpp"

#include "SubWidgetPrivateData.hpp"
#include "TopLevelWidgetPrivateData.hpp"
#include "WidgetPrivateData.hpp"
#include "WindowPrivateData.hpp"

// templated classes
#include "ImageBaseWidgets.cpp"

START_NAMESPACE_DGL

// -----------------------------------------------------------------------

#ifdef DGL_USE_OPENGL3
static void notImplemented(const char* const name)
{
d_stderr2("OpenGL3 function not implemented: %s", name);
}
#endif

// -----------------------------------------------------------------------
// Color

void Color::setFor(const GraphicsContext&, const bool includeAlpha)
{
#ifndef DGL_USE_OPENGL3
if (includeAlpha)
glColor4f(red, green, blue, alpha);
else
glColor3f(red, green, blue);
#else
notImplemented("Color::setFor");
// unused
(void)includeAlpha;
#endif
}

// -----------------------------------------------------------------------
// Line

#ifndef DGL_USE_OPENGL3
template<typename T>
static void drawLine(const Point<T>& posStart, const Point<T>& posEnd)
{
DISTRHO_SAFE_ASSERT_RETURN(posStart != posEnd,);

glBegin(GL_LINES);

{
glVertex2d(posStart.getX(), posStart.getY());
glVertex2d(posEnd.getX(), posEnd.getY());
}

glEnd();
}
#endif

template<typename T>
void Line<T>::draw(const GraphicsContext&, const T width)
{
#ifndef DGL_USE_OPENGL3
DISTRHO_SAFE_ASSERT_RETURN(width != 0,);

glLineWidth(static_cast<GLfloat>(width));
drawLine<T>(posStart, posEnd);
#else
notImplemented("Line::draw");
#endif
}

// deprecated calls
template<typename T>
void Line<T>::draw()
{
#ifndef DGL_USE_OPENGL3
drawLine<T>(posStart, posEnd);
#else
notImplemented("Line::draw");
#endif
}

template class Line<double>;
template class Line<float>;
template class Line<int>;
template class Line<uint>;
template class Line<short>;
template class Line<ushort>;

// -----------------------------------------------------------------------
// Circle

#ifndef DGL_USE_OPENGL3
template<typename T>
static void drawCircle(const Point<T>& pos,
const uint numSegments,
const float size,
const float sin,
const float cos,
const bool outline)
{
DISTRHO_SAFE_ASSERT_RETURN(numSegments >= 3 && size > 0.0f,);

const T origx = pos.getX();
const T origy = pos.getY();
double t, x = size, y = 0.0;

glBegin(outline ? GL_LINE_LOOP : GL_POLYGON);

for (uint i=0; i<numSegments; ++i)
{
glVertex2d(x + origx, y + origy);

t = x;
x = cos * x - sin * y;
y = sin * t + cos * y;
}

glEnd();
}
#endif

template<typename T>
void Circle<T>::draw(const GraphicsContext&)
{
#ifndef DGL_USE_OPENGL3
drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, false);
#else
notImplemented("Circle::draw");
#endif
}

template<typename T>
void Circle<T>::drawOutline(const GraphicsContext&, const T lineWidth)
{
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,);

glLineWidth(static_cast<GLfloat>(lineWidth));
#ifndef DGL_USE_OPENGL3
drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, true);
#else
notImplemented("Circle::drawOutline");
#endif
}

// deprecated calls
template<typename T>
void Circle<T>::draw()
{
#ifndef DGL_USE_OPENGL3
drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, false);
#else
notImplemented("Circle::draw");
#endif
}

template<typename T>
void Circle<T>::drawOutline()
{
#ifndef DGL_USE_OPENGL3
drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, true);
#else
notImplemented("Circle::drawOutline");
#endif
}

template class Circle<double>;
template class Circle<float>;
template class Circle<int>;
template class Circle<uint>;
template class Circle<short>;
template class Circle<ushort>;

// -----------------------------------------------------------------------
// Triangle

#ifndef DGL_USE_OPENGL3
template<typename T>
static void drawTriangle(const Point<T>& pos1,
const Point<T>& pos2,
const Point<T>& pos3,
const bool outline)
{
DISTRHO_SAFE_ASSERT_RETURN(pos1 != pos2 && pos1 != pos3,);

glBegin(outline ? GL_LINE_LOOP : GL_TRIANGLES);

{
glVertex2d(pos1.getX(), pos1.getY());
glVertex2d(pos2.getX(), pos2.getY());
glVertex2d(pos3.getX(), pos3.getY());
}

glEnd();
}
#endif

template<typename T>
void Triangle<T>::draw(const GraphicsContext&)
{
#ifndef DGL_USE_OPENGL3
drawTriangle<T>(pos1, pos2, pos3, false);
#else
notImplemented("Triangle::draw");
#endif
}

template<typename T>
void Triangle<T>::drawOutline(const GraphicsContext&, const T lineWidth)
{
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,);

glLineWidth(static_cast<GLfloat>(lineWidth));
#ifndef DGL_USE_OPENGL3
drawTriangle<T>(pos1, pos2, pos3, true);
#else
notImplemented("Triangle::drawOutline");
#endif
}

// deprecated calls
template<typename T>
void Triangle<T>::draw()
{
#ifndef DGL_USE_OPENGL3
drawTriangle<T>(pos1, pos2, pos3, false);
#else
notImplemented("Triangle::draw");
#endif
}

template<typename T>
void Triangle<T>::drawOutline()
{
#ifndef DGL_USE_OPENGL3
drawTriangle<T>(pos1, pos2, pos3, true);
#else
notImplemented("Triangle::drawOutline");
#endif
}

template class Triangle<double>;
template class Triangle<float>;
template class Triangle<int>;
template class Triangle<uint>;
template class Triangle<short>;
template class Triangle<ushort>;

// -----------------------------------------------------------------------
// Rectangle

#ifndef DGL_USE_OPENGL3
template<typename T>
static void drawRectangle(const Rectangle<T>& rect, const bool outline)
{
DISTRHO_SAFE_ASSERT_RETURN(rect.isValid(),);

glBegin(outline ? GL_LINE_LOOP : GL_QUADS);

{
const T x = rect.getX();
const T y = rect.getY();
const T w = rect.getWidth();
const T h = rect.getHeight();

glTexCoord2f(0.0f, 0.0f);
glVertex2d(x, y);

glTexCoord2f(1.0f, 0.0f);
glVertex2d(x+w, y);

glTexCoord2f(1.0f, 1.0f);
glVertex2d(x+w, y+h);

glTexCoord2f(0.0f, 1.0f);
glVertex2d(x, y+h);
}

glEnd();
}
#endif

template<typename T>
void Rectangle<T>::draw(const GraphicsContext&)
{
#ifndef DGL_USE_OPENGL3
drawRectangle<T>(*this, false);
#else
notImplemented("Rectangle::draw");
#endif
}

template<typename T>
void Rectangle<T>::drawOutline(const GraphicsContext&, const T lineWidth)
{
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,);

glLineWidth(static_cast<GLfloat>(lineWidth));
#ifndef DGL_USE_OPENGL3
drawRectangle<T>(*this, true);
#else
notImplemented("Rectangle::drawOutline");
#endif
}

// deprecated calls
template<typename T>
void Rectangle<T>::draw()
{
#ifndef DGL_USE_OPENGL3
drawRectangle<T>(*this, false);
#else
notImplemented("Rectangle::draw");
#endif
}

template<typename T>
void Rectangle<T>::drawOutline()
{
#ifndef DGL_USE_OPENGL3
drawRectangle<T>(*this, true);
#else
notImplemented("Rectangle::drawOutline");
#endif
}

template class Rectangle<double>;
template class Rectangle<float>;
template class Rectangle<int>;
template class Rectangle<uint>;
template class Rectangle<short>;
template class Rectangle<ushort>;

// -----------------------------------------------------------------------
// OpenGLImage

static void setupOpenGLImage(const OpenGLImage& image, GLuint textureId)
{
DISTRHO_SAFE_ASSERT_RETURN(image.isValid(),);

glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, textureId);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);

static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans);

glPixelStorei(GL_PACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
static_cast<GLsizei>(image.getWidth()),
static_cast<GLsizei>(image.getHeight()),
0,
asOpenGLImageFormat(image.getFormat()), GL_UNSIGNED_BYTE, image.getRawData());

glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}

static void drawOpenGLImage(const OpenGLImage& image, const Point<int>& pos, const GLuint textureId, bool& setupCalled)
{
if (textureId == 0 || image.isInvalid())
return;

if (! setupCalled)
{
setupOpenGLImage(image, textureId);
setupCalled = true;
}

#ifndef DGL_USE_OPENGL3
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
#endif

glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, textureId);

#ifndef DGL_USE_OPENGL3
glBegin(GL_QUADS);

{
const int x = pos.getX();
const int y = pos.getY();
const int w = static_cast<int>(image.getWidth());
const int h = static_cast<int>(image.getHeight());

glTexCoord2f(0.0f, 0.0f);
glVertex2d(x, y);

glTexCoord2f(1.0f, 0.0f);
glVertex2d(x+w, y);

glTexCoord2f(1.0f, 1.0f);
glVertex2d(x+w, y+h);

glTexCoord2f(0.0f, 1.0f);
glVertex2d(x, y+h);
}

glEnd();
#endif

glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}

OpenGLImage::OpenGLImage()
: ImageBase(),
textureId(0),
setupCalled(false)
{
glGenTextures(1, &textureId);
DISTRHO_SAFE_ASSERT(textureId != 0);
}

OpenGLImage::OpenGLImage(const char* const rdata, const uint w, const uint h, const ImageFormat fmt)
: ImageBase(rdata, w, h, fmt),
textureId(0),
setupCalled(false)
{
glGenTextures(1, &textureId);
DISTRHO_SAFE_ASSERT(textureId != 0);
}

OpenGLImage::OpenGLImage(const char* const rdata, const Size<uint>& s, const ImageFormat fmt)
: ImageBase(rdata, s, fmt),
textureId(0),
setupCalled(false)
{
glGenTextures(1, &textureId);
DISTRHO_SAFE_ASSERT(textureId != 0);
}

OpenGLImage::OpenGLImage(const OpenGLImage& image)
: ImageBase(image),
textureId(0),
setupCalled(false)
{
glGenTextures(1, &textureId);
DISTRHO_SAFE_ASSERT(textureId != 0);
}

OpenGLImage::~OpenGLImage()
{
if (textureId != 0)
glDeleteTextures(1, &textureId);
}

void OpenGLImage::loadFromMemory(const char* const rdata, const Size<uint>& s, const ImageFormat fmt) noexcept
{
setupCalled = false;
ImageBase::loadFromMemory(rdata, s, fmt);
}

void OpenGLImage::drawAt(const GraphicsContext&, const Point<int>& pos)
{
drawOpenGLImage(*this, pos, textureId, setupCalled);
}

OpenGLImage& OpenGLImage::operator=(const OpenGLImage& image) noexcept
{
rawData = image.rawData;
size = image.size;
format = image.format;
setupCalled = false;
return *this;
}

// deprecated calls
OpenGLImage::OpenGLImage(const char* const rdata, const uint w, const uint h, const GLenum fmt)
: ImageBase(rdata, w, h, asDISTRHOImageFormat(fmt)),
textureId(0),
setupCalled(false)
{
glGenTextures(1, &textureId);
DISTRHO_SAFE_ASSERT(textureId != 0);
}

OpenGLImage::OpenGLImage(const char* const rdata, const Size<uint>& s, const GLenum fmt)
: ImageBase(rdata, s, asDISTRHOImageFormat(fmt)),
textureId(0),
setupCalled(false)
{
glGenTextures(1, &textureId);
DISTRHO_SAFE_ASSERT(textureId != 0);
}

void OpenGLImage::draw()
{
drawOpenGLImage(*this, Point<int>(0, 0), textureId, setupCalled);
}

void OpenGLImage::drawAt(const int x, const int y)
{
drawOpenGLImage(*this, Point<int>(x, y), textureId, setupCalled);
}

void OpenGLImage::drawAt(const Point<int>& pos)
{
drawOpenGLImage(*this, pos, textureId, setupCalled);
}

// -----------------------------------------------------------------------
// ImageBaseAboutWindow

#if 0
template <>
void ImageBaseAboutWindow<OpenGLImage>::onDisplay()
{
const GraphicsContext& context(getGraphicsContext());
img.draw(context);
}
#endif

template class ImageBaseAboutWindow<OpenGLImage>;

// -----------------------------------------------------------------------
// ImageBaseButton

template class ImageBaseButton<OpenGLImage>;

// -----------------------------------------------------------------------
// ImageBaseKnob

template <>
void ImageBaseKnob<OpenGLImage>::PrivateData::init()
{
glTextureId = 0;
glGenTextures(1, &glTextureId);
}

template <>
void ImageBaseKnob<OpenGLImage>::PrivateData::cleanup()
{
if (glTextureId == 0)
return;

glDeleteTextures(1, &glTextureId);
glTextureId = 0;
}

template <>
void ImageBaseKnob<OpenGLImage>::onDisplay()
{
const GraphicsContext& context(getGraphicsContext());
const float normValue = getNormalizedValue();

glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, pData->glTextureId);

if (! pData->isReady)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);

static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans);

glPixelStorei(GL_PACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

uint imageDataOffset = 0;

if (pData->rotationAngle == 0)
{
DISTRHO_SAFE_ASSERT_RETURN(pData->imgLayerCount > 0,);
DISTRHO_SAFE_ASSERT_RETURN(normValue >= 0.0f,);

const uint& v1(pData->isImgVertical ? pData->imgLayerWidth : pData->imgLayerHeight);
const uint& v2(pData->isImgVertical ? pData->imgLayerHeight : pData->imgLayerWidth);

// TODO kImageFormatGreyscale
const uint layerDataSize = v1 * v2 * ((pData->image.getFormat() == kImageFormatBGRA ||
pData->image.getFormat() == kImageFormatRGBA) ? 4 : 3);
/* */ imageDataOffset = layerDataSize * uint(normValue * float(pData->imgLayerCount-1));
}

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
static_cast<GLsizei>(getWidth()), static_cast<GLsizei>(getHeight()), 0,
asOpenGLImageFormat(pData->image.getFormat()), GL_UNSIGNED_BYTE, pData->image.getRawData() + imageDataOffset);

pData->isReady = true;
}

const int w = static_cast<int>(getWidth());
const int h = static_cast<int>(getHeight());

if (pData->rotationAngle != 0)
{
#ifndef DGL_USE_OPENGL3
glPushMatrix();
#endif

const int w2 = w/2;
const int h2 = h/2;

#ifndef DGL_USE_OPENGL3
glTranslatef(static_cast<float>(w2), static_cast<float>(h2), 0.0f);
glRotatef(normValue*static_cast<float>(pData->rotationAngle), 0.0f, 0.0f, 1.0f);
#endif

Rectangle<int>(-w2, -h2, w, h).draw(context);

#ifndef DGL_USE_OPENGL3
glPopMatrix();
#endif
}
else
{
Rectangle<int>(0, 0, w, h).draw(context);
}

glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}

template class ImageBaseKnob<OpenGLImage>;

// -----------------------------------------------------------------------
// ImageBaseSlider

template class ImageBaseSlider<OpenGLImage>;

// -----------------------------------------------------------------------
// ImageBaseSwitch

template class ImageBaseSwitch<OpenGLImage>;

// -----------------------------------------------------------------------

void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaleFactor)
{
if (skipDrawing)
return;

bool needsDisableScissor = false;

if (needsViewportScaling)
{
// limit viewport to widget bounds
const int x = absolutePos.getX();
const int w = static_cast<int>(self->getWidth());
const int h = static_cast<int>(self->getHeight());

if (viewportScaleFactor != 0.0 && viewportScaleFactor != 1.0)
{
glViewport(x,
-static_cast<int>(height * viewportScaleFactor - height + absolutePos.getY() + 0.5),
static_cast<int>(width * viewportScaleFactor + 0.5),
static_cast<int>(height * viewportScaleFactor + 0.5));
}
else
{
const int y = static_cast<int>(height - self->getHeight()) - absolutePos.getY();
glViewport(x, y, w, h);
}
}
else if (needsFullViewportForDrawing || (absolutePos.isZero() && self->getSize() == Size<uint>(width, height)))
{
// full viewport size
glViewport(0,
-static_cast<int>(height * autoScaleFactor - height + 0.5),
static_cast<int>(width * autoScaleFactor + 0.5),
static_cast<int>(height * autoScaleFactor + 0.5));
}
else
{
// set viewport pos
glViewport(static_cast<int>(absolutePos.getX() * autoScaleFactor + 0.5),
-static_cast<int>(std::round((height * autoScaleFactor - height)
+ (absolutePos.getY() * autoScaleFactor))),
static_cast<int>(std::round(width * autoScaleFactor)),
static_cast<int>(std::round(height * autoScaleFactor)));

// then cut the outer bounds
glScissor(static_cast<int>(absolutePos.getX() * autoScaleFactor + 0.5),
static_cast<int>(height - std::round((static_cast<int>(self->getHeight()) + absolutePos.getY())
* autoScaleFactor)),
static_cast<int>(std::round(self->getWidth() * autoScaleFactor)),
static_cast<int>(std::round(self->getHeight() * autoScaleFactor)));

glEnable(GL_SCISSOR_TEST);
needsDisableScissor = true;
}

// display widget
self->onDisplay();

if (needsDisableScissor)
glDisable(GL_SCISSOR_TEST);

selfw->pData->displaySubWidgets(width, height, autoScaleFactor);
}

// -----------------------------------------------------------------------

void TopLevelWidget::PrivateData::display()
{
if (! selfw->pData->visible)
return;

const Size<uint> size(window.getSize());
const uint width = size.getWidth();
const uint height = size.getHeight();

const double autoScaleFactor = window.pData->autoScaleFactor;

// full viewport size
if (window.pData->autoScaling)
{
glViewport(0,
-static_cast<int>(height * autoScaleFactor - height + 0.5),
static_cast<int>(width * autoScaleFactor + 0.5),
static_cast<int>(height * autoScaleFactor + 0.5));
}
else
{
glViewport(0, 0, static_cast<int>(width), static_cast<int>(height));
}

// main widget drawing
self->onDisplay();

// now draw subwidgets if there are any
selfw->pData->displaySubWidgets(width, height, autoScaleFactor);
}

// -----------------------------------------------------------------------

void Window::PrivateData::renderToPicture(const char* const filename,
const GraphicsContext&,
const uint width,
const uint height)
{
FILE* const f = fopen(filename, "w");
DISTRHO_SAFE_ASSERT_RETURN(f != nullptr,);

GLubyte* const pixels = new GLubyte[width * height * 3 * sizeof(GLubyte)];

glFlush();
glReadPixels(0, 0, static_cast<GLsizei>(width), static_cast<GLsizei>(height), GL_RGB, GL_UNSIGNED_BYTE, pixels);

fprintf(f, "P3\n%d %d\n255\n", width, height);
for (uint y = 0; y < height; y++)
{
for (uint i, x = 0; x < width; x++)
{
i = 3 * ((height - y - 1) * width + x);
fprintf(f, "%3d %3d %3d ", pixels[i], pixels[i+1], pixels[i+2]);
}
fprintf(f, "\n");
}

delete[] pixels;
fclose(f);
}

// -----------------------------------------------------------------------

const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept
{
return (const GraphicsContext&)graphicsContext;
}

// -----------------------------------------------------------------------

END_NAMESPACE_DGL

+ 189
- 0
source/modules/dgl/src/SubWidget.cpp View File

@@ -0,0 +1,189 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "SubWidgetPrivateData.hpp"
#include "WidgetPrivateData.hpp"
#include "../TopLevelWidget.hpp"

START_NAMESPACE_DGL

// --------------------------------------------------------------------------------------------------------------------

SubWidget::SubWidget(Widget* const parentWidget)
: Widget(parentWidget),
pData(new PrivateData(this, parentWidget)) {}

SubWidget::~SubWidget()
{
delete pData;
}

template<typename T>
bool SubWidget::contains(const T x, const T y) const noexcept
{
return Rectangle<double>(0, 0,
static_cast<double>(getWidth()),
static_cast<double>(getHeight())).contains(x, y);
}

template<typename T>
bool SubWidget::contains(const Point<T>& pos) const noexcept
{
return contains(pos.getX(), pos.getY());
}

int SubWidget::getAbsoluteX() const noexcept
{
return pData->absolutePos.getX();
}

int SubWidget::getAbsoluteY() const noexcept
{
return pData->absolutePos.getY();
}

Point<int> SubWidget::getAbsolutePos() const noexcept
{
return pData->absolutePos;
}

Rectangle<int> SubWidget::getAbsoluteArea() const noexcept
{
return Rectangle<int>(getAbsolutePos(), getSize().toInt());
}

Rectangle<uint> SubWidget::getConstrainedAbsoluteArea() const noexcept
{
const int x = getAbsoluteX();
const int y = getAbsoluteY();

if (x >= 0 && y >= 0)
return Rectangle<uint>(x, y, getSize());

const int xOffset = std::min(0, x);
const int yOffset = std::min(0, y);
const int width = std::max(0, static_cast<int>(getWidth()) + xOffset);
const int height = std::max(0, static_cast<int>(getHeight()) + yOffset);

return Rectangle<uint>(0, 0, static_cast<uint>(width), static_cast<uint>(height));
}

void SubWidget::setAbsoluteX(const int x) noexcept
{
setAbsolutePos(Point<int>(x, getAbsoluteY()));
}

void SubWidget::setAbsoluteY(const int y) noexcept
{
setAbsolutePos(Point<int>(getAbsoluteX(), y));
}

void SubWidget::setAbsolutePos(const int x, const int y) noexcept
{
setAbsolutePos(Point<int>(x, y));
}

void SubWidget::setAbsolutePos(const Point<int>& pos) noexcept
{
if (pData->absolutePos == pos)
return;

PositionChangedEvent ev;
ev.oldPos = pData->absolutePos;
ev.pos = pos;

pData->absolutePos = pos;
onPositionChanged(ev);

repaint();
}

Point<int> SubWidget::getMargin() const noexcept
{
return pData->margin;
}

void SubWidget::setMargin(const int x, const int y) noexcept
{
pData->margin = Point<int>(x, y);
}

void SubWidget::setMargin(const Point<int>& offset) noexcept
{
pData->margin = offset;
}

Widget* SubWidget::getParentWidget() const noexcept
{
return pData->parentWidget;
}

void SubWidget::repaint() noexcept
{
if (! isVisible())
return;

if (TopLevelWidget* const topw = getTopLevelWidget())
{
if (pData->needsFullViewportForDrawing)
topw->repaint();
else
topw->repaint(getConstrainedAbsoluteArea());
}
}

void SubWidget::toFront()
{
std::list<SubWidget*>& subwidgets(pData->parentWidget->pData->subWidgets);

subwidgets.remove(this);
subwidgets.push_back(this);
}

void SubWidget::setNeedsFullViewportDrawing(const bool needsFullViewportForDrawing)
{
pData->needsFullViewportForDrawing = needsFullViewportForDrawing;
}

void SubWidget::setNeedsViewportScaling(const bool needsViewportScaling, const double autoScaleFactor)
{
pData->needsViewportScaling = needsViewportScaling;
pData->viewportScaleFactor = autoScaleFactor;
}

void SubWidget::setSkipDrawing(const bool skipDrawing)
{
pData->skipDrawing = skipDrawing;
}

void SubWidget::onPositionChanged(const PositionChangedEvent&)
{
}

// --------------------------------------------------------------------------------------------------------------------
// Possible template data types

template<>
bool SubWidget::contains(const Point<double>& pos) const noexcept
{
return contains(pos.getX(), pos.getY());
}

// float, int, uint, short, ushort

// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL

+ 45
- 0
source/modules/dgl/src/SubWidgetPrivateData.cpp View File

@@ -0,0 +1,45 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "SubWidgetPrivateData.hpp"
#include "WidgetPrivateData.hpp"

START_NAMESPACE_DGL

// --------------------------------------------------------------------------------------------------------------------

SubWidget::PrivateData::PrivateData(SubWidget* const s, Widget* const pw)
: self(s),
selfw((Widget*)s),
parentWidget(pw),
absolutePos(),
margin(),
needsFullViewportForDrawing(false),
needsViewportScaling(false),
skipDrawing(false),
viewportScaleFactor(0.0)
{
parentWidget->pData->subWidgets.push_back(self);
}

SubWidget::PrivateData::~PrivateData()
{
parentWidget->pData->subWidgets.remove(self);
}

// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL

+ 50
- 0
source/modules/dgl/src/SubWidgetPrivateData.hpp View File

@@ -0,0 +1,50 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef DGL_SUBWIDGET_PRIVATE_DATA_HPP_INCLUDED
#define DGL_SUBWIDGET_PRIVATE_DATA_HPP_INCLUDED

#include "../SubWidget.hpp"

START_NAMESPACE_DGL

// --------------------------------------------------------------------------------------------------------------------

struct SubWidget::PrivateData {
SubWidget* const self;
Widget* const selfw;
Widget* const parentWidget;
Point<int> absolutePos;
Point<int> margin;
bool needsFullViewportForDrawing; // needed for widgets drawing out of bounds
bool needsViewportScaling; // needed for NanoVG
bool skipDrawing; // for context reuse in NanoVG based guis
double viewportScaleFactor; // auto-scaling for NanoVG

explicit PrivateData(SubWidget* const s, Widget* const pw);
~PrivateData();

// NOTE display function is different depending on build type, must call displaySubWidgets at the end
void display(uint width, uint height, double autoScaleFactor);

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData)
};

// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL

#endif // DGL_SUBWIDGET_PRIVATE_DATA_HPP_INCLUDED

+ 151
- 0
source/modules/dgl/src/TopLevelWidget.cpp View File

@@ -0,0 +1,151 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "TopLevelWidgetPrivateData.hpp"
#include "../Window.hpp"

START_NAMESPACE_DGL

// --------------------------------------------------------------------------------------------------------------------

TopLevelWidget::TopLevelWidget(Window& windowToMapTo)
: Widget(this),
pData(new PrivateData(this, windowToMapTo)) {}

TopLevelWidget::~TopLevelWidget()
{
delete pData;
}

Application& TopLevelWidget::getApp() const noexcept
{
return pData->window.getApp();
}

Window& TopLevelWidget::getWindow() const noexcept
{
return pData->window;
}

void TopLevelWidget::setWidth(const uint width)
{
pData->window.setWidth(width);
}

void TopLevelWidget::setHeight(const uint height)
{
pData->window.setHeight(height);
}

void TopLevelWidget::setSize(const uint width, const uint height)
{
pData->window.setSize(width, height);
}

void TopLevelWidget::setSize(const Size<uint>& size)
{
pData->window.setSize(size);
}

bool TopLevelWidget::setClipboard(const char* const mimeType, const void* const data, const size_t dataSize)
{
return pData->window.setClipboard(mimeType, data, dataSize);
}

const void* TopLevelWidget::getClipboard(const char*& mimeType, size_t& dataSize)
{
return pData->window.getClipboard(mimeType, dataSize);
}

bool TopLevelWidget::setCursor(const MouseCursor cursor)
{
return pData->window.setCursor(cursor);
}

bool TopLevelWidget::addIdleCallback(IdleCallback* const callback, const uint timerFrequencyInMs)
{
return pData->window.addIdleCallback(callback, timerFrequencyInMs);
}

bool TopLevelWidget::removeIdleCallback(IdleCallback* const callback)
{
return pData->window.removeIdleCallback(callback);
}

double TopLevelWidget::getScaleFactor() const noexcept
{
return pData->window.getScaleFactor();
}

void TopLevelWidget::repaint() noexcept
{
pData->window.repaint();
}

void TopLevelWidget::repaint(const Rectangle<uint>& rect) noexcept
{
pData->window.repaint(rect);
}

void TopLevelWidget::setGeometryConstraints(const uint minimumWidth,
const uint minimumHeight,
const bool keepAspectRatio,
const bool automaticallyScale,
const bool resizeNowIfAutoScaling)
{
pData->window.setGeometryConstraints(minimumWidth,
minimumHeight,
keepAspectRatio,
automaticallyScale,
resizeNowIfAutoScaling);
}

// --------------------------------------------------------------------------------------------------------------------

bool TopLevelWidget::onKeyboard(const KeyboardEvent& ev)
{
return pData->keyboardEvent(ev);
}

bool TopLevelWidget::onCharacterInput(const CharacterInputEvent& ev)
{
return pData->characterInputEvent(ev);
}

bool TopLevelWidget::onMouse(const MouseEvent& ev)
{
return pData->mouseEvent(ev);
}

bool TopLevelWidget::onMotion(const MotionEvent& ev)
{
return pData->motionEvent(ev);
}

bool TopLevelWidget::onScroll(const ScrollEvent& ev)
{
return pData->scrollEvent(ev);
}

// --------------------------------------------------------------------------------------------------------------------

void TopLevelWidget::requestSizeChange(uint, uint)
{
}

// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL

+ 134
- 0
source/modules/dgl/src/TopLevelWidgetPrivateData.cpp View File

@@ -0,0 +1,134 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "TopLevelWidgetPrivateData.hpp"
#include "WidgetPrivateData.hpp"
#include "WindowPrivateData.hpp"
#include "pugl.hpp"

START_NAMESPACE_DGL

// -----------------------------------------------------------------------

TopLevelWidget::PrivateData::PrivateData(TopLevelWidget* const s, Window& w)
: self(s),
selfw(s),
window(w)
{
window.pData->topLevelWidgets.push_back(self);
}

TopLevelWidget::PrivateData::~PrivateData()
{
window.pData->topLevelWidgets.remove(self);
}

bool TopLevelWidget::PrivateData::keyboardEvent(const KeyboardEvent& ev)
{
// ignore event if we are not visible
if (! selfw->pData->visible)
return false;

// propagate event to all subwidgets recursively
return selfw->pData->giveKeyboardEventForSubWidgets(ev);
}

bool TopLevelWidget::PrivateData::characterInputEvent(const CharacterInputEvent& ev)
{
// ignore event if we are not visible
if (! selfw->pData->visible)
return false;

// propagate event to all subwidgets recursively
return selfw->pData->giveCharacterInputEventForSubWidgets(ev);
}

bool TopLevelWidget::PrivateData::mouseEvent(const MouseEvent& ev)
{
// ignore event if we are not visible
if (! selfw->pData->visible)
return false;

MouseEvent rev = ev;

if (window.pData->autoScaling)
{
const double autoScaleFactor = window.pData->autoScaleFactor;

rev.pos.setX(ev.pos.getX() / autoScaleFactor);
rev.pos.setY(ev.pos.getY() / autoScaleFactor);
rev.absolutePos.setX(ev.absolutePos.getX() / autoScaleFactor);
rev.absolutePos.setY(ev.absolutePos.getY() / autoScaleFactor);
}

// propagate event to all subwidgets recursively
return selfw->pData->giveMouseEventForSubWidgets(rev);
}

bool TopLevelWidget::PrivateData::motionEvent(const MotionEvent& ev)
{
// ignore event if we are not visible
if (! selfw->pData->visible)
return false;

MotionEvent rev = ev;

if (window.pData->autoScaling)
{
const double autoScaleFactor = window.pData->autoScaleFactor;

rev.pos.setX(ev.pos.getX() / autoScaleFactor);
rev.pos.setY(ev.pos.getY() / autoScaleFactor);
rev.absolutePos.setX(ev.absolutePos.getX() / autoScaleFactor);
rev.absolutePos.setY(ev.absolutePos.getY() / autoScaleFactor);
}

// propagate event to all subwidgets recursively
return selfw->pData->giveMotionEventForSubWidgets(rev);
}

bool TopLevelWidget::PrivateData::scrollEvent(const ScrollEvent& ev)
{
// ignore event if we are not visible
if (! selfw->pData->visible)
return false;

ScrollEvent rev = ev;

if (window.pData->autoScaling)
{
const double autoScaleFactor = window.pData->autoScaleFactor;

rev.pos.setX(ev.pos.getX() / autoScaleFactor);
rev.pos.setY(ev.pos.getY() / autoScaleFactor);
rev.absolutePos.setX(ev.absolutePos.getX() / autoScaleFactor);
rev.absolutePos.setY(ev.absolutePos.getY() / autoScaleFactor);
rev.delta.setX(ev.delta.getX() / autoScaleFactor);
rev.delta.setY(ev.delta.getY() / autoScaleFactor);
}

// propagate event to all subwidgets recursively
return selfw->pData->giveScrollEventForSubWidgets(rev);
}

void TopLevelWidget::PrivateData::fallbackOnResize()
{
puglFallbackOnResize(window.pData->view);
}

// -----------------------------------------------------------------------

END_NAMESPACE_DGL

+ 50
- 0
source/modules/dgl/src/TopLevelWidgetPrivateData.hpp View File

@@ -0,0 +1,50 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef DGL_TOP_LEVEL_WIDGET_PRIVATE_DATA_HPP_INCLUDED
#define DGL_TOP_LEVEL_WIDGET_PRIVATE_DATA_HPP_INCLUDED

#include "../TopLevelWidget.hpp"

#include <list>

START_NAMESPACE_DGL

// -----------------------------------------------------------------------

struct TopLevelWidget::PrivateData {
TopLevelWidget* const self;
Widget* const selfw;
Window& window;

explicit PrivateData(TopLevelWidget* self, Window& window);
~PrivateData();
void display();
bool keyboardEvent(const KeyboardEvent& ev);
bool characterInputEvent(const CharacterInputEvent& ev);
bool mouseEvent(const MouseEvent& ev);
bool motionEvent(const MotionEvent& ev);
bool scrollEvent(const ScrollEvent& ev);
void fallbackOnResize();

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData)
};

// -----------------------------------------------------------------------

END_NAMESPACE_DGL

#endif // DGL_TOP_LEVEL_WIDGET_PRIVATE_DATA_HPP_INCLUDED

+ 43
- 94
source/modules/dgl/src/Widget.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -15,33 +15,22 @@
*/

#include "WidgetPrivateData.hpp"
#include "../TopLevelWidget.hpp"
#include "../Window.hpp"

START_NAMESPACE_DGL

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// Widget

Widget::Widget(Window& parent)
: pData(new PrivateData(this, parent, nullptr, false))
{
parent._addWidget(this);
}
Widget::Widget(TopLevelWidget* const topLevelWidget)
: pData(new PrivateData(this, topLevelWidget)) {}

Widget::Widget(Widget* groupWidget)
: pData(new PrivateData(this, groupWidget->getParentWindow(), groupWidget, true))
{
pData->parent._addWidget(this);
}

Widget::Widget(Widget* groupWidget, bool addToSubWidgets)
: pData(new PrivateData(this, groupWidget->getParentWindow(), groupWidget, addToSubWidgets))
{
pData->parent._addWidget(this);
}
Widget::Widget(Widget* const parentWidget)
: pData(new PrivateData(this, parentWidget)) {}

Widget::~Widget()
{
pData->parent._removeWidget(this);
delete pData;
}

@@ -50,13 +39,15 @@ bool Widget::isVisible() const noexcept
return pData->visible;
}

void Widget::setVisible(bool yesNo)
void Widget::setVisible(bool visible)
{
if (pData->visible == yesNo)
if (pData->visible == visible)
return;

pData->visible = yesNo;
pData->parent.repaint();
pData->visible = visible;
repaint();

// FIXME check case of hiding a previously visible widget, does it trigger a repaint?
}

void Widget::show()
@@ -79,7 +70,7 @@ uint Widget::getHeight() const noexcept
return pData->size.getHeight();
}

const Size<uint>& Widget::getSize() const noexcept
const Size<uint> Widget::getSize() const noexcept
{
return pData->size;
}
@@ -96,7 +87,7 @@ void Widget::setWidth(uint width) noexcept
pData->size.setWidth(width);
onResize(ev);

pData->parent.repaint();
repaint();
}

void Widget::setHeight(uint height) noexcept
@@ -111,7 +102,7 @@ void Widget::setHeight(uint height) noexcept
pData->size.setHeight(height);
onResize(ev);

pData->parent.repaint();
repaint();
}

void Widget::setSize(uint width, uint height) noexcept
@@ -131,79 +122,34 @@ void Widget::setSize(const Size<uint>& size) noexcept
pData->size = size;
onResize(ev);

pData->parent.repaint();
}

int Widget::getAbsoluteX() const noexcept
{
return pData->absolutePos.getX();
}

int Widget::getAbsoluteY() const noexcept
{
return pData->absolutePos.getY();
}

const Point<int>& Widget::getAbsolutePos() const noexcept
{
return pData->absolutePos;
}

void Widget::setAbsoluteX(int x) noexcept
{
if (pData->absolutePos.getX() == x)
return;

pData->absolutePos.setX(x);
pData->parent.repaint();
}

void Widget::setAbsoluteY(int y) noexcept
{
if (pData->absolutePos.getY() == y)
return;

pData->absolutePos.setY(y);
pData->parent.repaint();
}

void Widget::setAbsolutePos(int x, int y) noexcept
{
setAbsolutePos(Point<int>(x, y));
}

void Widget::setAbsolutePos(const Point<int>& pos) noexcept
{
if (pData->absolutePos == pos)
return;

pData->absolutePos = pos;
pData->parent.repaint();
repaint();
}

Application& Widget::getParentApp() const noexcept
Application& Widget::getApp() const noexcept
{
return pData->parent.getApp();
DISTRHO_SAFE_ASSERT(pData->topLevelWidget != nullptr);
return pData->topLevelWidget->getApp();
}

Window& Widget::getParentWindow() const noexcept
Window& Widget::getWindow() const noexcept
{
return pData->parent;
DISTRHO_SAFE_ASSERT(pData->topLevelWidget != nullptr);
return pData->topLevelWidget->getWindow();
}

bool Widget::contains(int x, int y) const noexcept
const GraphicsContext& Widget::getGraphicsContext() const noexcept
{
return (x >= 0 && y >= 0 && static_cast<uint>(x) < pData->size.getWidth() && static_cast<uint>(y) < pData->size.getHeight());
DISTRHO_SAFE_ASSERT(pData->topLevelWidget != nullptr);
return pData->topLevelWidget->getWindow().getGraphicsContext();
}

bool Widget::contains(const Point<int>& pos) const noexcept
TopLevelWidget* Widget::getTopLevelWidget() const noexcept
{
return contains(pos.getX(), pos.getY());
return pData->topLevelWidget;
}

void Widget::repaint() noexcept
{
pData->parent.repaint();
}

uint Widget::getId() const noexcept
@@ -216,35 +162,38 @@ void Widget::setId(uint id) noexcept
pData->id = id;
}

bool Widget::onKeyboard(const KeyboardEvent&)
bool Widget::onKeyboard(const KeyboardEvent& ev)
{
return false;
return pData->giveKeyboardEventForSubWidgets(ev);
}

bool Widget::onSpecial(const SpecialEvent&)
bool Widget::onCharacterInput(const CharacterInputEvent& ev)
{
return false;
return pData->giveCharacterInputEventForSubWidgets(ev);
}

bool Widget::onMouse(const MouseEvent&)
bool Widget::onMouse(const MouseEvent& ev)
{
return false;
MouseEvent rev = ev;
return pData->giveMouseEventForSubWidgets(rev);
}

bool Widget::onMotion(const MotionEvent&)
bool Widget::onMotion(const MotionEvent& ev)
{
return false;
MotionEvent rev = ev;
return pData->giveMotionEventForSubWidgets(rev);
}

bool Widget::onScroll(const ScrollEvent&)
bool Widget::onScroll(const ScrollEvent& ev)
{
return false;
ScrollEvent rev = ev;
return pData->giveScrollEventForSubWidgets(rev);
}

void Widget::onResize(const ResizeEvent&)
{
}

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL

+ 229
- 0
source/modules/dgl/src/WidgetPrivateData.cpp View File

@@ -0,0 +1,229 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "WidgetPrivateData.hpp"
#include "SubWidgetPrivateData.hpp"
#include "../TopLevelWidget.hpp"

START_NAMESPACE_DGL

#define FOR_EACH_SUBWIDGET(it) \
for (std::list<SubWidget*>::iterator it = subWidgets.begin(); it != subWidgets.end(); ++it)

#define FOR_EACH_SUBWIDGET_INV(rit) \
for (std::list<SubWidget*>::reverse_iterator rit = subWidgets.rbegin(); rit != subWidgets.rend(); ++rit)

// -----------------------------------------------------------------------

Widget::PrivateData::PrivateData(Widget* const s, TopLevelWidget* const tlw)
: self(s),
topLevelWidget(tlw),
parentWidget(nullptr),
id(0),
needsScaling(false),
visible(true),
size(0, 0),
subWidgets() {}

Widget::PrivateData::PrivateData(Widget* const s, Widget* const pw)
: self(s),
topLevelWidget(findTopLevelWidget(pw)),
parentWidget(pw),
id(0),
needsScaling(false),
visible(true),
size(0, 0),
subWidgets() {}

Widget::PrivateData::~PrivateData()
{
subWidgets.clear();
}

void Widget::PrivateData::displaySubWidgets(const uint width, const uint height, const double autoScaleFactor)
{
if (subWidgets.size() == 0)
return;

for (std::list<SubWidget*>::iterator it = subWidgets.begin(); it != subWidgets.end(); ++it)
{
SubWidget* const subwidget(*it);

if (subwidget->isVisible())
subwidget->pData->display(width, height, autoScaleFactor);
}
}

// -----------------------------------------------------------------------

bool Widget::PrivateData::giveKeyboardEventForSubWidgets(const KeyboardEvent& ev)
{
if (! visible)
return false;
if (subWidgets.size() == 0)
return false;

FOR_EACH_SUBWIDGET_INV(rit)
{
SubWidget* const widget(*rit);

if (widget->isVisible() && widget->onKeyboard(ev))
return true;
}

return false;
}

bool Widget::PrivateData::giveCharacterInputEventForSubWidgets(const CharacterInputEvent& ev)
{
if (! visible)
return false;
if (subWidgets.size() == 0)
return false;

FOR_EACH_SUBWIDGET_INV(rit)
{
SubWidget* const widget(*rit);

if (widget->isVisible() && widget->onCharacterInput(ev))
return true;
}

return false;
}

bool Widget::PrivateData::giveMouseEventForSubWidgets(MouseEvent& ev)
{
if (! visible)
return false;
if (subWidgets.size() == 0)
return false;

const double x = ev.absolutePos.getX();
const double y = ev.absolutePos.getY();

if (SubWidget* const selfw = dynamic_cast<SubWidget*>(self))
{
if (selfw->pData->needsViewportScaling)
{
ev.absolutePos.setX(x - selfw->getAbsoluteX() + selfw->getMargin().getX());
ev.absolutePos.setY(y - selfw->getAbsoluteY() + selfw->getMargin().getY());
}
}

FOR_EACH_SUBWIDGET_INV(rit)
{
SubWidget* const widget(*rit);

if (! widget->isVisible())
continue;

ev.pos = Point<double>(x - widget->getAbsoluteX() + widget->getMargin().getX(),
y - widget->getAbsoluteY() + widget->getMargin().getY());

if (widget->onMouse(ev))
return true;
}

return false;
}

bool Widget::PrivateData::giveMotionEventForSubWidgets(MotionEvent& ev)
{
if (! visible)
return false;
if (subWidgets.size() == 0)
return false;

const double x = ev.absolutePos.getX();
const double y = ev.absolutePos.getY();

if (SubWidget* const selfw = dynamic_cast<SubWidget*>(self))
{
if (selfw->pData->needsViewportScaling)
{
ev.absolutePos.setX(x - selfw->getAbsoluteX() + selfw->getMargin().getX());
ev.absolutePos.setY(y - selfw->getAbsoluteY() + selfw->getMargin().getY());
}
}

FOR_EACH_SUBWIDGET_INV(rit)
{
SubWidget* const widget(*rit);

if (! widget->isVisible())
continue;

ev.pos = Point<double>(x - widget->getAbsoluteX() + widget->getMargin().getX(),
y - widget->getAbsoluteY() + widget->getMargin().getY());

if (widget->onMotion(ev))
return true;
}

return false;
}

bool Widget::PrivateData::giveScrollEventForSubWidgets(ScrollEvent& ev)
{
if (! visible)
return false;
if (subWidgets.size() == 0)
return false;

const double x = ev.absolutePos.getX();
const double y = ev.absolutePos.getY();

if (SubWidget* const selfw = dynamic_cast<SubWidget*>(self))
{
if (selfw->pData->needsViewportScaling)
{
ev.absolutePos.setX(x - selfw->getAbsoluteX() + selfw->getMargin().getX());
ev.absolutePos.setY(y - selfw->getAbsoluteY() + selfw->getMargin().getY());
}
}

FOR_EACH_SUBWIDGET_INV(rit)
{
SubWidget* const widget(*rit);

if (! widget->isVisible())
continue;

ev.pos = Point<double>(x - widget->getAbsoluteX() + widget->getMargin().getX(),
y - widget->getAbsoluteY() + widget->getMargin().getY());

if (widget->onScroll(ev))
return true;
}

return false;
}

// -----------------------------------------------------------------------

TopLevelWidget* Widget::PrivateData::findTopLevelWidget(Widget* const pw)
{
if (pw->pData->topLevelWidget != nullptr)
return pw->pData->topLevelWidget;
if (pw->pData->parentWidget != nullptr)
return findTopLevelWidget(pw->pData->parentWidget);
return nullptr;
}

// -----------------------------------------------------------------------

END_NAMESPACE_DGL

+ 21
- 97
source/modules/dgl/src/WidgetPrivateData.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -18,119 +18,43 @@
#define DGL_WIDGET_PRIVATE_DATA_HPP_INCLUDED

#include "../Widget.hpp"
#include "../Window.hpp"

#include <vector>
#include <list>

START_NAMESPACE_DGL

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

struct Widget::PrivateData {
Widget* const self;
Window& parent;
Point<int> absolutePos;
Size<uint> size;
std::vector<Widget*> subWidgets;

TopLevelWidget* const topLevelWidget;
Widget* const parentWidget;
uint id;
bool needsFullViewport;
bool needsScaling;
bool skipDisplay;
bool visible;
Size<uint> size;
std::list<SubWidget*> subWidgets;

PrivateData(Widget* const s, Window& p, Widget* groupWidget, bool addToSubWidgets)
: self(s),
parent(p),
absolutePos(0, 0),
size(0, 0),
subWidgets(),
id(0),
needsFullViewport(false),
needsScaling(false),
skipDisplay(false),
visible(true)
{
if (addToSubWidgets && groupWidget != nullptr)
{
skipDisplay = true;
groupWidget->pData->subWidgets.push_back(self);
}
}

~PrivateData()
{
subWidgets.clear();
}

void display(const uint width, const uint height, const bool renderingSubWidget)
{
if ((skipDisplay && ! renderingSubWidget) || size.isInvalid() || ! visible)
return;

bool needsDisableScissor = false;

// reset color
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);

if (needsFullViewport || (absolutePos.isZero() && size == Size<uint>(width, height)))
{
// full viewport size
glViewport(0, 0, static_cast<GLsizei>(width), static_cast<GLsizei>(height));
}
else if (needsScaling)
{
// limit viewport to widget bounds
glViewport(absolutePos.getX(),
static_cast<int>(height - self->getHeight()) - absolutePos.getY(),
static_cast<GLsizei>(self->getWidth()),
static_cast<GLsizei>(self->getHeight()));
}
else
{
// only set viewport pos
glViewport(absolutePos.getX(),
/*static_cast<int>(height - self->getHeight())*/ - absolutePos.getY(),
static_cast<GLsizei>(width),
static_cast<GLsizei>(height));

// then cut the outer bounds
glScissor(absolutePos.getX(),
static_cast<int>(height - self->getHeight()) - absolutePos.getY(),
static_cast<GLsizei>(self->getWidth()),
static_cast<GLsizei>(self->getHeight()));

glEnable(GL_SCISSOR_TEST);
needsDisableScissor = true;
}

// display widget
self->onDisplay();

if (needsDisableScissor)
{
glDisable(GL_SCISSOR_TEST);
needsDisableScissor = false;
}
// called via TopLevelWidget
explicit PrivateData(Widget* const s, TopLevelWidget* const tlw);
// called via SubWidget
explicit PrivateData(Widget* const s, Widget* const pw);
~PrivateData();

displaySubWidgets(width, height);
}
void displaySubWidgets(uint width, uint height, double autoScaleFactor);

void displaySubWidgets(const uint width, const uint height)
{
for (std::vector<Widget*>::iterator it = subWidgets.begin(); it != subWidgets.end(); ++it)
{
Widget* const widget(*it);
DISTRHO_SAFE_ASSERT_CONTINUE(widget->pData != this);
bool giveKeyboardEventForSubWidgets(const KeyboardEvent& ev);
bool giveCharacterInputEventForSubWidgets(const CharacterInputEvent& ev);
bool giveMouseEventForSubWidgets(MouseEvent& ev);
bool giveMotionEventForSubWidgets(MotionEvent& ev);
bool giveScrollEventForSubWidgets(ScrollEvent& ev);

widget->pData->display(width, height, true);
}
}
static TopLevelWidget* findTopLevelWidget(Widget* const w);

DISTRHO_DECLARE_NON_COPY_STRUCT(PrivateData)
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData)
};

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL



+ 321
- 1230
source/modules/dgl/src/Window.cpp
File diff suppressed because it is too large
View File


+ 1079
- 0
source/modules/dgl/src/WindowPrivateData.cpp
File diff suppressed because it is too large
View File


+ 199
- 0
source/modules/dgl/src/WindowPrivateData.hpp View File

@@ -0,0 +1,199 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef DGL_WINDOW_PRIVATE_DATA_HPP_INCLUDED
#define DGL_WINDOW_PRIVATE_DATA_HPP_INCLUDED

#include "../Window.hpp"
#include "../Widget.hpp"
#include "ApplicationPrivateData.hpp"

#include "pugl.hpp"

#include <list>

START_NAMESPACE_DGL

class TopLevelWidget;

// -----------------------------------------------------------------------

struct Window::PrivateData : IdleCallback {
/** Reference to the DGL Application class this (private data) window associates with. */
Application& app;

/** Direct access to the DGL Application private data where we registers ourselves in. */
Application::PrivateData* const appData;

/** Pointer to the the DGL Window class that this private data belongs to. */
Window* const self;

/** Pugl view instance. */
PuglView* view;

/** Pugl view instance of the transient parent window. */
PuglView* const transientParentView;

/** Reserved space for graphics context. */
mutable uint8_t graphicsContext[sizeof(void*)];

/** The top-level widgets associated with this Window. */
std::list<TopLevelWidget*> topLevelWidgets;

/** Whether this Window is closed (not visible or counted in the Application it is tied to).
Defaults to true unless embed (embed windows are never closed). */
bool isClosed;

/** Whether this Window is currently visible/mapped. Defaults to false. */
bool isVisible;

/** Whether this Window is embed into another (usually not DGL-controlled) Window. */
const bool isEmbed;

/** Whether to ignore resize requests and feed them into the host instead. used for VST3 */
const bool usesSizeRequest;

/** Scale factor to report to widgets on request, purely informational. */
double scaleFactor;

/** Automatic scaling to apply on widgets, implemented internally. */
bool autoScaling;
double autoScaleFactor;

/** Pugl geometry constraints access. */
uint minWidth, minHeight;
bool keepAspectRatio;

/** Whether to ignore idle callback requests, useful for temporary windows. */
bool ignoreIdleCallbacks;

/** Whether to ignore pugl events (except create and destroy), used for puglGetClipboard. */
bool ignoreEvents;

/** Render to a picture file when non-null, automatically free+unset after saving. */
char* filenameToRenderInto;

#ifndef DGL_FILE_BROWSER_DISABLED
/** Handle for file browser dialog operations. */
FileBrowserHandle fileBrowserHandle;
#endif

/** Modal window setup. */
struct Modal {
PrivateData* parent; // parent of this window (so we can become modal)
PrivateData* child; // child window to give focus to when modal mode is enabled
bool enabled; // wherever modal mode is enabled (only possible if parent != null)

/** Constructor for a non-modal window. */
Modal() noexcept
: parent(nullptr),
child(nullptr),
enabled(false) {}

/** Constructor for a modal window (with a parent). */
Modal(PrivateData* const p) noexcept
: parent(p),
child(nullptr),
enabled(false) {}

/** Destructor. */
~Modal() noexcept
{
DISTRHO_SAFE_ASSERT(! enabled);
}

DISTRHO_DECLARE_NON_COPYABLE(Modal)
DISTRHO_PREVENT_HEAP_ALLOCATION
} modal;

/** Constructor for a regular, standalone window. */
explicit PrivateData(Application& app, Window* self);

/** Constructor for a modal window. */
explicit PrivateData(Application& app, Window* self, PrivateData* ppData);

/** Constructor for an embed Window, with a few extra hints from the host side. */
explicit PrivateData(Application& app, Window* self, uintptr_t parentWindowHandle, double scaling, bool resizable);

/** Constructor for an embed Window, with a few extra hints from the host side. */
explicit PrivateData(Application& app, Window* self, uintptr_t parentWindowHandle,
uint width, uint height, double scaling, bool resizable, bool isVST3);

/** Destructor. */
~PrivateData() override;

/** Helper initialization function called at the end of all this class constructors. */
void initPre(uint width, uint height, bool resizable);
/** Helper initialization function called on the Window constructor after we are done. */
bool initPost();

/** Hide window and notify application of a window close event.
* Does nothing if window is embed (that is, not standalone).
* The application event-loop will stop when all windows have been closed.
*
* @note It is possible to hide the window while not stopping the event-loop.
* A closed window is always hidden, but the reverse is not always true.
*/
void close();

void show();
void hide();

void focus();

void setResizable(bool resizable);

const GraphicsContext& getGraphicsContext() const noexcept;

// idle callback stuff
void idleCallback() override;
bool addIdleCallback(IdleCallback* callback, uint timerFrequencyInMs);
bool removeIdleCallback(IdleCallback* callback);

#ifndef DGL_FILE_BROWSER_DISABLED
// file handling
bool openFileBrowser(const FileBrowserOptions& options);
#endif

static void renderToPicture(const char* filename, const GraphicsContext& context, uint width, uint height);

// modal handling
void startModal();
void stopModal();
void runAsModal(bool blockWait);

// pugl events
void onPuglConfigure(double width, double height);
void onPuglExpose();
void onPuglClose();
void onPuglFocus(bool focus, CrossingMode mode);
void onPuglKey(const Widget::KeyboardEvent& ev);
void onPuglText(const Widget::CharacterInputEvent& ev);
void onPuglMouse(const Widget::MouseEvent& ev);
void onPuglMotion(const Widget::MotionEvent& ev);
void onPuglScroll(const Widget::ScrollEvent& ev);

// Pugl event handling entry point
static PuglStatus puglEventCallback(PuglView* view, const PuglEvent* event);

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData)
};

// -----------------------------------------------------------------------

END_NAMESPACE_DGL

#endif // DGL_WINDOW_PRIVATE_DATA_HPP_INCLUDED

+ 312
- 187
source/modules/dgl/src/nanovg/fontstash.h View File

@@ -38,10 +38,15 @@ enum FONSalign {
FONS_ALIGN_BASELINE = 1<<6, // Default
};

enum FONSglyphBitmap {
FONS_GLYPH_BITMAP_OPTIONAL = 1,
FONS_GLYPH_BITMAP_REQUIRED = 2,
};

enum FONSerrorCode {
// Font atlas is full.
FONS_ATLAS_FULL = 1,
// Scratch memory used to render glyphs is full, requested size reported in 'val', you may need to bump up FONS_SCRATCH_BUF_SIZE.
// Scratch memory used to render glyphs is full, requested size reported in 'val', you may need to bump up FONS_SCRATCH_BUF_SIZE.
FONS_SCRATCH_FULL = 2,
// Calls to fonsPushState has created too large stack, if you need deep state stack bump up FONS_MAX_STATES.
FONS_STATES_OVERFLOW = 3,
@@ -78,6 +83,7 @@ struct FONStextIter {
const char* next;
const char* end;
unsigned int utf8state;
int bitmapOption;
};
typedef struct FONStextIter FONStextIter;

@@ -90,14 +96,14 @@ void fonsDeleteInternal(FONScontext* s);
void fonsSetErrorCallback(FONScontext* s, void (*callback)(void* uptr, int error, int val), void* uptr);
// Returns current atlas size.
void fonsGetAtlasSize(FONScontext* s, int* width, int* height);
// Expands the atlas size.
// Expands the atlas size.
int fonsExpandAtlas(FONScontext* s, int width, int height);
// Resets the whole stash.
int fonsResetAtlas(FONScontext* stash, int width, int height);

// Add fonts
int fonsAddFont(FONScontext* s, const char* name, const char* path);
int fonsAddFontMem(FONScontext* s, const char* name, const unsigned char* data, int ndata, int freeData);
int fonsAddFont(FONScontext* s, const char* name, const char* path, int fontIndex);
int fonsAddFontMem(FONScontext* s, const char* name, unsigned char* data, int ndata, int freeData, int fontIndex);
int fonsGetFontByName(FONScontext* s, const char* name);

// State handling
@@ -122,7 +128,7 @@ void fonsLineBounds(FONScontext* s, float y, float* miny, float* maxy);
void fonsVertMetrics(FONScontext* s, float* ascender, float* descender, float* lineh);

// Text iterator
int fonsTextIterInit(FONScontext* stash, FONStextIter* iter, float x, float y, const char* str, const char* end);
int fonsTextIterInit(FONScontext* stash, FONStextIter* iter, float x, float y, const char* str, const char* end, int bitmapOption);
int fonsTextIterNext(FONScontext* stash, FONStextIter* iter, struct FONSquad* quad);

// Pull texture changes
@@ -151,91 +157,6 @@ struct FONSttFontImpl {
};
typedef struct FONSttFontImpl FONSttFontImpl;

static FT_Library ftLibrary;

int fons__tt_init(FONScontext *context)
{
FT_Error ftError;
FONS_NOTUSED(context);
ftError = FT_Init_FreeType(&ftLibrary);
return ftError == 0;
}

int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, const unsigned char *data, int dataSize)
{
FT_Error ftError;
FONS_NOTUSED(context);

//font->font.userdata = stash;
ftError = FT_New_Memory_Face(ftLibrary, (const FT_Byte*)data, dataSize, 0, &font->font);
return ftError == 0;
}

void fons__tt_getFontVMetrics(FONSttFontImpl *font, int *ascent, int *descent, int *lineGap)
{
*ascent = font->font->ascender;
*descent = font->font->descender;
*lineGap = font->font->height - (*ascent - *descent);
}

float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size)
{
return size / (font->font->ascender - font->font->descender);
}

int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint)
{
return FT_Get_Char_Index(font->font, codepoint);
}

int fons__tt_buildGlyphBitmap(FONSttFontImpl *font, int glyph, float size, float scale,
int *advance, int *lsb, int *x0, int *y0, int *x1, int *y1)
{
FT_Error ftError;
FT_GlyphSlot ftGlyph;
FONS_NOTUSED(scale);

ftError = FT_Set_Pixel_Sizes(font->font, 0, (FT_UInt)(size * (float)font->font->units_per_EM / (float)(font->font->ascender - font->font->descender)));
if (ftError) return 0;
ftError = FT_Load_Glyph(font->font, glyph, FT_LOAD_RENDER);
if (ftError) return 0;
ftError = FT_Get_Advance(font->font, glyph, FT_LOAD_NO_SCALE, (FT_Fixed*)advance);
if (ftError) return 0;
ftGlyph = font->font->glyph;
*lsb = ftGlyph->metrics.horiBearingX;
*x0 = ftGlyph->bitmap_left;
*x1 = *x0 + ftGlyph->bitmap.width;
*y0 = -ftGlyph->bitmap_top;
*y1 = *y0 + ftGlyph->bitmap.rows;
return 1;
}

void fons__tt_renderGlyphBitmap(FONSttFontImpl *font, unsigned char *output, int outWidth, int outHeight, int outStride,
float scaleX, float scaleY, int glyph)
{
FT_GlyphSlot ftGlyph = font->font->glyph;
int ftGlyphOffset = 0;
int x, y;
FONS_NOTUSED(outWidth);
FONS_NOTUSED(outHeight);
FONS_NOTUSED(scaleX);
FONS_NOTUSED(scaleY);
FONS_NOTUSED(glyph); // glyph has already been loaded by fons__tt_buildGlyphBitmap

for ( y = 0; y < ftGlyph->bitmap.rows; y++ ) {
for ( x = 0; x < ftGlyph->bitmap.width; x++ ) {
output[(y * outStride) + x] = ftGlyph->bitmap.buffer[ftGlyphOffset++];
}
}
}

int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2)
{
FT_Vector ftKerning;
FT_Get_Kerning(font->font, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning);
return ftKerning.x;
}

#else

#define STB_TRUETYPE_IMPLEMENTATION
@@ -250,61 +171,10 @@ struct FONSttFontImpl {
};
typedef struct FONSttFontImpl FONSttFontImpl;

int fons__tt_init(FONScontext *context)
{
FONS_NOTUSED(context);
return 1;
}

int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, const unsigned char *data, int dataSize)
{
int stbError;
FONS_NOTUSED(dataSize);

font->font.userdata = context;
stbError = stbtt_InitFont(&font->font, data, 0);
return stbError;
}

void fons__tt_getFontVMetrics(FONSttFontImpl *font, int *ascent, int *descent, int *lineGap)
{
stbtt_GetFontVMetrics(&font->font, ascent, descent, lineGap);
}

float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size)
{
return stbtt_ScaleForPixelHeight(&font->font, size);
}

int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint)
{
return stbtt_FindGlyphIndex(&font->font, codepoint);
}

int fons__tt_buildGlyphBitmap(FONSttFontImpl *font, int glyph, float size, float scale,
int *advance, int *lsb, int *x0, int *y0, int *x1, int *y1)
{
FONS_NOTUSED(size);
stbtt_GetGlyphHMetrics(&font->font, glyph, advance, lsb);
stbtt_GetGlyphBitmapBox(&font->font, glyph, scale, scale, x0, y0, x1, y1);
return 1;
}

void fons__tt_renderGlyphBitmap(FONSttFontImpl *font, unsigned char *output, int outWidth, int outHeight, int outStride,
float scaleX, float scaleY, int glyph)
{
stbtt_MakeGlyphBitmap(&font->font, output, outWidth, outHeight, outStride, scaleX, scaleY, glyph);
}

int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2)
{
return stbtt_GetGlyphKernAdvance(&font->font, glyph1, glyph2);
}

#endif

#ifndef FONS_SCRATCH_BUF_SIZE
# define FONS_SCRATCH_BUF_SIZE 16000
# define FONS_SCRATCH_BUF_SIZE 96000
#endif
#ifndef FONS_HASH_LUT_SIZE
# define FONS_HASH_LUT_SIZE 256
@@ -324,6 +194,9 @@ int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2)
#ifndef FONS_MAX_STATES
# define FONS_MAX_STATES 20
#endif
#ifndef FONS_MAX_FALLBACKS
# define FONS_MAX_FALLBACKS 20
#endif

static unsigned int fons__hashint(unsigned int a)
{
@@ -361,7 +234,7 @@ struct FONSfont
{
FONSttFontImpl font;
char name[64];
const unsigned char* data;
unsigned char* data;
int dataSize;
unsigned char freeData;
float ascender;
@@ -371,6 +244,8 @@ struct FONSfont
int cglyphs;
int nglyphs;
int lut[FONS_HASH_LUT_SIZE];
int fallbacks[FONS_MAX_FALLBACKS];
int nfallbacks;
};
typedef struct FONSfont FONSfont;

@@ -419,8 +294,191 @@ struct FONScontext
int nstates;
void (*handleError)(void* uptr, int error, int val);
void* errorUptr;
#ifdef FONS_USE_FREETYPE
FT_Library ftLibrary;
#endif
};

#ifdef FONS_USE_FREETYPE

int fons__tt_init(FONScontext *context)
{
FT_Error ftError;
ftError = FT_Init_FreeType(&context->ftLibrary);
return ftError == 0;
}

int fons__tt_done(FONScontext *context)
{
FT_Error ftError;
ftError = FT_Done_FreeType(context->ftLibrary);
return ftError == 0;
}

int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, unsigned char *data, int dataSize, int fontIndex)
{
FT_Error ftError;

//font->font.userdata = stash;
ftError = FT_New_Memory_Face(context->ftLibrary, (const FT_Byte*)data, dataSize, fontIndex, &font->font);
return ftError == 0;
}

void fons__tt_getFontVMetrics(FONSttFontImpl *font, int *ascent, int *descent, int *lineGap)
{
*ascent = font->font->ascender;
*descent = font->font->descender;
*lineGap = font->font->height - (*ascent - *descent);
}

float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size)
{
#if 1
// Note(DPF) maintain pixel-based units for compat after nanovg update
return size / (font->font->ascender - font->font->descender);
#else
return size / font->font->units_per_EM;
#endif
}

int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint)
{
return FT_Get_Char_Index(font->font, codepoint);
}

int fons__tt_buildGlyphBitmap(FONSttFontImpl *font, int glyph, float size, float scale,
int *advance, int *lsb, int *x0, int *y0, int *x1, int *y1)
{
FT_Error ftError;
FT_GlyphSlot ftGlyph;
FT_Fixed advFixed;
FONS_NOTUSED(scale);

#if 1
// Note(DPF) maintain pixel-based units for compat after nanovg update
ftError = FT_Set_Pixel_Sizes(font->font, 0, (FT_UInt)(size * (float)font->font->units_per_EM / (float)(font->font->ascender - font->font->descender)));
#else
ftError = FT_Set_Pixel_Sizes(font->font, 0, size);
#endif
if (ftError) return 0;
#if 1
// Note(DPF) maintain pixel-based units for compat after nanovg update
ftError = FT_Load_Glyph(font->font, glyph, FT_LOAD_RENDER);
#else
ftError = FT_Load_Glyph(font->font, glyph, FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT);
#endif
if (ftError) return 0;
ftError = FT_Get_Advance(font->font, glyph, FT_LOAD_NO_SCALE, &advFixed);
if (ftError) return 0;
ftGlyph = font->font->glyph;
*advance = (int)advFixed;
*lsb = (int)ftGlyph->metrics.horiBearingX;
*x0 = ftGlyph->bitmap_left;
*x1 = *x0 + ftGlyph->bitmap.width;
*y0 = -ftGlyph->bitmap_top;
*y1 = *y0 + ftGlyph->bitmap.rows;
return 1;
}

void fons__tt_renderGlyphBitmap(FONSttFontImpl *font, unsigned char *output, int outWidth, int outHeight, int outStride,
float scaleX, float scaleY, int glyph)
{
FT_GlyphSlot ftGlyph = font->font->glyph;
int ftGlyphOffset = 0;
unsigned int x, y;
FONS_NOTUSED(outWidth);
FONS_NOTUSED(outHeight);
FONS_NOTUSED(scaleX);
FONS_NOTUSED(scaleY);
FONS_NOTUSED(glyph); // glyph has already been loaded by fons__tt_buildGlyphBitmap

for ( y = 0; y < ftGlyph->bitmap.rows; y++ ) {
for ( x = 0; x < ftGlyph->bitmap.width; x++ ) {
output[(y * outStride) + x] = ftGlyph->bitmap.buffer[ftGlyphOffset++];
}
}
}

int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2)
{
FT_Vector ftKerning;
FT_Get_Kerning(font->font, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning);
return (int)((ftKerning.x + 32) >> 6); // Round up and convert to integer
}

#else

int fons__tt_init(FONScontext *context)
{
FONS_NOTUSED(context);
return 1;
}

int fons__tt_done(FONScontext *context)
{
FONS_NOTUSED(context);
return 1;
}

int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, unsigned char *data, int dataSize, int fontIndex)
{
int offset, stbError;
FONS_NOTUSED(dataSize);

font->font.userdata = context;
offset = stbtt_GetFontOffsetForIndex(data, fontIndex);
if (offset == -1) {
stbError = 0;
} else {
stbError = stbtt_InitFont(&font->font, data, offset);
}
return stbError;
}

void fons__tt_getFontVMetrics(FONSttFontImpl *font, int *ascent, int *descent, int *lineGap)
{
stbtt_GetFontVMetrics(&font->font, ascent, descent, lineGap);
}

float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size)
{
#if 1
// Note(DPF) maintain pixel-based units for compat after nanovg update
return stbtt_ScaleForPixelHeight(&font->font, size);
#else
return stbtt_ScaleForMappingEmToPixels(&font->font, size);
#endif
}

int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint)
{
return stbtt_FindGlyphIndex(&font->font, codepoint);
}

int fons__tt_buildGlyphBitmap(FONSttFontImpl *font, int glyph, float size, float scale,
int *advance, int *lsb, int *x0, int *y0, int *x1, int *y1)
{
FONS_NOTUSED(size);
stbtt_GetGlyphHMetrics(&font->font, glyph, advance, lsb);
stbtt_GetGlyphBitmapBox(&font->font, glyph, scale, scale, x0, y0, x1, y1);
return 1;
}

void fons__tt_renderGlyphBitmap(FONSttFontImpl *font, unsigned char *output, int outWidth, int outHeight, int outStride,
float scaleX, float scaleY, int glyph)
{
stbtt_MakeGlyphBitmap(&font->font, output, outWidth, outHeight, outStride, scaleX, scaleY, glyph);
}

int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2)
{
return stbtt_GetGlyphKernAdvance(&font->font, glyph1, glyph2);
}

#endif

#ifdef STB_TRUETYPE_IMPLEMENTATION

static void* fons__tmpalloc(size_t size, void* up)
{
unsigned char* ptr;
@@ -446,6 +504,8 @@ static void fons__tmpfree(void* ptr, void* up)
// empty
}

#endif // STB_TRUETYPE_IMPLEMENTATION

// Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de>
// See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.

@@ -751,6 +811,27 @@ static FONSstate* fons__getState(FONScontext* stash)
return &stash->states[stash->nstates-1];
}

int fonsAddFallbackFont(FONScontext* stash, int base, int fallback)
{
FONSfont* baseFont = stash->fonts[base];
if (baseFont->nfallbacks < FONS_MAX_FALLBACKS) {
baseFont->fallbacks[baseFont->nfallbacks++] = fallback;
return 1;
}
return 0;
}

void fonsResetFallbackFont(FONScontext* stash, int base)
{
int i;

FONSfont* baseFont = stash->fonts[base];
baseFont->nfallbacks = 0;
baseFont->nglyphs = 0;
for (i = 0; i < FONS_HASH_LUT_SIZE; i++)
baseFont->lut[i] = -1;
}

void fonsSetSize(FONScontext* stash, float size)
{
fons__getState(stash)->size = size;
@@ -818,7 +899,7 @@ static void fons__freeFont(FONSfont* font)
{
if (font == NULL) return;
if (font->glyphs) free(font->glyphs);
if (font->freeData && font->data) free((void*)font->data);
if (font->freeData && font->data) free(font->data);
free(font);
}

@@ -830,6 +911,8 @@ static int fons__allocFont(FONScontext* stash)
stash->fonts = (FONSfont**)realloc(stash->fonts, sizeof(FONSfont*) * stash->cfonts);
if (stash->fonts == NULL)
return -1;
for (int i=stash->nfonts; i<stash->cfonts; ++i)
stash->fonts[i] = NULL;
}
font = (FONSfont*)malloc(sizeof(FONSfont));
if (font == NULL) goto error;
@@ -849,12 +932,12 @@ error:
return FONS_INVALID;
}

int fonsAddFont(FONScontext* stash, const char* name, const char* path)
int fonsAddFont(FONScontext* stash, const char* name, const char* path, int fontIndex)
{
FILE* fp = 0;
int dataSize = 0;
size_t readed;
unsigned char* data = NULL;
size_t ignore;

// Read in the font data.
fp = fopen(path, "rb");
@@ -864,28 +947,30 @@ int fonsAddFont(FONScontext* stash, const char* name, const char* path)
fseek(fp,0,SEEK_SET);
data = (unsigned char*)malloc(dataSize);
if (data == NULL) goto error;
ignore = fread(data, 1, dataSize, fp);
readed = fread(data, 1, dataSize, fp);
fclose(fp);
fp = 0;
if (readed != (size_t)dataSize) goto error;

return fonsAddFontMem(stash, name, data, dataSize, 1);
return fonsAddFontMem(stash, name, data, dataSize, 1, fontIndex);

error:
if (data) free(data);
if (fp) fclose(fp);
return FONS_INVALID;

FONS_NOTUSED(ignore);
}

int fonsAddFontMem(FONScontext* stash, const char* name, const unsigned char* data, int dataSize, int freeData)
int fonsAddFontMem(FONScontext* stash, const char* name, unsigned char* data, int dataSize, int freeData, int fontIndex)
{
int i, ascent, descent, fh, lineGap;
FONSfont* font;

int idx = fons__allocFont(stash);
if (idx == FONS_INVALID)
{
if (freeData && data) free(data);
return FONS_INVALID;
}

font = stash->fonts[idx];

@@ -903,15 +988,16 @@ int fonsAddFontMem(FONScontext* stash, const char* name, const unsigned char* da

// Init font
stash->nscratch = 0;
if (!fons__tt_loadFont(stash, &font->font, data, dataSize)) goto error;
if (!fons__tt_loadFont(stash, &font->font, data, dataSize, fontIndex)) goto error;

// Store normalized line height. The real line height is got
// by multiplying the lineh by font size.
fons__tt_getFontVMetrics( &font->font, &ascent, &descent, &lineGap);
ascent += lineGap;
fh = ascent - descent;
font->ascender = (float)ascent / (float)fh;
font->descender = (float)descent / (float)fh;
font->lineh = (float)(fh + lineGap) / (float)fh;
font->lineh = font->ascender - font->descender;

return idx;

@@ -938,6 +1024,8 @@ static FONSglyph* fons__allocGlyph(FONSfont* font)
font->cglyphs = font->cglyphs == 0 ? 8 : font->cglyphs * 2;
font->glyphs = (FONSglyph*)realloc(font->glyphs, sizeof(FONSglyph) * font->cglyphs);
if (font->glyphs == NULL) return NULL;
for (int i=font->nglyphs; i<font->cglyphs; ++i)
memset(&font->glyphs[i], 0, sizeof(*font->glyphs));
}
font->nglyphs++;
return &font->glyphs[font->nglyphs-1];
@@ -1010,7 +1098,7 @@ static void fons__blur(FONScontext* stash, unsigned char* dst, int w, int h, int
}

static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned int codepoint,
short isize, short iblur)
short isize, short iblur, int bitmapOption)
{
int i, g, advance, lsb, x0, y0, x1, y1, gw, gh, gx, gy, x, y;
float scale;
@@ -1020,6 +1108,7 @@ static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned in
int pad, added;
unsigned char* bdst;
unsigned char* dst;
FONSfont* renderFont = font;

if (isize < 2) return NULL;
if (iblur > 20) iblur = 20;
@@ -1032,32 +1121,66 @@ static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned in
h = fons__hashint(codepoint) & (FONS_HASH_LUT_SIZE-1);
i = font->lut[h];
while (i != -1) {
if (font->glyphs[i].codepoint == codepoint && font->glyphs[i].size == isize && font->glyphs[i].blur == iblur)
return &font->glyphs[i];
if (font->glyphs[i].codepoint == codepoint && font->glyphs[i].size == isize && font->glyphs[i].blur == iblur) {
glyph = &font->glyphs[i];
if (bitmapOption == FONS_GLYPH_BITMAP_OPTIONAL || (glyph->x0 >= 0 && glyph->y0 >= 0)) {
return glyph;
}
// At this point, glyph exists but the bitmap data is not yet created.
break;
}
i = font->glyphs[i].next;
}

// Could not find glyph, create it.
scale = fons__tt_getPixelHeightScale(&font->font, size);
// Create a new glyph or rasterize bitmap data for a cached glyph.
g = fons__tt_getGlyphIndex(&font->font, codepoint);
fons__tt_buildGlyphBitmap(&font->font, g, size, scale, &advance, &lsb, &x0, &y0, &x1, &y1);
// Try to find the glyph in fallback fonts.
if (g == 0) {
for (i = 0; i < font->nfallbacks; ++i) {
FONSfont* fallbackFont = stash->fonts[font->fallbacks[i]];
int fallbackIndex = fons__tt_getGlyphIndex(&fallbackFont->font, codepoint);
if (fallbackIndex != 0) {
g = fallbackIndex;
renderFont = fallbackFont;
break;
}
}
// It is possible that we did not find a fallback glyph.
// In that case the glyph index 'g' is 0, and we'll proceed below and cache empty glyph.
}
scale = fons__tt_getPixelHeightScale(&renderFont->font, size);
fons__tt_buildGlyphBitmap(&renderFont->font, g, size, scale, &advance, &lsb, &x0, &y0, &x1, &y1);
gw = x1-x0 + pad*2;
gh = y1-y0 + pad*2;

// Find free spot for the rect in the atlas
added = fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy);
if (added == 0 && stash->handleError != NULL) {
// Atlas is full, let the user to resize the atlas (or not), and try again.
stash->handleError(stash->errorUptr, FONS_ATLAS_FULL, 0);
// Determines the spot to draw glyph in the atlas.
if (bitmapOption == FONS_GLYPH_BITMAP_REQUIRED) {
// Find free spot for the rect in the atlas
added = fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy);
if (added == 0 && stash->handleError != NULL) {
// Atlas is full, let the user to resize the atlas (or not), and try again.
stash->handleError(stash->errorUptr, FONS_ATLAS_FULL, 0);
added = fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy);
}
if (added == 0) return NULL;
} else {
// Negative coordinate indicates there is no bitmap data created.
gx = -1;
gy = -1;
}
if (added == 0) return NULL;

// Init glyph.
glyph = fons__allocGlyph(font);
glyph->codepoint = codepoint;
glyph->size = isize;
glyph->blur = iblur;
if (glyph == NULL) {
glyph = fons__allocGlyph(font);
glyph->codepoint = codepoint;
glyph->size = isize;
glyph->blur = iblur;
glyph->next = 0;

// Insert char to hash lookup.
glyph->next = font->lut[h];
font->lut[h] = font->nglyphs-1;
}
glyph->index = g;
glyph->x0 = (short)gx;
glyph->y0 = (short)gy;
@@ -1066,15 +1189,14 @@ static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned in
glyph->xadv = (short)(scale * advance * 10.0f);
glyph->xoff = (short)(x0 - pad);
glyph->yoff = (short)(y0 - pad);
glyph->next = 0;

// Insert char to hash lookup.
glyph->next = font->lut[h];
font->lut[h] = font->nglyphs-1;
if (bitmapOption == FONS_GLYPH_BITMAP_OPTIONAL) {
return glyph;
}

// Rasterize
dst = &stash->texData[(glyph->x0+pad) + (glyph->y0+pad) * stash->params.width];
fons__tt_renderGlyphBitmap(&font->font, dst, gw-pad*2,gh-pad*2, stash->params.width, scale,scale, g);
fons__tt_renderGlyphBitmap(&renderFont->font, dst, gw-pad*2,gh-pad*2, stash->params.width, scale, scale, g);

// Make sure there is one pixel empty border.
dst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width];
@@ -1101,7 +1223,7 @@ static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned in
if (iblur > 0) {
stash->nscratch = 0;
bdst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width];
fons__blur(stash, bdst, gw,gh, stash->params.width, iblur);
fons__blur(stash, bdst, gw, gh, stash->params.width, iblur);
}

stash->dirtyRect[0] = fons__mini(stash->dirtyRect[0], glyph->x0);
@@ -1134,8 +1256,8 @@ static void fons__getQuad(FONScontext* stash, FONSfont* font,
y1 = (float)(glyph->y1-1);

if (stash->params.flags & FONS_ZERO_TOPLEFT) {
rx = (float)(int)(*x + xoff);
ry = (float)(int)(*y + yoff);
rx = floorf(*x + xoff);
ry = floorf(*y + yoff);

q->x0 = rx;
q->y0 = ry;
@@ -1147,8 +1269,8 @@ static void fons__getQuad(FONScontext* stash, FONSfont* font,
q->s1 = x1 * stash->itw;
q->t1 = y1 * stash->ith;
} else {
rx = (float)(int)(*x + xoff);
ry = (float)(int)(*y - yoff);
rx = floorf(*x + xoff);
ry = floorf(*y - yoff);

q->x0 = rx;
q->y0 = ry;
@@ -1226,7 +1348,7 @@ float fonsDrawText(FONScontext* stash,
const char* str, const char* end)
{
FONSstate* state = fons__getState(stash);
unsigned int codepoint = 0;
unsigned int codepoint;
unsigned int utf8state = 0;
FONSglyph* glyph = NULL;
FONSquad q;
@@ -1263,7 +1385,7 @@ float fonsDrawText(FONScontext* stash,
for (; str != end; ++str) {
if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str))
continue;
glyph = fons__getGlyph(stash, font, codepoint, isize, iblur);
glyph = fons__getGlyph(stash, font, codepoint, isize, iblur, FONS_GLYPH_BITMAP_REQUIRED);
if (glyph != NULL) {
fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q);

@@ -1286,7 +1408,7 @@ float fonsDrawText(FONScontext* stash,
}

int fonsTextIterInit(FONScontext* stash, FONStextIter* iter,
float x, float y, const char* str, const char* end)
float x, float y, const char* str, const char* end, int bitmapOption)
{
FONSstate* state = fons__getState(stash);
float width;
@@ -1326,6 +1448,7 @@ int fonsTextIterInit(FONScontext* stash, FONStextIter* iter,
iter->end = end;
iter->codepoint = 0;
iter->prevGlyphIndex = -1;
iter->bitmapOption = bitmapOption;

return 1;
}
@@ -1346,7 +1469,8 @@ int fonsTextIterNext(FONScontext* stash, FONStextIter* iter, FONSquad* quad)
// Get glyph and quad
iter->x = iter->nextx;
iter->y = iter->nexty;
glyph = fons__getGlyph(stash, iter->font, iter->codepoint, iter->isize, iter->iblur);
glyph = fons__getGlyph(stash, iter->font, iter->codepoint, iter->isize, iter->iblur, iter->bitmapOption);
// If the iterator was initialized with FONS_GLYPH_BITMAP_OPTIONAL, then the UV coordinates of the quad will be invalid.
if (glyph != NULL)
fons__getQuad(stash, iter->font, iter->prevGlyphIndex, glyph, iter->scale, iter->spacing, &iter->nextx, &iter->nexty, quad);
iter->prevGlyphIndex = glyph != NULL ? glyph->index : -1;
@@ -1406,12 +1530,12 @@ void fonsDrawDebug(FONScontext* stash, float x, float y)
}

float fonsTextBounds(FONScontext* stash,
float x, float y,
float x, float y,
const char* str, const char* end,
float* bounds)
{
FONSstate* state = fons__getState(stash);
unsigned int codepoint = 0;
unsigned int codepoint;
unsigned int utf8state = 0;
FONSquad q;
FONSglyph* glyph = NULL;
@@ -1443,7 +1567,7 @@ float fonsTextBounds(FONScontext* stash,
for (; str != end; ++str) {
if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str))
continue;
glyph = fons__getGlyph(stash, font, codepoint, isize, iblur);
glyph = fons__getGlyph(stash, font, codepoint, isize, iblur, FONS_GLYPH_BITMAP_OPTIONAL);
if (glyph != NULL) {
fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q);
if (q.x0 < minx) minx = q.x0;
@@ -1567,6 +1691,7 @@ void fonsDeleteInternal(FONScontext* stash)
if (stash->fonts) free(stash->fonts);
if (stash->texData) free(stash->texData);
if (stash->scratch) free(stash->scratch);
fons__tt_done(stash);
free(stash);
}

@@ -1594,7 +1719,7 @@ int fonsExpandAtlas(FONScontext* stash, int width, int height)
height = fons__maxi(height, stash->params.height);

if (width == stash->params.width && height == stash->params.height)
return 1;
return 1;

// Flush pending glyphs.
fons__flush(stash);


+ 467
- 200
source/modules/dgl/src/nanovg/nanovg.c
File diff suppressed because it is too large
View File


+ 114
- 26
source/modules/dgl/src/nanovg/nanovg.h View File

@@ -79,10 +79,46 @@ enum NVGalign {
// Vertical align
NVG_ALIGN_TOP = 1<<3, // Align text vertically to top.
NVG_ALIGN_MIDDLE = 1<<4, // Align text vertically to middle.
NVG_ALIGN_BOTTOM = 1<<5, // Align text vertically to bottom.
NVG_ALIGN_BASELINE = 1<<6, // Default, align text vertically to baseline.
NVG_ALIGN_BOTTOM = 1<<5, // Align text vertically to bottom.
NVG_ALIGN_BASELINE = 1<<6, // Default, align text vertically to baseline.
};

enum NVGblendFactor {
NVG_ZERO = 1<<0,
NVG_ONE = 1<<1,
NVG_SRC_COLOR = 1<<2,
NVG_ONE_MINUS_SRC_COLOR = 1<<3,
NVG_DST_COLOR = 1<<4,
NVG_ONE_MINUS_DST_COLOR = 1<<5,
NVG_SRC_ALPHA = 1<<6,
NVG_ONE_MINUS_SRC_ALPHA = 1<<7,
NVG_DST_ALPHA = 1<<8,
NVG_ONE_MINUS_DST_ALPHA = 1<<9,
NVG_SRC_ALPHA_SATURATE = 1<<10,
};

enum NVGcompositeOperation {
NVG_SOURCE_OVER,
NVG_SOURCE_IN,
NVG_SOURCE_OUT,
NVG_ATOP,
NVG_DESTINATION_OVER,
NVG_DESTINATION_IN,
NVG_DESTINATION_OUT,
NVG_DESTINATION_ATOP,
NVG_LIGHTER,
NVG_COPY,
NVG_XOR,
};

struct NVGcompositeOperationState {
int srcRGB;
int dstRGB;
int srcAlpha;
int dstAlpha;
};
typedef struct NVGcompositeOperationState NVGcompositeOperationState;

struct NVGglyphPosition {
const char* str; // Position of the glyph in the input string.
float x; // The x-coordinate of the logical glyph position.
@@ -100,11 +136,20 @@ struct NVGtextRow {
typedef struct NVGtextRow NVGtextRow;

enum NVGimageFlags {
NVG_IMAGE_GENERATE_MIPMAPS = 1<<0, // Generate mipmaps during creation of the image.
NVG_IMAGE_GENERATE_MIPMAPS = 1<<0, // Generate mipmaps during creation of the image.
NVG_IMAGE_REPEATX = 1<<1, // Repeat image in X direction.
NVG_IMAGE_REPEATY = 1<<2, // Repeat image in Y direction.
NVG_IMAGE_FLIPY = 1<<3, // Flips (inverses) image in Y direction when rendered.
NVG_IMAGE_PREMULTIPLIED = 1<<4, // Image data has premultiplied alpha.
NVG_IMAGE_PREMULTIPLIED = 1<<4, // Image data has premultiplied alpha.
NVG_IMAGE_NEAREST = 1<<5, // Image interpolation is Nearest instead Linear
};

enum NVGtexture {
NVG_TEXTURE_ALPHA,
NVG_TEXTURE_BGR,
NVG_TEXTURE_BGRA,
NVG_TEXTURE_RGB,
NVG_TEXTURE_RGBA,
};

// Begin drawing a new frame
@@ -115,7 +160,7 @@ enum NVGimageFlags {
// For example, GLFW returns two dimension for an opened window: window size and
// frame buffer size. In that case you would set windowWidth/Height to the window size
// devicePixelRatio to: frameBufferWidth / windowWidth.
void nvgBeginFrame(NVGcontext* ctx, int windowWidth, int windowHeight, float devicePixelRatio);
void nvgBeginFrame(NVGcontext* ctx, float windowWidth, float windowHeight, float devicePixelRatio);

// Cancels drawing the current frame.
void nvgCancelFrame(NVGcontext* ctx);
@@ -123,6 +168,22 @@ void nvgCancelFrame(NVGcontext* ctx);
// Ends drawing flushing remaining render state.
void nvgEndFrame(NVGcontext* ctx);

//
// Composite operation
//
// The composite operations in NanoVG are modeled after HTML Canvas API, and
// the blend func is based on OpenGL (see corresponding manuals for more info).
// The colors in the blending state have premultiplied alpha.

// Sets the composite operation. The op parameter should be one of NVGcompositeOperation.
void nvgGlobalCompositeOperation(NVGcontext* ctx, int op);

// Sets the composite operation with custom pixel arithmetic. The parameters should be one of NVGblendFactor.
void nvgGlobalCompositeBlendFunc(NVGcontext* ctx, int sfactor, int dfactor);

// Sets the composite operation with custom pixel arithmetic for RGB and alpha components separately. The parameters should be one of NVGblendFactor.
void nvgGlobalCompositeBlendFuncSeparate(NVGcontext* ctx, int srcRGB, int dstRGB, int srcAlpha, int dstAlpha);

//
// Color utils
//
@@ -183,7 +244,10 @@ void nvgReset(NVGcontext* ctx);
// Solid color is simply defined as a color value, different kinds of paints can be created
// using nvgLinearGradient(), nvgBoxGradient(), nvgRadialGradient() and nvgImagePattern().
//
// Current render style can be saved and restored using nvgSave() and nvgRestore().
// Current render style can be saved and restored using nvgSave() and nvgRestore().

// Sets whether to draw antialias for nvgStroke() and nvgFill(). It's enabled by default.
void nvgShapeAntiAlias(NVGcontext* ctx, int enabled);

// Sets current stroke style to a solid color.
void nvgStrokeColor(NVGcontext* ctx, NVGcolor color);
@@ -215,6 +279,10 @@ void nvgLineJoin(NVGcontext* ctx, int join);
// Sets the transparency applied to all rendered shapes.
// Already transparent paths will get proportionally more transparent as well.
void nvgGlobalAlpha(NVGcontext* ctx, float alpha);
void nvgGlobalTint(NVGcontext* ctx, NVGcolor tint);
NVGcolor nvgGetGlobalTint(NVGcontext* ctx);
void nvgAlpha(NVGcontext* ctx, float alpha);
void nvgTint(NVGcontext* ctx, NVGcolor tint);

//
// Transforms
@@ -231,7 +299,7 @@ void nvgGlobalAlpha(NVGcontext* ctx, float alpha);
// Apart from nvgResetTransform(), each transformation function first creates
// specific transformation matrix and pre-multiplies the current transformation by it.
//
// Current coordinate system (transformation) can be saved and restored using nvgSave() and nvgRestore().
// Current coordinate system (transformation) can be saved and restored using nvgSave() and nvgRestore().

// Resets current transform to a identity matrix.
void nvgResetTransform(NVGcontext* ctx);
@@ -317,7 +385,11 @@ int nvgCreateImage(NVGcontext* ctx, const char* filename, int imageFlags);

// Creates image by loading it from the specified chunk of memory.
// Returns handle to the image.
int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, const unsigned char* data, int ndata);
int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, unsigned char* data, int ndata);

// Creates image from specified image data and texture format.
// Returns handle to the image.
int nvgCreateImageRaw(NVGcontext* ctx, int w, int h, int imageFlags, enum NVGtexture format, const unsigned char* data);

// Creates image from specified image data.
// Returns handle to the image.
@@ -358,7 +430,7 @@ NVGpaint nvgBoxGradient(NVGcontext* ctx, float x, float y, float w, float h,
NVGpaint nvgRadialGradient(NVGcontext* ctx, float cx, float cy, float inr, float outr,
NVGcolor icol, NVGcolor ocol);

// Creates and returns an image patter. Parameters (ox,oy) specify the left-top location of the image pattern,
// Creates and returns an image pattern. Parameters (ox,oy) specify the left-top location of the image pattern,
// (ex,ey) the size of one image, angle rotation around the top-left corner, image is handle to the image to render.
// The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint().
NVGpaint nvgImagePattern(NVGcontext* ctx, float ox, float oy, float ex, float ey,
@@ -368,7 +440,7 @@ NVGpaint nvgImagePattern(NVGcontext* ctx, float ox, float oy, float ex, float ey
// Scissoring
//
// Scissoring allows you to clip the rendering into a rectangle. This is useful for various
// user interface cases like rendering a text edit or a timeline.
// user interface cases like rendering a text edit or a timeline.

// Sets the current scissor rectangle.
// The scissor rectangle is transformed by the current transform.
@@ -423,7 +495,7 @@ void nvgArcTo(NVGcontext* ctx, float x1, float y1, float x2, float y2, float rad
// Closes current sub-path with a line segment.
void nvgClosePath(NVGcontext* ctx);

// Sets the current sub-path winding, see NVGwinding and NVGsolidity.
// Sets the current sub-path winding, see NVGwinding and NVGsolidity.
void nvgPathWinding(NVGcontext* ctx, int dir);

// Creates new circle arc shaped sub-path. The arc center is at cx,cy, the arc radius is r,
@@ -437,10 +509,13 @@ void nvgRect(NVGcontext* ctx, float x, float y, float w, float h);
// Creates new rounded rectangle shaped sub-path.
void nvgRoundedRect(NVGcontext* ctx, float x, float y, float w, float h, float r);

// Creates new rounded rectangle shaped sub-path with varying radii for each corner.
void nvgRoundedRectVarying(NVGcontext* ctx, float x, float y, float w, float h, float radTopLeft, float radTopRight, float radBottomRight, float radBottomLeft);

// Creates new ellipse shaped sub-path.
void nvgEllipse(NVGcontext* ctx, float cx, float cy, float rx, float ry);

// Creates new circle shaped sub-path.
// Creates new circle shaped sub-path.
void nvgCircle(NVGcontext* ctx, float cx, float cy, float r);

// Fills the current path with current fill style.
@@ -487,13 +562,31 @@ void nvgStroke(NVGcontext* ctx);
// Returns handle to the font.
int nvgCreateFont(NVGcontext* ctx, const char* name, const char* filename);

// Creates image by loading it from the specified memory chunk.
// fontIndex specifies which font face to load from a .ttf/.ttc file.
int nvgCreateFontAtIndex(NVGcontext* ctx, const char* name, const char* filename, const int fontIndex);

// Creates font by loading it from the specified memory chunk.
// Returns handle to the font.
int nvgCreateFontMem(NVGcontext* ctx, const char* name, const unsigned char* data, int ndata, int freeData);
int nvgCreateFontMem(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData);

// fontIndex specifies which font face to load from a .ttf/.ttc file.
int nvgCreateFontMemAtIndex(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData, const int fontIndex);

// Finds a loaded font of specified name, and returns handle to it, or -1 if the font is not found.
int nvgFindFont(NVGcontext* ctx, const char* name);

// Adds a fallback font by handle.
int nvgAddFallbackFontId(NVGcontext* ctx, int baseFont, int fallbackFont);

// Adds a fallback font by name.
int nvgAddFallbackFont(NVGcontext* ctx, const char* baseFont, const char* fallbackFont);

// Resets fallback fonts by handle.
void nvgResetFallbackFontsId(NVGcontext* ctx, int baseFont);

// Resets fallback fonts by name.
void nvgResetFallbackFonts(NVGcontext* ctx, const char* baseFont);

// Sets the font size of current text style.
void nvgFontSize(NVGcontext* ctx, float size);

@@ -503,7 +596,7 @@ void nvgFontBlur(NVGcontext* ctx, float blur);
// Sets the letter spacing of current text style.
void nvgTextLetterSpacing(NVGcontext* ctx, float spacing);

// Sets the proportional line height of current text style. The line height is specified as multiple of font size.
// Sets the proportional line height of current text style. The line height is specified as multiple of font size.
void nvgTextLineHeight(NVGcontext* ctx, float lineHeight);

// Sets the text align of current text style, see NVGalign for options.
@@ -550,11 +643,6 @@ int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, floa
//
// Internal Render API
//
enum NVGtexture {
NVG_TEXTURE_ALPHA = 0x01,
NVG_TEXTURE_RGBA = 0x02,
};

struct NVGscissor {
float xform[6];
float extent[2];
@@ -583,23 +671,23 @@ typedef struct NVGpath NVGpath;
struct NVGparams {
void* userPtr;
int edgeAntiAlias;
int (*renderCreate)(void* uptr);
int (*renderCreate)(void* uptr, void* otherUptr);
int (*renderCreateTexture)(void* uptr, int type, int w, int h, int imageFlags, const unsigned char* data);
int (*renderDeleteTexture)(void* uptr, int image);
int (*renderUpdateTexture)(void* uptr, int image, int x, int y, int w, int h, const unsigned char* data);
int (*renderGetTextureSize)(void* uptr, int image, int* w, int* h);
void (*renderViewport)(void* uptr, int width, int height);
void (*renderViewport)(void* uptr, float width, float height, float devicePixelRatio);
void (*renderCancel)(void* uptr);
void (*renderFlush)(void* uptr);
void (*renderFill)(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe, const float* bounds, const NVGpath* paths, int npaths);
void (*renderStroke)(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe, float strokeWidth, const NVGpath* paths, int npaths);
void (*renderTriangles)(void* uptr, NVGpaint* paint, NVGscissor* scissor, const NVGvertex* verts, int nverts);
void (*renderFill)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, const float* bounds, const NVGpath* paths, int npaths);
void (*renderStroke)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, float strokeWidth, const NVGpath* paths, int npaths);
void (*renderTriangles)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, const NVGvertex* verts, int nverts, float fringe);
void (*renderDelete)(void* uptr);
};
typedef struct NVGparams NVGparams;

// Constructor and destructor, called by the render back-end.
NVGcontext* nvgCreateInternal(NVGparams* params);
NVGcontext* nvgCreateInternal(NVGparams* params, NVGcontext* other);
void nvgDeleteInternal(NVGcontext* ctx);

NVGparams* nvgInternalParams(NVGcontext* ctx);


+ 336
- 97
source/modules/dgl/src/nanovg/nanovg_gl.h View File

@@ -57,29 +57,45 @@ enum NVGcreateFlags {
#if defined NANOVG_GL2

NVGcontext* nvgCreateGL2(int flags);
NVGcontext* nvgCreateSharedGL2(NVGcontext* other, int flags);
void nvgDeleteGL2(NVGcontext* ctx);

int nvglCreateImageFromHandleGL2(NVGcontext* ctx, GLuint textureId, int w, int h, int flags);
GLuint nvglImageHandleGL2(NVGcontext* ctx, int image);

#endif

#if defined NANOVG_GL3

NVGcontext* nvgCreateGL3(int flags);
NVGcontext* nvgCreateSharedGL3(NVGcontext* other, int flags);
void nvgDeleteGL3(NVGcontext* ctx);

int nvglCreateImageFromHandleGL3(NVGcontext* ctx, GLuint textureId, int w, int h, int flags);
GLuint nvglImageHandleGL3(NVGcontext* ctx, int image);

#endif

#if defined NANOVG_GLES2

NVGcontext* nvgCreateGLES2(int flags);
NVGcontext* nvgCreateSharedGLES2(NVGcontext* other, int flags);
void nvgDeleteGLES2(NVGcontext* ctx);

int nvglCreateImageFromHandleGLES2(NVGcontext* ctx, GLuint textureId, int w, int h, int flags);
GLuint nvglImageHandleGLES2(NVGcontext* ctx, int image);

#endif

#if defined NANOVG_GLES3

NVGcontext* nvgCreateGLES3(int flags);
NVGcontext* nvgCreateSharedGLES3(NVGcontext* other, int flags);
void nvgDeleteGLES3(NVGcontext* ctx);

int nvglCreateImageFromHandleGLES3(NVGcontext* ctx, GLuint textureId, int w, int h, int flags);
GLuint nvglImageHandleGLES3(NVGcontext* ctx, int image);

#endif

// These are additional flags on top of NVGimageFlags.
@@ -87,10 +103,6 @@ enum NVGimageFlagsGL {
NVG_IMAGE_NODELETE = 1<<16, // Do not delete GL texture handle.
};

int nvglCreateImageFromHandle(NVGcontext* ctx, GLuint textureId, int w, int h, int flags);
GLuint nvglImageHandle(NVGcontext* ctx, int image);


#ifdef __cplusplus
}
#endif
@@ -142,6 +154,15 @@ struct GLNVGtexture {
};
typedef struct GLNVGtexture GLNVGtexture;

struct GLNVGblend
{
GLenum srcRGB;
GLenum dstRGB;
GLenum srcAlpha;
GLenum dstAlpha;
};
typedef struct GLNVGblend GLNVGblend;

enum GLNVGcallType {
GLNVG_NONE = 0,
GLNVG_FILL,
@@ -158,6 +179,7 @@ struct GLNVGcall {
int triangleOffset;
int triangleCount;
int uniformOffset;
GLNVGblend blendFunc;
};
typedef struct GLNVGcall GLNVGcall;

@@ -210,13 +232,19 @@ struct GLNVGfragUniforms {
};
typedef struct GLNVGfragUniforms GLNVGfragUniforms;

struct GLNVGcontext {
GLNVGshader shader;
struct GLNVGtextureContext { // Textures; shared between shared NanoVG contexts.
int refCount;
GLNVGtexture* textures;
float view[2];
int ntextures;
int ctextures;
int textureId;
};
typedef struct GLNVGtextureContext GLNVGtextureContext;

struct GLNVGcontext {
GLNVGshader shader;
GLNVGtextureContext* textureContext;
float view[2];
GLuint vertBuf;
#if defined NANOVG_GL3
GLuint vertArr;
@@ -248,7 +276,10 @@ struct GLNVGcontext {
GLenum stencilFunc;
GLint stencilFuncRef;
GLuint stencilFuncMask;
GLNVGblend blendFunc;
#endif

int dummyTex;
};
typedef struct GLNVGcontext GLNVGcontext;

@@ -298,7 +329,7 @@ static void glnvg__stencilFunc(GLNVGcontext* gl, GLenum func, GLint ref, GLuint
if ((gl->stencilFunc != func) ||
(gl->stencilFuncRef != ref) ||
(gl->stencilFuncMask != mask)) {
gl->stencilFunc = func;
gl->stencilFuncRef = ref;
gl->stencilFuncMask = mask;
@@ -308,53 +339,68 @@ static void glnvg__stencilFunc(GLNVGcontext* gl, GLenum func, GLint ref, GLuint
glStencilFunc(func, ref, mask);
#endif
}
static void glnvg__blendFuncSeparate(GLNVGcontext* gl, const GLNVGblend* blend)
{
#if NANOVG_GL_USE_STATE_FILTER
if ((gl->blendFunc.srcRGB != blend->srcRGB) ||
(gl->blendFunc.dstRGB != blend->dstRGB) ||
(gl->blendFunc.srcAlpha != blend->srcAlpha) ||
(gl->blendFunc.dstAlpha != blend->dstAlpha)) {

gl->blendFunc = *blend;
glBlendFuncSeparate(blend->srcRGB, blend->dstRGB, blend->srcAlpha,blend->dstAlpha);
}
#else
glBlendFuncSeparate(blend->srcRGB, blend->dstRGB, blend->srcAlpha,blend->dstAlpha);
#endif
}

static GLNVGtexture* glnvg__allocTexture(GLNVGcontext* gl)
{
GLNVGtexture* tex = NULL;
int i;

for (i = 0; i < gl->ntextures; i++) {
if (gl->textures[i].id == 0) {
tex = &gl->textures[i];
for (i = 0; i < gl->textureContext->ntextures; i++) {
if (gl->textureContext->textures[i].id == 0) {
tex = &gl->textureContext->textures[i];
break;
}
}
if (tex == NULL) {
if (gl->ntextures+1 > gl->ctextures) {
if (gl->textureContext->ntextures+1 > gl->textureContext->ctextures) {
GLNVGtexture* textures;
int ctextures = glnvg__maxi(gl->ntextures+1, 4) + gl->ctextures/2; // 1.5x Overallocate
textures = (GLNVGtexture*)realloc(gl->textures, sizeof(GLNVGtexture)*ctextures);
int ctextures = glnvg__maxi(gl->textureContext->ntextures+1, 4) + gl->textureContext->ctextures/2; // 1.5x Overallocate
textures = (GLNVGtexture*)realloc(gl->textureContext->textures, sizeof(GLNVGtexture)*ctextures);
if (textures == NULL) return NULL;
gl->textures = textures;
gl->ctextures = ctextures;
gl->textureContext->textures = textures;
gl->textureContext->ctextures = ctextures;
}
tex = &gl->textures[gl->ntextures++];
tex = &gl->textureContext->textures[gl->textureContext->ntextures++];
}
memset(tex, 0, sizeof(*tex));
tex->id = ++gl->textureId;
tex->id = ++gl->textureContext->textureId;
return tex;
}

static GLNVGtexture* glnvg__findTexture(GLNVGcontext* gl, int id)
{
int i;
for (i = 0; i < gl->ntextures; i++)
if (gl->textures[i].id == id)
return &gl->textures[i];
for (i = 0; i < gl->textureContext->ntextures; i++)
if (gl->textureContext->textures[i].id == id)
return &gl->textureContext->textures[i];
return NULL;
}

static int glnvg__deleteTexture(GLNVGcontext* gl, int id)
{
int i;
for (i = 0; i < gl->ntextures; i++) {
if (gl->textures[i].id == id) {
if (gl->textures[i].tex != 0 && (gl->textures[i].flags & NVG_IMAGE_NODELETE) == 0)
glDeleteTextures(1, &gl->textures[i].tex);
memset(&gl->textures[i], 0, sizeof(gl->textures[i]));
for (i = 0; i < gl->textureContext->ntextures; i++) {
if (gl->textureContext->textures[i].id == id) {
if (gl->textureContext->textures[i].tex != 0 && (gl->textureContext->textures[i].flags & NVG_IMAGE_NODELETE) == 0)
glDeleteTextures(1, &gl->textureContext->textures[i].tex);
memset(&gl->textureContext->textures[i], 0, sizeof(gl->textureContext->textures[i]));
return 1;
}
}
@@ -363,8 +409,8 @@ static int glnvg__deleteTexture(GLNVGcontext* gl, int id)

static void glnvg__dumpShaderError(GLuint shader, const char* name, const char* type)
{
char str[512+1];
int len = 0;
GLchar str[512+1];
GLsizei len = 0;
glGetShaderInfoLog(shader, 512, &len, str);
if (len > 512) len = 512;
str[len] = '\0';
@@ -373,8 +419,8 @@ static void glnvg__dumpShaderError(GLuint shader, const char* name, const char*

static void glnvg__dumpProgramError(GLuint prog, const char* name)
{
char str[512+1];
int len = 0;
GLchar str[512+1];
GLsizei len = 0;
glGetProgramInfoLog(prog, 512, &len, str);
if (len > 512) len = 512;
str[len] = '\0';
@@ -466,9 +512,22 @@ static void glnvg__getUniforms(GLNVGshader* shader)
#endif
}

static int glnvg__renderCreate(void* uptr)
static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int imageFlags, const unsigned char* data);

static int glnvg__renderCreate(void* uptr, void* otherUptr) // Share the textures of GLNVGcontext 'otherUptr' if it's non-NULL.
{
GLNVGcontext* gl = (GLNVGcontext*)uptr;

if (otherUptr) {
GLNVGcontext* other = (GLNVGcontext*)otherUptr;
gl->textureContext = other->textureContext;
gl->textureContext->refCount++;
} else {
gl->textureContext = (GLNVGtextureContext*)malloc(sizeof(GLNVGtextureContext));
memset(gl->textureContext, 0, sizeof(GLNVGtextureContext));
gl->textureContext->refCount = 1;
}

int align = 4;

// TODO: mediump float may not be enough for GLES2 in iOS.
@@ -514,7 +573,7 @@ static int glnvg__renderCreate(void* uptr)
" gl_Position = vec4(2.0*vertex.x/viewSize.x - 1.0, 1.0 - 2.0*vertex.y/viewSize.y, 0, 1);\n"
"}\n";

static const char* fillFragShader =
static const char* fillFragShader =
"#ifdef GL_ES\n"
"#if defined(GL_FRAGMENT_PRECISION_HIGH) || defined(NANOVG_GL3)\n"
" precision highp float;\n"
@@ -592,6 +651,7 @@ static int glnvg__renderCreate(void* uptr)
" float scissor = scissorMask(fpos);\n"
"#ifdef EDGE_AA\n"
" float strokeAlpha = strokeMask();\n"
" if (strokeAlpha < strokeThr) discard;\n"
"#else\n"
" float strokeAlpha = 1.0;\n"
"#endif\n"
@@ -631,9 +691,6 @@ static int glnvg__renderCreate(void* uptr)
" color *= scissor;\n"
" result = color * innerCol;\n"
" }\n"
"#ifdef EDGE_AA\n"
" if (strokeAlpha < strokeThr) discard;\n"
"#endif\n"
"#ifdef NANOVG_GL3\n"
" outColor = result;\n"
"#else\n"
@@ -663,11 +720,15 @@ static int glnvg__renderCreate(void* uptr)
#if NANOVG_GL_USE_UNIFORMBUFFER
// Create UBOs
glUniformBlockBinding(gl->shader.prog, gl->shader.loc[GLNVG_LOC_FRAG], GLNVG_FRAG_BINDING);
glGenBuffers(1, &gl->fragBuf);
glGenBuffers(1, &gl->fragBuf);
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &align);
#endif
gl->fragSize = sizeof(GLNVGfragUniforms) + align - sizeof(GLNVGfragUniforms) % align;

// Some platforms does not allow to have samples to unset textures.
// Create empty one which is bound when there's no texture specified.
gl->dummyTex = glnvg__renderCreateTexture(gl, NVG_TEXTURE_ALPHA, 1, 1, 0, NULL);

glnvg__checkError(gl, "create done");

glFinish();
@@ -690,7 +751,7 @@ static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int im
printf("Repeat X/Y is not supported for non power-of-two textures (%d x %d)\n", w, h);
imageFlags &= ~(NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY);
}
// No mips.
// No mips.
if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) {
printf("Mip-maps is not support for non power-of-two textures (%d x %d)\n", w, h);
imageFlags &= ~NVG_IMAGE_GENERATE_MIPMAPS;
@@ -719,23 +780,50 @@ static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int im
}
#endif

if (type == NVG_TEXTURE_RGBA)
switch (type)
{
case NVG_TEXTURE_BGR:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGR, GL_UNSIGNED_BYTE, data);
break;
case NVG_TEXTURE_BGRA:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, data);
break;
case NVG_TEXTURE_RGB:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
break;
case NVG_TEXTURE_RGBA:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
else
#if defined(NANOVG_GLES2)
break;
default:
#if defined(NANOVG_GLES2) || defined (NANOVG_GL2)
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, data);
#elif defined(NANOVG_GLES3)
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data);
#else
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data);
#endif
break;
}

if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
if (imageFlags & NVG_IMAGE_NEAREST) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
} else {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
}
} else {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
if (imageFlags & NVG_IMAGE_NEAREST) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
} else {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
}

if (imageFlags & NVG_IMAGE_NEAREST) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
} else {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

if (imageFlags & NVG_IMAGE_REPEATX)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
@@ -790,22 +878,50 @@ static int glnvg__renderUpdateTexture(void* uptr, int image, int x, int y, int w
glPixelStorei(GL_UNPACK_SKIP_ROWS, y);
#else
// No support for all of skip, need to update a whole row at a time.
if (tex->type == NVG_TEXTURE_RGBA)
switch (tex->type)
{
case NVG_TEXTURE_BGR:
data += y*tex->width*3;
break;
case NVG_TEXTURE_BGRA:
data += y*tex->width*4;
else
break;
case NVG_TEXTURE_RGB:
data += y*tex->width*3;
break;
case NVG_TEXTURE_RGBA:
data += y*tex->width*4;
break;
default:
data += y*tex->width;
break;
}
x = 0;
w = tex->width;
#endif

if (tex->type == NVG_TEXTURE_RGBA)
switch (tex->type)
{
case NVG_TEXTURE_BGR:
glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_BGR, GL_UNSIGNED_BYTE, data);
break;
case NVG_TEXTURE_BGRA:
glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_BGRA, GL_UNSIGNED_BYTE, data);
break;
case NVG_TEXTURE_RGB:
glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGB, GL_UNSIGNED_BYTE, data);
break;
case NVG_TEXTURE_RGBA:
glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGBA, GL_UNSIGNED_BYTE, data);
else
#ifdef NANOVG_GLES2
break;
default:
#if defined(NANOVG_GLES2) || defined(NANOVG_GL2)
glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_LUMINANCE, GL_UNSIGNED_BYTE, data);
#else
glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RED, GL_UNSIGNED_BYTE, data);
#endif
break;
}

glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
#ifndef NANOVG_GLES2
@@ -887,19 +1003,46 @@ static int glnvg__convertPaint(GLNVGcontext* gl, GLNVGfragUniforms* frag, NVGpai
tex = glnvg__findTexture(gl, paint->image);
if (tex == NULL) return 0;
if ((tex->flags & NVG_IMAGE_FLIPY) != 0) {
float flipped[6];
nvgTransformScale(flipped, 1.0f, -1.0f);
nvgTransformMultiply(flipped, paint->xform);
nvgTransformInverse(invxform, flipped);
float m1[6], m2[6];
nvgTransformTranslate(m1, 0.0f, frag->extent[1] * 0.5f);
nvgTransformMultiply(m1, paint->xform);
nvgTransformScale(m2, 1.0f, -1.0f);
nvgTransformMultiply(m2, m1);
nvgTransformTranslate(m1, 0.0f, -frag->extent[1] * 0.5f);
nvgTransformMultiply(m1, m2);
nvgTransformInverse(invxform, m1);
} else {
nvgTransformInverse(invxform, paint->xform);
}
frag->type = NSVG_SHADER_FILLIMG;

if (tex->type == NVG_TEXTURE_RGBA)
#if NANOVG_GL_USE_UNIFORMBUFFER
switch (tex->type)
{
case NVG_TEXTURE_BGR:
case NVG_TEXTURE_BGRA:
case NVG_TEXTURE_RGB:
case NVG_TEXTURE_RGBA:
frag->texType = (tex->flags & NVG_IMAGE_PREMULTIPLIED) ? 0 : 1;
else
break;
default:
frag->texType = 2;
break;
}
#else
switch (tex->type)
{
case NVG_TEXTURE_BGR:
case NVG_TEXTURE_BGRA:
case NVG_TEXTURE_RGB:
case NVG_TEXTURE_RGBA:
frag->texType = (tex->flags & NVG_IMAGE_PREMULTIPLIED) ? 0.0f : 1.0f;
break;
default:
frag->texType = 2.0f;
break;
}
#endif
// printf("frag->texType = %d\n", frag->texType);
} else {
frag->type = NSVG_SHADER_FILLGRAD;
@@ -917,6 +1060,7 @@ static GLNVGfragUniforms* nvg__fragUniformPtr(GLNVGcontext* gl, int i);

static void glnvg__setUniforms(GLNVGcontext* gl, int uniformOffset, int image)
{
GLNVGtexture* tex = NULL;
#if NANOVG_GL_USE_UNIFORMBUFFER
glBindBufferRange(GL_UNIFORM_BUFFER, GLNVG_FRAG_BINDING, gl->fragBuf, uniformOffset, sizeof(GLNVGfragUniforms));
#else
@@ -925,19 +1069,22 @@ static void glnvg__setUniforms(GLNVGcontext* gl, int uniformOffset, int image)
#endif

if (image != 0) {
GLNVGtexture* tex = glnvg__findTexture(gl, image);
glnvg__bindTexture(gl, tex != NULL ? tex->tex : 0);
glnvg__checkError(gl, "tex paint tex");
} else {
glnvg__bindTexture(gl, 0);
tex = glnvg__findTexture(gl, image);
}
// If no image is set, use empty texture
if (tex == NULL) {
tex = glnvg__findTexture(gl, gl->dummyTex);
}
glnvg__bindTexture(gl, tex != NULL ? tex->tex : 0);
glnvg__checkError(gl, "tex paint tex");
}

static void glnvg__renderViewport(void* uptr, int width, int height)
static void glnvg__renderViewport(void* uptr, float width, float height, float devicePixelRatio)
{
NVG_NOTUSED(devicePixelRatio);
GLNVGcontext* gl = (GLNVGcontext*)uptr;
gl->view[0] = (float)width;
gl->view[1] = (float)height;
gl->view[0] = width;
gl->view[1] = height;
}

static void glnvg__fill(GLNVGcontext* gl, GLNVGcall* call)
@@ -979,7 +1126,7 @@ static void glnvg__fill(GLNVGcontext* gl, GLNVGcall* call)
// Draw fill
glnvg__stencilFunc(gl, GL_NOTEQUAL, 0x0, 0xff);
glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
glDrawArrays(GL_TRIANGLES, call->triangleOffset, call->triangleCount);
glDrawArrays(GL_TRIANGLE_STRIP, call->triangleOffset, call->triangleCount);

glDisable(GL_STENCIL_TEST);
}
@@ -992,12 +1139,12 @@ static void glnvg__convexFill(GLNVGcontext* gl, GLNVGcall* call)
glnvg__setUniforms(gl, call->uniformOffset, call->image);
glnvg__checkError(gl, "convex fill");

for (i = 0; i < npaths; i++)
for (i = 0; i < npaths; i++) {
glDrawArrays(GL_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount);
if (gl->flags & NVG_ANTIALIAS) {
// Draw fringes
for (i = 0; i < npaths; i++)
if (paths[i].strokeCount > 0) {
glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount);
}
}
}

@@ -1026,7 +1173,7 @@ static void glnvg__stroke(GLNVGcontext* gl, GLNVGcall* call)
for (i = 0; i < npaths; i++)
glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount);

// Clear stencil buffer.
// Clear stencil buffer.
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glnvg__stencilFunc(gl, GL_ALWAYS, 0x0, 0xff);
glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
@@ -1064,6 +1211,50 @@ static void glnvg__renderCancel(void* uptr) {
gl->nuniforms = 0;
}

static GLenum glnvg_convertBlendFuncFactor(int factor)
{
if (factor == NVG_ZERO)
return GL_ZERO;
if (factor == NVG_ONE)
return GL_ONE;
if (factor == NVG_SRC_COLOR)
return GL_SRC_COLOR;
if (factor == NVG_ONE_MINUS_SRC_COLOR)
return GL_ONE_MINUS_SRC_COLOR;
if (factor == NVG_DST_COLOR)
return GL_DST_COLOR;
if (factor == NVG_ONE_MINUS_DST_COLOR)
return GL_ONE_MINUS_DST_COLOR;
if (factor == NVG_SRC_ALPHA)
return GL_SRC_ALPHA;
if (factor == NVG_ONE_MINUS_SRC_ALPHA)
return GL_ONE_MINUS_SRC_ALPHA;
if (factor == NVG_DST_ALPHA)
return GL_DST_ALPHA;
if (factor == NVG_ONE_MINUS_DST_ALPHA)
return GL_ONE_MINUS_DST_ALPHA;
if (factor == NVG_SRC_ALPHA_SATURATE)
return GL_SRC_ALPHA_SATURATE;
return GL_INVALID_ENUM;
}

static GLNVGblend glnvg__blendCompositeOperation(NVGcompositeOperationState op)
{
GLNVGblend blend;
blend.srcRGB = glnvg_convertBlendFuncFactor(op.srcRGB);
blend.dstRGB = glnvg_convertBlendFuncFactor(op.dstRGB);
blend.srcAlpha = glnvg_convertBlendFuncFactor(op.srcAlpha);
blend.dstAlpha = glnvg_convertBlendFuncFactor(op.dstAlpha);
if (blend.srcRGB == GL_INVALID_ENUM || blend.dstRGB == GL_INVALID_ENUM || blend.srcAlpha == GL_INVALID_ENUM || blend.dstAlpha == GL_INVALID_ENUM)
{
blend.srcRGB = GL_ONE;
blend.dstRGB = GL_ONE_MINUS_SRC_ALPHA;
blend.srcAlpha = GL_ONE;
blend.dstAlpha = GL_ONE_MINUS_SRC_ALPHA;
}
return blend;
}

static void glnvg__renderFlush(void* uptr)
{
GLNVGcontext* gl = (GLNVGcontext*)uptr;
@@ -1074,7 +1265,6 @@ static void glnvg__renderFlush(void* uptr)
// Setup require GL state.
glUseProgram(gl->shader.prog);

glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glFrontFace(GL_CCW);
@@ -1093,6 +1283,10 @@ static void glnvg__renderFlush(void* uptr)
gl->stencilFunc = GL_ALWAYS;
gl->stencilFuncRef = 0;
gl->stencilFuncMask = 0xffffffff;
gl->blendFunc.srcRGB = GL_INVALID_ENUM;
gl->blendFunc.srcAlpha = GL_INVALID_ENUM;
gl->blendFunc.dstRGB = GL_INVALID_ENUM;
gl->blendFunc.dstAlpha = GL_INVALID_ENUM;
#endif

#if NANOVG_GL_USE_UNIFORMBUFFER
@@ -1122,6 +1316,7 @@ static void glnvg__renderFlush(void* uptr)

for (i = 0; i < gl->ncalls; i++) {
GLNVGcall* call = &gl->calls[i];
glnvg__blendFuncSeparate(gl,&call->blendFunc);
if (call->type == GLNVG_FILL)
glnvg__fill(gl, call);
else if (call->type == GLNVG_CONVEXFILL)
@@ -1136,9 +1331,9 @@ static void glnvg__renderFlush(void* uptr)
glDisableVertexAttribArray(1);
#if defined NANOVG_GL3
glBindVertexArray(0);
#endif
#endif
glDisable(GL_CULL_FACE);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glUseProgram(0);
glnvg__bindTexture(gl, 0);
}
@@ -1237,7 +1432,7 @@ static void glnvg__vset(NVGvertex* vtx, float x, float y, float u, float v)
vtx->v = v;
}

static void glnvg__renderFill(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe,
static void glnvg__renderFill(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe,
const float* bounds, const NVGpath* paths, int npaths)
{
GLNVGcontext* gl = (GLNVGcontext*)uptr;
@@ -1249,16 +1444,21 @@ static void glnvg__renderFill(void* uptr, NVGpaint* paint, NVGscissor* scissor,
if (call == NULL) return;

call->type = GLNVG_FILL;
call->triangleCount = 4;
call->pathOffset = glnvg__allocPaths(gl, npaths);
if (call->pathOffset == -1) goto error;
call->pathCount = npaths;
call->image = paint->image;
call->blendFunc = glnvg__blendCompositeOperation(compositeOperation);

if (npaths == 1 && paths[0].convex)
{
call->type = GLNVG_CONVEXFILL;
call->triangleCount = 0; // Bounding box fill quad not needed for convex fill
}

// Allocate vertices for all the paths.
maxverts = glnvg__maxVertCount(paths, npaths) + 6;
maxverts = glnvg__maxVertCount(paths, npaths) + call->triangleCount;
offset = glnvg__allocVerts(gl, maxverts);
if (offset == -1) goto error;

@@ -1280,20 +1480,16 @@ static void glnvg__renderFill(void* uptr, NVGpaint* paint, NVGscissor* scissor,
}
}

// Quad
call->triangleOffset = offset;
call->triangleCount = 6;
quad = &gl->verts[call->triangleOffset];
glnvg__vset(&quad[0], bounds[0], bounds[3], 0.5f, 1.0f);
glnvg__vset(&quad[1], bounds[2], bounds[3], 0.5f, 1.0f);
glnvg__vset(&quad[2], bounds[2], bounds[1], 0.5f, 1.0f);

glnvg__vset(&quad[3], bounds[0], bounds[3], 0.5f, 1.0f);
glnvg__vset(&quad[4], bounds[2], bounds[1], 0.5f, 1.0f);
glnvg__vset(&quad[5], bounds[0], bounds[1], 0.5f, 1.0f);

// Setup uniforms for draw calls
if (call->type == GLNVG_FILL) {
// Quad
call->triangleOffset = offset;
quad = &gl->verts[call->triangleOffset];
glnvg__vset(&quad[0], bounds[2], bounds[3], 0.5f, 1.0f);
glnvg__vset(&quad[1], bounds[2], bounds[1], 0.5f, 1.0f);
glnvg__vset(&quad[2], bounds[0], bounds[3], 0.5f, 1.0f);
glnvg__vset(&quad[3], bounds[0], bounds[1], 0.5f, 1.0f);

call->uniformOffset = glnvg__allocFragUniforms(gl, 2);
if (call->uniformOffset == -1) goto error;
// Simple shader for stencil
@@ -1318,7 +1514,7 @@ error:
if (gl->ncalls > 0) gl->ncalls--;
}

static void glnvg__renderStroke(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe,
static void glnvg__renderStroke(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe,
float strokeWidth, const NVGpath* paths, int npaths)
{
GLNVGcontext* gl = (GLNVGcontext*)uptr;
@@ -1332,6 +1528,7 @@ static void glnvg__renderStroke(void* uptr, NVGpaint* paint, NVGscissor* scissor
if (call->pathOffset == -1) goto error;
call->pathCount = npaths;
call->image = paint->image;
call->blendFunc = glnvg__blendCompositeOperation(compositeOperation);

// Allocate vertices for all the paths.
maxverts = glnvg__maxVertCount(paths, npaths);
@@ -1373,8 +1570,8 @@ error:
if (gl->ncalls > 0) gl->ncalls--;
}

static void glnvg__renderTriangles(void* uptr, NVGpaint* paint, NVGscissor* scissor,
const NVGvertex* verts, int nverts)
static void glnvg__renderTriangles(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor,
const NVGvertex* verts, int nverts, float fringe)
{
GLNVGcontext* gl = (GLNVGcontext*)uptr;
GLNVGcall* call = glnvg__allocCall(gl);
@@ -1384,6 +1581,7 @@ static void glnvg__renderTriangles(void* uptr, NVGpaint* paint, NVGscissor* scis

call->type = GLNVG_TRIANGLES;
call->image = paint->image;
call->blendFunc = glnvg__blendCompositeOperation(compositeOperation);

// Allocate vertices for all the paths.
call->triangleOffset = glnvg__allocVerts(gl, nverts);
@@ -1396,7 +1594,7 @@ static void glnvg__renderTriangles(void* uptr, NVGpaint* paint, NVGscissor* scis
call->uniformOffset = glnvg__allocFragUniforms(gl, 1);
if (call->uniformOffset == -1) goto error;
frag = nvg__fragUniformPtr(gl, call->uniformOffset);
glnvg__convertPaint(gl, frag, paint, scissor, 1.0f, 1.0f, -1.0f);
glnvg__convertPaint(gl, frag, paint, scissor, 1.0f, fringe, -1.0f);
frag->type = NSVG_SHADER_IMG;

return;
@@ -1426,11 +1624,14 @@ static void glnvg__renderDelete(void* uptr)
if (gl->vertBuf != 0)
glDeleteBuffers(1, &gl->vertBuf);

for (i = 0; i < gl->ntextures; i++) {
if (gl->textures[i].tex != 0 && (gl->textures[i].flags & NVG_IMAGE_NODELETE) == 0)
glDeleteTextures(1, &gl->textures[i].tex);
if (gl->textureContext != NULL && --gl->textureContext->refCount == 0) {
for (i = 0; i < gl->textureContext->ntextures; i++) {
if (gl->textureContext->textures[i].tex != 0 && (gl->textureContext->textures[i].flags & NVG_IMAGE_NODELETE) == 0)
glDeleteTextures(1, &gl->textureContext->textures[i].tex);
}
free(gl->textureContext->textures);
free(gl->textureContext);
}
free(gl->textures);

free(gl->paths);
free(gl->verts);
@@ -1450,6 +1651,28 @@ NVGcontext* nvgCreateGLES2(int flags)
#elif defined NANOVG_GLES3
NVGcontext* nvgCreateGLES3(int flags)
#endif
{
#if defined NANOVG_GL2
return nvgCreateSharedGL2(NULL, flags);
#elif defined NANOVG_GL3
return nvgCreateSharedGL3(NULL, flags);
#elif defined NANOVG_GLES2
return nvgCreateSharedGLES2(NULL, flags);
#elif defined NANOVG_GLES3
return nvgCreateSharedGLES3(NULL, flags);
#endif
}

// Share the fonts and textures of 'other' if it's non-NULL.
#if defined NANOVG_GL2
NVGcontext* nvgCreateSharedGL2(NVGcontext* other, int flags)
#elif defined NANOVG_GL3
NVGcontext* nvgCreateSharedGL3(NVGcontext* other, int flags)
#elif defined NANOVG_GLES2
NVGcontext* nvgCreateSharedGLES2(NVGcontext* other, int flags)
#elif defined NANOVG_GLES3
NVGcontext* nvgCreateSharedGLES3(NVGcontext* other, int flags)
#endif
{
NVGparams params;
NVGcontext* ctx = NULL;
@@ -1475,7 +1698,7 @@ NVGcontext* nvgCreateGLES3(int flags)

gl->flags = flags;

ctx = nvgCreateInternal(&params);
ctx = nvgCreateInternal(&params, other);
if (ctx == NULL) goto error;

return ctx;
@@ -1499,7 +1722,15 @@ void nvgDeleteGLES3(NVGcontext* ctx)
nvgDeleteInternal(ctx);
}

int nvglCreateImageFromHandle(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags)
#if defined NANOVG_GL2
int nvglCreateImageFromHandleGL2(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags)
#elif defined NANOVG_GL3
int nvglCreateImageFromHandleGL3(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags)
#elif defined NANOVG_GLES2
int nvglCreateImageFromHandleGLES2(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags)
#elif defined NANOVG_GLES3
int nvglCreateImageFromHandleGLES3(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags)
#endif
{
GLNVGcontext* gl = (GLNVGcontext*)nvgInternalParams(ctx)->userPtr;
GLNVGtexture* tex = glnvg__allocTexture(gl);
@@ -1515,7 +1746,15 @@ int nvglCreateImageFromHandle(NVGcontext* ctx, GLuint textureId, int w, int h, i
return tex->id;
}

GLuint nvglImageHandle(NVGcontext* ctx, int image)
#if defined NANOVG_GL2
GLuint nvglImageHandleGL2(NVGcontext* ctx, int image)
#elif defined NANOVG_GL3
GLuint nvglImageHandleGL3(NVGcontext* ctx, int image)
#elif defined NANOVG_GLES2
GLuint nvglImageHandleGLES2(NVGcontext* ctx, int image)
#elif defined NANOVG_GLES3
GLuint nvglImageHandleGLES3(NVGcontext* ctx, int image)
#endif
{
GLNVGcontext* gl = (GLNVGcontext*)nvgInternalParams(ctx)->userPtr;
GLNVGtexture* tex = glnvg__findTexture(gl, image);


+ 29
- 7
source/modules/dgl/src/nanovg/nanovg_gl_utils.h View File

@@ -30,7 +30,7 @@ typedef struct NVGLUframebuffer NVGLUframebuffer;
// Helper function to create GL frame buffer to render to.
void nvgluBindFramebuffer(NVGLUframebuffer* fb);
NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imageFlags);
void nvgluDeleteFramebuffer(NVGcontext* ctx, NVGLUframebuffer* fb);
void nvgluDeleteFramebuffer(NVGLUframebuffer* fb);

#endif // NANOVG_GL_UTILS_H

@@ -64,7 +64,18 @@ NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imag
memset(fb, 0, sizeof(NVGLUframebuffer));

fb->image = nvgCreateImageRGBA(ctx, w, h, imageFlags | NVG_IMAGE_FLIPY | NVG_IMAGE_PREMULTIPLIED, NULL);
fb->texture = nvglImageHandle(ctx, fb->image);

#if defined NANOVG_GL2
fb->texture = nvglImageHandleGL2(ctx, fb->image);
#elif defined NANOVG_GL3
fb->texture = nvglImageHandleGL3(ctx, fb->image);
#elif defined NANOVG_GLES2
fb->texture = nvglImageHandleGLES2(ctx, fb->image);
#elif defined NANOVG_GLES3
fb->texture = nvglImageHandleGLES3(ctx, fb->image);
#endif

fb->ctx = ctx;

// frame buffer object
glGenFramebuffers(1, &fb->fbo);
@@ -79,7 +90,18 @@ NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imag
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb->texture, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb->rbo);

if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) goto error;
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
#ifdef GL_DEPTH24_STENCIL8
// If GL_STENCIL_INDEX8 is not supported, try GL_DEPTH24_STENCIL8 as a fallback.
// Some graphics cards require a depth buffer along with a stencil.
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, w, h);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb->texture, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb->rbo);

if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
#endif // GL_DEPTH24_STENCIL8
goto error;
}

glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO);
glBindRenderbuffer(GL_RENDERBUFFER, defaultRBO);
@@ -87,7 +109,7 @@ NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imag
error:
glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO);
glBindRenderbuffer(GL_RENDERBUFFER, defaultRBO);
nvgluDeleteFramebuffer(ctx, fb);
nvgluDeleteFramebuffer(fb);
return NULL;
#else
NVG_NOTUSED(ctx);
@@ -108,7 +130,7 @@ void nvgluBindFramebuffer(NVGLUframebuffer* fb)
#endif
}

void nvgluDeleteFramebuffer(NVGcontext* ctx, NVGLUframebuffer* fb)
void nvgluDeleteFramebuffer(NVGLUframebuffer* fb)
{
#ifdef NANOVG_FBO_VALID
if (fb == NULL) return;
@@ -117,14 +139,14 @@ void nvgluDeleteFramebuffer(NVGcontext* ctx, NVGLUframebuffer* fb)
if (fb->rbo != 0)
glDeleteRenderbuffers(1, &fb->rbo);
if (fb->image >= 0)
nvgDeleteImage(ctx, fb->image);
nvgDeleteImage(fb->ctx, fb->image);
fb->ctx = NULL;
fb->fbo = 0;
fb->rbo = 0;
fb->texture = 0;
fb->image = -1;
free(fb);
#else
NVG_NOTUSED(ctx);
NVG_NOTUSED(fb);
#endif
}


+ 2835
- 869
source/modules/dgl/src/nanovg/stb_image.h
File diff suppressed because it is too large
View File


+ 5011
- 2081
source/modules/dgl/src/nanovg/stb_truetype.h
File diff suppressed because it is too large
View File


+ 1
- 0
source/modules/dgl/src/pugl-upstream

@@ -0,0 +1 @@
Subproject commit b1d9703ecbdb0a033fe0b9acdf58b90f7d81a8e5

+ 675
- 0
source/modules/dgl/src/pugl.cpp View File

@@ -0,0 +1,675 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "pugl.hpp"

/* we will include all header files used in pugl in their C++ friendly form, then pugl stuff in custom namespace */
#include <cassert>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <ctime>

#if defined(DISTRHO_OS_MAC)
# import <Cocoa/Cocoa.h>
# include <dlfcn.h>
# include <mach/mach_time.h>
# ifdef DGL_CAIRO
# include <cairo.h>
# include <cairo-quartz.h>
# endif
# ifdef DGL_OPENGL
# include <OpenGL/gl.h>
# endif
# ifdef DGL_VULKAN
# import <QuartzCore/CAMetalLayer.h>
# include <vulkan/vulkan_core.h>
# include <vulkan/vulkan_macos.h>
# endif
#elif defined(DISTRHO_OS_WINDOWS)
# include <wctype.h>
# include <winsock2.h>
# include <windows.h>
# include <windowsx.h>
# ifdef DGL_CAIRO
# include <cairo.h>
# include <cairo-win32.h>
# endif
# ifdef DGL_OPENGL
# include <GL/gl.h>
# endif
# ifdef DGL_VULKAN
# include <vulkan/vulkan.h>
# include <vulkan/vulkan_win32.h>
# endif
#else
# include <dlfcn.h>
# include <unistd.h>
# include <sys/select.h>
# include <sys/time.h>
# include <X11/X.h>
# include <X11/Xatom.h>
# include <X11/Xlib.h>
# include <X11/Xresource.h>
# include <X11/Xutil.h>
# include <X11/keysym.h>
# ifdef HAVE_XCURSOR
# include <X11/Xcursor/Xcursor.h>
# include <X11/cursorfont.h>
# endif
# ifdef HAVE_XRANDR
# include <X11/extensions/Xrandr.h>
# endif
# ifdef HAVE_XSYNC
# include <X11/extensions/sync.h>
# include <X11/extensions/syncconst.h>
# endif
# ifdef DGL_CAIRO
# include <cairo.h>
# include <cairo-xlib.h>
# endif
# ifdef DGL_OPENGL
# include <GL/gl.h>
# include <GL/glx.h>
# endif
# ifdef DGL_VULKAN
# include <vulkan/vulkan_core.h>
# include <vulkan/vulkan_xlib.h>
# endif
#endif

#ifndef DGL_FILE_BROWSER_DISABLED
# ifdef DISTRHO_OS_MAC
# import "../../distrho/extra/FileBrowserDialog.cpp"
# else
# include "../../distrho/extra/FileBrowserDialog.cpp"
# endif
#endif

#ifndef DISTRHO_OS_MAC
START_NAMESPACE_DGL
#endif

// --------------------------------------------------------------------------------------------------------------------

#if defined(DISTRHO_OS_MAC)
# ifndef DISTRHO_MACOS_NAMESPACE_MACRO
# define DISTRHO_MACOS_NAMESPACE_MACRO_HELPER(NS, SEP, INTERFACE) NS ## SEP ## INTERFACE
# define DISTRHO_MACOS_NAMESPACE_MACRO(NS, INTERFACE) DISTRHO_MACOS_NAMESPACE_MACRO_HELPER(NS, _, INTERFACE)
# define PuglStubView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PuglStubView)
# define PuglWrapperView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PuglWrapperView)
# define PuglWindow DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PuglWindow)
# endif
# ifndef __MAC_10_9
# define NSModalResponseOK NSOKButton
# endif
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
# import "pugl-upstream/src/mac.m"
# import "pugl-upstream/src/mac_stub.m"
# ifdef DGL_CAIRO
# import "pugl-upstream/src/mac_cairo.m"
# endif
# ifdef DGL_OPENGL
# import "pugl-upstream/src/mac_gl.m"
# endif
# ifdef DGL_VULKAN
# import "pugl-upstream/src/mac_vulkan.m"
# endif
# pragma clang diagnostic pop
#elif defined(DISTRHO_OS_WINDOWS)
# include "pugl-upstream/src/win.c"
# include "pugl-upstream/src/win_stub.c"
# ifdef DGL_CAIRO
# include "pugl-upstream/src/win_cairo.c"
# endif
# ifdef DGL_OPENGL
# include "pugl-upstream/src/win_gl.c"
# endif
# ifdef DGL_VULKAN
# include "pugl-upstream/src/win_vulkan.c"
# endif
#else
# include "pugl-upstream/src/x11.c"
# include "pugl-upstream/src/x11_stub.c"
# ifdef DGL_CAIRO
# include "pugl-upstream/src/x11_cairo.c"
# endif
# ifdef DGL_OPENGL
# include "pugl-upstream/src/x11_gl.c"
# endif
# ifdef DGL_VULKAN
# include "pugl-upstream/src/x11_vulkan.c"
# endif
#endif

#include "pugl-upstream/src/implementation.c"

// --------------------------------------------------------------------------------------------------------------------
// expose backend enter

bool puglBackendEnter(PuglView* const view)
{
return view->backend->enter(view, nullptr) == PUGL_SUCCESS;
}

// --------------------------------------------------------------------------------------------------------------------
// expose backend leave

void puglBackendLeave(PuglView* const view)
{
view->backend->leave(view, nullptr);
}

// --------------------------------------------------------------------------------------------------------------------
// clear minimum size to 0

void puglClearMinSize(PuglView* const view)
{
view->minWidth = 0;
view->minHeight = 0;
}

// --------------------------------------------------------------------------------------------------------------------
// missing in pugl, directly returns transient parent

PuglNativeView puglGetTransientParent(const PuglView* const view)
{
return view->transientParent;
}

// --------------------------------------------------------------------------------------------------------------------
// missing in pugl, directly returns title char* pointer

const char* puglGetWindowTitle(const PuglView* const view)
{
return view->title;
}

// --------------------------------------------------------------------------------------------------------------------
// get global scale factor

double puglGetDesktopScaleFactor(const PuglView* const view)
{
#if defined(DISTRHO_OS_MAC)
if (NSWindow* const window = view->impl->window ? view->impl->window
: [view->impl->wrapperView window])
return [window screen].backingScaleFactor;
return [NSScreen mainScreen].backingScaleFactor;
#elif defined(DISTRHO_OS_WINDOWS)
if (const HMODULE Shcore = LoadLibraryA("Shcore.dll"))
{
typedef HRESULT(WINAPI* PFN_GetProcessDpiAwareness)(HANDLE, DWORD*);
typedef HRESULT(WINAPI* PFN_GetScaleFactorForMonitor)(HMONITOR, DWORD*);

# if defined(__GNUC__) && (__GNUC__ >= 9)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wcast-function-type"
# endif
const PFN_GetProcessDpiAwareness GetProcessDpiAwareness
= (PFN_GetProcessDpiAwareness)GetProcAddress(Shcore, "GetProcessDpiAwareness");
const PFN_GetScaleFactorForMonitor GetScaleFactorForMonitor
= (PFN_GetScaleFactorForMonitor)GetProcAddress(Shcore, "GetScaleFactorForMonitor");
# if defined(__GNUC__) && (__GNUC__ >= 9)
# pragma GCC diagnostic pop
# endif

DWORD dpiAware = 0;
if (GetProcessDpiAwareness && GetScaleFactorForMonitor
&& GetProcessDpiAwareness(NULL, &dpiAware) == 0 && dpiAware != 0)
{
const HMONITOR hMon = MonitorFromWindow(view->impl->hwnd, MONITOR_DEFAULTTOPRIMARY);

DWORD scaleFactor = 0;
if (GetScaleFactorForMonitor(hMon, &scaleFactor) == 0 && scaleFactor != 0)
{
FreeLibrary(Shcore);
return static_cast<double>(scaleFactor) / 100.0;
}
}

FreeLibrary(Shcore);
}
#elif defined(HAVE_X11)
XrmInitialize();

if (char* const rms = XResourceManagerString(view->world->impl->display))
{
if (const XrmDatabase sdb = XrmGetStringDatabase(rms))
{
char* type = nullptr;
XrmValue ret;

if (XrmGetResource(sdb, "Xft.dpi", "String", &type, &ret)
&& ret.addr != nullptr
&& type != nullptr
&& std::strncmp("String", type, 6) == 0)
{
if (const double dpi = std::atof(ret.addr))
return dpi / 96;
}
}
}
#else
// unused
(void)view;
#endif

return 1.0;
}

// --------------------------------------------------------------------------------------------------------------------
// bring view window into the foreground, aka "raise" window

void puglRaiseWindow(PuglView* const view)
{
#if defined(DISTRHO_OS_MAC)
if (NSWindow* const window = view->impl->window ? view->impl->window
: [view->impl->wrapperView window])
[window orderFrontRegardless];
#elif defined(DISTRHO_OS_WINDOWS)
SetForegroundWindow(view->impl->hwnd);
SetActiveWindow(view->impl->hwnd);
#else
XRaiseWindow(view->impl->display, view->impl->win);
#endif
}

// --------------------------------------------------------------------------------------------------------------------
// set backend that matches current build

void puglSetMatchingBackendForCurrentBuild(PuglView* const view)
{
#ifdef DGL_CAIRO
puglSetBackend(view, puglCairoBackend());
#endif
#ifdef DGL_OPENGL
puglSetBackend(view, puglGlBackend());
#endif
#ifdef DGL_VULKAN
puglSetBackend(view, puglVulkanBackend());
#endif
if (view->backend == nullptr)
puglSetBackend(view, puglStubBackend());
}

// --------------------------------------------------------------------------------------------------------------------
// Combine puglSetMinSize and puglSetAspectRatio

PuglStatus puglSetGeometryConstraints(PuglView* const view, const uint width, const uint height, const bool aspect)
{
view->minWidth = (int)width;
view->minHeight = (int)height;

if (aspect) {
view->minAspectX = (int)width;
view->minAspectY = (int)height;
view->maxAspectX = (int)width;
view->maxAspectY = (int)height;
}

#if defined(DISTRHO_OS_MAC)
puglSetMinSize(view, width, height);

if (aspect) {
puglSetAspectRatio(view, width, height, width, height);
}
#elif defined(DISTRHO_OS_WINDOWS)
// nothing
#else
if (const PuglStatus status = updateSizeHints(view))
return status;

XFlush(view->impl->display);
#endif

return PUGL_SUCCESS;
}

// --------------------------------------------------------------------------------------------------------------------
// set window offset without changing size

PuglStatus puglSetWindowOffset(PuglView* const view, const int x, const int y)
{
// TODO custom setFrame version
PuglRect rect = puglGetFrame(view);
rect.x = x;
rect.y = y;
return puglSetFrame(view, rect);
}

// --------------------------------------------------------------------------------------------------------------------
// set window size with default size and without changing frame x/y position

PuglStatus puglSetWindowSize(PuglView* const view, const uint width, const uint height)
{
view->defaultWidth = width;
view->defaultHeight = height;
view->frame.width = width;
view->frame.height = height;

#if defined(DISTRHO_OS_MAC)
// replace setFrame with setFrameSize
PuglInternals* const impl = view->impl;

const PuglRect frame = view->frame;
const NSRect framePx = rectToNsRect(frame);
const NSRect framePt = nsRectToPoints(view, framePx);

if (impl->window)
{
// Resize window to fit new content rect
const NSRect screenPt = rectToScreen(viewScreen(view), framePt);
const NSRect winFrame = [impl->window frameRectForContentRect:screenPt];

[impl->window setFrame:winFrame display:NO];
}

// Resize views
const NSSize sizePx = NSMakeSize(frame.width, frame.height);
const NSSize sizePt = [impl->drawView convertSizeFromBacking:sizePx];

[impl->wrapperView setFrameSize:(impl->window ? sizePt : framePt.size)];
[impl->drawView setFrameSize:sizePt];
#elif defined(DISTRHO_OS_WINDOWS)
// matches upstream pugl, except we add SWP_NOMOVE flag
if (view->impl->hwnd)
{
const PuglRect frame = view->frame;

RECT rect = { (long)frame.x,
(long)frame.y,
(long)frame.x + (long)frame.width,
(long)frame.y + (long)frame.height };

AdjustWindowRectEx(&rect, puglWinGetWindowFlags(view), FALSE, puglWinGetWindowExFlags(view));

if (SetWindowPos(view->impl->hwnd,
HWND_TOP,
rect.left,
rect.top,
rect.right - rect.left,
rect.bottom - rect.top,
SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER))
{
// make sure to return context back to ourselves
view->backend->enter(view, nullptr);
return PUGL_SUCCESS;
}

return PUGL_UNKNOWN_ERROR;
}
#else
// matches upstream pugl, except we use XResizeWindow instead of XMoveResizeWindow
if (view->impl->win)
{
Display* const display = view->impl->display;

if (! XResizeWindow(display, view->impl->win, width, height))
return PUGL_UNKNOWN_ERROR;

if (const PuglStatus status = updateSizeHints(view))
return status;

XFlush(display);
}
#endif

return PUGL_SUCCESS;
}

// --------------------------------------------------------------------------------------------------------------------
// DGL specific, build-specific drawing prepare

void puglOnDisplayPrepare(PuglView*)
{
#ifdef DGL_OPENGL
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
#endif
}

// --------------------------------------------------------------------------------------------------------------------
// DGL specific, build-specific fallback resize

void puglFallbackOnResize(PuglView* const view)
{
#ifdef DGL_OPENGL
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, static_cast<GLdouble>(view->frame.width), static_cast<GLdouble>(view->frame.height), 0.0, 0.0, 1.0);
glViewport(0, 0, static_cast<GLsizei>(view->frame.width), static_cast<GLsizei>(view->frame.height));
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
#endif
}

#if defined(DISTRHO_OS_MAC)

// --------------------------------------------------------------------------------------------------------------------
// macOS specific, allow standalone window to gain focus

void puglMacOSActivateApp()
{
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
[NSApp activateIgnoringOtherApps:YES];
}

// --------------------------------------------------------------------------------------------------------------------
// macOS specific, add another view's window as child

PuglStatus
puglMacOSAddChildWindow(PuglView* const view, PuglView* const child)
{
if (NSWindow* const viewWindow = view->impl->window ? view->impl->window
: [view->impl->wrapperView window])
{
if (NSWindow* const childWindow = child->impl->window ? child->impl->window
: [child->impl->wrapperView window])
{
[viewWindow addChildWindow:childWindow ordered:NSWindowAbove];
return PUGL_SUCCESS;
}
}

return PUGL_FAILURE;
}

// --------------------------------------------------------------------------------------------------------------------
// macOS specific, remove another view's window as child

PuglStatus
puglMacOSRemoveChildWindow(PuglView* const view, PuglView* const child)
{
if (NSWindow* const viewWindow = view->impl->window ? view->impl->window
: [view->impl->wrapperView window])
{
if (NSWindow* const childWindow = child->impl->window ? child->impl->window
: [child->impl->wrapperView window])
{
[viewWindow removeChildWindow:childWindow];
return PUGL_SUCCESS;
}
}

return PUGL_FAILURE;
}

// --------------------------------------------------------------------------------------------------------------------
// macOS specific, center view based on parent coordinates (if there is one)

void puglMacOSShowCentered(PuglView* const view)
{
if (puglShow(view) != PUGL_SUCCESS)
return;

if (view->transientParent != 0)
{
NSWindow* const transientWindow = [(NSView*)view->transientParent window];
DISTRHO_SAFE_ASSERT_RETURN(transientWindow != nullptr,);

const NSRect ourFrame = [view->impl->window frame];
const NSRect transientFrame = [transientWindow frame];

const int x = transientFrame.origin.x + transientFrame.size.width / 2 - ourFrame.size.width / 2;
const int y = transientFrame.origin.y + transientFrame.size.height / 2 + ourFrame.size.height / 2;

[view->impl->window setFrameTopLeftPoint:NSMakePoint(x, y)];
}
else
{
[view->impl->window center];
}
}

// --------------------------------------------------------------------------------------------------------------------

#elif defined(DISTRHO_OS_WINDOWS)

// --------------------------------------------------------------------------------------------------------------------
// win32 specific, call ShowWindow with SW_RESTORE

void puglWin32RestoreWindow(PuglView* const view)
{
PuglInternals* impl = view->impl;
DISTRHO_SAFE_ASSERT_RETURN(impl->hwnd != nullptr,);

ShowWindow(impl->hwnd, SW_RESTORE);
SetFocus(impl->hwnd);
}

// --------------------------------------------------------------------------------------------------------------------
// win32 specific, center view based on parent coordinates (if there is one)

void puglWin32ShowCentered(PuglView* const view)
{
PuglInternals* impl = view->impl;
DISTRHO_SAFE_ASSERT_RETURN(impl->hwnd != nullptr,);

RECT rectChild, rectParent;

if (view->transientParent != 0 &&
GetWindowRect(impl->hwnd, &rectChild) &&
GetWindowRect((HWND)view->transientParent, &rectParent))
{
SetWindowPos(impl->hwnd, (HWND)view->transientParent,
rectParent.left + (rectChild.right-rectChild.left)/2,
rectParent.top + (rectChild.bottom-rectChild.top)/2,
0, 0, SWP_SHOWWINDOW|SWP_NOSIZE);
}
else
{
#ifdef DGL_WINDOWS_ICON_ID
WNDCLASSEX wClass;
std::memset(&wClass, 0, sizeof(wClass));

const HINSTANCE hInstance = GetModuleHandle(nullptr);

if (GetClassInfoEx(hInstance, view->world->className, &wClass))
wClass.hIcon = LoadIcon(nullptr, MAKEINTRESOURCE(DGL_WINDOWS_ICON_ID));

SetClassLongPtr(impl->hwnd, GCLP_HICON, (LONG_PTR) LoadIcon(hInstance, MAKEINTRESOURCE(DGL_WINDOWS_ICON_ID)));
#endif

MONITORINFO mInfo;
std::memset(&mInfo, 0, sizeof(mInfo));
mInfo.cbSize = sizeof(mInfo);

if (GetMonitorInfo(MonitorFromWindow(impl->hwnd, MONITOR_DEFAULTTOPRIMARY), &mInfo))
SetWindowPos(impl->hwnd,
HWND_TOP,
mInfo.rcWork.left + (mInfo.rcWork.right - view->frame.width) / 2,
mInfo.rcWork.top + (mInfo.rcWork.bottom - view->frame.height) / 2,
0, 0, SWP_SHOWWINDOW|SWP_NOSIZE);
else
ShowWindow(impl->hwnd, SW_NORMAL);
}

SetFocus(impl->hwnd);
}

// --------------------------------------------------------------------------------------------------------------------
// win32 specific, set or unset WS_SIZEBOX style flag

void puglWin32SetWindowResizable(PuglView* const view, const bool resizable)
{
PuglInternals* impl = view->impl;
DISTRHO_SAFE_ASSERT_RETURN(impl->hwnd != nullptr,);

const int winFlags = resizable ? GetWindowLong(impl->hwnd, GWL_STYLE) | WS_SIZEBOX
: GetWindowLong(impl->hwnd, GWL_STYLE) & ~WS_SIZEBOX;
SetWindowLong(impl->hwnd, GWL_STYLE, winFlags);
}

// --------------------------------------------------------------------------------------------------------------------

#elif defined(HAVE_X11)

// --------------------------------------------------------------------------------------------------------------------
// X11 specific, safer way to grab focus

PuglStatus puglX11GrabFocus(const PuglView* const view)
{
const PuglInternals* const impl = view->impl;

XWindowAttributes wa;
std::memset(&wa, 0, sizeof(wa));

DISTRHO_SAFE_ASSERT_RETURN(XGetWindowAttributes(impl->display, impl->win, &wa), PUGL_UNKNOWN_ERROR);

if (wa.map_state == IsViewable)
{
XRaiseWindow(impl->display, impl->win);
XSetInputFocus(impl->display, impl->win, RevertToPointerRoot, CurrentTime);
XSync(impl->display, False);
}

return PUGL_SUCCESS;
}

// --------------------------------------------------------------------------------------------------------------------
// X11 specific, set dialog window type and pid hints

void puglX11SetWindowTypeAndPID(const PuglView* const view, const bool isStandalone)
{
const PuglInternals* const impl = view->impl;

const pid_t pid = getpid();
const Atom _nwp = XInternAtom(impl->display, "_NET_WM_PID", False);
XChangeProperty(impl->display, impl->win, _nwp, XA_CARDINAL, 32, PropModeReplace, (const uchar*)&pid, 1);

const Atom _wt = XInternAtom(impl->display, "_NET_WM_WINDOW_TYPE", False);

Atom _wts[2];
int numAtoms = 0;

if (! isStandalone)
_wts[numAtoms++] = XInternAtom(impl->display, "_NET_WM_WINDOW_TYPE_DIALOG", False);

_wts[numAtoms++] = XInternAtom(impl->display, "_NET_WM_WINDOW_TYPE_NORMAL", False);

XChangeProperty(impl->display, impl->win, _wt, XA_ATOM, 32, PropModeReplace, (const uchar*)&_wts, numAtoms);
}

// --------------------------------------------------------------------------------------------------------------------

#endif // HAVE_X11

#ifndef DISTRHO_OS_MAC
END_NAMESPACE_DGL
#endif

+ 154
- 0
source/modules/dgl/src/pugl.hpp View File

@@ -0,0 +1,154 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef DGL_PUGL_HPP_INCLUDED
#define DGL_PUGL_HPP_INCLUDED

#include "../Base.hpp"

/* we will include all header files used in pugl in their C++ friendly form, then pugl stuff in custom namespace */
#include <cstddef>
#ifdef DISTRHO_PROPER_CPP11_SUPPORT
# include <cstdbool>
# include <cstdint>
#else
# include <stdbool.h>
# include <stdint.h>
#endif

#define PUGL_API
#define PUGL_DISABLE_DEPRECATED
#define PUGL_NO_INCLUDE_GLU_H

// --------------------------------------------------------------------------------------------------------------------

#ifndef DISTRHO_OS_MAC
START_NAMESPACE_DGL
#else
USE_NAMESPACE_DGL
#endif

#include "pugl-upstream/include/pugl/pugl.h"

// --------------------------------------------------------------------------------------------------------------------

PUGL_BEGIN_DECLS

// expose backend enter
PUGL_API bool
puglBackendEnter(PuglView* view);

// expose backend leave
PUGL_API void
puglBackendLeave(PuglView* view);

// clear minimum size to 0
PUGL_API void
puglClearMinSize(PuglView* view);

// missing in pugl, directly returns transient parent
PUGL_API PuglNativeView
puglGetTransientParent(const PuglView* view);

// missing in pugl, directly returns title char* pointer
PUGL_API const char*
puglGetWindowTitle(const PuglView* view);

// get global scale factor
PUGL_API double
puglGetDesktopScaleFactor(const PuglView* view);

// bring view window into the foreground, aka "raise" window
PUGL_API void
puglRaiseWindow(PuglView* view);

// DGL specific, assigns backend that matches current DGL build
PUGL_API void
puglSetMatchingBackendForCurrentBuild(PuglView* view);

// Combine puglSetMinSize and puglSetAspectRatio
PUGL_API PuglStatus
puglSetGeometryConstraints(PuglView* view, uint width, uint height, bool aspect);

// set window offset without changing size
PUGL_API PuglStatus
puglSetWindowOffset(PuglView* view, int x, int y);

// set window size with default size and without changing frame x/y position
PUGL_API PuglStatus
puglSetWindowSize(PuglView* view, uint width, uint height);

// DGL specific, build-specific drawing prepare
PUGL_API void
puglOnDisplayPrepare(PuglView* view);

// DGL specific, build-specific fallback resize
PUGL_API void
puglFallbackOnResize(PuglView* view);

#if defined(DISTRHO_OS_MAC)

// macOS specific, allow standalone window to gain focus
PUGL_API void
puglMacOSActivateApp();

// macOS specific, add another view's window as child
PUGL_API PuglStatus
puglMacOSAddChildWindow(PuglView* view, PuglView* child);

// macOS specific, remove another view's window as child
PUGL_API PuglStatus
puglMacOSRemoveChildWindow(PuglView* view, PuglView* child);

// macOS specific, center view based on parent coordinates (if there is one)
PUGL_API void
puglMacOSShowCentered(PuglView* view);

#elif defined(DISTRHO_OS_WINDOWS)

// win32 specific, call ShowWindow with SW_RESTORE
PUGL_API void
puglWin32RestoreWindow(PuglView* view);

// win32 specific, center view based on parent coordinates (if there is one)
PUGL_API void
puglWin32ShowCentered(PuglView* view);

// win32 specific, set or unset WS_SIZEBOX style flag
PUGL_API void
puglWin32SetWindowResizable(PuglView* view, bool resizable);

#elif defined(HAVE_X11)

// X11 specific, safer way to grab focus
PUGL_API PuglStatus
puglX11GrabFocus(const PuglView* view);

// X11 specific, set dialog window type and pid hints
PUGL_API void
puglX11SetWindowTypeAndPID(const PuglView* view, bool isStandalone);

#endif

PUGL_END_DECLS

// --------------------------------------------------------------------------------------------------------------------

#ifndef DISTRHO_OS_MAC
END_NAMESPACE_DGL
#endif

#endif // DGL_PUGL_HPP_INCLUDED

+ 1
- 0
source/modules/dgl/src/pugl.mm View File

@@ -0,0 +1 @@
pugl.cpp

+ 0
- 121
source/modules/dgl/src/pugl/common.h View File

@@ -1,121 +0,0 @@
/*
Copyright 2014 David Robillard <http://drobilla.net>

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef PUGL_COMMON_H_INCLUDED
#define PUGL_COMMON_H_INCLUDED

#ifdef __cplusplus
extern "C" {
#endif

/**
@addtogroup pugl
@{
*/

/**
A Pugl view.
*/
typedef struct PuglViewImpl PuglView;

/**
A native window handle.

On X11, this is a Window.
On OSX, this is an NSView*.
On Windows, this is a HWND.
*/
typedef intptr_t PuglNativeWindow;

/**
Handle for opaque user data.
*/
typedef void* PuglHandle;

/**
Return status code.
*/
typedef enum {
PUGL_SUCCESS = 0
} PuglStatus;

/**
Drawing context type.
*/
typedef enum {
PUGL_GL,
PUGL_CAIRO
} PuglContextType;

/**
Convenience symbols for ASCII control characters.
*/
typedef enum {
PUGL_CHAR_BACKSPACE = 0x08,
PUGL_CHAR_ESCAPE = 0x1B,
PUGL_CHAR_DELETE = 0x7F
} PuglChar;

/**
Keyboard modifier flags.
*/
typedef enum {
PUGL_MOD_SHIFT = 1 << 0, /**< Shift key */
PUGL_MOD_CTRL = 1 << 1, /**< Control key */
PUGL_MOD_ALT = 1 << 2, /**< Alt/Option key */
PUGL_MOD_SUPER = 1 << 3 /**< Mod4/Command/Windows key */
} PuglMod;

/**
Special (non-Unicode) keyboard keys.
*/
typedef enum {
PUGL_KEY_F1 = 1,
PUGL_KEY_F2,
PUGL_KEY_F3,
PUGL_KEY_F4,
PUGL_KEY_F5,
PUGL_KEY_F6,
PUGL_KEY_F7,
PUGL_KEY_F8,
PUGL_KEY_F9,
PUGL_KEY_F10,
PUGL_KEY_F11,
PUGL_KEY_F12,
PUGL_KEY_LEFT,
PUGL_KEY_UP,
PUGL_KEY_RIGHT,
PUGL_KEY_DOWN,
PUGL_KEY_PAGE_UP,
PUGL_KEY_PAGE_DOWN,
PUGL_KEY_HOME,
PUGL_KEY_END,
PUGL_KEY_INSERT,
PUGL_KEY_SHIFT,
PUGL_KEY_CTRL,
PUGL_KEY_ALT,
PUGL_KEY_SUPER
} PuglKey;

/**
@}
*/

#ifdef __cplusplus
} /* extern "C" */
#endif

#endif /* PUGL_COMMON_H_INCLUDED */

+ 0
- 41
source/modules/dgl/src/pugl/event.h View File

@@ -1,41 +0,0 @@
/*
Copyright 2014 David Robillard <http://drobilla.net>

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef PUGL_EVENT_H_INCLUDED
#define PUGL_EVENT_H_INCLUDED

#ifdef __cplusplus
extern "C" {
#else
# include <stdbool.h>
#endif

#include "pugl/common.h"

/**
@addtogroup pugl
@{
*/

/**
@}
*/

#ifdef __cplusplus
} /* extern "C" */
#endif

#endif /* PUGL_EVENT_H_INCLUDED */

+ 0
- 32
source/modules/dgl/src/pugl/gl.h View File

@@ -1,32 +0,0 @@
/*
Copyright 2012-2014 David Robillard <http://drobilla.net>

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

/**
@file gl.h Portable header wrapper for gl.h.

Unfortunately, GL includes vary across platforms so this header allows for
pure portable programs.
*/

#ifdef __APPLE__
# include "OpenGL/gl.h"
#else
# ifdef _WIN32
# include <windows.h> /* Broken Windows GL headers require this */
# endif
# include "GL/gl.h"
#endif


+ 0
- 32
source/modules/dgl/src/pugl/glu.h View File

@@ -1,32 +0,0 @@
/*
Copyright 2012-2014 David Robillard <http://drobilla.net>

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

/**
@file glu.h Portable header wrapper for glu.h.

Unfortunately, GL includes vary across platforms so this header allows for
pure portable programs.
*/

#ifdef __APPLE__
# include "OpenGL/glu.h"
#else
# ifdef _WIN32
# include <windows.h> /* Broken Windows GL headers require this */
# endif
# include "GL/glu.h"
#endif


+ 0
- 385
source/modules/dgl/src/pugl/pugl.h View File

@@ -1,385 +0,0 @@
/*
Copyright 2012-2014 David Robillard <http://drobilla.net>

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

/**
@file pugl.h API for Pugl, a minimal portable API for OpenGL.
*/

#ifndef PUGL_H_INCLUDED
#define PUGL_H_INCLUDED

#include <stdint.h>

#include "pugl/common.h"
#include "pugl/event.h"

#ifdef PUGL_SHARED
# ifdef _WIN32
# define PUGL_LIB_IMPORT __declspec(dllimport)
# define PUGL_LIB_EXPORT __declspec(dllexport)
# else
# define PUGL_LIB_IMPORT __attribute__((visibility("default")))
# define PUGL_LIB_EXPORT __attribute__((visibility("default")))
# endif
# ifdef PUGL_INTERNAL
# define PUGL_API PUGL_LIB_EXPORT
# else
# define PUGL_API PUGL_LIB_IMPORT
# endif
#else
# ifdef _WIN32
# define PUGL_API
# else
# define PUGL_API __attribute__((visibility("hidden")))
# endif
#endif

#ifdef __cplusplus
extern "C" {
#else
# include <stdbool.h>
#endif

/**
@defgroup pugl Pugl
A minimal portable API for OpenGL.
@{
*/

/**
A function called when the window is closed.
*/
typedef void (*PuglCloseFunc)(PuglView* view);

/**
A function called to draw the view contents with OpenGL.
*/
typedef void (*PuglDisplayFunc)(PuglView* view);

/**
A function called when a key is pressed or released.
@param view The view the event occured in.
@param press True if the key was pressed, false if released.
@param key Unicode point of the key pressed.
@return 0 if event was handled, otherwise send event to parent window.
*/
typedef int (*PuglKeyboardFunc)(PuglView* view, bool press, uint32_t key);

/**
A function called when the pointer moves.
@param view The view the event occured in.
@param x The window-relative x coordinate of the pointer.
@param y The window-relative y coordinate of the pointer.
*/
typedef void (*PuglMotionFunc)(PuglView* view, int x, int y);

/**
A function called when a mouse button is pressed or released.
@param view The view the event occured in.
@param button The button number (1 = left, 2 = middle, 3 = right).
@param press True if the key was pressed, false if released.
@param x The window-relative x coordinate of the pointer.
@param y The window-relative y coordinate of the pointer.
*/
typedef void (*PuglMouseFunc)(
PuglView* view, int button, bool press, int x, int y);

/**
A function called when the view is resized.
@param view The view being resized.
@param width The new view width.
@param height The new view height.
*/
typedef void (*PuglReshapeFunc)(PuglView* view, int width, int height);

/**
A function called on scrolling (e.g. mouse wheel or track pad).

The distances used here are in "lines", a single tick of a clicking mouse
wheel. For example, @p dy = 1.0 scrolls 1 line up. Some systems and
devices support finer resolution and/or higher values for fast scrolls,
so programs should handle any value gracefully.

@param view The view being scrolled.
@param x The window-relative x coordinate of the pointer.
@param y The window-relative y coordinate of the pointer.
@param dx The scroll x distance.
@param dx The scroll y distance.
*/
typedef void (*PuglScrollFunc)(PuglView* view, int x, int y, float dx, float dy);

/**
A function called when a special key is pressed or released.

This callback allows the use of keys that do not have unicode points.
Note that some are non-printable keys.

@param view The view the event occured in.
@param press True if the key was pressed, false if released.
@param key The key pressed.
@return 0 if event was handled, otherwise send event to parent window.
*/
typedef int (*PuglSpecialFunc)(PuglView* view, bool press, PuglKey key);

/**
A function called when a filename is selected via file-browser.

@param view The view the event occured in.
@param filename The selected file name or NULL if the dialog was canceled.
*/
typedef void (*PuglFileSelectedFunc)(PuglView* view, const char* filename);

/**
@name Initialization
Configuration functions which must be called before creating a window.
@{
*/

/**
Create a Pugl context.

To create a window, call the various puglInit* functions as necessary, then
call puglCreateWindow().
*/
PUGL_API PuglView*
puglInit(void);

/**
Set the parent window before creating a window (for embedding).
*/
PUGL_API void
puglInitWindowParent(PuglView* view, PuglNativeWindow parent);

/**
Set the window size before creating a window.
*/
PUGL_API void
puglInitWindowSize(PuglView* view, int width, int height);

/**
Set the minimum window size before creating a window.
*/
PUGL_API void
puglInitWindowMinSize(PuglView* view, int width, int height);

/**
Enable or disable resizing before creating a window.
*/
PUGL_API void
puglInitUserResizable(PuglView* view, bool resizable);

/**
Set transient parent before creating a window.

On X11, parent_id must be a Window.
On OSX, parent_id must be an NSView*.
*/
PUGL_API void
puglInitTransientFor(PuglView* view, uintptr_t parent);

/**
Set the context type before creating a window.
*/
PUGL_API void
puglInitContextType(PuglView* view, PuglContextType type);

/**
@}
*/

/**
@name Windows
Window management functions.
@{
*/

/**
Create a window with the settings given by the various puglInit functions.

@return 1 (pugl does not currently support multiple windows).
*/
PUGL_API int
puglCreateWindow(PuglView* view, const char* title);

/**
Show the current window.
*/
PUGL_API void
puglShowWindow(PuglView* view);

/**
Hide the current window.
*/
PUGL_API void
puglHideWindow(PuglView* view);

/**
Return the native window handle.
*/
PUGL_API PuglNativeWindow
puglGetNativeWindow(PuglView* view);

/**
@}
*/

/**
Set the handle to be passed to all callbacks.

This is generally a pointer to a struct which contains all necessary state.
Everything needed in callbacks should be here, not in static variables.

Note the lack of this facility makes GLUT unsuitable for plugins or
non-trivial programs; this mistake is largely why Pugl exists.
*/
PUGL_API void
puglSetHandle(PuglView* view, PuglHandle handle);

/**
Get the handle to be passed to all callbacks.
*/
PUGL_API PuglHandle
puglGetHandle(PuglView* view);

/**
Get the drawing context.

For PUGL_GL contexts, this is unused and returns NULL.
For PUGL_CAIRO contexts, this returns a pointer to a cairo_t.
*/
PUGL_API void*
puglGetContext(PuglView* view);

/**
Return the timestamp (if any) of the currently-processing event.
*/
PUGL_API uint32_t
puglGetEventTimestamp(PuglView* view);

/**
Get the currently active modifiers (PuglMod flags).

This should only be called from an event handler.
*/
PUGL_API int
puglGetModifiers(PuglView* view);

/**
Ignore synthetic repeated key events.
*/
PUGL_API void
puglIgnoreKeyRepeat(PuglView* view, bool ignore);

/**
@name Event Callbacks
Functions to set event callbacks for handling user input.
@{
*/

/**
Set the function to call when the window is closed.
*/
PUGL_API void
puglSetCloseFunc(PuglView* view, PuglCloseFunc closeFunc);

/**
Set the display function which should draw the UI using GL.
*/
PUGL_API void
puglSetDisplayFunc(PuglView* view, PuglDisplayFunc displayFunc);

/**
Set the function to call on keyboard events.
*/
PUGL_API void
puglSetKeyboardFunc(PuglView* view, PuglKeyboardFunc keyboardFunc);

/**
Set the function to call on mouse motion.
*/
PUGL_API void
puglSetMotionFunc(PuglView* view, PuglMotionFunc motionFunc);

/**
Set the function to call on mouse button events.
*/
PUGL_API void
puglSetMouseFunc(PuglView* view, PuglMouseFunc mouseFunc);

/**
Set the function to call on scroll events.
*/
PUGL_API void
puglSetScrollFunc(PuglView* view, PuglScrollFunc scrollFunc);

/**
Set the function to call on special events.
*/
PUGL_API void
puglSetSpecialFunc(PuglView* view, PuglSpecialFunc specialFunc);

/**
Set the function to call when the window size changes.
*/
PUGL_API void
puglSetReshapeFunc(PuglView* view, PuglReshapeFunc reshapeFunc);

/**
Set the function to call on file-browser selections.
*/
PUGL_API void
puglSetFileSelectedFunc(PuglView* view, PuglFileSelectedFunc fileSelectedFunc);

/**
@}
*/

/**
Grab the input focus.
*/
PUGL_API void
puglGrabFocus(PuglView* view);

/**
Process all pending window events.

This handles input events as well as rendering, so it should be called
regularly and rapidly enough to keep the UI responsive.
*/
PUGL_API PuglStatus
puglProcessEvents(PuglView* view);

/**
Request a redisplay on the next call to puglProcessEvents().
*/
PUGL_API void
puglPostRedisplay(PuglView* view);

/**
Destroy a GL window.
*/
PUGL_API void
puglDestroy(PuglView* view);

/**
@}
*/

#ifdef __cplusplus
} /* extern "C" */
#endif

#endif /* PUGL_H_INCLUDED */

+ 0
- 352
source/modules/dgl/src/pugl/pugl_internal.h View File

@@ -1,352 +0,0 @@
/*
Copyright 2012-2014 David Robillard <http://drobilla.net>

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

/**
@file pugl_internal.h Private platform-independent definitions.

Note this file contains function definitions, so it must be compiled into
the final binary exactly once. Each platform specific implementation file
including it once should achieve this.

If you are copying the pugl code into your source tree, the following
symbols can be defined to tweak pugl behaviour:

PUGL_HAVE_CAIRO: Include Cairo support code.
PUGL_HAVE_GL: Include OpenGL support code.
PUGL_GRAB_FOCUS: Work around reparent keyboard issues by grabbing focus.
PUGL_VERBOSE: Print GL information to console.
*/

#include "pugl/pugl.h"
#include "pugl/event.h"

#ifdef PUGL_VERBOSE
# include <stdio.h>
# define PUGL_LOG(str) fprintf(stderr, "pugl: " str)
# define PUGL_LOGF(fmt, ...) fprintf(stderr, "pugl: " fmt, __VA_ARGS__)
#else
# define PUGL_LOG(str)
# define PUGL_LOGF(fmt, ...)
#endif

typedef struct PuglInternalsImpl PuglInternals;

struct PuglViewImpl {
PuglHandle handle;
PuglCloseFunc closeFunc;
PuglDisplayFunc displayFunc;
PuglKeyboardFunc keyboardFunc;
PuglMotionFunc motionFunc;
PuglMouseFunc mouseFunc;
PuglReshapeFunc reshapeFunc;
PuglScrollFunc scrollFunc;
PuglSpecialFunc specialFunc;
PuglFileSelectedFunc fileSelectedFunc;

PuglInternals* impl;

PuglNativeWindow parent;
PuglContextType ctx_type;
uintptr_t transient_parent;

int width;
int height;
int min_width;
int min_height;
int mods;
bool mouse_in_view;
bool ignoreKeyRepeat;
bool redisplay;
bool resizable;
uint32_t event_timestamp_ms;
};

PuglInternals* puglInitInternals(void);

PuglView*
puglInit(void)
{
PuglView* view = (PuglView*)calloc(1, sizeof(PuglView));
if (!view) {
return NULL;
}

PuglInternals* impl = puglInitInternals();
if (!impl) {
free(view);
return NULL;
}

view->impl = impl;
view->width = 640;
view->height = 480;

return view;
}

void
puglInitWindowSize(PuglView* view, int width, int height)
{
view->width = width;
view->height = height;
}

void
puglInitWindowMinSize(PuglView* view, int width, int height)
{
view->min_width = width;
view->min_height = height;
}

void
puglInitWindowParent(PuglView* view, PuglNativeWindow parent)
{
view->parent = parent;
}

void
puglInitUserResizable(PuglView* view, bool resizable)
{
view->resizable = resizable;
}

void
puglInitTransientFor(PuglView* view, uintptr_t parent)
{
view->transient_parent = parent;
}

void
puglInitContextType(PuglView* view, PuglContextType type)
{
view->ctx_type = type;
}

void
puglSetHandle(PuglView* view, PuglHandle handle)
{
view->handle = handle;
}

PuglHandle
puglGetHandle(PuglView* view)
{
return view->handle;
}

uint32_t
puglGetEventTimestamp(PuglView* view)
{
return view->event_timestamp_ms;
}

int
puglGetModifiers(PuglView* view)
{
return view->mods;
}

void
puglIgnoreKeyRepeat(PuglView* view, bool ignore)
{
view->ignoreKeyRepeat = ignore;
}

void
puglSetCloseFunc(PuglView* view, PuglCloseFunc closeFunc)
{
view->closeFunc = closeFunc;
}

void
puglSetDisplayFunc(PuglView* view, PuglDisplayFunc displayFunc)
{
view->displayFunc = displayFunc;
}

void
puglSetKeyboardFunc(PuglView* view, PuglKeyboardFunc keyboardFunc)
{
view->keyboardFunc = keyboardFunc;
}

void
puglSetMotionFunc(PuglView* view, PuglMotionFunc motionFunc)
{
view->motionFunc = motionFunc;
}

void
puglSetMouseFunc(PuglView* view, PuglMouseFunc mouseFunc)
{
view->mouseFunc = mouseFunc;
}

void
puglSetReshapeFunc(PuglView* view, PuglReshapeFunc reshapeFunc)
{
view->reshapeFunc = reshapeFunc;
}

void
puglSetScrollFunc(PuglView* view, PuglScrollFunc scrollFunc)
{
view->scrollFunc = scrollFunc;
}

void
puglSetSpecialFunc(PuglView* view, PuglSpecialFunc specialFunc)
{
view->specialFunc = specialFunc;
}

void
puglSetFileSelectedFunc(PuglView* view, PuglFileSelectedFunc fileSelectedFunc)
{
view->fileSelectedFunc = fileSelectedFunc;
}

void
puglEnterContext(PuglView* view);

void
puglLeaveContext(PuglView* view, bool flush);

#if 0
/** Return the code point for buf, or the replacement character on error. */
static uint32_t
puglDecodeUTF8(const uint8_t* buf)
{
#define FAIL_IF(cond) { if (cond) return 0xFFFD; }

/* http://en.wikipedia.org/wiki/UTF-8 */

if (buf[0] < 0x80) {
return buf[0];
} else if (buf[0] < 0xC2) {
return 0xFFFD;
} else if (buf[0] < 0xE0) {
FAIL_IF((buf[1] & 0xC0) != 0x80);
return (buf[0] << 6) + buf[1] - 0x3080;
} else if (buf[0] < 0xF0) {
FAIL_IF((buf[1] & 0xC0) != 0x80);
FAIL_IF(buf[0] == 0xE0 && buf[1] < 0xA0);
FAIL_IF((buf[2] & 0xC0) != 0x80);
return (buf[0] << 12) + (buf[1] << 6) + buf[2] - 0xE2080;
} else if (buf[0] < 0xF5) {
FAIL_IF((buf[1] & 0xC0) != 0x80);
FAIL_IF(buf[0] == 0xF0 && buf[1] < 0x90);
FAIL_IF(buf[0] == 0xF4 && buf[1] >= 0x90);
FAIL_IF((buf[2] & 0xC0) != 0x80);
FAIL_IF((buf[3] & 0xC0) != 0x80);
return ((buf[0] << 18) +
(buf[1] << 12) +
(buf[2] << 6) +
buf[3] - 0x3C82080);
}
return 0xFFFD;
}
#endif

static void
puglDefaultReshape(PuglView* view, int width, int height)
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, width, height, 0, 0, 1);
glViewport(0, 0, width, height);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
return;

// unused
(void)view;
}

#if 0
static void
puglDispatchEvent(PuglView* view, const PuglEvent* event)
{
if (view->eventFunc) {
view->eventFunc(view, event);
}

switch (event->type) {
case PUGL_CONFIGURE:
puglEnterContext(view);
view->width = event->configure.width;
view->height = event->configure.height;
if (view->reshapeFunc) {
view->reshapeFunc(view, view->width, view->height);
}
puglLeaveContext(view, false);
break;
case PUGL_EXPOSE:
if (event->expose.count == 0) {
puglEnterContext(view);
if (view->displayFunc) {
view->displayFunc(view);
}
view->redisplay = false;
puglLeaveContext(view, true);
}
break;
case PUGL_MOTION_NOTIFY:
view->event_timestamp_ms = event->motion.time;
view->mods = event->motion.state;
if (view->motionFunc) {
view->motionFunc(view, event->motion.x, event->motion.y);
}
break;
case PUGL_SCROLL:
if (view->scrollFunc) {
view->scrollFunc(view,
event->scroll.x, event->scroll.y,
event->scroll.dx, event->scroll.dy);
}
break;
case PUGL_BUTTON_PRESS:
case PUGL_BUTTON_RELEASE:
view->event_timestamp_ms = event->button.time;
view->mods = event->button.state;
if (view->mouseFunc) {
view->mouseFunc(view,
event->button.button,
event->type == PUGL_BUTTON_PRESS,
event->button.x,
event->button.y);
}
break;
case PUGL_KEY_PRESS:
case PUGL_KEY_RELEASE:
view->event_timestamp_ms = event->key.time;
view->mods = event->key.state;
if (event->key.special && view->specialFunc) {
view->specialFunc(view,
event->type == PUGL_KEY_PRESS,
event->key.special);
} else if (event->key.character && view->keyboardFunc) {
view->keyboardFunc(view,
event->type == PUGL_KEY_PRESS,
event->key.character);
}
break;
default:
break;
}
}
#endif

+ 0
- 576
source/modules/dgl/src/pugl/pugl_osx.m View File

@@ -1,576 +0,0 @@
/*
Copyright 2012 David Robillard <http://drobilla.net>

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

/**
@file pugl_osx.m OSX/Cocoa Pugl Implementation.
*/

#include <stdlib.h>

#import <Cocoa/Cocoa.h>

#include "pugl_internal.h"

@interface PuglWindow : NSWindow
{
@public
PuglView* puglview;
}

- (id) initWithContentRect:(NSRect)contentRect
styleMask:(unsigned int)aStyle
backing:(NSBackingStoreType)bufferingType
defer:(BOOL)flag;
- (void) setPuglview:(PuglView*)view;
- (BOOL) canBecomeKeyWindow;
- (BOOL) windowShouldClose:(id)sender;
@end

@implementation PuglWindow

- (id)initWithContentRect:(NSRect)contentRect
styleMask:(unsigned int)aStyle
backing:(NSBackingStoreType)bufferingType
defer:(BOOL)flag
{
NSWindow* result = [super initWithContentRect:contentRect
styleMask:(NSClosableWindowMask |
NSTitledWindowMask |
NSResizableWindowMask)
backing:NSBackingStoreBuffered defer:NO];

[result setAcceptsMouseMovedEvents:YES];
[result setLevel: CGShieldingWindowLevel() + 1];

return (PuglWindow*)result;

// unused
(void)aStyle; (void)bufferingType; (void)flag;
}

- (void)setPuglview:(PuglView*)view
{
puglview = view;
[self setContentSize:NSMakeSize(view->width, view->height)];
}

- (BOOL)canBecomeKeyWindow
{
return YES;
}

- (BOOL)windowShouldClose:(id)sender
{
if (puglview->closeFunc)
puglview->closeFunc(puglview);
return YES;

// unused
(void)sender;
}

@end

static void
puglDisplay(PuglView* view)
{
view->redisplay = false;
if (view->displayFunc) {
view->displayFunc(view);
}
}

@interface PuglOpenGLView : NSOpenGLView
{
@public
PuglView* puglview;
NSTrackingArea* trackingArea;
bool doubleBuffered;
}

- (BOOL) acceptsFirstMouse:(NSEvent*)e;
- (BOOL) acceptsFirstResponder;
- (BOOL) isFlipped;
- (BOOL) isOpaque;
- (BOOL) preservesContentInLiveResize;
- (id) initWithFrame:(NSRect)frame;
- (void) reshape;
- (void) drawRect:(NSRect)r;
- (void) cursorUpdate:(NSEvent*)e;
- (void) updateTrackingAreas;
- (void) viewWillMoveToWindow:(NSWindow*)newWindow;
- (void) mouseMoved:(NSEvent*)event;
- (void) mouseDragged:(NSEvent*)event;
- (void) rightMouseDragged:(NSEvent*)event;
- (void) otherMouseDragged:(NSEvent*)event;
- (void) mouseDown:(NSEvent*)event;
- (void) rightMouseDown:(NSEvent*)event;
- (void) otherMouseDown:(NSEvent*)event;
- (void) mouseUp:(NSEvent*)event;
- (void) rightMouseUp:(NSEvent*)event;
- (void) otherMouseUp:(NSEvent*)event;
- (void) scrollWheel:(NSEvent*)event;
- (void) keyDown:(NSEvent*)event;
- (void) keyUp:(NSEvent*)event;
- (void) flagsChanged:(NSEvent*)event;

@end

@implementation PuglOpenGLView

- (BOOL) acceptsFirstMouse:(NSEvent*)e
{
return YES;

// unused
(void)e;
}

- (BOOL) acceptsFirstResponder
{
return YES;
}

- (BOOL) isFlipped
{
return YES;
}

- (BOOL) isOpaque
{
return YES;
}

- (BOOL) preservesContentInLiveResize
{
return NO;
}

- (id) initWithFrame:(NSRect)frame
{
puglview = nil;
trackingArea = nil;
doubleBuffered = true;

NSOpenGLPixelFormatAttribute pixelAttribs[] = {
NSOpenGLPFAColorSize, 24,
NSOpenGLPFAAlphaSize, 8,
NSOpenGLPFADepthSize, 16,
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAAccelerated,
0
};

NSOpenGLPixelFormat* pixelFormat = [[NSOpenGLPixelFormat alloc]
initWithAttributes:pixelAttribs];

if (pixelFormat) {
self = [super initWithFrame:frame pixelFormat:pixelFormat];
[pixelFormat release];
printf("Is doubleBuffered? TRUE\n");
} else {
self = [super initWithFrame:frame];
doubleBuffered = false;
printf("Is doubleBuffered? FALSE\n");
}

if (self) {
GLint swapInterval = 1;
[[self openGLContext] setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval];

[self reshape];
}

return self;
}

- (void) reshape
{
if (!puglview) {
/* NOTE: Apparently reshape gets called when the GC gets around to
deleting the view (?), so we must have reset puglview to NULL when
this comes around.
*/
return;
}

[[self openGLContext] update];

NSRect bounds = [self bounds];
int width = bounds.size.width;
int height = bounds.size.height;

puglEnterContext(puglview);

if (puglview->reshapeFunc) {
puglview->reshapeFunc(puglview, width, height);
} else {
puglDefaultReshape(puglview, width, height);
}

puglLeaveContext(puglview, false);

puglview->width = width;
puglview->height = height;
}

- (void) drawRect:(NSRect)r
{
puglEnterContext(puglview);
puglDisplay(puglview);
puglLeaveContext(puglview, true);

// unused
return; (void)r;
}

- (void) cursorUpdate:(NSEvent*)e
{
[[NSCursor arrowCursor] set];

// unused
return; (void)e;
}

- (void) updateTrackingAreas
{
static const int opts = NSTrackingMouseEnteredAndExited
| NSTrackingMouseMoved
| NSTrackingEnabledDuringMouseDrag
| NSTrackingInVisibleRect
| NSTrackingActiveAlways
| NSTrackingCursorUpdate;

if (trackingArea != nil) {
[self removeTrackingArea:trackingArea];
[trackingArea release];
}

trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds]
options:opts
owner:self
userInfo:nil];
[self addTrackingArea:trackingArea];
[super updateTrackingAreas];
}

- (void) viewWillMoveToWindow:(NSWindow*)newWindow
{
if (newWindow != nil) {
[newWindow setAcceptsMouseMovedEvents:YES];
[newWindow makeFirstResponder:self];
}

[super viewWillMoveToWindow:newWindow];
}

static unsigned
getModifiers(PuglView* view, NSEvent* ev)
{
const unsigned modifierFlags = [ev modifierFlags];

view->event_timestamp_ms = fmod([ev timestamp] * 1000.0, UINT32_MAX);

unsigned mods = 0;
mods |= (modifierFlags & NSShiftKeyMask) ? PUGL_MOD_SHIFT : 0;
mods |= (modifierFlags & NSControlKeyMask) ? PUGL_MOD_CTRL : 0;
mods |= (modifierFlags & NSAlternateKeyMask) ? PUGL_MOD_ALT : 0;
mods |= (modifierFlags & NSCommandKeyMask) ? PUGL_MOD_SUPER : 0;
return mods;
}

static int
getFixedAppKitButton(NSInteger button)
{
switch (button) {
case 0: return 1;
case 1: return 3;
case 2: return 2;
default: return button;
}
}

- (void) mouseMoved:(NSEvent*)event
{
if (puglview->motionFunc) {
NSPoint loc = [self convertPoint:[event locationInWindow] fromView:nil];
puglview->mods = getModifiers(puglview, event);
puglview->motionFunc(puglview, loc.x, loc.y);
}
}

- (void) mouseDragged:(NSEvent*)event
{
[self mouseMoved:event];
}

- (void) rightMouseDragged:(NSEvent*)event
{
[self mouseDragged:event];
}

- (void) otherMouseDragged:(NSEvent*)event
{
[self mouseDragged:event];
}

- (void) mouseDown:(NSEvent*)event
{
if (puglview->mouseFunc) {
NSPoint loc = [self convertPoint:[event locationInWindow] fromView:nil];
puglview->mods = getModifiers(puglview, event);
puglview->mouseFunc(puglview, getFixedAppKitButton([event buttonNumber]), true, loc.x, loc.y);
}
}

- (void) rightMouseDown:(NSEvent*)event
{
[self mouseDown:event];
}

- (void) otherMouseDown:(NSEvent*)event
{
[self mouseDown:event];
}

- (void) mouseUp:(NSEvent*)event
{
if (puglview->mouseFunc) {
NSPoint loc = [self convertPoint:[event locationInWindow] fromView:nil];
puglview->mods = getModifiers(puglview, event);
puglview->mouseFunc(puglview, getFixedAppKitButton([event buttonNumber]), false, loc.x, loc.y);
}
}

- (void) rightMouseUp:(NSEvent*)event
{
[self mouseUp:event];
}

- (void) otherMouseUp:(NSEvent*)event
{
[self mouseUp:event];
}

- (void) scrollWheel:(NSEvent*)event
{
if (puglview->scrollFunc) {
NSPoint loc = [self convertPoint:[event locationInWindow] fromView:nil];
puglview->mods = getModifiers(puglview, event);
puglview->scrollFunc(puglview,
loc.x, loc.y,
[event deltaX], [event deltaY]);
}
}

- (void) keyDown:(NSEvent*)event
{
if (puglview->keyboardFunc && !(puglview->ignoreKeyRepeat && [event isARepeat])) {
NSString* chars = [event characters];
puglview->mods = getModifiers(puglview, event);
puglview->keyboardFunc(puglview, true, [chars characterAtIndex:0]);
}
}

- (void) keyUp:(NSEvent*)event
{
if (puglview->keyboardFunc) {
NSString* chars = [event characters];
puglview->mods = getModifiers(puglview, event);
puglview->keyboardFunc(puglview, false, [chars characterAtIndex:0]);
}
}

- (void) flagsChanged:(NSEvent*)event
{
if (puglview->specialFunc) {
const unsigned mods = getModifiers(puglview, event);
if ((mods & PUGL_MOD_SHIFT) != (puglview->mods & PUGL_MOD_SHIFT)) {
puglview->specialFunc(puglview, mods & PUGL_MOD_SHIFT, PUGL_KEY_SHIFT);
} else if ((mods & PUGL_MOD_CTRL) != (puglview->mods & PUGL_MOD_CTRL)) {
puglview->specialFunc(puglview, mods & PUGL_MOD_CTRL, PUGL_KEY_CTRL);
} else if ((mods & PUGL_MOD_ALT) != (puglview->mods & PUGL_MOD_ALT)) {
puglview->specialFunc(puglview, mods & PUGL_MOD_ALT, PUGL_KEY_ALT);
} else if ((mods & PUGL_MOD_SUPER) != (puglview->mods & PUGL_MOD_SUPER)) {
puglview->specialFunc(puglview, mods & PUGL_MOD_SUPER, PUGL_KEY_SUPER);
}
puglview->mods = mods;
}
}

@end

struct PuglInternalsImpl {
PuglOpenGLView* glview;
id window;
};

PuglInternals*
puglInitInternals()
{
return (PuglInternals*)calloc(1, sizeof(PuglInternals));
}

void
puglEnterContext(PuglView* view)
{
#ifdef PUGL_HAVE_GL
if (view->ctx_type == PUGL_GL) {
[[view->impl->glview openGLContext] makeCurrentContext];
}
#endif
}

void
puglLeaveContext(PuglView* view, bool flush)
{
#ifdef PUGL_HAVE_GL
if (view->ctx_type == PUGL_GL && flush) {
if (view->impl->glview->doubleBuffered) {
[[view->impl->glview openGLContext] flushBuffer];
} else {
glFlush();
}
//[NSOpenGLContext clearCurrentContext];
}
#endif
}

int
puglCreateWindow(PuglView* view, const char* title)
{
PuglInternals* impl = view->impl;

[NSAutoreleasePool new];
[NSApplication sharedApplication];

impl->glview = [PuglOpenGLView new];

if (!impl->glview) {
return 1;
}
impl->glview->puglview = view;

if (view->resizable) {
[impl->glview setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
}

if (view->parent) {
[impl->glview retain];
NSView* pview = (NSView*)view->parent;
[pview addSubview:impl->glview];
return 0;
}

id window = [[PuglWindow new]retain];

if (title) {
NSString* titleString = [[NSString alloc]
initWithBytes:title
length:strlen(title)
encoding:NSUTF8StringEncoding];

[window setTitle:titleString];
}

[window setPuglview:view];
[window setContentView:impl->glview];
[window makeFirstResponder:impl->glview];
[window makeKeyAndOrderFront:window];

// wait for first puglShowWindow
[window setIsVisible:NO];

[NSApp activateIgnoringOtherApps:YES];
[window center];

impl->window = window;

return 0;
}

void
puglShowWindow(PuglView* view)
{
PuglInternals* impl = view->impl;

if (impl->window) {
[impl->window setIsVisible:YES];
} else {
[view->impl->glview setHidden:NO];
}
}

void
puglHideWindow(PuglView* view)
{
PuglInternals* impl = view->impl;

if (impl->window) {
[impl->window setIsVisible:NO];
} else {
[impl->glview setHidden:YES];
}
}

void
puglDestroy(PuglView* view)
{
view->impl->glview->puglview = NULL;

if (view->impl->window) {
[view->impl->window close];
[view->impl->glview release];
[view->impl->window release];
} else {
[view->impl->glview release];
}

free(view->impl);
free(view);
}

PuglStatus
puglProcessEvents(PuglView* view)
{
return PUGL_SUCCESS;

// unused
(void)view;
}

void
puglPostRedisplay(PuglView* view)
{
view->redisplay = true;
[view->impl->glview setNeedsDisplay:YES];
}

PuglNativeWindow
puglGetNativeWindow(PuglView* view)
{
return (PuglNativeWindow)view->impl->glview;
}

void*
puglGetContext(PuglView* view)
{
return NULL;

// unused
(void)view;
}

+ 0
- 489
source/modules/dgl/src/pugl/pugl_win.cpp View File

@@ -1,489 +0,0 @@
/*
Copyright 2012-2014 David Robillard <http://drobilla.net>

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

/**
@file pugl_win.cpp Windows/WGL Pugl Implementation.
*/

#include <winsock2.h>
#include <windows.h>
#include <windowsx.h>
#include <GL/gl.h>

#include <ctime>
#include <cstdio>
#include <cstdlib>

#include "pugl/pugl_internal.h"

#ifndef WM_MOUSEWHEEL
# define WM_MOUSEWHEEL 0x020A
#endif
#ifndef WM_MOUSEHWHEEL
# define WM_MOUSEHWHEEL 0x020E
#endif
#ifndef WHEEL_DELTA
# define WHEEL_DELTA 120
#endif
#ifndef GWLP_USERDATA
# define GWLP_USERDATA (-21)
#endif

#define PUGL_LOCAL_CLOSE_MSG (WM_USER + 50)

HINSTANCE hInstance = NULL;

struct PuglInternalsImpl {
HWND hwnd;
HDC hdc;
HGLRC hglrc;
WNDCLASS wc;
};

LRESULT CALLBACK
wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);

#if 0
extern "C" {
BOOL WINAPI
DllMain(HINSTANCE hInst, DWORD, LPVOID)
{
hInstance = hInst;
return 1;
}
} // extern "C"
#endif

PuglInternals*
puglInitInternals()
{
return (PuglInternals*)calloc(1, sizeof(PuglInternals));
}

void
puglEnterContext(PuglView* view)
{
#ifdef PUGL_HAVE_GL
if (view->ctx_type == PUGL_GL) {
wglMakeCurrent(view->impl->hdc, view->impl->hglrc);
}
#endif
}

void
puglLeaveContext(PuglView* view, bool flush)
{
#ifdef PUGL_HAVE_GL
if (view->ctx_type == PUGL_GL) {
if (flush) {
glFlush();
SwapBuffers(view->impl->hdc);
}
wglMakeCurrent(NULL, NULL);
}
#endif
}

int
puglCreateWindow(PuglView* view, const char* title)
{
PuglInternals* impl = view->impl;

if (!title) {
title = "Window";
}

// FIXME: This is nasty, and pugl should not have static anything.
// Should class be a parameter? Does this make sense on other platforms?
static int wc_count = 0;
char classNameBuf[256];
std::srand((std::time(NULL)));
#ifdef __WINE__
std::snprintf(classNameBuf, sizeof(classNameBuf), "%s_%d-%d", title, std::rand(), ++wc_count);
#else
_snprintf(classNameBuf, sizeof(classNameBuf), "%s_%d-%d", title, std::rand(), ++wc_count);
#endif
classNameBuf[sizeof(classNameBuf)-1] = '\0';

impl->wc.style = CS_OWNDC;
impl->wc.lpfnWndProc = wndProc;
impl->wc.cbClsExtra = 0;
impl->wc.cbWndExtra = 0;
impl->wc.hInstance = hInstance;
impl->wc.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
impl->wc.hCursor = LoadCursor(hInstance, IDC_ARROW);
impl->wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
impl->wc.lpszMenuName = NULL;
impl->wc.lpszClassName = strdup(classNameBuf);

if (!RegisterClass(&impl->wc)) {
free((void*)impl->wc.lpszClassName);
free(impl);
free(view);
return 1;
}

int winFlags = WS_POPUPWINDOW | WS_CAPTION;
if (view->resizable) {
winFlags |= WS_SIZEBOX;
if (view->min_width > 0 && view->min_height > 0) {
// Adjust the minimum window size to accomodate requested view size
RECT mr = { 0, 0, view->min_width, view->min_height };
AdjustWindowRectEx(&mr, view->parent ? WS_CHILD : winFlags, FALSE, WS_EX_TOPMOST);
view->min_width = mr.right - mr.left;
view->min_height = mr.bottom - mr.top;
}
}

// Adjust the window size to accomodate requested view size
RECT wr = { 0, 0, view->width, view->height };
AdjustWindowRectEx(&wr, view->parent ? WS_CHILD : winFlags, FALSE, WS_EX_TOPMOST);

impl->hwnd = CreateWindowEx(
WS_EX_TOPMOST,
classNameBuf, title,
view->parent ? (WS_CHILD | WS_VISIBLE) : winFlags,
CW_USEDEFAULT, CW_USEDEFAULT, wr.right-wr.left, wr.bottom-wr.top,
(HWND)view->parent, NULL, hInstance, NULL);

if (!impl->hwnd) {
UnregisterClass(impl->wc.lpszClassName, NULL);
free((void*)impl->wc.lpszClassName);
free(impl);
free(view);
return 1;
}

SetWindowLongPtr(impl->hwnd, GWLP_USERDATA, (LONG_PTR)view);

impl->hdc = GetDC(impl->hwnd);

PIXELFORMATDESCRIPTOR pfd;
ZeroMemory(&pfd, sizeof(pfd));
pfd.nSize = sizeof(pfd);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 24;
pfd.cDepthBits = 16;
pfd.iLayerType = PFD_MAIN_PLANE;

int format = ChoosePixelFormat(impl->hdc, &pfd);
SetPixelFormat(impl->hdc, format, &pfd);

impl->hglrc = wglCreateContext(impl->hdc);
if (!impl->hglrc) {
ReleaseDC (impl->hwnd, impl->hdc);
DestroyWindow (impl->hwnd);
UnregisterClass (impl->wc.lpszClassName, NULL);
free((void*)impl->wc.lpszClassName);
free(impl);
free(view);
return 1;
}

return PUGL_SUCCESS;
}

void
puglShowWindow(PuglView* view)
{
ShowWindow(view->impl->hwnd, SW_SHOWNORMAL);
}

void
puglHideWindow(PuglView* view)
{
ShowWindow(view->impl->hwnd, SW_HIDE);
}

void
puglDestroy(PuglView* view)
{
wglMakeCurrent(NULL, NULL);
wglDeleteContext(view->impl->hglrc);
ReleaseDC(view->impl->hwnd, view->impl->hdc);
DestroyWindow(view->impl->hwnd);
UnregisterClass(view->impl->wc.lpszClassName, NULL);
free((void*)view->impl->wc.lpszClassName);
free(view->impl);
free(view);
}

static void
puglReshape(PuglView* view, int width, int height)
{
puglEnterContext(view);

if (view->reshapeFunc) {
view->reshapeFunc(view, width, height);
} else {
puglDefaultReshape(view, width, height);
}

view->width = width;
view->height = height;
}

static void
puglDisplay(PuglView* view)
{
puglEnterContext(view);

view->redisplay = false;
if (view->displayFunc) {
view->displayFunc(view);
}

puglLeaveContext(view, true);
}

static PuglKey
keySymToSpecial(int sym)
{
switch (sym) {
case VK_F1: return PUGL_KEY_F1;
case VK_F2: return PUGL_KEY_F2;
case VK_F3: return PUGL_KEY_F3;
case VK_F4: return PUGL_KEY_F4;
case VK_F5: return PUGL_KEY_F5;
case VK_F6: return PUGL_KEY_F6;
case VK_F7: return PUGL_KEY_F7;
case VK_F8: return PUGL_KEY_F8;
case VK_F9: return PUGL_KEY_F9;
case VK_F10: return PUGL_KEY_F10;
case VK_F11: return PUGL_KEY_F11;
case VK_F12: return PUGL_KEY_F12;
case VK_LEFT: return PUGL_KEY_LEFT;
case VK_UP: return PUGL_KEY_UP;
case VK_RIGHT: return PUGL_KEY_RIGHT;
case VK_DOWN: return PUGL_KEY_DOWN;
case VK_PRIOR: return PUGL_KEY_PAGE_UP;
case VK_NEXT: return PUGL_KEY_PAGE_DOWN;
case VK_HOME: return PUGL_KEY_HOME;
case VK_END: return PUGL_KEY_END;
case VK_INSERT: return PUGL_KEY_INSERT;
case VK_SHIFT: return PUGL_KEY_SHIFT;
case VK_CONTROL: return PUGL_KEY_CTRL;
case VK_MENU: return PUGL_KEY_ALT;
case VK_LWIN: return PUGL_KEY_SUPER;
case VK_RWIN: return PUGL_KEY_SUPER;
}
return (PuglKey)0;
}

static void
processMouseEvent(PuglView* view, int button, bool press, LPARAM lParam)
{
view->event_timestamp_ms = GetMessageTime();
if (press) {
SetCapture(view->impl->hwnd);
} else {
ReleaseCapture();
}

if (view->mouseFunc) {
view->mouseFunc(view, button, press,
GET_X_LPARAM(lParam),
GET_Y_LPARAM(lParam));
}
}

static void
setModifiers(PuglView* view)
{
view->mods = 0;
view->mods |= (GetKeyState(VK_SHIFT) < 0) ? PUGL_MOD_SHIFT : 0;
view->mods |= (GetKeyState(VK_CONTROL) < 0) ? PUGL_MOD_CTRL : 0;
view->mods |= (GetKeyState(VK_MENU) < 0) ? PUGL_MOD_ALT : 0;
view->mods |= (GetKeyState(VK_LWIN) < 0) ? PUGL_MOD_SUPER : 0;
view->mods |= (GetKeyState(VK_RWIN) < 0) ? PUGL_MOD_SUPER : 0;
}

static LRESULT
handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
PuglKey key;
RECT rect;
MINMAXINFO* mmi;

setModifiers(view);
switch (message) {
case WM_CREATE:
case WM_SHOWWINDOW:
case WM_SIZE:
GetClientRect(view->impl->hwnd, &rect);
puglReshape(view, rect.right, rect.bottom);
view->width = rect.right;
view->height = rect.bottom;
break;
case WM_GETMINMAXINFO:
mmi = (MINMAXINFO*)lParam;
mmi->ptMinTrackSize.x = view->min_width;
mmi->ptMinTrackSize.y = view->min_height;
break;
case WM_PAINT:
BeginPaint(view->impl->hwnd, &ps);
puglDisplay(view);
EndPaint(view->impl->hwnd, &ps);
break;
case WM_MOUSEMOVE:
if (view->motionFunc) {
view->event_timestamp_ms = GetMessageTime();
view->motionFunc(view, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
}
break;
case WM_LBUTTONDOWN:
processMouseEvent(view, 1, true, lParam);
break;
case WM_MBUTTONDOWN:
processMouseEvent(view, 2, true, lParam);
break;
case WM_RBUTTONDOWN:
processMouseEvent(view, 3, true, lParam);
break;
case WM_LBUTTONUP:
processMouseEvent(view, 1, false, lParam);
break;
case WM_MBUTTONUP:
processMouseEvent(view, 2, false, lParam);
break;
case WM_RBUTTONUP:
processMouseEvent(view, 3, false, lParam);
break;
case WM_MOUSEWHEEL:
if (view->scrollFunc) {
view->event_timestamp_ms = GetMessageTime();
POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
ScreenToClient(view->impl->hwnd, &pt);
view->scrollFunc(
view, pt.x, pt.y,
0.0f, GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA);
}
break;
case WM_MOUSEHWHEEL:
if (view->scrollFunc) {
view->event_timestamp_ms = GetMessageTime();
POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
ScreenToClient(view->impl->hwnd, &pt);
view->scrollFunc(
view, pt.x, pt.y,
GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA, 0.0f);
}
break;
case WM_KEYDOWN:
if (view->ignoreKeyRepeat && (lParam & (1 << 30))) {
break;
} // else nobreak
case WM_KEYUP:
view->event_timestamp_ms = GetMessageTime();
if ((key = keySymToSpecial(wParam))) {
if (view->specialFunc) {
view->specialFunc(view, message == WM_KEYDOWN, key);
}
} else if (view->keyboardFunc) {
static BYTE kbs[256];
if (GetKeyboardState(kbs) != FALSE) {
char lb[2];
UINT scanCode = (lParam >> 8) & 0xFFFFFF00;
if ( 1 == ToAscii(wParam, scanCode, kbs, (LPWORD)lb, 0)) {
view->keyboardFunc(view, message == WM_KEYDOWN, (char)lb[0]);
}
}
}
break;
case WM_QUIT:
case PUGL_LOCAL_CLOSE_MSG:
if (view->closeFunc) {
view->closeFunc(view);
view->redisplay = false;
}
break;
default:
return DefWindowProc(
view->impl->hwnd, message, wParam, lParam);
}

return 0;
}

void
puglGrabFocus(PuglView* /*view*/)
{
// TODO
}

PuglStatus
puglProcessEvents(PuglView* view)
{
MSG msg;
while (PeekMessage(&msg, view->impl->hwnd, 0, 0, PM_REMOVE)) {
handleMessage(view, msg.message, msg.wParam, msg.lParam);
}

if (view->redisplay) {
InvalidateRect(view->impl->hwnd, NULL, FALSE);
}

return PUGL_SUCCESS;
}

LRESULT CALLBACK
wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PuglView* view = (PuglView*)GetWindowLongPtr(hwnd, GWLP_USERDATA);

switch (message) {
case WM_CREATE:
PostMessage(hwnd, WM_SHOWWINDOW, TRUE, 0);
return 0;
case WM_CLOSE:
PostMessage(hwnd, PUGL_LOCAL_CLOSE_MSG, wParam, lParam);
return 0;
case WM_DESTROY:
return 0;
default:
if (view && hwnd == view->impl->hwnd) {
return handleMessage(view, message, wParam, lParam);
} else {
return DefWindowProc(hwnd, message, wParam, lParam);
}
}
}

void
puglPostRedisplay(PuglView* view)
{
view->redisplay = true;
}

PuglNativeWindow
puglGetNativeWindow(PuglView* view)
{
return (PuglNativeWindow)view->impl->hwnd;
}

void*
puglGetContext(PuglView* /*view*/)
{
#ifdef PUGL_HAVE_CAIRO
if (view->ctx_type == PUGL_CAIRO) {
// TODO
}
#endif
return NULL;
}

+ 0
- 636
source/modules/dgl/src/pugl/pugl_x11.c View File

@@ -1,636 +0,0 @@
/*
Copyright 2012-2014 David Robillard <http://drobilla.net>
Copyright 2013 Robin Gareus <robin@gareus.org>
Copyright 2011-2012 Ben Loftis, Harrison Consoles

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

/**
@file pugl_x11.c X11 Pugl Implementation.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>

#ifdef PUGL_HAVE_GL
#include <GL/gl.h>
#include <GL/glx.h>
#endif

#ifdef PUGL_HAVE_CAIRO
#include <cairo/cairo.h>
#include <cairo/cairo-xlib.h>
#endif

#include "pugl/pugl_internal.h"

#ifndef DGL_FILE_BROWSER_DISABLED
#define SOFD_HAVE_X11
#include "../sofd/libsofd.h"
#include "../sofd/libsofd.c"
#endif

struct PuglInternalsImpl {
Display* display;
int screen;
Window win;
XIM xim;
XIC xic;
#ifdef PUGL_HAVE_CAIRO
cairo_t* cr;
cairo_surface_t* surface;
#endif
#ifdef PUGL_HAVE_GL
GLXContext ctx;
Bool doubleBuffered;
#endif
};

PuglInternals*
puglInitInternals(void)
{
return (PuglInternals*)calloc(1, sizeof(PuglInternals));
}

static XVisualInfo*
getVisual(PuglView* view)
{
PuglInternals* const impl = view->impl;
XVisualInfo* vi = NULL;

#ifdef PUGL_HAVE_GL
if (view->ctx_type == PUGL_GL) {
/**
Attributes for single-buffered RGBA with at least
4 bits per color and a 16 bit depth buffer.
*/
int attrListSgl[] = {
GLX_RGBA,
GLX_RED_SIZE, 4,
GLX_GREEN_SIZE, 4,
GLX_BLUE_SIZE, 4,
GLX_DEPTH_SIZE, 16,
GLX_ARB_multisample, 1,
None
};

/**
Attributes for double-buffered RGBA with at least
4 bits per color and a 16 bit depth buffer.
*/
int attrListDbl[] = {
GLX_RGBA,
GLX_DOUBLEBUFFER,
GLX_RED_SIZE, 4,
GLX_GREEN_SIZE, 4,
GLX_BLUE_SIZE, 4,
GLX_DEPTH_SIZE, 16,
GLX_ARB_multisample, 1,
None
};

/**
Attributes for double-buffered RGBA with multi-sampling
(antialiasing)
*/
int attrListDblMS[] = {
GLX_RGBA,
GLX_DOUBLEBUFFER,
GLX_RED_SIZE, 4,
GLX_GREEN_SIZE, 4,
GLX_BLUE_SIZE, 4,
GLX_ALPHA_SIZE, 4,
GLX_DEPTH_SIZE, 16,
GLX_SAMPLE_BUFFERS, 1,
GLX_SAMPLES, 4,
None
};

impl->doubleBuffered = True;

vi = glXChooseVisual(impl->display, impl->screen, attrListDblMS);

if (vi == NULL) {
vi = glXChooseVisual(impl->display, impl->screen, attrListDbl);
PUGL_LOG("multisampling (antialiasing) is not available\n");
}

if (vi == NULL) {
vi = glXChooseVisual(impl->display, impl->screen, attrListSgl);
impl->doubleBuffered = False;
PUGL_LOG("singlebuffered rendering will be used, no doublebuffering available\n");
}
}
#endif
#ifdef PUGL_HAVE_CAIRO
if (view->ctx_type == PUGL_CAIRO) {
XVisualInfo pat;
int n;
pat.screen = impl->screen;
vi = XGetVisualInfo(impl->display, VisualScreenMask, &pat, &n);
}
#endif

return vi;
}

static bool
createContext(PuglView* view, XVisualInfo* vi)
{
PuglInternals* const impl = view->impl;

#ifdef PUGL_HAVE_GL
if (view->ctx_type == PUGL_GL) {
impl->ctx = glXCreateContext(impl->display, vi, 0, GL_TRUE);
return (impl->ctx != NULL);
}
#endif
#ifdef PUGL_HAVE_CAIRO
if (view->ctx_type == PUGL_CAIRO) {
impl->surface = cairo_xlib_surface_create(
impl->display, impl->win, vi->visual, view->width, view->height);
if (impl->surface == NULL) {
PUGL_LOG("failed to create cairo surface\n");
return false;
}
impl->cr = cairo_create(impl->surface);
if (impl->cr == NULL) {
cairo_surface_destroy(impl->surface);
impl->surface = NULL;
PUGL_LOG("failed to create cairo context\n");
return false;
}
return true;
}
#endif

return false;
}

static void
destroyContext(PuglView* view)
{
PuglInternals* const impl = view->impl;

#ifdef PUGL_HAVE_GL
if (view->ctx_type == PUGL_GL) {
glXDestroyContext(impl->display, impl->ctx);
impl->ctx = NULL;
}
#endif
#ifdef PUGL_HAVE_CAIRO
if (view->ctx_type == PUGL_CAIRO) {
cairo_destroy(impl->cr);
impl->cr = NULL;

cairo_surface_destroy(impl->surface);
impl->surface = NULL;
}
#endif
}

void
puglEnterContext(PuglView* view)
{
#ifdef PUGL_HAVE_GL
if (view->ctx_type == PUGL_GL) {
glXMakeCurrent(view->impl->display, view->impl->win, view->impl->ctx);
}
#endif
}

void
puglLeaveContext(PuglView* view, bool flush)
{
#ifdef PUGL_HAVE_GL
if (view->ctx_type == PUGL_GL) {
if (flush) {
glFlush();
if (view->impl->doubleBuffered) {
glXSwapBuffers(view->impl->display, view->impl->win);
}
}
glXMakeCurrent(view->impl->display, None, NULL);
}
#endif
}

int
puglCreateWindow(PuglView* view, const char* title)
{
PuglInternals* const impl = view->impl;

impl->display = XOpenDisplay(NULL);
impl->screen = DefaultScreen(impl->display);

XVisualInfo* const vi = getVisual(view);
if (!vi) {
XCloseDisplay(impl->display);
impl->display = NULL;
return 1;
}

#ifdef PUGL_HAVE_GL
int glxMajor, glxMinor;
glXQueryVersion(impl->display, &glxMajor, &glxMinor);
PUGL_LOGF("GLX Version %d.%d\n", glxMajor, glxMinor);
#endif

Window xParent = view->parent
? (Window)view->parent
: RootWindow(impl->display, impl->screen);

Colormap cmap = XCreateColormap(
impl->display, xParent, vi->visual, AllocNone);

XSetWindowAttributes attr;
memset(&attr, 0, sizeof(XSetWindowAttributes));
attr.background_pixel = BlackPixel(impl->display, impl->screen);
attr.border_pixel = BlackPixel(impl->display, impl->screen);
attr.colormap = cmap;
attr.event_mask = (ExposureMask | StructureNotifyMask |
EnterWindowMask | LeaveWindowMask |
KeyPressMask | KeyReleaseMask |
ButtonPressMask | ButtonReleaseMask |
PointerMotionMask | FocusChangeMask);

impl->win = XCreateWindow(
impl->display, xParent,
0, 0, view->width, view->height, 0, vi->depth, InputOutput, vi->visual,
CWBackPixel | CWBorderPixel | CWColormap | CWEventMask, &attr);

if (!createContext(view, vi)) {
XDestroyWindow(impl->display, impl->win);
impl->win = 0;

XCloseDisplay(impl->display);
impl->display = NULL;

return 1;
}

XSizeHints sizeHints;
memset(&sizeHints, 0, sizeof(sizeHints));
if (!view->resizable) {
sizeHints.flags = PMinSize|PMaxSize;
sizeHints.min_width = view->width;
sizeHints.min_height = view->height;
sizeHints.max_width = view->width;
sizeHints.max_height = view->height;
XSetNormalHints(impl->display, impl->win, &sizeHints);
} else if (view->min_width > 0 && view->min_height > 0) {
sizeHints.flags = PMinSize;
sizeHints.min_width = view->min_width;
sizeHints.min_height = view->min_height;
XSetNormalHints(impl->display, impl->win, &sizeHints);
}

if (title) {
XStoreName(impl->display, impl->win, title);
}

if (!view->parent) {
Atom wmDelete = XInternAtom(impl->display, "WM_DELETE_WINDOW", True);
XSetWMProtocols(impl->display, impl->win, &wmDelete, 1);
}

if (glXIsDirect(impl->display, impl->ctx)) {
PUGL_LOG("DRI enabled (to disable, set LIBGL_ALWAYS_INDIRECT=1\n");
} else {
PUGL_LOG("No DRI available\n");
}

XFree(vi);

return PUGL_SUCCESS;
}

void
puglShowWindow(PuglView* view)
{
XMapRaised(view->impl->display, view->impl->win);
}

void
puglHideWindow(PuglView* view)
{
XUnmapWindow(view->impl->display, view->impl->win);
}

void
puglDestroy(PuglView* view)
{
if (!view) {
return;
}

#ifndef DGL_FILE_BROWSER_DISABLED
x_fib_close(view->impl->display);
#endif

destroyContext(view);
XDestroyWindow(view->impl->display, view->impl->win);
XCloseDisplay(view->impl->display);
free(view->impl);
free(view);
}

static void
puglReshape(PuglView* view, int width, int height)
{
puglEnterContext(view);

if (view->reshapeFunc) {
view->reshapeFunc(view, width, height);
} else {
puglDefaultReshape(view, width, height);
}

puglLeaveContext(view, false);

view->width = width;
view->height = height;
}

static void
puglDisplay(PuglView* view)
{
puglEnterContext(view);

view->redisplay = false;

if (view->displayFunc) {
view->displayFunc(view);
}

puglLeaveContext(view, true);
}

static PuglKey
keySymToSpecial(KeySym sym)
{
switch (sym) {
case XK_F1: return PUGL_KEY_F1;
case XK_F2: return PUGL_KEY_F2;
case XK_F3: return PUGL_KEY_F3;
case XK_F4: return PUGL_KEY_F4;
case XK_F5: return PUGL_KEY_F5;
case XK_F6: return PUGL_KEY_F6;
case XK_F7: return PUGL_KEY_F7;
case XK_F8: return PUGL_KEY_F8;
case XK_F9: return PUGL_KEY_F9;
case XK_F10: return PUGL_KEY_F10;
case XK_F11: return PUGL_KEY_F11;
case XK_F12: return PUGL_KEY_F12;
case XK_Left: return PUGL_KEY_LEFT;
case XK_Up: return PUGL_KEY_UP;
case XK_Right: return PUGL_KEY_RIGHT;
case XK_Down: return PUGL_KEY_DOWN;
case XK_Page_Up: return PUGL_KEY_PAGE_UP;
case XK_Page_Down: return PUGL_KEY_PAGE_DOWN;
case XK_Home: return PUGL_KEY_HOME;
case XK_End: return PUGL_KEY_END;
case XK_Insert: return PUGL_KEY_INSERT;
case XK_Shift_L: return PUGL_KEY_SHIFT;
case XK_Shift_R: return PUGL_KEY_SHIFT;
case XK_Control_L: return PUGL_KEY_CTRL;
case XK_Control_R: return PUGL_KEY_CTRL;
case XK_Alt_L: return PUGL_KEY_ALT;
case XK_Alt_R: return PUGL_KEY_ALT;
case XK_Super_L: return PUGL_KEY_SUPER;
case XK_Super_R: return PUGL_KEY_SUPER;
}
return (PuglKey)0;
}

static void
setModifiers(PuglView* view, unsigned xstate, unsigned xtime)
{
view->event_timestamp_ms = xtime;

view->mods = 0;
view->mods |= (xstate & ShiftMask) ? PUGL_MOD_SHIFT : 0;
view->mods |= (xstate & ControlMask) ? PUGL_MOD_CTRL : 0;
view->mods |= (xstate & Mod1Mask) ? PUGL_MOD_ALT : 0;
view->mods |= (xstate & Mod4Mask) ? PUGL_MOD_SUPER : 0;
}

static void
dispatchKey(PuglView* view, XEvent* event, bool press)
{
KeySym sym;
char str[5];
PuglKey special;
const int n = XLookupString(&event->xkey, str, 4, &sym, NULL);

if (sym == XK_Escape && view->closeFunc && !press && !view->parent) {
view->closeFunc(view);
view->redisplay = false;
return;
}
if (n == 0 && sym == 0) {
goto send_event;
return;
}
if (n > 1) {
fprintf(stderr, "warning: Unsupported multi-byte key %X\n", (int)sym);
goto send_event;
return;
}

special = keySymToSpecial(sym);
if (special && view->specialFunc) {
if (view->specialFunc(view, press, special) == 0) {
return;
}
} else if (!special && view->keyboardFunc) {
if (view->keyboardFunc(view, press, str[0]) == 0) {
return;
}
}

send_event:
if (view->parent != 0) {
event->xkey.time = 0; // purposefully set an invalid time, used for feedback detection on bad hosts
event->xany.window = view->parent;
XSendEvent(view->impl->display, view->parent, False, NoEventMask, event);
}
}

PuglStatus
puglProcessEvents(PuglView* view)
{
XEvent event;
while (XPending(view->impl->display) > 0) {
XNextEvent(view->impl->display, &event);

#ifndef DGL_FILE_BROWSER_DISABLED
if (x_fib_handle_events(view->impl->display, &event)) {
const int status = x_fib_status();

if (status > 0) {
char* const filename = x_fib_filename();
x_fib_close(view->impl->display);
if (view->fileSelectedFunc) {
view->fileSelectedFunc(view, filename);
}
free(filename);
} else if (status < 0) {
x_fib_close(view->impl->display);
if (view->fileSelectedFunc) {
view->fileSelectedFunc(view, NULL);
}
}
break;
}
#endif

if (event.xany.window != view->impl->win &&
(view->parent == 0 || event.xany.window != (Window)view->parent)) {
continue;
}
if ((event.type == KeyPress || event.type == KeyRelease) && event.xkey.time == 0) {
continue;
}

switch (event.type) {
case MapNotify:
puglReshape(view, view->width, view->height);
break;
case ConfigureNotify:
if ((event.xconfigure.width != view->width) ||
(event.xconfigure.height != view->height)) {
puglReshape(view,
event.xconfigure.width,
event.xconfigure.height);
}
break;
case Expose:
if (event.xexpose.count != 0) {
break;
}
puglDisplay(view);
break;
case MotionNotify:
setModifiers(view, event.xmotion.state, event.xmotion.time);
if (view->motionFunc) {
view->motionFunc(view, event.xmotion.x, event.xmotion.y);
}
break;
case ButtonPress:
setModifiers(view, event.xbutton.state, event.xbutton.time);
if (event.xbutton.button >= 4 && event.xbutton.button <= 7) {
if (view->scrollFunc) {
float dx = 0, dy = 0;
switch (event.xbutton.button) {
case 4: dy = 1.0f; break;
case 5: dy = -1.0f; break;
case 6: dx = -1.0f; break;
case 7: dx = 1.0f; break;
}
view->scrollFunc(view, event.xbutton.x, event.xbutton.y, dx, dy);
}
break;
}
// nobreak
case ButtonRelease:
setModifiers(view, event.xbutton.state, event.xbutton.time);
if (view->mouseFunc &&
(event.xbutton.button < 4 || event.xbutton.button > 7)) {
view->mouseFunc(view,
event.xbutton.button, event.type == ButtonPress,
event.xbutton.x, event.xbutton.y);
}
break;
case KeyPress:
setModifiers(view, event.xkey.state, event.xkey.time);
dispatchKey(view, &event, true);
break;
case KeyRelease: {
setModifiers(view, event.xkey.state, event.xkey.time);
bool repeated = false;
if (view->ignoreKeyRepeat &&
XEventsQueued(view->impl->display, QueuedAfterReading)) {
XEvent next;
XPeekEvent(view->impl->display, &next);
if (next.type == KeyPress &&
next.xkey.time == event.xkey.time &&
next.xkey.keycode == event.xkey.keycode) {
XNextEvent(view->impl->display, &event);
repeated = true;
}
}
if (!repeated) {
dispatchKey(view, &event, false);
}
} break;
case ClientMessage: {
char* type = XGetAtomName(view->impl->display,
event.xclient.message_type);
if (!strcmp(type, "WM_PROTOCOLS")) {
if (view->closeFunc) {
view->closeFunc(view);
view->redisplay = false;
}
}
XFree(type);
} break;
#ifdef PUGL_GRAB_FOCUS
case EnterNotify:
XSetInputFocus(view->impl->display, view->impl->win, RevertToPointerRoot, CurrentTime);
break;
#endif
default:
break;
}
}

if (view->redisplay) {
puglDisplay(view);
}

return PUGL_SUCCESS;
}

void
puglPostRedisplay(PuglView* view)
{
view->redisplay = true;
}

PuglNativeWindow
puglGetNativeWindow(PuglView* view)
{
return view->impl->win;
}

void*
puglGetContext(PuglView* view)
{
#ifdef PUGL_HAVE_CAIRO
if (view->ctx_type == PUGL_CAIRO) {
return view->impl->cr;
}
#endif
return NULL;

// may be unused
(void)view;
}

+ 0
- 570
source/modules/distrho/DistrhoInfo.hpp View File

@@ -1,570 +0,0 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifdef DOXYGEN

#include "src/DistrhoDefines.h"

START_NAMESPACE_DISTRHO

/* ------------------------------------------------------------------------------------------------------------
* Intro */

/**
@mainpage DISTRHO %Plugin Framework

DISTRHO %Plugin Framework (or @b DPF for short)
is a plugin framework designed to make development of new plugins an easy and enjoyable task.@n
It allows developers to create plugins with custom UIs using a simple C++ API.@n
The framework facilitates exporting various different plugin formats from the same code-base.

DPF can build for LADSPA, DSSI, LV2 and VST2 formats.@n
A JACK/Standalone mode is also available, allowing you to quickly test plugins.

@section Macros
You start by creating a "DistrhoPluginInfo.h" file describing the plugin via macros, see @ref PluginMacros.@n
This file is included in the main DPF code to select which features to activate for each plugin format.

For example, a plugin (with %UI) that use states will require LV2 hosts to support Atom and Worker extensions for
message passing from the %UI to the plugin.@n
If your plugin does not make use of states, the Worker extension is not set as a required feature.

@section Plugin
The next step is to create your plugin code by subclassing DPF's Plugin class.@n
You need to pass the number of parameters in the constructor and also the number of programs and states, if any.

Here's an example of an audio plugin that simply mutes the host output:
@code
class MutePlugin : public Plugin
{
public:
/**
Plugin class constructor.
*/
MutePlugin()
: Plugin(0, 0, 0) // 0 parameters, 0 programs and 0 states
{
}

protected:
/* ----------------------------------------------------------------------------------------
* Information */

/**
Get the plugin label.
This label is a short restricted name consisting of only _, a-z, A-Z and 0-9 characters.
*/
const char* getLabel() const override
{
return "Mute";
}

/**
Get the plugin author/maker.
*/
const char* getMaker() const override
{
return "DPF";
}

/**
Get the plugin license name (a single line of text).
For commercial plugins this should return some short copyright information.
*/
const char* getLicense() const override
{
return "MIT";
}

/**
Get the plugin version, in hexadecimal.
*/
uint32_t getVersion() const override
{
return d_version(1, 0, 0);
}

/**
Get the plugin unique Id.
This value is used by LADSPA, DSSI and VST plugin formats.
*/
int64_t getUniqueId() const override
{
return d_cconst('M', 'u', 't', 'e');
}

/* ----------------------------------------------------------------------------------------
* This example has no parameters, so skip parameter stuff */

void initParameter(uint32_t, Parameter&) override {}
float getParameterValue(uint32_t) const override { return 0.0f; }
void setParameterValue(uint32_t, float) override {}

/* ----------------------------------------------------------------------------------------
* Audio/MIDI Processing */

/**
Run/process function for plugins without MIDI input.
NOTE: Some parameters might be null if there are no audio inputs or outputs.
*/
void run(const float**, float** outputs, uint32_t frames) override
{
// get the left and right audio outputs
float* const outL = outputs[0];
float* const outR = outputs[1];

// mute audio
std::memset(outL, 0, sizeof(float)*frames);
std::memset(outR, 0, sizeof(float)*frames);
}

};
@endcode

See the Plugin class for more information and to understand what each function does.

@section Parameters
A plugin is nothing without parameters.@n
In DPF parameters can be inputs or outputs.@n
They have hints to describe how they behave plus a name and a symbol identifying them.@n
Parameters also have 'ranges' – a minimum, maximum and default value.

Input parameters are "read-only": the plugin can read them but not change them.
(the exception being when changing programs, more on that below)@n
It's the host responsibility to save, restore and set input parameters.

Output parameters can be changed at anytime by the plugin.@n
The host will simply read their values and not change them.

Here's an example of an audio plugin that has 1 input parameter:
@code
class GainPlugin : public Plugin
{
public:
/**
Plugin class constructor.
You must set all parameter values to their defaults, matching ParameterRanges::def.
*/
GainPlugin()
: Plugin(1, 0, 0), // 1 parameter, 0 programs and 0 states
fGain(1.0f)
{
}

protected:
/* ----------------------------------------------------------------------------------------
* Information */

const char* getLabel() const override
{
return "Gain";
}

const char* getMaker() const override
{
return "DPF";
}

const char* getLicense() const override
{
return "MIT";
}

uint32_t getVersion() const override
{
return d_version(1, 0, 0);
}

int64_t getUniqueId() const override
{
return d_cconst('G', 'a', 'i', 'n');
}

/* ----------------------------------------------------------------------------------------
* Init */

/**
Initialize a parameter.
This function will be called once, shortly after the plugin is created.
*/
void initParameter(uint32_t index, Parameter& parameter) override
{
// we only have one parameter so we can skip checking the index

parameter.hints = kParameterIsAutomable;
parameter.name = "Gain";
parameter.symbol = "gain";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 2.0f;
parameter.ranges.def = 1.0f;
}

/* ----------------------------------------------------------------------------------------
* Internal data */

/**
Get the current value of a parameter.
*/
float getParameterValue(uint32_t index) const override
{
// same as before, ignore index check

return fGain;
}

/**
Change a parameter value.
*/
void setParameterValue(uint32_t index, float value) override
{
// same as before, ignore index check

fGain = value;
}

/* ----------------------------------------------------------------------------------------
* Audio/MIDI Processing */

void run(const float**, float** outputs, uint32_t frames) override
{
// get the mono input and output
const float* const in = inputs[0];
/* */ float* const out = outputs[0];

// apply gain against all samples
for (uint32_t i=0; i < frames; ++i)
out[i] = in[i] * fGain;
}

private:
float fGain;
};
@endcode

See the Parameter struct for more information about parameters.

@section Programs
Programs in DPF refer to plugin-side presets (usually called "factory presets"),
an initial set of presets provided by plugin authors included in the actual plugin.

To use programs you must first enable them by setting @ref DISTRHO_PLUGIN_WANT_PROGRAMS to 1 in your DistrhoPluginInfo.h file.@n
When enabled you'll need to override 2 new function in your plugin code,
Plugin::initProgramName(uint32_t, String&) and Plugin::loadProgram(uint32_t).

Here's an example of a plugin with a "default" program:
@code
class PluginWithPresets : public Plugin
{
public:
PluginWithPresets()
: Plugin(2, 1, 0), // 2 parameters, 1 program and 0 states
fGainL(1.0f),
fGainR(1.0f),
{
}

protected:
/* ----------------------------------------------------------------------------------------
* Information */

const char* getLabel() const override
{
return "Prog";
}

const char* getMaker() const override
{
return "DPF";
}

const char* getLicense() const override
{
return "MIT";
}

uint32_t getVersion() const override
{
return d_version(1, 0, 0);
}

int64_t getUniqueId() const override
{
return d_cconst('P', 'r', 'o', 'g');
}

/* ----------------------------------------------------------------------------------------
* Init */

/**
Initialize a parameter.
This function will be called once, shortly after the plugin is created.
*/
void initParameter(uint32_t index, Parameter& parameter) override
{
parameter.hints = kParameterIsAutomable;
parameter.ranges.min = 0.0f;
parameter.ranges.max = 2.0f;
parameter.ranges.def = 1.0f;

switch (index)
{
case 0;
parameter.name = "Gain Right";
parameter.symbol = "gainR";
break;
case 1;
parameter.name = "Gain Left";
parameter.symbol = "gainL";
break;
}
}

/**
Set the name of the program @a index.
This function will be called once, shortly after the plugin is created.
*/
void initProgramName(uint32_t index, String& programName)
{
switch(index)
{
case 0:
programName = "Default";
break;
}
}

/* ----------------------------------------------------------------------------------------
* Internal data */

/**
Get the current value of a parameter.
*/
float getParameterValue(uint32_t index) const override
{
switch (index)
{
case 0;
return fGainL;
case 1;
return fGainR;
}
}

/**
Change a parameter value.
*/
void setParameterValue(uint32_t index, float value) override
{
switch (index)
{
case 0;
fGainL = value;
break;
case 1;
fGainR = value;
break;
}
}

/**
Load a program.
*/
void loadProgram(uint32_t index)
{
switch(index)
{
case 0:
fGainL = 1.0f;
fGainR = 1.0f;
break;
}
}

/* ----------------------------------------------------------------------------------------
* Audio/MIDI Processing */

void run(const float**, float** outputs, uint32_t frames) override
{
// get the left and right audio buffers
const float* const inL = inputs[0];
const float* const inR = inputs[0];
/* */ float* const outL = outputs[0];
/* */ float* const outR = outputs[0];

// apply gain against all samples
for (uint32_t i=0; i < frames; ++i)
{
outL[i] = inL[i] * fGainL;
outR[i] = inR[i] * fGainR;
}
}

private:
float fGainL, fGainR;
};
@endcode

@section States
describe them

@section MIDI
describe them

@section Latency
describe it

@section Time-Position
describe it

@section UI
describe them
*/

/* ------------------------------------------------------------------------------------------------------------
* Plugin Macros */

/**
@defgroup PluginMacros Plugin Macros

C Macros that describe your plugin. (defined in the "DistrhoPluginInfo.h" file)

With these macros you can tell the host what features your plugin requires.@n
Depending on which macros you enable, new functions will be available to call and/or override.

All values are either integer or strings.@n
For boolean-like values 1 means 'on' and 0 means 'off'.

The values defined in this group are for documentation purposes only.@n
All macros are disabled by default.

Only 4 macros are required, they are:
- @ref DISTRHO_PLUGIN_NAME
- @ref DISTRHO_PLUGIN_NUM_INPUTS
- @ref DISTRHO_PLUGIN_NUM_OUTPUTS
- @ref DISTRHO_PLUGIN_URI
@{
*/

/**
The plugin name.@n
This is used to identify your plugin before a Plugin instance can be created.
@note This macro is required.
*/
#define DISTRHO_PLUGIN_NAME "Plugin Name"

/**
Number of audio inputs the plugin has.
@note This macro is required.
*/
#define DISTRHO_PLUGIN_NUM_INPUTS 2

/**
Number of audio outputs the plugin has.
@note This macro is required.
*/
#define DISTRHO_PLUGIN_NUM_OUTPUTS 2

/**
The plugin URI when exporting in LV2 format.
@note This macro is required.
*/
#define DISTRHO_PLUGIN_URI "urn:distrho:name"

/**
Wherever the plugin has a custom %UI.
@see DISTRHO_UI_USE_NANOVG
@see UI
*/
#define DISTRHO_PLUGIN_HAS_UI 1

/**
Wherever the plugin processing is realtime-safe.@n
TODO - list rtsafe requirements
*/
#define DISTRHO_PLUGIN_IS_RT_SAFE 1

/**
Wherever the plugin is a synth.@n
@ref DISTRHO_PLUGIN_WANT_MIDI_INPUT is automatically enabled when this is too.
@see DISTRHO_PLUGIN_WANT_MIDI_INPUT
*/
#define DISTRHO_PLUGIN_IS_SYNTH 1

/**
Enable direct access between the %UI and plugin code.
@see UI::getPluginInstancePointer()
@note DO NOT USE THIS UNLESS STRICTLY NECESSARY!!
Try to avoid it at all costs!
*/
#define DISTRHO_PLUGIN_WANT_DIRECT_ACCESS 0

/**
Wherever the plugin introduces latency during audio or midi processing.
@see Plugin::setLatency(uint32_t)
*/
#define DISTRHO_PLUGIN_WANT_LATENCY 1

/**
Wherever the plugin wants MIDI input.@n
This is automatically enabled if @ref DISTRHO_PLUGIN_IS_SYNTH is true.
*/
#define DISTRHO_PLUGIN_WANT_MIDI_INPUT 1

/**
Wherever the plugin wants MIDI output.
@see Plugin::writeMidiEvent(const MidiEvent&)
*/
#define DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 1

/**
Wherever the plugin provides its own internal programs.
@see Plugin::initProgramName(uint32_t, String&)
@see Plugin::loadProgram(uint32_t)
*/
#define DISTRHO_PLUGIN_WANT_PROGRAMS 1

/**
Wherever the plugin uses internal non-parameter data.
@see Plugin::initState(uint32_t, String&, String&)
@see Plugin::setState(const char*, const char*)
*/
#define DISTRHO_PLUGIN_WANT_STATE 1

/**
Wherever the plugin wants time position information from the host.
@see Plugin::getTimePosition()
*/
#define DISTRHO_PLUGIN_WANT_TIMEPOS 1

/**
Wherever the %UI uses NanoVG for drawing instead of the default raw OpenGL calls.@n
When enabled your %UI instance will subclass @ref NanoWidget instead of @ref Widget.
*/
#define DISTRHO_UI_USE_NANOVG 1

/**
The %UI URI when exporting in LV2 format.@n
By default this is set to @ref DISTRHO_PLUGIN_URI with "#UI" as suffix.
*/
#define DISTRHO_UI_URI DISTRHO_PLUGIN_URI "#UI"

/** @} */

// -----------------------------------------------------------------------------------------------------------

END_NAMESPACE_DISTRHO

#endif // DOXYGEN

+ 382
- 59
source/modules/distrho/DistrhoPlugin.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -35,15 +35,43 @@ START_NAMESPACE_DISTRHO
*/

/**
Audio port can be used as control voltage (LV2 only).
Audio port can be used as control voltage (LV2 and JACK standalone only).
*/
static const uint32_t kAudioPortIsCV = 0x1;

/**
Audio port should be used as sidechan (LV2 only).
Audio port should be used as sidechan (LV2 and VST3 only).
This hint should not be used with CV style ports.
@note non-sidechain audio ports must exist in the plugin if this flag is set.
*/
static const uint32_t kAudioPortIsSidechain = 0x2;

/**
CV port has bipolar range (-1 to +1, or -5 to +5 if scaled).
This is merely a hint to tell the host what value range to expect.
*/
static const uint32_t kCVPortHasBipolarRange = 0x10;

/**
CV port has negative unipolar range (-1 to 0, or -10 to 0 if scaled).
This is merely a hint to tell the host what value range to expect.
*/
static const uint32_t kCVPortHasNegativeUnipolarRange = 0x20;

/**
CV port has positive unipolar range (0 to +1, or 0 to +10 if scaled).
This is merely a hint to tell the host what value range to expect.
*/
static const uint32_t kCVPortHasPositiveUnipolarRange = 0x40;

/**
CV port has scaled range to match real values (-5 to +5v bipolar, +/-10 to 0v unipolar).
One other range flag is required if this flag is set.

When enabled, this makes the port a mod:CVPort, compatible with the MOD Devices platform.
*/
static const uint32_t kCVPortHasScaledRange = 0x80;

/** @} */

/* ------------------------------------------------------------------------------------------------------------
@@ -58,10 +86,14 @@ static const uint32_t kAudioPortIsSidechain = 0x2;
*/

/**
Parameter is automable (real-time safe).
Parameter is automatable (real-time safe).
@see Plugin::setParameterValue(uint32_t, float)
*/
static const uint32_t kParameterIsAutomable = 0x01;
static const uint32_t kParameterIsAutomatable = 0x01;

/** It was a typo, sorry.. */
DISTRHO_DEPRECATED_BY("kParameterIsAutomatable")
static const uint32_t kParameterIsAutomable = kParameterIsAutomatable;

/**
Parameter value is boolean.@n
@@ -83,9 +115,12 @@ static const uint32_t kParameterIsLogarithmic = 0x08;
Parameter is of output type.@n
When unset, parameter is assumed to be of input type.

Parameter inputs are changed by the host and must not be changed by the plugin.@n
The only exception being when changing programs, see Plugin::loadProgram().@n
Parameter inputs are changed by the host and typically should not be changed by the plugin.@n
One exception is when changing programs, see Plugin::loadProgram().@n
The other exception is with parameter change requests, see Plugin::requestParameterValueChange().@n
Outputs are changed by the plugin and never modified by the host.

If you are targetting VST2, make sure to order your parameters so that all inputs are before any outputs.
*/
static const uint32_t kParameterIsOutput = 0x10;

@@ -100,6 +135,52 @@ static const uint32_t kParameterIsTrigger = 0x20 | kParameterIsBoolean;

/** @} */

/* ------------------------------------------------------------------------------------------------------------
* State Hints */

/**
@defgroup StateHints State Hints

Various state hints.
@see State::hints
@{
*/

/**
State is visible and readable by hosts that support string-type plugin parameters.
*/
static const uint32_t kStateIsHostReadable = 0x01;

/**
State is writable by the host, allowing users to arbitrarily change the state.@n
For obvious reasons a writable state is also readable by the host.
*/
static const uint32_t kStateIsHostWritable = 0x02 | kStateIsHostReadable;

/**
State is a filename path instead of a regular string.@n
The readable and writable hints are required for filenames to work, and thus are automatically set.
*/
static const uint32_t kStateIsFilenamePath = 0x04 | kStateIsHostWritable;

/**
State is a base64 encoded string.
*/
static const uint32_t kStateIsBase64Blob = 0x08;

/**
State is for Plugin/DSP side only, meaning there is never a need to notify the UI when it changes.
*/
static const uint32_t kStateIsOnlyForDSP = 0x10;

/**
State is for UI side only.@n
If the DSP and UI are separate and the UI is not available, this property won't be saved.
*/
static const uint32_t kStateIsOnlyForUI = 0x20;

/** @} */

/* ------------------------------------------------------------------------------------------------------------
* Base Plugin structs */

@@ -108,8 +189,61 @@ static const uint32_t kParameterIsTrigger = 0x20 | kParameterIsBoolean;
@{
*/

/**
Parameter designation.@n
Allows a parameter to be specially designated for a task, like bypass.

Each designation is unique, there must be only one parameter that uses it.@n
The use of designated parameters is completely optional.

@note Designated parameters have strict ranges.
@see ParameterRanges::adjustForDesignation()
*/
enum ParameterDesignation {
/**
Null or unset designation.
*/
kParameterDesignationNull = 0,

/**
Bypass designation.@n
When on (> 0.5f), it means the plugin must run in a bypassed state.
*/
kParameterDesignationBypass = 1
};

/**
Predefined Port Groups Ids.

This enumeration provides a few commonly used groups for convenient use in plugins.
For preventing conflicts with user code, negative values are used here.
When rolling your own port groups, you MUST start their group ids from 0 and they MUST be sequential.

@see PortGroup
*/
enum PredefinedPortGroupsIds {
/**
Null or unset port group.
*/
kPortGroupNone = (uint32_t)-1,

/**
A single channel audio group.
*/
kPortGroupMono = (uint32_t)-2,

/**
A 2-channel discrete stereo audio group,
where the 1st audio port is the left channel and the 2nd port is the right channel.
*/
kPortGroupStereo = (uint32_t)-3
};

/**
Audio Port.

Can be used as CV port by specifying kAudioPortIsCV in hints,@n
but this is only supported in LV2 and JACK standalone formats.
*/
struct AudioPort {
/**
@@ -134,35 +268,23 @@ struct AudioPort {
String symbol;

/**
Default constructor for a regular audio port.
*/
AudioPort() noexcept
: hints(0x0),
name(),
symbol() {}
};

/**
Parameter designation.@n
Allows a parameter to be specially designated for a task, like bypass.

Each designation is unique, there must be only one parameter that uses it.@n
The use of designated parameters is completely optional.
The group id that this audio/cv port belongs to.
No group is assigned by default.

@note Designated parameters have strict ranges.
@see ParameterRanges::adjustForDesignation()
*/
enum ParameterDesignation {
/**
Null or unset designation.
You can use a group from PredefinedPortGroups or roll your own.@n
When rolling your own port groups, you MUST start their group ids from 0 and they MUST be sequential.
@see PortGroup, Plugin::initPortGroup
*/
kParameterDesignationNull = 0,
uint32_t groupId;

/**
Bypass designation.@n
When on (> 0.5f), it means the plugin must run in a bypassed state.
Default constructor for a regular audio port.
*/
kParameterDesignationBypass = 1
AudioPort() noexcept
: hints(0x0),
name(),
symbol(),
groupId(kPortGroupNone) {}
};

/**
@@ -189,7 +311,7 @@ struct ParameterRanges {
float max;

/**
Default constructor, using 0.0 as minimum, 1.0 as maximum and 0.0 as default.
Default constructor, using 0.0 as default, 0.0 as minimum, 1.0 as maximum.
*/
ParameterRanges() noexcept
: def(0.0f),
@@ -226,7 +348,7 @@ struct ParameterRanges {
/**
Get a fixed value within range.
*/
const float& getFixedValue(const float& value) const noexcept
float getFixedValue(const float& value) const noexcept
{
if (value <= min)
return min;
@@ -333,9 +455,9 @@ struct ParameterEnumerationValues {

/**
Array of @ParameterEnumerationValue items.@n
This pointer must be null or have been allocated on the heap with `new`.
This pointer must be null or have been allocated on the heap with `new ParameterEnumerationValue[count]`.
*/
const ParameterEnumerationValue* values;
ParameterEnumerationValue* values;

/**
Default constructor, for zero enumeration values.
@@ -349,7 +471,7 @@ struct ParameterEnumerationValues {
Constructor using custom values.@n
The pointer to @values must have been allocated on the heap with `new`.
*/
ParameterEnumerationValues(uint32_t c, bool r, const ParameterEnumerationValue* v) noexcept
ParameterEnumerationValues(uint32_t c, bool r, ParameterEnumerationValue* v) noexcept
: count(c),
restrictedMode(r),
values(v) {}
@@ -365,6 +487,8 @@ struct ParameterEnumerationValues {
values = nullptr;
}
}

DISTRHO_DECLARE_NON_COPYABLE(ParameterEnumerationValues)
};

/**
@@ -384,6 +508,13 @@ struct Parameter {
*/
String name;

/**
The short name of this parameter.@n
Used when displaying the parameter name in a very limited space.
@note This value is optional, the full name is used when the short one is missing.
*/
String shortName;

/**
The symbol of this parameter.@n
A parameter symbol is a short restricted name used as a machine and human readable identifier.@n
@@ -399,6 +530,12 @@ struct Parameter {
*/
String unit;

/**
An extensive description/comment about the parameter.
@note This value is optional and only used for LV2.
*/
String description;

/**
Ranges of this parameter.@n
The ranges describe the default, minimum and maximum values.
@@ -424,18 +561,30 @@ struct Parameter {
*/
uint8_t midiCC;

/**
The group id that this parameter belongs to.
No group is assigned by default.

You can use a group from PredefinedPortGroups or roll your own.@n
When rolling your own port groups, you MUST start their group ids from 0 and they MUST be sequential.
@see PortGroup, Plugin::initPortGroup
*/
uint32_t groupId;

/**
Default constructor for a null parameter.
*/
Parameter() noexcept
: hints(0x0),
name(),
shortName(),
symbol(),
unit(),
ranges(),
enumValues(),
designation(kParameterDesignationNull),
midiCC(0) {}
midiCC(0),
groupId(kPortGroupNone) {}

/**
Constructor using custom values.
@@ -443,12 +592,14 @@ struct Parameter {
Parameter(uint32_t h, const char* n, const char* s, const char* u, float def, float min, float max) noexcept
: hints(h),
name(n),
shortName(),
symbol(s),
unit(u),
ranges(def, min, max),
enumValues(),
designation(kParameterDesignationNull),
midiCC(0) {}
midiCC(0),
groupId(kPortGroupNone) {}

/**
Initialize a parameter for a specific designation.
@@ -462,11 +613,13 @@ struct Parameter {
case kParameterDesignationNull:
break;
case kParameterDesignationBypass:
hints = kParameterIsAutomable|kParameterIsBoolean|kParameterIsInteger;
name = "Bypass";
symbol = "dpf_bypass";
unit = "";
midiCC = 0;
hints = kParameterIsAutomatable|kParameterIsBoolean|kParameterIsInteger;
name = "Bypass";
shortName = "Bypass";
symbol = "dpf_bypass";
unit = "";
midiCC = 0;
groupId = kPortGroupNone;
ranges.def = 0.0f;
ranges.min = 0.0f;
ranges.max = 1.0f;
@@ -475,6 +628,83 @@ struct Parameter {
}
};

/**
Port Group.@n
Allows to group together audio/cv ports or parameters.

Each unique group MUST have an unique symbol and a name.
A group can be applied to both inputs and outputs (at the same time).
The same group cannot be used in audio ports and parameters.

An audio port group logically combines ports which should be considered part of the same stream.@n
For example, two audio ports in a group may form a stereo stream.

A parameter group provides meta-data to the host to indicate that some parameters belong together.

The use of port groups is completely optional.

@see Plugin::initPortGroup, AudioPort::group, Parameter::group
*/
struct PortGroup {
/**
The name of this port group.@n
A port group name can contain any character, but hosts might have a hard time with non-ascii ones.@n
The name doesn't have to be unique within a plugin instance, but it's recommended.
*/
String name;

/**
The symbol of this port group.@n
A port group symbol is a short restricted name used as a machine and human readable identifier.@n
The first character must be one of _, a-z or A-Z and subsequent characters can be from _, a-z, A-Z and 0-9.
@note Port group symbols MUST be unique within a plugin instance.
*/
String symbol;
};

/**
State.

In DPF states refer to key:value string pairs, used to store arbitrary non-parameter data.@n
By default states are completely internal to the plugin and not visible by the host.@n
Flags can be set to allow hosts to see and/or change them.

TODO API under construction
*/
struct State {
/**
Hints describing this state.
@note Changing these hints can break compatibility with previously saved data.
@see StateHints
*/
uint32_t hints;

/**
The key or "symbol" of this state.@n
A state key is a short restricted name used as a machine and human readable identifier.
@note State keys MUST be unique within a plugin instance.
TODO define rules for allowed characters, must be usable as URI non-encoded parameters
*/
String key;

/**
The default value of this state.@n
Can be left empty if considered a valid initial state.
*/
String defaultValue;

/**
String representation of this state.
*/
String label;

/**
An extensive description/comment about this state.
@note This value is optional and only used for LV2.
*/
String description;
};

/**
MIDI event.
*/
@@ -497,6 +727,9 @@ struct MidiEvent {
/**
MIDI data.@n
If size > kDataSize, dataExt is used (otherwise null).

When dataExt is used, the event holder is responsible for
keeping the pointer valid during the entirety of the run function.
*/
uint8_t data[kDataSize];
const uint8_t* dataExt;
@@ -507,7 +740,7 @@ struct MidiEvent {
The @a playing and @a frame values are always valid.@n
BBT values are only valid when @a bbt.valid is true.

This struct is inspired by the JACK Transport API.
This struct is inspired by the [JACK Transport API](https://jackaudio.org/api/structjack__position__t.html).
*/
struct TimePosition {
/**
@@ -546,10 +779,11 @@ struct TimePosition {

/**
Current tick within beat.@n
Should always be > 0 and <= @a ticksPerBeat.@n
Should always be >= 0 and < @a ticksPerBeat.@n
The first tick is tick '0'.
@note Fraction part of tick is only available on some plugin formats.
*/
int32_t tick;
double tick;

/**
Number of ticks that have elapsed between frame 0 and the first beat of the current measure.
@@ -567,7 +801,7 @@ struct TimePosition {
float beatType;

/**
Number of ticks within a bar.@n
Number of ticks within a beat.@n
Usually a moderately large integer with many denominators, such as 1920.0.
*/
double ticksPerBeat;
@@ -590,6 +824,22 @@ struct TimePosition {
beatType(0.0f),
ticksPerBeat(0.0),
beatsPerMinute(0.0) {}

/**
Reinitialize this position using the default null initialization.
*/
void clear() noexcept
{
valid = false;
bar = 0;
beat = 0;
tick = 0;
barStartTick = 0.0;
beatsPerBar = 0.0f;
beatType = 0.0f;
ticksPerBeat = 0.0;
beatsPerMinute = 0.0;
}
} bbt;

/**
@@ -599,6 +849,16 @@ struct TimePosition {
: playing(false),
frame(0),
bbt() {}

/**
Reinitialize this position using the default null initialization.
*/
void clear() noexcept
{
playing = false;
frame = 0;
bbt.clear();
}
};

/** @} */
@@ -670,6 +930,22 @@ public:
*/
double getSampleRate() const noexcept;

/**
Get the bundle path where the plugin resides.
Can return null if the plugin is not available in a bundle (if it is a single binary).
@see getBinaryFilename
@see getResourcePath
*/
const char* getBundlePath() const noexcept;

/**
Check if this plugin instance is a "dummy" one used for plugin meta-data/information export.@n
When true no processing will be done, the plugin is created only to extract information.@n
In DPF, LADSPA/DSSI, VST2 and VST3 formats create one global instance per plugin binary
while LV2 creates one when generating turtle meta-data.
*/
bool isDummyInstance() const noexcept;

#if DISTRHO_PLUGIN_WANT_TIMEPOS
/**
Get the current host transport time position.@n
@@ -698,6 +974,37 @@ public:
bool writeMidiEvent(const MidiEvent& midiEvent) noexcept;
#endif

#if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
/**
Check if parameter value change requests will work with the current plugin host.
@note This function is only available if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST is enabled.
@see requestParameterValueChange(uint32_t, float)
*/
bool canRequestParameterValueChanges() const noexcept;

/**
Request a parameter value change from the host.
If successful, this function will automatically trigger a parameter update on the UI side as well.
This function can fail, for example if the host is busy with the parameter for read-only automation.
Some hosts simply do not have this functionality, which can be verified with canRequestParameterValueChanges().
@note This function is only available if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST is enabled.
*/
bool requestParameterValueChange(uint32_t index, float value) noexcept;
#endif

#if DISTRHO_PLUGIN_WANT_STATE
/**
Set state value and notify the host about the change.@n
This function will call `setState()` and also trigger an update on the UI side as necessary.@n
It must not be called during run.@n
The state must be host readable.
@note this function does nothing on DSSI plugin format, as DSSI only supports UI->DSP messages.

TODO API under construction
*/
bool updateStateValue(const char* key, const char* value) noexcept;
#endif

protected:
/* --------------------------------------------------------------------------------------------------------
* Information */
@@ -763,7 +1070,14 @@ protected:
Initialize the parameter @a index.@n
This function will be called once, shortly after the plugin is created.
*/
virtual void initParameter(uint32_t index, Parameter& parameter) = 0;
virtual void initParameter(uint32_t index, Parameter& parameter);

/**
Initialize the port group @a groupId.@n
This function will be called once,
shortly after the plugin is created and all audio ports and parameters have been enumerated.
*/
virtual void initPortGroup(uint32_t groupId, PortGroup& portGroup);

#if DISTRHO_PLUGIN_WANT_PROGRAMS
/**
@@ -776,11 +1090,17 @@ protected:

#if DISTRHO_PLUGIN_WANT_STATE
/**
Set the state key and default value of @a index.@n
Initialize the state @a index.@n
This function will be called once, shortly after the plugin is created.@n
Must be implemented by your plugin class only if DISTRHO_PLUGIN_WANT_STATE is enabled.
*/
virtual void initState(uint32_t index, String& stateKey, String& defaultStateValue) = 0;
virtual void initState(uint32_t index, State& state);

DISTRHO_DEPRECATED_BY("initState(uint32_t,State&)")
virtual void initState(uint32_t, String&, String&) {}

DISTRHO_DEPRECATED_BY("initState(uint32_t,State&)")
virtual bool isStateFile(uint32_t) { return false; }
#endif

/* --------------------------------------------------------------------------------------------------------
@@ -790,15 +1110,15 @@ protected:
Get the current value of a parameter.@n
The host may call this function from any context, including realtime processing.
*/
virtual float getParameterValue(uint32_t index) const = 0;
virtual float getParameterValue(uint32_t index) const;

/**
Change a parameter value.@n
The host may call this function from any context, including realtime processing.@n
When a parameter is marked as automable, you must ensure no non-realtime operations are performed.
When a parameter is marked as automatable, you must ensure no non-realtime operations are performed.
@note This function will only be called for parameter inputs.
*/
virtual void setParameterValue(uint32_t index, float value) = 0;
virtual void setParameterValue(uint32_t index, float value);

#if DISTRHO_PLUGIN_WANT_PROGRAMS
/**
@@ -806,7 +1126,7 @@ protected:
The host may call this function from any context, including realtime processing.@n
Must be implemented by your plugin class only if DISTRHO_PLUGIN_WANT_PROGRAMS is enabled.
*/
virtual void loadProgram(uint32_t index) = 0;
virtual void loadProgram(uint32_t index);
#endif

#if DISTRHO_PLUGIN_WANT_FULL_STATE
@@ -816,7 +1136,7 @@ protected:
Must be implemented by your plugin class if DISTRHO_PLUGIN_WANT_FULL_STATE is enabled.
@note The use of this function breaks compatibility with the DSSI format.
*/
virtual String getState(const char* key) const = 0;
virtual String getState(const char* key) const;
#endif

#if DISTRHO_PLUGIN_WANT_STATE
@@ -824,7 +1144,7 @@ protected:
Change an internal state @a key to @a value.@n
Must be implemented by your plugin class only if DISTRHO_PLUGIN_WANT_STATE is enabled.
*/
virtual void setState(const char* key, const char* value) = 0;
virtual void setState(const char* key, const char* value);
#endif

/* --------------------------------------------------------------------------------------------------------
@@ -845,14 +1165,14 @@ protected:
Run/process function for plugins with MIDI input.
@note Some parameters might be null if there are no audio inputs/outputs or MIDI events.
*/
virtual void run(const float* const* inputs, float** outputs, uint32_t frames,
virtual void run(const float** inputs, float** outputs, uint32_t frames,
const MidiEvent* midiEvents, uint32_t midiEventCount) = 0;
#else
/**
Run/process function for plugins without MIDI input.
@note Some parameters might be null if there are no audio inputs or outputs.
*/
virtual void run(const float* const* inputs, float** outputs, uint32_t frames) = 0;
virtual void run(const float** inputs, float** outputs, uint32_t frames) = 0;
#endif

/* --------------------------------------------------------------------------------------------------------
@@ -895,7 +1215,10 @@ private:
*/

/**
TODO.
Create an instance of the Plugin class.@n
This is the entry point for DPF plugins.@n
DPF will call this to either create an instance of your plugin for the host
or to fetch some initial information for internal caching.
*/
extern Plugin* createPlugin();



+ 20
- 4
source/modules/distrho/DistrhoPluginMain.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -19,14 +19,30 @@
#if defined(DISTRHO_PLUGIN_TARGET_CARLA)
# include "src/DistrhoPluginCarla.cpp"
#elif defined(DISTRHO_PLUGIN_TARGET_JACK)
# include "src/DistrhoPluginJack.cpp"
# include "src/DistrhoPluginJACK.cpp"
#elif (defined(DISTRHO_PLUGIN_TARGET_LADSPA) || defined(DISTRHO_PLUGIN_TARGET_DSSI))
# include "src/DistrhoPluginLADSPA+DSSI.cpp"
#elif defined(DISTRHO_PLUGIN_TARGET_LV2)
# include "src/DistrhoPluginLV2.cpp"
# include "src/DistrhoPluginLV2export.cpp"
#elif defined(DISTRHO_PLUGIN_TARGET_VST)
# include "src/DistrhoPluginVST.cpp"
#elif defined(DISTRHO_PLUGIN_TARGET_VST2)
# include "src/DistrhoPluginVST2.cpp"
#elif defined(DISTRHO_PLUGIN_TARGET_VST3)
# include "src/DistrhoPluginVST3.cpp"
#elif defined(DISTRHO_PLUGIN_TARGET_SHARED)
DISTRHO_PLUGIN_EXPORT DISTRHO_NAMESPACE::Plugin* createSharedPlugin();
DISTRHO_PLUGIN_EXPORT DISTRHO_NAMESPACE::Plugin* createSharedPlugin() { return DISTRHO_NAMESPACE::createPlugin(); }
#elif defined(DISTRHO_PLUGIN_TARGET_STATIC)
START_NAMESPACE_DISTRHO
Plugin* createStaticPlugin() { return createPlugin(); }
END_NAMESPACE_DISTRHO
#else
# error unsupported format
#endif

#if defined(DISTRHO_PLUGIN_TARGET_JACK)
# define DISTRHO_IS_STANDALONE 1
#else
# define DISTRHO_IS_STANDALONE 0
#endif
#include "src/DistrhoUtils.cpp"

+ 215
- 0
source/modules/distrho/DistrhoPluginUtils.hpp View File

@@ -0,0 +1,215 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef DISTRHO_PLUGIN_UTILS_HPP_INCLUDED
#define DISTRHO_PLUGIN_UTILS_HPP_INCLUDED

#include "DistrhoPlugin.hpp"

START_NAMESPACE_DISTRHO

/* ------------------------------------------------------------------------------------------------------------
* Plugin related utilities */

/**
@defgroup PluginRelatedUtilities Plugin related utilities

@{
*/

/**
Get the absolute filename of the plugin DSP/UI binary.@n
Under certain systems or plugin formats the binary will be inside the plugin bundle.@n
Also, in some formats or setups, the DSP and UI binaries are in different files.
*/
const char* getBinaryFilename();

/**
Get a string representation of the current plugin format we are building against.@n
This can be "JACK/Standalone", "LADSPA", "DSSI", "LV2", "VST2" or "VST3".@n
This string is purely informational and must not be used to tweak plugin behaviour.

@note DO NOT CHANGE PLUGIN BEHAVIOUR BASED ON PLUGIN FORMAT.
*/
const char* getPluginFormatName() noexcept;

/**
Get the path to where resources are stored within the plugin bundle.@n
Requires a valid plugin bundle path.

Returns a path inside the bundle where the plugin is meant to store its resources in.@n
This path varies between systems and plugin formats, like so:

- LV2: <bundle>/resources (can be stored anywhere inside the bundle really, DPF just uses this one)
- VST2 macOS: <bundle>/Contents/Resources
- VST2 non-macOS: <bundle>/resources (see note)

The other non-mentioned formats do not support bundles.@n

@note For VST2 on non-macOS systems, this assumes you have your plugin inside a dedicated directory
rather than only shipping with the binary (e.g. <myplugin.vst>/myplugin.dll)
*/
const char* getResourcePath(const char* bundlePath) noexcept;

/** @} */

/* ------------------------------------------------------------------------------------------------------------
* Plugin helper classes */

/**
@defgroup PluginHelperClasses Plugin helper classes

@{
*/

#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
/**
Handy class to help keep audio buffer in sync with incoming MIDI events.
To use it, create a local variable (on the stack) and call nextEvent() until it returns false.
@code
for (AudioMidiSyncHelper amsh(outputs, frames, midiEvents, midiEventCount); amsh.nextEvent();)
{
float* const outL = amsh.outputs[0];
float* const outR = amsh.outputs[1];

for (uint32_t i=0; i<amsh.midiEventCount; ++i)
{
const MidiEvent& ev(amsh.midiEvents[i]);
// ... do something with the midi event
}

renderSynth(outL, outR, amsh.frames);
}
@endcode

Some important notes when using this class:
1. MidiEvent::frame retains its original value, but it is useless, do not use it.
2. The class variable names are the same as the default ones in the run function.
Keep that in mind and try to avoid typos. :)
*/
struct AudioMidiSyncHelper
{
/** Parameters from the run function, adjusted for event sync */
float* outputs[DISTRHO_PLUGIN_NUM_OUTPUTS];
uint32_t frames;
const MidiEvent* midiEvents;
uint32_t midiEventCount;

/**
Constructor, using values from the run function.
*/
AudioMidiSyncHelper(float** const o, uint32_t f, const MidiEvent* m, uint32_t mc)
: outputs(),
frames(0),
midiEvents(m),
midiEventCount(0),
remainingFrames(f),
remainingMidiEventCount(mc),
totalFramesUsed(0)
{
for (uint i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
outputs[i] = o[i];
}

/**
Process a batch of events untill no more are available.
You must not read any more values from this class after this function returns false.
*/
bool nextEvent()
{
// nothing else to do
if (remainingFrames == 0)
return false;

// initial setup, need to find first MIDI event
if (totalFramesUsed == 0)
{
// no MIDI events at all in this process cycle
if (remainingMidiEventCount == 0)
{
frames = remainingFrames;
remainingFrames = 0;
totalFramesUsed += frames;
return true;
}

// render audio until first midi event, if needed
if (const uint32_t firstEventFrame = midiEvents[0].frame)
{
DISTRHO_SAFE_ASSERT_UINT2_RETURN(firstEventFrame < remainingFrames,
firstEventFrame, remainingFrames, false);
frames = firstEventFrame;
remainingFrames -= firstEventFrame;
totalFramesUsed += firstEventFrame;
return true;
}
}
else
{
for (uint32_t i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
outputs[i] += frames;
}

// no more MIDI events available
if (remainingMidiEventCount == 0)
{
frames = remainingFrames;
midiEvents = nullptr;
midiEventCount = 0;
remainingFrames = 0;
totalFramesUsed += frames;
return true;
}

// if there were midi events before, increment pointer
if (midiEventCount != 0)
midiEvents += midiEventCount;

const uint32_t firstEventFrame = midiEvents[0].frame;
DISTRHO_SAFE_ASSERT_UINT2_RETURN(firstEventFrame >= totalFramesUsed,
firstEventFrame, totalFramesUsed, false);

midiEventCount = 1;
while (midiEventCount < remainingMidiEventCount)
{
if (midiEvents[midiEventCount].frame == firstEventFrame)
++midiEventCount;
else
break;
}

frames = firstEventFrame - totalFramesUsed;
remainingFrames -= frames;
remainingMidiEventCount -= midiEventCount;
totalFramesUsed += frames;
return true;
}

private:
/** @internal */
uint32_t remainingFrames;
uint32_t remainingMidiEventCount;
uint32_t totalFramesUsed;
};
#endif

/** @} */

// -----------------------------------------------------------------------------------------------------------

END_NAMESPACE_DISTRHO

#endif // DISTRHO_PLUGIN_UTILS_HPP_INCLUDED

+ 175
- 32
source/modules/distrho/DistrhoUI.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -20,17 +20,42 @@
#include "extra/LeakDetector.hpp"
#include "src/DistrhoPluginChecks.h"

#ifndef HAVE_DGL
#ifdef DGL_CAIRO
# include "Cairo.hpp"
#endif
#ifdef DGL_OPENGL
# include "OpenGL.hpp"
#endif
#ifdef DGL_VULKAN
# include "Vulkan.hpp"
#endif

#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
# include "../dgl/Base.hpp"
# include "extra/ExternalWindow.hpp"
typedef DISTRHO_NAMESPACE::ExternalWindow UIWidget;
#elif DISTRHO_UI_USE_CUSTOM
# include DISTRHO_UI_CUSTOM_INCLUDE_PATH
typedef DISTRHO_UI_CUSTOM_WIDGET_TYPE UIWidget;
#elif DISTRHO_UI_USE_CAIRO
# include "../dgl/Cairo.hpp"
typedef DGL_NAMESPACE::CairoTopLevelWidget UIWidget;
#elif DISTRHO_UI_USE_NANOVG
# include "../dgl/NanoVG.hpp"
typedef DGL_NAMESPACE::NanoWidget UIWidget;
typedef DGL_NAMESPACE::NanoTopLevelWidget UIWidget;
#else
# include "../dgl/Widget.hpp"
typedef DGL_NAMESPACE::Widget UIWidget;
# include "../dgl/TopLevelWidget.hpp"
typedef DGL_NAMESPACE::TopLevelWidget UIWidget;
#endif

#ifndef DGL_FILE_BROWSER_DISABLED
# include "extra/FileBrowserDialog.hpp"
#endif

START_NAMESPACE_DGL
class PluginWindow;
END_NAMESPACE_DGL

START_NAMESPACE_DISTRHO

/* ------------------------------------------------------------------------------------------------------------
@@ -53,8 +78,13 @@ public:
/**
UI class constructor.
The UI should be initialized to a default state that matches the plugin side.

When @a automaticallyScale is set to true, DPF will automatically scale up the UI
to fit the host/desktop scale factor.@n
It assumes aspect ratio is meant to be kept.
Manually call setGeometryConstraints instead if keeping UI aspect ratio is not required.
*/
UI(uint width = 0, uint height = 0);
UI(uint width = 0, uint height = 0, bool automaticallyScaleAndSetAsMinimumSize = false);

/**
Destructor.
@@ -64,21 +94,67 @@ public:
/* --------------------------------------------------------------------------------------------------------
* Host state */

/**
Check if this UI window is resizable (by the user or window manager).
There are situations where an UI supports resizing but the plugin host does not, so this could return false.

You might want to add a resize handle for such cases, so the user is still allowed to resize the window.
(programatically resizing a window is always possible, but the same is not true for the window manager)
*/
bool isResizable() const noexcept;

/**
Get the color used for UI background (i.e. window color) in RGBA format.
Returns 0 by default, in case of error or lack of host support.

The following example code can be use to extract individual colors:
```
const int red = (bgColor >> 24) & 0xff;
const int green = (bgColor >> 16) & 0xff;
const int blue = (bgColor >> 8) & 0xff;
```
*/
uint getBackgroundColor() const noexcept;

/**
Get the color used for UI foreground (i.e. text color) in RGBA format.
Returns 0xffffffff by default, in case of error or lack of host support.

The following example code can be use to extract individual colors:
```
const int red = (fgColor >> 24) & 0xff;
const int green = (fgColor >> 16) & 0xff;
const int blue = (fgColor >> 8) & 0xff;
```
*/
uint getForegroundColor() const noexcept;

/**
Get the current sample rate used in plugin processing.
@see sampleRateChanged(double)
*/
double getSampleRate() const noexcept;

/**
Get the bundle path where the UI resides.@n
Can return null if the UI is not available in a bundle (if it is a single binary).
@see getBinaryFilename
*/
const char* getBundlePath() const noexcept;

/**
editParameter.
@TODO Document this.

Touch/pressed-down event.
Lets the host know the user is tweaking a parameter.
Required in some hosts to record automation.
*/
void editParameter(uint32_t index, bool started);

/**
setParameterValue.
@TODO Document this.

Change a parameter value in the Plugin.
*/
void setParameterValue(uint32_t index, float value);

@@ -88,17 +164,43 @@ public:
@TODO Document this.
*/
void setState(const char* key, const char* value);

/**
Request a new file from the host, matching the properties of a state key.@n
This will use the native host file browser if available, otherwise a DPF built-in file browser is used.@n
Response will be sent asynchronously to stateChanged, with the matching key and the new file as the value.@n
It is not possible to know if the action was cancelled by the user.

@return Success if a file-browser was opened, otherwise false.
@note You cannot request more than one file at a time.
*/
bool requestStateFile(const char* key);
#endif

#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
/**
sendNote.
@TODO Document this.
@note Work in progress. Implemented for DSSI and LV2 formats.
Send a single MIDI note from the UI to the plugin DSP side.@n
A note with zero velocity will be sent as note-off (MIDI 0x80), otherwise note-on (MIDI 0x90).
*/
void sendNote(uint8_t channel, uint8_t note, uint8_t velocity);
#endif

#ifndef DGL_FILE_BROWSER_DISABLED
/**
Open a file browser dialog with this window as transient parent.@n
A few options can be specified to setup the dialog.

If a path is selected, onFileSelected() will be called with the user chosen path.
If the user cancels or does not pick a file, onFileSelected() will be called with nullptr as filename.

This function does not block the event loop.

@note This is exactly the same API as provided by the Window class,
but redeclared here so that non-embed/DGL based UIs can still use file browser related functions.
*/
bool openFileBrowser(const FileBrowserOptions& options = FileBrowserOptions());
#endif

#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
/* --------------------------------------------------------------------------------------------------------
* Direct DSP access - DO NOT USE THIS UNLESS STRICTLY NECESSARY!! */
@@ -121,6 +223,13 @@ public:
*/
static const char* getNextBundlePath() noexcept;

/**
Get the scale factor that will be used for the next UI.
@note: This function is only valid during createUI(),
it will return 1.0 when called from anywhere else.
*/
static double getNextScaleFactor() noexcept;

# if DISTRHO_PLUGIN_HAS_EMBED_UI
/**
Get the Window Id that will be used for the next created window.
@@ -166,36 +275,74 @@ protected:
*/
virtual void sampleRateChanged(double newSampleRate);

#ifdef HAVE_DGL
/* --------------------------------------------------------------------------------------------------------
* UI Callbacks (optional) */

/**
uiIdle.
@TODO Document this.
UI idle function, called to give idle time to the plugin UI directly from the host.
This is called right after OS event handling and Window idle events (within the same cycle).
There are no guarantees in terms of timing.
@see addIdleCallback(IdleCallback*, uint).
*/
virtual void uiIdle() {}

#ifndef DGL_FILE_BROWSER_DISABLED
/**
File browser selected function.
@see Window::fileBrowserSelected(const char*)
Window scale factor function, called when the scale factor changes.
This function is for plugin UIs to be able to override Window::onScaleFactorChanged(double).

The default implementation does nothing.
WARNING function needs a proper name
*/
virtual void uiFileBrowserSelected(const char* filename);
#endif
virtual void uiScaleFactorChanged(double scaleFactor);

#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
/**
Windows focus function, called when the window gains or loses the keyboard focus.
This function is for plugin UIs to be able to override Window::onFocus(bool, CrossingMode).

The default implementation does nothing.
*/
virtual void uiFocus(bool focus, DGL_NAMESPACE::CrossingMode mode);

/**
OpenGL window reshape function, called when parent window is resized.
You can reimplement this function for a custom OpenGL state.
@see Window::onReshape(uint,uint)
Window reshape function, called when the window is resized.
This function is for plugin UIs to be able to override Window::onReshape(uint, uint).

The plugin UI size will be set right after this function.
The default implementation sets up the drawing context where necessary.

You should almost never need to override this function.
The most common exception is custom OpenGL setup, but only really needed for custom OpenGL drawing code.
*/
virtual void uiReshape(uint width, uint height);
#endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI

#ifndef DGL_FILE_BROWSER_DISABLED
/**
Window file selected function, called when a path is selected by the user, as triggered by openFileBrowser().
This function is for plugin UIs to be able to override Window::onFileSelected(const char*).

This action happens after the user confirms the action, so the file browser dialog will be closed at this point.
The default implementation does nothing.

If you need to use files as plugin state, please setup and use states with kStateIsFilenamePath instead.
*/
virtual void uiFileBrowserSelected(const char* filename);
#endif

/* --------------------------------------------------------------------------------------------------------
* UI Resize Handling, internal */

#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
/**
External Window resize function, called when the window is resized.
This is overriden here so the host knows when the UI is resized by you.
@see ExternalWindow::sizeChanged(uint,uint)
*/
void sizeChanged(uint width, uint height) override;
#else
/**
OpenGL widget resize function, called when the widget is resized.
Widget resize function, called when the widget is resized.
This is overriden here so the host knows when the UI is resized by you.
@see Widget::onResize(const ResizeEvent&)
*/
@@ -206,16 +353,12 @@ protected:

private:
struct PrivateData;
PrivateData* const pData;
PrivateData* const uiData;
friend class DGL_NAMESPACE::PluginWindow;
friend class UIExporter;
friend class UIExporterWindow;

#ifdef HAVE_DGL
// these should not be used
void setAbsoluteX(int) const noexcept {}
void setAbsoluteY(int) const noexcept {}
void setAbsolutePos(int, int) const noexcept {}
void setAbsolutePos(const DGL_NAMESPACE::Point<int>&) const noexcept {}
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
/** @internal */
void requestSizeChange(uint width, uint height) override;
#endif

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(UI)


+ 15
- 2
source/modules/distrho/DistrhoUIMain.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -24,8 +24,21 @@
# include "src/DistrhoUIDSSI.cpp"
#elif defined(DISTRHO_PLUGIN_TARGET_LV2)
# include "src/DistrhoUILV2.cpp"
#elif defined(DISTRHO_PLUGIN_TARGET_VST)
#elif defined(DISTRHO_PLUGIN_TARGET_VST2)
// nothing
#elif defined(DISTRHO_PLUGIN_TARGET_VST3)
# include "src/DistrhoUIVST3.cpp"
#elif defined(DISTRHO_PLUGIN_TARGET_SHARED) || defined(DISTRHO_PLUGIN_TARGET_STATIC)
// nothing
#else
# error unsupported format
#endif

#if !DISTRHO_PLUGIN_WANT_DIRECT_ACCESS && !DISTRHO_PLUGIN_TARGET_JACK && !DISTRHO_PLUGIN_TARGET_VST2 && !DISTRHO_PLUGIN_TARGET_VST3
# ifdef DISTRHO_PLUGIN_TARGET_DSSI
# define DISTRHO_IS_STANDALONE 1
# else
# define DISTRHO_IS_STANDALONE 0
# endif
# include "src/DistrhoUtils.cpp"
#endif

+ 137
- 58
source/modules/distrho/DistrhoUtils.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -25,10 +25,6 @@
#include <cstring>

#include <cmath>

#undef max
#undef min

#include <limits>

#ifdef DISTRHO_PROPER_CPP11_SUPPORT
@@ -37,7 +33,12 @@
# include <stdint.h>
#endif

#if defined(DISTRHO_OS_MAC) && ! defined(CARLA_OS_MAC) && ! defined(DISTRHO_PROPER_CPP11_SUPPORT)
#if defined(DISTRHO_OS_WINDOWS) && defined(_MSC_VER)
#include <basetsd.h>
typedef SSIZE_T ssize_t;
#endif

#if ! defined(CARLA_MATH_UTILS_HPP_INCLUDED) && ! defined(DISTRHO_PROPER_CPP11_SUPPORT)
namespace std {
inline float fmin(float __x, float __y)
{ return __builtin_fminf(__x, __y); }
@@ -54,39 +55,57 @@ inline float round(float __x)
# define M_PI 3.14159265358979323846
#endif

// -----------------------------------------------------------------------
// misc functions
#define DISTRHO_MACRO_AS_STRING_VALUE(MACRO) #MACRO
#define DISTRHO_MACRO_AS_STRING(MACRO) DISTRHO_MACRO_AS_STRING_VALUE(MACRO)

/*
* Return a 64-bit number from 4 8-bit numbers.
/* ------------------------------------------------------------------------------------------------------------
* misc functions */

/**
@defgroup MiscellaneousFunctions Miscellaneous functions

@{
*/
static inline

/**
Return a 32-bit number from 4 8-bit numbers.@n
The return type is a int64_t for better compatibility with plugin formats that use such numbers.
*/
static inline constexpr
int64_t d_cconst(const uint8_t a, const uint8_t b, const uint8_t c, const uint8_t d) noexcept
{
return (a << 24) | (b << 16) | (c << 8) | (d << 0);
}

/*
* Return an hexadecimal representation of a MAJ.MIN.MICRO version number.
/**
Return an hexadecimal representation of a MAJ.MIN.MICRO version number.
*/
static inline
static inline constexpr
uint32_t d_version(const uint8_t major, const uint8_t minor, const uint8_t micro) noexcept
{
return uint32_t(major << 16) | uint32_t(minor << 8) | uint32_t(micro << 0);
return uint32_t(major << 16) | uint32_t(minor << 8) | (micro << 0);
}

/*
* Dummy function.
/**
Dummy, no-op function.
*/
static inline
void d_pass() noexcept {}

// -----------------------------------------------------------------------
// string print functions
/** @} */

/*
* Print a string to stdout with newline (gray color).
* Does nothing if DEBUG is not defined.
/* ------------------------------------------------------------------------------------------------------------
* string print functions */

/**
@defgroup StringPrintFunctions String print functions

@{
*/

/**
Print a string to stdout with newline (gray color).
Does nothing if DEBUG is not defined.
*/
#ifndef DEBUG
# define d_debug(...)
@@ -95,64 +114,64 @@ static inline
void d_debug(const char* const fmt, ...) noexcept
{
try {
::va_list args;
::va_start(args, fmt);
va_list args;
va_start(args, fmt);
std::fprintf(stdout, "\x1b[30;1m");
std::vfprintf(stdout, fmt, args);
std::fprintf(stdout, "\x1b[0m\n");
::va_end(args);
va_end(args);
} catch (...) {}
}
#endif

/*
* Print a string to stdout with newline.
/**
Print a string to stdout with newline.
*/
static inline
void d_stdout(const char* const fmt, ...) noexcept
{
try {
::va_list args;
::va_start(args, fmt);
va_list args;
va_start(args, fmt);
std::vfprintf(stdout, fmt, args);
std::fprintf(stdout, "\n");
::va_end(args);
va_end(args);
} catch (...) {}
}

/*
* Print a string to stderr with newline.
/**
Print a string to stderr with newline.
*/
static inline
void d_stderr(const char* const fmt, ...) noexcept
{
try {
::va_list args;
::va_start(args, fmt);
va_list args;
va_start(args, fmt);
std::vfprintf(stderr, fmt, args);
std::fprintf(stderr, "\n");
::va_end(args);
va_end(args);
} catch (...) {}
}

/*
* Print a string to stderr with newline (red color).
/**
Print a string to stderr with newline (red color).
*/
static inline
void d_stderr2(const char* const fmt, ...) noexcept
{
try {
::va_list args;
::va_start(args, fmt);
va_list args;
va_start(args, fmt);
std::fprintf(stderr, "\x1b[31m");
std::vfprintf(stderr, fmt, args);
std::fprintf(stderr, "\x1b[0m\n");
::va_end(args);
va_end(args);
} catch (...) {}
}

/*
* Print a safe assertion error message.
/**
Print a safe assertion error message.
*/
static inline
void d_safe_assert(const char* const assertion, const char* const file, const int line) noexcept
@@ -160,8 +179,58 @@ void d_safe_assert(const char* const assertion, const char* const file, const in
d_stderr2("assertion failure: \"%s\" in file %s, line %i", assertion, file, line);
}

/*
* Print a safe exception error message.
/**
Print a safe assertion error message, with 1 extra signed integer value.
*/
static inline
void d_safe_assert_int(const char* const assertion, const char* const file,
const int line, const int value) noexcept
{
d_stderr2("assertion failure: \"%s\" in file %s, line %i, value %i", assertion, file, line, value);
}

/**
Print a safe assertion error message, with 1 extra unsigned integer value.
*/
static inline
void d_safe_assert_uint(const char* const assertion, const char* const file,
const int line, const uint value) noexcept
{
d_stderr2("assertion failure: \"%s\" in file %s, line %i, value %u", assertion, file, line, value);
}

/**
Print a safe assertion error message, with 2 extra signed integer values.
*/
static inline
void d_safe_assert_int2(const char* const assertion, const char* const file,
const int line, const int v1, const int v2) noexcept
{
d_stderr2("assertion failure: \"%s\" in file %s, line %i, v1 %i, v2 %i", assertion, file, line, v1, v2);
}

/**
Print a safe assertion error message, with 2 extra unsigned integer values.
*/
static inline
void d_safe_assert_uint2(const char* const assertion, const char* const file,
const int line, const uint v1, const uint v2) noexcept
{
d_stderr2("assertion failure: \"%s\" in file %s, line %i, v1 %u, v2 %u", assertion, file, line, v1, v2);
}

/**
Print a safe assertion error message, with a custom error message.
*/
static inline
void d_custom_safe_assert(const char* const message, const char* const assertion, const char* const file,
const int line) noexcept
{
d_stderr2("assertion failure: %s, condition \"%s\" in file %s, line %i", message, assertion, file, line);
}

/**
Print a safe exception error message.
*/
static inline
void d_safe_exception(const char* const exception, const char* const file, const int line) noexcept
@@ -169,12 +238,20 @@ void d_safe_exception(const char* const exception, const char* const file, const
d_stderr2("exception caught: \"%s\" in file %s, line %i", exception, file, line);
}

// -----------------------------------------------------------------------
// math functions
/** @} */

/*
* Safely compare two floating point numbers.
* Returns true if they match.
/* ------------------------------------------------------------------------------------------------------------
* math functions */

/**
@defgroup MathFunctions Math related functions

@{
*/

/**
Safely compare two floating point numbers.
Returns true if they match.
*/
template<typename T>
static inline
@@ -183,9 +260,9 @@ bool d_isEqual(const T& v1, const T& v2)
return std::abs(v1-v2) < std::numeric_limits<T>::epsilon();
}

/*
* Safely compare two floating point numbers.
* Returns true if they don't match.
/**
Safely compare two floating point numbers.
Returns true if they don't match.
*/
template<typename T>
static inline
@@ -194,8 +271,8 @@ bool d_isNotEqual(const T& v1, const T& v2)
return std::abs(v1-v2) >= std::numeric_limits<T>::epsilon();
}

/*
* Safely check if a floating point number is zero.
/**
Safely check if a floating point number is zero.
*/
template<typename T>
static inline
@@ -204,8 +281,8 @@ bool d_isZero(const T& value)
return std::abs(value) < std::numeric_limits<T>::epsilon();
}

/*
* Safely check if a floating point number is not zero.
/**
Safely check if a floating point number is not zero.
*/
template<typename T>
static inline
@@ -214,8 +291,8 @@ bool d_isNotZero(const T& value)
return std::abs(value) >= std::numeric_limits<T>::epsilon();
}

/*
* Get next power of 2.
/**
Get next power of 2.
*/
static inline
uint32_t d_nextPowerOf2(uint32_t size) noexcept
@@ -232,6 +309,8 @@ uint32_t d_nextPowerOf2(uint32_t size) noexcept
return ++size;
}

/** @} */

// -----------------------------------------------------------------------

#ifndef DONT_SET_USING_DISTRHO_NAMESPACE


+ 484
- 78
source/modules/distrho/extra/ExternalWindow.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -19,9 +19,7 @@

#include "String.hpp"

#ifdef DISTRHO_OS_WINDOWS
# error Unsupported platform!
#else
#ifndef DISTRHO_OS_WINDOWS
# include <cerrno>
# include <signal.h>
# include <sys/wait.h>
@@ -33,136 +31,544 @@ START_NAMESPACE_DISTRHO
// -----------------------------------------------------------------------
// ExternalWindow class

/**
External Window class.

This is a standalone TopLevelWidget/Window-compatible class, but without any real event handling.
Being compatible with TopLevelWidget/Window, it allows to be used as DPF UI target.

It can be used to embed non-DPF things or to run a tool in a new process as the "UI".
The uiIdle() function will be called at regular intervals to keep UI running.
There are helper methods in place to launch external tools and keep track of its running state.

External windows can be setup to run in 3 different modes:
* Embed:
Embed into the host UI, even-loop driven by the host.
This is basically working as a regular plugin UI, as you typically expect them to.
The plugin side does not get control over showing, hiding or closing the window (as usual for plugins).
No restrictions on supported plugin format, everything should work.
Requires DISTRHO_PLUGIN_HAS_EMBED_UI to be set to 1.

* Semi-external:
The UI is not embed into the host, but the even-loop is still driven by it.
In this mode the host does not have control over the UI except for showing, hiding and setting transient parent.
It is possible to close the window from the plugin, the host will be notified of such case.
Host regularly calls isQuitting() to check if the UI got closed by the user or plugin side.
This mode is only possible in LV2 plugin formats, using lv2ui:showInterface extension.

* Standalone:
The UI is not embed into the host or uses its event-loop, basically running as standalone.
The host only has control over showing and hiding the window, nothing else.
The UI is still free to close itself at any point.
DPF will keep calling isRunning() to check if it should keep the event-loop running.
Only possible in JACK and DSSI targets, as the UIs are literally standalone applications there.

Please note that for non-embed windows, you cannot show the window yourself.
The plugin window is only allowed to hide or close itself, a "show" action needs to come from the host.

A few callbacks are provided so that implementations do not need to care about checking for state changes.
They are not called on construction, but will be everytime something changes either by the host or the window itself.
*/
class ExternalWindow
{
public:
ExternalWindow(const uint w = 1, const uint h = 1, const char* const t = "")
: width(w),
height(h),
title(t),
pid(0) {}
struct PrivateData;

public:
/**
Constructor.
*/
explicit ExternalWindow()
: pData() {}

/**
Constructor for DPF internal use.
*/
explicit ExternalWindow(const PrivateData& data)
: pData(data) {}

/**
Destructor.
*/
virtual ~ExternalWindow()
{
terminateAndWaitForProcess();
DISTRHO_SAFE_ASSERT(!pData.visible);
}

/* --------------------------------------------------------------------------------------------------------
* ExternalWindow specific calls - Host side calls that you can reimplement for fine-grained funtionality */

/**
Check if main-loop is running.
This is used under standalone mode to check whether to keep things running.
Returning false from this function will stop the event-loop and close the window.
*/
virtual bool isRunning() const
{
#ifndef DISTRHO_OS_WINDOWS
if (ext.inUse)
return ext.isRunning();
#endif
return isVisible();
}

/**
Check if we are about to close.
This is used when the event-loop is provided by the host to check if it should close the window.
It is also used in standalone mode right after isRunning() returns false to verify if window needs to be closed.
*/
virtual bool isQuitting() const
{
#ifndef DISTRHO_OS_WINDOWS
return ext.inUse ? ext.isQuitting : pData.isQuitting;
#else
return pData.isQuitting;
#endif
}

/**
Get the "native" window handle.
This can be reimplemented in order to pass the native window to hosts that can use such informaton.

Returned value type depends on the platform:
- HaikuOS: This is a pointer to a `BView`.
- MacOS: This is a pointer to an `NSView*`.
- Windows: This is a `HWND`.
- Everything else: This is an [X11] `Window`.

@note Only available to override if DISTRHO_PLUGIN_HAS_EMBED_UI is set to 1.
*/
virtual uintptr_t getNativeWindowHandle() const noexcept
{
return 0;
}

/**
Grab the keyboard input focus.
Typically you would setup OS-native methods to bring the window to front and give it focus.
Default implementation does nothing.
*/
virtual void focus() {}

/* --------------------------------------------------------------------------------------------------------
* TopLevelWidget-like calls - Information, can be called by either host or plugin */

#if DISTRHO_PLUGIN_HAS_EMBED_UI
/**
Whether this Window is embed into another (usually not DGL-controlled) Window.
*/
bool isEmbed() const noexcept
{
return pData.parentWindowHandle != 0;
}
#endif

/**
Check if this window is visible.
@see setVisible(bool)
*/
bool isVisible() const noexcept
{
return pData.visible;
}

/**
Whether this Window is running as standalone, that is, without being coupled to a host event-loop.
When in standalone mode, isRunning() is called to check if the event-loop should keep running.
*/
bool isStandalone() const noexcept
{
return pData.isStandalone;
}

/**
Get width of this window.
Only relevant to hosts when the UI is embedded.
*/
uint getWidth() const noexcept
{
return width;
return pData.width;
}

/**
Get height of this window.
Only relevant to hosts when the UI is embedded.
*/
uint getHeight() const noexcept
{
return height;
return pData.height;
}

/**
Get the scale factor requested for this window.
This is purely informational, and up to developers to choose what to do with it.
*/
double getScaleFactor() const noexcept
{
return pData.scaleFactor;
}

/**
Get the title of the window previously set with setTitle().
This is typically displayed in the title bar or in window switchers.
*/
const char* getTitle() const noexcept
{
return title;
return pData.title;
}

void setTitle(const char* const t) noexcept
#if DISTRHO_PLUGIN_HAS_EMBED_UI
/**
Get the "native" window handle that this window should embed itself into.
Returned value type depends on the platform:
- HaikuOS: This is a pointer to a `BView`.
- MacOS: This is a pointer to an `NSView*`.
- Windows: This is a `HWND`.
- Everything else: This is an [X11] `Window`.
*/
uintptr_t getParentWindowHandle() const noexcept
{
title = t;
return pData.parentWindowHandle;
}
#endif

bool isRunning() noexcept
/**
Get the transient window that we should attach ourselves to.
TODO what id? also NSView* on macOS, or NSWindow?
*/
uintptr_t getTransientWindowId() const noexcept
{
if (pid <= 0)
return false;
return pData.transientWinId;
}

const pid_t p = ::waitpid(pid, nullptr, WNOHANG);
/* --------------------------------------------------------------------------------------------------------
* TopLevelWidget-like calls - actions called by either host or plugin */

if (p == pid || (p == -1 && errno == ECHILD))
{
printf("NOTICE: Child process exited while idle\n");
pid = 0;
return false;
}
/**
Hide window.
This is the same as calling setVisible(false).
Embed windows should never call this!
@see isVisible(), setVisible(bool)
*/
void hide()
{
setVisible(false);
}

/**
Hide the UI and gracefully terminate.
Embed windows should never call this!
*/
virtual void close()
{
pData.isQuitting = true;
hide();
#ifndef DISTRHO_OS_WINDOWS
if (ext.inUse)
terminateAndWaitForExternalProcess();
#endif
}

/**
Set width of this window.
Can trigger a sizeChanged callback.
Only relevant to hosts when the UI is embedded.
*/
void setWidth(uint width)
{
setSize(width, getHeight());
}

return true;
/**
Set height of this window.
Can trigger a sizeChanged callback.
Only relevant to hosts when the UI is embedded.
*/
void setHeight(uint height)
{
setSize(getWidth(), height);
}

/**
Set size of this window using @a width and @a height values.
Can trigger a sizeChanged callback.
Only relevant to hosts when the UI is embedded.
*/
void setSize(uint width, uint height)
{
DISTRHO_SAFE_ASSERT_UINT_RETURN(width > 1, width,);
DISTRHO_SAFE_ASSERT_UINT_RETURN(height > 1, height,);

if (pData.width == width && pData.height == height)
return;

pData.width = width;
pData.height = height;
sizeChanged(width, height);
}

/**
Set the title of the window, typically displayed in the title bar or in window switchers.
Can trigger a titleChanged callback.
Only relevant to hosts when the UI is not embedded.
*/
void setTitle(const char* title)
{
if (pData.title == title)
return;

pData.title = title;
titleChanged(title);
}

/**
Set geometry constraints for the Window when resized by the user.
*/
void setGeometryConstraints(uint minimumWidth, uint minimumHeight, bool keepAspectRatio = false)
{
DISTRHO_SAFE_ASSERT_UINT_RETURN(minimumWidth > 0, minimumWidth,);
DISTRHO_SAFE_ASSERT_UINT_RETURN(minimumHeight > 0, minimumHeight,);

pData.minWidth = minimumWidth;
pData.minHeight = minimumHeight;
pData.keepAspectRatio = keepAspectRatio;
}

/* --------------------------------------------------------------------------------------------------------
* TopLevelWidget-like calls - actions called by the host */

/**
Show window.
This is the same as calling setVisible(true).
@see isVisible(), setVisible(bool)
*/
void show()
{
setVisible(true);
}

/**
Set window visible (or not) according to @a visible.
@see isVisible(), hide(), show()
*/
void setVisible(bool visible)
{
if (pData.visible == visible)
return;

pData.visible = visible;
visibilityChanged(visible);
}

/**
Called by the host to set the transient parent window that we should attach ourselves to.
TODO what id? also NSView* on macOS, or NSWindow?
*/
void setTransientWindowId(uintptr_t winId)
{
if (pData.transientWinId == winId)
return;

pData.transientWinId = winId;
transientParentWindowChanged(winId);
}

protected:
/* --------------------------------------------------------------------------------------------------------
* ExternalWindow special calls for running externals tools */

bool startExternalProcess(const char* args[])
{
terminateAndWaitForProcess();
#ifndef DISTRHO_OS_WINDOWS
ext.inUse = true;

pid = vfork();
return ext.start(args);
#else
(void)args;
return false; // TODO
#endif
}

switch (pid)
{
case 0:
execvp(args[0], (char**)args);
_exit(1);
return false;
void terminateAndWaitForExternalProcess()
{
#ifndef DISTRHO_OS_WINDOWS
ext.isQuitting = true;
ext.terminateAndWait();
#else
// TODO
#endif
}

case -1:
printf("Could not start external ui\n");
return false;
/* --------------------------------------------------------------------------------------------------------
* ExternalWindow specific callbacks */

default:
return true;
}
/**
A callback for when the window size changes.
@note WIP this might need to get fed back into the host somehow.
*/
virtual void sizeChanged(uint /* width */, uint /* height */)
{
// unused, meant for custom implementations
}

private:
uint width;
uint height;
String title;
pid_t pid;
/**
A callback for when the window title changes.
@note WIP this might need to get fed back into the host somehow.
*/
virtual void titleChanged(const char* /* title */)
{
// unused, meant for custom implementations
}

friend class UIExporter;
/**
A callback for when the window visibility changes.
@note WIP this might need to get fed back into the host somehow.
*/
virtual void visibilityChanged(bool /* visible */)
{
// unused, meant for custom implementations
}

void terminateAndWaitForProcess()
/**
A callback for when the transient parent window changes.
*/
virtual void transientParentWindowChanged(uintptr_t /* winId */)
{
if (pid <= 0)
return;
// unused, meant for custom implementations
}

private:
friend class PluginWindow;
friend class UI;

printf("Waiting for previous process to stop,,,\n");
#ifndef DISTRHO_OS_WINDOWS
struct ExternalProcess {
bool inUse;
bool isQuitting;
mutable pid_t pid;

bool sendTerm = true;
ExternalProcess()
: inUse(false),
isQuitting(false),
pid(0) {}

for (pid_t p;;)
bool isRunning() const noexcept
{
p = ::waitpid(pid, nullptr, WNOHANG);
if (pid <= 0)
return false;

const pid_t p = ::waitpid(pid, nullptr, WNOHANG);

switch (p)
if (p == pid || (p == -1 && errno == ECHILD))
{
d_stdout("NOTICE: Child process exited while idle");
pid = 0;
return false;
}

return true;
}

bool start(const char* args[])
{
terminateAndWait();

pid = vfork();

switch (pid)
{
case 0:
if (sendTerm)
{
sendTerm = false;
::kill(pid, SIGTERM);
}
break;
execvp(args[0], (char**)args);
_exit(1);
return false;

case -1:
if (errno == ECHILD)
{
printf("Done! (no such process)\n");
pid = 0;
return;
}
break;
d_stderr("Could not start external ui");
return false;

default:
if (p == pid)
return true;
}
}

void terminateAndWait()
{
if (pid <= 0)
return;

d_stdout("Waiting for external process to stop,,,");

bool sendTerm = true;

for (pid_t p;;)
{
p = ::waitpid(pid, nullptr, WNOHANG);

switch (p)
{
printf("Done! (clean wait)\n");
pid = 0;
return;
case 0:
if (sendTerm)
{
sendTerm = false;
::kill(pid, SIGTERM);
}
break;

case -1:
if (errno == ECHILD)
{
d_stdout("Done! (no such process)");
pid = 0;
return;
}
break;

default:
if (p == pid)
{
d_stdout("Done! (clean wait)");
pid = 0;
return;
}
break;
}
break;
}

// 5 msec
usleep(5*1000);
// 5 msec
usleep(5*1000);
}
}
}
} ext;
#endif

DISTRHO_DECLARE_NON_COPY_CLASS(ExternalWindow)
struct PrivateData {
uintptr_t parentWindowHandle;
uintptr_t transientWinId;
uint width;
uint height;
double scaleFactor;
String title;
uint minWidth;
uint minHeight;
bool keepAspectRatio;
bool isQuitting;
bool isStandalone;
bool visible;

PrivateData()
: parentWindowHandle(0),
transientWinId(0),
width(1),
height(1),
scaleFactor(1.0),
title(),
minWidth(0),
minHeight(0),
keepAspectRatio(false),
isQuitting(false),
isStandalone(false),
visible(false) {}
} pData;

DISTRHO_DECLARE_NON_COPYABLE(ExternalWindow)
};

// -----------------------------------------------------------------------


+ 21
- 3
source/modules/distrho/extra/LeakDetector.hpp View File

@@ -23,7 +23,25 @@ START_NAMESPACE_DISTRHO

// -----------------------------------------------------------------------
// The following code was based from juce-core LeakDetector class
// Copyright (C) 2013 Raw Material Software Ltd.

/**
Copyright (C) 2013 Raw Material Software Ltd.

Permission is granted to use this software under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license/

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
*/

/** A good old-fashioned C macro concatenation helper.
This combines two items (which may themselves be macros) into a single string,
@@ -54,13 +72,13 @@ START_NAMESPACE_DISTRHO
DISTRHO_NAMESPACE::LeakedObjectDetector<ClassName> DISTRHO_JOIN_MACRO(leakDetector_, ClassName);

# define DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ClassName) \
DISTRHO_DECLARE_NON_COPY_CLASS(ClassName) \
DISTRHO_DECLARE_NON_COPYABLE(ClassName) \
DISTRHO_LEAK_DETECTOR(ClassName)
#else
/** Don't use leak detection on release builds. */
# define DISTRHO_LEAK_DETECTOR(ClassName)
# define DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ClassName) \
DISTRHO_DECLARE_NON_COPY_CLASS(ClassName)
DISTRHO_DECLARE_NON_COPYABLE(ClassName)
#endif

//==============================================================================


+ 20
- 14
source/modules/distrho/extra/Mutex.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -20,6 +20,9 @@
#include "../DistrhoUtils.hpp"

#ifdef DISTRHO_OS_WINDOWS
# ifndef NOMINMAX
# define NOMINMAX
# endif
# include <winsock2.h>
# include <windows.h>
#endif
@@ -39,7 +42,7 @@ public:
/*
* Constructor.
*/
Mutex(bool inheritPriority = true) noexcept
Mutex(const bool inheritPriority = true) noexcept
: fMutex()
{
pthread_mutexattr_t attr;
@@ -61,9 +64,9 @@ public:
/*
* Lock the mutex.
*/
void lock() const noexcept
bool lock() const noexcept
{
pthread_mutex_lock(&fMutex);
return (pthread_mutex_lock(&fMutex) == 0);
}

/*
@@ -86,8 +89,7 @@ public:
private:
mutable pthread_mutex_t fMutex;

DISTRHO_PREVENT_HEAP_ALLOCATION
DISTRHO_DECLARE_NON_COPY_CLASS(Mutex)
DISTRHO_DECLARE_NON_COPYABLE(Mutex)
};

// -----------------------------------------------------------------------
@@ -133,12 +135,13 @@ public:
/*
* Lock the mutex.
*/
void lock() const noexcept
bool lock() const noexcept
{
#ifdef DISTRHO_OS_WINDOWS
EnterCriticalSection(&fSection);
return true;
#else
pthread_mutex_lock(&fMutex);
return (pthread_mutex_lock(&fMutex) == 0);
#endif
}

@@ -174,8 +177,7 @@ private:
mutable pthread_mutex_t fMutex;
#endif

DISTRHO_PREVENT_HEAP_ALLOCATION
DISTRHO_DECLARE_NON_COPY_CLASS(RecursiveMutex)
DISTRHO_DECLARE_NON_COPYABLE(RecursiveMutex)
};

// -----------------------------------------------------------------------
@@ -256,7 +258,7 @@ private:
volatile bool fTriggered;

DISTRHO_PREVENT_HEAP_ALLOCATION
DISTRHO_DECLARE_NON_COPY_CLASS(Signal)
DISTRHO_DECLARE_NON_COPYABLE(Signal)
};

// -----------------------------------------------------------------------
@@ -281,7 +283,7 @@ private:
const Mutex& fMutex;

DISTRHO_PREVENT_HEAP_ALLOCATION
DISTRHO_DECLARE_NON_COPY_CLASS(ScopeLocker)
DISTRHO_DECLARE_NON_COPYABLE(ScopeLocker)
};

// -----------------------------------------------------------------------
@@ -295,6 +297,10 @@ public:
: fMutex(mutex),
fLocked(mutex.tryLock()) {}

ScopeTryLocker(const Mutex& mutex, const bool forceLock) noexcept
: fMutex(mutex),
fLocked(forceLock ? mutex.lock() : mutex.tryLock()) {}

~ScopeTryLocker() noexcept
{
if (fLocked)
@@ -316,7 +322,7 @@ private:
const bool fLocked;

DISTRHO_PREVENT_HEAP_ALLOCATION
DISTRHO_DECLARE_NON_COPY_CLASS(ScopeTryLocker)
DISTRHO_DECLARE_NON_COPYABLE(ScopeTryLocker)
};

// -----------------------------------------------------------------------
@@ -341,7 +347,7 @@ private:
const Mutex& fMutex;

DISTRHO_PREVENT_HEAP_ALLOCATION
DISTRHO_DECLARE_NON_COPY_CLASS(ScopeUnlocker)
DISTRHO_DECLARE_NON_COPYABLE(ScopeUnlocker)
};

// -----------------------------------------------------------------------


+ 22
- 1
source/modules/distrho/extra/ScopedPointer.hpp View File

@@ -25,7 +25,25 @@ START_NAMESPACE_DISTRHO

// -----------------------------------------------------------------------
// The following code was based from juce-core ScopedPointer class
// Copyright (C) 2013 Raw Material Software Ltd.

/**
Copyright (C) 2013 Raw Material Software Ltd.

Permission is granted to use this software under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license/

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
*/

//==============================================================================
/**
@@ -145,6 +163,9 @@ public:
/** Returns the object that this ScopedPointer refers to. */
ObjectType* get() const noexcept { return object; }

/** Returns the object that this ScopedPointer refers to. */
ObjectType& getObject() const noexcept { return *object; }

/** Returns the object that this ScopedPointer refers to. */
ObjectType& operator*() const noexcept { return *object; }



+ 3
- 0
source/modules/distrho/extra/Sleep.hpp View File

@@ -20,6 +20,9 @@
#include "../DistrhoUtils.hpp"

#ifdef DISTRHO_OS_WINDOWS
# ifndef NOMINMAX
# define NOMINMAX
# endif
# include <winsock2.h>
# include <windows.h>
#else


+ 215
- 70
source/modules/distrho/extra/String.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -18,6 +18,7 @@
#define DISTRHO_STRING_HPP_INCLUDED

#include "../DistrhoUtils.hpp"
#include "../extra/ScopedSafeLocale.hpp"

#include <algorithm>

@@ -37,14 +38,16 @@ public:
*/
explicit String() noexcept
: fBuffer(_null()),
fBufferLen(0) {}
fBufferLen(0),
fBufferAlloc(false) {}

/*
* Simple character.
*/
explicit String(const char c) noexcept
: fBuffer(_null()),
fBufferLen(0)
fBufferLen(0),
fBufferAlloc(false)
{
char ch[2];
ch[0] = c;
@@ -56,20 +59,21 @@ public:
/*
* Simple char string.
*/
explicit String(char* const strBuf, const bool copyData = true) noexcept
explicit String(char* const strBuf, const bool reallocData = true) noexcept
: fBuffer(_null()),
fBufferLen(0)
fBufferLen(0),
fBufferAlloc(false)
{
if (copyData || strBuf == nullptr)
if (reallocData || strBuf == nullptr)
{
_dup(strBuf);
}
else
{
fBuffer = strBuf;
fBufferLen = std::strlen(strBuf);
fBuffer = strBuf;
fBufferLen = std::strlen(strBuf);
fBufferAlloc = true;
}

}

/*
@@ -77,7 +81,8 @@ public:
*/
explicit String(const char* const strBuf) noexcept
: fBuffer(_null()),
fBufferLen(0)
fBufferLen(0),
fBufferAlloc(false)
{
_dup(strBuf);
}
@@ -87,7 +92,8 @@ public:
*/
explicit String(const int value) noexcept
: fBuffer(_null()),
fBufferLen(0)
fBufferLen(0),
fBufferAlloc(false)
{
char strBuf[0xff+1];
std::snprintf(strBuf, 0xff, "%d", value);
@@ -101,7 +107,8 @@ public:
*/
explicit String(const unsigned int value, const bool hexadecimal = false) noexcept
: fBuffer(_null()),
fBufferLen(0)
fBufferLen(0),
fBufferAlloc(false)
{
char strBuf[0xff+1];
std::snprintf(strBuf, 0xff, hexadecimal ? "0x%x" : "%u", value);
@@ -115,7 +122,8 @@ public:
*/
explicit String(const long value) noexcept
: fBuffer(_null()),
fBufferLen(0)
fBufferLen(0),
fBufferAlloc(false)
{
char strBuf[0xff+1];
std::snprintf(strBuf, 0xff, "%ld", value);
@@ -129,7 +137,8 @@ public:
*/
explicit String(const unsigned long value, const bool hexadecimal = false) noexcept
: fBuffer(_null()),
fBufferLen(0)
fBufferLen(0),
fBufferAlloc(false)
{
char strBuf[0xff+1];
std::snprintf(strBuf, 0xff, hexadecimal ? "0x%lx" : "%lu", value);
@@ -143,7 +152,8 @@ public:
*/
explicit String(const long long value) noexcept
: fBuffer(_null()),
fBufferLen(0)
fBufferLen(0),
fBufferAlloc(false)
{
char strBuf[0xff+1];
std::snprintf(strBuf, 0xff, "%lld", value);
@@ -157,7 +167,8 @@ public:
*/
explicit String(const unsigned long long value, const bool hexadecimal = false) noexcept
: fBuffer(_null()),
fBufferLen(0)
fBufferLen(0),
fBufferAlloc(false)
{
char strBuf[0xff+1];
std::snprintf(strBuf, 0xff, hexadecimal ? "0x%llx" : "%llu", value);
@@ -171,10 +182,16 @@ public:
*/
explicit String(const float value) noexcept
: fBuffer(_null()),
fBufferLen(0)
fBufferLen(0),
fBufferAlloc(false)
{
char strBuf[0xff+1];
std::snprintf(strBuf, 0xff, "%f", value);

{
const ScopedSafeLocale ssl;
std::snprintf(strBuf, 0xff, "%.12g", static_cast<double>(value));
}

strBuf[0xff] = '\0';

_dup(strBuf);
@@ -185,10 +202,16 @@ public:
*/
explicit String(const double value) noexcept
: fBuffer(_null()),
fBufferLen(0)
fBufferLen(0),
fBufferAlloc(false)
{
char strBuf[0xff+1];
std::snprintf(strBuf, 0xff, "%g", value);

{
const ScopedSafeLocale ssl;
std::snprintf(strBuf, 0xff, "%.24g", value);
}

strBuf[0xff] = '\0';

_dup(strBuf);
@@ -202,7 +225,8 @@ public:
*/
String(const String& str) noexcept
: fBuffer(_null()),
fBufferLen(0)
fBufferLen(0),
fBufferAlloc(false)
{
_dup(str.fBuffer);
}
@@ -217,13 +241,12 @@ public:
{
DISTRHO_SAFE_ASSERT_RETURN(fBuffer != nullptr,);

if (fBuffer == _null())
return;

std::free(fBuffer);
if (fBufferAlloc)
std::free(fBuffer);

fBuffer = nullptr;
fBufferLen = 0;
fBuffer = nullptr;
fBufferLen = 0;
fBufferAlloc = false;
}

// -------------------------------------------------------------------
@@ -253,6 +276,20 @@ public:
return (fBufferLen != 0);
}

/*
* Check if the string contains a specific character, case-sensitive.
*/
bool contains(const char c) const noexcept
{
for (std::size_t i=0; i<fBufferLen; ++i)
{
if (fBuffer[i] == c)
return true;
}

return false;
}

/*
* Check if the string contains another string, optionally ignoring case.
*/
@@ -388,7 +425,7 @@ public:
if (ret < 0)
{
// should never happen!
d_safe_assert("ret >= 0", __FILE__, __LINE__);
d_safe_assert_int("ret >= 0", __FILE__, __LINE__, int(ret));

if (found != nullptr)
*found = false;
@@ -479,7 +516,7 @@ public:
*/
String& replace(const char before, const char after) noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(before != '\0' && after != '\0', *this);
DISTRHO_SAFE_ASSERT_RETURN(before != '\0' /* && after != '\0' */, *this);

for (std::size_t i=0; i < fBufferLen; ++i)
{
@@ -490,6 +527,29 @@ public:
return *this;
}

/*
* Remove all occurrences of character 'c', shifting and truncating the string as necessary.
*/
String& remove(const char c) noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(c != '\0', *this);

if (fBufferLen == 0)
return *this;

for (std::size_t i=0; i < fBufferLen; ++i)
{
if (fBuffer[i] == c)
{
--fBufferLen;
std::memmove(fBuffer+i, fBuffer+i+1, fBufferLen-i);
}
}

fBuffer[fBufferLen] = '\0';
return *this;
}

/*
* Truncate the string to size 'n'.
*/
@@ -498,9 +558,7 @@ public:
if (n >= fBufferLen)
return *this;

for (std::size_t i=n; i < fBufferLen; ++i)
fBuffer[i] = '\0';

fBuffer[n] = '\0';
fBufferLen = n;

return *this;
@@ -529,7 +587,7 @@ public:
}

/*
* Convert to all ascii characters to lowercase.
* Convert all ascii characters to lowercase.
*/
String& toLower() noexcept
{
@@ -545,7 +603,7 @@ public:
}

/*
* Convert to all ascii characters to uppercase.
* Convert all ascii characters to uppercase.
*/
String& toUpper() noexcept
{
@@ -560,6 +618,36 @@ public:
return *this;
}

/*
* Create a new string where all non-basic characters are converted to '_'.
* @see toBasic()
*/
String asBasic() const noexcept
{
String s(*this);
return s.toBasic();
}

/*
* Create a new string where all ascii characters are converted lowercase.
* @see toLower()
*/
String asLower() const noexcept
{
String s(*this);
return s.toLower();
}

/*
* Create a new string where all ascii characters are converted to uppercase.
* @see toUpper()
*/
String asUpper() const noexcept
{
String s(*this);
return s.toUpper();
}

/*
* Direct access to the string buffer (read-only).
*/
@@ -568,6 +656,20 @@ public:
return fBuffer;
}

/*
* Get and release the string buffer, while also clearing this string.
* This allows to keep a pointer to the buffer after this object is deleted.
* Result must be freed.
*/
char* getAndReleaseBuffer() noexcept
{
char* ret = fBufferLen > 0 ? fBuffer : nullptr;
fBuffer = _null();
fBufferLen = 0;
fBufferAlloc = false;
return ret;
}

// -------------------------------------------------------------------
// base64 stuff, based on http://www.adp-gmbh.ch/cpp/common/base64.html
// Copyright (C) 2004-2008 René Nyffenegger
@@ -579,14 +681,18 @@ public:
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";

const std::size_t kTmpBufSize = std::min(d_nextPowerOf2(dataSize/3), 65536U);
#ifndef _MSC_VER
const std::size_t kTmpBufSize = std::min(d_nextPowerOf2(static_cast<uint32_t>(dataSize/3)), 65536U);
#else
constexpr std::size_t kTmpBufSize = 65536U;
#endif

const uchar* bytesToEncode((const uchar*)data);

uint i=0, j=0;
uint charArray3[3], charArray4[4];

char strBuf[kTmpBufSize+1];
char strBuf[kTmpBufSize + 1];
strBuf[kTmpBufSize] = '\0';
std::size_t strBufIndex = 0;

@@ -711,16 +817,26 @@ public:

String& operator+=(const char* const strBuf) noexcept
{
if (strBuf == nullptr)
if (strBuf == nullptr || strBuf[0] == '\0')
return *this;

const std::size_t newBufSize = fBufferLen + std::strlen(strBuf) + 1;
char newBuf[newBufSize];
const std::size_t strBufLen = std::strlen(strBuf);

// for empty strings, we can just take the appended string as our entire data
if (isEmpty())
{
_dup(strBuf, strBufLen);
return *this;
}

// we have some data ourselves, reallocate to add the new stuff
char* const newBuf = (char*)realloc(fBuffer, fBufferLen + strBufLen + 1);
DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, *this);

std::strcpy(newBuf, fBuffer);
std::strcat(newBuf, strBuf);
std::memcpy(newBuf + fBufferLen, strBuf, strBufLen + 1);

_dup(newBuf, newBufSize-1);
fBuffer = newBuf;
fBufferLen += strBufLen;

return *this;
}
@@ -732,15 +848,20 @@ public:

String operator+(const char* const strBuf) noexcept
{
const std::size_t newBufSize = fBufferLen + ((strBuf != nullptr) ? std::strlen(strBuf) : 0) + 1;
char newBuf[newBufSize];
if (strBuf == nullptr || strBuf[0] == '\0')
return *this;
if (isEmpty())
return String(strBuf);

std::strcpy(newBuf, fBuffer);
const std::size_t strBufLen = std::strlen(strBuf);
const std::size_t newBufSize = fBufferLen + strBufLen;
char* const newBuf = (char*)malloc(newBufSize + 1);
DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String());

if (strBuf != nullptr)
std::strcat(newBuf, strBuf);
std::memcpy(newBuf, fBuffer, fBufferLen);
std::memcpy(newBuf + fBufferLen, strBuf, strBufLen + 1);

return String(newBuf);
return String(newBuf, false);
}

String operator+(const String& str) noexcept
@@ -748,11 +869,18 @@ public:
return operator+(str.fBuffer);
}

// needed for std::map compatibility
bool operator<(const String& str) const noexcept
{
return std::strcmp(fBuffer, str.fBuffer) < 0;
}

// -------------------------------------------------------------------

private:
char* fBuffer; // the actual string buffer
std::size_t fBufferLen; // string length
char* fBuffer; // the actual string buffer
std::size_t fBufferLen; // string length
bool fBufferAlloc; // wherever the buffer is allocated, not using _null()

/*
* Static null string.
@@ -780,7 +908,7 @@ private:
if (std::strcmp(fBuffer, strBuf) == 0)
return;

if (fBuffer != _null())
if (fBufferAlloc)
std::free(fBuffer);

fBufferLen = (size > 0) ? size : std::strlen(strBuf);
@@ -788,28 +916,31 @@ private:

if (fBuffer == nullptr)
{
fBuffer = _null();
fBufferLen = 0;
fBuffer = _null();
fBufferLen = 0;
fBufferAlloc = false;
return;
}

std::strcpy(fBuffer, strBuf);
fBufferAlloc = true;

std::strcpy(fBuffer, strBuf);
fBuffer[fBufferLen] = '\0';
}
else
{
DISTRHO_SAFE_ASSERT(size == 0);
DISTRHO_SAFE_ASSERT_UINT(size == 0, static_cast<uint>(size));

// don't recreate null string
if (fBuffer == _null())
if (! fBufferAlloc)
return;

DISTRHO_SAFE_ASSERT(fBuffer != nullptr);
std::free(fBuffer);

fBuffer = _null();
fBufferLen = 0;
fBuffer = _null();
fBufferLen = 0;
fBufferAlloc = false;
}
}

@@ -821,27 +952,41 @@ private:
static inline
String operator+(const String& strBefore, const char* const strBufAfter) noexcept
{
const char* const strBufBefore = strBefore.buffer();
const std::size_t newBufSize = strBefore.length() + ((strBufAfter != nullptr) ? std::strlen(strBufAfter) : 0) + 1;
char newBuf[newBufSize];
if (strBufAfter == nullptr || strBufAfter[0] == '\0')
return strBefore;
if (strBefore.isEmpty())
return String(strBufAfter);

std::strcpy(newBuf, strBufBefore);
std::strcat(newBuf, strBufAfter);
const std::size_t strBeforeLen = strBefore.length();
const std::size_t strBufAfterLen = std::strlen(strBufAfter);
const std::size_t newBufSize = strBeforeLen + strBufAfterLen;
char* const newBuf = (char*)malloc(newBufSize + 1);
DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String());

return String(newBuf);
std::memcpy(newBuf, strBefore.buffer(), strBeforeLen);
std::memcpy(newBuf + strBeforeLen, strBufAfter, strBufAfterLen + 1);

return String(newBuf, false);
}

static inline
String operator+(const char* const strBufBefore, const String& strAfter) noexcept
{
const char* const strBufAfter = strAfter.buffer();
const std::size_t newBufSize = ((strBufBefore != nullptr) ? std::strlen(strBufBefore) : 0) + strAfter.length() + 1;
char newBuf[newBufSize];
if (strAfter.isEmpty())
return String(strBufBefore);
if (strBufBefore == nullptr || strBufBefore[0] == '\0')
return strAfter;

const std::size_t strBufBeforeLen = std::strlen(strBufBefore);
const std::size_t strAfterLen = strAfter.length();
const std::size_t newBufSize = strBufBeforeLen + strAfterLen;
char* const newBuf = (char*)malloc(newBufSize + 1);
DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String());

std::strcpy(newBuf, strBufBefore);
std::strcat(newBuf, strBufAfter);
std::memcpy(newBuf, strBufBefore, strBufBeforeLen);
std::memcpy(newBuf + strBufBeforeLen, strAfter.buffer(), strAfterLen + 1);

return String(newBuf);
return String(newBuf, false);
}

// -----------------------------------------------------------------------


+ 65
- 22
source/modules/distrho/extra/Thread.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -88,33 +88,70 @@ public:
/*
* Start the thread.
*/
bool startThread() noexcept
bool startThread(const bool withRealtimePriority = false) noexcept
{
// check if already running
DISTRHO_SAFE_ASSERT_RETURN(! isThreadRunning(), true);

pthread_t handle;

pthread_attr_t attr;
pthread_attr_init(&attr);

struct sched_param sched_param;
std::memset(&sched_param, 0, sizeof(sched_param));

if (withRealtimePriority)
{
sched_param.sched_priority = 80;

#ifndef DISTRHO_OS_HAIKU
if (pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM) == 0 &&
pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED) == 0 &&
# ifndef DISTRHO_OS_WINDOWS
(pthread_attr_setschedpolicy(&attr, SCHED_FIFO) == 0 ||
pthread_attr_setschedpolicy(&attr, SCHED_RR) == 0) &&
# endif
pthread_attr_setschedparam(&attr, &sched_param) == 0)
{
d_stdout("Thread setup with realtime priority successful");
}
else
#endif
{
d_stdout("Thread setup with realtime priority failed, going with normal priority instead");
pthread_attr_destroy(&attr);
pthread_attr_init(&attr);
}
}

const MutexLocker ml(fLock);

fShouldExit = false;

pthread_t handle;
bool ok = pthread_create(&handle, &attr, _entryPoint, this) == 0;
pthread_attr_destroy(&attr);

if (pthread_create(&handle, nullptr, _entryPoint, this) == 0)
{
if (withRealtimePriority && !ok)
{
d_stdout("Thread with realtime priority failed on creation, going with normal priority instead");
pthread_attr_init(&attr);
ok = pthread_create(&handle, &attr, _entryPoint, this) == 0;
pthread_attr_destroy(&attr);
}

DISTRHO_SAFE_ASSERT_RETURN(ok, false);
#ifdef PTW32_DLLPORT
DISTRHO_SAFE_ASSERT_RETURN(handle.p != nullptr, false);
DISTRHO_SAFE_ASSERT_RETURN(handle.p != nullptr, false);
#else
DISTRHO_SAFE_ASSERT_RETURN(handle != 0, false);
DISTRHO_SAFE_ASSERT_RETURN(handle != 0, false);
#endif
pthread_detach(handle);
_copyFrom(handle);
pthread_detach(handle);
_copyFrom(handle);

// wait for thread to start
fSignal.wait();
return true;
}

return false;
// wait for thread to start
fSignal.wait();
return true;
}

/*
@@ -161,10 +198,7 @@ public:
_copyTo(threadId);
_init();

try {
pthread_cancel(threadId);
} DISTRHO_SAFE_EXCEPTION("pthread_cancel");

pthread_detach(threadId);
return false;
}
}
@@ -191,6 +225,14 @@ public:
return fName;
}

/*
* Returns the Id/handle of the thread.
*/
pthread_t getThreadId() const noexcept
{
return fHandle;
}

/*
* Changes the name of the caller thread.
*/
@@ -201,7 +243,7 @@ public:
#ifdef DISTRHO_OS_LINUX
prctl(PR_SET_NAME, name, 0, 0, 0);
#endif
#if defined(__GLIBC__) && (__GLIBC__ * 1000 + __GLIBC_MINOR__) >= 2012
#if defined(__GLIBC__) && (__GLIBC__ * 1000 + __GLIBC_MINOR__) >= 2012 && !defined(DISTRHO_OS_GNU_HURD)
pthread_setname_np(pthread_self(), name);
#endif
}
@@ -259,7 +301,8 @@ private:
*/
void _runEntryPoint() noexcept
{
setCurrentThreadName(fName);
if (fName.isNotEmpty())
setCurrentThreadName(fName);

// report ready
fSignal.signal();
@@ -281,7 +324,7 @@ private:
return nullptr;
}

DISTRHO_DECLARE_NON_COPY_CLASS(Thread)
DISTRHO_DECLARE_NON_COPYABLE(Thread)
};

// -----------------------------------------------------------------------


+ 119
- 30
source/modules/distrho/src/DistrhoDefines.h View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -27,10 +27,12 @@

/* Check OS */
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
# define DISTRHO_API
# define DISTRHO_PLUGIN_EXPORT extern "C" __declspec (dllexport)
# define DISTRHO_OS_WINDOWS 1
# define DISTRHO_DLL_EXTENSION "dll"
#else
# define DISTRHO_API
# define DISTRHO_PLUGIN_EXPORT extern "C" __attribute__ ((visibility("default")))
# if defined(__APPLE__)
# define DISTRHO_OS_MAC 1
@@ -39,6 +41,10 @@
# define DISTRHO_OS_HAIKU 1
# elif defined(__linux__) || defined(__linux)
# define DISTRHO_OS_LINUX 1
# elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
# define DISTRHO_OS_BSD 1
# elif defined(__GNU__)
# define DISTRHO_OS_GNU_HURD 1
# endif
#endif

@@ -51,26 +57,82 @@
# if HAVE_CPP11_SUPPORT
# define DISTRHO_PROPER_CPP11_SUPPORT
# endif
#elif __cplusplus >= 201103L || (defined(__GNUC__) && defined(__GXX_EXPERIMENTAL_CXX0X__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 405) || __has_extension(cxx_noexcept)
#elif __cplusplus >= 201103L || (defined(__GNUC__) && defined(__GXX_EXPERIMENTAL_CXX0X__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 405) || __has_extension(cxx_noexcept) || (defined(_MSC_VER) && _MSVC_LANG >= 201103L)
# define DISTRHO_PROPER_CPP11_SUPPORT
# if (defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) < 407 && ! defined(__clang__)) || (defined(__clang__) && ! __has_extension(cxx_override_control))
# define override // gcc4.7+ only
# define final // gcc4.7+ only
# define override /* gcc4.7+ only */
# define final /* gcc4.7+ only */
# endif
#endif

#ifndef DISTRHO_PROPER_CPP11_SUPPORT
# define constexpr
# define noexcept throw()
# define override
# define final
# define nullptr NULL
#endif

/* Define unlikely */
#ifdef __GNUC__
# define unlikely(x) __builtin_expect(x,0)
#else
# define unlikely(x) x
#endif

/* Define DISTRHO_DEPRECATED */
#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 480
# define DISTRHO_DEPRECATED __attribute__((deprecated))
#elif defined(_MSC_VER)
# define DISTRHO_DEPRECATED [[deprecated]] /* Note: __declspec(deprecated) it not applicable to enum members */
#else
# define DISTRHO_DEPRECATED
#endif

/* Define DISTRHO_DEPRECATED_BY */
#if defined(__clang__) && defined(DISTRHO_PROPER_CPP11_SUPPORT)
# define DISTRHO_DEPRECATED_BY(other) __attribute__((deprecated("", other)))
#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 480
# define DISTRHO_DEPRECATED_BY(other) __attribute__((deprecated("Use " other)))
#else
# define DISTRHO_DEPRECATED_BY(other) DISTRHO_DEPRECATED
#endif

/* Define DISTRHO_SAFE_ASSERT* */
#define DISTRHO_SAFE_ASSERT(cond) if (! (cond)) d_safe_assert(#cond, __FILE__, __LINE__);
#define DISTRHO_SAFE_ASSERT_BREAK(cond) if (! (cond)) { d_safe_assert(#cond, __FILE__, __LINE__); break; }
#define DISTRHO_SAFE_ASSERT_CONTINUE(cond) if (! (cond)) { d_safe_assert(#cond, __FILE__, __LINE__); continue; }
#define DISTRHO_SAFE_ASSERT_RETURN(cond, ret) if (! (cond)) { d_safe_assert(#cond, __FILE__, __LINE__); return ret; }
#define DISTRHO_SAFE_ASSERT(cond) if (unlikely(!(cond))) d_safe_assert (#cond, __FILE__, __LINE__);
#define DISTRHO_SAFE_ASSERT_INT(cond, value) if (unlikely(!(cond))) d_safe_assert_int (#cond, __FILE__, __LINE__, static_cast<int>(value));
#define DISTRHO_SAFE_ASSERT_INT2(cond, v1, v2) if (unlikely(!(cond))) d_safe_assert_int2 (#cond, __FILE__, __LINE__, static_cast<int>(v1), static_cast<int>(v2));
#define DISTRHO_SAFE_ASSERT_UINT(cond, value) if (unlikely(!(cond))) d_safe_assert_uint (#cond, __FILE__, __LINE__, static_cast<uint>(value));
#define DISTRHO_SAFE_ASSERT_UINT2(cond, v1, v2) if (unlikely(!(cond))) d_safe_assert_uint2(#cond, __FILE__, __LINE__, static_cast<uint>(v1), static_cast<uint>(v2));

#define DISTRHO_SAFE_ASSERT_BREAK(cond) if (unlikely(!(cond))) { d_safe_assert(#cond, __FILE__, __LINE__); break; }
#define DISTRHO_SAFE_ASSERT_CONTINUE(cond) if (unlikely(!(cond))) { d_safe_assert(#cond, __FILE__, __LINE__); continue; }
#define DISTRHO_SAFE_ASSERT_RETURN(cond, ret) if (unlikely(!(cond))) { d_safe_assert(#cond, __FILE__, __LINE__); return ret; }

#define DISTRHO_CUSTOM_SAFE_ASSERT(msg, cond) if (unlikely(!(cond))) d_custom_safe_assert(msg, #cond, __FILE__, __LINE__);
#define DISTRHO_CUSTOM_SAFE_ASSERT_BREAK(msg, cond) if (unlikely(!(cond))) { d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); break; }
#define DISTRHO_CUSTOM_SAFE_ASSERT_CONTINUE(msg, cond) if (unlikely(!(cond))) { d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); continue; }
#define DISTRHO_CUSTOM_SAFE_ASSERT_RETURN(msg, cond, ret) if (unlikely(!(cond))) { d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); return ret; }

#define DISTRHO_CUSTOM_SAFE_ASSERT_ONCE_BREAK(msg, cond) if (unlikely(!(cond))) { static bool _p; if (!_p) { _p = true; d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); } break; }
#define DISTRHO_CUSTOM_SAFE_ASSERT_ONCE_CONTINUE(msg, cond) if (unlikely(!(cond))) { static bool _p; if (!_p) { _p = true; d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); } continue; }
#define DISTRHO_CUSTOM_SAFE_ASSERT_ONCE_RETURN(msg, cond, ret) if (unlikely(!(cond))) { static bool _p; if (!_p) { _p = true; d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); } return ret; }

#define DISTRHO_SAFE_ASSERT_INT_BREAK(cond, value) if (unlikely(!(cond))) { d_safe_assert_int(#cond, __FILE__, __LINE__, static_cast<int>(value)); break; }
#define DISTRHO_SAFE_ASSERT_INT_CONTINUE(cond, value) if (unlikely(!(cond))) { d_safe_assert_int(#cond, __FILE__, __LINE__, static_cast<int>(value)); continue; }
#define DISTRHO_SAFE_ASSERT_INT_RETURN(cond, value, ret) if (unlikely(!(cond))) { d_safe_assert_int(#cond, __FILE__, __LINE__, static_cast<int>(value)); return ret; }

#define DISTRHO_SAFE_ASSERT_INT2_BREAK(cond, v1, v2) if (unlikely(!(cond))) { d_safe_assert_int2(#cond, __FILE__, __LINE__, static_cast<int>(v1), static_cast<int>(v2)); break; }
#define DISTRHO_SAFE_ASSERT_INT2_CONTINUE(cond, v1, v2) if (unlikely(!(cond))) { d_safe_assert_int2(#cond, __FILE__, __LINE__, static_cast<int>(v1), static_cast<int>(v2)); continue; }
#define DISTRHO_SAFE_ASSERT_INT2_RETURN(cond, v1, v2, ret) if (unlikely(!(cond))) { d_safe_assert_int2(#cond, __FILE__, __LINE__, static_cast<int>(v1), static_cast<int>(v2)); return ret; }

#define DISTRHO_SAFE_ASSERT_UINT_BREAK(cond, value) if (unlikely(!(cond))) { d_safe_assert_uint(#cond, __FILE__, __LINE__, static_cast<uint>(value)); break; }
#define DISTRHO_SAFE_ASSERT_UINT_CONTINUE(cond, value) if (unlikely(!(cond))) { d_safe_assert_uint(#cond, __FILE__, __LINE__, static_cast<uint>(value)); continue; }
#define DISTRHO_SAFE_ASSERT_UINT_RETURN(cond, value, ret) if (unlikely(!(cond))) { d_safe_assert_uint(#cond, __FILE__, __LINE__, static_cast<uint>(value)); return ret; }

#define DISTRHO_SAFE_ASSERT_UINT2_BREAK(cond, v1, v2) if (unlikely(!(cond))) { d_safe_assert_uint2(#cond, __FILE__, __LINE__, static_cast<uint>(v1), static_cast<uint>(v2)); break; }
#define DISTRHO_SAFE_ASSERT_UINT2_CONTINUE(cond, v1, v2) if (unlikely(!(cond))) { d_safe_assert_uint2(#cond, __FILE__, __LINE__, static_cast<uint>(v1), static_cast<uint>(v2)); continue; }
#define DISTRHO_SAFE_ASSERT_UINT2_RETURN(cond, v1, v2, ret) if (unlikely(!(cond))) { d_safe_assert_uint2(#cond, __FILE__, __LINE__, static_cast<uint>(v1), static_cast<uint>(v2)); return ret; }

/* Define DISTRHO_SAFE_EXCEPTION */
#define DISTRHO_SAFE_EXCEPTION(msg) catch(...) { d_safe_exception(msg, __FILE__, __LINE__); }
@@ -78,34 +140,23 @@
#define DISTRHO_SAFE_EXCEPTION_CONTINUE(msg) catch(...) { d_safe_exception(msg, __FILE__, __LINE__); continue; }
#define DISTRHO_SAFE_EXCEPTION_RETURN(msg, ret) catch(...) { d_safe_exception(msg, __FILE__, __LINE__); return ret; }

/* Define DISTRHO_DECLARE_NON_COPY_CLASS */
/* Define DISTRHO_DECLARE_NON_COPYABLE */
#ifdef DISTRHO_PROPER_CPP11_SUPPORT
# define DISTRHO_DECLARE_NON_COPY_CLASS(ClassName) \
private: \
ClassName(ClassName&) = delete; \
ClassName(const ClassName&) = delete; \
ClassName& operator=(ClassName&) = delete ; \
# define DISTRHO_DECLARE_NON_COPYABLE(ClassName) \
private: \
ClassName(ClassName&) = delete; \
ClassName(const ClassName&) = delete; \
ClassName& operator=(ClassName&) = delete; \
ClassName& operator=(const ClassName&) = delete;
#else
# define DISTRHO_DECLARE_NON_COPY_CLASS(ClassName) \
private: \
ClassName(ClassName&); \
ClassName(const ClassName&); \
ClassName& operator=(ClassName&); \
# define DISTRHO_DECLARE_NON_COPYABLE(ClassName) \
private: \
ClassName(ClassName&); \
ClassName(const ClassName&); \
ClassName& operator=(ClassName&); \
ClassName& operator=(const ClassName&);
#endif

/* Define DISTRHO_DECLARE_NON_COPY_STRUCT */
#ifdef DISTRHO_PROPER_CPP11_SUPPORT
# define DISTRHO_DECLARE_NON_COPY_STRUCT(StructName) \
StructName(StructName&) = delete; \
StructName(const StructName&) = delete; \
StructName& operator=(StructName&) = delete; \
StructName& operator=(const StructName&) = delete;
#else
# define DISTRHO_DECLARE_NON_COPY_STRUCT(StructName)
#endif

/* Define DISTRHO_PREVENT_HEAP_ALLOCATION */
#ifdef DISTRHO_PROPER_CPP11_SUPPORT
# define DISTRHO_PREVENT_HEAP_ALLOCATION \
@@ -119,6 +170,17 @@ private: \
static void operator delete(void*);
#endif

/* Define DISTRHO_PREVENT_VIRTUAL_HEAP_ALLOCATION */
#ifdef DISTRHO_PROPER_CPP11_SUPPORT
# define DISTRHO_PREVENT_VIRTUAL_HEAP_ALLOCATION \
private: \
static void* operator new(std::size_t) = delete;
#else
# define DISTRHO_PREVENT_VIRTUAL_HEAP_ALLOCATION \
private: \
static void* operator new(std::size_t);
#endif

/* Define namespace */
#ifndef DISTRHO_NAMESPACE
# define DISTRHO_NAMESPACE DISTRHO
@@ -127,10 +189,37 @@ private: \
#define END_NAMESPACE_DISTRHO }
#define USE_NAMESPACE_DISTRHO using namespace DISTRHO_NAMESPACE;

/* Define DISTRHO_OS_SEP and DISTRHO_OS_SPLIT */
#ifdef DISTRHO_OS_WINDOWS
# define DISTRHO_OS_SEP '\\'
# define DISTRHO_OS_SEP_STR "\\"
# define DISTRHO_OS_SPLIT ';'
# define DISTRHO_OS_SPLIT_STR ";"
#else
# define DISTRHO_OS_SEP '/'
# define DISTRHO_OS_SEP_STR "/"
# define DISTRHO_OS_SPLIT ':'
# define DISTRHO_OS_SPLIT_STR ":"
#endif

/* MSVC warnings */
#ifdef _MSC_VER
# define strdup _strdup
# pragma warning(disable:4244) /* possible loss of data */
#endif

/* Useful macros */
#define ARRAY_SIZE(ARRAY) sizeof(ARRAY)/sizeof(ARRAY[0])

/* Useful typedefs */
typedef unsigned char uchar;
typedef unsigned short int ushort;
typedef unsigned int uint;
typedef unsigned long int ulong;
typedef unsigned long long int ulonglong;

/* Deprecated macros */
#define DISTRHO_DECLARE_NON_COPY_CLASS(ClassName) DISTRHO_DECLARE_NON_COPYABLE(ClassName)
#define DISTRHO_DECLARE_NON_COPY_STRUCT(StructName) DISTRHO_DECLARE_NON_COPYABLE(StructName)

#endif // DISTRHO_DEFINES_H_INCLUDED

+ 115
- 15
source/modules/distrho/src/DistrhoPlugin.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -21,16 +21,20 @@ START_NAMESPACE_DISTRHO
/* ------------------------------------------------------------------------------------------------------------
* Static data, see DistrhoPluginInternal.hpp */

uint32_t d_lastBufferSize = 0;
double d_lastSampleRate = 0.0;
uint32_t d_nextBufferSize = 0;
double d_nextSampleRate = 0.0;
const char* d_nextBundlePath = nullptr;
bool d_nextPluginIsDummy = false;
bool d_nextCanRequestParameterValueChanges = false;

/* ------------------------------------------------------------------------------------------------------------
* Static fallback data, see DistrhoPluginInternal.hpp */

const String PluginExporter::sFallbackString;
const AudioPort PluginExporter::sFallbackAudioPort;
/* */ AudioPortWithBusId PluginExporter::sFallbackAudioPort;
const ParameterRanges PluginExporter::sFallbackRanges;
const ParameterEnumerationValues PluginExporter::sFallbackEnumValues;
const PortGroupWithId PluginExporter::sFallbackPortGroup;

/* ------------------------------------------------------------------------------------------------------------
* Plugin */
@@ -39,7 +43,13 @@ Plugin::Plugin(uint32_t parameterCount, uint32_t programCount, uint32_t stateCou
: pData(new PrivateData())
{
#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
pData->audioPorts = new AudioPort[DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS];
pData->audioPorts = new AudioPortWithBusId[DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS];
#endif

#ifdef DPF_ABORT_ON_ERROR
# define DPF_ABORT abort();
#else
# define DPF_ABORT
#endif

if (parameterCount > 0)
@@ -48,26 +58,29 @@ Plugin::Plugin(uint32_t parameterCount, uint32_t programCount, uint32_t stateCou
pData->parameters = new Parameter[parameterCount];
}

#if DISTRHO_PLUGIN_WANT_PROGRAMS
if (programCount > 0)
{
#if DISTRHO_PLUGIN_WANT_PROGRAMS
pData->programCount = programCount;
pData->programNames = new String[programCount];
}
#else
DISTRHO_SAFE_ASSERT(programCount == 0);
d_stderr2("DPF warning: Plugins with programs must define `DISTRHO_PLUGIN_WANT_PROGRAMS` to 1");
DPF_ABORT
#endif
}

#if DISTRHO_PLUGIN_WANT_STATE
if (stateCount > 0)
{
pData->stateCount = stateCount;
pData->stateKeys = new String[stateCount];
pData->stateDefValues = new String[stateCount];
}
#if DISTRHO_PLUGIN_WANT_STATE
pData->stateCount = stateCount;
pData->states = new State[stateCount];
#else
DISTRHO_SAFE_ASSERT(stateCount == 0);
d_stderr2("DPF warning: Plugins with state must define `DISTRHO_PLUGIN_WANT_STATE` to 1");
DPF_ABORT
#endif
}

#undef DPF_ABORT
}

Plugin::~Plugin()
@@ -88,6 +101,16 @@ double Plugin::getSampleRate() const noexcept
return pData->sampleRate;
}

const char* Plugin::getBundlePath() const noexcept
{
return pData->bundlePath;
}

bool Plugin::isDummyInstance() const noexcept
{
return pData->isDummy;
}

#if DISTRHO_PLUGIN_WANT_TIMEPOS
const TimePosition& Plugin::getTimePosition() const noexcept
{
@@ -109,6 +132,25 @@ bool Plugin::writeMidiEvent(const MidiEvent& midiEvent) noexcept
}
#endif

#if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
bool Plugin::canRequestParameterValueChanges() const noexcept
{
return pData->canRequestParameterValueChanges;
}

bool Plugin::requestParameterValueChange(const uint32_t index, const float value) noexcept
{
return pData->requestParameterValueChangeCallback(index, value);
}
#endif

#if DISTRHO_PLUGIN_WANT_STATE
bool Plugin::updateStateValue(const char* const key, const char* const value) noexcept
{
return pData->updateStateValueCallback(key, value);
}
#endif

/* ------------------------------------------------------------------------------------------------------------
* Init */

@@ -130,11 +172,69 @@ void Plugin::initAudioPort(bool input, uint32_t index, AudioPort& port)
}
}

void Plugin::initParameter(uint32_t, Parameter&) {}

void Plugin::initPortGroup(const uint32_t groupId, PortGroup& portGroup)
{
fillInPredefinedPortGroupData(groupId, portGroup);
}

#if DISTRHO_PLUGIN_WANT_PROGRAMS
void Plugin::initProgramName(uint32_t, String&) {}
#endif

#if DISTRHO_PLUGIN_WANT_STATE
void Plugin::initState(const uint32_t index, State& state)
{
uint hints = 0x0;
String stateKey, defaultStateValue;

#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
initState(index, stateKey, defaultStateValue);
if (isStateFile(index))
hints = kStateIsFilenamePath;
#if defined(__clang__)
#pragma clang diagnostic pop
#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
#pragma GCC diagnostic pop
#endif

state.hints = hints;
state.key = stateKey;
state.label = stateKey;
state.defaultValue = defaultStateValue;
}
#endif

/* ------------------------------------------------------------------------------------------------------------
* Init */

float Plugin::getParameterValue(uint32_t) const { return 0.0f; }
void Plugin::setParameterValue(uint32_t, float) {}

#if DISTRHO_PLUGIN_WANT_PROGRAMS
void Plugin::loadProgram(uint32_t) {}
#endif

#if DISTRHO_PLUGIN_WANT_FULL_STATE
String Plugin::getState(const char*) const { return String(); }
#endif

#if DISTRHO_PLUGIN_WANT_STATE
void Plugin::setState(const char*, const char*) {}
#endif

/* ------------------------------------------------------------------------------------------------------------
* Callbacks (optional) */

void Plugin::bufferSizeChanged(uint32_t) {}
void Plugin::sampleRateChanged(double) {}
void Plugin::sampleRateChanged(double) {}

// -----------------------------------------------------------------------------------------------------------



+ 30
- 16
source/modules/distrho/src/DistrhoPluginCarla.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -22,6 +22,10 @@

#include "CarlaNative.hpp"

// TODO
#undef DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
#define DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST 0

// -----------------------------------------------------------------------

START_NAMESPACE_DISTRHO
@@ -29,6 +33,9 @@ START_NAMESPACE_DISTRHO
#if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
static const writeMidiFunc writeMidiCallback = nullptr;
#endif
#if ! DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
static const requestParameterValueChangeFunc requestParameterValueChangeCallback = nullptr;
#endif

#if DISTRHO_PLUGIN_HAS_UI
// -----------------------------------------------------------------------
@@ -98,13 +105,9 @@ public:
// ---------------------------------------------

protected:
void handleEditParameter(const uint32_t rindex, const bool touch)
void handleEditParameter(const uint32_t, const bool)
{
fHost->dispatcher(fHost->handle,
NATIVE_HOST_OPCODE_UI_TOUCH_PARAMETER,
static_cast<int32_t>(rindex),
touch ? 1 : 0,
nullptr, 0.0f);
// TODO
}

void handleSetParameterValue(const uint32_t rindex, const float value)
@@ -188,7 +191,7 @@ class PluginCarla : public NativePluginClass
public:
PluginCarla(const NativeHostDescriptor* const host)
: NativePluginClass(host),
fPlugin(this, writeMidiCallback),
fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback),
fScalePointsCache(nullptr)
{
#if DISTRHO_PLUGIN_HAS_UI
@@ -235,8 +238,8 @@ protected:
int nativeParamHints = ::NATIVE_PARAMETER_IS_ENABLED;
const uint32_t paramHints = fPlugin.getParameterHints(index);

if (paramHints & kParameterIsAutomable)
nativeParamHints |= ::NATIVE_PARAMETER_IS_AUTOMATABLE;
if (paramHints & kParameterIsAutomatable)
nativeParamHints |= ::NATIVE_PARAMETER_IS_AUTOMABLE;
if (paramHints & kParameterIsBoolean)
nativeParamHints |= ::NATIVE_PARAMETER_IS_BOOLEAN;
if (paramHints & kParameterIsInteger)
@@ -364,8 +367,7 @@ protected:
}

#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
void process(const float* const* const inBuffer, float** const outBuffer, const uint32_t frames,
const NativeMidiEvent* const midiEvents, const uint32_t midiEventCount) override
void process(float** const inBuffer, float** const outBuffer, const uint32_t frames, const NativeMidiEvent* const midiEvents, const uint32_t midiEventCount) override
{
MidiEvent realMidiEvents[midiEventCount];

@@ -386,13 +388,12 @@ protected:
realMidiEvent.dataExt = nullptr;
}

fPlugin.run(inBuffer, outBuffer, frames, realMidiEvents, midiEventCount);
fPlugin.run(const_cast<const float**>(inBuffer), outBuffer, frames, realMidiEvents, midiEventCount);
}
#else
void process(const float* const* const inBuffer, float** const outBuffer, const uint32_t frames,
const NativeMidiEvent* const, const uint32_t) override
void process(float** const inBuffer, float** const outBuffer, const uint32_t frames, const NativeMidiEvent* const, const uint32_t) override
{
fPlugin.run(inBuffer, outBuffer, frames);
fPlugin.run(const_cast<const float**>(inBuffer), outBuffer, frames);
}
#endif

@@ -518,6 +519,19 @@ private:
}
#endif

#if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
bool requestParameterValueChange(const uint32_t index, const float value)
{
// TODO implementation
return false;
}

static bool requestParameterValueChangeCallback(void* ptr, const uint32_t index, const float value)
{
return thisPtr->requestParameterValueChange(index, value);
}
#endif

CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginCarla)

// -------------------------------------------------------------------


+ 39
- 5
source/modules/distrho/src/DistrhoPluginChecks.h View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -69,6 +69,10 @@
# define DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 0
#endif

#ifndef DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
# define DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST 0
#endif

#ifndef DISTRHO_PLUGIN_WANT_PROGRAMS
# define DISTRHO_PLUGIN_WANT_PROGRAMS 0
#endif
@@ -79,12 +83,17 @@

#ifndef DISTRHO_PLUGIN_WANT_FULL_STATE
# define DISTRHO_PLUGIN_WANT_FULL_STATE 0
# define DISTRHO_PLUGIN_WANT_FULL_STATE_WAS_NOT_SET
#endif

#ifndef DISTRHO_PLUGIN_WANT_TIMEPOS
# define DISTRHO_PLUGIN_WANT_TIMEPOS 0
#endif

#ifndef DISTRHO_UI_USER_RESIZABLE
# define DISTRHO_UI_USER_RESIZABLE 0
#endif

#ifndef DISTRHO_UI_USE_NANOVG
# define DISTRHO_UI_USE_NANOVG 0
#endif
@@ -93,7 +102,7 @@
// Define DISTRHO_PLUGIN_HAS_EMBED_UI if needed

#ifndef DISTRHO_PLUGIN_HAS_EMBED_UI
# ifdef HAVE_DGL
# if (defined(DGL_CAIRO) && defined(HAVE_CAIRO)) || (defined(DGL_OPENGL) && defined(HAVE_OPENGL))
# define DISTRHO_PLUGIN_HAS_EMBED_UI 1
# else
# define DISTRHO_PLUGIN_HAS_EMBED_UI 0
@@ -104,7 +113,7 @@
// Define DISTRHO_UI_URI if needed

#ifndef DISTRHO_UI_URI
# define DISTRHO_UI_URI DISTRHO_PLUGIN_URI "#UI"
# define DISTRHO_UI_URI DISTRHO_PLUGIN_URI "#DPF_UI"
#endif

// -----------------------------------------------------------------------
@@ -123,10 +132,23 @@
# error Synths need MIDI input to work!
#endif

// -----------------------------------------------------------------------
// Enable state if plugin wants state files (deprecated)

#ifdef DISTRHO_PLUGIN_WANT_STATEFILES
# warning DISTRHO_PLUGIN_WANT_STATEFILES is deprecated
# undef DISTRHO_PLUGIN_WANT_STATEFILES
# if ! DISTRHO_PLUGIN_WANT_STATE
# undef DISTRHO_PLUGIN_WANT_STATE
# define DISTRHO_PLUGIN_WANT_STATE 1
# endif
#endif

// -----------------------------------------------------------------------
// Enable full state if plugin exports presets

#if DISTRHO_PLUGIN_WANT_PROGRAMS && DISTRHO_PLUGIN_WANT_STATE
#if DISTRHO_PLUGIN_WANT_PROGRAMS && DISTRHO_PLUGIN_WANT_STATE && defined(DISTRHO_PLUGIN_WANT_FULL_STATE_WAS_NOT_SET)
# warning Plugins with programs and state should implement full state API too
# undef DISTRHO_PLUGIN_WANT_FULL_STATE
# define DISTRHO_PLUGIN_WANT_FULL_STATE 1
#endif
@@ -134,11 +156,23 @@
// -----------------------------------------------------------------------
// Disable UI if DGL or External UI is not available

#if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_HAS_EXTERNAL_UI && ! defined(HAVE_DGL)
#if (defined(DGL_CAIRO) && ! defined(HAVE_CAIRO)) || (defined(DGL_OPENGL) && ! defined(HAVE_OPENGL))
# undef DISTRHO_PLUGIN_HAS_EMBED_UI
# define DISTRHO_PLUGIN_HAS_EMBED_UI 0
#endif

#if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_HAS_EMBED_UI && ! DISTRHO_PLUGIN_HAS_EXTERNAL_UI
# undef DISTRHO_PLUGIN_HAS_UI
# define DISTRHO_PLUGIN_HAS_UI 0
#endif

// -----------------------------------------------------------------------
// Prevent users from messing about with DPF internals

#ifdef DISTRHO_UI_IS_STANDALONE
# error DISTRHO_UI_IS_STANDALONE must not be defined
#endif

// -----------------------------------------------------------------------

#endif // DISTRHO_PLUGIN_CHECKS_H_INCLUDED

+ 334
- 33
source/modules/distrho/src/DistrhoPluginInternal.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -19,6 +19,12 @@

#include "../DistrhoPlugin.hpp"

#ifdef DISTRHO_PLUGIN_TARGET_VST3
# include "DistrhoPluginVST3.hpp"
#endif

#include <set>

START_NAMESPACE_DISTRHO

// -----------------------------------------------------------------------
@@ -29,28 +35,76 @@ static const uint32_t kMaxMidiEvents = 512;
// -----------------------------------------------------------------------
// Static data, see DistrhoPlugin.cpp

extern uint32_t d_lastBufferSize;
extern double d_lastSampleRate;
extern uint32_t d_nextBufferSize;
extern double d_nextSampleRate;
extern const char* d_nextBundlePath;
extern bool d_nextPluginIsDummy;
extern bool d_nextCanRequestParameterValueChanges;

// -----------------------------------------------------------------------
// DSP callbacks

typedef bool (*writeMidiFunc) (void* ptr, const MidiEvent& midiEvent);
typedef bool (*requestParameterValueChangeFunc) (void* ptr, uint32_t index, float value);
typedef bool (*updateStateValueFunc) (void* ptr, const char* key, const char* value);

// -----------------------------------------------------------------------
// Helpers

struct AudioPortWithBusId : AudioPort {
uint32_t busId;

AudioPortWithBusId()
: AudioPort(),
busId(0) {}
};

struct PortGroupWithId : PortGroup {
uint32_t groupId;

PortGroupWithId()
: PortGroup(),
groupId(kPortGroupNone) {}
};

static void fillInPredefinedPortGroupData(const uint32_t groupId, PortGroup& portGroup)
{
switch (groupId)
{
case kPortGroupNone:
portGroup.name.clear();
portGroup.symbol.clear();
break;
case kPortGroupMono:
portGroup.name = "Mono";
portGroup.symbol = "dpf_mono";
break;
case kPortGroupStereo:
portGroup.name = "Stereo";
portGroup.symbol = "dpf_stereo";
break;
}
}

// -----------------------------------------------------------------------
// Plugin private data

struct Plugin::PrivateData {
const bool canRequestParameterValueChanges;
const bool isDummy;
bool isProcessing;

#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
AudioPort* audioPorts;
AudioPortWithBusId* audioPorts;
#endif

uint32_t parameterCount;
uint32_t parameterOffset;
Parameter* parameters;

uint32_t portGroupCount;
PortGroupWithId* portGroups;

#if DISTRHO_PLUGIN_WANT_PROGRAMS
uint32_t programCount;
String* programNames;
@@ -58,8 +112,7 @@ struct Plugin::PrivateData {

#if DISTRHO_PLUGIN_WANT_STATE
uint32_t stateCount;
String* stateKeys;
String* stateDefValues;
State* states;
#endif

#if DISTRHO_PLUGIN_WANT_LATENCY
@@ -73,34 +126,43 @@ struct Plugin::PrivateData {
// Callbacks
void* callbacksPtr;
writeMidiFunc writeMidiCallbackFunc;
requestParameterValueChangeFunc requestParameterValueChangeCallbackFunc;
updateStateValueFunc updateStateValueCallbackFunc;

uint32_t bufferSize;
double sampleRate;
char* bundlePath;

PrivateData() noexcept
: isProcessing(false),
: canRequestParameterValueChanges(d_nextCanRequestParameterValueChanges),
isDummy(d_nextPluginIsDummy),
isProcessing(false),
#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
audioPorts(nullptr),
#endif
parameterCount(0),
parameterOffset(0),
parameters(nullptr),
portGroupCount(0),
portGroups(nullptr),
#if DISTRHO_PLUGIN_WANT_PROGRAMS
programCount(0),
programNames(nullptr),
#endif
#if DISTRHO_PLUGIN_WANT_STATE
stateCount(0),
stateKeys(nullptr),
stateDefValues(nullptr),
states(nullptr),
#endif
#if DISTRHO_PLUGIN_WANT_LATENCY
latency(0),
#endif
callbacksPtr(nullptr),
writeMidiCallbackFunc(nullptr),
bufferSize(d_lastBufferSize),
sampleRate(d_lastSampleRate)
requestParameterValueChangeCallbackFunc(nullptr),
updateStateValueCallbackFunc(nullptr),
bufferSize(d_nextBufferSize),
sampleRate(d_nextSampleRate),
bundlePath(d_nextBundlePath != nullptr ? strdup(d_nextBundlePath) : nullptr)
{
DISTRHO_SAFE_ASSERT(bufferSize != 0);
DISTRHO_SAFE_ASSERT(d_isNotZero(sampleRate));
@@ -113,12 +175,16 @@ struct Plugin::PrivateData {
#endif

#ifdef DISTRHO_PLUGIN_TARGET_LV2
# if (DISTRHO_PLUGIN_IS_SYNTH || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE)
# if (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_STATE || DISTRHO_PLUGIN_WANT_TIMEPOS)
parameterOffset += 1;
# if DISTRHO_PLUGIN_WANT_STATE
# endif
# if (DISTRHO_PLUGIN_WANT_MIDI_OUTPUT || DISTRHO_PLUGIN_WANT_STATE)
parameterOffset += 1;
# endif
# endif
#endif

#ifdef DISTRHO_PLUGIN_TARGET_VST3
parameterOffset += kVst3InternalParameterCount;
#endif
}

@@ -138,6 +204,12 @@ struct Plugin::PrivateData {
parameters = nullptr;
}

if (portGroups != nullptr)
{
delete[] portGroups;
portGroups = nullptr;
}

#if DISTRHO_PLUGIN_WANT_PROGRAMS
if (programNames != nullptr)
{
@@ -147,18 +219,18 @@ struct Plugin::PrivateData {
#endif

#if DISTRHO_PLUGIN_WANT_STATE
if (stateKeys != nullptr)
if (states != nullptr)
{
delete[] stateKeys;
stateKeys = nullptr;
delete[] states;
states = nullptr;
}
#endif

if (stateDefValues != nullptr)
if (bundlePath != nullptr)
{
delete[] stateDefValues;
stateDefValues = nullptr;
std::free(bundlePath);
bundlePath = nullptr;
}
#endif
}

#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
@@ -170,6 +242,27 @@ struct Plugin::PrivateData {
return false;
}
#endif

#if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
bool requestParameterValueChangeCallback(const uint32_t index, const float value)
{
if (requestParameterValueChangeCallbackFunc != nullptr)
return requestParameterValueChangeCallbackFunc(callbacksPtr, index, value);

return false;
}
#endif

#if DISTRHO_PLUGIN_WANT_STATE
bool updateStateValueCallback(const char* const key, const char* const value)
{
d_stdout("updateStateValueCallback %p", updateStateValueCallbackFunc);
if (updateStateValueCallbackFunc != nullptr)
return updateStateValueCallbackFunc(callbacksPtr, key, value);

return false;
}
#endif
};

// -----------------------------------------------------------------------
@@ -178,7 +271,10 @@ struct Plugin::PrivateData {
class PluginExporter
{
public:
PluginExporter(void* const callbacksPtr, const writeMidiFunc writeMidiCall)
PluginExporter(void* const callbacksPtr,
const writeMidiFunc writeMidiCall,
const requestParameterValueChangeFunc requestParameterValueChangeCall,
const updateStateValueFunc updateStateValueCall)
: fPlugin(createPlugin()),
fData((fPlugin != nullptr) ? fPlugin->pData : nullptr),
fIsActive(false)
@@ -186,6 +282,79 @@ public:
DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,);

#if defined(DPF_RUNTIME_TESTING) && defined(__GNUC__) && !defined(__clang__)
/* Run-time testing build.
* Verify that virtual functions are overriden if parameters, programs or states are in use.
* This does not work on all compilers, but we use it purely as informational check anyway. */
if (fData->parameterCount != 0)
{
if ((void*)(fPlugin->*(&Plugin::initParameter)) == (void*)&Plugin::initParameter)
{
d_stderr2("DPF warning: Plugins with parameters must implement `initParameter`");
abort();
}
if ((void*)(fPlugin->*(&Plugin::getParameterValue)) == (void*)&Plugin::getParameterValue)
{
d_stderr2("DPF warning: Plugins with parameters must implement `getParameterValue`");
abort();
}
if ((void*)(fPlugin->*(&Plugin::setParameterValue)) == (void*)&Plugin::setParameterValue)
{
d_stderr2("DPF warning: Plugins with parameters must implement `setParameterValue`");
abort();
}
}

# if DISTRHO_PLUGIN_WANT_PROGRAMS
if (fData->programCount != 0)
{
if ((void*)(fPlugin->*(&Plugin::initProgramName)) == (void*)&Plugin::initProgramName)
{
d_stderr2("DPF warning: Plugins with programs must implement `initProgramName`");
abort();
}
if ((void*)(fPlugin->*(&Plugin::loadProgram)) == (void*)&Plugin::loadProgram)
{
d_stderr2("DPF warning: Plugins with programs must implement `loadProgram`");
abort();
}
}
# endif

# if DISTRHO_PLUGIN_WANT_STATE
if (fData->stateCount != 0)
{
if ((void*)(fPlugin->*(&Plugin::initState)) == (void*)&Plugin::initState)
{
d_stderr2("DPF warning: Plugins with state must implement `initState`");
abort();
}

if ((void*)(fPlugin->*(&Plugin::setState)) == (void*)&Plugin::setState)
{
d_stderr2("DPF warning: Plugins with state must implement `setState`");
abort();
}
}
# endif

# if DISTRHO_PLUGIN_WANT_FULL_STATE
if (fData->stateCount != 0)
{
if ((void*)(fPlugin->*(&Plugin::getState)) == (void*)&Plugin::getState)
{
d_stderr2("DPF warning: Plugins with full state must implement `getState`");
abort();
}
}
else
{
d_stderr2("DPF warning: Plugins with full state must have at least 1 state");
abort();
}
# endif
#endif

#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
{
uint32_t j=0;
@@ -198,11 +367,42 @@ public:
fPlugin->initAudioPort(false, i, fData->audioPorts[j]);
# endif
}
#endif
#endif // DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0

for (uint32_t i=0, count=fData->parameterCount; i < count; ++i)
fPlugin->initParameter(i, fData->parameters[i]);

{
std::set<uint32_t> portGroupIndices;

#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
portGroupIndices.insert(fData->audioPorts[i].groupId);
#endif
for (uint32_t i=0, count=fData->parameterCount; i < count; ++i)
portGroupIndices.insert(fData->parameters[i].groupId);

portGroupIndices.erase(kPortGroupNone);

if (const uint32_t portGroupSize = static_cast<uint32_t>(portGroupIndices.size()))
{
fData->portGroups = new PortGroupWithId[portGroupSize];
fData->portGroupCount = portGroupSize;

uint32_t index = 0;
for (std::set<uint32_t>::iterator it = portGroupIndices.begin(); it != portGroupIndices.end(); ++it, ++index)
{
PortGroupWithId& portGroup(fData->portGroups[index]);
portGroup.groupId = *it;

if (portGroup.groupId < portGroupSize)
fPlugin->initPortGroup(portGroup.groupId, portGroup);
else
fillInPredefinedPortGroupData(portGroup.groupId, portGroup);
}
}
}

#if DISTRHO_PLUGIN_WANT_PROGRAMS
for (uint32_t i=0, count=fData->programCount; i < count; ++i)
fPlugin->initProgramName(i, fData->programNames[i]);
@@ -210,11 +410,13 @@ public:

#if DISTRHO_PLUGIN_WANT_STATE
for (uint32_t i=0, count=fData->stateCount; i < count; ++i)
fPlugin->initState(i, fData->stateKeys[i], fData->stateDefValues[i]);
fPlugin->initState(i, fData->states[i]);
#endif

fData->callbacksPtr = callbacksPtr;
fData->callbacksPtr = callbacksPtr;
fData->writeMidiCallbackFunc = writeMidiCall;
fData->requestParameterValueChangeCallbackFunc = requestParameterValueChangeCall;
fData->updateStateValueCallbackFunc = updateStateValueCall;
}

~PluginExporter()
@@ -297,7 +499,7 @@ public:
#endif

#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
const AudioPort& getAudioPort(const bool input, const uint32_t index) const noexcept
AudioPortWithBusId& getAudioPort(const bool input, const uint32_t index) const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, sFallbackAudioPort);

@@ -316,6 +518,11 @@ public:

return fData->audioPorts[index + (input ? 0 : DISTRHO_PLUGIN_NUM_INPUTS)];
}

uint32_t getAudioPortHints(const bool input, const uint32_t index) const noexcept
{
return getAudioPort(input, index).hints;
}
#endif

uint32_t getParameterCount() const noexcept
@@ -356,6 +563,11 @@ public:
return (getParameterHints(index) & kParameterIsOutput) != 0x0;
}

bool isParameterTrigger(const uint32_t index) const noexcept
{
return (getParameterHints(index) & kParameterIsTrigger) == kParameterIsTrigger;
}

bool isParameterOutputOrTrigger(const uint32_t index) const noexcept
{
const uint32_t hints = getParameterHints(index);
@@ -375,6 +587,13 @@ public:
return fData->parameters[index].name;
}

const String& getParameterShortName(const uint32_t index) const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, sFallbackString);

return fData->parameters[index].shortName;
}

const String& getParameterSymbol(const uint32_t index) const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, sFallbackString);
@@ -389,6 +608,13 @@ public:
return fData->parameters[index].unit;
}

const String& getParameterDescription(const uint32_t index) const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, sFallbackString);

return fData->parameters[index].description;
}

const ParameterEnumerationValues& getParameterEnumValues(const uint32_t index) const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, sFallbackEnumValues);
@@ -410,6 +636,21 @@ public:
return fData->parameters[index].midiCC;
}

uint32_t getParameterGroupId(const uint32_t index) const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, kPortGroupNone);

return fData->parameters[index].groupId;
}

float getParameterDefault(const uint32_t index) const
{
DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr, 0.0f);
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, 0.0f);

return fData->parameters[index].ranges.def;
}

float getParameterValue(const uint32_t index) const
{
DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr, 0.0f);
@@ -426,6 +667,42 @@ public:
fPlugin->setParameterValue(index, value);
}

uint32_t getPortGroupCount() const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, 0);

return fData->portGroupCount;
}

const PortGroupWithId& getPortGroupById(const uint32_t groupId) const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && fData->portGroupCount != 0, sFallbackPortGroup);

for (uint32_t i=0; i < fData->portGroupCount; ++i)
{
const PortGroupWithId& portGroup(fData->portGroups[i]);

if (portGroup.groupId == groupId)
return portGroup;
}

return sFallbackPortGroup;
}

const PortGroupWithId& getPortGroupByIndex(const uint32_t index) const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->portGroupCount, sFallbackPortGroup);

return fData->portGroups[index];
}

const String& getPortGroupSymbolForId(const uint32_t groupId) const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, sFallbackString);

return getPortGroupById(groupId).symbol;
}

#if DISTRHO_PLUGIN_WANT_PROGRAMS
uint32_t getProgramCount() const noexcept
{
@@ -458,22 +735,43 @@ public:
return fData->stateCount;
}

uint32_t getStateHints(const uint32_t index) const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->stateCount, 0x0);

return fData->states[index].hints;
}

const String& getStateKey(const uint32_t index) const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->stateCount, sFallbackString);

return fData->stateKeys[index];
return fData->states[index].key;
}

const String& getStateDefaultValue(const uint32_t index) const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->stateCount, sFallbackString);

return fData->stateDefValues[index];
return fData->states[index].defaultValue;
}

const String& getStateLabel(const uint32_t index) const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->stateCount, sFallbackString);

return fData->states[index].label;
}

const String& getStateDescription(const uint32_t index) const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->stateCount, sFallbackString);

return fData->states[index].description;
}

# if DISTRHO_PLUGIN_WANT_FULL_STATE
String getState(const char* key) const
String getStateValue(const char* const key) const
{
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, sFallbackString);
DISTRHO_SAFE_ASSERT_RETURN(key != nullptr && key[0] != '\0', sFallbackString);
@@ -498,7 +796,7 @@ public:

for (uint32_t i=0; i < fData->stateCount; ++i)
{
if (fData->stateKeys[i] == key)
if (fData->states[i].key == key)
return true;
}

@@ -552,7 +850,7 @@ public:
}

#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
void run(const float* const* const inputs, float** const outputs, const uint32_t frames,
void run(const float** const inputs, float** const outputs, const uint32_t frames,
const MidiEvent* const midiEvents, const uint32_t midiEventCount)
{
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,);
@@ -569,7 +867,7 @@ public:
fData->isProcessing = false;
}
#else
void run(const float* const* const inputs, float** const outputs, const uint32_t frames)
void run(const float** const inputs, float** const outputs, const uint32_t frames)
{
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr,);
@@ -650,12 +948,15 @@ private:
// Static fallback data, see DistrhoPlugin.cpp

static const String sFallbackString;
static const AudioPort sFallbackAudioPort;
static /* */ AudioPortWithBusId sFallbackAudioPort;
static const ParameterRanges sFallbackRanges;
static const ParameterEnumerationValues sFallbackEnumValues;
static const PortGroupWithId sFallbackPortGroup;

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginExporter)
#ifndef DISTRHO_PLUGIN_TARGET_VST3 /* there is no way around this for VST3 */
DISTRHO_PREVENT_HEAP_ALLOCATION
#endif
};

// -----------------------------------------------------------------------


+ 289
- 45
source/modules/distrho/src/DistrhoUI.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -14,78 +14,280 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "DistrhoUIInternal.hpp"
#include "src/DistrhoPluginChecks.h"
#include "src/DistrhoDefines.h"

#ifdef HAVE_DGL
# include "src/WidgetPrivateData.hpp"
#if !defined(DGL_FILE_BROWSER_DISABLED) && !defined(DISTRHO_OS_MAC)
# define DISTRHO_PUGL_NAMESPACE_MACRO_HELPER(NS, SEP, FUNCTION) NS ## SEP ## FUNCTION
# define DISTRHO_PUGL_NAMESPACE_MACRO(NS, FUNCTION) DISTRHO_PUGL_NAMESPACE_MACRO_HELPER(NS, _, FUNCTION)
# define DISTRHO_FILE_BROWSER_DIALOG_EXTRA_NAMESPACE Plugin
# define x_fib_add_recent DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_add_recent)
# define x_fib_cfg_buttons DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_cfg_buttons)
# define x_fib_cfg_filter_callback DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_cfg_filter_callback)
# define x_fib_close DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_close)
# define x_fib_configure DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_configure)
# define x_fib_filename DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_filename)
# define x_fib_free_recent DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_free_recent)
# define x_fib_handle_events DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_handle_events)
# define x_fib_load_recent DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_load_recent)
# define x_fib_recent_at DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_recent_at)
# define x_fib_recent_count DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_recent_count)
# define x_fib_recent_file DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_recent_file)
# define x_fib_save_recent DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_save_recent)
# define x_fib_show DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_show)
# define x_fib_status DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_status)
# include "../extra/FileBrowserDialog.cpp"
#endif

#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
# if defined(DISTRHO_OS_WINDOWS)
# include <winsock2.h>
# include <windows.h>
# elif defined(HAVE_X11)
# include <X11/Xresource.h>
# endif
#else
# include "src/TopLevelWidgetPrivateData.hpp"
# include "src/WindowPrivateData.hpp"
#endif

#include "DistrhoUIPrivateData.hpp"

START_NAMESPACE_DISTRHO

/* ------------------------------------------------------------------------------------------------------------
* Static data, see DistrhoUIInternal.hpp */

double d_lastUiSampleRate = 0.0;
void* d_lastUiDspPtr = nullptr;
#ifdef HAVE_DGL
Window* d_lastUiWindow = nullptr;
const char* g_nextBundlePath = nullptr;
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
uintptr_t g_nextWindowId = 0;
double g_nextScaleFactor = 1.0;
#endif
uintptr_t g_nextWindowId = 0;
const char* g_nextBundlePath = nullptr;

#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
/* ------------------------------------------------------------------------------------------------------------
* UI */
* get global scale factor */

#ifdef HAVE_DGL
UI::UI(uint width, uint height)
: UIWidget(*d_lastUiWindow),
pData(new PrivateData())
#ifdef DISTRHO_OS_MAC
double getDesktopScaleFactor(uintptr_t parentWindowHandle);
#else
static double getDesktopScaleFactor(const uintptr_t parentWindowHandle)
{
((UIWidget*)this)->pData->needsFullViewport = false;
// allow custom scale for testing
if (const char* const scale = getenv("DPF_SCALE_FACTOR"))
return std::max(1.0, std::atof(scale));

#if defined(DISTRHO_OS_WINDOWS)
if (const HMODULE Shcore = LoadLibraryA("Shcore.dll"))
{
typedef HRESULT(WINAPI* PFN_GetProcessDpiAwareness)(HANDLE, DWORD*);
typedef HRESULT(WINAPI* PFN_GetScaleFactorForMonitor)(HMONITOR, DWORD*);

# if defined(__GNUC__) && (__GNUC__ >= 9)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wcast-function-type"
# endif
const PFN_GetProcessDpiAwareness GetProcessDpiAwareness
= (PFN_GetProcessDpiAwareness)GetProcAddress(Shcore, "GetProcessDpiAwareness");
const PFN_GetScaleFactorForMonitor GetScaleFactorForMonitor
= (PFN_GetScaleFactorForMonitor)GetProcAddress(Shcore, "GetScaleFactorForMonitor");
# if defined(__GNUC__) && (__GNUC__ >= 9)
# pragma GCC diagnostic pop
# endif

DWORD dpiAware = 0;
if (GetProcessDpiAwareness && GetScaleFactorForMonitor
&& GetProcessDpiAwareness(NULL, &dpiAware) == 0 && dpiAware != 0)
{
const HMONITOR hMon = parentWindowHandle != 0
? MonitorFromWindow((HWND)parentWindowHandle, MONITOR_DEFAULTTOPRIMARY)
: MonitorFromPoint(POINT{0,0}, MONITOR_DEFAULTTOPRIMARY);

DWORD scaleFactor = 0;
if (GetScaleFactorForMonitor(hMon, &scaleFactor) == 0 && scaleFactor != 0)
{
FreeLibrary(Shcore);
return static_cast<double>(scaleFactor) / 100.0;
}
}

FreeLibrary(Shcore);
}
#elif defined(HAVE_X11)
::Display* const display = XOpenDisplay(nullptr);
DISTRHO_SAFE_ASSERT_RETURN(display != nullptr, 1.0);

XrmInitialize();

if (char* const rms = XResourceManagerString(display))
{
if (const XrmDatabase sdb = XrmGetStringDatabase(rms))
{
char* type = nullptr;
XrmValue ret;

if (XrmGetResource(sdb, "Xft.dpi", "String", &type, &ret)
&& ret.addr != nullptr
&& type != nullptr
&& std::strncmp("String", type, 6) == 0)
{
if (const double dpi = std::atof(ret.addr))
{
XCloseDisplay(display);
return dpi / 96;
}
}
}
}

if (width > 0 && height > 0)
setSize(width, height);
XCloseDisplay(display);
#endif

return 1.0;

// might be unused
(void)parentWindowHandle;
}
#endif // !DISTRHO_OS_MAC

#endif

/* ------------------------------------------------------------------------------------------------------------
* UI::PrivateData special handling */

UI::PrivateData* UI::PrivateData::s_nextPrivateData = nullptr;

#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
ExternalWindow::PrivateData
#else
UI::UI(uint width, uint height)
: UIWidget(width, height),
pData(new PrivateData()) {}
PluginWindow&
#endif
UI::PrivateData::createNextWindow(UI* const ui, const uint width, const uint height)
{
UI::PrivateData* const pData = s_nextPrivateData;
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
pData->window = new PluginWindow(ui, pData->app);
ExternalWindow::PrivateData ewData;
ewData.parentWindowHandle = pData->winId;
ewData.width = width;
ewData.height = height;
ewData.scaleFactor = pData->scaleFactor != 0.0 ? pData->scaleFactor : getDesktopScaleFactor(pData->winId);
ewData.title = DISTRHO_PLUGIN_NAME;
ewData.isStandalone = DISTRHO_UI_IS_STANDALONE;
return ewData;
#else
pData->window = new PluginWindow(ui, pData->app, pData->winId, width, height, pData->scaleFactor);

// If there are no callbacks, this is most likely a temporary window, so ignore idle callbacks
if (pData->callbacksPtr == nullptr)
pData->window->setIgnoreIdleCallbacks();

return pData->window.getObject();
#endif
}

/* ------------------------------------------------------------------------------------------------------------
* UI */

UI::UI(const uint width, const uint height, const bool automaticallyScaleAndSetAsMinimumSize)
: UIWidget(UI::PrivateData::createNextWindow(this, width, height)),
uiData(UI::PrivateData::s_nextPrivateData)
{
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
if (width != 0 && height != 0)
{
Widget::setSize(width, height);

if (automaticallyScaleAndSetAsMinimumSize)
setGeometryConstraints(width, height, true, true, true);
}
#else
// unused
(void)automaticallyScaleAndSetAsMinimumSize;
#endif
}

UI::~UI()
{
delete pData;
}

/* ------------------------------------------------------------------------------------------------------------
* Host state */

bool UI::isResizable() const noexcept
{
#if DISTRHO_UI_USER_RESIZABLE
# if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
return true;
# else
return uiData->window->isResizable();
# endif
#else
return false;
#endif
}

uint UI::getBackgroundColor() const noexcept
{
return uiData->bgColor;
}

uint UI::getForegroundColor() const noexcept
{
return uiData->fgColor;
}

double UI::getSampleRate() const noexcept
{
return pData->sampleRate;
return uiData->sampleRate;
}

const char* UI::getBundlePath() const noexcept
{
return uiData->bundlePath;
}

void UI::editParameter(uint32_t index, bool started)
{
pData->editParamCallback(index + pData->parameterOffset, started);
uiData->editParamCallback(index + uiData->parameterOffset, started);
}

void UI::setParameterValue(uint32_t index, float value)
{
pData->setParamCallback(index + pData->parameterOffset, value);
uiData->setParamCallback(index + uiData->parameterOffset, value);
}

#if DISTRHO_PLUGIN_WANT_STATE
void UI::setState(const char* key, const char* value)
{
pData->setStateCallback(key, value);
uiData->setStateCallback(key, value);
}
#endif

#if DISTRHO_PLUGIN_WANT_STATE
bool UI::requestStateFile(const char* key)
{
return uiData->fileRequestCallback(key);
}
#endif

#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
void UI::sendNote(uint8_t channel, uint8_t note, uint8_t velocity)
{
pData->sendNoteCallback(channel, note, velocity);
uiData->sendNoteCallback(channel, note, velocity);
}
#endif

#ifndef DGL_FILE_BROWSER_DISABLED
bool UI::openFileBrowser(const FileBrowserOptions& options)
{
# if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
// TODO
return false;
(void)options;
# else
return getWindow().openFileBrowser(options);
# endif
}
#endif

@@ -95,60 +297,102 @@ void UI::sendNote(uint8_t channel, uint8_t note, uint8_t velocity)

void* UI::getPluginInstancePointer() const noexcept
{
return pData->dspPtr;
return uiData->dspPtr;
}
#endif

#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
/* ------------------------------------------------------------------------------------------------------------
* External UI helpers */
* External UI helpers (static calls) */

const char* UI::getNextBundlePath() noexcept
{
return g_nextBundlePath;
}

double UI::getNextScaleFactor() noexcept
{
return g_nextScaleFactor;
}

# if DISTRHO_PLUGIN_HAS_EMBED_UI
uintptr_t UI::getNextWindowId() noexcept
{
return g_nextWindowId;
}
# endif
#endif
#endif // DISTRHO_PLUGIN_HAS_EXTERNAL_UI

/* ------------------------------------------------------------------------------------------------------------
* DSP/Plugin Callbacks (optional) */

void UI::sampleRateChanged(double) {}
void UI::sampleRateChanged(double)
{
}

#ifdef HAVE_DGL
/* ------------------------------------------------------------------------------------------------------------
* UI Callbacks (optional) */

#ifndef DGL_FILE_BROWSER_DISABLED
void UI::uiFileBrowserSelected(const char*)
void UI::uiScaleFactorChanged(double)
{
}

#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
void UI::uiFocus(bool, DGL_NAMESPACE::CrossingMode)
{
}
#endif

void UI::uiReshape(uint width, uint height)
void UI::uiReshape(uint, uint)
{
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, static_cast<GLdouble>(width), static_cast<GLdouble>(height), 0.0, 0.0, 1.0);
glViewport(0, 0, static_cast<GLsizei>(width), static_cast<GLsizei>(height));
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// NOTE this must be the same as Window::onReshape
pData->fallbackOnResize();
}
#endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI

#ifndef DGL_FILE_BROWSER_DISABLED
void UI::uiFileBrowserSelected(const char*)
{
}
#endif

/* ------------------------------------------------------------------------------------------------------------
* UI Resize Handling, internal */

#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
void UI::sizeChanged(const uint width, const uint height)
{
UIWidget::sizeChanged(width, height);

uiData->setSizeCallback(width, height);
}
#else
void UI::onResize(const ResizeEvent& ev)
{
pData->setSizeCallback(ev.size.getWidth(), ev.size.getHeight());
UIWidget::onResize(ev);

#ifndef DISTRHO_PLUGIN_TARGET_VST3
if (uiData->initializing)
return;

const uint width = ev.size.getWidth();
const uint height = ev.size.getHeight();
uiData->setSizeCallback(width, height);
#endif
}

// NOTE: only used for VST3
void UI::requestSizeChange(const uint width, const uint height)
{
# ifdef DISTRHO_PLUGIN_TARGET_VST3
if (uiData->initializing)
uiData->window->setSizeForVST3(width, height);
else
uiData->setSizeCallback(width, height);
# else
// unused
(void)width;
(void)height;
# endif
}
#endif



+ 254
- 313
source/modules/distrho/src/DistrhoUIInternal.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -17,462 +17,403 @@
#ifndef DISTRHO_UI_INTERNAL_HPP_INCLUDED
#define DISTRHO_UI_INTERNAL_HPP_INCLUDED

#include "../DistrhoUI.hpp"

#ifdef HAVE_DGL
# include "../../dgl/Application.hpp"
# include "../../dgl/Window.hpp"
using DGL_NAMESPACE::Application;
using DGL_NAMESPACE::IdleCallback;
using DGL_NAMESPACE::Window;
#endif
#include "DistrhoUIPrivateData.hpp"

START_NAMESPACE_DISTRHO

// -----------------------------------------------------------------------
// Static data, see DistrhoUI.cpp

extern double d_lastUiSampleRate;
extern void* d_lastUiDspPtr;
#ifdef HAVE_DGL
extern Window* d_lastUiWindow;
#endif
extern uintptr_t g_nextWindowId;
extern const char* g_nextBundlePath;
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
extern uintptr_t g_nextWindowId;
extern double g_nextScaleFactor;
#endif

// -----------------------------------------------------------------------
// UI callbacks
// UI exporter class

typedef void (*editParamFunc) (void* ptr, uint32_t rindex, bool started);
typedef void (*setParamFunc) (void* ptr, uint32_t rindex, float value);
typedef void (*setStateFunc) (void* ptr, const char* key, const char* value);
typedef void (*sendNoteFunc) (void* ptr, uint8_t channel, uint8_t note, uint8_t velo);
typedef void (*setSizeFunc) (void* ptr, uint width, uint height);
class UIExporter
{
// -------------------------------------------------------------------
// UI Widget and its private data

// -----------------------------------------------------------------------
// UI private data

struct UI::PrivateData {
// DSP
double sampleRate;
uint32_t parameterOffset;
#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
void* dspPtr;
#endif
UI* ui;
UI::PrivateData* uiData;

// Callbacks
void* callbacksPtr;
editParamFunc editParamCallbackFunc;
setParamFunc setParamCallbackFunc;
setStateFunc setStateCallbackFunc;
sendNoteFunc sendNoteCallbackFunc;
setSizeFunc setSizeCallbackFunc;

PrivateData() noexcept
: sampleRate(d_lastUiSampleRate),
parameterOffset(0),
#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
dspPtr(d_lastUiDspPtr),
#endif
callbacksPtr(nullptr),
editParamCallbackFunc(nullptr),
setParamCallbackFunc(nullptr),
setStateCallbackFunc(nullptr),
sendNoteCallbackFunc(nullptr),
setSizeCallbackFunc(nullptr)
{
DISTRHO_SAFE_ASSERT(d_isNotZero(sampleRate));

#if defined(DISTRHO_PLUGIN_TARGET_DSSI) || defined(DISTRHO_PLUGIN_TARGET_LV2)
parameterOffset += DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS;
# if DISTRHO_PLUGIN_WANT_LATENCY
parameterOffset += 1;
# endif
#endif
// -------------------------------------------------------------------

#ifdef DISTRHO_PLUGIN_TARGET_LV2
# if (DISTRHO_PLUGIN_IS_SYNTH || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE)
parameterOffset += 1;
# if DISTRHO_PLUGIN_WANT_STATE
parameterOffset += 1;
# endif
# endif
public:
UIExporter(void* const callbacksPtr,
const uintptr_t winId,
const double sampleRate,
const editParamFunc editParamCall,
const setParamFunc setParamCall,
const setStateFunc setStateCall,
const sendNoteFunc sendNoteCall,
const setSizeFunc setSizeCall,
const fileRequestFunc fileRequestCall,
const char* const bundlePath = nullptr,
void* const dspPtr = nullptr,
const double scaleFactor = 0.0,
const uint32_t bgColor = 0,
const uint32_t fgColor = 0xffffffff)
: ui(nullptr),
uiData(new UI::PrivateData())
{
uiData->sampleRate = sampleRate;
uiData->bundlePath = bundlePath != nullptr ? strdup(bundlePath) : nullptr;
uiData->dspPtr = dspPtr;

uiData->bgColor = bgColor;
uiData->fgColor = fgColor;
uiData->scaleFactor = scaleFactor;
uiData->winId = winId;

uiData->callbacksPtr = callbacksPtr;
uiData->editParamCallbackFunc = editParamCall;
uiData->setParamCallbackFunc = setParamCall;
uiData->setStateCallbackFunc = setStateCall;
uiData->sendNoteCallbackFunc = sendNoteCall;
uiData->setSizeCallbackFunc = setSizeCall;
uiData->fileRequestCallbackFunc = fileRequestCall;

g_nextBundlePath = bundlePath;
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
g_nextWindowId = winId;
g_nextScaleFactor = scaleFactor;
#endif
}
UI::PrivateData::s_nextPrivateData = uiData;

void editParamCallback(const uint32_t rindex, const bool started)
{
if (editParamCallbackFunc != nullptr)
editParamCallbackFunc(callbacksPtr, rindex, started);
}
UI* const uiPtr = createUI();

void setParamCallback(const uint32_t rindex, const float value)
{
if (setParamCallbackFunc != nullptr)
setParamCallbackFunc(callbacksPtr, rindex, value);
}
g_nextBundlePath = nullptr;
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
g_nextWindowId = 0;
g_nextScaleFactor = 0.0;
#else
// enter context called in the PluginWindow constructor, see DistrhoUIPrivateData.hpp
uiData->window->leaveContext();
#endif
UI::PrivateData::s_nextPrivateData = nullptr;

void setStateCallback(const char* const key, const char* const value)
{
if (setStateCallbackFunc != nullptr)
setStateCallbackFunc(callbacksPtr, key, value);
}
DISTRHO_SAFE_ASSERT_RETURN(uiPtr != nullptr,);
ui = uiPtr;
uiData->initializing = false;

void sendNoteCallback(const uint8_t channel, const uint8_t note, const uint8_t velocity)
{
if (sendNoteCallbackFunc != nullptr)
sendNoteCallbackFunc(callbacksPtr, channel, note, velocity);
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
// unused
(void)bundlePath;
#endif
}

void setSizeCallback(const uint width, const uint height)
~UIExporter()
{
if (setSizeCallbackFunc != nullptr)
setSizeCallbackFunc(callbacksPtr, width, height);
quit();
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
uiData->window->enterContextForDeletion();
#endif
delete ui;
delete uiData;
}
};

// -----------------------------------------------------------------------
// Plugin Window, needed to take care of resize properly
// -------------------------------------------------------------------

#ifdef HAVE_DGL
static inline
UI* createUiWrapper(void* const dspPtr, Window* const window)
{
d_lastUiDspPtr = dspPtr;
d_lastUiWindow = window;
UI* const ret = createUI();
d_lastUiDspPtr = nullptr;
d_lastUiWindow = nullptr;
return ret;
}

class UIExporterWindow : public Window
{
public:
UIExporterWindow(Application& app, const intptr_t winId, void* const dspPtr)
: Window(app, winId),
fUI(createUiWrapper(dspPtr, this)),
fIsReady(false)
uint getWidth() const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);

// set window size
setResizable(false);
setSize(fUI->getWidth(), fUI->getHeight());
return uiData->window->getWidth();
}

~UIExporterWindow()
uint getHeight() const noexcept
{
delete fUI;
return uiData->window->getHeight();
}

UI* getUI() const noexcept
double getScaleFactor() const noexcept
{
return fUI;
return uiData->window->getScaleFactor();
}

bool isReady() const noexcept
bool getGeometryConstraints(uint& minimumWidth, uint& minimumHeight, bool& keepAspectRatio) const noexcept
{
return fIsReady;
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
uiData->window->getGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio);
#else
const DGL_NAMESPACE::Size<uint> size(uiData->window->getGeometryConstraints(keepAspectRatio));
minimumWidth = size.getWidth();
minimumHeight = size.getHeight();
#endif
return true;
}

protected:
// custom window reshape
void onReshape(uint width, uint height) override
bool isResizable() const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);

fUI->uiReshape(width, height);
fIsReady = true;
return uiData->window->isResizable();
}

#ifndef DGL_FILE_BROWSER_DISABLED
// custom file-browser selected
void fileBrowserSelected(const char* filename) override
bool isVisible() const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);

fUI->uiFileBrowserSelected(filename);
return uiData->window->isVisible();
}
#endif

private:
UI* const fUI;
bool fIsReady;
};
#else
static inline
UI* createUiWrapper(void* const dspPtr, const uintptr_t winId, const char* const bundlePath)
{
d_lastUiDspPtr = dspPtr;
g_nextWindowId = winId;
g_nextBundlePath = bundlePath;
UI* const ret = createUI();
d_lastUiDspPtr = nullptr;
g_nextWindowId = 0;
g_nextBundlePath = nullptr;
return ret;
}
#endif

// -----------------------------------------------------------------------
// UI exporter class

class UIExporter
{
public:
UIExporter(void* const callbacksPtr,
const intptr_t winId,
const editParamFunc editParamCall,
const setParamFunc setParamCall,
const setStateFunc setStateCall,
const sendNoteFunc sendNoteCall,
const setSizeFunc setSizeCall,
void* const dspPtr = nullptr,
const char* const bundlePath = nullptr)
#ifdef HAVE_DGL
: glApp(),
glWindow(glApp, winId, dspPtr),
fChangingSize(false),
fUI(glWindow.getUI()),
#else
: fUI(createUiWrapper(dspPtr, winId, bundlePath)),
#endif
fData((fUI != nullptr) ? fUI->pData : nullptr)
uintptr_t getNativeWindowHandle() const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,);

fData->callbacksPtr = callbacksPtr;
fData->editParamCallbackFunc = editParamCall;
fData->setParamCallbackFunc = setParamCall;
fData->setStateCallbackFunc = setStateCall;
fData->sendNoteCallbackFunc = sendNoteCall;
fData->setSizeCallbackFunc = setSizeCall;

#ifdef HAVE_DGL
// unused
return; (void)bundlePath;
#endif
return uiData->window->getNativeWindowHandle();
}

// -------------------------------------------------------------------

uint getWidth() const noexcept
uint getBackgroundColor() const noexcept
{
#ifdef HAVE_DGL
return glWindow.getWidth();
#else
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, 1);
return fUI->getWidth();
#endif
}
DISTRHO_SAFE_ASSERT_RETURN(uiData != nullptr, 0);

uint getHeight() const noexcept
{
#ifdef HAVE_DGL
return glWindow.getHeight();
#else
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, 1);
return fUI->getHeight();
#endif
return uiData->bgColor;
}

bool isVisible() const noexcept
uint getForegroundColor() const noexcept
{
#ifdef HAVE_DGL
return glWindow.isVisible();
#else
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, false);
return fUI->isRunning();
#endif
}
DISTRHO_SAFE_ASSERT_RETURN(uiData != nullptr, 0xffffffff);

// -------------------------------------------------------------------

intptr_t getWindowId() const noexcept
{
#ifdef HAVE_DGL
return glWindow.getWindowId();
#else
return 0;
#endif
return uiData->fgColor;
}

// -------------------------------------------------------------------

uint32_t getParameterOffset() const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, 0);
DISTRHO_SAFE_ASSERT_RETURN(uiData != nullptr, 0);

return fData->parameterOffset;
return uiData->parameterOffset;
}

// -------------------------------------------------------------------

void parameterChanged(const uint32_t index, const float value)
{
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);

fUI->parameterChanged(index, value);
ui->parameterChanged(index, value);
}

#if DISTRHO_PLUGIN_WANT_PROGRAMS
void programLoaded(const uint32_t index)
{
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);

fUI->programLoaded(index);
ui->programLoaded(index);
}
#endif

#if DISTRHO_PLUGIN_WANT_STATE
void stateChanged(const char* const key, const char* const value)
{
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(key != nullptr && key[0] != '\0',);
DISTRHO_SAFE_ASSERT_RETURN(value != nullptr,);

fUI->stateChanged(key, value);
ui->stateChanged(key, value);
}
#endif

// -------------------------------------------------------------------

#ifdef HAVE_DGL
void exec(IdleCallback* const cb)
#if DISTRHO_UI_IS_STANDALONE
void exec(DGL_NAMESPACE::IdleCallback* const cb)
{
DISTRHO_SAFE_ASSERT_RETURN(cb != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);

glWindow.addIdleCallback(cb);
glWindow.setVisible(true);
glApp.exec();
uiData->window->show();
uiData->window->focus();
uiData->app.addIdleCallback(cb);
uiData->app.exec();
}

void exec_idle()
{
if (glWindow.isReady())
fUI->uiIdle();
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr, );

ui->uiIdle();
}
#endif

bool idle()
void showAndFocus()
{
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, false);
uiData->window->show();
uiData->window->focus();
}
#endif

#ifdef HAVE_DGL
glApp.idle();
bool plugin_idle()
{
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr, false);

if (glWindow.isReady())
fUI->uiIdle();
uiData->app.idle();
ui->uiIdle();
return ! uiData->app.isQuitting();
}

return ! glApp.isQuiting();
#else
return fUI->isRunning();
#endif
void focus()
{
uiData->window->focus();
}

void quit()
{
#ifdef HAVE_DGL
glWindow.close();
glApp.quit();
#else
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
fUI->terminateAndWaitForProcess();
#endif
uiData->window->close();
uiData->app.quit();
}

// -------------------------------------------------------------------

void setWindowTitle(const char* const uiTitle)
#if defined(DISTRHO_PLUGIN_TARGET_VST3) && (defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS))
void idleForVST3()
{
#ifdef HAVE_DGL
glWindow.setTitle(uiTitle);
#else
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
fUI->setTitle(uiTitle);
#endif
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);

uiData->app.triggerIdleCallbacks();
ui->uiIdle();
}

#ifdef HAVE_DGL
void setWindowSize(const uint width, const uint height, const bool updateUI = false)
# if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
void addIdleCallbackForVST3(IdleCallback* const cb, const uint timerFrequencyInMs)
{
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(! fChangingSize,);
uiData->window->addIdleCallback(cb, timerFrequencyInMs);
}

fChangingSize = true;
void removeIdleCallbackForVST3(IdleCallback* const cb)
{
uiData->window->removeIdleCallback(cb);
}
# endif
#endif

if (updateUI)
fUI->setSize(width, height);
// -------------------------------------------------------------------

glWindow.setSize(width, height);
void setWindowOffset(const int x, const int y)
{
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
// TODO
(void)x; (void)y;
#else
uiData->window->setOffset(x, y);
#endif
}

#ifdef DISTRHO_PLUGIN_TARGET_VST3
void setWindowSizeForVST3(const uint width, const uint height)
{
# if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
ui->setSize(width, height);
# else
uiData->window->setSizeForVST3(width, height);
# endif
}
#endif

fChangingSize = false;
void setWindowTitle(const char* const uiTitle)
{
uiData->window->setTitle(uiTitle);
}

void setWindowTransientWinId(const uintptr_t winId)
{
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
ui->setTransientWindowId(winId);
#elif 0 /* TODO */
glWindow.setTransientWinId(winId);
#else
(void)winId;
#endif
}

bool setWindowVisible(const bool yesNo)
{
glWindow.setVisible(yesNo);
uiData->window->setVisible(yesNo);

return ! glApp.isQuiting();
return ! uiData->app.isQuitting();
}

bool handlePluginKeyboard(const bool press, const uint key)
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
bool handlePluginKeyboardVST2(const bool press, const uint key, const uint16_t mods)
{
return glWindow.handlePluginKeyboard(press, key);
DGL_NAMESPACE::Widget::KeyboardEvent ev;
ev.mod = mods;
ev.press = press;
ev.key = key;

const bool ret = ui->onKeyboard(ev);

if (! press)
return ret;

DGL_NAMESPACE::Widget::CharacterInputEvent cev;
cev.mod = mods;
cev.character = key;

// if shift modifier is on, convert a-z -> A-Z for character input
if (key >= 'a' && key <= 'z' && (mods & DGL_NAMESPACE::kModifierShift) != 0)
cev.character -= 'a' - 'A';

ui->onCharacterInput(cev);
return ret;
}

bool handlePluginSpecial(const bool press, const Key key)
bool handlePluginKeyboardVST3(const bool press, const uint keychar, const uint keycode, const uint16_t mods)
{
return glWindow.handlePluginKeyboard(press, key);
DGL_NAMESPACE::Widget::KeyboardEvent ev;
ev.mod = mods;
ev.press = press;
ev.key = keychar;
ev.keycode = keycode;

const bool ret = ui->onKeyboard(ev);

if (! press)
return ret;

DGL_NAMESPACE::Widget::CharacterInputEvent cev;
cev.mod = mods;
cev.keycode = keycode;
cev.character = keychar;

// if shift modifier is on, convert a-z -> A-Z for character input
if (keychar >= 'a' && keychar <= 'z' && (mods & DGL_NAMESPACE::kModifierShift) != 0)
cev.character -= 'a' - 'A';

ui->onCharacterInput(cev);
return ret;
}
#else
void setWindowSize(const uint, const uint, const bool) {}
void setWindowTransientWinId(const uintptr_t) {}
bool setWindowVisible(const bool) { return true; }
#endif

// -------------------------------------------------------------------

void setSampleRate(const double sampleRate, const bool doCallback = false)
void notifyScaleFactorChanged(const double scaleFactor)
{
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
DISTRHO_SAFE_ASSERT(sampleRate > 0.0);
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);

if (d_isEqual(fData->sampleRate, sampleRate))
return;
ui->uiScaleFactorChanged(scaleFactor);
}

fData->sampleRate = sampleRate;
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
void notifyFocusChanged(const bool focus)
{
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);

if (doCallback)
fUI->sampleRateChanged(sampleRate);
ui->uiFocus(focus, DGL_NAMESPACE::kCrossingNormal);
}
#endif

private:
#ifdef HAVE_DGL
// -------------------------------------------------------------------
// DGL Application and Window for this widget

Application glApp;
UIExporterWindow glWindow;
void setSampleRate(const double sampleRate, const bool doCallback = false)
{
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(uiData != nullptr,);
DISTRHO_SAFE_ASSERT(sampleRate > 0.0);

// prevent recursion
bool fChangingSize;
#endif
if (d_isEqual(uiData->sampleRate, sampleRate))
return;

// -------------------------------------------------------------------
// Widget and DistrhoUI data
uiData->sampleRate = sampleRate;

UI* const fUI;
UI::PrivateData* const fData;
if (doCallback)
ui->sampleRateChanged(sampleRate);
}

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(UIExporter)
};


+ 500
- 0
source/modules/distrho/src/DistrhoUIPrivateData.hpp View File

@@ -0,0 +1,500 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef DISTRHO_UI_PRIVATE_DATA_HPP_INCLUDED
#define DISTRHO_UI_PRIVATE_DATA_HPP_INCLUDED

#include "../DistrhoUI.hpp"

#ifdef DISTRHO_PLUGIN_TARGET_VST3
# include "DistrhoPluginVST3.hpp"
#endif

#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
# include "../extra/Sleep.hpp"
#else
# include "../../dgl/src/ApplicationPrivateData.hpp"
# include "../../dgl/src/WindowPrivateData.hpp"
# include "../../dgl/src/pugl.hpp"
#endif

#if defined(DISTRHO_PLUGIN_TARGET_JACK) || defined(DISTRHO_PLUGIN_TARGET_DSSI)
# define DISTRHO_UI_IS_STANDALONE 1
#else
# define DISTRHO_UI_IS_STANDALONE 0
#endif

#ifdef DISTRHO_PLUGIN_TARGET_VST3
# define DISTRHO_UI_IS_VST3 1
#else
# define DISTRHO_UI_IS_VST3 0
#endif

#ifdef DISTRHO_PLUGIN_TARGET_VST2
# undef DISTRHO_UI_USER_RESIZABLE
# define DISTRHO_UI_USER_RESIZABLE 0
#endif

// -----------------------------------------------------------------------

#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
START_NAMESPACE_DISTRHO
#else
START_NAMESPACE_DGL
#endif

// -----------------------------------------------------------------------
// Plugin Application, will set class name based on plugin details

#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
struct PluginApplication
{
DGL_NAMESPACE::IdleCallback* idleCallback;
UI* ui;

explicit PluginApplication()
: idleCallback(nullptr),
ui(nullptr) {}

void addIdleCallback(DGL_NAMESPACE::IdleCallback* const cb)
{
DISTRHO_SAFE_ASSERT_RETURN(cb != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(idleCallback == nullptr,);

idleCallback = cb;
}

bool isQuitting() const noexcept
{
return ui->isQuitting();
}

bool isStandalone() const noexcept
{
return DISTRHO_UI_IS_STANDALONE;
}

void exec()
{
while (ui->isRunning())
{
d_msleep(30);
idleCallback->idleCallback();
}

if (! ui->isQuitting())
ui->close();
}

// these are not needed
void idle() {}
void quit() {}
void triggerIdleCallbacks() {}

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginApplication)
};
#else
class PluginApplication : public Application
{
public:
explicit PluginApplication()
: Application(DISTRHO_UI_IS_STANDALONE)
{
const char* const className = (
#ifdef DISTRHO_PLUGIN_BRAND
DISTRHO_PLUGIN_BRAND
#else
DISTRHO_MACRO_AS_STRING(DISTRHO_NAMESPACE)
#endif
"-" DISTRHO_PLUGIN_NAME
);
setClassName(className);
}

void triggerIdleCallbacks()
{
pData->triggerIdleCallbacks();
}

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginApplication)
};
#endif

// -----------------------------------------------------------------------
// Plugin Window, will pass some Window events to UI

#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
class PluginWindow
{
UI* const ui;

public:
explicit PluginWindow(UI* const uiPtr, PluginApplication& app)
: ui(uiPtr)
{
app.ui = ui;
}

// fetch cached data
uint getWidth() const noexcept { return ui->pData.width; }
uint getHeight() const noexcept { return ui->pData.height; }
double getScaleFactor() const noexcept { return ui->pData.scaleFactor; }

// direct mappings
void close() { ui->close(); }
void focus() { ui->focus(); }
void show() { ui->show(); }
bool isResizable() const noexcept { return ui->isResizable(); }
bool isVisible() const noexcept { return ui->isVisible(); }
void setTitle(const char* const title) { ui->setTitle(title); }
void setVisible(const bool visible) { ui->setVisible(visible); }
uintptr_t getNativeWindowHandle() const noexcept { return ui->getNativeWindowHandle(); }
void getGeometryConstraints(uint& minimumWidth, uint& minimumHeight, bool& keepAspectRatio) const noexcept
{
minimumWidth = ui->pData.minWidth;
minimumHeight = ui->pData.minHeight;
keepAspectRatio = ui->pData.keepAspectRatio;
}

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginWindow)
};
#else // DISTRHO_PLUGIN_HAS_EXTERNAL_UI
class PluginWindow : public Window
{
DISTRHO_NAMESPACE::UI* const ui;
bool initializing;
bool receivedReshapeDuringInit;

public:
explicit PluginWindow(DISTRHO_NAMESPACE::UI* const uiPtr,
PluginApplication& app,
const uintptr_t parentWindowHandle,
const uint width,
const uint height,
const double scaleFactor)
: Window(app, parentWindowHandle, width, height, scaleFactor,
DISTRHO_UI_USER_RESIZABLE, DISTRHO_UI_IS_VST3, false),
ui(uiPtr),
initializing(true),
receivedReshapeDuringInit(false)
{
if (pData->view == nullptr)
return;

// this is called just before creating UI, ensuring proper context to it
if (pData->initPost())
puglBackendEnter(pData->view);
}

~PluginWindow()
{
if (pData->view != nullptr)
puglBackendLeave(pData->view);
}

// called after creating UI, restoring proper context
void leaveContext()
{
if (pData->view == nullptr)
return;

if (receivedReshapeDuringInit)
ui->uiReshape(getWidth(), getHeight());

initializing = false;
puglBackendLeave(pData->view);
}

// used for temporary windows (VST2/3 get size without active/visible view)
void setIgnoreIdleCallbacks(const bool ignore = true)
{
pData->ignoreIdleCallbacks = ignore;
}

// called right before deleting UI, ensuring correct context
void enterContextForDeletion()
{
if (pData->view != nullptr)
puglBackendEnter(pData->view);
}

#ifdef DISTRHO_PLUGIN_TARGET_VST3
void setSizeForVST3(const uint width, const uint height)
{
puglSetWindowSize(pData->view, width, height);
}
#endif

protected:
void onFocus(const bool focus, const DGL_NAMESPACE::CrossingMode mode) override
{
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);

if (initializing)
return;

ui->uiFocus(focus, mode);
}

void onReshape(const uint width, const uint height) override
{
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);

if (initializing)
{
receivedReshapeDuringInit = true;
return;
}

ui->uiReshape(width, height);
}

void onScaleFactorChanged(const double scaleFactor) override
{
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);

if (initializing)
return;

ui->uiScaleFactorChanged(scaleFactor);
}

# ifndef DGL_FILE_BROWSER_DISABLED
void onFileSelected(const char* filename) override;
# endif

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginWindow)
};
#endif // DISTRHO_PLUGIN_HAS_EXTERNAL_UI

#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
END_NAMESPACE_DISTRHO
#else
END_NAMESPACE_DGL
#endif

// -----------------------------------------------------------------------

START_NAMESPACE_DISTRHO

#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
using DGL_NAMESPACE::PluginApplication;
using DGL_NAMESPACE::PluginWindow;
#endif

// -----------------------------------------------------------------------
// UI callbacks

typedef void (*editParamFunc) (void* ptr, uint32_t rindex, bool started);
typedef void (*setParamFunc) (void* ptr, uint32_t rindex, float value);
typedef void (*setStateFunc) (void* ptr, const char* key, const char* value);
typedef void (*sendNoteFunc) (void* ptr, uint8_t channel, uint8_t note, uint8_t velo);
typedef void (*setSizeFunc) (void* ptr, uint width, uint height);
typedef bool (*fileRequestFunc) (void* ptr, const char* key);

// -----------------------------------------------------------------------
// UI private data

struct UI::PrivateData {
// DGL
PluginApplication app;
ScopedPointer<PluginWindow> window;

// DSP
double sampleRate;
uint32_t parameterOffset;
void* dspPtr;

// UI
uint bgColor;
uint fgColor;
double scaleFactor;
uintptr_t winId;
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !defined(DGL_FILE_BROWSER_DISABLED)
char* uiStateFileKeyRequest;
#endif
char* bundlePath;

// Ignore initial resize events while initializing
bool initializing;

// Callbacks
void* callbacksPtr;
editParamFunc editParamCallbackFunc;
setParamFunc setParamCallbackFunc;
setStateFunc setStateCallbackFunc;
sendNoteFunc sendNoteCallbackFunc;
setSizeFunc setSizeCallbackFunc;
fileRequestFunc fileRequestCallbackFunc;

PrivateData() noexcept
: app(),
window(nullptr),
sampleRate(0),
parameterOffset(0),
dspPtr(nullptr),
bgColor(0),
fgColor(0xffffffff),
scaleFactor(1.0),
winId(0),
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !defined(DGL_FILE_BROWSER_DISABLED)
uiStateFileKeyRequest(nullptr),
#endif
bundlePath(nullptr),
initializing(true),
callbacksPtr(nullptr),
editParamCallbackFunc(nullptr),
setParamCallbackFunc(nullptr),
setStateCallbackFunc(nullptr),
sendNoteCallbackFunc(nullptr),
setSizeCallbackFunc(nullptr),
fileRequestCallbackFunc(nullptr)
{
#if defined(DISTRHO_PLUGIN_TARGET_DSSI) || defined(DISTRHO_PLUGIN_TARGET_LV2)
parameterOffset += DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS;
# if DISTRHO_PLUGIN_WANT_LATENCY
parameterOffset += 1;
# endif
#endif

#ifdef DISTRHO_PLUGIN_TARGET_LV2
# if (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE)
parameterOffset += 1;
# endif
# if (DISTRHO_PLUGIN_WANT_MIDI_OUTPUT || DISTRHO_PLUGIN_WANT_STATE)
parameterOffset += 1;
# endif
#endif

#ifdef DISTRHO_PLUGIN_TARGET_VST3
parameterOffset += kVst3InternalParameterCount;
#endif
}

~PrivateData() noexcept
{
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !defined(DGL_FILE_BROWSER_DISABLED)
std::free(uiStateFileKeyRequest);
#endif
std::free(bundlePath);
}

void editParamCallback(const uint32_t rindex, const bool started)
{
if (editParamCallbackFunc != nullptr)
editParamCallbackFunc(callbacksPtr, rindex, started);
}

void setParamCallback(const uint32_t rindex, const float value)
{
if (setParamCallbackFunc != nullptr)
setParamCallbackFunc(callbacksPtr, rindex, value);
}

void setStateCallback(const char* const key, const char* const value)
{
if (setStateCallbackFunc != nullptr)
setStateCallbackFunc(callbacksPtr, key, value);
}

void sendNoteCallback(const uint8_t channel, const uint8_t note, const uint8_t velocity)
{
if (sendNoteCallbackFunc != nullptr)
sendNoteCallbackFunc(callbacksPtr, channel, note, velocity);
}

void setSizeCallback(const uint width, const uint height)
{
if (setSizeCallbackFunc != nullptr)
setSizeCallbackFunc(callbacksPtr, width, height);
}

// implemented below, after PluginWindow
bool fileRequestCallback(const char* const key);

static UI::PrivateData* s_nextPrivateData;
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
static ExternalWindow::PrivateData createNextWindow(UI* ui, uint width, uint height);
#else
static PluginWindow& createNextWindow(UI* ui, uint width, uint height);
#endif
};

// -----------------------------------------------------------------------
// UI private data fileRequestCallback, which requires PluginWindow definitions

inline bool UI::PrivateData::fileRequestCallback(const char* const key)
{
if (fileRequestCallbackFunc != nullptr)
return fileRequestCallbackFunc(callbacksPtr, key);

#if DISTRHO_PLUGIN_WANT_STATE && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !defined(DGL_FILE_BROWSER_DISABLED)
std::free(uiStateFileKeyRequest);
uiStateFileKeyRequest = strdup(key);
DISTRHO_SAFE_ASSERT_RETURN(uiStateFileKeyRequest != nullptr, false);

char title[0xff];
snprintf(title, sizeof(title)-1u, DISTRHO_PLUGIN_NAME ": %s", key);
title[sizeof(title)-1u] = '\0';

FileBrowserOptions opts;
opts.title = title;
return window->openFileBrowser(opts);
#endif

return false;
}

END_NAMESPACE_DISTRHO

// -----------------------------------------------------------------------
// PluginWindow onFileSelected that require UI::PrivateData definitions

#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !defined(DGL_FILE_BROWSER_DISABLED)
START_NAMESPACE_DGL

inline void PluginWindow::onFileSelected(const char* const filename)
{
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);

if (initializing)
return;

# if DISTRHO_PLUGIN_WANT_STATE
if (char* const key = ui->uiData->uiStateFileKeyRequest)
{
ui->uiData->uiStateFileKeyRequest = nullptr;
if (filename != nullptr)
{
// notify DSP
ui->setState(key, filename);
// notify UI
ui->stateChanged(key, filename);
}
std::free(key);
return;
}
# endif

ui->uiFileBrowserSelected(filename);
}

END_NAMESPACE_DGL
#endif

// -----------------------------------------------------------------------

#endif // DISTRHO_UI_PRIVATE_DATA_HPP_INCLUDED

+ 140
- 0
source/modules/distrho/src/DistrhoUtils.cpp View File

@@ -0,0 +1,140 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef DISTRHO_IS_STANDALONE
# error Wrong build configuration
#endif

#include "../extra/String.hpp"

#ifdef DISTRHO_OS_WINDOWS
# include <windows.h>
#else
# ifndef STATIC_BUILD
# include <dlfcn.h>
# endif
# include <limits.h>
# include <stdlib.h>
#endif

#if defined(DISTRHO_OS_WINDOWS) && !DISTRHO_IS_STANDALONE
static HINSTANCE hInstance = nullptr;

DISTRHO_PLUGIN_EXPORT
BOOL WINAPI DllMain(HINSTANCE hInst, DWORD reason, LPVOID)
{
if (reason == DLL_PROCESS_ATTACH)
hInstance = hInst;
return 1;
}
#endif

START_NAMESPACE_DISTRHO

// -----------------------------------------------------------------------

const char* getBinaryFilename()
{
static String filename;

if (filename.isNotEmpty())
return filename;

#ifdef DISTRHO_OS_WINDOWS
# if DISTRHO_IS_STANDALONE
constexpr const HINSTANCE hInstance = nullptr;
# endif
CHAR filenameBuf[MAX_PATH];
filenameBuf[0] = '\0';
GetModuleFileNameA(hInstance, filenameBuf, sizeof(filenameBuf));
filename = filenameBuf;
#elif !defined(STATIC_BUILD)
Dl_info info;
dladdr((void*)getBinaryFilename, &info);
char filenameBuf[PATH_MAX];
filename = realpath(info.dli_fname, filenameBuf);
#endif

return filename;
}

const char* getPluginFormatName() noexcept
{
#if defined(DISTRHO_PLUGIN_TARGET_CARLA)
return "Carla";
#elif defined(DISTRHO_PLUGIN_TARGET_JACK)
return "JACK/Standalone";
#elif defined(DISTRHO_PLUGIN_TARGET_LADSPA)
return "LADSPA";
#elif defined(DISTRHO_PLUGIN_TARGET_DSSI)
return "DSSI";
#elif defined(DISTRHO_PLUGIN_TARGET_LV2)
return "LV2";
#elif defined(DISTRHO_PLUGIN_TARGET_VST2)
return "VST2";
#elif defined(DISTRHO_PLUGIN_TARGET_VST3)
return "VST3";
#else
return "Unknown";
#endif
}

const char* getResourcePath(const char* const bundlePath) noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(bundlePath != nullptr, nullptr);

#if defined(DISTRHO_PLUGIN_TARGET_JACK) || defined(DISTRHO_PLUGIN_TARGET_VST2)
static String resourcePath;

if (resourcePath.isEmpty())
{
resourcePath = bundlePath;
# ifdef DISTRHO_OS_MAC
resourcePath += "/Contents/Resources";
# else
resourcePath += DISTRHO_OS_SEP_STR "resources";
# endif
}

return resourcePath.buffer();
#elif defined(DISTRHO_PLUGIN_TARGET_LV2)
static String resourcePath;

if (resourcePath.isEmpty())
{
resourcePath = bundlePath;
resourcePath += DISTRHO_OS_SEP_STR "resources";
}

return resourcePath.buffer();
#elif defined(DISTRHO_PLUGIN_TARGET_VST3)
static String resourcePath;

if (resourcePath.isEmpty())
{
resourcePath = bundlePath;
resourcePath += "/Contents/Resources";
}

return resourcePath.buffer();
#endif

return nullptr;
}

// -----------------------------------------------------------------------

END_NAMESPACE_DISTRHO

+ 0
- 1
source/modules/distrho/src/dssi View File

@@ -1 +0,0 @@
../../../includes/dssi

+ 0
- 1
source/modules/distrho/src/ladspa View File

@@ -1 +0,0 @@
../../../includes/ladspa

+ 0
- 1
source/modules/distrho/src/lv2 View File

@@ -1 +0,0 @@
../../../includes/lv2

+ 0
- 1
source/modules/distrho/src/vestige View File

@@ -1 +0,0 @@
../../../includes/vestige

+ 4
- 3
source/plugin/carla-vst-export.cpp View File

@@ -21,6 +21,7 @@
#include "ui_launcher_res.cpp"

#include <cstring>
#include <vector>

#ifdef __WINE__
__cdecl static intptr_t cvst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t index, intptr_t value, void* ptr, float opt)
@@ -130,9 +131,9 @@ intptr_t VSTAudioMaster(AEffect* effect, int32_t opcode, int32_t index, intptr_t

bool isUsingUILauncher()
{
#ifdef CARLA_OS_LINUX
return false;
#else
#if defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN)
return true;
#else
return false;
#endif
}

+ 2
- 2
source/plugin/carla-vst.hpp View File

@@ -1,6 +1,6 @@
/*
* Carla Native Plugins
* Copyright (C) 2013-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2013-2022 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -32,7 +32,7 @@ struct VstObject {
NativePlugin* plugin;
};

CarlaUILauncher* createUILauncher(intptr_t winId, const NativePluginDescriptor* d, NativePluginHandle h);
CarlaUILauncher* createUILauncher(uintptr_t winId, const NativePluginDescriptor* d, NativePluginHandle h);
void idleUILauncher(CarlaUILauncher* ui);
void destoryUILauncher(CarlaUILauncher* ui);



+ 70
- 18
source/plugin/ui_launcher.cpp View File

@@ -1,6 +1,6 @@
/*
* Carla Native Plugin UI launcher
* Copyright (C) 2018 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2018-2022 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -15,8 +15,10 @@
* For a full copy of the GNU General Public License see the doc/GPL.txt file.
*/

#include "dgl/Application.hpp"
#include "dgl/ImageWidgets.hpp"
#include "dgl/OpenGL.hpp"
#include "dgl/src/pugl.hpp"
#include "dgl/src/WindowPrivateData.hpp"

#include "CarlaNative.h"
#include "ui_launcher_res.hpp"
#include "CarlaDefines.h"
@@ -25,24 +27,71 @@

START_NAMESPACE_DGL

class CarlaButtonWidget : public Widget,
private ImageButton::Callback
class PluginApplication : public Application
{
public:
explicit PluginApplication()
: Application(false)
{
setClassName("CarlaPluginWrapper");
}
};

class PluginWindow : public Window
{
public:
explicit PluginWindow(PluginApplication& app, const uintptr_t winId)
: Window(app, winId, ui_launcher_res::carla_uiWidth, ui_launcher_res::carla_uiHeight, 0.0, false, false, false)
{
// this is called just before creating UI, ensuring proper context to it
if (pData->view != nullptr && pData->initPost())
puglBackendEnter(pData->view);
}

~PluginWindow()
{
if (pData->view != nullptr)
puglBackendLeave(pData->view);
}

// called right before deleting UI, ensuring correct context
void enterContextForDeletion()
{
if (pData->view != nullptr)
puglBackendEnter(pData->view);
}

// called after creating UI, restoring proper context
void leaveContextAfterCreation()
{
if (pData->view != nullptr)
puglBackendLeave(pData->view);
}
};

class CarlaButtonWidget : public TopLevelWidget,
private OpenGLImageButton::Callback
{
public:
CarlaButtonWidget(Window& parent, const NativePluginDescriptor* const d, const NativePluginHandle h)
: Widget(parent),
explicit CarlaButtonWidget(PluginWindow& parent, const NativePluginDescriptor* const d, const NativePluginHandle h)
: TopLevelWidget(parent),
startButtonImage(ui_launcher_res::carla_uiData,
ui_launcher_res::carla_uiWidth,
ui_launcher_res::carla_uiHeight,
GL_BGR),
kImageFormatBGR),
startButton(this, startButtonImage),
descriptor(d),
handle(h)
handle(h),
pluginWindow(parent)
{
startButton.setCallback(this);
setSize(startButtonImage.getSize());
parent.setSize(startButtonImage.getSize());

pluginWindow.leaveContextAfterCreation();
}

~CarlaButtonWidget() override
{
pluginWindow.enterContextForDeletion();
}

protected:
@@ -50,7 +99,7 @@ protected:
{
}

void imageButtonClicked(ImageButton* imageButton, int) override
void imageButtonClicked(OpenGLImageButton* imageButton, int) override
{
if (imageButton != &startButton)
return;
@@ -60,10 +109,11 @@ protected:
}

private:
Image startButtonImage;
ImageButton startButton;
OpenGLImage startButtonImage;
OpenGLImageButton startButton;
const NativePluginDescriptor* const descriptor;
const NativePluginHandle handle;
PluginWindow& pluginWindow;

CARLA_DECLARE_NON_COPY_CLASS(CarlaButtonWidget);
};
@@ -72,18 +122,20 @@ END_NAMESPACE_DGL

// --------------------------------------------------------------------------------------------------------------------

USE_NAMESPACE_DGL

struct CarlaUILauncher {
DGL_NAMESPACE::Application app;
DGL_NAMESPACE::Window window;
PluginApplication app;
PluginWindow window;
CarlaButtonWidget widget;

CarlaUILauncher(const intptr_t winId, const NativePluginDescriptor* const d, const NativePluginHandle h)
CarlaUILauncher(const uintptr_t winId, const NativePluginDescriptor* const d, const NativePluginHandle h)
: app(),
window(app, winId),
widget(window, d, h) {}
};

CarlaUILauncher* createUILauncher(const intptr_t winId,
CarlaUILauncher* createUILauncher(const uintptr_t winId,
const NativePluginDescriptor* const d,
const NativePluginHandle h)
{


Loading…
Cancel
Save