summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien Zammit <damien@zamaudio.com>2015-02-17 11:59:35 +1100
committerDamien Zammit <damien@zamaudio.com>2015-02-17 11:59:35 +1100
commit78883fa7f7ea9d85477a27ed3c569a899ee305fb (patch)
treef1add63ce6f3af86340d7e4c93092776b5479b14
parentaf7a094a0c0e8b658736d77361a2d8d2f99b0006 (diff)
parent8da011deb152227714196f61eab26e1af37a2890 (diff)
Merge pull request #24 from falkTX/master
Update DPF, inital file-browser for ZamSFZ
-rw-r--r--.gitignore3
-rw-r--r--Makefile.mk2
-rw-r--r--libs/dgl/App.hpp7
-rw-r--r--libs/dgl/Base.hpp20
-rw-r--r--libs/dgl/CairoWidget.hpp208
-rw-r--r--libs/dgl/Color.hpp29
-rw-r--r--libs/dgl/Geometry.hpp142
-rw-r--r--libs/dgl/ImageAboutWindow.hpp3
-rw-r--r--libs/dgl/ImageKnob.hpp15
-rw-r--r--libs/dgl/ImageSlider.hpp8
-rw-r--r--libs/dgl/ImageSwitch.hpp8
-rw-r--r--libs/dgl/NanoVG.hpp7
-rw-r--r--libs/dgl/Widget.hpp73
-rw-r--r--libs/dgl/Window.hpp45
-rw-r--r--libs/dgl/ntk/NtkApp.hpp291
-rw-r--r--libs/dgl/ntk/NtkWidget.hpp202
-rw-r--r--libs/dgl/ntk/NtkWindow.hpp215
-rw-r--r--libs/dgl/src/Color.cpp222
-rw-r--r--libs/dgl/src/Geometry.cpp130
-rw-r--r--libs/dgl/src/Image.cpp6
-rw-r--r--libs/dgl/src/ImageAboutWindow.cpp8
-rw-r--r--libs/dgl/src/ImageKnob.cpp60
-rw-r--r--libs/dgl/src/ImageSlider.cpp36
-rw-r--r--libs/dgl/src/ImageSwitch.cpp18
-rw-r--r--libs/dgl/src/NanoVG.cpp84
-rw-r--r--libs/dgl/src/Widget.cpp11
-rw-r--r--libs/dgl/src/Window.cpp251
-rw-r--r--libs/dgl/src/nanovg/fontstash.h8
-rw-r--r--libs/dgl/src/pugl/pugl.h36
-rw-r--r--libs/dgl/src/pugl/pugl_internal.h20
-rw-r--r--libs/dgl/src/pugl/pugl_osx.m283
-rw-r--r--libs/dgl/src/pugl/pugl_win.cpp76
-rw-r--r--libs/dgl/src/pugl/pugl_x11.c99
-rw-r--r--libs/dgl/src/sofd/libsofd.c2431
-rw-r--r--libs/dgl/src/sofd/libsofd.h175
-rw-r--r--libs/distrho/DistrhoUI.hpp28
-rw-r--r--libs/distrho/DistrhoUIMain.cpp25
-rw-r--r--libs/distrho/DistrhoUtils.hpp87
-rw-r--r--libs/distrho/src/DistrhoDefines.h18
-rw-r--r--libs/distrho/src/DistrhoPluginCarla.cpp58
-rw-r--r--libs/distrho/src/DistrhoPluginChecks.h4
-rw-r--r--libs/distrho/src/DistrhoPluginInternal.hpp4
-rw-r--r--libs/distrho/src/DistrhoPluginVST.cpp23
-rw-r--r--libs/distrho/src/DistrhoUI.cpp24
-rw-r--r--libs/distrho/src/DistrhoUIInternal.hpp80
-rw-r--r--libs/distrho/src/DistrhoUILV2.cpp18
-rw-r--r--plugins/ZamSFZ/Makefile6
-rw-r--r--plugins/ZamSFZ/ZamSFZUI.cpp39
-rw-r--r--plugins/ZamSFZ/ZamSFZUI.hpp3
49 files changed, 4046 insertions, 1603 deletions
diff --git a/.gitignore b/.gitignore
index 677f4ae..ffeaa84 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,6 +21,9 @@ bin/ZamComp
bin/ZamCompX2
bin/ZamEQ2
bin/ZamGEQ31
+bin/ZamGEQ31X2
bin/ZamNoise
+bin/ZamPiano
+bin/ZamSFZ
bin/ZamSynth
bin/ZamTube
diff --git a/Makefile.mk b/Makefile.mk
index 64c15b7..e450b8a 100644
--- a/Makefile.mk
+++ b/Makefile.mk
@@ -26,7 +26,7 @@ BASE_OPTS = -O2 -ffast-math -fdata-sections -ffunction-sections
ifneq ($(NOOPT),true)
BASE_OPTS += -mtune=generic -msse -msse2 -mfpmath=sse
endif
-LINK_OPTS = -fdata-sections -ffunction-sections -Wl,-O1 -Wl,--as-needed -Wl,--gc-sections -Wl,--strip-all $(shell pkg-config --libs sndfile rubberband)
+LINK_OPTS = -fdata-sections -ffunction-sections -Wl,-O1 -Wl,--as-needed -Wl,--gc-sections -Wl,--strip-all
ifeq ($(MACOS),true)
# MacOS linker flags
diff --git a/libs/dgl/App.hpp b/libs/dgl/App.hpp
index b6fce01..4babf1f 100644
--- a/libs/dgl/App.hpp
+++ b/libs/dgl/App.hpp
@@ -21,6 +21,9 @@
START_NAMESPACE_DGL
+// -----------------------------------------------------------------------
+// Forward class names
+
class Window;
// -----------------------------------------------------------------------
@@ -50,7 +53,7 @@ public:
/**
Idle function.
- This calls all this app Windows' idle functions and idle callbacks.
+ This runs the application event-loop once.
*/
void idle();
@@ -69,7 +72,7 @@ public:
/**
Check if the application is about to quit.
- Returning true means there's no event-loop running at the moment.
+ Returning true means there's no event-loop running at the moment (or it's just about to stop).
*/
bool isQuiting() const noexcept;
diff --git a/libs/dgl/Base.hpp b/libs/dgl/Base.hpp
index 0d62d72..edefbcf 100644
--- a/libs/dgl/Base.hpp
+++ b/libs/dgl/Base.hpp
@@ -127,6 +127,16 @@ enum Char {
};
/**
+ Keyboard modifier flags.
+ */
+enum Modifier {
+ MODIFIER_SHIFT = 1 << 0, /**< Shift key */
+ MODIFIER_CTRL = 1 << 1, /**< Control key */
+ MODIFIER_ALT = 1 << 2, /**< Alt/Option key */
+ MODIFIER_SUPER = 1 << 3 /**< Mod4/Command/Windows key */
+};
+
+/**
Special (non-Unicode) keyboard keys.
*/
enum Key {
@@ -157,16 +167,6 @@ enum Key {
KEY_SUPER
};
-/**
- Keyboard modifier flags.
- */
-enum Modifier {
- MODIFIER_SHIFT = 1 << 0, /**< Shift key */
- MODIFIER_CTRL = 1 << 1, /**< Control key */
- MODIFIER_ALT = 1 << 2, /**< Alt/Option key */
- MODIFIER_SUPER = 1 << 3 /**< Mod4/Command/Windows key */
-};
-
// -----------------------------------------------------------------------
// Base DGL classes
diff --git a/libs/dgl/CairoWidget.hpp b/libs/dgl/CairoWidget.hpp
deleted file mode 100644
index 9ebfe8f..0000000
--- a/libs/dgl/CairoWidget.hpp
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * DISTRHO Plugin Framework (DPF)
- * Copyright (C) 2012-2014 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_CAIRO_WIDGET_HPP_INCLUDED
-#define DGL_CAIRO_WIDGET_HPP_INCLUDED
-
-#include "Widget.hpp"
-
-#include <cairo.h>
-
-#include <cstdio>
-
-START_NAMESPACE_DGL
-
-// -----------------------------------------------------------------------
-
-class CairoWidget : public Widget
-{
-public:
- CairoWidget(Window& parent)
- : Widget(parent),
- fContext(nullptr),
- fSurface(nullptr),
- fTextureId(0)
- {
- }
-
- virtual void setWidth(int width) override
- {
- if (fArea.getWidth() == width)
- return;
-
- Widget::setWidth(width);
- _recreateSurface();
- }
-
- virtual void setHeight(int height) override
- {
- if (fArea.getHeight() == height)
- return;
-
- Widget::setHeight(height);
- _recreateSurface();
- }
-
- virtual void setSize(const Size<int>& size) override
- {
- if (fArea.getSize() == size)
- return;
-
- Widget::setSize(size);
- _recreateSurface();
- }
-
- void setSize(int width, int height)
- {
- setSize(Size<int>(width, height));
- }
-
-protected:
- virtual void cairoDisplay(cairo_t* const context) = 0;
-
-private:
- void onDisplay() override
- {
- // wait for sizing
- if (fSurface == nullptr || fContext == nullptr)
- {
- printf("invalid surface\n");
- return;
- }
-
- if (fTextureId == 0)
- glGenTextures(1, &fTextureId);
- if (fTextureId == 0)
- {
- // TODO: invalidate widget
- printf("invalid texture\n");
- return;
- }
-
-#if 1
- const int x = getX();
- const int y = getY();
- const int width = getWidth();
- const int height = getHeight();
-
- // draw cairo stuff
- cairoDisplay(fContext);
-
- // get cairo surface data (RGB24)
- uchar* const surfaceData = cairo_image_surface_get_data(fSurface);
-
- // enable GL texture
- glEnable(GL_TEXTURE_RECTANGLE_ARB);
-
- // set texture params
- glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
- glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
- glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
- // bind texture to surface data
- glBindTexture(GL_TEXTURE_RECTANGLE_ARB, fTextureId);
- glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, surfaceData);
-
- // draw the texture
-
-// glBegin(GL_QUADS);
-// glTexCoord2f(0.0f, 0.0f);
-// glVertex2i(x, y);
-//
-// glTexCoord2f(1.0f, 0.0f);
-// glVertex2i(x+width, y);
-//
-// glTexCoord2f(1.0f, 1.0f);
-// glVertex2i(x+width, y+height);
-//
-// glTexCoord2f(0.0f, 1.0f);
-// glVertex2i(x, y+height);
-// glEnd();
-
- glBegin(GL_QUADS);
- //glTexCoord2i(x, y);
- glTexCoord2i(0, 0);
- glVertex2i(x, y);
-
- //glTexCoord2i(x+width, y);
- glTexCoord2i(width, 0);
- glVertex2i(x+width, y);
-
- //glTexCoord2i(x+width, y+height);
- glTexCoord2i(width, height);
- glVertex2i(x+width, y+height);
-
- //glTexCoord2i(x, y+height);
- glTexCoord2i(0, height);
- glVertex2i(x, y+height);
- glEnd();
-
- // cleanup
- glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
- glDisable(GL_TEXTURE_RECTANGLE_ARB);
-#endif
- }
-
- void onClose() override
- {
- if (fContext != nullptr)
- {
- cairo_destroy(fContext);
- fContext = nullptr;
- }
-
- if (fSurface != nullptr)
- {
- cairo_surface_destroy(fSurface);
- fSurface = nullptr;
- }
-
- if (fTextureId != 0)
- {
- glDeleteTextures(1, &fTextureId);
- fTextureId = 0;
- }
- }
-
- void _recreateSurface()
- {
- if (fContext != nullptr)
- cairo_destroy(fContext);
-
- if (fSurface != nullptr)
- cairo_surface_destroy(fSurface);
-
- fSurface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, fArea.getWidth(), fArea.getHeight());
-
- if (fSurface != nullptr)
- fContext = cairo_create(fSurface);
- else
- fContext = nullptr;
- }
-
-private:
- cairo_t* fContext;
- cairo_surface_t* fSurface;
- GLuint fTextureId;
-
- DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CairoWidget)
-};
-
-// -----------------------------------------------------------------------
-
-END_NAMESPACE_DGL
-
-#endif // DGL_CAIRO_WIDGET_HPP_INCLUDED
diff --git a/libs/dgl/Color.hpp b/libs/dgl/Color.hpp
index dbe2cd1..d47f354 100644
--- a/libs/dgl/Color.hpp
+++ b/libs/dgl/Color.hpp
@@ -28,7 +28,7 @@ START_NAMESPACE_DGL
// TODO: create color from "#333" and "#112233" like strings
/**
- A color made from red, green, blue and alpha floating-point values in [0..1] range.
+ A color made from red, green, blue and alpha floating-point values in [0..1] range.
*/
struct Color {
/**
@@ -48,13 +48,13 @@ struct Color {
Create a color from red, green, blue and alpha numeric values.
Values must be in [0..255] range.
*/
- Color(const int red, const int green, const int blue, const int alpha = 255) noexcept;
+ Color(int red, int green, int blue, int alpha = 255) noexcept;
/**
Create a color from red, green, blue and alpha floating-point values.
Values must in [0..1] range.
*/
- Color(const float red, const float green, const float blue, const float alpha = 1.0f) noexcept;
+ Color(float red, float green, float blue, float alpha = 1.0f) noexcept;
/**
Create a color by copying another color.
@@ -65,26 +65,39 @@ struct Color {
/**
Create a color by linearly interpolating two other colors.
*/
- Color(const Color& color1, const Color& color2, const float u) noexcept;
+ Color(const Color& color1, const Color& color2, float u) noexcept;
/**
- Create a color specified by hue, saturation, lightness and alpha.
- HSL values are all in [0..1] range, alpha in [0..255] range.
+ Create a color specified by hue, saturation and lightness.
+ Values must in [0..1] range.
+ */
+ static Color fromHSL(float hue, float saturation, float lightness, float alpha = 1.0f);
+
+ /**
+ Create a color from a HTML string like "#333" or "#112233".
*/
- static Color HSL(const float hue, const float saturation, const float lightness, const int alpha = 255);
+ static Color fromHTML(const char* rgb, float alpha = 1.0f);
/**
Linearly interpolate this color against another.
*/
- void interpolate(const Color& other, const float u) noexcept;
+ void interpolate(const Color& other, float u) noexcept;
/**
Check if this color matches another.
+ @note: Comparison is forced within 8-bit color values.
*/
+ bool isEqual(const Color& color, bool withAlpha = true) noexcept;
+ bool isNotEqual(const Color& color, bool withAlpha = true) noexcept;
bool operator==(const Color& color) noexcept;
bool operator!=(const Color& color) noexcept;
/**
+ Fix color bounds if needed.
+ */
+ void fixBounds() noexcept;
+
+ /**
@internal
Needed for NanoVG compatibility.
*/
diff --git a/libs/dgl/Geometry.hpp b/libs/dgl/Geometry.hpp
index 117c0a9..725a15e 100644
--- a/libs/dgl/Geometry.hpp
+++ b/libs/dgl/Geometry.hpp
@@ -30,8 +30,12 @@ template<typename> class Triangle;
template<typename> class Rectangle;
// -----------------------------------------------------------------------
-// Point
+/**
+ DGL Point class.
+
+ This class describes a single point in space, defined by an X and Y value.
+ */
template<typename T>
class Point
{
@@ -62,17 +66,17 @@ public:
const T& getY() const noexcept;
/**
- Set X value as @a x.
+ Set X value to @a x.
*/
void setX(const T& x) noexcept;
/**
- Set Y value as @a y.
+ Set Y value to @a y.
*/
void setY(const T& y) noexcept;
/**
- Set X and Y values as @a x and @a y respectively.
+ Set X and Y values to @a x and @a y respectively.
*/
void setPos(const T& x, const T& y) noexcept;
@@ -96,6 +100,11 @@ public:
*/
bool isZero() const noexcept;
+ /**
+ Return true if point is not (0, 0).
+ */
+ bool isNotZero() const noexcept;
+
Point<T> operator+(const Point<T>& pos) noexcept;
Point<T> operator-(const Point<T>& pos) noexcept;
Point<T>& operator=(const Point<T>& pos) noexcept;
@@ -113,8 +122,12 @@ private:
};
// -----------------------------------------------------------------------
-// Size
+/**
+ DGL Size class.
+
+ This class describes a size, defined by a width and height value.
+ */
template<typename T>
class Size
{
@@ -155,7 +168,7 @@ public:
void setHeight(const T& height) noexcept;
/**
- Set size using @a width and @a height.
+ Set size to @a width and @a height.
*/
void setSize(const T& width, const T& height) noexcept;
@@ -167,30 +180,43 @@ public:
/**
Grow size by @a multiplier.
*/
- void growBy(const T& multiplier) noexcept;
+ void growBy(double multiplier) noexcept;
/**
Shrink size by @a divider.
*/
- void shrinkBy(const T& divider) noexcept;
+ void shrinkBy(double divider) noexcept;
/**
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 is negative.
*/
bool isNotNull() const noexcept;
+ /**
+ Return true if size is valid (width and height are higher than zero).
+ */
+ 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;
+
Size<T> operator+(const Size<T>& size) 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;
Size<T>& operator-=(const Size<T>& size) noexcept;
- Size<T>& operator*=(const T& m) noexcept;
- Size<T>& operator/=(const T& d) noexcept;
+ Size<T>& operator*=(double m) noexcept;
+ Size<T>& operator/=(double d) noexcept;
bool operator==(const Size<T>& size) const noexcept;
bool operator!=(const Size<T>& size) const noexcept;
@@ -200,14 +226,18 @@ private:
};
// -----------------------------------------------------------------------
-// Line
+/**
+ DGL Line class.
+
+ This class describes a line, defined by two points.
+ */
template<typename T>
class Line
{
public:
/**
- Constructor for a null line ([0, 0] to [0, 0]).
+ Constructor for a null line ([0,0] to [0,0]).
*/
Line() noexcept;
@@ -217,7 +247,7 @@ public:
Line(const T& startX, const T& startY, const T& endX, const T& endY) noexcept;
/**
- Constructor using custom start X, start Y, end pos values.
+ Constructor using custom start X, start Y and end pos values.
*/
Line(const T& startX, const T& startY, const Point<T>& endPos) noexcept;
@@ -267,17 +297,17 @@ public:
const Point<T>& getEndPos() const noexcept;
/**
- Set start X value as @a x.
+ Set start X value to @a x.
*/
void setStartX(const T& x) noexcept;
/**
- Set start Y value as @a y.
+ Set start Y value to @a y.
*/
void setStartY(const T& y) noexcept;
/**
- Set start X and Y values as @a x and @a y respectively.
+ Set start X and Y values to @a x and @a y respectively.
*/
void setStartPos(const T& x, const T& y) noexcept;
@@ -287,17 +317,17 @@ public:
void setStartPos(const Point<T>& pos) noexcept;
/**
- Set end X value as @a x.
+ Set end X value to @a x.
*/
void setEndX(const T& x) noexcept;
/**
- Set end Y value as @a y.
+ Set end Y value to @a y.
*/
void setEndY(const T& y) noexcept;
/**
- Set end X and Y values as @a x and @a y respectively.
+ Set end X and Y values to @a x and @a y respectively.
*/
void setEndPos(const T& x, const T& y) noexcept;
@@ -321,6 +351,16 @@ public:
*/
void draw();
+ /**
+ Return true if line is null (start and end pos are equal).
+ */
+ bool isNull() const noexcept;
+
+ /**
+ Return true if line is not null (start and end pos are different).
+ */
+ bool isNotNull() const noexcept;
+
Line<T>& operator=(const Line<T>& line) noexcept;
bool operator==(const Line<T>& line) const noexcept;
bool operator!=(const Line<T>& line) const noexcept;
@@ -330,8 +370,15 @@ private:
};
// -----------------------------------------------------------------------
-// Circle
+/**
+ DGL Circle class.
+
+ This class describes a circle, defined by position, size and a minimum of 3 segments.
+
+ TODO: report if circle starts at top-left, bottom-right or center.
+ and size grows from which point?
+ */
template<typename T>
class Circle
{
@@ -372,17 +419,17 @@ public:
const Point<T>& getPos() const noexcept;
/**
- Set X value as @a x.
+ Set X value to @a x.
*/
void setX(const T& x) noexcept;
/**
- Set Y value as @a y.
+ Set Y value to @a y.
*/
void setY(const T& y) noexcept;
/**
- Set X and Y values as @a x and @a y respectively.
+ Set X and Y values to @a x and @a y respectively.
*/
void setPos(const T& x, const T& y) noexcept;
@@ -435,12 +482,16 @@ private:
// cached values
float fTheta, fCos, fSin;
- void _draw(const bool isOutline);
+ void _draw(const bool outline);
};
// -----------------------------------------------------------------------
-// Triangle
+/**
+ DGL Triangle class.
+
+ This class describes a triangle, defined by 3 points.
+ */
template<typename T>
class Triangle
{
@@ -475,6 +526,29 @@ public:
*/
void drawOutline();
+ /**
+ Return true if triangle is null (all its points are equal).
+ An null triangle is also invalid.
+ */
+ bool isNull() const noexcept;
+
+ /**
+ Return true if triangle is not null (one its points is different from the others).
+ A non-null triangle is still invalid if two of its points are equal.
+ */
+ bool isNotNull() const noexcept;
+
+ /**
+ Return true if triangle is valid (all its points are different).
+ */
+ bool isValid() const noexcept;
+
+ /**
+ Return true if triangle is invalid (one or two of its points are equal).
+ An invalid triangle might not be null under some circumstances.
+ */
+ bool isInvalid() const noexcept;
+
Triangle<T>& operator=(const Triangle<T>& tri) noexcept;
bool operator==(const Triangle<T>& tri) const noexcept;
bool operator!=(const Triangle<T>& tri) const noexcept;
@@ -482,12 +556,16 @@ public:
private:
Point<T> fPos1, fPos2, fPos3;
- void _draw(const bool isOutline);
+ void _draw(const bool outline);
};
// -----------------------------------------------------------------------
-// Rectangle
+/**
+ DGL Rectangle class.
+
+ This class describes a rectangle, defined by a starting point and a size.
+ */
template<typename T>
class Rectangle
{
@@ -605,12 +683,12 @@ public:
/**
Grow size by @a multiplier.
*/
- void growBy(const T& multiplier) noexcept;
+ void growBy(double multiplier) noexcept;
/**
Shrink size by @a divider.
*/
- void shrinkBy(const T& divider) noexcept;
+ void shrinkBy(double divider) noexcept;
/**
Set rectangle using @a pos and @a size.
@@ -653,8 +731,8 @@ public:
void drawOutline();
Rectangle<T>& operator=(const Rectangle<T>& rect) noexcept;
- Rectangle<T>& operator*=(const T& m) noexcept;
- Rectangle<T>& operator/=(const T& d) noexcept;
+ Rectangle<T>& operator*=(double m) noexcept;
+ Rectangle<T>& operator/=(double d) noexcept;
bool operator==(const Rectangle<T>& size) const noexcept;
bool operator!=(const Rectangle<T>& size) const noexcept;
@@ -662,7 +740,7 @@ private:
Point<T> fPos;
Size<T> fSize;
- void _draw(const bool isOutline);
+ void _draw(const bool outline);
};
// -----------------------------------------------------------------------
diff --git a/libs/dgl/ImageAboutWindow.hpp b/libs/dgl/ImageAboutWindow.hpp
index 5a72ea2..2ffc0de 100644
--- a/libs/dgl/ImageAboutWindow.hpp
+++ b/libs/dgl/ImageAboutWindow.hpp
@@ -43,7 +43,8 @@ protected:
private:
Image fImgBackground;
- DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageAboutWindow)
+ DISTRHO_DECLARE_NON_COPY_CLASS(ImageAboutWindow)
+ //DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageAboutWindow)
};
// -----------------------------------------------------------------------
diff --git a/libs/dgl/ImageKnob.hpp b/libs/dgl/ImageKnob.hpp
index 3563824..2dd2669 100644
--- a/libs/dgl/ImageKnob.hpp
+++ b/libs/dgl/ImageKnob.hpp
@@ -41,15 +41,12 @@ public:
virtual void imageKnobValueChanged(ImageKnob* imageKnob, float value) = 0;
};
- explicit ImageKnob(Window& parent, const Image& image, Orientation orientation = Vertical, int id = 0) noexcept;
- explicit ImageKnob(Widget* widget, const Image& image, Orientation orientation = Vertical, int id = 0) noexcept;
+ 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;
- int getId() const noexcept;
- void setId(int id) noexcept;
-
float getValue() const noexcept;
void setDefault(float def) noexcept;
@@ -70,7 +67,6 @@ protected:
private:
Image fImage;
- int fId;
float fMinimum;
float fMaximum;
float fStep;
@@ -89,11 +85,10 @@ private:
Callback* fCallback;
bool fIsImgVertical;
- int fImgLayerSize;
- int fImgLayerCount;
- Rectangle<int> fKnobArea;
- GLuint fTextureId;
+ uint fImgLayerSize;
+ uint fImgLayerCount;
bool fIsReady;
+ GLuint fTextureId;
float _logscale(float value) const;
float _invlogscale(float value) const;
diff --git a/libs/dgl/ImageSlider.hpp b/libs/dgl/ImageSlider.hpp
index 613b25c..e6f2716 100644
--- a/libs/dgl/ImageSlider.hpp
+++ b/libs/dgl/ImageSlider.hpp
@@ -36,14 +36,11 @@ public:
virtual void imageSliderValueChanged(ImageSlider* imageSlider, float value) = 0;
};
- explicit ImageSlider(Window& parent, const Image& image, int id = 0) noexcept;
- explicit ImageSlider(Widget* widget, const Image& image, int id = 0) noexcept;
+ explicit ImageSlider(Window& parent, const Image& image) noexcept;
+ explicit ImageSlider(Widget* widget, const Image& image) noexcept;
explicit ImageSlider(const ImageSlider& imageSlider) noexcept;
ImageSlider& operator=(const ImageSlider& imageSlider) noexcept;
- int getId() const noexcept;
- void setId(int id) noexcept;
-
float getValue() const noexcept;
void setStartPos(const Point<int>& startPos) noexcept;
@@ -65,7 +62,6 @@ protected:
private:
Image fImage;
- int fId;
float fMinimum;
float fMaximum;
float fStep;
diff --git a/libs/dgl/ImageSwitch.hpp b/libs/dgl/ImageSwitch.hpp
index bb963ac..172d61c 100644
--- a/libs/dgl/ImageSwitch.hpp
+++ b/libs/dgl/ImageSwitch.hpp
@@ -34,14 +34,11 @@ public:
virtual void imageSwitchClicked(ImageSwitch* imageButton, bool down) = 0;
};
- explicit ImageSwitch(Window& parent, const Image& imageNormal, const Image& imageDown, int id = 0) noexcept;
- explicit ImageSwitch(Widget* widget, const Image& imageNormal, const Image& imageDown, int id = 0) noexcept;
+ 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;
- int getId() const noexcept;
- void setId(int id) noexcept;
-
bool isDown() const noexcept;
void setDown(bool down) noexcept;
@@ -55,7 +52,6 @@ private:
Image fImageNormal;
Image fImageDown;
bool fIsDown;
- int fId;
Callback* fCallback;
diff --git a/libs/dgl/NanoVG.hpp b/libs/dgl/NanoVG.hpp
index 32d467d..1135f55 100644
--- a/libs/dgl/NanoVG.hpp
+++ b/libs/dgl/NanoVG.hpp
@@ -258,7 +258,7 @@ public:
/**
Destructor.
*/
- ~NanoVG();
+ virtual ~NanoVG();
/**
Get the NanoVG context.
@@ -641,7 +641,7 @@ public:
Creates font by loading it from the specified memory chunk.
Returns handle to the font.
*/
- FontId createFontMem(const char* name, uchar* data, int ndata, bool freeData);
+ FontId createFontMem(const char* name, const uchar* data, int ndata, bool freeData);
/**
Finds a loaded font of specified name, and returns handle to it, or -1 if the font is not found.
@@ -761,7 +761,8 @@ public:
*/
NanoWidget(Window& parent)
: Widget(parent),
- NanoVG()
+ NanoVG(),
+ leakDetector_NanoWidget()
{
setNeedsScaling(true);
}
diff --git a/libs/dgl/Widget.hpp b/libs/dgl/Widget.hpp
index 86c42d4..faed6b5 100644
--- a/libs/dgl/Widget.hpp
+++ b/libs/dgl/Widget.hpp
@@ -26,6 +26,7 @@ START_NAMESPACE_DGL
class App;
class Window;
+class StandaloneWindow;
// -----------------------------------------------------------------------
@@ -52,12 +53,17 @@ class Widget
public:
/**
Base event data.
- @a mod The currently active modifiers.
- @a time The timestamp (if any) of the currently-processing event.
+ @a mod The currently active keyboard modifiers, @see Modifier.
+ @a time The timestamp (if any).
*/
struct BaseEvent {
- Modifier mod;
+ uint mod;
uint32_t time;
+
+ /** Constuctor */
+ BaseEvent() noexcept : mod(0x0), time(0) {}
+ /** Destuctor */
+ virtual ~BaseEvent() noexcept {}
};
/**
@@ -69,6 +75,12 @@ public:
struct KeyboardEvent : BaseEvent {
bool press;
uint key;
+
+ /** Constuctor */
+ KeyboardEvent() noexcept
+ : BaseEvent(),
+ press(false),
+ key(0) {}
};
/**
@@ -79,7 +91,13 @@ public:
*/
struct SpecialEvent : BaseEvent {
bool press;
- Key key;
+ Key key;
+
+ /** Constuctor */
+ SpecialEvent() noexcept
+ : BaseEvent(),
+ press(false),
+ key(Key(0)) {}
};
/**
@@ -90,9 +108,16 @@ public:
@see onMouse
*/
struct MouseEvent : BaseEvent {
- int button;
+ int button;
bool press;
Point<int> pos;
+
+ /** Constuctor */
+ MouseEvent() noexcept
+ : BaseEvent(),
+ button(0),
+ press(false),
+ pos(0, 0) {}
};
/**
@@ -102,6 +127,11 @@ public:
*/
struct MotionEvent : BaseEvent {
Point<int> pos;
+
+ /** Constuctor */
+ MotionEvent() noexcept
+ : BaseEvent(),
+ pos(0, 0) {}
};
/**
@@ -113,6 +143,12 @@ public:
struct ScrollEvent : BaseEvent {
Point<int> pos;
Point<float> delta;
+
+ /** Constuctor */
+ ScrollEvent() noexcept
+ : BaseEvent(),
+ pos(0, 0),
+ delta(0.0f, 0.0f) {}
};
/**
@@ -124,6 +160,11 @@ public:
struct ResizeEvent {
Size<uint> size;
Size<uint> oldSize;
+
+ /** Constuctor */
+ ResizeEvent() noexcept
+ : size(0, 0),
+ oldSize(0, 0) {}
};
/**
@@ -177,22 +218,22 @@ public:
/**
Set width.
*/
- virtual void setWidth(uint width) noexcept;
+ void setWidth(uint width) noexcept;
/**
Set height.
*/
- virtual void setHeight(uint height) noexcept;
+ void setHeight(uint height) noexcept;
/**
Set size using @a width and @a height values.
*/
- virtual void setSize(uint width, uint height) noexcept;
+ void setSize(uint width, uint height) noexcept;
/**
Set size.
*/
- virtual void setSize(const Size<uint>& size) noexcept;
+ void setSize(const Size<uint>& size) noexcept;
/**
Get absolute X.
@@ -255,6 +296,18 @@ public:
*/
void repaint() noexcept;
+ /**
+ Get the Id associated with this widget.
+ @see setId
+ */
+ uint getId() const noexcept;
+
+ /**
+ Set an Id to be associated with this widget.
+ @see getId
+ */
+ void setId(uint id) noexcept;
+
protected:
/**
A function called to draw the view contents with OpenGL.
@@ -317,10 +370,10 @@ private:
bool fNeedsFullViewport;
bool fNeedsScaling;
bool fVisible;
+ uint fId;
Point<int> fAbsolutePos;
Size<uint> fSize;
- friend class CairoWidget;
friend class Window;
friend class StandaloneWindow;
diff --git a/libs/dgl/Window.hpp b/libs/dgl/Window.hpp
index 89e91de..37ae278 100644
--- a/libs/dgl/Window.hpp
+++ b/libs/dgl/Window.hpp
@@ -25,10 +25,48 @@ START_NAMESPACE_DGL
class App;
class Widget;
+class StandaloneWindow;
class Window
{
public:
+ /**
+ 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() {}
+ };
+
explicit Window(App& app);
explicit Window(App& app, Window& parent);
explicit Window(App& app, intptr_t parentId);
@@ -42,6 +80,8 @@ public:
void focus();
void repaint() noexcept;
+ bool openFileBrowser(const FileBrowserOptions& options);
+
bool isVisible() const noexcept;
void setVisible(bool yesNo);
@@ -54,9 +94,10 @@ public:
void setSize(uint width, uint height);
void setSize(Size<uint> size);
+ const char* getTitle() const noexcept;
void setTitle(const char* title);
- void setTransientWinId(intptr_t winId);
+ void setTransientWinId(uintptr_t winId);
App& getApp() const noexcept;
intptr_t getWindowId() const noexcept;
@@ -70,6 +111,8 @@ protected:
virtual void onReshape(uint width, uint height);
virtual void onClose();
+ virtual void fileBrowserSelected(const char* filename);
+
private:
struct PrivateData;
PrivateData* const pData;
diff --git a/libs/dgl/ntk/NtkApp.hpp b/libs/dgl/ntk/NtkApp.hpp
deleted file mode 100644
index 2c32d56..0000000
--- a/libs/dgl/ntk/NtkApp.hpp
+++ /dev/null
@@ -1,291 +0,0 @@
-/*
- * DISTRHO Plugin Framework (DPF)
- * Copyright (C) 2012-2014 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_NTK_APP_HPP_INCLUDED
-#define DGL_NTK_APP_HPP_INCLUDED
-
-#include "../Base.hpp"
-#include "../../distrho/DistrhoUI.hpp"
-#include "../../distrho/extra/d_thread.hpp"
-
-#ifdef override
-# define override_defined
-# undef override
-#endif
-
-#include <list>
-#include <FL/Fl.H>
-#include <FL/Fl_Double_Window.H>
-#include <FL/Fl_Shared_Image.H>
-#include <FL/x.H>
-
-#ifdef override_defined
-# define override
-# undef override_defined
-#endif
-
-struct ScopedDisplayLock {
- ScopedDisplayLock()
- {
-#ifdef DISTRHO_OS_LINUX
- XLockDisplay(fl_display);
-#endif
- }
-
- ~ScopedDisplayLock()
- {
-#ifdef DISTRHO_OS_LINUX
- XUnlockDisplay(fl_display);
-#endif
- }
-};
-
-// -----------------------------------------------------------------------
-
-namespace DISTRHO_NAMESPACE {
- class UI;
-}
-
-START_NAMESPACE_DGL
-
-class NtkWindow;
-
-typedef DISTRHO_NAMESPACE::Mutex d_Mutex;
-typedef DISTRHO_NAMESPACE::MutexLocker d_MutexLocker;
-typedef DISTRHO_NAMESPACE::Thread d_Thread;
-typedef DISTRHO_NAMESPACE::UI d_UI;
-
-// -----------------------------------------------------------------------
-
-/**
- DGL compatible App class that uses NTK instead of OpenGL.
- @see App
- */
-class NtkApp : d_Thread
-{
-public:
- /**
- Constructor.
- */
- NtkApp()
- : d_Thread("NtkApp"),
- fWindows(),
- fWindowMutex(),
- fNextUI(),
- fDoNextUI(false),
- fInitialized(false)
- {
-#ifdef DISTRHO_OS_LINUX
- //XInitThreads();
-#endif
-
- startThread();
-
- for (; ! fInitialized;)
- d_msleep(10);
- }
-
- /**
- Destructor.
- */
- ~NtkApp()
- {
- stopThread(-1);
- fWindows.clear();
- }
-
- /**
- Idle function.
- This calls does nothing.
- */
- void idle() {}
-
- /**
- Run the application event-loop until all Windows are closed.
- @note: This function is meant for standalones only, *never* call this from plugins.
- */
- void exec()
- {
- while (isThreadRunning() && ! shouldThreadExit())
- d_sleep(1);
- }
-
- /**
- Quit the application.
- This stops the event-loop and closes all Windows.
- */
- void quit()
- {
- signalThreadShouldExit();
- }
-
- /**
- Check if the application is about to quit.
- Returning true means there's no event-loop running at the moment.
- */
- bool isQuiting() const noexcept
- {
- if (isThreadRunning() && ! shouldThreadExit())
- return false;
- return true;
- }
-
- // -------------------------------------------------------------------
-
- /**
- Create UI on our separate thread.
- Blocks until the UI is created and returns it.
- */
- d_UI* createUI(void* const func)
- {
- DISTRHO_SAFE_ASSERT_RETURN(isThreadRunning(), nullptr);
- DISTRHO_SAFE_ASSERT_RETURN(! fDoNextUI, nullptr);
-
- fNextUI.create = true;
- fNextUI.func = (NextUI::UiFunc)func;
- fDoNextUI = true;
-
- for (; fDoNextUI;)
- d_msleep(10);
-
- return fNextUI.ui;
- }
-
- /**
- Delete UI on our separate thread.
- Blocks until the UI is deleted.
- */
- void deleteUI(d_UI* const ui)
- {
- DISTRHO_SAFE_ASSERT_RETURN(! fDoNextUI,);
-
- fNextUI.create = false;
- fNextUI.ui = ui;
- fDoNextUI = true;
-
- if (isThreadRunning())
- {
- for (; fDoNextUI;)
- d_msleep(10);
- }
- else
- {
- fNextUI.run();
- fDoNextUI = false;
- }
- }
-
- // -------------------------------------------------------------------
-
-private:
- struct NextUI {
- typedef d_UI* (*UiFunc)();
-
- bool create;
-
- union {
- UiFunc func;
- d_UI* ui;
- };
-
- NextUI()
- : create(false),
- func(nullptr) {}
-
- void run();
- };
-
- std::list<Fl_Double_Window*> fWindows;
- d_Mutex fWindowMutex;
- NextUI fNextUI;
- volatile bool fDoNextUI;
- volatile bool fInitialized;
-
- /** @internal used by NtkWindow. */
- void addWindow(Fl_Double_Window* const window)
- {
- DISTRHO_SAFE_ASSERT_RETURN(window != nullptr,);
-
- if (fWindows.size() == 0 && ! isThreadRunning())
- startThread();
-
- const d_MutexLocker sl(fWindowMutex);
- fWindows.push_back(window);
- }
-
- /** @internal used by NtkWindow. */
- void removeWindow(Fl_Double_Window* const window)
- {
- DISTRHO_SAFE_ASSERT_RETURN(window != nullptr,);
-
- const d_MutexLocker sl(fWindowMutex);
- fWindows.remove(window);
-
- if (fWindows.size() == 0)
- signalThreadShouldExit();
- }
-
- /** @internal */
- void run() override
- {
- static bool initialized = false;
-
- if (! initialized)
- {
- initialized = true;
- fl_register_images();
-#ifdef DISTRHO_OS_LINUX
- fl_open_display();
-#endif
- }
-
- fInitialized = true;
-
- for (; ! shouldThreadExit();)
- {
- if (fDoNextUI)
- {
- const ScopedDisplayLock csdl;
- fNextUI.run();
- fDoNextUI = false;
- }
-
- const ScopedDisplayLock csdl;
- Fl::check();
- Fl::flush();
-
- d_msleep(20);
- }
-
- const d_MutexLocker sl(fWindowMutex);
- const ScopedDisplayLock csdl;
-
- for (std::list<Fl_Double_Window*>::reverse_iterator rit = fWindows.rbegin(), rite = fWindows.rend(); rit != rite; ++rit)
- {
- Fl_Double_Window* const window(*rit);
- window->hide();
- }
- }
-
- friend class NtkWindow;
-
- DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(NtkApp)
-};
-// -----------------------------------------------------------------------
-
-END_NAMESPACE_DGL
-
-#endif // DGL_NTK_APP_HPP_INCLUDED
diff --git a/libs/dgl/ntk/NtkWidget.hpp b/libs/dgl/ntk/NtkWidget.hpp
deleted file mode 100644
index 2247be3..0000000
--- a/libs/dgl/ntk/NtkWidget.hpp
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * DISTRHO Plugin Framework (DPF)
- * Copyright (C) 2012-2014 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_NTK_WIDGET_HPP_INCLUDED
-#define DGL_NTK_WIDGET_HPP_INCLUDED
-
-#include "NtkWindow.hpp"
-
-START_NAMESPACE_DGL
-
-// -----------------------------------------------------------------------
-
-/**
- DGL compatible Widget class that uses NTK instead of OpenGL.
- @see Widget
- */
-class NtkWidget : public Fl_Double_Window
-{
-public:
- /**
- Constructor.
- */
- explicit NtkWidget(NtkWindow& parent)
- : Fl_Double_Window(100, 100),
- fParent(parent)
- {
- fParent.add(this);
- show();
- }
-
- /**
- Destructor.
- */
- ~NtkWidget() override
- {
- hide();
- fParent.remove(this);
- }
-
- /**
- Check if this widget is visible within its parent window.
- Invisible widgets do not receive events except resize.
- */
- bool isVisible() const
- {
- return visible();
- }
-
- /**
- Set widget visible (or not) according to @a yesNo.
- */
- void setVisible(bool yesNo)
- {
- if (yesNo)
- show();
- else
- hide();
- }
-
- /**
- Get width.
- */
- int getWidth() const
- {
- return w();
- }
-
- /**
- Get height.
- */
- int getHeight() const
- {
- return h();
- }
-
- /**
- Set width.
- */
- void setWidth(int width)
- {
- resize(x(), y(), width, h());
- }
-
- /**
- Set height.
- */
- void setHeight(int height)
- {
- resize(x(), y(), w(), height);
- }
-
- /**
- Set size using @a width and @a height values.
- */
- void setSize(int width, int height)
- {
- resize(x(), y(), width, height);
- }
-
- /**
- Get absolute X.
- */
- int getAbsoluteX() const
- {
- return x();
- }
-
- /**
- Get absolute Y.
- */
- int getAbsoluteY() const
- {
- return y();
- }
-
- /**
- Set absolute X.
- */
- void setAbsoluteX(int x)
- {
- resize(x, y(), w(), h());
- }
-
- /**
- Set absolute Y.
- */
- void setAbsoluteY(int y)
- {
- resize(x(), y, w(), h());
- }
-
- /**
- Set absolute position using @a x and @a y values.
- */
- void setAbsolutePos(int x, int y)
- {
- resize(x, y, w(), h());
- }
-
- /**
- Get this widget's window application.
- Same as calling getParentWindow().getApp().
- */
- NtkApp& getParentApp() const noexcept
- {
- return fParent.getApp();
- }
-
- /**
- Get parent window, as passed in the constructor.
- */
- NtkWindow& getParentWindow() const noexcept
- {
- return fParent;
- }
-
- /**
- Check if this widget contains the point defined by @a x and @a y.
- */
- bool contains(int x, int y) const
- {
- return (x >= 0 && y >= 0 && x < w() && y < h());
- }
-
- /**
- Tell this widget's window to repaint itself.
- */
- void repaint()
- {
- redraw();
- }
-
-protected:
- /** @internal used for DGL compatibility. */
- void setNeedsFullViewport(bool) noexcept {}
- /** @internal used for DGL compatibility. */
- void setNeedsScaling(bool) noexcept {}
-
-private:
- NtkWindow& fParent;
-
- DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(NtkWidget)
-};
-
-// -----------------------------------------------------------------------
-
-END_NAMESPACE_DGL
-
-#endif // DGL_NTK_WIDGET_HPP_INCLUDED
diff --git a/libs/dgl/ntk/NtkWindow.hpp b/libs/dgl/ntk/NtkWindow.hpp
deleted file mode 100644
index 47e9f10..0000000
--- a/libs/dgl/ntk/NtkWindow.hpp
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * DISTRHO Plugin Framework (DPF)
- * Copyright (C) 2012-2014 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_NTK_WINDOW_HPP_INCLUDED
-#define DGL_NTK_WINDOW_HPP_INCLUDED
-
-#include "NtkApp.hpp"
-
-START_NAMESPACE_DGL
-
-class NtkWidget;
-
-// -----------------------------------------------------------------------
-
-class NtkWindow : public Fl_Double_Window
-{
-public:
- explicit NtkWindow(NtkApp& app)
- : Fl_Double_Window(100, 100),
- fApp(app),
- fIsVisible(false),
- fUsingEmbed(false),
- fParent(nullptr) {}
-
- explicit NtkWindow(NtkApp& app, NtkWindow& parent)
- : Fl_Double_Window(100, 100),
- fApp(app),
- fIsVisible(false),
- fUsingEmbed(false),
- fParent(&parent) {}
-
- explicit NtkWindow(NtkApp& app, intptr_t parentId)
- : Fl_Double_Window(100, 100),
- fApp(app),
- fIsVisible(parentId != 0),
- fUsingEmbed(parentId != 0),
- fParent(nullptr)
- {
- if (fUsingEmbed)
- {
- fl_embed(this, (Window)parentId);
- Fl_Double_Window::show();
- fApp.addWindow(this);
- }
- }
-
- ~NtkWindow() override
- {
- if (fUsingEmbed)
- {
- fApp.removeWindow(this);
- Fl_Double_Window::hide();
- }
- }
-
- void show() override
- {
- if (fUsingEmbed || fIsVisible)
- return;
-
- Fl_Double_Window::show();
- fApp.addWindow(this);
- fIsVisible = true;
-
- if (fParent != nullptr)
- setTransientWinId((intptr_t)fl_xid(fParent));
- }
-
- void hide() override
- {
- if (fUsingEmbed || ! fIsVisible)
- return;
-
- fIsVisible = false;
- fApp.removeWindow(this);
- Fl_Double_Window::hide();
- }
-
- void close()
- {
- hide();
- }
-
- bool isVisible() const
- {
- return visible();
- }
-
- void setVisible(bool yesNo)
- {
- if (yesNo)
- show();
- else
- hide();
- }
-
- bool isResizable() const
- {
- // TODO
- return false;
- }
-
- void setResizable(bool /*yesNo*/)
- {
- // TODO
- }
-
- int getWidth() const noexcept
- {
- return w();
- }
-
- int getHeight() const noexcept
- {
- return h();
- }
-
- void setSize(uint width, uint height)
- {
- resize(x(), y(), width, height);
- }
-
- void setTitle(const char* title)
- {
- label(title);
- }
-
- void setTransientWinId(intptr_t winId)
- {
- DISTRHO_SAFE_ASSERT_RETURN(winId != 0,);
-
-#ifdef DISTRHO_OS_LINUX
- DISTRHO_SAFE_ASSERT_RETURN(fl_display != nullptr,);
-
- const ::Window ourWindow(fl_xid(this));
- DISTRHO_SAFE_ASSERT_RETURN(ourWindow != 0,);
-
- XSetTransientForHint(fl_display, ourWindow, winId);
-#endif
- }
-
- NtkApp& getApp() const noexcept
- {
- return fApp;
- }
-
- intptr_t getWindowId() const
- {
- return (intptr_t)fl_xid(this);
- }
-
- void addIdleCallback(IdleCallback* const callback)
- {
- DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,);
-
- if (fIdleCallbacks.size() == 0)
- Fl::add_idle(_idleHandler, this);
-
- fIdleCallbacks.push_back(callback);
- }
-
- void removeIdleCallback(IdleCallback* const callback)
- {
- DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,);
-
- fIdleCallbacks.remove(callback);
-
- if (fIdleCallbacks.size() == 0)
- Fl::remove_idle(_idleHandler, this);
- }
-
-private:
- NtkApp& fApp;
- bool fIsVisible;
- bool fUsingEmbed;
-
- // transient parent, may be null
- NtkWindow* const fParent;
-
- std::list<IdleCallback*> fIdleCallbacks;
-
- friend class NtkWidget;
-
- static void _idleHandler(void* data)
- {
- NtkWindow* const self((NtkWindow*)data);
-
- for (std::list<IdleCallback*>::iterator it=self->fIdleCallbacks.begin(), ite=self->fIdleCallbacks.end(); it != ite; ++it)
- {
- IdleCallback* const idleCallback(*it);
- idleCallback->idleCallback();
- }
- }
-
- DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(NtkWindow)
-};
-
-// -----------------------------------------------------------------------
-
-END_NAMESPACE_DGL
-
-#endif // DGL_NTK_WINDOW_HPP_INCLUDED
diff --git a/libs/dgl/src/Color.cpp b/libs/dgl/src/Color.cpp
index 46b7ad3..57b8c07 100644
--- a/libs/dgl/src/Color.cpp
+++ b/libs/dgl/src/Color.cpp
@@ -22,51 +22,66 @@ START_NAMESPACE_DGL
// -----------------------------------------------------------------------
-Color::Color() noexcept
- : red(1.0f), green(1.0f), blue(1.0f), alpha(1.0f) {}
-
-Color::Color(const int r, const int g, const int b, const int 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) {}
-
-Color::Color(const float r, const float g, const float b, const float a) noexcept
- : red(r), green(g), blue(b), alpha(a) {}
-
-Color::Color(const Color& color) noexcept
- : red(color.red), green(color.green), blue(color.blue), alpha(color.alpha) {}
-
-Color::Color(const Color& color1, const Color& color2, const float u) noexcept
- : red(color1.red), green(color1.green), blue(color1.blue), alpha(color1.alpha)
+static void fixRange(float& value)
{
- interpolate(color2, u);
+ /**/ if (value < 0.0f)
+ value = 0.0f;
+ else if (value > 1.0f)
+ value = 1.0f;
}
-void Color::interpolate(const Color& other, const float u) noexcept
+static float getFixedRange(const float& value)
{
- const float u2 = (u < 0.0f) ? 0.0f : ((u > 1.0f) ? 1.0f : u);
- const float oneMinusU = 1.0f - u;
+ if (value <= 0.0f)
+ return 0.0f;
+ if (value >= 1.0f)
+ return 1.0f;
+ return value;
+}
- red = red * oneMinusU + other.red * u2;
- green = green * oneMinusU + other.green * u2;
- blue = blue * oneMinusU + other.blue * u2;
- alpha = alpha * oneMinusU + other.alpha * u2;
+static uchar getFixedRange2(const float& value)
+{
+ const float value2(getFixedRange(value)*255.0f);
+ if (value2 <= 0.0f)
+ return 0;
+ if (value2 >= 255.0f)
+ return 255;
+ return static_cast<uchar>(value2);
}
-Color Color::HSL(const float hue, const float saturation, const float lightness, const int alpha)
+// -----------------------------------------------------------------------
+
+Color::Color() noexcept
+ : red(1.0f),
+ green(1.0f),
+ blue(1.0f),
+ alpha(1.0f) {}
+
+Color::Color(int r, int g, int b, int 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)
{
- return nvgHSLA(hue, saturation, lightness, alpha);
+ fixBounds();
}
-Color::Color(const NVGcolor& c) noexcept
- : red(c.r), green(c.g), blue(c.b), alpha(c.a) {}
+Color::Color(float r, float g, float b, float a) noexcept
+ : red(r),
+ green(g),
+ blue(b),
+ alpha(a)
+{
+ fixBounds();
+}
-Color::operator NVGcolor() const noexcept
+Color::Color(const Color& color) noexcept
+ : red(color.red),
+ green(color.green),
+ blue(color.blue),
+ alpha(color.alpha)
{
- NVGcolor nc;
- nc.r = red;
- nc.g = green;
- nc.b = blue;
- nc.a = alpha;
- return nc;
+ fixBounds();
}
Color& Color::operator=(const Color& color) noexcept
@@ -75,17 +90,154 @@ Color& Color::operator=(const Color& color) noexcept
green = color.green;
blue = color.blue;
alpha = color.alpha;
+ fixBounds();
return *this;
}
+Color::Color(const Color& color1, const Color& color2, float u) noexcept
+ : red(color1.red),
+ green(color1.green),
+ blue(color1.blue),
+ alpha(color1.alpha)
+{
+ interpolate(color2, u);
+}
+
+Color Color::fromHSL(float hue, float saturation, float lightness, float alpha)
+{
+ return nvgHSLA(hue, saturation, lightness, static_cast<uchar>(getFixedRange(alpha)*255.0f));
+}
+
+Color Color::fromHTML(const char* rgb, float alpha)
+{
+ Color fallback;
+ DISTRHO_SAFE_ASSERT_RETURN(rgb != nullptr && rgb[0] != '\0', fallback);
+
+ if (rgb[0] == '#') ++rgb;
+ DISTRHO_SAFE_ASSERT_RETURN(rgb[0] != '\0', fallback);
+
+ std::size_t rgblen(std::strlen(rgb));
+ DISTRHO_SAFE_ASSERT_RETURN(rgblen == 3 || rgblen == 6, fallback);
+
+ char rgbtmp[3] = { '\0', '\0', '\0' };
+ int r, g, b;
+
+ if (rgblen == 3)
+ {
+ rgbtmp[0] = rgb[0];
+ r = static_cast<int>(std::strtol(rgbtmp, nullptr, 16));
+
+ rgbtmp[0] = rgb[1];
+ g = static_cast<int>(std::strtol(rgbtmp, nullptr, 16));
+
+ rgbtmp[0] = rgb[2];
+ b = static_cast<int>(std::strtol(rgbtmp, nullptr, 16));
+ }
+ else
+ {
+ rgbtmp[0] = rgb[0];
+ rgbtmp[1] = rgb[1];
+ r = static_cast<int>(std::strtol(rgbtmp, nullptr, 16));
+
+ rgbtmp[0] = rgb[2];
+ rgbtmp[1] = rgb[3];
+ g = static_cast<int>(std::strtol(rgbtmp, nullptr, 16));
+
+ rgbtmp[0] = rgb[4];
+ rgbtmp[1] = rgb[5];
+ b = static_cast<int>(std::strtol(rgbtmp, nullptr, 16));
+ }
+
+ return Color(r, g, b, static_cast<int>(getFixedRange(alpha)*255.0f));
+}
+
+void Color::interpolate(const Color& other, float u) noexcept
+{
+ fixRange(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;
+
+ fixBounds();
+}
+
+// -----------------------------------------------------------------------
+
+bool Color::isEqual(const Color& color, bool withAlpha) noexcept
+{
+ const uchar r1 = getFixedRange2(rgba[0]);
+ const uchar g1 = getFixedRange2(rgba[1]);
+ const uchar b1 = getFixedRange2(rgba[2]);
+ const uchar a1 = getFixedRange2(rgba[3]);
+
+ const uchar r2 = getFixedRange2(color.rgba[0]);
+ const uchar g2 = getFixedRange2(color.rgba[1]);
+ const uchar b2 = getFixedRange2(color.rgba[2]);
+ const uchar a2 = getFixedRange2(color.rgba[3]);
+
+ if (withAlpha)
+ return (r1 == r2 && g1 == g2 && b1 == b2 && a1 == a2);
+ else
+ return (r1 == r2 && g1 == g2 && b1 == b2);
+}
+
+bool Color::isNotEqual(const Color& color, bool withAlpha) noexcept
+{
+ const uchar r1 = getFixedRange2(rgba[0]);
+ const uchar g1 = getFixedRange2(rgba[1]);
+ const uchar b1 = getFixedRange2(rgba[2]);
+ const uchar a1 = getFixedRange2(rgba[3]);
+
+ const uchar r2 = getFixedRange2(color.rgba[0]);
+ const uchar g2 = getFixedRange2(color.rgba[1]);
+ const uchar b2 = getFixedRange2(color.rgba[2]);
+ const uchar a2 = getFixedRange2(color.rgba[3]);
+
+ if (withAlpha)
+ return (r1 != r2 || g1 != g2 || b1 != b2 || a1 != a2);
+ else
+ return (r1 != r2 || g1 != g2 || b1 != b2);
+}
+
bool Color::operator==(const Color& color) noexcept
{
- return (red == color.red && green == color.green && blue == color.blue && alpha == color.alpha);
+ return isEqual(color, true);
}
bool Color::operator!=(const Color& color) noexcept
{
- return (red != color.red || green != color.green || blue != color.blue || alpha != color.alpha);
+ return isNotEqual(color, true);
+}
+
+// -----------------------------------------------------------------------
+
+void Color::fixBounds() noexcept
+{
+ fixRange(red);
+ fixRange(green);
+ fixRange(blue);
+ fixRange(alpha);
+}
+
+// -----------------------------------------------------------------------
+
+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;
}
// -----------------------------------------------------------------------
diff --git a/libs/dgl/src/Geometry.cpp b/libs/dgl/src/Geometry.cpp
index a7565fa..6964816 100644
--- a/libs/dgl/src/Geometry.cpp
+++ b/libs/dgl/src/Geometry.cpp
@@ -99,6 +99,12 @@ bool Point<T>::isZero() const noexcept
}
template<typename T>
+bool Point<T>::isNotZero() const noexcept
+{
+ return fX != 0 || fY != 0;
+}
+
+template<typename T>
Point<T> Point<T>::operator+(const Point<T>& pos) noexcept
{
return Point<T>(fX+pos.fX, fY+pos.fY);
@@ -203,17 +209,17 @@ void Size<T>::setSize(const Size<T>& size) noexcept
}
template<typename T>
-void Size<T>::growBy(const T& multiplier) noexcept
+void Size<T>::growBy(double multiplier) noexcept
{
- fWidth = static_cast<T>(fWidth*multiplier);
- fHeight = static_cast<T>(fHeight*multiplier);
+ fWidth = static_cast<T>(static_cast<double>(fWidth)*multiplier);
+ fHeight = static_cast<T>(static_cast<double>(fHeight)*multiplier);
}
template<typename T>
-void Size<T>::shrinkBy(const T& divider) noexcept
+void Size<T>::shrinkBy(double divider) noexcept
{
- fWidth = static_cast<T>(fWidth/divider);
- fHeight = static_cast<T>(fHeight/divider);
+ fWidth = static_cast<T>(static_cast<double>(fWidth)/divider);
+ fHeight = static_cast<T>(static_cast<double>(fHeight)/divider);
}
template<typename T>
@@ -228,6 +234,17 @@ bool Size<T>::isNotNull() const noexcept
return fWidth != 0 || fHeight != 0;
}
+template<typename T>
+bool Size<T>::isValid() const noexcept
+{
+ return fWidth > 1 && fHeight > 1;
+}
+
+template<typename T>
+bool Size<T>::isInvalid() const noexcept
+{
+ return fWidth <= 0 || fHeight <= 0;
+}
template<typename T>
Size<T> Size<T>::operator+(const Size<T>& size) noexcept
@@ -266,18 +283,18 @@ Size<T>& Size<T>::operator-=(const Size<T>& size) noexcept
}
template<typename T>
-Size<T>& Size<T>::operator*=(const T& m) noexcept
+Size<T>& Size<T>::operator*=(double m) noexcept
{
- fWidth = static_cast<T>(fWidth*m);
- fHeight = static_cast<T>(fHeight*m);
+ fWidth = static_cast<T>(static_cast<double>(fWidth)*m);
+ fHeight = static_cast<T>(static_cast<double>(fHeight)*m);
return *this;
}
template<typename T>
-Size<T>& Size<T>::operator/=(const T& d) noexcept
+Size<T>& Size<T>::operator/=(double d) noexcept
{
- fWidth = static_cast<T>(fWidth/d);
- fHeight = static_cast<T>(fHeight/d);
+ fWidth = static_cast<T>(static_cast<double>(fWidth)/d);
+ fHeight = static_cast<T>(static_cast<double>(fHeight)/d);
return *this;
}
@@ -427,17 +444,31 @@ void Line<T>::moveBy(const Point<T>& pos) noexcept
template<typename T>
void Line<T>::draw()
{
+ DISTRHO_SAFE_ASSERT_RETURN(fPosStart != fPosEnd,);
+
glBegin(GL_LINES);
{
- glVertex2i(fPosStart.fX, fPosStart.fY);
- glVertex2i(fPosEnd.fX, fPosEnd.fY);
+ glVertex2d(fPosStart.fX, fPosStart.fY);
+ glVertex2d(fPosEnd.fX, fPosEnd.fY);
}
glEnd();
}
template<typename T>
+bool Line<T>::isNull() const noexcept
+{
+ return fPosStart == fPosEnd;
+}
+
+template<typename T>
+bool Line<T>::isNotNull() const noexcept
+{
+ return fPosStart != fPosEnd;
+}
+
+template<typename T>
Line<T>& Line<T>::operator=(const Line<T>& line) noexcept
{
fPosStart = line.fPosStart;
@@ -610,24 +641,23 @@ Circle<T>& Circle<T>::operator=(const Circle<T>& cir) noexcept
template<typename T>
bool Circle<T>::operator==(const Circle<T>& cir) const noexcept
{
- return (fPos == cir.fPos && fSize == cir.fSize && fNumSegments == cir.fNumSegments);
+ return (fPos == cir.fPos && d_isEqual(fSize, cir.fSize) && fNumSegments == cir.fNumSegments);
}
template<typename T>
bool Circle<T>::operator!=(const Circle<T>& cir) const noexcept
{
- return (fPos != cir.fPos || fSize != cir.fSize || fNumSegments != cir.fNumSegments);
+ return (fPos != cir.fPos || d_isNotEqual(fSize, cir.fSize) || fNumSegments != cir.fNumSegments);
}
template<typename T>
-void Circle<T>::_draw(const bool isOutline)
+void Circle<T>::_draw(const bool outline)
{
- if (fNumSegments < 3 || fSize <= 0.0f)
- return;
+ DISTRHO_SAFE_ASSERT_RETURN(fNumSegments >= 3 && fSize > 0.0f,);
- float t, x = fSize, y = 0;
+ float t, x = fSize, y = 0.0f;
- glBegin(isOutline ? GL_LINE_LOOP : GL_POLYGON);
+ glBegin(outline ? GL_LINE_LOOP : GL_POLYGON);
for (uint i=0; i<fNumSegments; ++i)
{
@@ -681,6 +711,30 @@ void Triangle<T>::drawOutline()
}
template<typename T>
+bool Triangle<T>::isNull() const noexcept
+{
+ return fPos1 == fPos2 && fPos1 == fPos3;
+}
+
+template<typename T>
+bool Triangle<T>::isNotNull() const noexcept
+{
+ return fPos1 != fPos2 || fPos1 != fPos3;
+}
+
+template<typename T>
+bool Triangle<T>::isValid() const noexcept
+{
+ return fPos1 != fPos2 && fPos1 != fPos3;
+}
+
+template<typename T>
+bool Triangle<T>::isInvalid() const noexcept
+{
+ return fPos1 == fPos2 || fPos1 == fPos3;
+}
+
+template<typename T>
Triangle<T>& Triangle<T>::operator=(const Triangle<T>& tri) noexcept
{
fPos1 = tri.fPos1;
@@ -702,14 +756,16 @@ bool Triangle<T>::operator!=(const Triangle<T>& tri) const noexcept
}
template<typename T>
-void Triangle<T>::_draw(const bool isOutline)
+void Triangle<T>::_draw(const bool outline)
{
- glBegin(isOutline ? GL_LINE_LOOP : GL_TRIANGLES);
+ DISTRHO_SAFE_ASSERT_RETURN(fPos1 != fPos2 && fPos1 != fPos3,);
+
+ glBegin(outline ? GL_LINE_LOOP : GL_TRIANGLES);
{
- glVertex2i(fPos1.fX, fPos1.fY);
- glVertex2i(fPos2.fX, fPos2.fY);
- glVertex2i(fPos3.fX, fPos3.fY);
+ glVertex2d(fPos1.fX, fPos1.fY);
+ glVertex2d(fPos2.fX, fPos2.fY);
+ glVertex2d(fPos3.fX, fPos3.fY);
}
glEnd();
@@ -847,13 +903,13 @@ void Rectangle<T>::setSize(const Size<T>& size) noexcept
}
template<typename T>
-void Rectangle<T>::growBy(const T& multiplier) noexcept
+void Rectangle<T>::growBy(double multiplier) noexcept
{
fSize.growBy(multiplier);
}
template<typename T>
-void Rectangle<T>::shrinkBy(const T& divider) noexcept
+void Rectangle<T>::shrinkBy(double divider) noexcept
{
fSize.shrinkBy(divider);
}
@@ -917,14 +973,14 @@ Rectangle<T>& Rectangle<T>::operator=(const Rectangle<T>& rect) noexcept
}
template<typename T>
-Rectangle<T>& Rectangle<T>::operator*=(const T& m) noexcept
+Rectangle<T>& Rectangle<T>::operator*=(double m) noexcept
{
fSize *= m;
return *this;
}
template<typename T>
-Rectangle<T>& Rectangle<T>::operator/=(const T& d) noexcept
+Rectangle<T>& Rectangle<T>::operator/=(double d) noexcept
{
fSize /= d;
return *this;
@@ -943,22 +999,24 @@ bool Rectangle<T>::operator!=(const Rectangle<T>& rect) const noexcept
}
template<typename T>
-void Rectangle<T>::_draw(const bool isOutline)
+void Rectangle<T>::_draw(const bool outline)
{
- glBegin(isOutline ? GL_LINE_LOOP : GL_QUADS);
+ DISTRHO_SAFE_ASSERT_RETURN(fSize.isValid(),);
+
+ glBegin(outline ? GL_LINE_LOOP : GL_QUADS);
{
glTexCoord2f(0.0f, 0.0f);
- glVertex2i(fPos.fX, fPos.fY);
+ glVertex2d(fPos.fX, fPos.fY);
glTexCoord2f(1.0f, 0.0f);
- glVertex2i(fPos.fX+fSize.fWidth, fPos.fY);
+ glVertex2d(fPos.fX+fSize.fWidth, fPos.fY);
glTexCoord2f(1.0f, 1.0f);
- glVertex2i(fPos.fX+fSize.fWidth, fPos.fY+fSize.fHeight);
+ glVertex2d(fPos.fX+fSize.fWidth, fPos.fY+fSize.fHeight);
glTexCoord2f(0.0f, 1.0f);
- glVertex2i(fPos.fX, fPos.fY+fSize.fHeight);
+ glVertex2d(fPos.fX, fPos.fY+fSize.fHeight);
}
glEnd();
diff --git a/libs/dgl/src/Image.cpp b/libs/dgl/src/Image.cpp
index 55ee0af..74dceed 100644
--- a/libs/dgl/src/Image.cpp
+++ b/libs/dgl/src/Image.cpp
@@ -152,12 +152,14 @@ void Image::drawAt(const Point<int>& pos)
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fSize.getWidth(), fSize.getHeight(), 0, fFormat, fType, fRawData);
+ 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, fSize.getWidth(), fSize.getHeight()).draw();
+ Rectangle<int>(pos, static_cast<int>(fSize.getWidth()), static_cast<int>(fSize.getHeight())).draw();
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
diff --git a/libs/dgl/src/ImageAboutWindow.cpp b/libs/dgl/src/ImageAboutWindow.cpp
index a12aa57..9e90f29 100644
--- a/libs/dgl/src/ImageAboutWindow.cpp
+++ b/libs/dgl/src/ImageAboutWindow.cpp
@@ -23,8 +23,8 @@ START_NAMESPACE_DGL
ImageAboutWindow::ImageAboutWindow(Window& parent, const Image& image)
: Window(parent.getApp(), parent),
Widget((Window&)*this),
- fImgBackground(image),
- leakDetector_ImageAboutWindow()
+ fImgBackground(image)/*,
+ leakDetector_ImageAboutWindow()*/
{
Window::setResizable(false);
Window::setSize(static_cast<uint>(image.getWidth()), static_cast<uint>(image.getHeight()));
@@ -34,8 +34,8 @@ ImageAboutWindow::ImageAboutWindow(Window& parent, const Image& image)
ImageAboutWindow::ImageAboutWindow(Widget* widget, const Image& image)
: Window(widget->getParentApp(), widget->getParentWindow()),
Widget((Window&)*this),
- fImgBackground(image),
- leakDetector_ImageAboutWindow()
+ fImgBackground(image)/*,
+ leakDetector_ImageAboutWindow()*/
{
Window::setResizable(false);
Window::setSize(static_cast<uint>(image.getWidth()), static_cast<uint>(image.getHeight()));
diff --git a/libs/dgl/src/ImageKnob.cpp b/libs/dgl/src/ImageKnob.cpp
index 0fc8c69..57dae6a 100644
--- a/libs/dgl/src/ImageKnob.cpp
+++ b/libs/dgl/src/ImageKnob.cpp
@@ -22,10 +22,9 @@ START_NAMESPACE_DGL
// -----------------------------------------------------------------------
-ImageKnob::ImageKnob(Window& parent, const Image& image, Orientation orientation, int id) noexcept
+ImageKnob::ImageKnob(Window& parent, const Image& image, Orientation orientation) noexcept
: Widget(parent),
fImage(image),
- fId(id),
fMinimum(0.0f),
fMaximum(1.0f),
fStep(0.0f),
@@ -43,19 +42,17 @@ ImageKnob::ImageKnob(Window& parent, const Image& image, Orientation orientation
fIsImgVertical(image.getHeight() > image.getWidth()),
fImgLayerSize(fIsImgVertical ? image.getWidth() : image.getHeight()),
fImgLayerCount(fIsImgVertical ? image.getHeight()/fImgLayerSize : image.getWidth()/fImgLayerSize),
- fKnobArea(0, 0, fImgLayerSize, fImgLayerSize),
- fTextureId(0),
fIsReady(false),
+ fTextureId(0),
leakDetector_ImageKnob()
{
glGenTextures(1, &fTextureId);
setSize(fImgLayerSize, fImgLayerSize);
}
-ImageKnob::ImageKnob(Widget* widget, const Image& image, Orientation orientation, int id) noexcept
+ImageKnob::ImageKnob(Widget* widget, const Image& image, Orientation orientation) noexcept
: Widget(widget->getParentWindow()),
fImage(image),
- fId(id),
fMinimum(0.0f),
fMaximum(1.0f),
fStep(0.0f),
@@ -73,9 +70,8 @@ ImageKnob::ImageKnob(Widget* widget, const Image& image, Orientation orientation
fIsImgVertical(image.getHeight() > image.getWidth()),
fImgLayerSize(fIsImgVertical ? image.getWidth() : image.getHeight()),
fImgLayerCount(fIsImgVertical ? image.getHeight()/fImgLayerSize : image.getWidth()/fImgLayerSize),
- fKnobArea(0, 0, fImgLayerSize, fImgLayerSize),
- fTextureId(0),
fIsReady(false),
+ fTextureId(0),
leakDetector_ImageKnob()
{
glGenTextures(1, &fTextureId);
@@ -85,7 +81,6 @@ ImageKnob::ImageKnob(Widget* widget, const Image& image, Orientation orientation
ImageKnob::ImageKnob(const ImageKnob& imageKnob)
: Widget(imageKnob.getParentWindow()),
fImage(imageKnob.fImage),
- fId(imageKnob.fId),
fMinimum(imageKnob.fMinimum),
fMaximum(imageKnob.fMaximum),
fStep(imageKnob.fStep),
@@ -103,9 +98,8 @@ ImageKnob::ImageKnob(const ImageKnob& imageKnob)
fIsImgVertical(imageKnob.fIsImgVertical),
fImgLayerSize(imageKnob.fImgLayerSize),
fImgLayerCount(imageKnob.fImgLayerCount),
- fKnobArea(imageKnob.fKnobArea),
- fTextureId(0),
fIsReady(false),
+ fTextureId(0),
leakDetector_ImageKnob()
{
glGenTextures(1, &fTextureId);
@@ -115,7 +109,6 @@ ImageKnob::ImageKnob(const ImageKnob& imageKnob)
ImageKnob& ImageKnob::operator=(const ImageKnob& imageKnob)
{
fImage = imageKnob.fImage;
- fId = imageKnob.fId;
fMinimum = imageKnob.fMinimum;
fMaximum = imageKnob.fMaximum;
fStep = imageKnob.fStep;
@@ -133,7 +126,6 @@ ImageKnob& ImageKnob::operator=(const ImageKnob& imageKnob)
fIsImgVertical = imageKnob.fIsImgVertical;
fImgLayerSize = imageKnob.fImgLayerSize;
fImgLayerCount = imageKnob.fImgLayerCount;
- fKnobArea = imageKnob.fKnobArea;
fIsReady = false;
if (fTextureId != 0)
@@ -157,16 +149,6 @@ ImageKnob::~ImageKnob()
}
}
-int ImageKnob::getId() const noexcept
-{
- return fId;
-}
-
-void ImageKnob::setId(int id) noexcept
-{
- fId = id;
-}
-
float ImageKnob::getValue() const noexcept
{
return fValue;
@@ -220,12 +202,12 @@ void ImageKnob::setStep(float step) noexcept
// NOTE: value is assumed to be scaled if using log
void ImageKnob::setValue(float value, bool sendCallback) noexcept
{
- if (fValue == value)
+ if (d_isEqual(fValue, value))
return;
fValue = value;
- if (fStep == 0.0f)
+ if (d_isZero(fStep))
fValueTmp = value;
if (fRotationAngle == 0)
@@ -288,36 +270,44 @@ void ImageKnob::onDisplay()
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- int imageDataOffset = 0;
+ uint imageDataOffset = 0;
if (fRotationAngle == 0)
{
- int layerDataSize = fImgLayerSize * fImgLayerSize * ((fImage.getFormat() == GL_BGRA || fImage.getFormat() == GL_RGBA) ? 4 : 3);
- imageDataOffset = layerDataSize * int(normValue * float(fImgLayerCount-1));
+ DISTRHO_SAFE_ASSERT_RETURN(fImgLayerCount > 0,);
+ DISTRHO_SAFE_ASSERT_RETURN(normValue >= 0.0f,);
+
+ const uint layerDataSize = fImgLayerSize * fImgLayerSize * ((fImage.getFormat() == GL_BGRA || fImage.getFormat() == GL_RGBA) ? 4 : 3);
+ /* */ imageDataOffset = layerDataSize * uint(normValue * float(fImgLayerCount-1));
}
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getWidth(), getHeight(), 0, fImage.getFormat(), fImage.getType(), fImage.getRawData() + imageDataOffset);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
+ static_cast<GLsizei>(getWidth()), static_cast<GLsizei>(getHeight()), 0,
+ fImage.getFormat(), fImage.getType(), fImage.getRawData() + imageDataOffset);
fIsReady = true;
}
+ const int w = static_cast<int>(getWidth());
+ const int h = static_cast<int>(getHeight());
+
if (fRotationAngle != 0)
{
glPushMatrix();
- const GLint w2 = getWidth()/2;
- const GLint h2 = getHeight()/2;
+ const int w2 = w/2;
+ const int h2 = h/2;
glTranslatef(static_cast<float>(w2), static_cast<float>(h2), 0.0f);
glRotatef(normValue*static_cast<float>(fRotationAngle), 0.0f, 0.0f, 1.0f);
- Rectangle<int>(-w2, -h2, getWidth(), getHeight()).draw();
+ Rectangle<int>(-w2, -h2, w, h).draw();
glPopMatrix();
}
else
{
- Rectangle<int>(0, 0, getWidth(), getHeight()).draw();
+ Rectangle<int>(0, 0, w, h).draw();
}
glBindTexture(GL_TEXTURE_2D, 0);
@@ -403,7 +393,7 @@ bool ImageKnob::onMotion(const MotionEvent& ev)
{
fValueTmp = value = fMaximum;
}
- else if (fStep != 0.0f)
+ else if (d_isNotZero(fStep))
{
fValueTmp = value;
const float rest = std::fmod(value, fStep);
@@ -437,7 +427,7 @@ bool ImageKnob::onScroll(const ScrollEvent& ev)
{
fValueTmp = value = fMaximum;
}
- else if (fStep != 0.0f)
+ else if (d_isNotZero(fStep))
{
fValueTmp = value;
const float rest = std::fmod(value, fStep);
diff --git a/libs/dgl/src/ImageSlider.cpp b/libs/dgl/src/ImageSlider.cpp
index b9ae84e..12f7b37 100644
--- a/libs/dgl/src/ImageSlider.cpp
+++ b/libs/dgl/src/ImageSlider.cpp
@@ -22,10 +22,9 @@ START_NAMESPACE_DGL
// -----------------------------------------------------------------------
-ImageSlider::ImageSlider(Window& parent, const Image& image, int id) noexcept
+ImageSlider::ImageSlider(Window& parent, const Image& image) noexcept
: Widget(parent),
fImage(image),
- fId(id),
fMinimum(0.0f),
fMaximum(1.0f),
fStep(0.0f),
@@ -44,10 +43,9 @@ ImageSlider::ImageSlider(Window& parent, const Image& image, int id) noexcept
Widget::setNeedsFullViewport(true);
}
-ImageSlider::ImageSlider(Widget* widget, const Image& image, int id) noexcept
+ImageSlider::ImageSlider(Widget* widget, const Image& image) noexcept
: Widget(widget->getParentWindow()),
fImage(image),
- fId(id),
fMinimum(0.0f),
fMaximum(1.0f),
fStep(0.0f),
@@ -69,7 +67,6 @@ ImageSlider::ImageSlider(Widget* widget, const Image& image, int id) noexcept
ImageSlider::ImageSlider(const ImageSlider& imageSlider) noexcept
: Widget(imageSlider.getParentWindow()),
fImage(imageSlider.fImage),
- fId(imageSlider.fId),
fMinimum(imageSlider.fMinimum),
fMaximum(imageSlider.fMaximum),
fStep(imageSlider.fStep),
@@ -91,7 +88,6 @@ ImageSlider::ImageSlider(const ImageSlider& imageSlider) noexcept
ImageSlider& ImageSlider::operator=(const ImageSlider& imageSlider) noexcept
{
fImage = imageSlider.fImage;
- fId = imageSlider.fId;
fMinimum = imageSlider.fMinimum;
fMaximum = imageSlider.fMaximum;
fStep = imageSlider.fStep;
@@ -109,16 +105,6 @@ ImageSlider& ImageSlider::operator=(const ImageSlider& imageSlider) noexcept
return *this;
}
-int ImageSlider::getId() const noexcept
-{
- return fId;
-}
-
-void ImageSlider::setId(int id) noexcept
-{
- fId = id;
-}
-
float ImageSlider::getValue() const noexcept
{
return fValue;
@@ -193,12 +179,12 @@ void ImageSlider::setStep(float step) noexcept
void ImageSlider::setValue(float value, bool sendCallback) noexcept
{
- if (fValue == value)
+ if (d_isEqual(fValue, value))
return;
fValue = value;
- if (fStep == 0.0f)
+ if (d_isZero(fStep))
fValueTmp = value;
repaint();
@@ -224,7 +210,7 @@ void ImageSlider::onDisplay()
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
#endif
- float normValue = (fValue - fMinimum) / (fMaximum - fMinimum);
+ const float normValue = (fValue - fMinimum) / (fMaximum - fMinimum);
int x, y;
@@ -292,7 +278,7 @@ bool ImageSlider::onMouse(const MouseEvent& ev)
{
fValueTmp = value = fMaximum;
}
- else if (fStep != 0.0f)
+ else if (d_isNotZero(fStep))
{
fValueTmp = value;
const float rest = std::fmod(value, fStep);
@@ -361,7 +347,7 @@ bool ImageSlider::onMotion(const MotionEvent& ev)
{
fValueTmp = value = fMaximum;
}
- else if (fStep != 0.0f)
+ else if (d_isNotZero(fStep))
{
fValueTmp = value;
const float rest = std::fmod(value, fStep);
@@ -395,16 +381,16 @@ void ImageSlider::_recheckArea() noexcept
// horizontal
fSliderArea = Rectangle<int>(fStartPos.getX(),
fStartPos.getY(),
- fEndPos.getX() + fImage.getWidth() - fStartPos.getX(),
- fImage.getHeight());
+ fEndPos.getX() + static_cast<int>(fImage.getWidth()) - fStartPos.getX(),
+ static_cast<int>(fImage.getHeight()));
}
else
{
// vertical
fSliderArea = Rectangle<int>(fStartPos.getX(),
fStartPos.getY(),
- fImage.getWidth(),
- fEndPos.getY() + fImage.getHeight() - fStartPos.getY());
+ static_cast<int>(fImage.getWidth()),
+ fEndPos.getY() + static_cast<int>(fImage.getHeight()) - fStartPos.getY());
}
}
diff --git a/libs/dgl/src/ImageSwitch.cpp b/libs/dgl/src/ImageSwitch.cpp
index bd99786..37b3e56 100644
--- a/libs/dgl/src/ImageSwitch.cpp
+++ b/libs/dgl/src/ImageSwitch.cpp
@@ -20,12 +20,11 @@ START_NAMESPACE_DGL
// -----------------------------------------------------------------------
-ImageSwitch::ImageSwitch(Window& parent, const Image& imageNormal, const Image& imageDown, int id) noexcept
+ImageSwitch::ImageSwitch(Window& parent, const Image& imageNormal, const Image& imageDown) noexcept
: Widget(parent),
fImageNormal(imageNormal),
fImageDown(imageDown),
fIsDown(false),
- fId(id),
fCallback(nullptr),
leakDetector_ImageSwitch()
{
@@ -34,12 +33,11 @@ ImageSwitch::ImageSwitch(Window& parent, const Image& imageNormal, const Image&
setSize(fImageNormal.getSize());
}
-ImageSwitch::ImageSwitch(Widget* widget, const Image& imageNormal, const Image& imageDown, int id) noexcept
+ImageSwitch::ImageSwitch(Widget* widget, const Image& imageNormal, const Image& imageDown) noexcept
: Widget(widget->getParentWindow()),
fImageNormal(imageNormal),
fImageDown(imageDown),
fIsDown(false),
- fId(id),
fCallback(nullptr),
leakDetector_ImageSwitch()
{
@@ -53,7 +51,6 @@ ImageSwitch::ImageSwitch(const ImageSwitch& imageSwitch) noexcept
fImageNormal(imageSwitch.fImageNormal),
fImageDown(imageSwitch.fImageDown),
fIsDown(imageSwitch.fIsDown),
- fId(imageSwitch.fId),
fCallback(imageSwitch.fCallback),
leakDetector_ImageSwitch()
{
@@ -67,7 +64,6 @@ ImageSwitch& ImageSwitch::operator=(const ImageSwitch& imageSwitch) noexcept
fImageNormal = imageSwitch.fImageNormal;
fImageDown = imageSwitch.fImageDown;
fIsDown = imageSwitch.fIsDown;
- fId = imageSwitch.fId;
fCallback = imageSwitch.fCallback;
DISTRHO_SAFE_ASSERT(fImageNormal.getSize() == fImageDown.getSize());
@@ -77,16 +73,6 @@ ImageSwitch& ImageSwitch::operator=(const ImageSwitch& imageSwitch) noexcept
return *this;
}
-int ImageSwitch::getId() const noexcept
-{
- return fId;
-}
-
-void ImageSwitch::setId(int id) noexcept
-{
- fId = id;;
-}
-
bool ImageSwitch::isDown() const noexcept
{
return fIsDown;
diff --git a/libs/dgl/src/NanoVG.cpp b/libs/dgl/src/NanoVG.cpp
index bb7b9f1..a4c357b 100644
--- a/libs/dgl/src/NanoVG.cpp
+++ b/libs/dgl/src/NanoVG.cpp
@@ -18,8 +18,32 @@
#include "../Window.hpp"
// -----------------------------------------------------------------------
+// Ignore some warnings if debugging
+
+#if 0 //def DEBUG
+# define NANOVG_GL3 0
+# define NANOVG_GLES2 0
+# define NANOVG_GLES3 0
+# define NANOVG_GL_USE_UNIFORMBUFFER 0
+# if defined(__clang__)
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Weverything"
+# elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wall"
+# pragma GCC diagnostic ignored "-Wextra"
+# pragma GCC diagnostic ignored "-Wconversion"
+# pragma GCC diagnostic ignored "-Weffc++"
+# pragma GCC diagnostic ignored "-Wsign-conversion"
+# pragma GCC diagnostic ignored "-Wundef"
+# pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
+# endif
+#endif
+
+// -----------------------------------------------------------------------
+// Include NanoVG OpenGL implementation
-#define NANOVG_GL2_IMPLEMENTATION
+#define NANOVG_GL2_IMPLEMENTATION 1
#include "nanovg/nanovg_gl.h"
#if defined(NANOVG_GL2)
@@ -36,6 +60,19 @@
# define nvgDeleteGL nvgDeleteGLES3
#endif
+// -----------------------------------------------------------------------
+// Restore normal state if debugging
+
+#if 0//def DEBUG
+# if defined(__clang__)
+# pragma clang diagnostic pop
+# elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
+# pragma GCC diagnostic pop
+# endif
+#endif
+
+// -----------------------------------------------------------------------
+
START_NAMESPACE_DGL
// -----------------------------------------------------------------------
@@ -75,7 +112,8 @@ NanoVG::Paint::operator NVGpaint() const noexcept
NanoImage::NanoImage(NVGcontext* const context, const int imageId) noexcept
: fContext(context),
fImageId(imageId),
- fSize()
+ fSize(),
+ leakDetector_NanoImage()
{
_updateSize();
}
@@ -114,7 +152,7 @@ void NanoImage::_updateSize()
if (h < 0) h = 0;
}
- fSize.setSize(w, h);
+ fSize.setSize(static_cast<uint>(w), static_cast<uint>(h));
}
// -----------------------------------------------------------------------
@@ -122,14 +160,16 @@ void NanoImage::_updateSize()
NanoVG::NanoVG()
: fContext(nvgCreateGL(512, 512, NVG_ANTIALIAS)),
- fInFrame(false)
+ fInFrame(false),
+ leakDetector_NanoVG()
{
DISTRHO_SAFE_ASSERT_RETURN(fContext != nullptr,);
}
NanoVG::NanoVG(const int textAtlasWidth, const int textAtlasHeight)
: fContext(nvgCreateGL(textAtlasWidth, textAtlasHeight, NVG_ANTIALIAS)),
- fInFrame(false)
+ fInFrame(false),
+ leakDetector_NanoVG()
{
DISTRHO_SAFE_ASSERT_RETURN(fContext != nullptr,);
}
@@ -151,7 +191,7 @@ void NanoVG::beginFrame(const uint width, const uint height, const float scaleFa
DISTRHO_SAFE_ASSERT_RETURN(! fInFrame,);
fInFrame = true;
- nvgBeginFrame(fContext, width, height, scaleFactor, static_cast<NVGalpha>(alpha));
+ nvgBeginFrame(fContext, static_cast<int>(width), static_cast<int>(height), scaleFactor, static_cast<NVGalpha>(alpha));
}
void NanoVG::beginFrame(Widget* const widget)
@@ -163,7 +203,7 @@ void NanoVG::beginFrame(Widget* const widget)
Window& window(widget->getParentWindow());
fInFrame = true;
- nvgBeginFrame(fContext, window.getWidth(), window.getHeight(), 1.0f, NVG_PREMULTIPLIED_ALPHA);
+ nvgBeginFrame(fContext, static_cast<int>(window.getWidth()), static_cast<int>(window.getHeight()), 1.0f, NVG_PREMULTIPLIED_ALPHA);
}
void NanoVG::endFrame()
@@ -209,7 +249,17 @@ void NanoVG::strokeColor(const Color& color)
void NanoVG::strokeColor(const int red, const int green, const int blue, const int alpha)
{
if (fContext != nullptr)
- nvgStrokeColor(fContext, nvgRGBA(red, green, blue, alpha));
+ {
+ DISTRHO_SAFE_ASSERT_RETURN(red >= 0 && red <= 255,);
+ DISTRHO_SAFE_ASSERT_RETURN(green >= 0 && green <= 255,);
+ DISTRHO_SAFE_ASSERT_RETURN(blue >= 0 && blue <= 255,);
+ DISTRHO_SAFE_ASSERT_RETURN(alpha >= 0 && alpha <= 255,);
+
+ nvgStrokeColor(fContext, nvgRGBA(static_cast<uchar>(red),
+ static_cast<uchar>(green),
+ static_cast<uchar>(blue),
+ static_cast<uchar>(alpha)));
+ }
}
void NanoVG::strokeColor(const float red, const float green, const float blue, const float alpha)
@@ -233,7 +283,17 @@ void NanoVG::fillColor(const Color& color)
void NanoVG::fillColor(const int red, const int green, const int blue, const int alpha)
{
if (fContext != nullptr)
- nvgFillColor(fContext, nvgRGBA(red, green, blue, alpha));
+ {
+ DISTRHO_SAFE_ASSERT_RETURN(red >= 0 && red <= 255,);
+ DISTRHO_SAFE_ASSERT_RETURN(green >= 0 && green <= 255,);
+ DISTRHO_SAFE_ASSERT_RETURN(blue >= 0 && blue <= 255,);
+ DISTRHO_SAFE_ASSERT_RETURN(alpha >= 0 && alpha <= 255,);
+
+ nvgFillColor(fContext, nvgRGBA(static_cast<uchar>(red),
+ static_cast<uchar>(green),
+ static_cast<uchar>(blue),
+ static_cast<uchar>(alpha)));
+ }
}
void NanoVG::fillColor(const float red, const float green, const float blue, const float alpha)
@@ -427,7 +487,7 @@ NanoImage* NanoVG::createImageRGBA(uint w, uint h, const uchar* data)
if (fContext == nullptr) return nullptr;
DISTRHO_SAFE_ASSERT_RETURN(data != nullptr, nullptr);
- if (const int imageId = nvgCreateImageRGBA(fContext, w, h, data))
+ if (const int imageId = nvgCreateImageRGBA(fContext, static_cast<int>(w), static_cast<int>(h), data))
return new NanoImage(fContext, imageId);
return nullptr;
@@ -576,13 +636,13 @@ NanoVG::FontId NanoVG::createFont(const char* name, const char* filename)
return nvgCreateFont(fContext, name, filename);
}
-NanoVG::FontId NanoVG::createFontMem(const char* name, uchar* data, int ndata, bool freeData)
+NanoVG::FontId NanoVG::createFontMem(const char* name, const uchar* data, int ndata, bool freeData)
{
if (fContext == nullptr) return -1;
DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', -1);
DISTRHO_SAFE_ASSERT_RETURN(data != nullptr, -1);
- return nvgCreateFontMem(fContext, name, data, ndata, freeData);
+ return nvgCreateFontMem(fContext, name, const_cast<uchar*>(data), ndata, freeData);
}
NanoVG::FontId NanoVG::findFont(const char* name)
diff --git a/libs/dgl/src/Widget.cpp b/libs/dgl/src/Widget.cpp
index 6013ea2..d2898b3 100644
--- a/libs/dgl/src/Widget.cpp
+++ b/libs/dgl/src/Widget.cpp
@@ -27,6 +27,7 @@ Widget::Widget(Window& parent)
fNeedsFullViewport(false),
fNeedsScaling(false),
fVisible(true),
+ fId(0),
fAbsolutePos(0, 0),
fSize(0, 0),
leakDetector_Widget()
@@ -200,6 +201,16 @@ void Widget::repaint() noexcept
fParent.repaint();
}
+uint Widget::getId() const noexcept
+{
+ return fId;
+}
+
+void Widget::setId(uint id) noexcept
+{
+ fId = id;
+}
+
bool Widget::onKeyboard(const KeyboardEvent&)
{
return false;
diff --git a/libs/dgl/src/Window.cpp b/libs/dgl/src/Window.cpp
index e31b75c..c901ff2 100644
--- a/libs/dgl/src/Window.cpp
+++ b/libs/dgl/src/Window.cpp
@@ -15,11 +15,12 @@
*/
// we need this for now
-#define PUGL_GRAB_FOCUS 1
+//#define PUGL_GRAB_FOCUS 1
#include "AppPrivateData.hpp"
#include "../Widget.hpp"
#include "../Window.hpp"
+#include "../../distrho/extra/d_string.hpp"
#include "pugl/pugl.h"
@@ -62,13 +63,15 @@ struct Window::PrivateData {
PrivateData(App& app, Window* const self)
: fApp(app),
fSelf(self),
- fView(puglInit(nullptr, nullptr)),
+ fView(puglInit()),
fFirstInit(true),
fVisible(false),
fResizable(true),
fUsingEmbed(false),
fWidth(1),
fHeight(1),
+ fTitle(nullptr),
+ fWidgets(),
fModal(),
#if defined(DISTRHO_OS_WINDOWS)
hwnd(0),
@@ -89,13 +92,15 @@ struct Window::PrivateData {
PrivateData(App& app, Window* const self, Window& parent)
: fApp(app),
fSelf(self),
- fView(puglInit(nullptr, nullptr)),
+ fView(puglInit()),
fFirstInit(true),
fVisible(false),
fResizable(true),
fUsingEmbed(false),
fWidth(1),
fHeight(1),
+ fTitle(nullptr),
+ fWidgets(),
fModal(parent.pData),
#if defined(DISTRHO_OS_WINDOWS)
hwnd(0),
@@ -112,23 +117,29 @@ struct Window::PrivateData {
DBG("Creating window with parent..."); DBGF;
init();
-#ifdef DISTRHO_OS_LINUX
const PuglInternals* const parentImpl(parent.pData->fView->impl);
-
+#if defined(DISTRHO_OS_LINUX)
XSetTransientForHint(xDisplay, xWindow, parentImpl->win);
+//#elif defined(DISTRHO_OS_MAC)
+// [parentImpl->window orderWindow:NSWindowBelow relativeTo:[[mView window] windowNumber]];
+#else
+ // unused
+ return; (void)parentImpl;
#endif
}
PrivateData(App& app, Window* const self, const intptr_t parentId)
: fApp(app),
fSelf(self),
- fView(puglInit(nullptr, nullptr)),
+ fView(puglInit()),
fFirstInit(true),
fVisible(parentId != 0),
fResizable(parentId == 0),
fUsingEmbed(parentId != 0),
fWidth(1),
fHeight(1),
+ fTitle(nullptr),
+ fWidgets(),
fModal(),
#if defined(DISTRHO_OS_WINDOWS)
hwnd(0),
@@ -136,7 +147,7 @@ struct Window::PrivateData {
xDisplay(nullptr),
xWindow(0),
#elif defined(DISTRHO_OS_MAC)
- fNeedsIdle(false),
+ fNeedsIdle(parentId == 0),
mView(nullptr),
mWindow(nullptr),
#endif
@@ -172,7 +183,7 @@ struct Window::PrivateData {
}
puglInitResizable(fView, fResizable);
- puglInitWindowSize(fView, fWidth, fHeight);
+ puglInitWindowSize(fView, static_cast<int>(fWidth), static_cast<int>(fHeight));
puglSetHandle(fView, this);
puglSetDisplayFunc(fView, onDisplayCallback);
@@ -183,6 +194,7 @@ struct Window::PrivateData {
puglSetSpecialFunc(fView, onSpecialCallback);
puglSetReshapeFunc(fView, onReshapeCallback);
puglSetCloseFunc(fView, onCloseCallback);
+ puglSetFileSelectedFunc(fView, fileBrowserSelectedCallback);
puglCreateWindow(fView, nullptr);
@@ -221,7 +233,12 @@ struct Window::PrivateData {
{
DBG("Destroying window..."); DBGF;
- //fOnModal = false;
+ if (fModal.enabled)
+ {
+ exec_fini();
+ close();
+ }
+
fWidgets.clear();
if (fUsingEmbed)
@@ -444,7 +461,11 @@ struct Window::PrivateData {
fResizable = yesNo;
-#ifdef CARLA_OS_MAC
+#if defined(DISTRHO_OS_WINDOWS)
+ const int winFlags = fResizable ? GetWindowLong(hwnd, GWL_STYLE) | WS_SIZEBOX
+ : GetWindowLong(hwnd, GWL_STYLE) & ~WS_SIZEBOX;
+ SetWindowLong(hwnd, GWL_STYLE, winFlags);
+#elif defined(DISTRHO_OS_MAC)
// FIXME?
const uint flags(yesNo ? (NSViewWidthSizable|NSViewHeightSizable) : 0x0);
[mView setAutoresizingMask:flags];
@@ -457,7 +478,7 @@ struct Window::PrivateData {
void setSize(uint width, uint height, const bool forced = false)
{
- if (width == 0 || height == 0)
+ if (width <= 1 || height <= 1)
{
DBGp("Window setSize called with invalid value(s) %i %i, ignoring request\n", width, height);
return;
@@ -472,18 +493,15 @@ struct Window::PrivateData {
fWidth = width;
fHeight = height;
- DBGp("Window setSize called %s, size %i %i\n", forced ? "(forced)" : "(not forced)", width, height);
+ DBGp("Window setSize called %s, size %i %i, resizable %s\n", forced ? "(forced)" : "(not forced)", width, height, fResizable?"true":"false");
#if defined(DISTRHO_OS_WINDOWS)
- int winFlags = WS_POPUPWINDOW | WS_CAPTION;
-
- if (fResizable)
- winFlags |= WS_SIZEBOX;
-
+ const int winFlags = WS_POPUPWINDOW | WS_CAPTION | (fResizable ? WS_SIZEBOX : 0x0);
RECT wr = { 0, 0, static_cast<long>(width), static_cast<long>(height) };
- AdjustWindowRectEx(&wr, winFlags, FALSE, WS_EX_TOPMOST);
+ AdjustWindowRectEx(&wr, fUsingEmbed ? WS_CHILD : winFlags, FALSE, WS_EX_TOPMOST);
- SetWindowPos(hwnd, 0, 0, 0, wr.right-wr.left, wr.bottom-wr.top, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOOWNERZORDER|SWP_NOZORDER);
+ SetWindowPos(hwnd, 0, 0, 0, wr.right-wr.left, wr.bottom-wr.top,
+ SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOOWNERZORDER|SWP_NOZORDER);
if (! forced)
UpdateWindow(hwnd);
@@ -522,10 +540,22 @@ struct Window::PrivateData {
// -------------------------------------------------------------------
+ const char* getTitle() const noexcept
+ {
+ static const char* const kFallback = "";
+
+ return fTitle != nullptr ? fTitle : kFallback;
+ }
+
void setTitle(const char* const title)
{
DBGp("Window setTitle \"%s\"\n", title);
+ if (fTitle != nullptr)
+ std::free(fTitle);
+
+ fTitle = strdup(title);
+
#if defined(DISTRHO_OS_WINDOWS)
SetWindowTextA(hwnd, title);
#elif defined(DISTRHO_OS_MAC)
@@ -543,7 +573,7 @@ struct Window::PrivateData {
#endif
}
- void setTransientWinId(const intptr_t winId)
+ void setTransientWinId(const uintptr_t winId)
{
#if defined(DISTRHO_OS_LINUX)
XSetTransientForHint(xDisplay, xWindow, static_cast< ::Window>(winId));
@@ -600,7 +630,7 @@ struct Window::PrivateData {
// -------------------------------------------------------------------
- void onDisplay()
+ void onPuglDisplay()
{
fSelf->onDisplayBefore();
@@ -618,22 +648,35 @@ struct Window::PrivateData {
if (widget->fNeedsFullViewport || (widget->fAbsolutePos.isZero() && widget->fSize == Size<uint>(fWidth, fHeight)))
{
// full viewport size
- glViewport(0, 0, fWidth, fHeight);
+ glViewport(0,
+ 0,
+ static_cast<GLsizei>(fWidth),
+ static_cast<GLsizei>(fHeight));
}
else if (! widget->fNeedsScaling)
{
// only set viewport pos
- glViewport(widget->getAbsoluteX(), /*fView->height - widget->getHeight()*/ - widget->getAbsoluteY(), fWidth, fHeight);
+ glViewport(widget->getAbsoluteX(),
+ /*fView->height - static_cast<int>(widget->getHeight())*/ - widget->getAbsoluteY(),
+ static_cast<GLsizei>(fWidth),
+ static_cast<GLsizei>(fHeight));
// then cut the outer bounds
- glScissor(widget->getAbsoluteX(), fView->height - widget->getHeight() - widget->getAbsoluteY(), widget->getWidth(), widget->getHeight());
+ glScissor(widget->getAbsoluteX(),
+ fView->height - static_cast<int>(widget->getHeight()) - widget->getAbsoluteY(),
+ static_cast<GLsizei>(widget->getWidth()),
+ static_cast<GLsizei>(widget->getHeight()));
+
glEnable(GL_SCISSOR_TEST);
needsDisableScissor = true;
}
else
{
// limit viewport to widget bounds
- glViewport(widget->getAbsoluteX(), fView->height - widget->getHeight() - widget->getAbsoluteY(), widget->getWidth(), widget->getHeight());
+ glViewport(widget->getAbsoluteX(),
+ fView->height - static_cast<int>(widget->getHeight()) - widget->getAbsoluteY(),
+ static_cast<GLsizei>(widget->getWidth()),
+ static_cast<GLsizei>(widget->getHeight()));
}
// display widget
@@ -650,7 +693,7 @@ struct Window::PrivateData {
fSelf->onDisplayAfter();
}
- void onKeyboard(const bool press, const uint key)
+ void onPuglKeyboard(const bool press, const uint key)
{
DBGp("PUGL: onKeyboard : %i %i\n", press, key);
@@ -672,7 +715,7 @@ struct Window::PrivateData {
}
}
- void onSpecial(const bool press, const Key key)
+ void onPuglSpecial(const bool press, const Key key)
{
DBGp("PUGL: onSpecial : %i %i\n", press, key);
@@ -681,9 +724,9 @@ struct Window::PrivateData {
Widget::SpecialEvent ev;
ev.press = press;
- ev.key = key;
- ev.mod = static_cast<Modifier>(puglGetModifiers(fView));
- ev.time = puglGetEventTimestamp(fView);
+ ev.key = key;
+ ev.mod = static_cast<Modifier>(puglGetModifiers(fView));
+ ev.time = puglGetEventTimestamp(fView);
FOR_EACH_WIDGET_INV(rit)
{
@@ -694,18 +737,21 @@ struct Window::PrivateData {
}
}
- void onMouse(const int button, const bool press, const int x, const int y)
+ void onPuglMouse(const int button, const bool press, const int x, const int y)
{
DBGp("PUGL: onMouse : %i %i %i %i\n", button, press, x, y);
+ // FIXME - pugl sends 2 of these for each window on init, don't ask me why. we'll ignore it
+ if (press && button == 0 && x == 0 && y == 0) return;
+
if (fModal.childFocus != nullptr)
return fModal.childFocus->focus();
Widget::MouseEvent ev;
ev.button = button;
- ev.press = press;
- ev.mod = static_cast<Modifier>(puglGetModifiers(fView));
- ev.time = puglGetEventTimestamp(fView);
+ ev.press = press;
+ ev.mod = static_cast<Modifier>(puglGetModifiers(fView));
+ ev.time = puglGetEventTimestamp(fView);
FOR_EACH_WIDGET_INV(rit)
{
@@ -718,7 +764,7 @@ struct Window::PrivateData {
}
}
- void onMotion(const int x, const int y)
+ void onPuglMotion(const int x, const int y)
{
DBGp("PUGL: onMotion : %i %i\n", x, y);
@@ -740,7 +786,7 @@ struct Window::PrivateData {
}
}
- void onScroll(const int x, const int y, const float dx, const float dy)
+ void onPuglScroll(const int x, const int y, const float dx, const float dy)
{
DBGp("PUGL: onScroll : %i %i %f %f\n", x, y, dx, dy);
@@ -763,38 +809,38 @@ struct Window::PrivateData {
}
}
- void onReshape(const int width, const int height)
+ void onPuglReshape(const int width, const int height)
{
DBGp("PUGL: onReshape : %i %i\n", width, height);
- if (width == 1 && height == 1)
+ if (width <= 1 && height <= 1)
return;
- fWidth = width;
- fHeight = height;
+ fWidth = static_cast<uint>(width);
+ fHeight = static_cast<uint>(height);
- fSelf->onReshape(width, height);
+ fSelf->onReshape(fWidth, fHeight);
FOR_EACH_WIDGET(it)
{
Widget* const widget(*it);
if (widget->fNeedsFullViewport)
- widget->setSize(width, height);
+ widget->setSize(fWidth, fHeight);
}
}
- void onClose()
+ void onPuglClose()
{
DBG("PUGL: onClose\n");
- if (fModal.enabled && fModal.parent != nullptr)
+ if (fModal.enabled)
exec_fini();
fSelf->onClose();
if (fModal.childFocus != nullptr)
- fModal.childFocus->onClose();
+ fModal.childFocus->fSelf->onClose();
close();
}
@@ -811,6 +857,7 @@ struct Window::PrivateData {
bool fUsingEmbed;
uint fWidth;
uint fHeight;
+ char* fTitle;
std::list<Widget*> fWidgets;
struct Modal {
@@ -833,6 +880,8 @@ struct Window::PrivateData {
DISTRHO_SAFE_ASSERT(! enabled);
DISTRHO_SAFE_ASSERT(childFocus == nullptr);
}
+
+ DISTRHO_DECLARE_NON_COPY_STRUCT(Modal)
} fModal;
#if defined(DISTRHO_OS_WINDOWS)
@@ -853,42 +902,47 @@ struct Window::PrivateData {
static void onDisplayCallback(PuglView* view)
{
- handlePtr->onDisplay();
+ handlePtr->onPuglDisplay();
}
static void onKeyboardCallback(PuglView* view, bool press, uint32_t key)
{
- handlePtr->onKeyboard(press, key);
+ handlePtr->onPuglKeyboard(press, key);
}
static void onSpecialCallback(PuglView* view, bool press, PuglKey key)
{
- handlePtr->onSpecial(press, static_cast<Key>(key));
+ handlePtr->onPuglSpecial(press, static_cast<Key>(key));
}
static void onMouseCallback(PuglView* view, int button, bool press, int x, int y)
{
- handlePtr->onMouse(button, press, x, y);
+ handlePtr->onPuglMouse(button, press, x, y);
}
static void onMotionCallback(PuglView* view, int x, int y)
{
- handlePtr->onMotion(x, y);
+ handlePtr->onPuglMotion(x, y);
}
static void onScrollCallback(PuglView* view, int x, int y, float dx, float dy)
{
- handlePtr->onScroll(x, y, dx, dy);
+ handlePtr->onPuglScroll(x, y, dx, dy);
}
static void onReshapeCallback(PuglView* view, int width, int height)
{
- handlePtr->onReshape(width, height);
+ handlePtr->onPuglReshape(width, height);
}
static void onCloseCallback(PuglView* view)
{
- handlePtr->onClose();
+ handlePtr->onPuglClose();
+ }
+
+ static void fileBrowserSelectedCallback(PuglView* view, const char* filename)
+ {
+ handlePtr->fSelf->fileBrowserSelected(filename);
}
#undef handlePtr
@@ -900,13 +954,16 @@ struct Window::PrivateData {
// Window
Window::Window(App& app)
- : pData(new PrivateData(app, this)) {}
+ : pData(new PrivateData(app, this)),
+ leakDetector_Window() {}
Window::Window(App& app, Window& parent)
- : pData(new PrivateData(app, this, parent)) {}
+ : pData(new PrivateData(app, this, parent)),
+ leakDetector_Window() {}
Window::Window(App& app, intptr_t parentId)
- : pData(new PrivateData(app, this, parentId)) {}
+ : pData(new PrivateData(app, this, parentId)),
+ leakDetector_Window() {}
Window::~Window()
{
@@ -943,6 +1000,73 @@ void Window::repaint() noexcept
puglPostRedisplay(pData->fView);
}
+// static int fib_filter_filename_filter(const char* const name)
+// {
+// return 1;
+// (void)name;
+// }
+
+bool Window::openFileBrowser(const FileBrowserOptions& options)
+{
+ using DISTRHO_NAMESPACE::d_string;
+
+ // --------------------------------------------------------------------------
+ // configure start dir
+
+ // TODO: get abspath if needed
+ // TODO: cross-platform
+
+ d_string startDir(options.startDir);
+
+ if (startDir.isEmpty())
+ {
+ if (char* const dir_name = get_current_dir_name())
+ {
+ startDir = dir_name;
+ std::free(dir_name);
+ }
+ }
+
+ DISTRHO_SAFE_ASSERT_RETURN(startDir.isNotEmpty(), false);
+
+ if (! startDir.endsWith('/'))
+ startDir += "/";
+
+ DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(0, startDir) == 0, false);
+
+ // --------------------------------------------------------------------------
+ // configure title
+
+ d_string title(options.title);
+
+ if (title.isEmpty())
+ {
+ title = pData->getTitle();
+
+ if (title.isEmpty())
+ title = "FileBrowser";
+ }
+
+ DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(1, title) == 0, false);
+
+ // --------------------------------------------------------------------------
+ // configure filters
+
+ x_fib_cfg_filter_callback(nullptr); //fib_filter_filename_filter);
+
+ // --------------------------------------------------------------------------
+ // configure buttons
+
+ x_fib_cfg_buttons(3, options.buttons.listAllFiles-1);
+ x_fib_cfg_buttons(1, options.buttons.showHidden-1);
+ x_fib_cfg_buttons(2, options.buttons.showPlaces-1);
+
+ // --------------------------------------------------------------------------
+ // show
+
+ return (x_fib_show(pData->xDisplay, pData->xWindow, /*options.width*/0, /*options.height*/0) == 0);
+}
+
bool Window::isVisible() const noexcept
{
return pData->fVisible;
@@ -988,12 +1112,17 @@ void Window::setSize(Size<uint> size)
pData->setSize(size.getWidth(), size.getHeight());
}
+const char* Window::getTitle() const noexcept
+{
+ return pData->getTitle();
+}
+
void Window::setTitle(const char* title)
{
pData->setTitle(title);
}
-void Window::setTransientWinId(intptr_t winId)
+void Window::setTransientWinId(uintptr_t winId)
{
pData->setTransientWinId(winId);
}
@@ -1057,8 +1186,8 @@ void Window::onReshape(uint width, uint height)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
- glOrtho(0, width, height, 0, 0.0f, 1.0f);
- glViewport(0, 0, width, height);
+ 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();
}
@@ -1067,6 +1196,10 @@ void Window::onClose()
{
}
+void Window::fileBrowserSelected(const char*)
+{
+}
+
// -----------------------------------------------------------------------
END_NAMESPACE_DGL
diff --git a/libs/dgl/src/nanovg/fontstash.h b/libs/dgl/src/nanovg/fontstash.h
index 7fb8955..a97ce53 100644
--- a/libs/dgl/src/nanovg/fontstash.h
+++ b/libs/dgl/src/nanovg/fontstash.h
@@ -41,7 +41,7 @@ enum FONSalign {
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 craeted too large stack, if you need deep state stack bump up FONS_MAX_STATES.
FONS_STATES_OVERFLOW = 3,
@@ -85,7 +85,7 @@ void fonsDeleteInternal(struct FONScontext* s);
void fonsSetErrorCallback(struct FONScontext* s, void (*callback)(void* uptr, int error, int val), void* uptr);
// Returns current atlas size.
void fonsGetAtlasSize(struct FONScontext* s, int* width, int* height);
-// Expands the atlas size.
+// Expands the atlas size.
int fonsExpandAtlas(struct FONScontext* s, int width, int height);
// Reseta the whole stash.
int fonsResetAtlas(struct FONScontext* stash, int width, int height);
@@ -1388,7 +1388,7 @@ void fonsDrawDebug(struct FONScontext* stash, float x, float y)
}
float fonsTextBounds(struct FONScontext* stash,
- float x, float y,
+ float x, float y,
const char* str, const char* end,
float* bounds)
{
@@ -1576,7 +1576,7 @@ int fonsExpandAtlas(struct 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);
diff --git a/libs/dgl/src/pugl/pugl.h b/libs/dgl/src/pugl/pugl.h
index 360f8e8..7cd8b84 100644
--- a/libs/dgl/src/pugl/pugl.h
+++ b/libs/dgl/src/pugl/pugl.h
@@ -51,7 +51,11 @@
# define PUGL_API PUGL_LIB_IMPORT
# endif
#else
-# define PUGL_API
+# ifdef _WIN32
+# define PUGL_API
+# else
+# define PUGL_API __attribute__((visibility("hidden")))
+# endif
#endif
#ifdef __cplusplus
@@ -196,19 +200,18 @@ typedef void (*PuglReshapeFunc)(PuglView* view, int width, int height);
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);
+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.
@@ -217,16 +220,21 @@ typedef void (*PuglScrollFunc)(PuglView* view,
typedef void (*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);
+
+/**
Create a Pugl context.
To create a window, call the various puglInit* functions as necessary, then
call puglCreateWindow().
-
- @param pargc Pointer to argument count (unused, for GLUT compatibility).
- @param argv Arguments (unused, for GLUT compatibility).
*/
PUGL_API PuglView*
-puglInit(int* pargc, char** argv);
+puglInit(void);
/**
Set the parent window before creating a window (for embedding).
@@ -244,7 +252,7 @@ puglInitWindowSize(PuglView* view, int width, int height);
Enable or disable resizing before creating a window.
*/
PUGL_API void
-puglInitResizable(PuglView* view, bool resizable);
+puglInitUserResizable(PuglView* view, bool resizable);
/**
Create a window with the settings given by the various puglInit functions.
@@ -353,6 +361,12 @@ 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);
+
+/**
Return the native window handle.
*/
PUGL_API PuglNativeWindow
diff --git a/libs/dgl/src/pugl/pugl_internal.h b/libs/dgl/src/pugl/pugl_internal.h
index 1f75830..7301091 100644
--- a/libs/dgl/src/pugl/pugl_internal.h
+++ b/libs/dgl/src/pugl/pugl_internal.h
@@ -51,9 +51,9 @@ struct PuglViewImpl {
PuglReshapeFunc reshapeFunc;
PuglScrollFunc scrollFunc;
PuglSpecialFunc specialFunc;
+ PuglFileSelectedFunc fileSelectedFunc;
PuglInternals* impl;
-
PuglNativeWindow parent;
int width;
@@ -66,12 +66,10 @@ struct PuglViewImpl {
uint32_t event_timestamp_ms;
};
-PuglInternals* puglInitInternals();
-
-void puglDefaultReshape(PuglView* view, int width, int height);
+PuglInternals* puglInitInternals(void);
PuglView*
-puglInit(int* pargc, char** argv)
+puglInit(void)
{
PuglView* view = (PuglView*)calloc(1, sizeof(PuglView));
if (!view) {
@@ -80,6 +78,7 @@ puglInit(int* pargc, char** argv)
PuglInternals* impl = puglInitInternals();
if (!impl) {
+ free(view);
return NULL;
}
@@ -88,9 +87,6 @@ puglInit(int* pargc, char** argv)
view->height = 480;
return view;
-
- // unused
- (void)pargc; (void)argv;
}
void
@@ -136,7 +132,7 @@ puglGetModifiers(PuglView* view)
return view->mods;
}
-void
+static void
puglDefaultReshape(PuglView* view, int width, int height)
{
glMatrixMode(GL_PROJECTION);
@@ -205,3 +201,9 @@ puglSetSpecialFunc(PuglView* view, PuglSpecialFunc specialFunc)
{
view->specialFunc = specialFunc;
}
+
+void
+puglSetFileSelectedFunc(PuglView* view, PuglFileSelectedFunc fileSelectedFunc)
+{
+ view->fileSelectedFunc = fileSelectedFunc;
+}
diff --git a/libs/dgl/src/pugl/pugl_osx.m b/libs/dgl/src/pugl/pugl_osx.m
index 96aa57a..a99d8d0 100644
--- a/libs/dgl/src/pugl/pugl_osx.m
+++ b/libs/dgl/src/pugl/pugl_osx.m
@@ -35,6 +35,7 @@
backing:(NSBackingStoreType)bufferingType
defer:(BOOL)flag;
- (void) setPuglview:(PuglView*)view;
+- (BOOL) canBecomeKeyWindow;
- (BOOL) windowShouldClose:(id)sender;
@end
@@ -54,7 +55,7 @@
[result setAcceptsMouseMovedEvents:YES];
[result setLevel: CGShieldingWindowLevel() + 1];
- return result;
+ return (PuglWindow*)result;
// unused
(void)aStyle; (void)bufferingType; (void)flag;
@@ -66,6 +67,11 @@
[self setContentSize:NSMakeSize(view->width, view->height)];
}
+- (BOOL)canBecomeKeyWindow
+{
+ return YES;
+}
+
- (BOOL)windowShouldClose:(id)sender
{
if (puglview->closeFunc)
@@ -81,6 +87,7 @@
static void
puglDisplay(PuglView* view)
{
+ view->redisplay = false;
if (view->displayFunc) {
view->displayFunc(view);
}
@@ -88,28 +95,33 @@ puglDisplay(PuglView* view)
@interface PuglOpenGLView : NSOpenGLView
{
- int colorBits;
- int depthBits;
@public
PuglView* puglview;
-
NSTrackingArea* trackingArea;
+ bool doubleBuffered;
}
-- (id) initWithFrame:(NSRect)frame
- colorBits:(int)numColorBits
- depthBits:(int)numDepthBits;
+- (BOOL) acceptsFirstMouse:(NSEvent*)e;
+- (BOOL) acceptsFirstResponder;
+- (BOOL) isFlipped;
+- (BOOL) isOpaque;
+- (BOOL) preservesContentInLiveResize;
+- (id) initWithFrame:(NSRect)frame;
- (void) reshape;
-- (void) drawRect:(NSRect)rect;
-- (void) mouseEntered:(NSEvent*)event;
-- (void) mouseExited:(NSEvent*)event;
+- (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) mouseUp:(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;
@@ -119,22 +131,46 @@ puglDisplay(PuglView* view)
@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
- colorBits:(int)numColorBits
- depthBits:(int)numDepthBits
{
- colorBits = numColorBits;
- depthBits = numDepthBits;
- puglview = nil;
- trackingArea = nil;
+ puglview = nil;
+ trackingArea = nil;
+ doubleBuffered = true;
- NSOpenGLPixelFormatAttribute pixelAttribs[16] = {
+ NSOpenGLPixelFormatAttribute pixelAttribs[] = {
+ NSOpenGLPFAColorSize, 24,
+ NSOpenGLPFAAlphaSize, 8,
+ NSOpenGLPFADepthSize, 16,
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAAccelerated,
- NSOpenGLPFAColorSize,
- colorBits,
- NSOpenGLPFADepthSize,
- depthBits,
0
};
@@ -144,12 +180,21 @@ puglDisplay(PuglView* view)
if (pixelFormat) {
self = [super initWithFrame:frame pixelFormat:pixelFormat];
[pixelFormat release];
- if (self) {
- [[self openGLContext] makeCurrentContext];
- [self reshape];
- }
+ printf("Is doubleBuffered? TRUE\n");
} else {
- self = nil;
+ self = [super initWithFrame:frame];
+ doubleBuffered = false;
+ printf("Is doubleBuffered? FALSE\n");
+ }
+
+ if (self) {
+ NSOpenGLContext* context = [self openGLContext];
+ [context makeCurrentContext];
+
+ GLint swapInterval = 1;
+ [context setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval];
+
+ [self reshape];
}
return self;
@@ -159,156 +204,180 @@ puglDisplay(PuglView* view)
{
[[self openGLContext] update];
- NSRect bounds = [self bounds];
- int width = bounds.size.width;
- int height = bounds.size.height;
-
- if (puglview) {
+ 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.
*/
- if (puglview->reshapeFunc) {
- puglview->reshapeFunc(puglview, width, height);
- } else {
- puglDefaultReshape(puglview, width, height);
- }
+ return;
+ }
+
+ NSRect bounds = [self bounds];
+ int width = bounds.size.width;
+ int height = bounds.size.height;
- puglview->width = width;
- puglview->height = height;
+ if (puglview->reshapeFunc) {
+ puglview->reshapeFunc(puglview, width, height);
+ } else {
+ puglDefaultReshape(puglview, width, height);
}
+
+ puglview->width = width;
+ puglview->height = height;
}
-- (void) drawRect:(NSRect)rect
+- (void) drawRect:(NSRect)r
{
puglDisplay(puglview);
- glFlush();
- glSwapAPPLE();
+
+ if (doubleBuffered) {
+ [[self openGLContext] flushBuffer];
+ } else {
+ glFlush();
+ //glSwapAPPLE();
+ }
// unused
- return; (void)rect;
+ return; (void)r;
}
-static unsigned
-getModifiers(PuglView* view, NSEvent* ev)
+- (void) cursorUpdate:(NSEvent*)e
{
- const unsigned modifierFlags = [ev modifierFlags];
+ [[NSCursor arrowCursor] set];
- 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;
+ // unused
+ return; (void)e;
}
--(void)updateTrackingAreas
+- (void) updateTrackingAreas
{
+ static const int opts = NSTrackingMouseEnteredAndExited
+ | NSTrackingMouseMoved
+ | NSTrackingEnabledDuringMouseDrag
+ | NSTrackingInVisibleRect
+ | NSTrackingActiveAlways
+ | NSTrackingCursorUpdate;
+
if (trackingArea != nil) {
[self removeTrackingArea:trackingArea];
[trackingArea release];
}
- const int opts = (NSTrackingMouseEnteredAndExited |
- NSTrackingMouseMoved |
- NSTrackingActiveAlways);
- trackingArea = [ [NSTrackingArea alloc] initWithRect:[self bounds]
- options:opts
- owner:self
- userInfo:nil];
+ trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds]
+ options:opts
+ owner:self
+ userInfo:nil];
[self addTrackingArea:trackingArea];
+ [super updateTrackingAreas];
}
-- (void)mouseEntered:(NSEvent*)theEvent
+- (void) viewWillMoveToWindow:(NSWindow*)newWindow
{
- [self updateTrackingAreas];
+ if (newWindow != nil) {
+ [newWindow setAcceptsMouseMovedEvents:YES];
+ [newWindow makeFirstResponder:self];
+ }
- // unused
- return; (void)theEvent;
+ [super viewWillMoveToWindow:newWindow];
}
-- (void)mouseExited:(NSEvent*)theEvent
+static unsigned
+getModifiers(PuglView* view, NSEvent* ev)
{
- // unused
- return; (void)theEvent;
+ 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 = [event locationInWindow];
+ NSPoint loc = [self convertPoint:[event locationInWindow] fromView:nil];
puglview->mods = getModifiers(puglview, event);
- puglview->motionFunc(puglview, loc.x, puglview->height - loc.y);
+ puglview->motionFunc(puglview, loc.x, loc.y);
}
}
- (void) mouseDragged:(NSEvent*)event
{
- if (puglview->motionFunc) {
- NSPoint loc = [event locationInWindow];
- puglview->mods = getModifiers(puglview, event);
- puglview->motionFunc(puglview, loc.x, puglview->height - loc.y);
- }
+ [self mouseMoved:event];
}
- (void) rightMouseDragged:(NSEvent*)event
{
- if (puglview->motionFunc) {
- NSPoint loc = [event locationInWindow];
- puglview->mods = getModifiers(puglview, event);
- puglview->motionFunc(puglview, loc.x, puglview->height - loc.y);
- }
+ [self mouseDragged:event];
}
-- (void) mouseDown:(NSEvent*)event
+- (void) otherMouseDragged:(NSEvent*)event
{
- if (puglview->mouseFunc) {
- NSPoint loc = [event locationInWindow];
- puglview->mods = getModifiers(puglview, event);
- puglview->mouseFunc(puglview, 1, true, loc.x, puglview->height - loc.y);
- }
+ [self mouseDragged:event];
}
-- (void) mouseUp:(NSEvent*)event
+- (void) mouseDown:(NSEvent*)event
{
if (puglview->mouseFunc) {
- NSPoint loc = [event locationInWindow];
+ NSPoint loc = [self convertPoint:[event locationInWindow] fromView:nil];
puglview->mods = getModifiers(puglview, event);
- puglview->mouseFunc(puglview, 1, false, loc.x, puglview->height - loc.y);
+ puglview->mouseFunc(puglview, getFixedAppKitButton([event buttonNumber]), true, loc.x, loc.y);
}
- [self updateTrackingAreas];
}
- (void) rightMouseDown:(NSEvent*)event
{
+ [self mouseDown:event];
+}
+
+- (void) otherMouseDown:(NSEvent*)event
+{
+ [self mouseDown:event];
+}
+
+- (void) mouseUp:(NSEvent*)event
+{
if (puglview->mouseFunc) {
- NSPoint loc = [event locationInWindow];
+ NSPoint loc = [self convertPoint:[event locationInWindow] fromView:nil];
puglview->mods = getModifiers(puglview, event);
- puglview->mouseFunc(puglview, 3, true, loc.x, puglview->height - loc.y);
+ puglview->mouseFunc(puglview, getFixedAppKitButton([event buttonNumber]), false, loc.x, loc.y);
}
}
- (void) rightMouseUp:(NSEvent*)event
{
- if (puglview->mouseFunc) {
- NSPoint loc = [event locationInWindow];
- puglview->mods = getModifiers(puglview, event);
- puglview->mouseFunc(puglview, 3, false, loc.x, puglview->height - loc.y);
- }
+ [self mouseUp:event];
+}
+
+- (void) otherMouseUp:(NSEvent*)event
+{
+ [self mouseUp:event];
}
- (void) scrollWheel:(NSEvent*)event
{
if (puglview->scrollFunc) {
- NSPoint loc = [event locationInWindow];
+ NSPoint loc = [self convertPoint:[event locationInWindow] fromView:nil];
puglview->mods = getModifiers(puglview, event);
puglview->scrollFunc(puglview,
- loc.x, puglview->height - loc.y,
+ loc.x, loc.y,
[event deltaX], [event deltaY]);
}
- [self updateTrackingAreas];
}
- (void) keyDown:(NSEvent*)event
@@ -368,6 +437,11 @@ puglCreateWindow(PuglView* view, const char* title)
[NSApplication sharedApplication];
impl->glview = [PuglOpenGLView new];
+
+ if (!impl->glview) {
+ return 1;
+ }
+
impl->glview->puglview = view;
if (view->resizable) {
@@ -375,6 +449,7 @@ puglCreateWindow(PuglView* view, const char* title)
}
if (view->parent) {
+ [impl->glview retain];
NSView* pview = (NSView*)view->parent;
[pview addSubview:impl->glview];
return 0;
@@ -451,15 +526,17 @@ puglDestroy(PuglView* view)
PuglStatus
puglProcessEvents(PuglView* view)
{
- [view->impl->glview setNeedsDisplay: YES];
-
return PUGL_SUCCESS;
+
+ // unused
+ (void)view;
}
void
puglPostRedisplay(PuglView* view)
{
view->redisplay = true;
+ [view->impl->glview setNeedsDisplay:YES];
}
PuglNativeWindow
diff --git a/libs/dgl/src/pugl/pugl_win.cpp b/libs/dgl/src/pugl/pugl_win.cpp
index 9837671..0837ac1 100644
--- a/libs/dgl/src/pugl/pugl_win.cpp
+++ b/libs/dgl/src/pugl/pugl_win.cpp
@@ -22,6 +22,7 @@
#include <windowsx.h>
#include <GL/gl.h>
+#include <ctime>
#include <stdio.h>
#include <stdlib.h>
@@ -37,7 +38,7 @@
# define WHEEL_DELTA 120
#endif
-#define PUGL_LOCAL_CLOSE_MSG (WM_USER + 50)
+const int LOCAL_CLOSE_MSG = WM_USER + 50;
HINSTANCE hInstance = NULL;
@@ -51,6 +52,7 @@ struct PuglInternalsImpl {
LRESULT CALLBACK
wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
+#if 0
extern "C" {
BOOL WINAPI
DllMain(HINSTANCE hInst, DWORD, LPVOID)
@@ -59,6 +61,7 @@ DllMain(HINSTANCE hInst, DWORD, LPVOID)
return 1;
}
} // extern "C"
+#endif
PuglInternals*
puglInitInternals()
@@ -79,7 +82,9 @@ puglCreateWindow(PuglView* view, const char* title)
// Should class be a parameter? Does this make sense on other platforms?
static int wc_count = 0;
char classNameBuf[256];
- _snprintf(classNameBuf, sizeof(classNameBuf), "%s_%d\n", title, wc_count++);
+ std::srand((std::time(NULL)));
+ _snprintf(classNameBuf, sizeof(classNameBuf), "%s_%d-%d", title, std::rand(), ++wc_count);
+ classNameBuf[sizeof(classNameBuf)-1] = '\0';
impl->wc.style = CS_OWNDC;
impl->wc.lpfnWndProc = wndProc;
@@ -90,36 +95,36 @@ puglCreateWindow(PuglView* view, const char* title)
impl->wc.hCursor = LoadCursor(hInstance, IDC_ARROW);
impl->wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
impl->wc.lpszMenuName = NULL;
- impl->wc.lpszClassName = classNameBuf;
- RegisterClass(&impl->wc);
+ impl->wc.lpszClassName = strdup(classNameBuf);
- int winFlags = WS_POPUPWINDOW | WS_CAPTION;
- if (view->resizable) {
- winFlags |= WS_SIZEBOX;
+ if (!RegisterClass(&impl->wc)) {
+ free((void*)impl->wc.lpszClassName);
+ free(impl);
+ free(view);
+ return 1;
}
// Adjust the overall window size to accomodate our requested client size
+ const int winFlags = WS_POPUPWINDOW | WS_CAPTION | (view->resizable ? WS_SIZEBOX : 0x0);
RECT wr = { 0, 0, view->width, view->height };
- AdjustWindowRectEx(&wr, winFlags, FALSE, WS_EX_TOPMOST);
+ AdjustWindowRectEx(&wr, view->parent ? WS_CHILD : winFlags, FALSE, WS_EX_TOPMOST);
impl->hwnd = CreateWindowEx(
WS_EX_TOPMOST,
classNameBuf, title,
- view->parent ? WS_CHILD : winFlags,
+ 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;
}
-#ifdef _WIN64
SetWindowLongPtr(impl->hwnd, GWLP_USERDATA, (LONG_PTR)view);
-#else
- SetWindowLongPtr(impl->hwnd, GWL_USERDATA, (LONG)view);
-#endif
impl->hdc = GetDC(impl->hwnd);
@@ -137,6 +142,16 @@ puglCreateWindow(PuglView* view, const char* title)
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;
+ }
+
wglMakeCurrent(impl->hdc, impl->hglrc);
return 0;
@@ -166,6 +181,7 @@ puglDestroy(PuglView* view)
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);
}
@@ -190,13 +206,13 @@ puglDisplay(PuglView* view)
{
wglMakeCurrent(view->impl->hdc, view->impl->hglrc);
+ view->redisplay = false;
if (view->displayFunc) {
view->displayFunc(view);
}
glFlush();
SwapBuffers(view->impl->hdc);
- view->redisplay = false;
}
static PuglKey
@@ -309,17 +325,21 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam)
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, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam),
- 0.0f, (int16_t)HIWORD(wParam) / (float)WHEEL_DELTA);
+ view, pt.x, pt.y,
+ 0, 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, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam),
- (int16_t)HIWORD(wParam) / float(WHEEL_DELTA), 0.0f);
+ view, pt.x, pt.y,
+ GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA, 0);
}
break;
case WM_KEYDOWN:
@@ -333,11 +353,18 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam)
view->specialFunc(view, message == WM_KEYDOWN, key);
}
} else if (view->keyboardFunc) {
- view->keyboardFunc(view, message == WM_KEYDOWN, wParam);
+ 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:
+ case LOCAL_CLOSE_MSG:
if (view->closeFunc) {
view->closeFunc(view);
}
@@ -358,7 +385,6 @@ puglProcessEvents(PuglView* view)
handleMessage(view, msg.message, msg.wParam, msg.lParam);
}
-
if (view->redisplay) {
InvalidateRect(view->impl->hwnd, NULL, FALSE);
}
@@ -369,23 +395,19 @@ puglProcessEvents(PuglView* view)
LRESULT CALLBACK
wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
-#ifdef _WIN64
PuglView* view = (PuglView*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
-#else
- PuglView* view = (PuglView*)GetWindowLongPtr(hwnd, GWL_USERDATA);
-#endif
switch (message) {
case WM_CREATE:
PostMessage(hwnd, WM_SHOWWINDOW, TRUE, 0);
return 0;
case WM_CLOSE:
- PostMessage(hwnd, PUGL_LOCAL_CLOSE_MSG, wParam, lParam);
+ PostMessage(hwnd, LOCAL_CLOSE_MSG, wParam, lParam);
return 0;
case WM_DESTROY:
return 0;
default:
- if (view) {
+ if (view && hwnd == view->impl->hwnd) {
return handleMessage(view, message, wParam, lParam);
} else {
return DefWindowProc(hwnd, message, wParam, lParam);
diff --git a/libs/dgl/src/pugl/pugl_x11.c b/libs/dgl/src/pugl/pugl_x11.c
index fdba1b6..eac570c 100644
--- a/libs/dgl/src/pugl/pugl_x11.c
+++ b/libs/dgl/src/pugl/pugl_x11.c
@@ -1,6 +1,7 @@
/*
Copyright 2012-2014 David Robillard <http://drobilla.net>
Copyright 2011-2012 Ben Loftis, Harrison Consoles
+ Copyright 2013 Robin Gareus <robin@gareus.org>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ -31,6 +32,10 @@
#include "pugl_internal.h"
+#define SOFD_HAVE_X11
+#include "../sofd/libsofd.h"
+#include "../sofd/libsofd.c"
+
struct PuglInternalsImpl {
Display* display;
int screen;
@@ -49,6 +54,7 @@ static int attrListSgl[] = {
GLX_GREEN_SIZE, 4,
GLX_BLUE_SIZE, 4,
GLX_DEPTH_SIZE, 16,
+ GLX_ARB_multisample, 1,
None
};
@@ -62,11 +68,29 @@ static int attrListDbl[] = {
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)
+*/
+static int attrListDblMS[] = {
+ GLX_RGBA,
+ GLX_DOUBLEBUFFER , True,
+ 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
};
PuglInternals*
-puglInitInternals()
+puglInitInternals(void)
{
return (PuglInternals*)calloc(1, sizeof(PuglInternals));
}
@@ -76,17 +100,21 @@ puglCreateWindow(PuglView* view, const char* title)
{
PuglInternals* impl = view->impl;
- impl->display = XOpenDisplay(0);
+ impl->display = XOpenDisplay(NULL);
impl->screen = DefaultScreen(impl->display);
+ impl->doubleBuffered = True;
+
+ XVisualInfo* vi = glXChooseVisual(impl->display, impl->screen, attrListDblMS);
+
+ if (!vi) {
+ vi = glXChooseVisual(impl->display, impl->screen, attrListDbl);
+ PUGL_LOG("multisampling (antialiasing) is not available\n");
+ }
- XVisualInfo* vi = glXChooseVisual(impl->display, impl->screen, attrListDbl);
if (!vi) {
vi = glXChooseVisual(impl->display, impl->screen, attrListSgl);
impl->doubleBuffered = False;
- PUGL_LOG("No double buffering available\n");
- } else {
- impl->doubleBuffered = True;
- PUGL_LOG("Double buffered rendering enabled\n");
+ PUGL_LOG("singlebuffered rendering will be used, no doublebuffering available\n");
}
int glxMajor, glxMinor;
@@ -202,16 +230,17 @@ puglDisplay(PuglView* view)
{
glXMakeCurrent(view->impl->display, view->impl->win, view->impl->ctx);
+ view->redisplay = false;
+
if (view->displayFunc) {
view->displayFunc(view);
}
glFlush();
+
if (view->impl->doubleBuffered) {
glXSwapBuffers(view->impl->display, view->impl->win);
}
-
- view->redisplay = false;
}
static PuglKey
@@ -269,9 +298,16 @@ dispatchKey(PuglView* view, XEvent* event, bool press)
KeySym sym;
char str[5];
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) {
return;
- } else if (n > 1) {
+ }
+ if (n > 1) {
fprintf(stderr, "warning: Unsupported multi-byte key %X\n", (int)sym);
return;
}
@@ -290,6 +326,26 @@ puglProcessEvents(PuglView* view)
XEvent event;
while (XPending(view->impl->display) > 0) {
XNextEvent(view->impl->display, &event);
+
+ 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;
+ }
+
switch (event.type) {
case MapNotify:
puglReshape(view, view->width, view->height);
@@ -325,9 +381,7 @@ puglProcessEvents(PuglView* view)
case 6: dx = -1.0f; break;
case 7: dx = 1.0f; break;
}
- view->scrollFunc(view,
- event.xbutton.x, event.xbutton.y,
- dx, dy);
+ view->scrollFunc(view, event.xbutton.x, event.xbutton.y, dx, dy);
}
break;
}
@@ -345,8 +399,9 @@ puglProcessEvents(PuglView* view)
setModifiers(view, event.xkey.state, event.xkey.time);
dispatchKey(view, &event, true);
break;
- case KeyRelease:
+ case KeyRelease: {
setModifiers(view, event.xkey.state, event.xkey.time);
+ bool repeated = false;
if (view->ignoreKeyRepeat &&
XEventsQueued(view->impl->display, QueuedAfterReading)) {
XEvent next;
@@ -355,27 +410,27 @@ puglProcessEvents(PuglView* view)
next.xkey.time == event.xkey.time &&
next.xkey.keycode == event.xkey.keycode) {
XNextEvent(view->impl->display, &event);
- break;
+ repeated = true;
}
}
- dispatchKey(view, &event, false);
- break;
+ 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;
+ } break;
#ifdef PUGL_GRAB_FOCUS
case EnterNotify:
- XSetInputFocus(view->impl->display,
- view->impl->win,
- RevertToPointerRoot,
- CurrentTime);
+ XSetInputFocus(view->impl->display, view->impl->win, RevertToPointerRoot, CurrentTime);
break;
#endif
default:
diff --git a/libs/dgl/src/sofd/libsofd.c b/libs/dgl/src/sofd/libsofd.c
new file mode 100644
index 0000000..bcca9f7
--- /dev/null
+++ b/libs/dgl/src/sofd/libsofd.c
@@ -0,0 +1,2431 @@
+/* libSOFD - Simple Open File Dialog [for X11 without toolkit]
+ *
+ * Copyright (C) 2014 Robin Gareus <robin@gareus.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* Test and example:
+ * gcc -Wall -D SOFD_TEST -g -o sofd libsofd.c -lX11
+ *
+ * public API documentation and example code at the bottom of this file
+ *
+ * This small lib may one day include openGL rendering and
+ * wayland window support, but not today. Today we celebrate
+ * 30 years of X11.
+ */
+
+#ifdef SOFD_TEST
+#define SOFD_HAVE_X11
+#include "libsofd.h"
+#endif
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <assert.h>
+
+#if defined(__clang__)
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wnarrowing"
+#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wnarrowing"
+#endif
+
+// shared 'recently used' implementation
+// sadly, xbel does not qualify as simple.
+// hence we use a simple format alike the
+// gtk-bookmark list (one file per line)
+
+#define MAX_RECENT_ENTRIES 24
+#define MAX_RECENT_AGE (15552000) // 180 days (in sec)
+
+typedef struct {
+ char path[1024];
+ time_t atime;
+} FibRecentFile;
+
+static FibRecentFile *_recentlist = NULL;
+static unsigned int _recentcnt = 0;
+static uint8_t _recentlock = 0;
+
+static int fib_isxdigit (const char x) {
+ if (
+ (x >= '0' && x <= '9')
+ ||
+ (x >= 'a' && x <= 'f')
+ ||
+ (x >= 'A' && x <= 'F')
+ ) return 1;
+ return 0;
+}
+
+static void decode_3986 (char *str) {
+ int len = strlen (str);
+ int idx = 0;
+ while (idx + 2 < len) {
+ char *in = &str[idx];
+ if (('%' == *in) && fib_isxdigit (in[1]) && fib_isxdigit (in[2])) {
+ char hexstr[3];
+ hexstr[0] = in[1];
+ hexstr[1] = in[2];
+ hexstr[2] = 0;
+ long hex = strtol (hexstr, NULL, 16);
+ *in = hex;
+ memmove (&str[idx+1], &str[idx + 3], len - idx - 2);
+ len -= 2;
+ }
+ ++idx;
+ }
+}
+
+static char *encode_3986 (const char *str) {
+ size_t alloc, newlen;
+ char *ns = NULL;
+ unsigned char in;
+ size_t i = 0;
+ size_t length;
+
+ if (!str) return strdup ("");
+
+ alloc = strlen (str) + 1;
+ newlen = alloc;
+
+ ns = (char*) malloc (alloc);
+
+ length = alloc;
+ while (--length) {
+ in = *str;
+
+ switch (in) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case 'a': case 'b': case 'c': case 'd': case 'e':
+ case 'f': case 'g': case 'h': case 'i': case 'j':
+ case 'k': case 'l': case 'm': case 'n': case 'o':
+ case 'p': case 'q': case 'r': case 's': case 't':
+ case 'u': case 'v': case 'w': case 'x': case 'y': case 'z':
+ case 'A': case 'B': case 'C': case 'D': case 'E':
+ case 'F': case 'G': case 'H': case 'I': case 'J':
+ case 'K': case 'L': case 'M': case 'N': case 'O':
+ case 'P': case 'Q': case 'R': case 'S': case 'T':
+ case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z':
+ case '_': case '~': case '.': case '-':
+ case '/': case ',': // XXX not in RFC3986
+ ns[i++] = in;
+ break;
+ default:
+ newlen += 2; /* this'll become a %XX */
+ if (newlen > alloc) {
+ alloc *= 2;
+ ns = (char*) realloc (ns, alloc);
+ }
+ snprintf (&ns[i], 4, "%%%02X", in);
+ i += 3;
+ break;
+ }
+ ++str;
+ }
+ ns[i] = 0;
+ return ns;
+}
+
+void x_fib_free_recent () {
+ free (_recentlist);
+ _recentlist = NULL;
+ _recentcnt = 0;
+}
+
+static int cmp_recent (const void *p1, const void *p2) {
+ FibRecentFile *a = (FibRecentFile*) p1;
+ FibRecentFile *b = (FibRecentFile*) p2;
+ if (a->atime == b->atime) return 0;
+ return a->atime < b->atime;
+}
+
+int x_fib_add_recent (const char *path, time_t atime) {
+ unsigned int i;
+ struct stat fs;
+ if (_recentlock) { return -1; }
+ if (access (path, R_OK)) {
+ return -1;
+ }
+ if (stat (path, &fs)) {
+ return -1;
+ }
+ if (!S_ISREG (fs.st_mode)) {
+ return -1;
+ }
+ if (atime == 0) atime = time (NULL);
+ if (MAX_RECENT_AGE > 0 && atime + MAX_RECENT_AGE < time (NULL)) {
+ return -1;
+ }
+
+ for (i = 0; i < _recentcnt; ++i) {
+ if (!strcmp (_recentlist[i].path, path)) {
+ if (_recentlist[i].atime < atime) {
+ _recentlist[i].atime = atime;
+ }
+ qsort (_recentlist, _recentcnt, sizeof(FibRecentFile), cmp_recent);
+ return _recentcnt;
+ }
+ }
+ _recentlist = (FibRecentFile*)realloc (_recentlist, (_recentcnt + 1) * sizeof(FibRecentFile));
+ _recentlist[_recentcnt].atime = atime;
+ strcpy (_recentlist[_recentcnt].path, path);
+ qsort (_recentlist, _recentcnt + 1, sizeof(FibRecentFile), cmp_recent);
+
+ if (_recentcnt >= MAX_RECENT_ENTRIES) {
+ return (_recentcnt);
+ }
+ return (++_recentcnt);
+}
+
+#ifdef PATHSEP
+#undef PATHSEP
+#endif
+
+#ifdef PLATFORM_WINDOWS
+#define DIRSEP '\\'
+#else
+#define DIRSEP '/'
+#endif
+
+static void mkpath(const char *dir) {
+ char tmp[1024];
+ char *p;
+ size_t len;
+
+ snprintf (tmp, sizeof(tmp), "%s", dir);
+ len = strlen(tmp);
+ if (tmp[len - 1] == '/')
+ tmp[len - 1] = 0;
+ for (p = tmp + 1; *p; ++p)
+ if(*p == DIRSEP) {
+ *p = 0;
+#ifdef PLATFORM_WINDOWS
+ mkdir(tmp);
+#else
+ mkdir(tmp, 0755);
+#endif
+ *p = DIRSEP;
+ }
+#ifdef PLATFORM_WINDOWS
+ mkdir(tmp);
+#else
+ mkdir(tmp, 0755);
+#endif
+}
+
+int x_fib_save_recent (const char *fn) {
+ if (_recentlock) { return -1; }
+ if (!fn) { return -1; }
+ if (_recentcnt < 1 || !_recentlist) { return -1; }
+ unsigned int i;
+ char *dn = strdup (fn);
+ mkpath (dirname (dn));
+ free (dn);
+
+ FILE *rf = fopen (fn, "w");
+ if (!rf) return -1;
+
+ qsort (_recentlist, _recentcnt, sizeof(FibRecentFile), cmp_recent);
+ for (i = 0; i < _recentcnt; ++i) {
+ char *n = encode_3986 (_recentlist[i].path);
+ fprintf (rf, "%s %lu\n", n, _recentlist[i].atime);
+ free (n);
+ }
+ fclose (rf);
+ return 0;
+}
+
+int x_fib_load_recent (const char *fn) {
+ char tmp[1024];
+ if (_recentlock) { return -1; }
+ if (!fn) { return -1; }
+ x_fib_free_recent ();
+ if (access (fn, R_OK)) {
+ return -1;
+ }
+ FILE *rf = fopen (fn, "r");
+ if (!rf) return -1;
+ while (fgets (tmp, sizeof(tmp), rf)
+ && strlen (tmp) > 1
+ && strlen (tmp) < sizeof(tmp))
+ {
+ char *s;
+ tmp[strlen (tmp) - 1] = '\0'; // strip newline
+ if (!(s = strchr (tmp, ' '))) { // find name <> atime sep
+ continue;
+ }
+ *s = '\0';
+ time_t t = atol (++s);
+ decode_3986 (tmp);
+ x_fib_add_recent (tmp, t);
+ }
+ fclose (rf);
+ return 0;
+}
+
+unsigned int x_fib_recent_count () {
+ return _recentcnt;
+}
+
+const char *x_fib_recent_at (unsigned int i) {
+ if (i >= _recentcnt)
+ return NULL;
+ return _recentlist[i].path;
+}
+
+#ifdef PLATFORM_WINDOWS
+#define PATHSEP "\\"
+#else
+#define PATHSEP "/"
+#endif
+
+const char *x_fib_recent_file(const char *appname) {
+ static char recent_file[1024];
+ assert(!strchr(appname, '/'));
+ const char *xdg = getenv("XDG_DATA_HOME");
+ if (xdg && (strlen(xdg) + strlen(appname) + 10) < sizeof(recent_file)) {
+ sprintf(recent_file, "%s" PATHSEP "%s" PATHSEP "recent", xdg, appname);
+ return recent_file;
+ }
+#ifdef PLATFORM_WINDOWS
+ const char * homedrive = getenv("HOMEDRIVE");
+ const char * homepath = getenv("HOMEPATH");
+ if (homedrive && homepath && (strlen(homedrive) + strlen(homepath) + strlen(appname) + 29) < PATH_MAX) {
+ sprintf(recent_file, "%s%s" PATHSEP "Application Data" PATHSEP "%s" PATHSEP "recent.txt", homedrive, homepath, appname);
+ return recent_file;
+ }
+#elif defined PLATFORM_OSX
+ const char *home = getenv("HOME");
+ if (home && (strlen(home) + strlen(appname) + 29) < sizeof(recent_file)) {
+ sprintf(recent_file, "%s/Library/Preferences/%s/recent", home, appname);
+ return recent_file;
+ }
+#else
+ const char *home = getenv("HOME");
+ if (home && (strlen(home) + strlen(appname) + 22) < sizeof(recent_file)) {
+ sprintf(recent_file, "%s/.local/share/%s/recent", home, appname);
+ return recent_file;
+ }
+#endif
+ return NULL;
+}
+
+#ifdef SOFD_HAVE_X11
+#include <mntent.h>
+#include <dirent.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+#include <X11/keysym.h>
+#include <X11/Xos.h>
+
+#ifndef MIN
+#define MIN(A,B) ( (A) < (B) ? (A) : (B) )
+#endif
+
+#ifndef MAX
+#define MAX(A,B) ( (A) < (B) ? (B) : (A) )
+#endif
+
+static Window _fib_win = 0;
+static GC _fib_gc = 0;
+static XColor _c_gray0, _c_gray1, _c_gray2, _c_gray3, _c_gray4, _c_gray5, _c_gray6;
+static Font _fibfont = 0;
+static Pixmap _pixbuffer = None;
+
+static int _fib_width = 100;
+static int _fib_height = 100;
+static int _btn_w = 0;
+static int _btn_span = 0;
+
+static int _fib_font_height = 0;
+static int _fib_dir_indent = 0;
+static int _fib_spc_norm = 0;
+static int _fib_font_ascent = 0;
+static int _fib_font_vsep = 0;
+static int _fib_font_size_width = 0;
+static int _fib_font_time_width = 0;
+static int _fib_place_width = 0;
+
+static int _scrl_f = 0;
+static int _scrl_y0 = -1;
+static int _scrl_y1 = -1;
+static int _scrl_my = -1;
+static int _scrl_mf = -1;
+static int _view_p = -1;
+
+static int _fsel = -1;
+static int _hov_b = -1;
+static int _hov_f = -1;
+static int _hov_p = -1;
+static int _hov_h = -1;
+static int _hov_l = -1;
+static int _hov_s = -1;
+static int _sort = 0;
+static int _columns = 0;
+static int _fib_filter_fn = 1;
+static int _fib_hidden_fn = 0;
+static int _fib_show_places = 0;
+
+static uint8_t _fib_mapped = 0;
+static uint8_t _fib_resized = 0;
+static unsigned long _dblclk = 0;
+
+static int _status = -2;
+static char _rv_open[1024] = "";
+
+static char _fib_cfg_custom_places[1024] = "";
+static char _fib_cfg_custom_font[256] = "";
+static char _fib_cfg_title[128] = "xjadeo - Open Video File";
+
+typedef struct {
+ char name[256];
+ int x0;
+ int xw;
+} FibPathButton;
+
+typedef struct {
+ char name[256];
+ char strtime[32];
+ char strsize[32];
+ int ssizew;
+ off_t size;
+ time_t mtime;
+ uint8_t flags; // 2: selected, 4: isdir 8: recent-entry
+ FibRecentFile *rfp;
+} FibFileEntry;
+
+typedef struct {
+ char text[24];
+ uint8_t flags; // 2: selected, 4: toggle, 8 disable
+ int x0;
+ int tw;
+ int xw;
+ void (*callback)(Display*);
+} FibButton;
+
+typedef struct {
+ char name[256];
+ char path[1024];
+ uint8_t flags; // 1: hover, 2: selected, 4:add sep
+} FibPlace;
+
+static char _cur_path[1024] = "";
+static FibFileEntry *_dirlist = NULL;
+static FibPathButton *_pathbtn = NULL;
+static FibPlace *_placelist = NULL;
+static int _dircount = 0;
+static int _pathparts = 0;
+static int _placecnt = 0;
+
+static FibButton _btn_ok;
+static FibButton _btn_cancel;
+static FibButton _btn_filter;
+static FibButton _btn_places;
+static FibButton _btn_hidden;
+static FibButton *_btns[] = {&_btn_places, &_btn_filter, &_btn_hidden, &_btn_cancel, &_btn_ok};
+
+static int (*_fib_filter_function)(const char *filename);
+
+/* hardcoded layout */
+#define DSEP 6 // px; horiz space beween elements, also l+r margin for file-list
+#define PSEP 4 // px; horiz space beween paths
+#define FILECOLUMN (17 * _fib_dir_indent) //px; min width of file-column
+#define LISTTOP 2.7 //em; top of the file-browser list
+#define LISTBOT 4.75 //em; bottom of the file-browers list
+#define BTNBTMMARGIN 0.75 //em; height/margin of the button row
+#define BTNPADDING 2 // px - only used for open/cancel buttons
+#define SCROLLBARW (3 + (_fib_spc_norm&~1)) //px; - should be SCROLLBARW = (N * 2 + 3)
+#define SCROLLBOXH 10 //px; arrow box top+bottom
+#define PLACESW _fib_place_width //px;
+#define PLACESWMAX (15 *_fib_spc_norm) //px;
+#define PATHBTNTOP _fib_font_vsep //px; offset by (_fib_font_ascent);
+#define FAREAMRGB 3 //px; base L+R margin
+#define FAREAMRGR (FAREAMRGB + 1) //px; right margin of file-area + 1 (line width)
+#define FAREAMRGL (_fib_show_places ? PLACESW + FAREAMRGB : FAREAMRGB) //px; left margin of file-area
+#define TEXTSEP 4 //px;
+#define FAREATEXTL (FAREAMRGL + TEXTSEP) //px; filename text-left FAREAMRGL + TEXTSEP
+#define SORTBTNOFF -10 //px;
+
+#define DBLCLKTME 400 //msec; double click time
+#define DRAW_OUTLINE
+#define DOUBLE_BUFFER
+
+static int query_font_geometry (Display *dpy, GC gc, const char *txt, int *w, int *h, int *a, int *d) {
+ XCharStruct text_structure;
+ int font_direction, font_ascent, font_descent;
+ XFontStruct *fontinfo = XQueryFont (dpy, XGContextFromGC (gc));
+
+ if (!fontinfo) { return -1; }
+ XTextExtents (fontinfo, txt, strlen (txt), &font_direction, &font_ascent, &font_descent, &text_structure);
+ if (w) *w = XTextWidth (fontinfo, txt, strlen (txt));
+ if (h) *h = text_structure.ascent + text_structure.descent;
+ if (a) *a = text_structure.ascent;
+ if (d) *d = text_structure.descent;
+ XFreeFontInfo (NULL, fontinfo, 1);
+ return 0;
+}
+
+static void VDrawRectangle (Display *dpy, Drawable d, GC gc, int x, int y, unsigned int w, unsigned int h) {
+ const unsigned long blackColor = BlackPixel (dpy, DefaultScreen (dpy));
+#ifdef DRAW_OUTLINE
+ XSetForeground (dpy, gc, _c_gray5.pixel);
+ XDrawLine (dpy, d, gc, x + 1, y + h, x + w, y + h);
+ XDrawLine (dpy, d, gc, x + w, y + 1, x + w, y + h);
+
+ XSetForeground (dpy, gc, blackColor);
+ XDrawLine (dpy, d, gc, x + 1, y, x + w, y);
+ XDrawLine (dpy, d, gc, x, y + 1, x, y + h);
+#else
+ XSetForeground (dpy, _fib_gc, blackColor);
+ XDrawRectangle (dpy, d, gc, x, y, w, h);
+#endif
+}
+
+static void fib_expose (Display *dpy, Window realwin) {
+ int i;
+ XID win;
+ const unsigned long whiteColor = WhitePixel (dpy, DefaultScreen (dpy));
+ const unsigned long blackColor = BlackPixel (dpy, DefaultScreen (dpy));
+ if (!_fib_mapped) return;
+
+ if (_fib_resized
+#ifdef DOUBLE_BUFFER
+ || !_pixbuffer
+#endif
+ )
+ {
+#ifdef DOUBLE_BUFFER
+ unsigned int w = 0, h = 0;
+ if (_pixbuffer != None) {
+ Window ignored_w;
+ int ignored_i;
+ unsigned int ignored_u;
+ XGetGeometry(dpy, _pixbuffer, &ignored_w, &ignored_i, &ignored_i, &w, &h, &ignored_u, &ignored_u);
+ if (_fib_width != (int)w || _fib_height != (int)h) {
+ XFreePixmap (dpy, _pixbuffer);
+ _pixbuffer = None;
+ }
+ }
+ if (_pixbuffer == None) {
+ XWindowAttributes wa;
+ XGetWindowAttributes (dpy, realwin, &wa);
+ _pixbuffer = XCreatePixmap (dpy, realwin, _fib_width, _fib_height, wa.depth);
+ }
+#endif
+ if (_pixbuffer != None) {
+ XSetForeground (dpy, _fib_gc, _c_gray1.pixel);
+ XFillRectangle (dpy, _pixbuffer, _fib_gc, 0, 0, _fib_width, _fib_height);
+ } else {
+ XSetForeground (dpy, _fib_gc, _c_gray1.pixel);
+ XFillRectangle (dpy, realwin, _fib_gc, 0, 0, _fib_width, _fib_height);
+ }
+ _fib_resized = 0;
+ }
+
+ if (_pixbuffer == None) {
+ win = realwin;
+ } else {
+ win = _pixbuffer;
+ }
+
+ // Top Row: dirs and up navigation
+
+ int ppw = 0;
+ int ppx = FAREAMRGB;
+
+ for (i = _pathparts - 1; i >= 0; --i) {
+ ppw += _pathbtn[i].xw + PSEP;
+ if (ppw >= _fib_width - PSEP - _pathbtn[0].xw - FAREAMRGB) break; // XXX, first change is from "/" to "<", NOOP
+ }
+ ++i;
+ // border-less "<" parent/up, IFF space is limited
+ if (i > 0) {
+ if (0 == _hov_p || (_hov_p > 0 && _hov_p < _pathparts - 1)) {
+ XSetForeground (dpy, _fib_gc, _c_gray4.pixel);
+ } else {
+ XSetForeground (dpy, _fib_gc, blackColor);
+ }
+ XDrawString (dpy, win, _fib_gc, ppx, PATHBTNTOP, "<", 1);
+ ppx += _pathbtn[0].xw + PSEP;
+ if (i == _pathparts) --i;
+ }
+
+ _view_p = i;
+
+ while (i < _pathparts) {
+ if (i == _hov_p) {
+ XSetForeground (dpy, _fib_gc, _c_gray0.pixel);
+ } else {
+ XSetForeground (dpy, _fib_gc, _c_gray2.pixel);
+ }
+ XFillRectangle (dpy, win, _fib_gc,
+ ppx + 1, PATHBTNTOP - _fib_font_ascent,
+ _pathbtn[i].xw - 1, _fib_font_height);
+ VDrawRectangle (dpy, win, _fib_gc,
+ ppx, PATHBTNTOP - _fib_font_ascent,
+ _pathbtn[i].xw, _fib_font_height);
+ XDrawString (dpy, win, _fib_gc, ppx + 1 + BTNPADDING, PATHBTNTOP,
+ _pathbtn[i].name, strlen (_pathbtn[i].name));
+ _pathbtn[i].x0 = ppx; // current position
+ ppx += _pathbtn[i].xw + PSEP;
+ ++i;
+ }
+
+ // middle, scroll list of file names
+ const int ltop = LISTTOP * _fib_font_vsep;
+ const int llen = (_fib_height - LISTBOT * _fib_font_vsep) / _fib_font_vsep;
+ const int fsel_height = 4 + llen * _fib_font_vsep;
+ const int fsel_width = _fib_width - FAREAMRGL - FAREAMRGR - (llen < _dircount ? SCROLLBARW : 0);
+ const int t_x = FAREATEXTL;
+ int t_s = FAREATEXTL + fsel_width;
+ int t_t = FAREATEXTL + fsel_width;
+
+ // check which colums can be visible
+ // depending on available width of window.
+ _columns = 0;
+ if (fsel_width > FILECOLUMN + _fib_font_size_width + _fib_font_time_width) {
+ _columns |= 2;
+ t_s = FAREAMRGL + fsel_width - _fib_font_time_width - TEXTSEP;
+ }
+ if (fsel_width > FILECOLUMN + _fib_font_size_width) {
+ _columns |= 1;
+ t_t = t_s - _fib_font_size_width - TEXTSEP;
+ }
+
+ int fstop = _scrl_f; // first entry in scroll position
+ const int ttop = ltop - _fib_font_height + _fib_font_ascent;
+
+ if (fstop > 0 && fstop + llen > _dircount) {
+ fstop = MAX (0, _dircount - llen);
+ _scrl_f = fstop;
+ }
+
+ // list header
+ XSetForeground (dpy, _fib_gc, _c_gray3.pixel);
+ XFillRectangle (dpy, win, _fib_gc, FAREAMRGL, ltop - _fib_font_vsep, fsel_width, _fib_font_vsep);
+
+ // draw background of file list
+ XSetForeground (dpy, _fib_gc, _c_gray2.pixel);
+ XFillRectangle (dpy, win, _fib_gc, FAREAMRGL, ltop, fsel_width, fsel_height);
+
+#ifdef DRAW_OUTLINE
+ VDrawRectangle (dpy, win, _fib_gc, FAREAMRGL, ltop - _fib_font_vsep -1, _fib_width - FAREAMRGL - FAREAMRGR, fsel_height + _fib_font_vsep + 1);
+#endif
+
+ switch (_hov_h) {
+ case 1:
+ XSetForeground (dpy, _fib_gc, _c_gray0.pixel);
+ XFillRectangle (dpy, win, _fib_gc, t_x + _fib_dir_indent - TEXTSEP + 1, ltop - _fib_font_vsep, t_t - t_x - _fib_dir_indent - 1, _fib_font_vsep);
+ break;
+ case 2:
+ XSetForeground (dpy, _fib_gc, _c_gray0.pixel);
+ XFillRectangle (dpy, win, _fib_gc, t_t - TEXTSEP + 1, ltop - _fib_font_vsep, _fib_font_size_width + TEXTSEP - 1, _fib_font_vsep);
+ break;
+ case 3:
+ XSetForeground (dpy, _fib_gc, _c_gray0.pixel);
+ XFillRectangle (dpy, win, _fib_gc, t_s - TEXTSEP + 1, ltop - _fib_font_vsep, TEXTSEP + TEXTSEP + _fib_font_time_width - 1, _fib_font_vsep);
+ break;
+ default:
+ break;
+ }
+
+ // column headings and sort order
+ int arp = MAX (2, _fib_font_height / 5); // arrow scale
+ const int trioff = _fib_font_height - _fib_font_ascent - arp + 1;
+ XPoint ptri[4] = { {0, ttop - trioff }, {arp, -arp - arp - 1}, {-arp - arp, 0}, {arp, arp + arp + 1}};
+ if (_sort & 1) {
+ ptri[0].y = ttop -arp - arp - 1;
+ ptri[1].y *= -1;
+ ptri[3].y *= -1;
+ }
+ switch (_sort) {
+ case 0:
+ case 1:
+ ptri[0].x = t_t + SORTBTNOFF + 2 - arp;
+ XSetForeground (dpy, _fib_gc, _c_gray6.pixel);
+ XFillPolygon (dpy, win, _fib_gc, ptri, 3, Convex, CoordModePrevious);
+ XDrawLines (dpy, win, _fib_gc, ptri, 4, CoordModePrevious);
+ break;
+ case 2:
+ case 3:
+ if (_columns & 1) {
+ ptri[0].x = t_s + SORTBTNOFF + 2 - arp;
+ XSetForeground (dpy, _fib_gc, _c_gray6.pixel);
+ XFillPolygon (dpy, win, _fib_gc, ptri, 3, Convex, CoordModePrevious);
+ XDrawLines (dpy, win, _fib_gc, ptri, 4, CoordModePrevious);
+ }
+ break;
+ case 4:
+ case 5:
+ if (_columns & 2) {
+ ptri[0].x = FAREATEXTL + fsel_width + SORTBTNOFF + 2 - arp;
+ XSetForeground (dpy, _fib_gc, _c_gray6.pixel);
+ XFillPolygon (dpy, win, _fib_gc, ptri, 3, Convex, CoordModePrevious);
+ XDrawLines (dpy, win, _fib_gc, ptri, 4, CoordModePrevious);
+ }
+ break;
+ }
+
+#if 0 // bottom header bottom border
+ XSetForeground (dpy, _fib_gc, _c_gray5.pixel);
+ XSetLineAttributes (dpy, _fib_gc, 1, LineOnOffDash, CapButt, JoinMiter);
+ XDrawLine (dpy, win, _fib_gc,
+ FAREAMRGL + 1, ltop,
+ FAREAMRGL + fsel_width, ltop);
+ XSetLineAttributes (dpy, _fib_gc, 1, LineSolid, CapButt, JoinMiter);
+#endif
+
+ XSetForeground (dpy, _fib_gc, _c_gray4.pixel);
+ XDrawLine (dpy, win, _fib_gc,
+ t_x + _fib_dir_indent - TEXTSEP, ltop - _fib_font_vsep + 3,
+ t_x + _fib_dir_indent - TEXTSEP, ltop - 3);
+
+ XSetForeground (dpy, _fib_gc, blackColor);
+ XDrawString (dpy, win, _fib_gc, t_x + _fib_dir_indent, ttop, "Name", 4);
+
+ if (_columns & 1) {
+ XSetForeground (dpy, _fib_gc, _c_gray4.pixel);
+ XDrawLine (dpy, win, _fib_gc,
+ t_t - TEXTSEP, ltop - _fib_font_vsep + 3,
+ t_t - TEXTSEP, ltop - 3);
+ XSetForeground (dpy, _fib_gc, blackColor);
+ XDrawString (dpy, win, _fib_gc, t_t, ttop, "Size", 4);
+ }
+
+ if (_columns & 2) {
+ XSetForeground (dpy, _fib_gc, _c_gray4.pixel);
+ XDrawLine (dpy, win, _fib_gc,
+ t_s - TEXTSEP, ltop - _fib_font_vsep + 3,
+ t_s - TEXTSEP, ltop - 3);
+ XSetForeground (dpy, _fib_gc, blackColor);
+ if (_pathparts > 0)
+ XDrawString (dpy, win, _fib_gc, t_s, ttop, "Last Modified", 13);
+ else
+ XDrawString (dpy, win, _fib_gc, t_s, ttop, "Last Used", 9);
+ }
+
+ // scrollbar sep
+ if (llen < _dircount) {
+ const int sx0 = _fib_width - SCROLLBARW - FAREAMRGR;
+ XSetForeground (dpy, _fib_gc, _c_gray4.pixel);
+ XDrawLine (dpy, win, _fib_gc,
+ sx0 - 1, ltop - _fib_font_vsep,
+#ifdef DRAW_OUTLINE
+ sx0 - 1, ltop + fsel_height
+#else
+ sx0 - 1, ltop - 1
+#endif
+ );
+ }
+
+ // clip area for file-name
+ XRectangle clp = {FAREAMRGL + 1, ltop, t_t - FAREAMRGL - TEXTSEP - TEXTSEP - 1, fsel_height};
+
+ // list files in view
+ for (i = 0; i < llen; ++i) {
+ const int j = i + fstop;
+ if (j >= _dircount) break;
+
+ const int t_y = ltop + (i+1) * _fib_font_vsep - 4;
+
+ XSetForeground (dpy, _fib_gc, blackColor);
+ if (_dirlist[j].flags & 2) {
+ XSetForeground (dpy, _fib_gc, blackColor);
+ XFillRectangle (dpy, win, _fib_gc,
+ FAREAMRGL, t_y - _fib_font_ascent, fsel_width, _fib_font_height);
+ XSetForeground (dpy, _fib_gc, whiteColor);
+ }
+ if (_hov_f == j && !(_dirlist[j].flags & 2)) {
+ XSetForeground (dpy, _fib_gc, _c_gray4.pixel);
+ }
+ if (_dirlist[j].flags & 4) {
+ XDrawString (dpy, win, _fib_gc, t_x, t_y, "D", 1);
+ }
+ XSetClipRectangles (dpy, _fib_gc, 0, 0, &clp, 1, Unsorted);
+ XDrawString (dpy, win, _fib_gc,
+ t_x + _fib_dir_indent, t_y,
+ _dirlist[j].name, strlen (_dirlist[j].name));
+ XSetClipMask (dpy, _fib_gc, None);
+
+ if (_columns & 1) // right-aligned 'size'
+ XDrawString (dpy, win, _fib_gc,
+ t_s - TEXTSEP - 2 - _dirlist[j].ssizew, t_y,
+ _dirlist[j].strsize, strlen (_dirlist[j].strsize));
+ if (_columns & 2)
+ XDrawString (dpy, win, _fib_gc,
+ t_s, t_y,
+ _dirlist[j].strtime, strlen (_dirlist[j].strtime));
+ }
+
+ // scrollbar
+ if (llen < _dircount) {
+ float sl = (fsel_height + _fib_font_vsep - (SCROLLBOXH + SCROLLBOXH)) / (float) _dircount;
+ sl = MAX ((8. / llen), sl); // 8px min height of scroller
+ const int sy1 = llen * sl;
+ const float mx = (fsel_height + _fib_font_vsep - (SCROLLBOXH + SCROLLBOXH) - sy1) / (float)(_dircount - llen);
+ const int sy0 = fstop * mx;
+ const int sx0 = _fib_width - SCROLLBARW - FAREAMRGR;
+ const int stop = ltop - _fib_font_vsep;
+
+ _scrl_y0 = stop + SCROLLBOXH + sy0;
+ _scrl_y1 = _scrl_y0 + sy1;
+
+ assert (fstop + llen <= _dircount);
+ // scroll-bar background
+ XSetForeground (dpy, _fib_gc, _c_gray3.pixel);
+ XFillRectangle (dpy, win, _fib_gc, sx0, stop, SCROLLBARW, fsel_height + _fib_font_vsep);
+
+ // scroller
+ if (_hov_s == 0) {
+ XSetForeground (dpy, _fib_gc, _c_gray0.pixel);
+ } else {
+ XSetForeground (dpy, _fib_gc, _c_gray1.pixel);
+ }
+ XFillRectangle (dpy, win, _fib_gc, sx0 + 1, stop + SCROLLBOXH + sy0, SCROLLBARW - 2, sy1);
+
+ int scrw = (SCROLLBARW -3) / 2;
+ // arrows top and bottom
+ if (_hov_s == 1) {
+ XSetForeground (dpy, _fib_gc, _c_gray0.pixel);
+ } else {
+ XSetForeground (dpy, _fib_gc, _c_gray1.pixel);
+ }
+ XPoint ptst[4] = { {sx0 + 1, stop + 8}, {scrw, -7}, {scrw, 7}, {-2 * scrw, 0}};
+ XFillPolygon (dpy, win, _fib_gc, ptst, 3, Convex, CoordModePrevious);
+ XDrawLines (dpy, win, _fib_gc, ptst, 4, CoordModePrevious);
+
+ if (_hov_s == 2) {
+ XSetForeground (dpy, _fib_gc, _c_gray0.pixel);
+ } else {
+ XSetForeground (dpy, _fib_gc, _c_gray1.pixel);
+ }
+ XPoint ptsb[4] = { {sx0 + 1, ltop + fsel_height - 9}, {2*scrw, 0}, {-scrw, 7}, {-scrw, -7}};
+ XFillPolygon (dpy, win, _fib_gc, ptsb, 3, Convex, CoordModePrevious);
+ XDrawLines (dpy, win, _fib_gc, ptsb, 4, CoordModePrevious);
+ } else {
+ _scrl_y0 = _scrl_y1 = -1;
+ }
+
+ if (_fib_show_places) {
+ assert (_placecnt > 0);
+
+ // heading
+ XSetForeground (dpy, _fib_gc, _c_gray3.pixel);
+ XFillRectangle (dpy, win, _fib_gc, FAREAMRGB, ltop - _fib_font_vsep, PLACESW - TEXTSEP, _fib_font_vsep);
+
+ // body
+ XSetForeground (dpy, _fib_gc, _c_gray2.pixel);
+ XFillRectangle (dpy, win, _fib_gc, FAREAMRGB, ltop, PLACESW - TEXTSEP, fsel_height);
+
+#ifdef DRAW_OUTLINE
+ VDrawRectangle (dpy, win, _fib_gc, FAREAMRGB, ltop - _fib_font_vsep -1, PLACESW - TEXTSEP, fsel_height + _fib_font_vsep + 1);
+#endif
+
+ XSetForeground (dpy, _fib_gc, blackColor);
+ XDrawString (dpy, win, _fib_gc, FAREAMRGB + TEXTSEP, ttop, "Places", 6);
+
+ XRectangle pclip = {FAREAMRGB + 1, ltop, PLACESW - TEXTSEP -1, fsel_height};
+ XSetClipRectangles (dpy, _fib_gc, 0, 0, &pclip, 1, Unsorted);
+ const int plx = FAREAMRGB + TEXTSEP;
+ for (i = 0; i < llen && i < _placecnt; ++i) {
+ const int ply = ltop + (i+1) * _fib_font_vsep - 4;
+ if (i == _hov_l) {
+ XSetForeground (dpy, _fib_gc, _c_gray4.pixel);
+ } else {
+ XSetForeground (dpy, _fib_gc, blackColor);
+ }
+ XDrawString (dpy, win, _fib_gc,
+ plx, ply,
+ _placelist[i].name, strlen (_placelist[i].name));
+ if (_placelist[i].flags & 4) {
+ XSetForeground (dpy, _fib_gc, _c_gray3.pixel);
+ const int plly = ply - _fib_font_ascent + _fib_font_height;
+ const int pllx0 = FAREAMRGB;
+ const int pllx1 = FAREAMRGB + (PLACESW - TEXTSEP);
+ XDrawLine (dpy, win, _fib_gc, pllx0, plly, pllx1, plly);
+ }
+ }
+ XSetClipMask (dpy, _fib_gc, None);
+
+ if (_placecnt > llen) {
+ const int plly = ltop + fsel_height - _fib_font_height + _fib_font_ascent;
+ const int pllx0 = FAREAMRGB + (PLACESW - TEXTSEP) * .75;
+ const int pllx1 = FAREAMRGB + (PLACESW - TEXTSEP - TEXTSEP);
+
+ XSetForeground (dpy, _fib_gc, blackColor);
+ XSetLineAttributes (dpy, _fib_gc, 1, LineOnOffDash, CapButt, JoinMiter);
+ XDrawLine (dpy, win, _fib_gc, pllx0, plly, pllx1, plly);
+ XSetLineAttributes (dpy, _fib_gc, 1, LineSolid, CapButt, JoinMiter);
+ }
+ }
+
+ // Bottom Buttons
+ const int numb = sizeof(_btns) / sizeof(FibButton*);
+ int xtra = _fib_width - _btn_span;
+ const int cbox = _fib_font_ascent - 2;
+ const int bbase = _fib_height - BTNBTMMARGIN * _fib_font_vsep - BTNPADDING;
+ const int cblw = cbox > 20 ? 5 : ( cbox > 9 ? 3 : 1);
+
+ int bx = FAREAMRGB;
+ for (i = 0; i < numb; ++i) {
+ if (_btns[i]->flags & 8) { continue; }
+ if (_btns[i]->flags & 4) {
+ // checkbutton
+ const int cby0 = bbase - cbox + 1 + BTNPADDING;
+ if (i == _hov_b) {
+ XSetForeground (dpy, _fib_gc, _c_gray4.pixel);
+ } else {
+ XSetForeground (dpy, _fib_gc, blackColor);
+ }
+ XDrawRectangle (dpy, win, _fib_gc,
+ bx, cby0 - 1, cbox + 1, cbox + 1);
+
+ if (i == _hov_b) {
+ XSetForeground (dpy, _fib_gc, _c_gray5.pixel);
+ } else {
+ XSetForeground (dpy, _fib_gc, blackColor);
+ }
+ XDrawString (dpy, win, _fib_gc, BTNPADDING + bx + _fib_font_ascent, 1 + bbase + BTNPADDING,
+ _btns[i]->text, strlen (_btns[i]->text));
+
+ if (i == _hov_b) {
+ XSetForeground (dpy, _fib_gc, _c_gray0.pixel);
+ } else {
+ if (_btns[i]->flags & 2) {
+ XSetForeground (dpy, _fib_gc, _c_gray1.pixel);
+ } else {
+ XSetForeground (dpy, _fib_gc, _c_gray2.pixel);
+ }
+ }
+ XFillRectangle (dpy, win, _fib_gc,
+ bx+1, cby0, cbox, cbox);
+
+ if (_btns[i]->flags & 2) {
+ XSetLineAttributes (dpy, _fib_gc, cblw, LineSolid, CapRound, JoinMiter);
+ XSetForeground (dpy, _fib_gc, _c_gray6.pixel);
+ XDrawLine (dpy, win, _fib_gc,
+ bx + 2, cby0 + 1,
+ bx + cbox - 1, cby0 + cbox - 2);
+ XDrawLine (dpy, win, _fib_gc,
+ bx + cbox - 1, cby0 + 1,
+ bx + 2, cby0 + cbox - 2);
+ XSetLineAttributes (dpy, _fib_gc, 1, LineSolid, CapButt, JoinMiter);
+ }
+ } else {
+ if (xtra > 0) {
+ bx += xtra;
+ xtra = 0;
+ }
+ // pushbutton
+
+ uint8_t can_hover = 1; // special case
+ if (_btns[i] == &_btn_ok) {
+ if (_fsel < 0 || _fsel >= _dircount) {
+ can_hover = 0;
+ }
+ }
+
+ if (can_hover && i == _hov_b) {
+ XSetForeground (dpy, _fib_gc, _c_gray0.pixel);
+ } else {
+ XSetForeground (dpy, _fib_gc, _c_gray2.pixel);
+ }
+ XFillRectangle (dpy, win, _fib_gc,
+ bx + 1, bbase - _fib_font_ascent,
+ _btn_w - 1, _fib_font_height + BTNPADDING + BTNPADDING);
+ VDrawRectangle (dpy, win, _fib_gc,
+ bx, bbase - _fib_font_ascent,
+ _btn_w, _fib_font_height + BTNPADDING + BTNPADDING);
+ XDrawString (dpy, win, _fib_gc, bx + (_btn_w - _btns[i]->tw) * .5, 1 + bbase + BTNPADDING,
+ _btns[i]->text, strlen (_btns[i]->text));
+ }
+ _btns[i]->x0 = bx;
+ bx += _btns[i]->xw + DSEP;
+ }
+
+ if (_pixbuffer != None) {
+ XCopyArea(dpy, _pixbuffer, realwin, _fib_gc, 0, 0, _fib_width, _fib_height, 0, 0);
+ }
+ XFlush (dpy);
+}
+
+static void fib_reset () {
+ _hov_p = _hov_f = _hov_h = _hov_l = -1;
+ _scrl_f = 0;
+ _fib_resized = 1;
+}
+
+static int cmp_n_up (const void *p1, const void *p2) {
+ FibFileEntry *a = (FibFileEntry*) p1;
+ FibFileEntry *b = (FibFileEntry*) p2;
+ if ((a->flags & 4) && !(b->flags & 4)) return -1;
+ if (!(a->flags & 4) && (b->flags & 4)) return 1;
+ return strcmp (a->name, b->name);
+}
+
+static int cmp_n_down (const void *p1, const void *p2) {
+ FibFileEntry *a = (FibFileEntry*) p1;
+ FibFileEntry *b = (FibFileEntry*) p2;
+ if ((a->flags & 4) && !(b->flags & 4)) return -1;
+ if (!(a->flags & 4) && (b->flags & 4)) return 1;
+ return strcmp (b->name, a->name);
+}
+
+static int cmp_t_up (const void *p1, const void *p2) {
+ FibFileEntry *a = (FibFileEntry*) p1;
+ FibFileEntry *b = (FibFileEntry*) p2;
+ if ((a->flags & 4) && !(b->flags & 4)) return -1;
+ if (!(a->flags & 4) && (b->flags & 4)) return 1;
+ if (a->mtime == b->mtime) return 0;
+ return a->mtime > b->mtime ? -1 : 1;
+}
+
+static int cmp_t_down (const void *p1, const void *p2) {
+ FibFileEntry *a = (FibFileEntry*) p1;
+ FibFileEntry *b = (FibFileEntry*) p2;
+ if ((a->flags & 4) && !(b->flags & 4)) return -1;
+ if (!(a->flags & 4) && (b->flags & 4)) return 1;
+ if (a->mtime == b->mtime) return 0;
+ return a->mtime > b->mtime ? 1 : -1;
+}
+
+static int cmp_s_up (const void *p1, const void *p2) {
+ FibFileEntry *a = (FibFileEntry*) p1;
+ FibFileEntry *b = (FibFileEntry*) p2;
+ if ((a->flags & 4) && (b->flags & 4)) return 0; // dir, no size, retain order
+ if ((a->flags & 4) && !(b->flags & 4)) return -1;
+ if (!(a->flags & 4) && (b->flags & 4)) return 1;
+ if (a->size == b->size) return 0;
+ return a->size > b->size ? -1 : 1;
+}
+
+static int cmp_s_down (const void *p1, const void *p2) {
+ FibFileEntry *a = (FibFileEntry*) p1;
+ FibFileEntry *b = (FibFileEntry*) p2;
+ if ((a->flags & 4) && (b->flags & 4)) return 0; // dir, no size, retain order
+ if ((a->flags & 4) && !(b->flags & 4)) return -1;
+ if (!(a->flags & 4) && (b->flags & 4)) return 1;
+ if (a->size == b->size) return 0;
+ return a->size > b->size ? 1 : -1;
+}
+
+static void fmt_size (Display *dpy, FibFileEntry *f) {
+ if (f->size > 10995116277760) {
+ sprintf (f->strsize, "%.0f TB", f->size / 1099511627776.f);
+ }
+ if (f->size > 1099511627776) {
+ sprintf (f->strsize, "%.1f TB", f->size / 1099511627776.f);
+ }
+ else if (f->size > 10737418240) {
+ sprintf (f->strsize, "%.0f GB", f->size / 1073741824.f);
+ }
+ else if (f->size > 1073741824) {
+ sprintf (f->strsize, "%.1f GB", f->size / 1073741824.f);
+ }
+ else if (f->size > 10485760) {
+ sprintf (f->strsize, "%.0f MB", f->size / 1048576.f);
+ }
+ else if (f->size > 1048576) {
+ sprintf (f->strsize, "%.1f MB", f->size / 1048576.f);
+ }
+ else if (f->size > 10240) {
+ sprintf (f->strsize, "%.0f KB", f->size / 1024.f);
+ }
+ else if (f->size >= 1000) {
+ sprintf (f->strsize, "%.1f KB", f->size / 1024.f);
+ }
+ else {
+ sprintf (f->strsize, "%.0f B", f->size / 1.f);
+ }
+ int sw = 0;
+ query_font_geometry (dpy, _fib_gc, f->strsize, &sw, NULL, NULL, NULL);
+ if (sw > _fib_font_size_width) {
+ _fib_font_size_width = sw;
+ }
+ f->ssizew = sw;
+}
+
+static void fmt_time (Display *dpy, FibFileEntry *f) {
+ struct tm *tmp;
+ tmp = localtime (&f->mtime);
+ if (!tmp) {
+ return;
+ }
+ strftime (f->strtime, sizeof(f->strtime), "%F %H:%M", tmp);
+
+ int tw = 0;
+ query_font_geometry (dpy, _fib_gc, f->strtime, &tw, NULL, NULL, NULL);
+ if (tw > _fib_font_time_width) {
+ _fib_font_time_width = tw;
+ }
+}
+
+static void fib_resort (const char * sel) {
+ if (_dircount < 1) { return; }
+ int (*sortfn)(const void *p1, const void *p2);
+ switch (_sort) {
+ case 1: sortfn = &cmp_n_down; break;
+ case 2: sortfn = &cmp_s_down; break;
+ case 3: sortfn = &cmp_s_up; break;
+ case 4: sortfn = &cmp_t_down; break;
+ case 5: sortfn = &cmp_t_up; break;
+ default:
+ sortfn = &cmp_n_up;
+ break;
+ }
+ qsort (_dirlist, _dircount, sizeof(_dirlist[0]), sortfn);
+ int i;
+ for (i = 0; i < _dircount && sel; ++i) {
+ if (!strcmp (_dirlist[i].name, sel)) {
+ _fsel = i;
+ break;
+ }
+ }
+}
+
+static void fib_select (Display *dpy, int item) {
+ if (_fsel >= 0) {
+ _dirlist[_fsel].flags &= ~2;
+ }
+ _fsel = item;
+ if (_fsel >= 0 && _fsel < _dircount) {
+ _dirlist[_fsel].flags |= 2;
+ const int llen = (_fib_height - LISTBOT * _fib_font_vsep) / _fib_font_vsep;
+ if (_fsel < _scrl_f) {
+ _scrl_f = _fsel;
+ }
+ else if (_fsel >= _scrl_f + llen) {
+ _scrl_f = 1 + _fsel - llen;
+ }
+ } else {
+ _fsel = -1;
+ }
+
+ fib_expose (dpy, _fib_win);
+}
+
+static inline int fib_filter (const char *name) {
+ if (_fib_filter_function) {
+ return _fib_filter_function (name);
+ } else {
+ return 1;
+ }
+}
+
+static void fib_pre_opendir (Display *dpy) {
+ if (_dirlist) free (_dirlist);
+ if (_pathbtn) free (_pathbtn);
+ _dirlist = NULL;
+ _pathbtn = NULL;
+ _dircount = 0;
+ _pathparts = 0;
+ query_font_geometry (dpy, _fib_gc, "Size ", &_fib_font_size_width, NULL, NULL, NULL);
+ fib_reset ();
+ _fsel = -1;
+}
+
+static void fib_post_opendir (Display *dpy, const char *sel) {
+ if (_dircount > 0)
+ _fsel = 0; // select first
+ else
+ _fsel = -1;
+ fib_resort (sel);
+
+ if (_dircount > 0 && _fsel >= 0) {
+ fib_select (dpy, _fsel);
+ } else {
+ fib_expose (dpy, _fib_win);
+ }
+}
+
+static int fib_dirlistadd (Display *dpy, const int i, const char* path, const char *name, time_t mtime) {
+ char tp[1024];
+ struct stat fs;
+ if (!_fib_hidden_fn && name[0] == '.') return -1;
+ if (!strcmp (name, ".")) return -1;
+ if (!strcmp (name, "..")) return -1;
+ strcpy (tp, path);
+ strcat (tp, name);
+ if (access (tp, R_OK)) {
+ return -1;
+ }
+ if (stat (tp, &fs)) {
+ return -1;
+ }
+ assert (i < _dircount); // could happen if dir changes while we're reading.
+ if (i >= _dircount) return -1;
+ if (S_ISDIR (fs.st_mode)) {
+ _dirlist[i].flags |= 4;
+ }
+ else if (S_ISREG (fs.st_mode)) {
+ if (!fib_filter (name)) return -1;
+ }
+#if 0 // only needed with lstat()
+ else if (S_ISLNK (fs.st_mode)) {
+ if (!fib_filter (name)) return -1;
+ }
+#endif
+ else {
+ return -1;
+ }
+ strcpy (_dirlist[i].name, name);
+ _dirlist[i].mtime = mtime > 0 ? mtime : fs.st_mtime;
+ _dirlist[i].size = fs.st_size;
+ if (!(_dirlist[i].flags & 4))
+ fmt_size (dpy, &_dirlist[i]);
+ fmt_time (dpy, &_dirlist[i]);
+ return 0;
+}
+
+static int fib_openrecent (Display *dpy, const char *sel) {
+ int i;
+ unsigned int j;
+ assert (_recentcnt > 0);
+ fib_pre_opendir (dpy);
+ query_font_geometry (dpy, _fib_gc, "Last Used", &_fib_font_time_width, NULL, NULL, NULL);
+ _dirlist = (FibFileEntry*) calloc (_recentcnt, sizeof(FibFileEntry));
+ _dircount = _recentcnt;
+ for (j = 0, i = 0; j < _recentcnt; ++j) {
+ char base[1024];
+ char *s = strrchr (_recentlist[j].path, '/');
+ if (!s || !*++s) continue;
+ size_t len = (s - _recentlist[j].path);
+ strncpy (base, _recentlist[j].path, len);
+ base[len] = '\0';
+ if (!fib_dirlistadd (dpy, i, base, s, _recentlist[j].atime)) {
+ _dirlist[i].rfp = &_recentlist[j];
+ _dirlist[i].flags |= 8;
+ ++i;
+ }
+ }
+ _dircount = i;
+ fib_post_opendir (dpy, sel);
+ return _dircount;
+}
+
+static int fib_opendir (Display *dpy, const char* path, const char *sel) {
+ char *t0, *t1;
+ int i;
+
+ assert (path);
+
+ if (strlen (path) == 0 && _recentcnt > 0) { // XXX we should use a better indication for this
+ strcpy (_cur_path, "");
+ return fib_openrecent (dpy, sel);
+ }
+
+ assert (strlen (path) < sizeof(_cur_path) -1);
+ assert (strlen (path) > 0);
+ assert (strstr (path, "//") == NULL);
+ assert (path[0] == '/');
+
+ fib_pre_opendir (dpy);
+
+ query_font_geometry (dpy, _fib_gc, "Last Modified", &_fib_font_time_width, NULL, NULL, NULL);
+ DIR *dir = opendir (path);
+ if (!dir) {
+ strcpy (_cur_path, "/");
+ } else {
+ int i;
+ struct dirent *de;
+ strcpy (_cur_path, path);
+
+ if (_cur_path[strlen (_cur_path) -1] != '/')
+ strcat (_cur_path, "/");
+
+ while ((de = readdir (dir))) {
+ if (!_fib_hidden_fn && de->d_name[0] == '.') continue;
+ ++_dircount;
+ }
+
+ if (_dircount > 0)
+ _dirlist = (FibFileEntry*) calloc (_dircount, sizeof(FibFileEntry));
+
+ rewinddir (dir);
+
+ i = 0;
+ while ((de = readdir (dir))) {
+ if (!fib_dirlistadd (dpy, i, _cur_path, de->d_name, 0))
+ ++i;
+ }
+ _dircount = i;
+ closedir (dir);
+ }
+
+ t0 = _cur_path;
+ while (*t0 && (t0 = strchr (t0, '/'))) {
+ ++_pathparts;
+ ++t0;
+ }
+ assert (_pathparts > 0);
+ _pathbtn = (FibPathButton*) calloc (_pathparts + 1, sizeof(FibPathButton));
+
+ t1 = _cur_path;
+ i = 0;
+ while (*t1 && (t0 = strchr (t1, '/'))) {
+ if (i == 0) {
+ strcpy (_pathbtn[i].name, "/");
+ } else {
+ *t0 = 0;
+ strcpy (_pathbtn[i].name, t1);
+ }
+ query_font_geometry (dpy, _fib_gc, _pathbtn[i].name, &_pathbtn[i].xw, NULL, NULL, NULL);
+ _pathbtn[i].xw += BTNPADDING + BTNPADDING;
+ *t0 = '/';
+ t1 = t0 + 1;
+ ++i;
+ }
+ fib_post_opendir (dpy, sel);
+ return _dircount;
+}
+
+static int fib_open (Display *dpy, int item) {
+ char tp[1024];
+ if (_dirlist[item].flags & 8) {
+ assert (_dirlist[item].rfp);
+ strcpy (_rv_open, _dirlist[item].rfp->path);
+ _status = 1;
+ return 0;
+ }
+ strcpy (tp, _cur_path);
+ strcat (tp, _dirlist[item].name);
+ if (_dirlist[item].flags & 4) {
+ fib_opendir (dpy, tp, NULL);
+ return 0;
+ } else {
+ _status = 1;
+ strcpy (_rv_open, tp);
+ }
+ return 0;
+}
+
+static void cb_cancel (Display *dpy) {
+ _status = -1;
+
+ // unused
+ return; (void)dpy;
+}
+
+static void cb_open (Display *dpy) {
+ if (_fsel >= 0 && _fsel < _dircount) {
+ fib_open (dpy, _fsel);
+ }
+}
+
+static void sync_button_states () {
+ if (_fib_show_places)
+ _btn_places.flags |= 2;
+ else
+ _btn_places.flags &= ~2;
+ if (_fib_filter_fn) // inverse -> show all
+ _btn_filter.flags &= ~2;
+ else
+ _btn_filter.flags |= 2;
+ if (_fib_hidden_fn)
+ _btn_hidden.flags |= 2;
+ else
+ _btn_hidden.flags &= ~2;
+}
+
+static void cb_places (Display *dpy) {
+ _fib_show_places = ! _fib_show_places;
+ if (_placecnt < 1)
+ _fib_show_places = 0;
+ sync_button_states ();
+ _fib_resized = 1;
+ fib_expose (dpy, _fib_win);
+}
+
+static void cb_filter (Display *dpy) {
+ _fib_filter_fn = ! _fib_filter_fn;
+ sync_button_states ();
+ char *sel = _fsel >= 0 ? strdup (_dirlist[_fsel].name) : NULL;
+ fib_opendir (dpy, _cur_path, sel);
+ free (sel);
+}
+
+static void cb_hidden (Display *dpy) {
+ _fib_hidden_fn = ! _fib_hidden_fn;
+ sync_button_states ();
+ char *sel = _fsel >= 0 ? strdup (_dirlist[_fsel].name) : NULL;
+ fib_opendir (dpy, _cur_path, sel);
+ free (sel);
+}
+
+static int fib_widget_at_pos (Display *dpy, int x, int y, int *it) {
+ const int btop = _fib_height - BTNBTMMARGIN * _fib_font_vsep - _fib_font_ascent - BTNPADDING;
+ const int bbot = btop + _fib_font_height + BTNPADDING + BTNPADDING;
+ const int llen = (_fib_height - LISTBOT * _fib_font_vsep) / _fib_font_vsep;
+ const int ltop = LISTTOP * _fib_font_vsep;
+ const int fbot = ltop + 4 + llen * _fib_font_vsep;
+ const int ptop = PATHBTNTOP - _fib_font_ascent;
+ assert (it);
+
+ // paths at top
+ if (y > ptop && y < ptop + _fib_font_height && _view_p >= 0 && _pathparts > 0) {
+ int i = _view_p;
+ *it = -1;
+ if (i > 0) { // special case '<'
+ if (x > FAREAMRGB && x <= FAREAMRGB + _pathbtn[0].xw) {
+ *it = _view_p - 1;
+ i = _pathparts;
+ }
+ }
+ while (i < _pathparts) {
+ if (x >= _pathbtn[i].x0 && x <= _pathbtn[i].x0 + _pathbtn[i].xw) {
+ *it = i;
+ break;
+ }
+ ++i;
+ }
+ assert (*it < _pathparts);
+ if (*it >= 0) return 1;
+ else return 0;
+ }
+
+ // buttons at bottom
+ if (y > btop && y < bbot) {
+ size_t i;
+ *it = -1;
+ for (i = 0; i < sizeof(_btns) / sizeof(FibButton*); ++i) {
+ const int bx = _btns[i]->x0;
+ if (_btns[i]->flags & 8) { continue; }
+ if (x > bx && x < bx + _btns[i]->xw) {
+ *it = i;
+ }
+ }
+ if (*it >= 0) return 3;
+ else return 0;
+ }
+
+ // main file area
+ if (y >= ltop - _fib_font_vsep && y < fbot && x > FAREAMRGL && x < _fib_width - FAREAMRGR) {
+ // scrollbar
+ if (_scrl_y0 > 0 && x >= _fib_width - (FAREAMRGR + SCROLLBARW) && x <= _fib_width - FAREAMRGR) {
+ if (y >= _scrl_y0 && y < _scrl_y1) {
+ *it = 0;
+ } else if (y >= _scrl_y1) {
+ *it = 2;
+ } else {
+ *it = 1;
+ }
+ return 4;
+ }
+ // file-list
+ else if (y >= ltop) {
+ const int item = (y - ltop) / _fib_font_vsep + _scrl_f;
+ *it = -1;
+ if (item >= 0 && item < _dircount) {
+ *it = item;
+ }
+ if (*it >= 0) return 2;
+ else return 0;
+ }
+ else {
+ *it = -1;
+ const int fsel_width = _fib_width - FAREAMRGL - FAREAMRGR - (llen < _dircount ? SCROLLBARW : 0);
+ const int t_s = FAREAMRGL + fsel_width - _fib_font_time_width - TEXTSEP - TEXTSEP;
+ const int t_t = FAREAMRGL + fsel_width - TEXTSEP - _fib_font_size_width - ((_columns & 2) ? ( _fib_font_time_width + TEXTSEP + TEXTSEP) : 0);
+ if (x >= fsel_width + FAREAMRGL) ;
+ else if ((_columns & 2) && x >= t_s) *it = 3;
+ else if ((_columns & 1) && x >= t_t) *it = 2;
+ else if (x >= FAREATEXTL + _fib_dir_indent - TEXTSEP) *it = 1;
+ if (*it >= 0) return 5;
+ else return 0;
+ }
+ }
+
+ // places list
+ if (_fib_show_places && y >= ltop && y < fbot && x > FAREAMRGB && x < FAREAMRGL - FAREAMRGB) {
+ const int item = (y - ltop) / _fib_font_vsep;
+ *it = -1;
+ if (item >= 0 && item < _placecnt) {
+ *it = item;
+ }
+ if (*it >= 0) return 6;
+ else return 0;
+ }
+
+ return 0;
+
+ // unused
+ (void)dpy;
+}
+
+static void fib_update_hover (Display *dpy, int need_expose, const int type, const int item) {
+ int hov_p = -1;
+ int hov_b = -1;
+ int hov_h = -1;
+ int hov_s = -1;
+#ifdef LIST_ENTRY_HOVER
+ int hov_f = -1;
+ int hov_l = -1;
+#endif
+
+ switch (type) {
+ case 1: hov_p = item; break;
+ case 3: hov_b = item; break;
+ case 4: hov_s = item; break;
+ case 5: hov_h = item; break;
+#ifdef LIST_ENTRY_HOVER
+ case 6: hov_l = item; break;
+ case 2: hov_f = item; break;
+#endif
+ default: break;
+ }
+#ifdef LIST_ENTRY_HOVER
+ if (hov_f != _hov_f) { _hov_f = hov_f; need_expose = 1; }
+ if (hov_l != _hov_l) { _hov_l = hov_l; need_expose = 1; }
+#endif
+ if (hov_b != _hov_b) { _hov_b = hov_b; need_expose = 1; }
+ if (hov_p != _hov_p) { _hov_p = hov_p; need_expose = 1; }
+ if (hov_h != _hov_h) { _hov_h = hov_h; need_expose = 1; }
+ if (hov_s != _hov_s) { _hov_s = hov_s; need_expose = 1; }
+
+ if (need_expose) {
+ fib_expose (dpy, _fib_win);
+ }
+}
+
+static void fib_motion (Display *dpy, int x, int y) {
+ int it = -1;
+
+ if (_scrl_my >= 0) {
+ const int sdiff = y - _scrl_my;
+ const int llen = (_fib_height - LISTBOT * _fib_font_vsep) / _fib_font_vsep;
+ const int fsel_height = 4 + llen * _fib_font_vsep;
+ const float sl = (fsel_height + _fib_font_vsep - (SCROLLBOXH + SCROLLBOXH)) / (float) _dircount;
+
+ int news = _scrl_mf + sdiff / sl;
+ if (news < 0) news = 0;
+ if (news >= (_dircount - llen)) news = _dircount - llen;
+ if (news != _scrl_f) {
+ _scrl_f = news;
+ fib_expose (dpy, _fib_win);
+ }
+ return;
+ }
+
+ const int type = fib_widget_at_pos (dpy, x, y, &it);
+ fib_update_hover (dpy, 0, type, it);
+}
+
+static void fib_mousedown (Display *dpy, int x, int y, int btn, unsigned long time) {
+ int it;
+ switch (fib_widget_at_pos (dpy, x, y, &it)) {
+ case 4: // scrollbar
+ if (btn == 1) {
+ _dblclk = 0;
+ if (it == 0) {
+ _scrl_my = y;
+ _scrl_mf = _scrl_f;
+ } else {
+ int llen = (_fib_height - LISTBOT * _fib_font_vsep) / _fib_font_vsep;
+ if (llen < 2) llen = 2;
+ int news = _scrl_f;
+ if (it == 1) {
+ news -= llen - 1;
+ } else {
+ news += llen - 1;
+ }
+ if (news < 0) news = 0;
+ if (news >= (_dircount - llen)) news = _dircount - llen;
+ if (news != _scrl_f && _scrl_y0 >= 0) {
+ assert (news >=0);
+ _scrl_f = news;
+ fib_update_hover (dpy, 1, 4, it);
+ }
+ }
+ }
+ break;
+ case 2: // file-list
+ if (btn == 4 || btn == 5) {
+ const int llen = (_fib_height - LISTBOT * _fib_font_vsep) / _fib_font_vsep;
+ int news = _scrl_f + ((btn == 4) ? - 1 : 1);
+ if (news < 0) news = 0;
+ if (news >= (_dircount - llen)) news = _dircount - llen;
+ if (news != _scrl_f && _scrl_y0 >= 0) {
+ assert (news >=0);
+ _scrl_f = news;
+ fib_update_hover (dpy, 1, 0, 0);
+ }
+ _dblclk = 0;
+ }
+ else if (btn == 1 && it >= 0 && it < _dircount) {
+ if (_fsel == it) {
+ if (time - _dblclk < DBLCLKTME) {
+ fib_open (dpy, it);
+ _dblclk = 0;
+ }
+ _dblclk = time;
+ } else {
+ fib_select (dpy, it);
+ _dblclk = time;
+ }
+ /*if (_fsel >= 0) {
+ if (!(_dirlist[_fsel].flags & 4));
+ }*/
+ }
+ break;
+ case 1: // paths
+ assert (_fsel < _dircount);
+ assert (it >= 0 && it < _pathparts);
+ {
+ int i = 0;
+ char path[1024] = "/";
+ while (++i <= it) {
+ strcat (path, _pathbtn[i].name);
+ strcat (path, "/");
+ }
+ char *sel = NULL;
+ if (i < _pathparts)
+ sel = strdup (_pathbtn[i].name);
+ else if (i == _pathparts && _fsel >= 0)
+ sel = strdup (_dirlist[_fsel].name);
+ fib_opendir (dpy, path, sel);
+ free (sel);
+ }
+ break;
+ case 3: // btn
+ if (btn == 1 && _btns[it]->callback) {
+ _btns[it]->callback (dpy);
+ }
+ break;
+ case 5: // sort
+ if (btn == 1) {
+ switch (it) {
+ case 1: if (_sort == 0) _sort = 1; else _sort = 0; break;
+ case 2: if (_sort == 2) _sort = 3; else _sort = 2; break;
+ case 3: if (_sort == 4) _sort = 5; else _sort = 4; break;
+ }
+ if (_fsel >= 0) {
+ assert (_dirlist && _dircount >= _fsel);
+ _dirlist[_fsel].flags &= ~2;
+ char *sel = strdup (_dirlist[_fsel].name);
+ fib_resort (sel);
+ free (sel);
+ } else {
+ fib_resort (NULL);
+ _fsel = -1;
+ }
+ fib_reset ();
+ _hov_h = it;
+ fib_select (dpy, _fsel);
+ }
+ break;
+ case 6:
+ if (btn == 1 && it >= 0 && it < _placecnt) {
+ fib_opendir (dpy, _placelist[it].path, NULL);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void fib_mouseup (Display *dpy, int x, int y, int btn, unsigned long time) {
+ _scrl_my = -1;
+
+ // unused
+ return; (void)dpy; (void)x; (void)y; (void)btn; (void)time;
+}
+
+static void add_place_raw (Display *dpy, const char *name, const char *path) {
+ _placelist = (FibPlace*) realloc (_placelist, (_placecnt + 1) * sizeof(FibPlace));
+ strcpy (_placelist[_placecnt].path, path);
+ strcpy (_placelist[_placecnt].name, name);
+ _placelist[_placecnt].flags = 0;
+
+ int sw;
+ query_font_geometry (dpy, _fib_gc, name, &sw, NULL, NULL, NULL);
+ if (sw > _fib_place_width) {
+ _fib_place_width = sw;
+ }
+ ++_placecnt;
+}
+
+static int add_place_places (Display *dpy, const char *name, const char *url) {
+ char const * path;
+ struct stat fs;
+ int i;
+ if (!url || strlen (url) < 1) return -1;
+ if (!name || strlen (name) < 1) return -1;
+ if (url[0] == '/') {
+ path = url;
+ }
+ else if (!strncmp (url, "file:///", 8)) {
+ path = &url[7];
+ }
+ else {
+ return -1;
+ }
+
+ if (access (path, R_OK)) {
+ return -1;
+ }
+ if (stat (path, &fs)) {
+ return -1;
+ }
+ if (!S_ISDIR (fs.st_mode)) {
+ return -1;
+ }
+
+ for (i = 0; i < _placecnt; ++i) {
+ if (!strcmp (path, _placelist[i].path)) {
+ return -1;
+ }
+ }
+ add_place_raw (dpy, name, path);
+ return 0;
+}
+
+static int parse_gtk_bookmarks (Display *dpy, const char *fn) {
+ char tmp[1024];
+ if (access (fn, R_OK)) {
+ return -1;
+ }
+ FILE *bm = fopen (fn, "r");
+ if (!bm) return -1;
+ int found = 0;
+ while (fgets (tmp, sizeof(tmp), bm)
+ && strlen (tmp) > 1
+ && strlen (tmp) < sizeof(tmp))
+ {
+ char *s, *n;
+ tmp[strlen (tmp) - 1] = '\0'; // strip newline
+ if ((s = strchr (tmp, ' '))) {
+ *s = '\0';
+ n = strdup (++s);
+ decode_3986 (tmp);
+ if (!add_place_places (dpy, n, tmp)) {
+ ++found;
+ }
+ free (n);
+ } else if ((s = strrchr (tmp, '/'))) {
+ n = strdup (++s);
+ decode_3986 (tmp);
+ if (!add_place_places (dpy, n, tmp)) {
+ ++found;
+ }
+ free (n);
+ }
+ }
+ fclose (bm);
+ return found;
+}
+
+static const char *ignore_mountpoints[] = {
+ "/bin", "/boot", "/dev", "/etc",
+ "/lib", "/live", "/mnt", "/opt",
+ "/root", "/sbin", "/srv", "/tmp",
+ "/usr", "/var", "/proc", "/sbin",
+ "/net", "/sys"
+};
+
+static const char *ignore_fs[] = {
+ "auto", "autofs",
+ "debugfs", "devfs",
+ "devpts", "ecryptfs",
+ "fusectl", "kernfs",
+ "linprocfs", "proc",
+ "ptyfs", "rootfs",
+ "selinuxfs", "sysfs",
+ "tmpfs", "usbfs",
+ "nfsd", "rpc_pipefs",
+};
+
+static const char *ignore_devices[] = {
+ "binfmt_", "devpts",
+ "gvfs", "none",
+ "nfsd", "sunrpc",
+ "/dev/loop", "/dev/vn"
+};
+
+static int check_mount (const char *mountpoint, const char *fs, const char *device) {
+ size_t i;
+ if (!mountpoint || !fs || !device) return -1;
+ //printf("%s %s %s\n", mountpoint, fs, device);
+ for (i = 0 ; i < sizeof(ignore_mountpoints) / sizeof(char*); ++i) {
+ if (!strncmp (mountpoint, ignore_mountpoints[i], strlen (ignore_mountpoints[i]))) {
+ return 1;
+ }
+ }
+ if (!strncmp (mountpoint, "/home", 5)) {
+ return 1;
+ }
+ for (i = 0 ; i < sizeof(ignore_fs) / sizeof(char*); ++i) {
+ if (!strncmp (fs, ignore_fs[i], strlen (ignore_fs[i]))) {
+ return 1;
+ }
+ }
+ for (i = 0 ; i < sizeof(ignore_devices) / sizeof(char*); ++i) {
+ if (!strncmp (device, ignore_devices[i], strlen (ignore_devices[i]))) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int read_mtab (Display *dpy, const char *mtab) {
+ FILE *mt = fopen (mtab, "r");
+ if (!mt) return -1;
+ int found = 0;
+ struct mntent *mntent;
+ while ((mntent = getmntent (mt)) != NULL) {
+ char *s;
+ if (check_mount (mntent->mnt_dir, mntent->mnt_type, mntent->mnt_fsname))
+ continue;
+
+ if ((s = strrchr (mntent->mnt_dir, '/'))) {
+ ++s;
+ } else {
+ s = mntent->mnt_dir;
+ }
+ if (!add_place_places (dpy, s, mntent->mnt_dir)) {
+ ++found;
+ }
+ }
+ fclose (mt);
+ return found;
+}
+
+static void populate_places (Display *dpy) {
+ char tmp[1024];
+ int spacer = -1;
+ if (_placecnt > 0) return;
+ _fib_place_width = 0;
+
+ if (_recentcnt > 0) {
+ add_place_raw (dpy, "Recently Used", "");
+ _placelist[0].flags |= 4;
+ }
+
+ add_place_places (dpy, "Home", getenv ("HOME"));
+
+ if (getenv ("HOME")) {
+ strcpy (tmp, getenv ("HOME"));
+ strcat (tmp, "/Desktop");
+ add_place_places (dpy, "Desktop", tmp);
+ }
+
+ add_place_places (dpy, "Filesystem", "/");
+
+ if (_placecnt > 0) spacer = _placecnt -1;
+
+ if (strlen (_fib_cfg_custom_places) > 0) {
+ parse_gtk_bookmarks (dpy, _fib_cfg_custom_places);
+ }
+
+ if (read_mtab (dpy, "/proc/mounts") < 1) {
+ read_mtab (dpy, "/etc/mtab");
+ }
+
+ int parsed_bookmarks = 0;
+ if (!parsed_bookmarks && getenv ("HOME")) {
+ strcpy (tmp, getenv ("HOME"));
+ strcat (tmp, "/.gtk-bookmarks");
+ if (parse_gtk_bookmarks (dpy, tmp) > 0) {
+ parsed_bookmarks = 1;
+ }
+ }
+ if (!parsed_bookmarks && getenv ("XDG_CONFIG_HOME")) {
+ strcpy (tmp, getenv ("XDG_CONFIG_HOME"));
+ strcat (tmp, "/gtk-3.0/bookmarks");
+ if (parse_gtk_bookmarks (dpy, tmp) > 0) {
+ parsed_bookmarks = 1;
+ }
+ }
+ if (!parsed_bookmarks && getenv ("HOME")) {
+ strcpy (tmp, getenv ("HOME"));
+ strcat (tmp, "/.config/gtk-3.0/bookmarks");
+ if (parse_gtk_bookmarks (dpy, tmp) > 0) {
+ parsed_bookmarks = 1;
+ }
+ }
+ if (_fib_place_width > 0) {
+ _fib_place_width = MIN (_fib_place_width + TEXTSEP + _fib_dir_indent /*extra*/ , PLACESWMAX);
+ }
+ if (spacer > 0 && spacer < _placecnt -1) {
+ _placelist[ spacer ].flags |= 4;
+ }
+}
+
+static uint8_t font_err = 0;
+static int x_error_handler (Display *d, XErrorEvent *e) {
+ font_err = 1;
+ return 0;
+
+ // unused
+ (void)d; (void)e;
+}
+
+int x_fib_show (Display *dpy, Window parent, int x, int y) {
+ if (_fib_win) {
+ XSetInputFocus (dpy, _fib_win, RevertToParent, CurrentTime);
+ return -1;
+ }
+
+ _status = 0;
+ _rv_open[0] = '\0';
+
+ Colormap colormap = DefaultColormap (dpy, DefaultScreen (dpy));
+ _c_gray1.flags= DoRed | DoGreen | DoBlue;
+ _c_gray0.red = _c_gray0.green = _c_gray0.blue = 61710; // 95% hover prelight
+ _c_gray1.red = _c_gray1.green = _c_gray1.blue = 60416; // 93% window bg, scrollbar-fg
+ _c_gray2.red = _c_gray2.green = _c_gray2.blue = 54016; // 83% button & list bg
+ _c_gray3.red = _c_gray3.green = _c_gray3.blue = 48640; // 75% heading + scrollbar-bg
+ _c_gray4.red = _c_gray4.green = _c_gray4.blue = 26112; // 40% prelight text, sep lines
+ _c_gray5.red = _c_gray5.green = _c_gray5.blue = 12800; // 20% 3D border
+ _c_gray6.red = _c_gray6.green = _c_gray6.blue = 6400; // 10% checkbox cross, sort triangles
+
+ if (!XAllocColor (dpy, colormap, &_c_gray0)) return -1;
+ if (!XAllocColor (dpy, colormap, &_c_gray1)) return -1;
+ if (!XAllocColor (dpy, colormap, &_c_gray2)) return -1;
+ if (!XAllocColor (dpy, colormap, &_c_gray3)) return -1;
+ if (!XAllocColor (dpy, colormap, &_c_gray4)) return -1;
+ if (!XAllocColor (dpy, colormap, &_c_gray5)) return -1;
+ if (!XAllocColor (dpy, colormap, &_c_gray6)) return -1;
+
+ XSetWindowAttributes attr;
+ memset (&attr, 0, sizeof(XSetWindowAttributes));
+ attr.border_pixel = _c_gray2.pixel;
+
+ attr.event_mask = ExposureMask | KeyPressMask
+ | ButtonPressMask | ButtonReleaseMask
+ | ConfigureNotify | StructureNotifyMask
+ | PointerMotionMask | LeaveWindowMask;
+
+ _fib_win = XCreateWindow (
+ dpy, DefaultRootWindow (dpy),
+ x, y, _fib_width, _fib_height,
+ 1, CopyFromParent, InputOutput, CopyFromParent,
+ CWEventMask | CWBorderPixel, &attr);
+
+ if (!_fib_win) { return 1; }
+
+ if (parent)
+ XSetTransientForHint (dpy, _fib_win, parent);
+
+ XStoreName (dpy, _fib_win, "Select File");
+
+ Atom wmDelete = XInternAtom (dpy, "WM_DELETE_WINDOW", True);
+ XSetWMProtocols (dpy, _fib_win, &wmDelete, 1);
+
+ _fib_gc = XCreateGC (dpy, _fib_win, 0, NULL);
+ XSetLineAttributes (dpy, _fib_gc, 1, LineSolid, CapButt, JoinMiter);
+ const char dl[1] = {1};
+ XSetDashes (dpy, _fib_gc, 0, dl, 1);
+
+ int (*handler)(Display *, XErrorEvent *) = XSetErrorHandler (&x_error_handler);
+
+#define _XTESTFONT(FN) \
+ { \
+ font_err = 0; \
+ _fibfont = XLoadFont (dpy, FN); \
+ XSetFont (dpy, _fib_gc, _fibfont); \
+ XSync (dpy, False); \
+ }
+
+ font_err = 1;
+ if (getenv ("XJFONT")) _XTESTFONT (getenv ("XJFONT"));
+ if (font_err && strlen (_fib_cfg_custom_font) > 0) _XTESTFONT (_fib_cfg_custom_font);
+ if (font_err) _XTESTFONT ("-*-helvetica-medium-r-normal-*-12-*-*-*-*-*-*-*");
+ if (font_err) _XTESTFONT ("-*-verdana-medium-r-normal-*-12-*-*-*-*-*-*-*");
+ if (font_err) _XTESTFONT ("-misc-fixed-medium-r-normal-*-13-*-*-*-*-*-*-*");
+ if (font_err) _XTESTFONT ("-misc-fixed-medium-r-normal-*-12-*-*-*-*-*-*-*");
+ if (font_err) _fibfont = None;
+ XSync (dpy, False);
+ XSetErrorHandler (handler);
+
+ if (_fib_font_height == 0) { // 1st time only
+ query_font_geometry (dpy, _fib_gc, "D ", &_fib_dir_indent, NULL, NULL, NULL);
+ query_font_geometry (dpy, _fib_gc, "_", &_fib_spc_norm, NULL, NULL, NULL);
+ if (query_font_geometry (dpy, _fib_gc, "|0Yy", NULL, &_fib_font_height, &_fib_font_ascent, NULL)) {
+ XFreeGC (dpy, _fib_gc);
+ XDestroyWindow (dpy, _fib_win);
+ _fib_win = 0;
+ return -1;
+ }
+ _fib_font_height += 3;
+ _fib_font_ascent += 2;
+ _fib_font_vsep = _fib_font_height + 2;
+ }
+
+ populate_places (dpy);
+
+ strcpy (_btn_ok.text, "Open");
+ strcpy (_btn_cancel.text, "Cancel");
+ strcpy (_btn_filter.text, "List All Files");
+ strcpy (_btn_places.text, "Show Places");
+ strcpy (_btn_hidden.text, "Show Hidden");
+
+ _btn_ok.callback = &cb_open;
+ _btn_cancel.callback = &cb_cancel;
+ _btn_filter.callback = &cb_filter;
+ _btn_places.callback = &cb_places;
+ _btn_hidden.callback = &cb_hidden;
+ _btn_filter.flags |= 4;
+ _btn_places.flags |= 4;
+ _btn_hidden.flags |= 4;
+
+ if (!_fib_filter_function) {
+ _btn_filter.flags |= 8;
+ }
+
+ size_t i;
+ int btncnt = 0;
+ _btn_w = 0;
+ _btn_span = 0;
+ for (i = 0; i < sizeof(_btns) / sizeof(FibButton*); ++i) {
+ if (_btns[i]->flags & 8) { continue; }
+ query_font_geometry (dpy, _fib_gc, _btns[i]->text, &_btns[i]->tw, NULL, NULL, NULL);
+ if (_btns[i]->flags & 4) {
+ _btn_span += _btns[i]->tw + _fib_font_ascent + TEXTSEP;
+ } else {
+ ++btncnt;
+ if (_btns[i]->tw > _btn_w)
+ _btn_w = _btns[i]->tw;
+ }
+ }
+
+ _btn_w += BTNPADDING + BTNPADDING + TEXTSEP + TEXTSEP + TEXTSEP;
+ _btn_span += _btn_w * btncnt + DSEP * (i - 1) + FAREAMRGR + FAREAMRGB;
+
+ for (i = 0; i < sizeof(_btns) / sizeof(FibButton*); ++i) {
+ if (_btns[i]->flags & 8) { continue; }
+ if (_btns[i]->flags & 4) {
+ _btns[i]->xw = _btns[i]->tw + _fib_font_ascent + TEXTSEP;
+ } else {
+ _btns[i]->xw = _btn_w;
+ }
+ }
+
+ sync_button_states () ;
+
+ _fib_height = _fib_font_vsep * (15.8);
+ _fib_width = MAX (_btn_span, 440);
+
+ XResizeWindow (dpy, _fib_win, _fib_width, _fib_height);
+
+ XTextProperty x_wname, x_iname;
+ XSizeHints hints;
+ XWMHints wmhints;
+
+ hints.flags = PSize | PMinSize;
+ hints.min_width = _btn_span;
+ hints.min_height = 8 * _fib_font_vsep;
+
+ char *w_name = & _fib_cfg_title[0];
+
+ wmhints.input = True;
+ wmhints.flags = InputHint;
+ if (XStringListToTextProperty (&w_name, 1, &x_wname) &&
+ XStringListToTextProperty (&w_name, 1, &x_iname))
+ {
+ XSetWMProperties (dpy, _fib_win, &x_wname, &x_iname, NULL, 0, &hints, &wmhints, NULL);
+ XFree (x_wname.value);
+ XFree (x_iname.value);
+ }
+
+ XSetWindowBackground (dpy, _fib_win, _c_gray1.pixel);
+
+ _fib_mapped = 0;
+ XMapRaised (dpy, _fib_win);
+
+ if (!strlen (_cur_path) || !fib_opendir (dpy, _cur_path, NULL)) {
+ fib_opendir (dpy, getenv ("HOME") ? getenv ("HOME") : "/", NULL);
+ }
+
+#if 0
+ XGrabPointer (dpy, _fib_win, True,
+ ButtonReleaseMask | ButtonPressMask | EnterWindowMask | LeaveWindowMask | PointerMotionMask | StructureNotifyMask,
+ GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
+ XGrabKeyboard (dpy, _fib_win, True, GrabModeAsync, GrabModeAsync, CurrentTime);
+ //XSetInputFocus (dpy, parent, RevertToNone, CurrentTime);
+#endif
+ _recentlock = 1;
+ return 0;
+}
+
+void x_fib_close (Display *dpy) {
+ if (!_fib_win) return;
+ XFreeGC (dpy, _fib_gc);
+ XDestroyWindow (dpy, _fib_win);
+ _fib_win = 0;
+ free (_dirlist);
+ _dirlist = NULL;
+ free (_pathbtn);
+ _pathbtn = NULL;
+ if (_fibfont != None) XUnloadFont (dpy, _fibfont);
+ _fibfont = None;
+ free (_placelist);
+ _placelist = NULL;
+ _dircount = 0;
+ _pathparts = 0;
+ _placecnt = 0;
+ if (_pixbuffer != None) XFreePixmap (dpy, _pixbuffer);
+ _pixbuffer = None;
+ Colormap colormap = DefaultColormap (dpy, DefaultScreen (dpy));
+ XFreeColors (dpy, colormap, &_c_gray0.pixel, 1, 0);
+ XFreeColors (dpy, colormap, &_c_gray1.pixel, 1, 0);
+ XFreeColors (dpy, colormap, &_c_gray2.pixel, 1, 0);
+ XFreeColors (dpy, colormap, &_c_gray3.pixel, 1, 0);
+ XFreeColors (dpy, colormap, &_c_gray4.pixel, 1, 0);
+ XFreeColors (dpy, colormap, &_c_gray5.pixel, 1, 0);
+ XFreeColors (dpy, colormap, &_c_gray6.pixel, 1, 0);
+ _recentlock = 0;
+}
+
+int x_fib_handle_events (Display *dpy, XEvent *event) {
+ if (!_fib_win) return 0;
+ if (_status) return 0;
+ if (event->xany.window != _fib_win) {
+ return 0;
+ }
+
+ switch (event->type) {
+ case MapNotify:
+ _fib_mapped = 1;
+ break;
+ case UnmapNotify:
+ _fib_mapped = 0;
+ break;
+ case LeaveNotify:
+ fib_update_hover (dpy, 1, 0, 0);
+ break;
+ case ClientMessage:
+ if (!strcmp (XGetAtomName (dpy, event->xclient.message_type), "WM_PROTOCOLS")) {
+ _status = -1;
+ }
+ case ConfigureNotify:
+ if (
+ (event->xconfigure.width > 1 && event->xconfigure.height > 1)
+ &&
+ (event->xconfigure.width != _fib_width || event->xconfigure.height != _fib_height)
+ )
+ {
+ _fib_width = event->xconfigure.width;
+ _fib_height = event->xconfigure.height;
+ _fib_resized = 1;
+ }
+ break;
+ case Expose:
+ if (event->xexpose.count == 0) {
+ fib_expose (dpy, event->xany.window);
+ }
+ break;
+ case MotionNotify:
+ fib_motion (dpy, event->xmotion.x, event->xmotion.y);
+ if (event->xmotion.is_hint == NotifyHint) {
+ XGetMotionEvents (dpy, event->xany.window, CurrentTime, CurrentTime, NULL);
+ }
+ break;
+ case ButtonPress:
+ fib_mousedown (dpy, event->xbutton.x, event->xbutton.y, event->xbutton.button, event->xbutton.time);
+ break;
+ case ButtonRelease:
+ fib_mouseup (dpy, event->xbutton.x, event->xbutton.y, event->xbutton.button, event->xbutton.time);
+ break;
+ case KeyRelease:
+ break;
+ case KeyPress:
+ {
+ KeySym key;
+ char buf[100];
+ static XComposeStatus stat;
+ XLookupString (&event->xkey, buf, sizeof(buf), &key, &stat);
+ switch (key) {
+ case XK_Escape:
+ _status = -1;
+ break;
+ case XK_Up:
+ if (_fsel > 0) {
+ fib_select (dpy, _fsel - 1);
+ }
+ break;
+ case XK_Down:
+ if (_fsel < _dircount -1) {
+ fib_select ( dpy, _fsel + 1);
+ }
+ break;
+ case XK_Page_Up:
+ if (_fsel > 0) {
+ int llen = (_fib_height - LISTBOT * _fib_font_vsep) / _fib_font_vsep;
+ if (llen < 1) llen = 1; else --llen;
+ int fs = MAX (0, _fsel - llen);
+ fib_select ( dpy, fs);
+ }
+ break;
+ case XK_Page_Down:
+ if (_fsel < _dircount) {
+ int llen = (_fib_height - LISTBOT * _fib_font_vsep) / _fib_font_vsep;
+ if (llen < 1) llen = 1; else --llen;
+ int fs = MIN (_dircount - 1, _fsel + llen);
+ fib_select ( dpy, fs);
+ }
+ break;
+ case XK_Left:
+ if (_pathparts > 1) {
+ int i = 0;
+ char path[1024] = "/";
+ while (++i < _pathparts - 1) {
+ strcat (path, _pathbtn[i].name);
+ strcat (path, "/");
+ }
+ char *sel = strdup (_pathbtn[_pathparts-1].name);
+ fib_opendir (dpy, path, sel);
+ free (sel);
+ }
+ break;
+ case XK_Right:
+ if (_fsel >= 0 && _fsel < _dircount) {
+ if (_dirlist[_fsel].flags & 4) {
+ cb_open (dpy);
+ }
+ }
+ break;
+ case XK_Return:
+ cb_open (dpy);
+ break;
+ default:
+ if ((key >= XK_a && key <= XK_z) || (key >= XK_0 && key <= XK_9)) {
+ int i;
+ for (i = 0; i < _dircount; ++i) {
+ int j = (_fsel + i + 1) % _dircount;
+ char kcmp = _dirlist[j].name[0];
+ if (kcmp > 0x40 && kcmp <= 0x5A) kcmp |= 0x20;
+ if (kcmp == (char)key) {
+ fib_select ( dpy, j);
+ break;
+ }
+ }
+ }
+ break;
+ }
+ }
+ break;
+ }
+
+ if (_status) {
+ x_fib_close (dpy);
+ }
+ return _status;
+}
+
+int x_fib_status () {
+ return _status;
+}
+
+int x_fib_configure (int k, const char *v) {
+ if (_fib_win) { return -1; }
+ switch (k) {
+ case 0:
+ if (strlen (v) >= sizeof(_cur_path) -1) return -2;
+ if (strlen (v) < 1) return -2;
+ if (v[0] != '/') return -2;
+ if (strstr (v, "//")) return -2;
+ strncpy (_cur_path, v, sizeof(_cur_path));
+ break;
+ case 1:
+ if (strlen (v) >= sizeof(_fib_cfg_title) -1) return -2;
+ strncpy (_fib_cfg_title, v, sizeof(_fib_cfg_title));
+ break;
+ case 2:
+ if (strlen (v) >= sizeof(_fib_cfg_custom_font) -1) return -2;
+ strncpy (_fib_cfg_custom_font, v, sizeof(_fib_cfg_custom_font));
+ break;
+ case 3:
+ if (strlen (v) >= sizeof(_fib_cfg_custom_places) -1) return -2;
+ strncpy (_fib_cfg_custom_places, v, sizeof(_fib_cfg_custom_places));
+ break;
+ default:
+ return -2;
+ }
+ return 0;
+}
+
+int x_fib_cfg_buttons (int k, int v) {
+ if (_fib_win) { return -1; }
+ switch (k) {
+ case 1:
+ if (v < 0) {
+ _btn_hidden.flags |= 8;
+ } else {
+ _btn_hidden.flags &= ~8;
+ }
+ if (v == 1) {
+ _btn_hidden.flags |= 2;
+ _fib_hidden_fn = 1;
+ } else if (v == 0) {
+ _btn_hidden.flags &= 2;
+ _fib_hidden_fn = 0;
+ }
+ break;
+ case 2:
+ if (v < 0) {
+ _btn_places.flags |= 8;
+ } else {
+ _btn_places.flags &= ~8;
+ }
+ if (v == 1) {
+ _btn_places.flags |= 2;
+ _fib_show_places = 1;
+ } else if (v == 0) {
+ _btn_places.flags &= ~2;
+ _fib_show_places = 0;
+ }
+ break;
+ case 3:
+ // NB. filter button is automatically hidden
+ // IFF the filter-function is NULL.
+ if (v < 0) {
+ _btn_filter.flags |= 8;
+ } else {
+ _btn_filter.flags &= ~8;
+ }
+ if (v == 1) {
+ _btn_filter.flags &= ~2; // inverse - 'show all' = !filter
+ _fib_filter_fn = 1;
+ } else if (v == 0) {
+ _btn_filter.flags |= 2;
+ _fib_filter_fn = 0;
+ }
+ default:
+ return -2;
+ }
+ return 0;
+}
+
+int x_fib_cfg_filter_callback (int (*cb)(const char*)) {
+ if (_fib_win) { return -1; }
+ _fib_filter_function = cb;
+ return 0;
+}
+
+char *x_fib_filename () {
+ if (_status > 0 && !_fib_win)
+ return strdup (_rv_open);
+ else
+ return NULL;
+}
+#endif // SOFD_HAVE_X11
+
+#if defined(__clang__)
+# pragma clang diagnostic pop
+#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
+# pragma GCC diagnostic pop
+#endif
+
+/* example usage */
+#ifdef SOFD_TEST
+
+static int fib_filter_movie_filename (const char *name) {
+ if (!_fib_filter_fn) return 1;
+ const int l3 = strlen (name) - 3;
+ const int l4 = l3 - 1;
+ const int l5 = l4 - 1;
+ const int l6 = l5 - 1;
+ const int l9 = l6 - 3;
+ if (
+ (l4 > 0 && (
+ !strcasecmp (&name[l4], ".avi")
+ || !strcasecmp (&name[l4], ".mov")
+ || !strcasecmp (&name[l4], ".ogg")
+ || !strcasecmp (&name[l4], ".ogv")
+ || !strcasecmp (&name[l4], ".mpg")
+ || !strcasecmp (&name[l4], ".mov")
+ || !strcasecmp (&name[l4], ".mp4")
+ || !strcasecmp (&name[l4], ".mkv")
+ || !strcasecmp (&name[l4], ".vob")
+ || !strcasecmp (&name[l4], ".asf")
+ || !strcasecmp (&name[l4], ".avs")
+ || !strcasecmp (&name[l4], ".dts")
+ || !strcasecmp (&name[l4], ".flv")
+ || !strcasecmp (&name[l4], ".m4v")
+ )) ||
+ (l5 > 0 && (
+ !strcasecmp (&name[l5], ".h264")
+ || !strcasecmp (&name[l5], ".webm")
+ )) ||
+ (l6 > 0 && (
+ !strcasecmp (&name[l6], ".dirac")
+ )) ||
+ (l9 > 0 && (
+ !strcasecmp (&name[l9], ".matroska")
+ )) ||
+ (l3 > 0 && (
+ !strcasecmp (&name[l3], ".dv")
+ || !strcasecmp (&name[l3], ".ts")
+ ))
+ )
+ {
+ return 1;
+ }
+ return 0;
+}
+
+int main (int argc, char **argv) {
+ Display* dpy = XOpenDisplay (0);
+ if (!dpy) return -1;
+
+ x_fib_cfg_filter_callback (fib_filter_movie_filename);
+ x_fib_configure (1, "Open Movie File");
+ x_fib_load_recent ("/tmp/sofd.recent");
+ x_fib_show (dpy, 0, 300, 300);
+
+ while (1) {
+ XEvent event;
+ while (XPending (dpy) > 0) {
+ XNextEvent (dpy, &event);
+ if (x_fib_handle_events (dpy, &event)) {
+ if (x_fib_status () > 0) {
+ char *fn = x_fib_filename ();
+ printf ("OPEN '%s'\n", fn);
+ x_fib_add_recent (fn, time (NULL));
+ free (fn);
+ }
+ }
+ }
+ if (x_fib_status ()) {
+ break;
+ }
+ usleep (80000);
+ }
+ x_fib_close (dpy);
+
+ x_fib_save_recent ("/tmp/sofd.recent");
+
+ x_fib_free_recent ();
+ XCloseDisplay (dpy);
+ return 0;
+}
+#endif
diff --git a/libs/dgl/src/sofd/libsofd.h b/libs/dgl/src/sofd/libsofd.h
new file mode 100644
index 0000000..412db85
--- /dev/null
+++ b/libs/dgl/src/sofd/libsofd.h
@@ -0,0 +1,175 @@
+/* libSOFD - Simple Open File Dialog [for X11 without toolkit]
+ *
+ * Copyright (C) 2014 Robin Gareus <robin@gareus.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifdef SOFD_HAVE_X11
+#include <X11/Xlib.h>
+
+///////////////////////////////////////////////////////////////////////////////
+/* public API */
+
+typedef struct FibInternalsImpl FibInternals;
+
+/** open a file select dialog
+ * @param dpy X Display connection
+ * @param parent (optional) if not NULL, become transient for given window
+ * @param x if >0 set explict initial width of the window
+ * @param y if >0 set explict initial height of the window
+ * @return 0 on success
+ */
+int x_fib_show (Display *dpy, Window parent, int x, int y);
+
+/** force close the dialog.
+ * This is normally not needed, the dialog closes itself
+ * when a file is selected or the user cancels selection.
+ * @param dpy X Display connection
+ */
+void x_fib_close (Display *dpy);
+
+/** non-blocking X11 event handler.
+ * It is safe to run this function even if the dialog is
+ * closed or was not initialized.
+ *
+ * @param dpy X Display connection
+ * @param event the XEvent to process
+ * @return status
+ * 0: the event was not for this window, or file-dialog still
+ * active, or the dialog window is not displayed.
+ * >0: file was selected, dialog closed
+ * <0: file selection was cancelled.
+ */
+int x_fib_handle_events (Display *dpy, XEvent *event);
+
+/** last status of the dialog
+ * @return >0: file was selected, <0: canceled or inactive. 0: active
+ */
+int x_fib_status ();
+
+/** query the selected filename
+ * @return NULL if none set, or allocated string to be free()ed by the called
+ */
+char *x_fib_filename ();
+
+/** customize/configure the dialog before calling \ref x_fib_show
+ * changes only have any effect if the dialog is not visible.
+ * @param k key to change
+ * 0: set current dir to display (must end with slash)
+ * 1: set title of dialog window
+ * 2: specify a custom X11 font to use
+ * 3: specify a custom 'places' file to include
+ * (following gtk-bookmark convention)
+ * @param v value
+ * @return 0 on success.
+ */
+int x_fib_configure (int k, const char *v);
+
+/** customize/configure the dialog before calling \ref x_fib_show
+ * changes only have any effect if the dialog is not visible.
+ *
+ * @param k button to change:
+ * 1: show hidden files
+ * 2: show places
+ * 3: show filter/list all (automatically hidden if there is no
+ * filter function)
+ * @param v <0 to hide the button >=0 show button,
+ * 0: set button-state to not-checked
+ * 1: set button-state to checked
+ * >1: retain current state
+ * @return 0 on success.
+ */
+int x_fib_cfg_buttons (int k, int v);
+
+/** set custom callback to filter file-names.
+ * NULL will disable the filter and hide the 'show all' button.
+ * changes only have any effect if the dialog is not visible.
+ *
+ * @param cb callback function to check file
+ * the callback function is called with the file name (basename only)
+ * and is expected to return 1 if the file passes the filter
+ * and 0 if the file should not be listed by default.
+ * @return 0 on success.
+ */
+int x_fib_cfg_filter_callback (int (*cb)(const char*));
+
+#endif /* END X11 specific functions */
+
+/* 'recently used' API. x-platform
+ * NOTE: all functions use a static cache and are not reentrant.
+ * It is expected that none of these functions are called in
+ * parallel from different threads.
+ */
+
+/** release static resources of 'recently used files'
+ */
+void x_fib_free_recent ();
+
+/** add an entry to the recently used list
+ *
+ * The dialog does not add files automatically on open,
+ * if the application succeeds to open a selected file,
+ * this function should be called.
+ *
+ * @param path complete path to file
+ * @param atime time of last use, 0: NOW
+ * @return -1 on error, number of current entries otherwise
+ */
+int x_fib_add_recent (const char *path, time_t atime);
+
+/** get a platform specific path to a good location for
+ * saving the recently used file list.
+ * (follows XDG_DATA_HOME on Unix, and CSIDL_LOCAL_APPDATA spec)
+ *
+ * @param application-name to use to include in file
+ * @return pointer to static path or NULL
+ */
+const char *x_fib_recent_file(const char *appname);
+
+/** save the current list of recently used files to the given filename
+ * (the format is one file per line, filename URL encoded and space separated
+ * with last-used timestamp)
+ *
+ * This function tries to creates the containing directory if it does
+ * not exist.
+ *
+ * @param fn file to save the list to
+ * @return 0: on success
+ */
+int x_fib_save_recent (const char *fn);
+
+/** load a recently used file list.
+ *
+ * @param fn file to load the list from
+ * @return 0: on success
+ */
+int x_fib_load_recent (const char *fn);
+
+/** get number of entries in the current list
+ * @return number of entries in the recently used list
+ */
+unsigned int x_fib_recent_count ();
+
+/** get recently used entry at given position
+ *
+ * @param i entry to query
+ * @return pointer to static string
+ */
+const char *x_fib_recent_at (unsigned int i);
diff --git a/libs/distrho/DistrhoUI.hpp b/libs/distrho/DistrhoUI.hpp
index b6b2402..2832db0 100644
--- a/libs/distrho/DistrhoUI.hpp
+++ b/libs/distrho/DistrhoUI.hpp
@@ -20,13 +20,10 @@
#include "extra/d_leakdetector.hpp"
#include "src/DistrhoPluginChecks.h"
-#if DISTRHO_UI_USE_NTK
-# include "../dgl/ntk/NtkWidget.hpp"
-typedef DGL::NtkWidget UIWidget;
-#elif DISTRHO_UI_USE_NANOVG
+#if DISTRHO_UI_USE_NANOVG
# include "../dgl/NanoVG.hpp"
typedef DGL::NanoWidget UIWidget;
-# else
+#else
# include "../dgl/Widget.hpp"
typedef DGL::Widget UIWidget;
#endif
@@ -143,32 +140,28 @@ protected:
*/
virtual void d_uiIdle() {}
-#if ! DISTRHO_UI_USE_NTK
+ /**
+ File browser selected function.
+ @see Window::fileBrowserSelected(const char*)
+ */
+ virtual void d_uiFileBrowserSelected(const char* filename);
+
/**
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)
*/
virtual void d_uiReshape(uint width, uint height);
-#endif
/* --------------------------------------------------------------------------------------------------------
* UI Resize Handling, internal */
-#if DISTRHO_UI_USE_NTK
- /**
- NTK widget resize function, called when the widget is resized.
- This is overriden here so the host knows when the UI is resized by you.
- */
- void resize(int x, int y, int w, int h) override;
-#else
/**
OpenGL 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&)
*/
void onResize(const ResizeEvent& ev) override;
-#endif
// -------------------------------------------------------------------------------------------------------
@@ -179,14 +172,11 @@ private:
friend class UIExporterWindow;
// these should not be used
- void position(int, int) noexcept {}
void setAbsoluteX(int) const noexcept {}
void setAbsoluteY(int) const noexcept {}
void setAbsolutePos(int, int) const noexcept {}
- void setNeedsFullViewport(bool) const noexcept {}
-#if ! DISTRHO_UI_USE_NTK
void setAbsolutePos(const DGL::Point<int>&) const noexcept {}
-#endif
+ void setNeedsFullViewport(bool) const noexcept {}
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(UI)
};
diff --git a/libs/distrho/DistrhoUIMain.cpp b/libs/distrho/DistrhoUIMain.cpp
index 3bc0971..cb1cb6e 100644
--- a/libs/distrho/DistrhoUIMain.cpp
+++ b/libs/distrho/DistrhoUIMain.cpp
@@ -27,28 +27,3 @@
#elif defined(DISTRHO_PLUGIN_TARGET_VST)
// nothing
#endif
-
-#ifdef DGL_NTK_APP_HPP_INCLUDED
-
-START_NAMESPACE_DGL
-
-void NtkApp::NextUI::run()
-{
- if (create)
- {
- d_stdout("Creating NTK UI in separate thread...");
- d_UI* const ui2 = (func)();
- ui = ui2;
- }
- else
- {
- d_stdout("Destroying NTK UI in separate thread...");
- d_UI* const ui2 = ui;
- ui = nullptr;
- delete ui2;
- }
-}
-
-END_NAMESPACE_DGL
-
-#endif
diff --git a/libs/distrho/DistrhoUtils.hpp b/libs/distrho/DistrhoUtils.hpp
index 2dd4195..b1391ef 100644
--- a/libs/distrho/DistrhoUtils.hpp
+++ b/libs/distrho/DistrhoUtils.hpp
@@ -24,6 +24,9 @@
#include <cstdlib>
#include <cstring>
+#include <cmath>
+#include <limits>
+
#ifdef DISTRHO_PROPER_CPP11_SUPPORT
# include <cstdint>
#else
@@ -32,17 +35,13 @@
#if defined(DISTRHO_OS_MAC) && ! defined(CARLA_OS_MAC)
namespace std {
-inline float
- fmin(float __x, float __y)
+inline float fmin(float __x, float __y)
{ return __builtin_fminf(__x, __y); }
-inline float
- fmax(float __x, float __y)
+inline float fmax(float __x, float __y)
{ return __builtin_fmaxf(__x, __y); }
-inline float
- rint(float __x)
+inline float rint(float __x)
{ return __builtin_rintf(__x); }
-inline float
- round(float __x)
+inline float round(float __x)
{ return __builtin_roundf(__x); }
}
#endif
@@ -50,18 +49,28 @@ inline float
// -----------------------------------------------------------------------
// misc functions
+/*
+ * Return a 64-bit number from 4 8-bit numbers.
+ */
static inline
-int64_t d_cconst(const int a, const int b, const int c, const int d) noexcept
+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);
}
+/*
+ * Dummy 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.
+ */
#ifndef DEBUG
# define d_debug(...)
#else
@@ -79,6 +88,9 @@ void d_debug(const char* const fmt, ...) noexcept
}
#endif
+/*
+ * Print a string to stdout with newline.
+ */
static inline
void d_stdout(const char* const fmt, ...) noexcept
{
@@ -91,6 +103,9 @@ void d_stdout(const char* const fmt, ...) noexcept
} catch (...) {}
}
+/*
+ * Print a string to stderr with newline.
+ */
static inline
void d_stderr(const char* const fmt, ...) noexcept
{
@@ -103,6 +118,9 @@ void d_stderr(const char* const fmt, ...) noexcept
} catch (...) {}
}
+/*
+ * Print a string to stderr with newline (red color).
+ */
static inline
void d_stderr2(const char* const fmt, ...) noexcept
{
@@ -116,12 +134,18 @@ void d_stderr2(const char* const fmt, ...) noexcept
} catch (...) {}
}
+/*
+ * Print a safe assertion error message.
+ */
static inline
void d_safe_assert(const char* const assertion, const char* const file, const int line) noexcept
{
d_stderr2("assertion failure: \"%s\" in file %s, line %i", 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
{
@@ -129,5 +153,50 @@ void d_safe_exception(const char* const exception, const char* const file, const
}
// -----------------------------------------------------------------------
+// math functions
+
+/*
+ * Safely compare two floating point numbers.
+ * Returns true if they match.
+ */
+template<typename T>
+static inline
+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.
+ */
+template<typename T>
+static inline
+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.
+ */
+template<typename T>
+static inline
+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.
+ */
+template<typename T>
+static inline
+bool d_isNotZero(const T& value)
+{
+ return std::abs(value) >= std::numeric_limits<T>::epsilon();
+}
+
+// -----------------------------------------------------------------------
#endif // DISTRHO_UTILS_HPP_INCLUDED
diff --git a/libs/distrho/src/DistrhoDefines.h b/libs/distrho/src/DistrhoDefines.h
index 501fc28..59e4c34 100644
--- a/libs/distrho/src/DistrhoDefines.h
+++ b/libs/distrho/src/DistrhoDefines.h
@@ -118,23 +118,17 @@ private: \
#endif
/* Define namespace */
-#ifndef DISTRHO_NO_NAMESPACE
-# ifndef DISTRHO_NAMESPACE
-# define DISTRHO_NAMESPACE DISTRHO
-# endif
-# define START_NAMESPACE_DISTRHO namespace DISTRHO_NAMESPACE {
-# define END_NAMESPACE_DISTRHO }
-# define USE_NAMESPACE_DISTRHO using namespace DISTRHO_NAMESPACE;
-#else
-# define START_NAMESPACE_DISTRHO
-# define END_NAMESPACE_DISTRHO
-# define USE_NAMESPACE_DISTRHO
+#ifndef DISTRHO_NAMESPACE
+# define DISTRHO_NAMESPACE DISTRHO
#endif
+#define START_NAMESPACE_DISTRHO namespace DISTRHO_NAMESPACE {
+#define END_NAMESPACE_DISTRHO }
+#define USE_NAMESPACE_DISTRHO using namespace DISTRHO_NAMESPACE;
/* Useful typedefs */
typedef unsigned char uchar;
-typedef unsigned long int ulong;
typedef unsigned short int ushort;
typedef unsigned int uint;
+typedef unsigned long int ulong;
#endif // DISTRHO_DEFINES_H_INCLUDED
diff --git a/libs/distrho/src/DistrhoPluginCarla.cpp b/libs/distrho/src/DistrhoPluginCarla.cpp
index 3ba0b11..1f0a75e 100644
--- a/libs/distrho/src/DistrhoPluginCarla.cpp
+++ b/libs/distrho/src/DistrhoPluginCarla.cpp
@@ -45,22 +45,27 @@ public:
fPlugin(plugin),
fUI(this, 0, editParameterCallback, setParameterCallback, setStateCallback, sendNoteCallback, setSizeCallback, plugin->getInstancePointer())
{
- fUI.setTitle(host->uiName);
+ fUI.setWindowTitle(host->uiName);
if (host->uiParentId != 0)
- fUI.setTransientWinId(host->uiParentId);
+ fUI.setWindowTransientWinId(host->uiParentId);
+ }
+
+ ~UICarla()
+ {
+ fUI.quit();
}
// ---------------------------------------------
void carla_show(const bool yesNo)
{
- fUI.setVisible(yesNo);
+ fUI.setWindowVisible(yesNo);
}
- void carla_idle()
+ bool carla_idle()
{
- fUI.idle();
+ return fUI.idle();
}
void carla_setParameterValue(const uint32_t index, const float value)
@@ -84,7 +89,7 @@ public:
void carla_setUiTitle(const char* const uiTitle)
{
- fUI.setTitle(uiTitle);
+ fUI.setWindowTitle(uiTitle);
}
// ---------------------------------------------
@@ -112,7 +117,7 @@ protected:
void handleSetSize(const uint width, const uint height)
{
- fUI.setSize(width, height);
+ fUI.setWindowSize(width, height);
}
// ---------------------------------------------
@@ -205,25 +210,23 @@ protected:
static NativeParameter param;
- // reset
- param.hints = ::PARAMETER_IS_ENABLED;
param.scalePointCount = 0;
param.scalePoints = nullptr;
{
- int nativeParamHints = ::PARAMETER_IS_ENABLED;
+ int nativeParamHints = ::NATIVE_PARAMETER_IS_ENABLED;
const uint32_t paramHints = fPlugin.getParameterHints(index);
if (paramHints & kParameterIsAutomable)
- nativeParamHints |= ::PARAMETER_IS_AUTOMABLE;
+ nativeParamHints |= ::NATIVE_PARAMETER_IS_AUTOMABLE;
if (paramHints & kParameterIsBoolean)
- nativeParamHints |= ::PARAMETER_IS_BOOLEAN;
+ nativeParamHints |= ::NATIVE_PARAMETER_IS_BOOLEAN;
if (paramHints & kParameterIsInteger)
- nativeParamHints |= ::PARAMETER_IS_INTEGER;
+ nativeParamHints |= ::NATIVE_PARAMETER_IS_INTEGER;
if (paramHints & kParameterIsLogarithmic)
- nativeParamHints |= ::PARAMETER_IS_LOGARITHMIC;
+ nativeParamHints |= ::NATIVE_PARAMETER_IS_LOGARITHMIC;
if (paramHints & kParameterIsOutput)
- nativeParamHints |= ::PARAMETER_IS_OUTPUT;
+ nativeParamHints |= ::NATIVE_PARAMETER_IS_OUTPUT;
param.hints = static_cast<NativeParameterHints>(nativeParamHints);
}
@@ -329,7 +332,13 @@ protected:
realMidiEvent.frame = midiEvent.time;
realMidiEvent.size = midiEvent.size;
- carla_copy<uint8_t>(realMidiEvent.buf, midiEvent.data, midiEvent.size);
+ uint8_t j=0;
+ for (; j<midiEvent.size; ++j)
+ realMidiEvent.data[j] = midiEvent.data[j];
+ for (; j<midiEvent.size; ++j)
+ realMidiEvent.data[j] = midiEvent.data[j];
+
+ realMidiEvent.dataExt = nullptr;
}
fPlugin.run(const_cast<const float**>(inBuffer), outBuffer, frames, realMidiEvents, midiEventCount);
@@ -348,17 +357,30 @@ protected:
void uiShow(const bool show) override
{
if (show)
+ {
createUiIfNeeded();
+ CARLA_SAFE_ASSERT_RETURN(fUiPtr != nullptr,);
- if (fUiPtr != nullptr)
fUiPtr->carla_show(show);
+ }
+ else if (fUiPtr != nullptr)
+ {
+ delete fUiPtr;
+ fUiPtr = nullptr;
+ }
}
void uiIdle() override
{
CARLA_SAFE_ASSERT_RETURN(fUiPtr != nullptr,);
- fUiPtr->carla_idle();
+ if (! fUiPtr->carla_idle())
+ {
+ uiClosed();
+
+ delete fUiPtr;
+ fUiPtr = nullptr;
+ }
}
void uiSetParameterValue(const uint32_t index, const float value) override
diff --git a/libs/distrho/src/DistrhoPluginChecks.h b/libs/distrho/src/DistrhoPluginChecks.h
index f556c06..16d6b43 100644
--- a/libs/distrho/src/DistrhoPluginChecks.h
+++ b/libs/distrho/src/DistrhoPluginChecks.h
@@ -93,10 +93,6 @@
# define DISTRHO_UI_USE_NANOVG 0
#endif
-#ifndef DISTRHO_UI_USE_NTK
-# define DISTRHO_UI_USE_NTK 0
-#endif
-
// -----------------------------------------------------------------------
// Define DISTRHO_UI_URI if needed
diff --git a/libs/distrho/src/DistrhoPluginInternal.hpp b/libs/distrho/src/DistrhoPluginInternal.hpp
index 7b4c04b..e9e2e23 100644
--- a/libs/distrho/src/DistrhoPluginInternal.hpp
+++ b/libs/distrho/src/DistrhoPluginInternal.hpp
@@ -83,7 +83,7 @@ struct Plugin::PrivateData {
sampleRate(d_lastSampleRate)
{
DISTRHO_SAFE_ASSERT(bufferSize != 0);
- DISTRHO_SAFE_ASSERT(sampleRate != 0.0);
+ DISTRHO_SAFE_ASSERT(d_isNotZero(sampleRate));
}
~PrivateData() noexcept
@@ -433,7 +433,7 @@ public:
DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr,);
DISTRHO_SAFE_ASSERT(sampleRate > 0.0);
- if (fData->sampleRate == sampleRate)
+ if (d_isEqual(fData->sampleRate, sampleRate))
return;
fData->sampleRate = sampleRate;
diff --git a/libs/distrho/src/DistrhoPluginVST.cpp b/libs/distrho/src/DistrhoPluginVST.cpp
index 703ad79..57ea038 100644
--- a/libs/distrho/src/DistrhoPluginVST.cpp
+++ b/libs/distrho/src/DistrhoPluginVST.cpp
@@ -270,7 +270,7 @@ public:
: fAudioMaster(audioMaster),
fEffect(effect)
{
- std::memset(fProgramName, 0, sizeof(char)*(64+1));
+ std::memset(fProgramName, 0, sizeof(char)*(32+1));
std::strcpy(fProgramName, "Default");
#if DISTRHO_PLUGIN_HAS_MIDI_INPUT
@@ -339,7 +339,7 @@ public:
case effSetProgramName:
if (char* const programName = (char*)ptr)
{
- DISTRHO::strncpy(fProgramName, programName, 64);
+ DISTRHO::strncpy(fProgramName, programName, 32);
return 1;
}
break;
@@ -371,8 +371,10 @@ public:
case effSetSampleRate:
fPlugin.setSampleRate(opt, true);
+#if DISTRHO_PLUGIN_HAS_UI
if (fVstUI != nullptr)
fVstUI->setSampleRate(opt);
+#endif
break;
case effSetBlockSize:
@@ -424,7 +426,6 @@ public:
# endif
d_lastUiSampleRate = fPlugin.getSampleRate();
- d_stdout("effEditOpen with ptr = %p", ptr);
fVstUI = new UIVst(fAudioMaster, fEffect, this, &fPlugin, (intptr_t)ptr);
# if DISTRHO_PLUGIN_WANT_STATE
@@ -584,7 +585,7 @@ public:
case effCanDo:
if (const char* const canDo = (const char*)ptr)
{
-# if DISTRHO_OS_MAC
+# if DISTRHO_OS_MAC && DISTRHO_PLUGIN_HAS_UI
if (std::strcmp(canDo, "hasCockosViewAsConfig") == 0)
{
fUsingNsView = true;
@@ -716,7 +717,7 @@ private:
PluginExporter fPlugin;
// Temporary data
- char fProgramName[64+1];
+ char fProgramName[32+1];
#if DISTRHO_PLUGIN_HAS_MIDI_INPUT
uint32_t fMidiEventCount;
@@ -903,25 +904,25 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t
#endif
case effGetEffectName:
- if (ptr != nullptr)
+ if (char* const cptr = (char*)ptr)
{
- DISTRHO::strncpy((char*)ptr, plugin.getName(), 64);
+ DISTRHO::strncpy(cptr, plugin.getName(), 32);
return 1;
}
return 0;
case effGetVendorString:
- if (ptr != nullptr)
+ if (char* const cptr = (char*)ptr)
{
- DISTRHO::strncpy((char*)ptr, plugin.getMaker(), 64);
+ DISTRHO::strncpy(cptr, plugin.getMaker(), 32);
return 1;
}
return 0;
case effGetProductString:
- if (ptr != nullptr)
+ if (char* const cptr = (char*)ptr)
{
- DISTRHO::strncpy((char*)ptr, plugin.getLabel(), 64);
+ DISTRHO::strncpy(cptr, plugin.getLabel(), 32);
return 1;
}
return 0;
diff --git a/libs/distrho/src/DistrhoUI.cpp b/libs/distrho/src/DistrhoUI.cpp
index eda57b0..49e8c3e 100644
--- a/libs/distrho/src/DistrhoUI.cpp
+++ b/libs/distrho/src/DistrhoUI.cpp
@@ -21,9 +21,9 @@ START_NAMESPACE_DISTRHO
/* ------------------------------------------------------------------------------------------------------------
* Static data, see DistrhoUIInternal.hpp */
-double d_lastUiSampleRate = 0.0;
-void* d_lastUiDspPtr = nullptr;
-UIWindow* d_lastUiWindow = nullptr;
+double d_lastUiSampleRate = 0.0;
+void* d_lastUiDspPtr = nullptr;
+Window* d_lastUiWindow = nullptr;
/* ------------------------------------------------------------------------------------------------------------
* UI */
@@ -90,35 +90,29 @@ void UI::d_sampleRateChanged(double) {}
/* ------------------------------------------------------------------------------------------------------------
* UI Callbacks (optional) */
-#if ! DISTRHO_UI_USE_NTK
+void UI::d_uiFileBrowserSelected(const char*)
+{
+}
+
void UI::d_uiReshape(uint width, uint height)
{
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
- glOrtho(0, width, height, 0, 0.0f, 1.0f);
- glViewport(0, 0, width, height);
+ 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();
}
-#endif
/* ------------------------------------------------------------------------------------------------------------
* UI Resize Handling, internal */
-#if DISTRHO_UI_USE_NTK
-void UI::resize(int x, int y, int w, int h)
-{
- UIWidget::resize(x, y, w, h);
- pData->setSizeCallback(w, h);
-}
-#else
void UI::onResize(const ResizeEvent& ev)
{
pData->setSizeCallback(ev.size.getWidth(), ev.size.getHeight());
}
-#endif
// -----------------------------------------------------------------------------------------------------------
diff --git a/libs/distrho/src/DistrhoUIInternal.hpp b/libs/distrho/src/DistrhoUIInternal.hpp
index 6bd421c..a07719c 100644
--- a/libs/distrho/src/DistrhoUIInternal.hpp
+++ b/libs/distrho/src/DistrhoUIInternal.hpp
@@ -18,29 +18,21 @@
#define DISTRHO_UI_INTERNAL_HPP_INCLUDED
#include "../DistrhoUI.hpp"
+#include "../../dgl/App.hpp"
+#include "../../dgl/Window.hpp"
-#if DISTRHO_UI_USE_NTK
-# include "../../dgl/ntk/NtkApp.hpp"
-# include "../../dgl/ntk/NtkWindow.hpp"
-typedef DGL::NtkApp App;
-typedef DGL::NtkWindow UIWindow;
-#else
-# include "../../dgl/App.hpp"
-# include "../../dgl/Window.hpp"
-typedef DGL::App App;
-typedef DGL::Window UIWindow;
-#endif
-
+using DGL::App;
using DGL::IdleCallback;
+using DGL::Window;
START_NAMESPACE_DISTRHO
// -----------------------------------------------------------------------
// Static data, see DistrhoUI.cpp
-extern double d_lastUiSampleRate;
-extern void* d_lastUiDspPtr;
-extern UIWindow* d_lastUiWindow;
+extern double d_lastUiSampleRate;
+extern void* d_lastUiDspPtr;
+extern Window* d_lastUiWindow;
// -----------------------------------------------------------------------
// UI callbacks
@@ -83,7 +75,7 @@ struct UI::PrivateData {
setSizeCallbackFunc(nullptr),
ptr(nullptr)
{
- DISTRHO_SAFE_ASSERT(sampleRate != 0.0);
+ 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;
@@ -137,25 +129,21 @@ struct UI::PrivateData {
// Plugin Window, needed to take care of resize properly
static inline
-UI* createUiWrapper(void* const dspPtr, UIWindow* const window)
+UI* createUiWrapper(void* const dspPtr, Window* const window)
{
d_lastUiDspPtr = dspPtr;
d_lastUiWindow = window;
-#if DISTRHO_UI_USE_NTK
- UI* const ret = window->getApp().createUI((void*)createUI);
-#else
UI* const ret = createUI();
-#endif
d_lastUiDspPtr = nullptr;
d_lastUiWindow = nullptr;
return ret;
}
-class UIExporterWindow : public UIWindow
+class UIExporterWindow : public Window
{
public:
UIExporterWindow(App& app, const intptr_t winId, void* const dspPtr)
- : UIWindow(app, winId),
+ : Window(app, winId),
fUI(createUiWrapper(dspPtr, this)),
fIsReady(false)
{
@@ -168,11 +156,7 @@ public:
~UIExporterWindow()
{
-#if DISTRHO_UI_USE_NTK
- getApp().deleteUI(fUI);
-#else
delete fUI;
-#endif
}
UI* getUI() const noexcept
@@ -185,24 +169,23 @@ public:
return fIsReady;
}
-//protected:
-#if DISTRHO_UI_USE_NTK
- void resize(int x, int y, int width, int height) override
- {
- UIWindow::resize(x, y, width, height);
- fIsReady = true;
- }
-#else
+protected:
+ // custom window reshape
void onReshape(uint width, uint height) override
{
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
- // custom window reshape
fUI->d_uiReshape(width, height);
-
fIsReady = true;
}
-#endif
+
+ // custom file-browser selected
+ void fileBrowserSelected(const char* filename) override
+ {
+ DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
+
+ fUI->d_uiFileBrowserSelected(filename);
+ }
private:
UI* const fUI;
@@ -254,6 +237,13 @@ public:
// -------------------------------------------------------------------
+ intptr_t getWindowId() const noexcept
+ {
+ return glWindow.getWindowId();
+ }
+
+ // -------------------------------------------------------------------
+
uint32_t getParameterOffset() const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, 0);
@@ -306,8 +296,6 @@ public:
{
if (glWindow.isReady())
fUI->d_uiIdle();
-
- fChangingSize = false;
}
bool idle()
@@ -319,8 +307,6 @@ public:
if (glWindow.isReady())
fUI->d_uiIdle();
- fChangingSize = false;
-
return ! glApp.isQuiting();
}
@@ -335,9 +321,7 @@ public:
void setWindowSize(const uint width, const uint height, const bool updateUI = false)
{
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
-
- if (fChangingSize)
- return;
+ DISTRHO_SAFE_ASSERT_RETURN(! fChangingSize,);
fChangingSize = true;
@@ -345,6 +329,8 @@ public:
fUI->setSize(width, height);
glWindow.setSize(width, height);
+
+ fChangingSize = false;
}
void setWindowTitle(const char* const uiTitle)
@@ -352,7 +338,7 @@ public:
glWindow.setTitle(uiTitle);
}
- void setWindowTransientWinId(const intptr_t winId)
+ void setWindowTransientWinId(const uintptr_t winId)
{
glWindow.setTransientWinId(winId);
}
@@ -372,7 +358,7 @@ public:
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
DISTRHO_SAFE_ASSERT(sampleRate > 0.0);
- if (fData->sampleRate == sampleRate)
+ if (d_isEqual(fData->sampleRate, sampleRate))
return;
fData->sampleRate = sampleRate;
diff --git a/libs/distrho/src/DistrhoUILV2.cpp b/libs/distrho/src/DistrhoUILV2.cpp
index 4de5346..2aa6565 100644
--- a/libs/distrho/src/DistrhoUILV2.cpp
+++ b/libs/distrho/src/DistrhoUILV2.cpp
@@ -37,7 +37,8 @@ class UiLv2
public:
UiLv2(const intptr_t winId,
const LV2_Options_Option* options, const LV2_URID_Map* const uridMap, const LV2UI_Resize* const uiResz, const LV2UI_Touch* uiTouch,
- const LV2UI_Controller controller, const LV2UI_Write_Function writeFunc, void* const dspPtr)
+ const LV2UI_Controller controller, const LV2UI_Write_Function writeFunc,
+ LV2UI_Widget* const widget, void* const dspPtr)
: fUI(this, winId, editParameterCallback, setParameterCallback, setStateCallback, sendNoteCallback, setSizeCallback, dspPtr),
fUridMap(uridMap),
fUiResize(uiResz),
@@ -51,6 +52,9 @@ public:
if (fUiResize != nullptr && winId != 0)
fUiResize->ui_resize(fUiResize->handle, fUI.getWidth(), fUI.getHeight());
+ if (widget != nullptr)
+ *widget = (LV2UI_Widget*)fUI.getWindowId();
+
#if DISTRHO_PLUGIN_WANT_STATE
// tell the DSP we're ready to receive msgs
setState("__dpf_ui_data__", "");
@@ -59,7 +63,7 @@ public:
if (winId != 0)
return;
- // if winId != 0 then options must not be null
+ // if winId == 0 then options must not be null
DISTRHO_SAFE_ASSERT_RETURN(options != nullptr,);
const LV2_URID uridWindowTitle(uridMap->map(uridMap->handle, LV2_UI__windowTitle));
@@ -106,10 +110,8 @@ public:
{
const uint32_t parameterOffset(fUI.getParameterOffset());
- if (rindex < parameterOffset)
- return;
- if (bufferSize != sizeof(float))
- return;
+ DISTRHO_SAFE_ASSERT_RETURN(rindex >= parameterOffset,)
+ DISTRHO_SAFE_ASSERT_RETURN(bufferSize == sizeof(float),)
const float value(*(const float*)buffer);
fUI.parameterChanged(rindex-parameterOffset, value);
@@ -391,8 +393,6 @@ static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, const char* uri,
}
#endif
- *widget = parentId;
-
const intptr_t winId((intptr_t)parentId);
if (options != nullptr)
@@ -419,7 +419,7 @@ static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, const char* uri,
d_lastUiSampleRate = 44100.0;
}
- return new UiLv2(winId, options, uridMap, uiResize, uiTouch, controller, writeFunction, instance);
+ return new UiLv2(winId, options, uridMap, uiResize, uiTouch, controller, writeFunction, widget, instance);
}
#define uiPtr ((UiLv2*)ui)
diff --git a/plugins/ZamSFZ/Makefile b/plugins/ZamSFZ/Makefile
index b6b4d8b..aa1ba72 100644
--- a/plugins/ZamSFZ/Makefile
+++ b/plugins/ZamSFZ/Makefile
@@ -27,6 +27,12 @@ OBJS_UI = \
include ../Makefile.mk
# --------------------------------------------------------------
+# Extra flags
+
+BASE_FLAGS += $(shell pkg-config --cflags sndfile rubberband)
+LINK_FLAGS += $(shell pkg-config --libs sndfile rubberband)
+
+# --------------------------------------------------------------
# Enable all possible plugin types
ifeq ($(LINUX),true)
diff --git a/plugins/ZamSFZ/ZamSFZUI.cpp b/plugins/ZamSFZ/ZamSFZUI.cpp
index eccdf02..3bb4d69 100644
--- a/plugins/ZamSFZ/ZamSFZUI.cpp
+++ b/plugins/ZamSFZ/ZamSFZUI.cpp
@@ -18,6 +18,8 @@
#include "ZamSFZUI.hpp"
#include "ZamSFZPlugin.hpp"
+#include "Window.hpp"
+
START_NAMESPACE_DISTRHO
// -----------------------------------------------------------------------
@@ -82,6 +84,13 @@ void ZamSFZUI::d_stateChanged(const char* key, const char*)
}
}
+void ZamSFZUI::d_uiFileBrowserSelected(const char* filename)
+{
+ // if a file was selected, tell DSP
+ if (filename != nullptr)
+ d_setState("filepath", filename);
+}
+
// -----------------------------------------------------------------------
// Widget Callbacks
@@ -105,33 +114,11 @@ void ZamSFZUI::imageKnobValueChanged(ImageKnob* knob, float value)
void ZamSFZUI::imageButtonClicked(ImageButton*, int)
{
-/*
- GtkWidget* dialog = gtk_file_chooser_dialog_new(
- "Load SFZ",
- NULL,
- GTK_FILE_CHOOSER_ACTION_OPEN,
- GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
- GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
- NULL);
-
- if (filepath) {
- gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (dialog), filepath);
- }
+ DGL::Window::FileBrowserOptions opts;
+ opts.title = "Load SFZ";
+ //opts.filters = "sfz;";
- if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
- gtk_widget_destroy(dialog);
- return;
- }
-
- if (filepath) {
- g_free(filepath);
- }
- filepath = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
-
- gtk_widget_destroy(dialog);
-*/
-// d_setState("filepath", "/home/damien/Music/rhodes-nord/rhodes-nord.sfz");
- d_setState("filepath", "/home/damien/Music/sfz/sfz LatelyBass/TX_LatelyBass.sfz");
+ getParentWindow().openFileBrowser(opts);
}
void ZamSFZUI::onDisplay()
diff --git a/plugins/ZamSFZ/ZamSFZUI.hpp b/plugins/ZamSFZ/ZamSFZUI.hpp
index f0d1047..e82f406 100644
--- a/plugins/ZamSFZ/ZamSFZUI.hpp
+++ b/plugins/ZamSFZ/ZamSFZUI.hpp
@@ -46,6 +46,8 @@ protected:
void d_programChanged(uint32_t index) override;
void d_stateChanged(const char* key, const char* value) override;
+ void d_uiFileBrowserSelected(const char* filename) override;
+
// -------------------------------------------------------------------
// Widget Callbacks
@@ -55,6 +57,7 @@ protected:
void imageButtonClicked(ImageButton*, int) override;
void onDisplay() override;
+
private:
Image fImgBackground;
ScopedPointer<ImageKnob> fKnobGain;