diff options
| author | Jean-Philippe Bruyère <jp_bruyere@hotmail.com> | 2021-11-24 23:50:38 +0100 |
|---|---|---|
| committer | Jean-Philippe Bruyère <jp_bruyere@hotmail.com> | 2021-11-24 23:50:38 +0100 |
| commit | b25ad7a6e15a7284e40407377a1624747ffe42e5 (patch) | |
| tree | f53a12fb2120eb7f1b82ccfbda37a2430c5be330 | |
| parent | 09026f4328d0e578823062d6506ffdacac457b25 (diff) | |
IBackend instead of IDevice to bind to Interface
| -rw-r--r-- | Backends/CairoBackend/src/DefaultBackend.cs | 16 | ||||
| -rw-r--r-- | Backends/CairoBackend/src/Device.cs | 94 | ||||
| -rw-r--r-- | Backends/CairoBackend/src/EGLDevice.cs | 7 | ||||
| -rw-r--r-- | Backends/CairoBackend/src/GLSurface.cs | 2 | ||||
| -rw-r--r-- | Backends/CairoBackend/src/ImageBackend.cs | 179 | ||||
| -rw-r--r-- | Crow/src/Fill/BmpPicture.cs | 10 | ||||
| -rw-r--r-- | Crow/src/Fill/Gradient.cs | 2 | ||||
| -rw-r--r-- | Crow/src/Fill/SvgPicture.cs | 8 | ||||
| -rw-r--r-- | Crow/src/Interface.cs | 615 | ||||
| -rw-r--r-- | Crow/src/Widgets/DockWindow.cs | 4 | ||||
| -rw-r--r-- | Crow/src/Widgets/GroupBase.cs | 2 | ||||
| -rw-r--r-- | Crow/src/Widgets/Label.cs | 4 | ||||
| -rw-r--r-- | Crow/src/Widgets/PrivateContainer.cs | 2 | ||||
| -rw-r--r-- | Crow/src/Widgets/ScrollingStack.cs | 2 | ||||
| -rw-r--r-- | Crow/src/Widgets/Shape.cs | 2 | ||||
| -rw-r--r-- | Crow/src/Widgets/Widget.cs | 7 | ||||
| -rw-r--r-- | Drawing2D/src/Enums.cs | 6 | ||||
| -rw-r--r-- | Drawing2D/src/IBackend.cs | 37 | ||||
| -rw-r--r-- | Drawing2D/src/IDevice.cs | 10 | ||||
| -rw-r--r-- | Samples/HelloWorld/main.cs | 8 | ||||
| -rw-r--r-- | Samples/PerfTests/Program.cs | 22 | ||||
| -rw-r--r-- | Samples/common/src/SampleBase.cs | 5 |
22 files changed, 559 insertions, 485 deletions
diff --git a/Backends/CairoBackend/src/DefaultBackend.cs b/Backends/CairoBackend/src/DefaultBackend.cs new file mode 100644 index 00000000..9ef04b33 --- /dev/null +++ b/Backends/CairoBackend/src/DefaultBackend.cs @@ -0,0 +1,16 @@ +using System; +using Crow.CairoBackend; + +namespace Crow.Backends +{ + public class DefaultBackend : ImageBackend { + /// <summary> + /// Create a new generic backend bound to the application surface + /// </summary> + /// <param name="width">backend surface width</param> + /// <param name="height">backend surface height</param> + public DefaultBackend (IntPtr nativeWindoPointer, int width, int height) + : base (nativeWindoPointer, width, height) { } + } +} + diff --git a/Backends/CairoBackend/src/Device.cs b/Backends/CairoBackend/src/Device.cs index c87b494b..2efe76ec 100644 --- a/Backends/CairoBackend/src/Device.cs +++ b/Backends/CairoBackend/src/Device.cs @@ -77,18 +77,9 @@ namespace Crow.CairoBackend } public class Device : IDevice { - /// <summary> Global font rendering settings for Cairo </summary> - FontOptions FontRenderingOptions; - /// <summary> Global font rendering settings for Cairo </summary> - Antialias Antialias = Antialias.Subpixel; public Device() { - FontRenderingOptions = new FontOptions (); - FontRenderingOptions.Antialias = Antialias.Subpixel; - FontRenderingOptions.HintMetrics = HintMetrics.On; - FontRenderingOptions.HintStyle = HintStyle.Full; - FontRenderingOptions.SubpixelOrder = SubpixelOrder.Default; } ~Device () @@ -106,87 +97,12 @@ namespace Crow.CairoBackend { throw new NotImplementedException(); } - public IRegion CreateRegion () => new Region (); - public virtual ISurface CreateSurface(int width, int height) - => new ImageSurface (Format.ARGB32, width, height); - public virtual ISurface CreateSurface(byte[] data, int width, int height) - => new ImageSurface (data, Format.ARGB32, width, height, 4 * width); - public virtual ISurface CreateSurface (IntPtr nativeWindoPointer, int width, int height) { - switch (Environment.OSVersion.Platform) { - case PlatformID.Unix: - IntPtr disp = Glfw3.GetX11Display (); - IntPtr nativeWin = Glfw3.GetX11Window (nativeWindoPointer); - Int32 scr = Glfw3.GetX11DefaultScreen (disp); - IntPtr visual = Glfw3.GetX11DefaultVisual (disp, scr); - return new XlibSurface (disp, nativeWin, visual, width, height); - case PlatformID.Win32NT: - case PlatformID.Win32S: - case PlatformID.Win32Windows: - IntPtr hWin32 = Glfw3.GetWin32Window (nativeWindoPointer); - IntPtr hdc = Glfw3.GetWin32DC (hWin32); - return new Win32Surface (hdc); - } - throw new PlatformNotSupportedException ("Unable to create cairo surface."); - } - public virtual IContext CreateContext(ISurface surf) - { - Context gr = new Context (surf); - gr.FontOptions = FontRenderingOptions; - gr.Antialias = Antialias; - return gr; - } - public byte[] LoadBitmap (Stream stream, out Size dimensions) { - byte[] image; -#if STB_SHARP - StbImageSharp.ImageResult stbi = StbImageSharp.ImageResult.FromStream (stream, StbImageSharp.ColorComponents.RedGreenBlueAlpha); - image = new byte[stbi.Data.Length]; - //rgba to argb for cairo. - for (int i = 0; i < stbi.Data.Length; i += 4) { - image[i] = stbi.Data[i + 2]; - image[i + 1] = stbi.Data[i + 1]; - image[i + 2] = stbi.Data[i]; - image[i + 3] = stbi.Data[i + 3]; - } - dimensions = new Size (stbi.Width, stbi.Height); -#else - using (StbImage stbi = new StbImage (stream)) { - image = new byte [stbi.Size]; - for (int i = 0; i < stbi.Size; i+=4) { - //rgba to argb for cairo. ???? looks like bgra to me. - image [i] = Marshal.ReadByte (stbi.Handle, i + 2); - image [i + 1] = Marshal.ReadByte (stbi.Handle, i + 1); - image [i + 2] = Marshal.ReadByte (stbi.Handle, i); - image [i + 3] = Marshal.ReadByte (stbi.Handle, i + 3); - } - dimensions = new Size (stbi.Width, stbi.Height); - } -#endif - return image; - } - public ISvgHandle LoadSvg(Stream stream) - { - using (BinaryReader sr = new BinaryReader (stream)) - return new SvgHandle (sr.ReadBytes ((int)stream.Length)); - } - public ISvgHandle LoadSvg(string svgFragment) => - new SvgHandle (System.Text.Encoding.Unicode.GetBytes (svgFragment)); - public IGradient CreateGradient (GradientType gradientType, Rectangle bounds) { - switch (gradientType) { - case GradientType.Vertical: - return new LinearGradient (bounds.Left, bounds.Top, bounds.Left, bounds.Bottom); - case GradientType.Horizontal: - return new LinearGradient (bounds.Left, bounds.Top, bounds.Right, bounds.Top); - case GradientType.Oblic: - return new LinearGradient (bounds.Left, bounds.Top, bounds.Right, bounds.Bottom); - case GradientType.Radial: - throw new NotImplementedException (); - } - return null; - } + + #endregion #region IDispose implementation @@ -198,8 +114,10 @@ namespace Crow.CairoBackend protected virtual void Dispose (bool disposing) { - if (disposing) - FontRenderingOptions.Dispose (); + if (disposing){ + + } + } #endregion } diff --git a/Backends/CairoBackend/src/EGLDevice.cs b/Backends/CairoBackend/src/EGLDevice.cs index 83629374..d9296b74 100644 --- a/Backends/CairoBackend/src/EGLDevice.cs +++ b/Backends/CairoBackend/src/EGLDevice.cs @@ -34,15 +34,16 @@ namespace Crow.CairoBackend { public class EGLDevice : GLDevice { - public EGLDevice (IntPtr dpy, IntPtr gl_ctx, bool threadAwayre = false) : base (NativeMethods.cairo_egl_device_create (dpy, gl_ctx), true) + public EGLDevice (IntPtr dpy, IntPtr gl_ctx, bool threadAwayre = false) + : base (NativeMethods.cairo_egl_device_create (dpy, gl_ctx), true) { SetThreadAware(threadAwayre); } - public override ISurface CreateSurface(int width, int height) + /*public override ISurface CreateSurface(int width, int height) => new ImageSurface (Format.ARGB32, width, height); public override ISurface CreateSurface (IntPtr nativeWindoPointer, int width, int height) { return new GLSurface (this, Glfw.Glfw3.GetEGLSurface (nativeWindoPointer), width, height); - } + }*/ } } diff --git a/Backends/CairoBackend/src/GLSurface.cs b/Backends/CairoBackend/src/GLSurface.cs index c7959dc3..1f486c50 100644 --- a/Backends/CairoBackend/src/GLSurface.cs +++ b/Backends/CairoBackend/src/GLSurface.cs @@ -54,7 +54,7 @@ namespace Crow.CairoBackend { {} public override void Flush () { - base.Flush (); + //base.Flush (); SwapBuffers (); } public override int Width => NativeMethods.cairo_gl_surface_get_width (handle); diff --git a/Backends/CairoBackend/src/ImageBackend.cs b/Backends/CairoBackend/src/ImageBackend.cs new file mode 100644 index 00000000..e390ca98 --- /dev/null +++ b/Backends/CairoBackend/src/ImageBackend.cs @@ -0,0 +1,179 @@ +using System; +using System.IO; +using Drawing2D; +using Glfw; + +namespace Crow.CairoBackend +{ + public class ImageBackend : IBackend { + ISurface surf; + /// <summary> Global font rendering settings for Cairo </summary> + FontOptions FontRenderingOptions; + /// <summary> Global font rendering settings for Cairo </summary> + Antialias Antialias = Antialias.Subpixel; + protected ImageBackend () + { + FontRenderingOptions = new FontOptions (); + FontRenderingOptions.Antialias = Antialias.Subpixel; + FontRenderingOptions.HintMetrics = HintMetrics.On; + FontRenderingOptions.HintStyle = HintStyle.Full; + FontRenderingOptions.SubpixelOrder = SubpixelOrder.Default; + } + /// <summary> + /// Create a new generic backend bound to the application surface + /// </summary> + /// <param name="width">backend surface width</param> + /// <param name="height">backend surface height</param> + public ImageBackend (IntPtr nativeWindoPointer, int width, int height) : this () { + + switch (Environment.OSVersion.Platform) { + case PlatformID.Unix: + IntPtr disp = Glfw3.GetX11Display (); + IntPtr nativeWin = Glfw3.GetX11Window (nativeWindoPointer); + Int32 scr = Glfw3.GetX11DefaultScreen (disp); + IntPtr visual = Glfw3.GetX11DefaultVisual (disp, scr); + surf = new XlibSurface (disp, nativeWin, visual, width, height); + break; + case PlatformID.Win32NT: + case PlatformID.Win32S: + case PlatformID.Win32Windows: + IntPtr hWin32 = Glfw3.GetWin32Window (nativeWindoPointer); + IntPtr hdc = Glfw3.GetWin32DC (hWin32); + surf = new Win32Surface (hdc); + break; + default: + throw new PlatformNotSupportedException ("Unable to create cairo image backend."); + } + } + /// <summary> + /// Create a new offscreen backend, used in perfTests + /// </summary> + /// <param name="width">backend surface width</param> + /// <param name="height">backend surface height</param> + public ImageBackend (int width, int height) : this () { + surf = new ImageSurface (Format.ARGB32, width, height); + } + ~ImageBackend () + { + Dispose (false); + } + public virtual ISurface CreateSurface(int width, int height) + => new ImageSurface (Format.ARGB32, width, height); + public virtual ISurface CreateSurface(byte[] data, int width, int height) + => new ImageSurface (data, Format.ARGB32, width, height, 4 * width); + public ISurface MainSurface => surf; + public IRegion CreateRegion () => new Region (); + public IContext CreateContext (ISurface surf) + { + Context gr = new Context (surf); + gr.FontOptions = FontRenderingOptions; + gr.Antialias = Antialias; + return gr; + } + //IPattern CreatePattern (PatternType patternType); + public IGradient CreateGradient (GradientType gradientType, Rectangle bounds) + { + switch (gradientType) { + case GradientType.Vertical: + return new LinearGradient (bounds.Left, bounds.Top, bounds.Left, bounds.Bottom); + case GradientType.Horizontal: + return new LinearGradient (bounds.Left, bounds.Top, bounds.Right, bounds.Top); + case GradientType.Oblic: + return new LinearGradient (bounds.Left, bounds.Top, bounds.Right, bounds.Bottom); + case GradientType.Radial: + throw new NotImplementedException (); + } + return null; + } + public byte[] LoadBitmap (Stream stream, out Size dimensions) + { + byte[] image; +#if STB_SHARP + StbImageSharp.ImageResult stbi = StbImageSharp.ImageResult.FromStream (stream, StbImageSharp.ColorComponents.RedGreenBlueAlpha); + image = new byte[stbi.Data.Length]; + //rgba to argb for cairo. + for (int i = 0; i < stbi.Data.Length; i += 4) { + image[i] = stbi.Data[i + 2]; + image[i + 1] = stbi.Data[i + 1]; + image[i + 2] = stbi.Data[i]; + image[i + 3] = stbi.Data[i + 3]; + } + dimensions = new Size (stbi.Width, stbi.Height); +#else + using (StbImage stbi = new StbImage (stream)) { + image = new byte [stbi.Size]; + for (int i = 0; i < stbi.Size; i+=4) { + //rgba to argb for cairo. ???? looks like bgra to me. + image [i] = Marshal.ReadByte (stbi.Handle, i + 2); + image [i + 1] = Marshal.ReadByte (stbi.Handle, i + 1); + image [i + 2] = Marshal.ReadByte (stbi.Handle, i); + image [i + 3] = Marshal.ReadByte (stbi.Handle, i + 3); + } + dimensions = new Size (stbi.Width, stbi.Height); + } +#endif + return image; + } + public ISvgHandle LoadSvg(Stream stream) + { + using (BinaryReader sr = new BinaryReader (stream)) + return new SvgHandle (sr.ReadBytes ((int)stream.Length)); + } + public ISvgHandle LoadSvg(string svgFragment) => + new SvgHandle (System.Text.Encoding.Unicode.GetBytes (svgFragment)); + bool disposeContextOnFlush; + IRegion clipping; + protected void clear(IContext ctx) { + for (int i = 0; i < clipping.NumRectangles; i++) + ctx.Rectangle (clipping.GetRectangle (i)); + + ctx.ClipPreserve (); + ctx.Operator = Operator.Clear; + ctx.Fill (); + ctx.Operator = Operator.Over; + } + public IContext PrepareUIFrame(IContext existingContext, IRegion clipping) + { + this.clipping = clipping; + IContext ctx = existingContext; + if (ctx == null) { + disposeContextOnFlush = true; + ctx = new Context (MainSurface); + } else + disposeContextOnFlush = false; + + clear (ctx); + ctx.PushGroup (); + + return ctx; + } + public void FlushUIFrame(IContext ctx) + { + ctx.PopGroupToSource (); + ctx.Paint (); + + if (disposeContextOnFlush) + ctx.Dispose (); + clipping = null; + } + + + #region IDispose implementation + bool isDisposed; + public void Dispose () + { + Dispose (true); + GC.SuppressFinalize (this); + } + protected virtual void Dispose (bool disposing) + { + if (!isDisposed && disposing) { + surf.Dispose (); + FontRenderingOptions.Dispose (); + } + isDisposed = true; + } + #endregion + } +} + diff --git a/Crow/src/Fill/BmpPicture.cs b/Crow/src/Fill/BmpPicture.cs index 72cc36b8..813e8dec 100644 --- a/Crow/src/Fill/BmpPicture.cs +++ b/Crow/src/Fill/BmpPicture.cs @@ -40,7 +40,7 @@ namespace Crow return; } using (Stream stream = iFace.GetStreamFromPath (Path)) { - image = iFace.Device.LoadBitmap (stream, out Size dimensions); + image = iFace.Backend.LoadBitmap (stream, out Size dimensions); Dimensions = dimensions; iFace.sharedPictures[Path] = new sharedPicture (image, Dimensions); } @@ -89,13 +89,13 @@ namespace Crow widthRatio = heightRatio; } - using (ISurface tmp = iFace.Device.CreateSurface (bounds.Width, bounds.Height)) { - using (IContext gr = iFace.Device.CreateContext (tmp)) { + using (ISurface tmp = iFace.Backend.CreateSurface (bounds.Width, bounds.Height)) { + using (IContext gr = iFace.Backend.CreateContext (tmp)) { gr.Translate (bounds.Left, bounds.Top); gr.Scale (widthRatio, heightRatio); gr.Translate ((bounds.Width/widthRatio - Dimensions.Width)/2, (bounds.Height/heightRatio - Dimensions.Height)/2); - using (ISurface imgSurf = iFace.Device.CreateSurface (bounds.Width, bounds.Height)) { + using (ISurface imgSurf = iFace.Backend.CreateSurface (bounds.Width, bounds.Height)) { gr.SetSource (imgSurf, 0,0); gr.Paint (); } @@ -138,7 +138,7 @@ namespace Crow gr.Scale (widthRatio, heightRatio); gr.Translate ((rect.Width/widthRatio - Dimensions.Width)/2, (rect.Height/heightRatio - Dimensions.Height)/2); - using (ISurface imgSurf = iFace.Device.CreateSurface (image, Dimensions.Width, Dimensions.Height)) { + using (ISurface imgSurf = iFace.Backend.CreateSurface (image, Dimensions.Width, Dimensions.Height)) { gr.SetSource (imgSurf, 0,0); gr.Paint (); } diff --git a/Crow/src/Fill/Gradient.cs b/Crow/src/Fill/Gradient.cs index d161eba8..abf841e3 100644 --- a/Crow/src/Fill/Gradient.cs +++ b/Crow/src/Fill/Gradient.cs @@ -54,7 +54,7 @@ namespace Crow public override void SetAsSource (Interface iFace, IContext ctx, Rectangle bounds = default(Rectangle)) { - IGradient grad = iFace.Device.CreateGradient (Type, bounds); + IGradient grad = iFace.Backend.CreateGradient (Type, bounds); foreach (ColorStop cs in Stops) { if (cs == null) continue; diff --git a/Crow/src/Fill/SvgPicture.cs b/Crow/src/Fill/SvgPicture.cs index d562b342..e6e54aff 100644 --- a/Crow/src/Fill/SvgPicture.cs +++ b/Crow/src/Fill/SvgPicture.cs @@ -38,13 +38,13 @@ namespace Crow return; } using (Stream stream = iFace.GetStreamFromPath (Path)) - hSVG = iFace.Device.LoadSvg (stream); + hSVG = iFace.Backend.LoadSvg (stream); Dimensions = hSVG.Dimensions; iFace.sharedPictures [Path] = new sharedPicture (hSVG, Dimensions); } public void LoadSvgFragment (Interface iface, string fragment) { - hSVG = iface.Device.LoadSvg (fragment); + hSVG = iface.Backend.LoadSvg (fragment); Dimensions = hSVG.Dimensions; } @@ -70,8 +70,8 @@ namespace Crow widthRatio = heightRatio; } - using (ISurface tmp = iFace.Device.CreateSurface (bounds.Width, bounds.Height)) { - using (IContext gr = iFace.Device.CreateContext (tmp)) { + using (ISurface tmp = iFace.Backend.CreateSurface (bounds.Width, bounds.Height)) { + using (IContext gr = iFace.Backend.CreateContext (tmp)) { gr.Translate (bounds.Left, bounds.Top); gr.Scale (widthRatio, heightRatio); gr.Translate ((bounds.Width/widthRatio - Dimensions.Width)/2, (bounds.Height/heightRatio - Dimensions.Height)/2); diff --git a/Crow/src/Interface.cs b/Crow/src/Interface.cs index e0c00ede..19d12f3f 100644 --- a/Crow/src/Interface.cs +++ b/Crow/src/Interface.cs @@ -50,6 +50,54 @@ namespace Crow } #endregion + #region Static and constants + //initial capacity for layouting and clipping queues. + const int INIT_QUEUE_CAPACITY = 512; + /// <summary>Time interval in milisecond between Updates of the interface</summary> + public static int UPDATE_INTERVAL = 5; + /// <summary> + /// Time interval in milisecond between Glfw polling for devices. Wait is done in + /// the 'UpdateFrame' method in the 'Run' cycle and may be overriden. + /// </summary> + public static int POLLING_INTERVAL = 1; + /// <summary>Crow configuration root path</summary> + public static string CROW_CONFIG_ROOT; + /// <summary>If true, mouse focus is given when mouse is over control</summary> + public static bool FOCUS_ON_HOVER = false; + /// <summary>If true, newly focused window will be put on top</summary> + public static bool RAISE_WIN_ON_FOCUS = true; + /// <summary> Threshold to catch borders for sizing </summary> + public static int BorderThreshold = 3; + /// <summary> delay before tooltip appears </summary> + public static int TOOLTIP_DELAY = 500; + /// <summary>Double click threshold in milisecond</summary> + public static int DOUBLECLICK_TRESHOLD = 320;//max duration between two mouse_down evt for a dbl clk in milisec. + /// <summary> Time to wait in millisecond before starting repeat loop</summary> + public static int DEVICE_REPEAT_DELAY = 600; + /// <summary> Time interval in millisecond between device event repeat</summary> + public static int DEVICE_REPEAT_INTERVAL = 100; + public static float WheelIncrement = 1; + /// <summary>Tabulation size in Text controls</summary> + public static int TAB_SIZE = 4; + [Obsolete]public static string LineBreak = "\n"; + /// <summary> Allow rendering of interface in development environment </summary> + public static bool DesignerMode = false; + /// <summary> Disable caching for a widget if this threshold is reached </summary> + public const int MaxCacheSize = 2048; + /// <summary> Above this count, the layouting is discard from the current + /// update cycle and requeued for the next</summary> + public static int MaxLayoutingTries = 30; + /// <summary> Above this count, the layouting is discard for the widget and it + /// will not be rendered on screen </summary> + public static int MaxDiscardCount = 5; + + /// <summary> + /// Each control need a ref to the root interface containing it, if not set in Widget.currentInterface, + /// the ref of this one will be stored in Widget.currentInterface + /// </summary> + //protected static Interface CurrentInterface; + #endregion + internal static List<Assembly> crowAssemblies = new List<Assembly> (); /// <summary> /// Add Assembly that may contains CROW ui ressources like custom widget classes, IML, images, ... @@ -78,8 +126,6 @@ namespace Crow init_internal (); } } - static void nativeHelpMessage () { - } static IntPtr resolveUnmanaged(Assembly assembly, String libraryName) { try { @@ -171,33 +217,31 @@ namespace Crow /// <param name="height">the height of the window</param> /// <param name="glfwWindowHandle">A valid GLFW window handle</param> /// <returns></returns> - public Interface (int width, int height, IntPtr glfwWindowHandle) : this (width, height, false, false) + public Interface (int width, int height, IntPtr glfwWindowHandle) : this (width, height, true, false) { hWin = glfwWindowHandle; PerformanceMeasure.InitMeasures (); } - IDevice dev; - public IDevice Device => dev; - /// <summary> /// Create a standard Crow interface. /// </summary> /// <param name="width">the width of the native window</param> /// <param name="height">the height of the native window</param> - /// <param name="startUIThread">If 'yes' start the ui update (InterfaceThread method) in a dedicated thread</param> + /// <param name="singleThreaded">If 'false' start the ui update (InterfaceThread method) in a dedicated thread</param> /// <param name="createSurface">If 'yes', create the main rendering surface on the native window</param> - public Interface (int width = 800, int height = 600, bool startUIThread = true, bool createSurface = true) + public Interface (int width = 800, int height = 600, bool singleThreaded = false, bool createSurface = true) { CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture; //CurrentInterface = this; clientRectangle = new Rectangle (0, 0, width, height); + SingleThreaded = singleThreaded; if (createSurface) initSurface (); PerformanceMeasure.InitMeasures (); - if (startUIThread) { + if (!SingleThreaded) { Thread t = new Thread (InterfaceThread) { IsBackground = true }; @@ -206,6 +250,57 @@ namespace Crow } #endregion + #region IDisposable Support + private bool disposedValue = false; // To detect redundant calls + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + disposeContextMenus (); + Backend.Dispose (); + } + + currentCursor?.Dispose (); + + if (ownWindow) { + Glfw3.DestroyWindow (hWin); + Glfw3.Terminate (); + } + + disposedValue = true; + } + } + ~Interface() { + Dispose(false); + } + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + #endregion + + + /// <summary> + /// Cache already searched extension methods to prevent searching again and again for + /// missing method or to speedup loading once a method is found. + /// </summary> + /// <remarks> + /// This cache is reseted when a crow assembly is removed, or the theme is changed. + /// </remarks> + protected Dictionary<string, MethodInfo> knownExtMethods; + /// <summary> + /// Cache already searched widget types. + /// </summary> + /// <remarks> + /// This cache is reseted when a crow assembly is removed, or the theme is changed. + /// </remarks> + protected Dictionary<string, Type> knownCrowWidgetTypes; + /// <summary>Client rectangle in the host context</summary> + protected Rectangle clientRectangle; #if MEASURE_TIME public PerformanceMeasure[] PerfMeasures => PerformanceMeasure.Measures; #endif @@ -214,12 +309,77 @@ namespace Crow static Dictionary<IntPtr, Interface> windows = new Dictionary<IntPtr, Interface> (); /** GLFW window native pointer and current native handle for mouse cursor */ IntPtr hWin; + protected IBackend backend;//backend device + /// <summary>Clipping rectangles on the root context</summary> + protected IRegion clipping; + static string backendDeviceTypeString = "Crow.CairoBackend.Device"; Cursor currentCursor; bool ownWindow; /// <summary> + /// If `true`, UI updates will be handle in the `Run()` method, so in the main thread of the application along with GLFW events polling. + /// If `false`, A dedicated thread will be started for the UI updates. + /// </summary> + public readonly bool SingleThreaded; + /// <summary> /// Native GLFW window handle bound to this interface. /// </summary> public IntPtr WindowHandle => hWin; + public IBackend Backend => backend; + /// <summary>Main backend surface</summary> + public ISurface MainSurface => backend.MainSurface; + public IntPtr SurfacePointer { + get { + lock(UpdateMutex) + return MainSurface.Handle; + } + } + + #region Public Fields + /// <summary>Graphic Tree of this interface</summary> + public List<Widget> GraphicTree = new List<Widget>(); + /// <summary>Interface's resulting bitmap</summary> + public byte[] bmp; + /// <summary>resulting bitmap limited to last redrawn part</summary> + public byte[] dirtyBmp; + /// <summary>True when host has to repaint Interface</summary> + public bool IsDirty; + /// <summary>Coordinate of the dirty bmp on the original bmp</summary> + public Rectangle DirtyRect; + /// <summary>Locked for each layouting operation</summary> + public object LayoutMutex = new object(); + /// <summary>Sync mutex between host and Crow for rendering operations (bmp, dirtyBmp,...)</summary> + public object RenderMutex = new object(); + /// <summary>Global lock of the update cycle</summary> + public object UpdateMutex = new object(); + /// <summary>Global lock of the clipping queue</summary> + public object ClippingMutex = new object(); + //TODO:share resource instances + /// <summary> + /// Store loaded resources instances shared among controls to reduce memory footprint + /// </summary> + public Dictionary<string,object> Ressources = new Dictionary<string, object>(); + /// <summary>The Main layouting queue.</summary> + public Queue<LayoutingQueueItem> LayoutingQueue = new Queue<LayoutingQueueItem> (INIT_QUEUE_CAPACITY); + /// <summary>Store discarded lqi between two updates</summary> + public Queue<LayoutingQueueItem> DiscardQueue; + /// <summary>Main drawing queue, holding layouted controls</summary> + public Queue<Widget> ClippingQueue = new Queue<Widget>(INIT_QUEUE_CAPACITY); + //TODO:use object instead for complex copy paste + public string Clipboard { + get => Glfw3.GetClipboardString (hWin); + set => Glfw3.SetClipboardString (hWin, value); + } + /// <summary>each IML and fragments (such as inline Templates) are compiled as a Dynamic Method stored here + /// on the first instance creation of a IML item. + /// </summary> + public Dictionary<String, Instantiator> Instantiators; + /// <summary> + /// Item templates stored with their index + /// </summary> + public Dictionary<String, ItemTemplate> ItemTemplates; + + public List<CrowThread> CrowThreads = new List<CrowThread>();//used to monitor thread finished + #endregion /// <summary> /// Register GLFW window callbacs (mouse, keyboard, sizing, refresh). @@ -240,19 +400,10 @@ namespace Crow #endif Glfw3.SetWindowRefreshCallback (hWin, HandleWindowRefreshDelegate); } -#if VKVG - protected VulkanContextBase vkCtx; - internal Device vkvgDevice => vkCtx.VkvgDevice; - /// <summary> - /// Create the VulkanContext with swapchain support. Override it to use another vulkan backend. - /// </summary> -#endif - static string backendDeviceTypeString = "Crow.CairoBackend.Device"; - protected void initBackend (bool offscreen = false) { - - dev = new Crow.CairoBackend.Device (); - clipping = dev.CreateRegion (); + protected virtual void initBackend () { + backend = new Crow.CairoBackend.ImageBackend (hWin, clientRectangle.Width, clientRectangle.Height); + clipping = Backend.CreateRegion (); } /// <summary> /// Create the main rendering surface. The default is a GLFW window with a cairo surface bound to it. @@ -261,89 +412,24 @@ namespace Crow { Glfw3.Init (); - Glfw3.WindowHint (WindowAttribute.ClientApi, 0); + Glfw3.WindowHint (WindowAttribute.ClientApi, Constants.OpenglEsApi); + Glfw3.WindowHint (WindowAttribute.ContextVersionMajor, 3); + //Glfw3.WindowHint (WindowAttribute.ContextVersionMajor, 0); + Glfw3.WindowHint (WindowAttribute.ContextCreationApi, Constants.EglContextApi); + Glfw3.WindowHint (WindowAttribute.Resizable, 1); Glfw3.WindowHint (WindowAttribute.Decorated, 1); hWin = Glfw3.CreateWindow (clientRectangle.Width, clientRectangle.Height, "win name", MonitorHandle.Zero, IntPtr.Zero); if (hWin == IntPtr.Zero) - throw new Exception ("[GLFW3] Unable to create vulkan Window"); + throw new Exception ("[GLFW3] Unable to create Window"); ownWindow = true; registerGlfwCallbacks (); initBackend (); - - CreateMainSurface (ref clientRectangle); - } - /// <summary> - /// ?? - /// </summary> - /// <param name="r"></param> - public void CreateMainSurface (ref Rectangle r) { - surf?.Dispose(); - surf = Device.CreateSurface (hWin, r.Width, r.Height); - } - public IntPtr SurfacePointer { - get { - lock(UpdateMutex) - return surf.Handle; - } - } - /// <summary> - /// Set the main GLFW window icon. - /// </summary> - /// <param name="path"></param> - public void SetWindowIcon (string path) { - using (Stream stream = GetStreamFromPath (path)) { -#if STB_SHARP - StbImageSharp.ImageResult stbi = StbImageSharp.ImageResult.FromStream (stream, StbImageSharp.ColorComponents.RedGreenBlueAlpha); - byte[] image = new byte[stbi.Data.Length]; - //rgba to argb for cairo. - for (int i = 0; i < stbi.Data.Length; i += 4) { - image[i] = stbi.Data[i + 2]; - image[i + 1] = stbi.Data[i + 1]; - image[i + 2] = stbi.Data[i]; - image[i + 3] = stbi.Data[i + 3]; - } - Glfw.Image icon = new Glfw.Image ((uint)stbi.Width, (uint)stbi.Height, image); - Glfw3.SetWindowIcon (hWin, 1, ref icon); - icon.Dispose(); - -#else - using (StbImage stbi = new StbImage (stream)) { - byte[] image = new byte [stbi.Size]; - //rgba to argb for cairo. - for (int i = 0; i < stbi.Size; i+=4) { - image [i] = Marshal.ReadByte (stbi.Handle, i + 2); - image [i + 1] = Marshal.ReadByte (stbi.Handle, i + 1); - image [i + 2] = Marshal.ReadByte (stbi.Handle, i); - image [i + 3] = Marshal.ReadByte (stbi.Handle, i + 3); - } - Glfw.Image icon = new Glfw.Image ((uint)stbi.Width, (uint)stbi.Height, image); - Glfw3.SetWindowIcon (hWin, 1, ref icon); - icon.Dispose(); - } -#endif - } } /// <summary> - /// Cache already searched extension methods to prevent searching again and again for - /// missing method or to speedup loading once a method is found. - /// </summary> - /// <remarks> - /// This cache is reseted when a crow assembly is removed, or the theme is changed. - /// </remarks> - protected Dictionary<string, MethodInfo> knownExtMethods; - /// <summary> - /// Cache already searched widget types. - /// </summary> - /// <remarks> - /// This cache is reseted when a crow assembly is removed, or the theme is changed. - /// </remarks> - protected Dictionary<string, Type> knownCrowWidgetTypes; - - /// <summary> /// search for graphic object type in crow assembly, if not found, /// search for type independently of namespace in all the loaded assemblies /// </summary> @@ -403,17 +489,34 @@ namespace Crow return mi; } - #region events delegates + #region Events + //public event EventHandler<MouseCursorChangedEventArgs> MouseCursorChanged; + ////public event EventHandler Quit; + public event EventHandler Initialized; + public event EventHandler StartDragOperation; + public event EventHandler EndDragOperation; + public event EventHandler<KeyEventArgs> KeyDown; + public event EventHandler<KeyEventArgs> KeyUp; + + //public event EventHandler<MouseWheelEventArgs> MouseWheelChanged; + //public event EventHandler<MouseButtonEventArgs> MouseButtonUp; + //public event EventHandler<MouseButtonEventArgs> MouseButtonDown; + //public event EventHandler<MouseButtonEventArgs> MouseClick; + //public event EventHandler<MouseMoveEventArgs> MouseMove; + //public event EventHandler<KeyEventArgs> KeyDown; + //public event EventHandler<KeyPressEventArgs> KeyPress; + /*public event EventHandler<KeyEventArgs> KeyboardKeyDown; + public event EventHandler<KeyEventArgs> KeyboardKeyUp;*/ + #region events delegates static CursorPosDelegate HandleCursorPosDelegate = (window, xPosition, yPosition) => { - windows [window].OnMouseMove ((int)(xPosition / windows [window].ZoomFactor), (int)(yPosition / windows [window].ZoomFactor)); + windows [window].OnMouseMove ((int)(xPosition), (int)(yPosition)); }; static MouseButtonDelegate HandleMouseButtonDelegate = (IntPtr window, MouseButton button, InputAction action, Modifier mods) => { if (action == InputAction.Release) windows [window].OnMouseButtonUp (button); else//press and repeat windows [window].OnMouseButtonDown (button); - }; static ScrollDelegate HandleScrollDelegate = (IntPtr window, double xOffset, double yOffset) => { windows [window].OnMouseWheelChanged ((int)yOffset); @@ -438,24 +541,53 @@ namespace Crow static WindowDelegate HandleWindowRefreshDelegate = (IntPtr window) => { windows [window].registerRefreshClientRectangle (); }; - + #endregion #endregion public string WindowTitle { set => Glfw3.SetWindowTitle (hWin, value); } + /// <summary> + /// Set the main GLFW window icon. + /// </summary> + /// <param name="path"></param> + public void SetWindowIcon (string path) { + using (Stream stream = GetStreamFromPath (path)) { +#if STB_SHARP + StbImageSharp.ImageResult stbi = StbImageSharp.ImageResult.FromStream (stream, StbImageSharp.ColorComponents.RedGreenBlueAlpha); + byte[] image = new byte[stbi.Data.Length]; + //rgba to argb for cairo. + for (int i = 0; i < stbi.Data.Length; i += 4) { + image[i] = stbi.Data[i + 2]; + image[i + 1] = stbi.Data[i + 1]; + image[i + 2] = stbi.Data[i]; + image[i + 3] = stbi.Data[i + 3]; + } + Glfw.Image icon = new Glfw.Image ((uint)stbi.Width, (uint)stbi.Height, image); + Glfw3.SetWindowIcon (hWin, 1, ref icon); + icon.Dispose(); +#else + using (StbImage stbi = new StbImage (stream)) { + byte[] image = new byte [stbi.Size]; + //rgba to argb for cairo. + for (int i = 0; i < stbi.Size; i+=4) { + image [i] = Marshal.ReadByte (stbi.Handle, i + 2); + image [i + 1] = Marshal.ReadByte (stbi.Handle, i + 1); + image [i + 2] = Marshal.ReadByte (stbi.Handle, i); + image [i + 3] = Marshal.ReadByte (stbi.Handle, i + 3); + } + Glfw.Image icon = new Glfw.Image ((uint)stbi.Width, (uint)stbi.Height, image); + Glfw3.SetWindowIcon (hWin, 1, ref icon); + icon.Dispose(); + } +#endif + } + } public bool Running { get => !Glfw3.WindowShouldClose (hWin); set => Glfw3.SetWindowShouldClose (hWin, value == true ? 0 : 1); } - public virtual void InterfaceThread () - { - while (!Glfw3.WindowShouldClose (hWin)) { - Update (); - Thread.Sleep (UPDATE_INTERVAL); - } - } protected virtual void OnInitialized () { /*try { @@ -484,29 +616,38 @@ namespace Crow loadThemeFiles (); initContextMenus (); } + public virtual void InterfaceThread () + { + while (!Glfw3.WindowShouldClose (hWin)) { + Update (); + Thread.Sleep (UPDATE_INTERVAL); + } + } /// <summary> /// call Init() then enter the running loop performing ProcessEvents until running==false. /// </summary> public virtual void Run () { Init (); - while (!Glfw3.WindowShouldClose (hWin)) { - Glfw3.PollEvents (); - UpdateFrame (); + if (SingleThreaded) { + while (!Glfw3.WindowShouldClose (WindowHandle)) { + Glfw3.PollEvents (); + Update(); + UpdateFrame (); + Thread.Sleep (UPDATE_INTERVAL); + } + } else { + while (!Glfw3.WindowShouldClose (hWin)) { + Glfw3.PollEvents (); + UpdateFrame (); + Thread.Sleep (POLLING_INTERVAL); + } } Terminate (); } public virtual void Terminate () {} - public virtual void UpdateFrame () { -#if VKVG - Update (); - Thread.Sleep (UPDATE_INTERVAL); -#else - Thread.Sleep (POLLING_INTERVAL); -#endif - } - + public virtual void UpdateFrame () {} public virtual void Quit () => Glfw3.SetWindowShouldClose (hWin, 1); public bool Shift => Glfw3.GetKey(hWin, Key.LeftShift) == InputAction.Press || @@ -532,164 +673,18 @@ namespace Crow } #endif - #region IDisposable Support - private bool disposedValue = false; // To detect redundant calls - - protected virtual void Dispose(bool disposing) - { - if (!disposedValue) - { - if (disposing) - { - disposeContextMenus (); - dev.Dispose (); - } - - currentCursor?.Dispose (); - - if (ownWindow) { - Glfw3.DestroyWindow (hWin); - Glfw3.Terminate (); - } - - disposedValue = true; - } - } - ~Interface() { - Dispose(false); - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - #endregion - - #region Static and constants - //initial capacity for layouting and clipping queues. - const int INIT_QUEUE_CAPACITY = 512; - /// <summary>Time interval in milisecond between Updates of the interface</summary> - public static int UPDATE_INTERVAL = 5; - /// <summary> - /// Time interval in milisecond between Glfw polling for devices. Wait is done in - /// the 'UpdateFrame' method in the 'Run' cycle and may be overriden. - /// </summary> - public static int POLLING_INTERVAL = 1; - /// <summary>Crow configuration root path</summary> - public static string CROW_CONFIG_ROOT; - /// <summary>If true, mouse focus is given when mouse is over control</summary> - public static bool FOCUS_ON_HOVER = false; - /// <summary>If true, newly focused window will be put on top</summary> - public static bool RAISE_WIN_ON_FOCUS = true; - /// <summary> Threshold to catch borders for sizing </summary> - public static int BorderThreshold = 3; - /// <summary> delay before tooltip appears </summary> - public static int TOOLTIP_DELAY = 500; - /// <summary>Double click threshold in milisecond</summary> - public static int DOUBLECLICK_TRESHOLD = 320;//max duration between two mouse_down evt for a dbl clk in milisec. - /// <summary> Time to wait in millisecond before starting repeat loop</summary> - public static int DEVICE_REPEAT_DELAY = 600; - /// <summary> Time interval in millisecond between device event repeat</summary> - public static int DEVICE_REPEAT_INTERVAL = 100; - public static float WheelIncrement = 1; - /// <summary>Tabulation size in Text controls</summary> - public static int TAB_SIZE = 4; - [Obsolete]public static string LineBreak = "\n"; - /// <summary> Allow rendering of interface in development environment </summary> - public static bool DesignerMode = false; - /// <summary> Disable caching for a widget if this threshold is reached </summary> - public const int MaxCacheSize = 2048; - /// <summary> Above this count, the layouting is discard from the current - /// update cycle and requeued for the next</summary> - public static int MaxLayoutingTries = 30; - /// <summary> Above this count, the layouting is discard for the widget and it - /// will not be rendered on screen </summary> - public static int MaxDiscardCount = 5; - - /// <summary> - /// Each control need a ref to the root interface containing it, if not set in Widget.currentInterface, - /// the ref of this one will be stored in Widget.currentInterface - /// </summary> - //protected static Interface CurrentInterface; - #endregion - #region Events - //public event EventHandler<MouseCursorChangedEventArgs> MouseCursorChanged; - ////public event EventHandler Quit; - public event EventHandler Initialized; - public event EventHandler StartDragOperation; - public event EventHandler EndDragOperation; - public event EventHandler<KeyEventArgs> KeyDown; - public event EventHandler<KeyEventArgs> KeyUp; - //public event EventHandler<MouseWheelEventArgs> MouseWheelChanged; - //public event EventHandler<MouseButtonEventArgs> MouseButtonUp; - //public event EventHandler<MouseButtonEventArgs> MouseButtonDown; - //public event EventHandler<MouseButtonEventArgs> MouseClick; - //public event EventHandler<MouseMoveEventArgs> MouseMove; - //public event EventHandler<KeyEventArgs> KeyDown; - //public event EventHandler<KeyPressEventArgs> KeyPress; - /*public event EventHandler<KeyEventArgs> KeyboardKeyDown; - public event EventHandler<KeyEventArgs> KeyboardKeyUp;*/ - #endregion - /// <summary>Main backend surface</summary> - public ISurface surf; - #region Public Fields - /// <summary>Graphic Tree of this interface</summary> - public List<Widget> GraphicTree = new List<Widget>(); - /// <summary>Interface's resulting bitmap</summary> - public byte[] bmp; - /// <summary>resulting bitmap limited to last redrawn part</summary> - public byte[] dirtyBmp; - /// <summary>True when host has to repaint Interface</summary> - public bool IsDirty; - /// <summary>Coordinate of the dirty bmp on the original bmp</summary> - public Rectangle DirtyRect; - /// <summary>Locked for each layouting operation</summary> - public object LayoutMutex = new object(); - /// <summary>Sync mutex between host and Crow for rendering operations (bmp, dirtyBmp,...)</summary> - public object RenderMutex = new object(); - /// <summary>Global lock of the update cycle</summary> - public object UpdateMutex = new object(); - /// <summary>Global lock of the clipping queue</summary> - public object ClippingMutex = new object(); - //TODO:share resource instances - /// <summary> - /// Store loaded resources instances shared among controls to reduce memory footprint - /// </summary> - public Dictionary<string,object> Ressources = new Dictionary<string, object>(); - /// <summary>The Main layouting queue.</summary> - public Queue<LayoutingQueueItem> LayoutingQueue = new Queue<LayoutingQueueItem> (INIT_QUEUE_CAPACITY); - /// <summary>Store discarded lqi between two updates</summary> - public Queue<LayoutingQueueItem> DiscardQueue; - /// <summary>Main drawing queue, holding layouted controls</summary> - public Queue<Widget> ClippingQueue = new Queue<Widget>(INIT_QUEUE_CAPACITY); - //TODO:use object instead for complex copy paste - public string Clipboard { - get => Glfw3.GetClipboardString (hWin); - set => Glfw3.SetClipboardString (hWin, value); - } - /// <summary>each IML and fragments (such as inline Templates) are compiled as a Dynamic Method stored here - /// on the first instance creation of a IML item. - /// </summary> - public Dictionary<String, Instantiator> Instantiators; - /// <summary> - /// Item templates stored with their index - /// </summary> - public Dictionary<String, ItemTemplate> ItemTemplates; - public List<CrowThread> CrowThreads = new List<CrowThread>();//used to monitor thread finished + #region DragAndDrop public bool DragAndDropInProgress => DragAndDropOperation != null; public Widget DropTarget => DragAndDropOperation?.DropTarget; - public DragDropEventArgs DragAndDropOperation = null; internal Widget dragndropHover; - public ISurface DragImage = null; public Rectangle DragImageBounds, lastDragImageBounds; public bool DragImageFolowMouse;//prevent dragImg to be moved by mouse @@ -715,15 +710,6 @@ namespace Crow } #endregion - #region Private Fields - /// <summary>Client rectangle in the host context</summary> - protected Rectangle clientRectangle; - /// <summary>Clipping rectangles on the root context</summary> - IRegion clipping; - /// <summary>Main Cairo context</summary> - //Context ctx; - #endregion - #region Default values and Style loading /// Default values of properties from Widgets are retrieve from XML Attributes. /// The reflexion process used to retrieve those values being very slow, it is compiled in MSIL @@ -816,10 +802,10 @@ namespace Crow string resId = $"#{pic.Substring (path.Length + 1).Replace (Path.DirectorySeparatorChar, '.')}"; using (Stream s = new FileStream (pic, FileMode.Open, FileAccess.Read)) { if (resId.EndsWith (".svg", StringComparison.OrdinalIgnoreCase)) { - ISvgHandle hSVG = Device.LoadSvg (s); + ISvgHandle hSVG = Backend.LoadSvg (s); sharedPictures[resId] = new sharedPicture (hSVG, hSVG.Dimensions); } else { - byte[] image = Device.LoadBitmap (s, out Size dimensions); + byte[] image = Backend.LoadBitmap (s, out Size dimensions); sharedPictures[resId] = new sharedPicture (image, dimensions); } } @@ -1151,11 +1137,9 @@ namespace Crow } if (!clipping.IsEmpty || shouldDrawTextCursor) { - if (ctx == null) { - using (ctx = Device.CreateContext (surf)) - processDrawing (ctx); - }else - processDrawing (ctx); + ctx = Backend.PrepareUIFrame (ctx, clipping); + processDrawing (ctx); + Backend.FlushUIFrame (ctx); } } finally { @@ -1217,40 +1201,7 @@ namespace Crow PerformanceMeasure.End (PerformanceMeasure.Kind.Clipping); DbgLogger.EndEvent (DbgEvtType.ClippingRegistration, true); } - void clear(IContext ctx) { - for (int i = 0; i < clipping.NumRectangles; i++) - ctx.Rectangle (clipping.GetRectangle (i)); - ctx.ClipPreserve (); - ctx.Operator = Operator.Clear; - ctx.Fill (); - ctx.Operator = Operator.Over; - } - bool solidBackground = false; - public bool SolidBackground { - get => solidBackground; - set { - if (Environment.OSVersion.Platform == PlatformID.Unix) - solidBackground = value; - else - Debug.WriteLine ("SolidBackground property only available on unix."); - } - } - double zoomFactor = 1.0; - public double ZoomFactor { - get => zoomFactor; - set { - if (zoomFactor == value) - return; - zoomFactor = value; - NotifyValueChanged (zoomFactor); - lock (UpdateMutex) { - foreach (Widget g in GraphicTree) - g.RegisterForLayouting (LayoutingType.All); - registerRefreshClientRectangle (); - } - } - } /// <summary>Clipping Rectangles drive the drawing process. For compositing, each object under a clip rectangle should be /// repainted. If it contains also clip rectangles, its cache will be update, or if not cached a full redraw will take place</summary> @@ -1258,18 +1209,8 @@ namespace Crow DbgLogger.StartEvent (DbgEvtType.ProcessDrawing); PerformanceMeasure.Begin (PerformanceMeasure.Kind.Drawing); - if (!clipping.IsEmpty) { - ctx.Scale (zoomFactor,zoomFactor); - -#if VKVG - clear (ctx); -#else - ctx.PushGroup (); - - if (SolidBackground) - clear (ctx); -#endif + if (!clipping.IsEmpty) { for (int i = GraphicTree.Count -1; i >= 0 ; i--){ Widget p = GraphicTree[i]; if (!p.IsVisible) @@ -1282,7 +1223,6 @@ namespace Crow ctx.Restore (); } - if (lastDragImageBounds != DragImageBounds) { DirtyRect += lastDragImageBounds; ctx.Save (); @@ -1304,28 +1244,13 @@ namespace Crow #endif -#if VKVG - ctx.Flush(); -#else - ctx.PopGroupToSource (); - - if (!SolidBackground) - clear (ctx); - - ctx.Paint (); - - surf.Flush (); -#endif - clipping.Reset (); PerformanceMeasure.End (PerformanceMeasure.Kind.Drawing); IsDirty = true; } -#if !VKVG drawTextCursor (ctx); -#endif debugHighlightFocus (ctx); @@ -1354,7 +1279,7 @@ namespace Crow /*if (DragAndDropInProgress) { }*/ - surf.Flush (); + MainSurface.Flush (); } #region Blinking text cursor @@ -1372,7 +1297,7 @@ namespace Crow if (textCursor != null && c != textCursor.Value) RegisterClip (textCursor.Value); textCursor = c; - surf.Flush (); + MainSurface.Flush (); } else if (textCursor != null) RegisterClip (textCursor.Value); } @@ -1386,7 +1311,7 @@ namespace Crow if (blinkingCursor.ElapsedMilliseconds > TEXT_CURSOR_BLINK_FREQUENCY) { if (lab.DrawCursor (ctx, out Rectangle c)) { textCursor = c; - surf.Flush (); + MainSurface.Flush (); blinkingCursor.Restart (); } } @@ -1506,7 +1431,7 @@ namespace Crow public virtual void ProcessResize(Rectangle bounds){ lock (UpdateMutex) { clientRectangle = bounds; - surf.Resize (clientRectangle.Width, clientRectangle.Height); + MainSurface.Resize (clientRectangle.Width, clientRectangle.Height); foreach (Widget g in GraphicTree) g.RegisterForLayouting (LayoutingType.All); @@ -1514,7 +1439,7 @@ namespace Crow } } - internal void registerRefreshClientRectangle () => RegisterClip (clientRectangle); + protected void registerRefreshClientRectangle () => RegisterClip (clientRectangle); #region Mouse and Keyboard Handling MouseCursor cursor = MouseCursor.top_left_arrow; @@ -1646,7 +1571,7 @@ namespace Crow /// Ask OS to force the mouse position to the actual coordinate of Interface.MousePosition /// </summary> public virtual void ForceMousePosition () { - Glfw3.SetCursorPosition (hWin, MousePosition.X * ZoomFactor, MousePosition.Y * ZoomFactor); + Glfw3.SetCursorPosition (hWin, MousePosition.X, MousePosition.Y); } /// <summary>Processes mouse move events from the root container, this function @@ -2123,7 +2048,7 @@ namespace Crow set { throw new NotImplementedException (); } } - public Rectangle ClientRectangle => clientRectangle.Scaled (1.0/zoomFactor); + public Rectangle ClientRectangle => clientRectangle; public Rectangle GetClientRectangleForChild (ILayoutable child) => ClientRectangle; public Interface HostContainer => this; diff --git a/Crow/src/Widgets/DockWindow.cs b/Crow/src/Widgets/DockWindow.cs index 6c6c49f9..620d31f3 100644 --- a/Crow/src/Widgets/DockWindow.cs +++ b/Crow/src/Widgets/DockWindow.cs @@ -200,8 +200,8 @@ namespace Crow r.Inflate (r.Width / -3, r.Height / -3); break; } - ISurface dragImg = IFace.Device.CreateSurface (r.Width, r.Height); - using (IContext gr = IFace.Device.CreateContext (dragImg)) { + ISurface dragImg = IFace.Backend.CreateSurface (r.Width, r.Height); + using (IContext gr = IFace.Backend.CreateContext (dragImg)) { gr.LineWidth = 1; gr.Rectangle (0,0,r.Width,r.Height); gr.SetSource (0.2,0.3,0.9,0.5); diff --git a/Crow/src/Widgets/GroupBase.cs b/Crow/src/Widgets/GroupBase.cs index 1f62eb90..e99b176e 100644 --- a/Crow/src/Widgets/GroupBase.cs +++ b/Crow/src/Widgets/GroupBase.cs @@ -251,7 +251,7 @@ namespace Crow { DbgLogger.StartEvent(DbgEvtType.GOUpdateCache, this); if (!Clipping.IsEmpty) { - using (IContext gr = IFace.Device.CreateContext (bmp)) { + using (IContext gr = IFace.Backend.CreateContext (bmp)) { for (int i = 0; i < Clipping.NumRectangles; i++) gr.Rectangle(Clipping.GetRectangle(i)); gr.ClipPreserve(); diff --git a/Crow/src/Widgets/Label.cs b/Crow/src/Widgets/Label.cs index 5ef09523..330cbe55 100644 --- a/Crow/src/Widgets/Label.cs +++ b/Crow/src/Widgets/Label.cs @@ -514,7 +514,7 @@ namespace Crow int hoverLine = _multiline ? (int)Math.Min (Math.Max (0, Math.Floor (mouseLocalPos.Y / (fe.Ascent + fe.Descent))), lines.Count - 1) : 0; hoverLoc = new CharLocation (hoverLine, -1, mouseLocalPos.X); - using (IContext gr = IFace.Device.CreateContext (IFace.surf)) { + using (IContext gr = IFace.Backend.CreateContext (IFace.MainSurface)) { gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight); gr.SetFontSize (Font.Size); updateLocation (gr, ClientRectangle.Width, ref hoverLoc); @@ -650,7 +650,7 @@ namespace Crow getLines (); if (!textMeasureIsUpToDate) { - using (IContext gr = IFace.Device.CreateContext (IFace.surf)) { + using (IContext gr = IFace.Backend.CreateContext (IFace.MainSurface)) { gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight); gr.SetFontSize (Font.Size); measureTextBounds (gr); diff --git a/Crow/src/Widgets/PrivateContainer.cs b/Crow/src/Widgets/PrivateContainer.cs index 3b705ea5..f5345523 100644 --- a/Crow/src/Widgets/PrivateContainer.cs +++ b/Crow/src/Widgets/PrivateContainer.cs @@ -215,7 +215,7 @@ namespace Crow Rectangle rb = Slot + Parent.ClientRectangle.Position; if (!Clipping.IsEmpty) { - using (IContext gr = IFace.Device.CreateContext (bmp)) { + using (IContext gr = IFace.Backend.CreateContext (bmp)) { for (int i = 0; i < Clipping.NumRectangles; i++) gr.Rectangle(Clipping.GetRectangle(i)); gr.ClipPreserve(); diff --git a/Crow/src/Widgets/ScrollingStack.cs b/Crow/src/Widgets/ScrollingStack.cs index c67bf7d1..55535ded 100644 --- a/Crow/src/Widgets/ScrollingStack.cs +++ b/Crow/src/Widgets/ScrollingStack.cs @@ -136,7 +136,7 @@ namespace Crow { { DbgLogger.StartEvent(DbgEvtType.GOUpdateCache, this); if (!Clipping.IsEmpty) { - using (IContext gr = IFace.Device.CreateContext (bmp)) { + using (IContext gr = IFace.Backend.CreateContext (bmp)) { for (int i = 0; i < Clipping.NumRectangles; i++) gr.Rectangle(Clipping.GetRectangle(i)); gr.ClipPreserve(); diff --git a/Crow/src/Widgets/Shape.cs b/Crow/src/Widgets/Shape.cs index efa8b45b..8322543e 100644 --- a/Crow/src/Widgets/Shape.cs +++ b/Crow/src/Widgets/Shape.cs @@ -242,7 +242,7 @@ namespace Crow if (size != default (Size)) contentSize = size; else { - using (IContext ctx = IFace.Device.CreateContext (IFace.surf)) { + using (IContext ctx = IFace.Backend.CreateContext (IFace.MainSurface)) { using (PathParser parser = new PathParser (path)) parser.Draw (ctx, true); Rectangle r = ctx.StrokeExtents (); diff --git a/Crow/src/Widgets/Widget.cs b/Crow/src/Widgets/Widget.cs index 57b65aab..5d3e4c45 100644 --- a/Crow/src/Widgets/Widget.cs +++ b/Crow/src/Widgets/Widget.cs @@ -1168,7 +1168,7 @@ namespace Crow { DbgLogger.StartEvent (DbgEvtType.GOInitialization, this); - Clipping = IFace.Device.CreateRegion (); + Clipping = IFace.Backend.CreateRegion (); Type thisType = this.GetType (); if (!string.IsNullOrEmpty (style)) { @@ -1954,12 +1954,13 @@ namespace Crow else if (LastPaintedSlot.Width != Slot.Width || LastPaintedSlot.Height != Slot.Height) bmp.SetSize (Slot.Width, Slot.Height);*/ bmp?.Dispose (); - bmp = IFace.Device.CreateSurface (Slot.Width, Slot.Height); + //bmp = IFace.Device.CreateSurface (Slot.Width, Slot.Height); + bmp = IFace.Backend.CreateSurface (Slot.Width, Slot.Height); //bmp = new ImageSurface(Format.Argb32, Slot.Width, Slot.Height); DbgLogger.StartEvent (DbgEvtType.GOCreateContext, this); - using (IContext gr = IFace.Device.CreateContext (bmp)) { + using (IContext gr = IFace.Backend.CreateContext (bmp)) { DbgLogger.EndEvent (DbgEvtType.GOCreateContext); onDraw (gr); } diff --git a/Drawing2D/src/Enums.cs b/Drawing2D/src/Enums.cs index 12a6f035..bf20a432 100644 --- a/Drawing2D/src/Enums.cs +++ b/Drawing2D/src/Enums.cs @@ -6,6 +6,12 @@ using System; namespace Drawing2D { + public enum BackendType { + Native, + OpenGL, + EGL, + Vulkan + } public enum Status { Success = 0, diff --git a/Drawing2D/src/IBackend.cs b/Drawing2D/src/IBackend.cs new file mode 100644 index 00000000..811b95d5 --- /dev/null +++ b/Drawing2D/src/IBackend.cs @@ -0,0 +1,37 @@ +// Copyright (c) 2018-2021 Jean-Philippe Bruyère <jp_bruyere@hotmail.com> +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; +using System.IO; + +namespace Drawing2D +{ + public interface IBackend: IDisposable + { + ISurface CreateSurface (int width, int height); + ISurface CreateSurface (byte[] data, int width, int height); + //ISurface CreateMainSurface (IntPtr glfwWinHandle, int width, int height); + ISurface MainSurface { get; } + IRegion CreateRegion (); + IContext CreateContext (ISurface surf); + //IPattern CreatePattern (PatternType patternType); + IGradient CreateGradient (GradientType gradientType, Rectangle bounds); + byte[] LoadBitmap (Stream stream, out Size dimensions); + ISvgHandle LoadSvg (Stream stream); + ISvgHandle LoadSvg (string svgFragment); + IContext PrepareUIFrame (IContext existingContext, IRegion clipping); + void FlushUIFrame (IContext ctx); + /*IRegion CreateRegion (); + ISurface CreateSurface (int width, int height); + ISurface CreateSurface (byte[] data, int width, int height); + ISurface CreateSurface (IntPtr glfwWinHandle, int width, int height); + IContext CreateContext (ISurface surf); + //IPattern CreatePattern (PatternType patternType); + IGradient CreateGradient (GradientType gradientType, Rectangle bounds); + byte[] LoadBitmap (Stream stream, out Size dimensions); + ISvgHandle LoadSvg (Stream stream); + ISvgHandle LoadSvg (string svgFragment);*/ + } +} + diff --git a/Drawing2D/src/IDevice.cs b/Drawing2D/src/IDevice.cs index 891f8b2e..14846271 100644 --- a/Drawing2D/src/IDevice.cs +++ b/Drawing2D/src/IDevice.cs @@ -13,16 +13,6 @@ namespace Drawing2D void GetDpy (out int hdpy, out int vdpy); void SetDpy (int hdpy, int vdpy); - IRegion CreateRegion (); - ISurface CreateSurface (int width, int height); - ISurface CreateSurface (byte[] data, int width, int height); - ISurface CreateSurface (IntPtr glfwWinHandle, int width, int height); - IContext CreateContext (ISurface surf); - //IPattern CreatePattern (PatternType patternType); - IGradient CreateGradient (GradientType gradientType, Rectangle bounds); - byte[] LoadBitmap (Stream stream, out Size dimensions); - ISvgHandle LoadSvg (Stream stream); - ISvgHandle LoadSvg (string svgFragment); } } diff --git a/Samples/HelloWorld/main.cs b/Samples/HelloWorld/main.cs index e60b9782..e84607b9 100644 --- a/Samples/HelloWorld/main.cs +++ b/Samples/HelloWorld/main.cs @@ -1,13 +1,15 @@ using System; using Crow; +using Glfw; using Samples; namespace HelloWorld { - class Program { + class Program : Interface { + Program() : base (800, 600, true, true) {} static void Main (string[] args) { - using (Interface app = new Interface ()) { - app.Initialized += (sender, e) => app.LoadIMLFragment (@"<Label Text='Hello World'/>"); + using (Program app = new Program ()) { + app.Initialized += (sender, e) => app.LoadIMLFragment (@"<Window Caption='Hello World'/>"); app.Run (); } } diff --git a/Samples/PerfTests/Program.cs b/Samples/PerfTests/Program.cs index d7f6a9be..cbe62738 100644 --- a/Samples/PerfTests/Program.cs +++ b/Samples/PerfTests/Program.cs @@ -64,7 +64,7 @@ namespace PerfTests } public TestInterface (string[] args, int width = 800, int height = 600) - : base (width, height, false, false) + : base (width, height, true, false) { string outDir = null; @@ -141,15 +141,10 @@ namespace PerfTests writer.WriteLine ("Path;MinEllapsed;MaxEllapsed;MeanEllapsed;MedianEllapsed;sigmaEllapsed;MinMem;MaxMem;MeanMem;MedianMem;sigmaMem;MinAlloc;MaxAlloc;MeanAlloc;MedianAlloc;sigmaAlloc"); } - if (screenOutput) { - initBackend (); + if (screenOutput) initSurface (); - } else { - SolidBackground = false; - initBackend (true); - - CreateMainSurface (ref clientRectangle); - } + else + initBackend (); initDictionaries (); loadStyling (); @@ -163,6 +158,15 @@ namespace PerfTests outStream.Dispose (); } } + protected override void initBackend() + { + if (screenOutput) + backend = new Crow.CairoBackend.ImageBackend (WindowHandle, clientRectangle.Width, clientRectangle.Height); + else + backend = new Crow.CairoBackend.ImageBackend (clientRectangle.Width, clientRectangle.Height); + + clipping = Backend.CreateRegion (); + } struct Measures { diff --git a/Samples/common/src/SampleBase.cs b/Samples/common/src/SampleBase.cs index 2d773af4..2e7440a1 100644 --- a/Samples/common/src/SampleBase.cs +++ b/Samples/common/src/SampleBase.cs @@ -20,11 +20,6 @@ namespace Samples { public SampleBase(IntPtr hWin) : base(800, 600, hWin) { } public SampleBase() : base(800, 600, true, true) { } - public SampleBase(int width, int height, bool startUIThread, bool createSurface) : - base(width, height, startUIThread, createSurface) - { - - } public Version CrowVersion => Assembly.GetAssembly(typeof(Widget)).GetName().Version; |