packages feed

brillo 1.13.3 → 2.0.0

raw patch · 39 files changed

+10209/−815 lines, 39 filesdep +textdep −bmpdep −bytestringdep −ghc-primdep ~brillo-renderingdep ~containersPVP ok

version bump matches the API change (PVP)

Dependencies added: text

Dependencies removed: bmp, bytestring, ghc-prim

Dependency ranges changed: brillo-rendering, containers

API changes (from Hackage documentation)

- Brillo.Data.Bitmap: bitmapSize :: BitmapData -> (Int, Int)
- Brillo.Data.Picture: arc :: Float -> Float -> Float -> Picture
- Brillo.Data.Picture: bitmap :: BitmapData -> Picture
- Brillo.Data.Picture: bitmapSection :: Rectangle -> BitmapData -> Picture
- Brillo.Data.Picture: blank :: Picture
- Brillo.Data.Picture: circle :: Float -> Picture
- Brillo.Data.Picture: color :: Color -> Picture -> Picture
- Brillo.Data.Picture: line :: Path -> Picture
- Brillo.Data.Picture: pictures :: [Picture] -> Picture
- Brillo.Data.Picture: polygon :: Path -> Picture
- Brillo.Data.Picture: rotate :: Float -> Picture -> Picture
- Brillo.Data.Picture: scale :: Float -> Float -> Picture -> Picture
- Brillo.Data.Picture: text :: String -> Picture
- Brillo.Data.Picture: thickArc :: Float -> Float -> Float -> Float -> Picture
- Brillo.Data.Picture: thickCircle :: Float -> Float -> Picture
- Brillo.Data.Picture: translate :: Float -> Float -> Picture -> Picture
+ Brillo.Data.Bitmap: BitmapData :: Int -> BitmapFormat -> (Int, Int) -> Bool -> ForeignPtr Word8 -> BitmapData
+ Brillo.Data.Bitmap: [bitmapCacheMe] :: BitmapData -> Bool
+ Brillo.Data.Bitmap: [bitmapDataLength] :: BitmapData -> Int
+ Brillo.Data.Bitmap: [bitmapFormat] :: BitmapData -> BitmapFormat
+ Brillo.Data.Bitmap: [bitmapPointer] :: BitmapData -> ForeignPtr Word8
+ Brillo.Data.Bitmap: [bitmapSize] :: BitmapData -> (Int, Int)
+ Brillo.Data.Controller: [controllerSetCursor] :: Controller -> CursorShape -> IO ()
+ Brillo.Data.Cursor: CursorArrow :: CursorShape
+ Brillo.Data.Cursor: CursorCrosshair :: CursorShape
+ Brillo.Data.Cursor: CursorHand :: CursorShape
+ Brillo.Data.Cursor: CursorHidden :: CursorShape
+ Brillo.Data.Cursor: CursorIBeam :: CursorShape
+ Brillo.Data.Cursor: CursorResizeH :: CursorShape
+ Brillo.Data.Cursor: CursorResizeV :: CursorShape
+ Brillo.Data.Cursor: data CursorShape
+ Brillo.Data.Cursor: instance GHC.Classes.Eq Brillo.Data.Cursor.CursorShape
+ Brillo.Data.Cursor: instance GHC.Classes.Ord Brillo.Data.Cursor.CursorShape
+ Brillo.Data.Cursor: instance GHC.Enum.Bounded Brillo.Data.Cursor.CursorShape
+ Brillo.Data.Cursor: instance GHC.Enum.Enum Brillo.Data.Cursor.CursorShape
+ Brillo.Data.Cursor: instance GHC.Show.Show Brillo.Data.Cursor.CursorShape
+ Brillo.Data.FileDialog: FileDialog :: !Text -> !Text -> ![Text] -> !Text -> !SelectionMode -> FileDialog
+ Brillo.Data.FileDialog: MultiFileSelect :: SelectionMode
+ Brillo.Data.FileDialog: SingleDirectorySelect :: SelectionMode
+ Brillo.Data.FileDialog: SingleFileSelect :: SelectionMode
+ Brillo.Data.FileDialog: [defaultPath] :: FileDialog -> !Text
+ Brillo.Data.FileDialog: [filterDescription] :: FileDialog -> !Text
+ Brillo.Data.FileDialog: [filterPatterns] :: FileDialog -> ![Text]
+ Brillo.Data.FileDialog: [selectionMode] :: FileDialog -> !SelectionMode
+ Brillo.Data.FileDialog: [title] :: FileDialog -> !Text
+ Brillo.Data.FileDialog: data FileDialog
+ Brillo.Data.FileDialog: data SelectionMode
+ Brillo.Data.FileDialog: instance GHC.Classes.Eq Brillo.Data.FileDialog.FileDialog
+ Brillo.Data.FileDialog: instance GHC.Classes.Eq Brillo.Data.FileDialog.SelectionMode
+ Brillo.Data.FileDialog: instance GHC.Show.Show Brillo.Data.FileDialog.FileDialog
+ Brillo.Data.FileDialog: instance GHC.Show.Show Brillo.Data.FileDialog.SelectionMode
+ Brillo.Data.Picture: ArcAliased :: Float -> Float -> Float -> Picture
+ Brillo.Data.Picture: CircleAliased :: Float -> Picture
+ Brillo.Data.Picture: LineAliased :: Path -> Picture
+ Brillo.Data.Picture: PolygonAliased :: Path -> Picture
+ Brillo.Data.Picture: TextAliased :: Text -> Picture
+ Brillo.Data.Picture: ThickArcAliased :: Float -> Float -> Float -> Float -> Picture
+ Brillo.Data.Picture: ThickCircleAliased :: Float -> Float -> Picture
+ Brillo.Data.Picture: ThickLine :: Path -> Float -> Picture
+ Brillo.Data.Picture: ThickLineAliased :: Path -> Float -> Picture
+ Brillo.Data.Picture: ThickText :: Text -> Float -> Picture
+ Brillo.Data.Picture: ThickTextAliased :: Text -> Float -> Picture
+ Brillo.Data.Picture: TrueTypeText :: FilePath -> Int -> Text -> Picture
+ Brillo.Data.Picture: arcSolidAliased :: Float -> Float -> Float -> Picture
+ Brillo.Data.Picture: circleSolidAliased :: Float -> Picture
+ Brillo.Data.Picture: lineLoopAliased :: Path -> Picture
+ Brillo.Data.Picture: rectangleSolidAliased :: Float -> Float -> Picture
+ Brillo.Data.Picture: rectangleUpperSolidAliased :: Float -> Float -> Picture
+ Brillo.Data.Picture: rectangleUpperWireAliased :: Float -> Float -> Picture
+ Brillo.Data.Picture: rectangleWireAliased :: Float -> Float -> Picture
+ Brillo.Data.Picture: sectorWireAliased :: Float -> Float -> Float -> Picture
+ Brillo.Interface.Environment: openFileDialog :: FileDialog -> IO (Maybe [FilePath])
+ Brillo.Interface.Environment: saveFileDialog :: Text -> Text -> [Text] -> Text -> IO (Maybe FilePath)
+ Brillo.Interface.IO.Animate: [controllerSetCursor] :: Controller -> CursorShape -> IO ()
+ Brillo.Interface.IO.Display: CursorArrow :: CursorShape
+ Brillo.Interface.IO.Display: CursorCrosshair :: CursorShape
+ Brillo.Interface.IO.Display: CursorHand :: CursorShape
+ Brillo.Interface.IO.Display: CursorHidden :: CursorShape
+ Brillo.Interface.IO.Display: CursorIBeam :: CursorShape
+ Brillo.Interface.IO.Display: CursorResizeH :: CursorShape
+ Brillo.Interface.IO.Display: CursorResizeV :: CursorShape
+ Brillo.Interface.IO.Display: [controllerSetCursor] :: Controller -> CursorShape -> IO ()
+ Brillo.Interface.IO.Display: data CursorShape
+ Brillo.Interface.IO.Game: EventDrop :: [FilePath] -> Event
+ Brillo.Interface.IO.Game: EventPick :: [FilePath] -> Event
+ Brillo.Interface.IO.Interact: CursorArrow :: CursorShape
+ Brillo.Interface.IO.Interact: CursorCrosshair :: CursorShape
+ Brillo.Interface.IO.Interact: CursorHand :: CursorShape
+ Brillo.Interface.IO.Interact: CursorHidden :: CursorShape
+ Brillo.Interface.IO.Interact: CursorIBeam :: CursorShape
+ Brillo.Interface.IO.Interact: CursorResizeH :: CursorShape
+ Brillo.Interface.IO.Interact: CursorResizeV :: CursorShape
+ Brillo.Interface.IO.Interact: EventDrop :: [FilePath] -> Event
+ Brillo.Interface.IO.Interact: EventPick :: [FilePath] -> Event
+ Brillo.Interface.IO.Interact: [controllerSetCursor] :: Controller -> CursorShape -> IO ()
+ Brillo.Interface.IO.Interact: data CursorShape
+ Brillo.Interface.Pure.Game: EventDrop :: [FilePath] -> Event
+ Brillo.Interface.Pure.Game: EventPick :: [FilePath] -> Event
- Brillo: InWindow :: String -> (Int, Int) -> (Int, Int) -> Display
+ Brillo: InWindow :: Text -> (Int, Int) -> (Int, Int) -> Display
- Brillo.Data.Bitmap: data () => BitmapData
+ Brillo.Data.Bitmap: data BitmapData
- Brillo.Data.Bitmap: data () => BitmapFormat
+ Brillo.Data.Bitmap: data BitmapFormat
- Brillo.Data.Bitmap: data () => PixelFormat
+ Brillo.Data.Bitmap: data PixelFormat
- Brillo.Data.Bitmap: data () => Rectangle
+ Brillo.Data.Bitmap: data Rectangle
- Brillo.Data.Bitmap: data () => RowOrder
+ Brillo.Data.Bitmap: data RowOrder
- Brillo.Data.Color: data () => Color
+ Brillo.Data.Color: data Color
- Brillo.Data.Controller: Controller :: IO () -> ((ViewPort -> IO ViewPort) -> IO ()) -> Controller
+ Brillo.Data.Controller: Controller :: IO () -> ((ViewPort -> IO ViewPort) -> IO ()) -> (CursorShape -> IO ()) -> Controller
- Brillo.Data.Display: InWindow :: String -> (Int, Int) -> (Int, Int) -> Display
+ Brillo.Data.Display: InWindow :: Text -> (Int, Int) -> (Int, Int) -> Display
- Brillo.Data.Picture: Text :: String -> Picture
+ Brillo.Data.Picture: Text :: Text -> Picture
- Brillo.Data.Picture: data () => Picture
+ Brillo.Data.Picture: data Picture
- Brillo.Interface.IO.Animate: Controller :: IO () -> ((ViewPort -> IO ViewPort) -> IO ()) -> Controller
+ Brillo.Interface.IO.Animate: Controller :: IO () -> ((ViewPort -> IO ViewPort) -> IO ()) -> (CursorShape -> IO ()) -> Controller
- Brillo.Interface.IO.Display: Controller :: IO () -> ((ViewPort -> IO ViewPort) -> IO ()) -> Controller
+ Brillo.Interface.IO.Display: Controller :: IO () -> ((ViewPort -> IO ViewPort) -> IO ()) -> (CursorShape -> IO ()) -> Controller
- Brillo.Interface.IO.Game: playIO :: forall world. Display -> Color -> Int -> world -> (world -> IO Picture) -> (Event -> world -> IO world) -> (Float -> world -> IO world) -> IO ()
+ Brillo.Interface.IO.Game: playIO :: Display -> Color -> Int -> world -> (world -> IO Picture) -> (Event -> world -> IO world) -> (Float -> world -> IO world) -> IO ()
- Brillo.Interface.IO.Interact: Controller :: IO () -> ((ViewPort -> IO ViewPort) -> IO ()) -> Controller
+ Brillo.Interface.IO.Interact: Controller :: IO () -> ((ViewPort -> IO ViewPort) -> IO ()) -> (CursorShape -> IO ()) -> Controller
- Brillo.Interface.IO.Simulate: simulateIO :: forall model. Display -> Color -> Int -> model -> (model -> IO Picture) -> (ViewPort -> Float -> model -> IO model) -> IO ()
+ Brillo.Interface.IO.Simulate: simulateIO :: Display -> Color -> Int -> model -> (model -> IO Picture) -> (ViewPort -> Float -> model -> IO model) -> IO ()

Files

Brillo/Data/Bitmap.hs view
@@ -1,8 +1,7 @@ -- | Functions to load bitmap data from various places. module Brillo.Data.Bitmap (   Rectangle (..),-  BitmapData,-  bitmapSize,+  BitmapData (..),   BitmapFormat (..),   RowOrder (..),   PixelFormat (..),
Brillo/Data/Color.hs view
@@ -49,17 +49,17 @@ -- Color functions ------------------------------------------------------------  -- | Mix two colors with the given ratios.-mixColors-  :: Float-  -- ^ Proportion of first color.-  -> Float-  -- ^ Proportion of second color.-  -> Color-  -- ^ First color.-  -> Color-  -- ^ Second color.-  -> Color-  -- ^ Resulting color.+mixColors ::+  -- | Proportion of first color.+  Float ->+  -- | Proportion of second color.+  Float ->+  -- | First color.+  Color ->+  -- | Second color.+  Color ->+  -- | Resulting color.+  Color mixColors m1 m2 c1 c2 =   let (r1, g1, b1, a1) = rgbaOfColor c1       (r2, g2, b2, a2) = rgbaOfColor c2
Brillo/Data/Controller.hs view
@@ -1,6 +1,7 @@ module Brillo.Data.Controller (Controller (..)) where +import Brillo.Data.Cursor (CursorShape) import Brillo.Data.ViewPort  @@ -11,4 +12,6 @@   -- ^ Indicate that we want the picture to be redrawn.   , controllerModifyViewPort :: (ViewPort -> IO ViewPort) -> IO ()   -- ^ Modify the current viewport, also indicating that it should be redrawn.+  , controllerSetCursor :: CursorShape -> IO ()+  -- ^ Set the mouse cursor shape.   }
+ Brillo/Data/Cursor.hs view
@@ -0,0 +1,21 @@+module Brillo.Data.Cursor (CursorShape (..))+where+++-- | Describes the shape of the mouse cursor.+data CursorShape+  = -- | Standard arrow cursor (default).+    CursorArrow+  | -- | Text input I-beam cursor.+    CursorIBeam+  | -- | Crosshair cursor.+    CursorCrosshair+  | -- | Pointing hand cursor (for clickable elements).+    CursorHand+  | -- | Horizontal resize cursor.+    CursorResizeH+  | -- | Vertical resize cursor.+    CursorResizeV+  | -- | Hidden cursor (invisible).+    CursorHidden+  deriving (Show, Eq, Ord, Bounded, Enum)
Brillo/Data/Display.hs view
@@ -1,11 +1,13 @@ module Brillo.Data.Display (Display (..)) where +import Data.Text (Text) + -- | Describes how Brillo should display its output. data Display   = -- | Display in a window with the given name, size and position.-    InWindow String (Int, Int) (Int, Int)+    InWindow Text (Int, Int) (Int, Int)   | -- | Display full screen.     FullScreen   deriving (Eq, Read, Show)
+ Brillo/Data/FileDialog.hs view
@@ -0,0 +1,33 @@+module Brillo.Data.FileDialog (+  FileDialog (..),+  SelectionMode (..),+)+where++import Data.Text (Text)+++data SelectionMode+  = SingleFileSelect+  | MultiFileSelect+  | SingleDirectorySelect+  deriving (Show, Eq)+++{-| The 'FileDialog' represents a dialog+| that can be opened by the user to select files or directories.+-}+data FileDialog+  = FileDialog+  { title :: !Text+  -- ^ Title of the dialog+  , defaultPath :: !Text+  -- ^ Default path to open the dialog at+  , filterPatterns :: ![Text]+  -- ^ Filter patterns like `["*.jpg", "*.png"]`+  , filterDescription :: !Text+  -- ^ Filter description like `"text files"`+  , selectionMode :: !SelectionMode+  -- ^ Single or multiple selections+  }+  deriving (Show, Eq)
Brillo/Data/Picture.hs view
@@ -4,35 +4,25 @@   Vector,   Path, -  -- * Aliases for Picture constructors-  blank,-  polygon,-  line,-  circle,-  thickCircle,-  arc,-  thickArc,-  text,-  bitmap,-  bitmapSection,-  -- , bitmap-  color,-  translate,-  rotate,-  scale,-  pictures,-   -- * Compound shapes   lineLoop,+  lineLoopAliased,   circleSolid,+  circleSolidAliased,   arcSolid,+  arcSolidAliased,   sectorWire,+  sectorWireAliased,   rectanglePath,   rectangleWire,+  rectangleWireAliased,   rectangleSolid,+  rectangleSolidAliased,   rectangleUpperPath,   rectangleUpperWire,+  rectangleUpperWireAliased,   rectangleUpperSolid,+  rectangleUpperSolidAliased, ) where @@ -40,117 +30,47 @@ import Brillo.Rendering  --- Constructors ------------------------------------------------------------------- NOTE: The docs here should be identical to the ones on the constructors.---- | A blank picture, with nothing in it.-blank :: Picture-blank = Blank----- | A convex polygon filled with a solid color.-polygon :: Path -> Picture-polygon = Polygon----- | A line along an arbitrary path.-line :: Path -> Picture-line = Line----- | A circle with the given radius.-circle :: Float -> Picture-circle = Circle---{-| A circle with the given thickness and radius.-  If the thickness is 0 then this is equivalent to `Circle`.--}-thickCircle :: Float -> Float -> Picture-thickCircle = ThickCircle---{-| A circular arc drawn counter-clockwise between two angles (in degrees)-  at the given radius.--}-arc :: Float -> Float -> Float -> Picture-arc = Arc---{-| A circular arc drawn counter-clockwise between two angles (in degrees),-  with the given radius  and thickness.-  If the thickness is 0 then this is equivalent to `Arc`.--}-thickArc :: Float -> Float -> Float -> Float -> Picture-thickArc = ThickArc----- | Some text to draw with a vector font.-text :: String -> Picture-text = Text----- | A bitmap image-bitmap :: BitmapData -> Picture-bitmap bitmapData = Bitmap bitmapData---{-| a subsection of a bitmap image-  first argument selects a sub section in the bitmap-  second argument determines the bitmap data--}-bitmapSection :: Rectangle -> BitmapData -> Picture-bitmapSection = BitmapSection----- | A picture drawn with this color.-color :: Color -> Picture -> Picture-color = Color----- | A picture translated by the given x and y coordinates.-translate :: Float -> Float -> Picture -> Picture-translate = Translate----- | A picture rotated clockwise by the given angle (in degrees).-rotate :: Float -> Picture -> Picture-rotate = Rotate----- | A picture scaled by the given x and y factors.-scale :: Float -> Float -> Picture -> Picture-scale = Scale----- | A picture consisting of several others.-pictures :: [Picture] -> Picture-pictures = Pictures----- Other Shapes ---------------------------------------------------------------+-- Shapes --------------------------------------------------------------------- --- | A closed loop along a path.+-- | A closed loop along a path, drawn with anti-aliasing. lineLoop :: Path -> Picture lineLoop [] = Line [] lineLoop (x : xs) = Line ((x : xs) ++ [x])  +-- | A closed loop along a path, drawn without anti-aliasing.+lineLoopAliased :: Path -> Picture+lineLoopAliased [] = LineAliased []+lineLoopAliased (x : xs) = LineAliased ((x : xs) ++ [x])++ -- Circles and Arcs ----------------------------------------------------------- --- | A solid circle with the given radius.+-- | A solid circle with the given radius, drawn with anti-aliasing. circleSolid :: Float -> Picture circleSolid r =-  thickCircle (r / 2) r+  ThickCircle (r / 2) r  --- | A solid arc, drawn counter-clockwise between two angles (in degrees) at the given radius.+-- | A solid circle with the given radius, drawn without anti-aliasing.+circleSolidAliased :: Float -> Picture+circleSolidAliased r =+  ThickCircleAliased (r / 2) r+++-- | A solid arc, drawn counter-clockwise between two angles (in degrees) at the given radius, with anti-aliasing. arcSolid :: Float -> Float -> Float -> Picture arcSolid a1 a2 r =-  thickArc a1 a2 (r / 2) r+  ThickArc a1 a2 (r / 2) r  -{-| A wireframe sector of a circle.+-- | A solid arc, drawn counter-clockwise between two angles (in degrees) at the given radius, without anti-aliasing.+arcSolidAliased :: Float -> Float -> Float -> Picture+arcSolidAliased a1 a2 r =+  ThickArcAliased a1 a2 (r / 2) r+++{-| A wireframe sector of a circle, drawn with anti-aliasing.   An arc is draw counter-clockwise from the first to the second angle (in degrees) at   the given radius. Lines are drawn from the origin to the ends of the arc. -}@@ -171,35 +91,61 @@         ]  +{-| A wireframe sector of a circle, drawn without anti-aliasing.+  An arc is draw counter-clockwise from the first to the second angle (in degrees) at+  the given radius. Lines are drawn from the origin to the ends of the arc.+-}+sectorWireAliased :: Float -> Float -> Float -> Picture+sectorWireAliased a1 a2 r_ =+  let r = abs r_+  in  Pictures+        [ ArcAliased a1 a2 r+        , LineAliased [(0, 0), (r * cos (degToRad a1), r * sin (degToRad a1))]+        , LineAliased [(0, 0), (r * cos (degToRad a2), r * sin (degToRad a2))]+        ]++ -- Rectangles ----------------------------------------------------------------- -- NOTE: Only the first of these rectangle functions has haddocks on the --       arguments to reduce the amount of noise in the extracted docs.  -- | A path representing a rectangle centered about the origin-rectanglePath-  :: Float-  -- ^ width of rectangle-  -> Float-  -- ^ height of rectangle-  -> Path+rectanglePath ::+  -- | width of rectangle+  Float ->+  -- | height of rectangle+  Float ->+  Path rectanglePath sizeX sizeY =   let sx = sizeX / 2       sy = sizeY / 2   in  [(-sx, -sy), (-sx, sy), (sx, sy), (sx, -sy)]  --- | A wireframe rectangle centered about the origin.+-- | A wireframe rectangle centered about the origin, drawn with anti-aliasing. rectangleWire :: Float -> Float -> Picture rectangleWire sizeX sizeY =   lineLoop $ rectanglePath sizeX sizeY  --- | A wireframe rectangle in the y > 0 half of the x-y plane.+-- | A wireframe rectangle centered about the origin, drawn without anti-aliasing.+rectangleWireAliased :: Float -> Float -> Picture+rectangleWireAliased sizeX sizeY =+  lineLoopAliased $ rectanglePath sizeX sizeY+++-- | A wireframe rectangle in the y > 0 half of the x-y plane, drawn with anti-aliasing. rectangleUpperWire :: Float -> Float -> Picture rectangleUpperWire sizeX sizeY =   lineLoop $ rectangleUpperPath sizeX sizeY  +-- | A wireframe rectangle in the y > 0 half of the x-y plane, drawn without anti-aliasing.+rectangleUpperWireAliased :: Float -> Float -> Picture+rectangleUpperWireAliased sizeX sizeY =+  lineLoopAliased $ rectangleUpperPath sizeX sizeY++ -- | A path representing a rectangle in the y > 0 half of the x-y plane. rectangleUpperPath :: Float -> Float -> Path rectangleUpperPath sizeX sy =@@ -207,13 +153,25 @@   in  [(-sx, 0), (-sx, sy), (sx, sy), (sx, 0)]  --- | A solid rectangle centered about the origin.+-- | A solid rectangle centered about the origin, drawn with anti-aliased edges. rectangleSolid :: Float -> Float -> Picture rectangleSolid sizeX sizeY =   Polygon $ rectanglePath sizeX sizeY  --- | A solid rectangle in the y > 0 half of the x-y plane.+-- | A solid rectangle centered about the origin, drawn without anti-aliasing.+rectangleSolidAliased :: Float -> Float -> Picture+rectangleSolidAliased sizeX sizeY =+  PolygonAliased $ rectanglePath sizeX sizeY+++-- | A solid rectangle in the y > 0 half of the x-y plane, drawn with anti-aliased edges. rectangleUpperSolid :: Float -> Float -> Picture rectangleUpperSolid sizeX sizeY =   Polygon $ rectangleUpperPath sizeX sizeY+++-- | A solid rectangle in the y > 0 half of the x-y plane, drawn without anti-aliasing.+rectangleUpperSolidAliased :: Float -> Float -> Picture+rectangleUpperSolidAliased sizeX sizeY =+  PolygonAliased $ rectangleUpperPath sizeX sizeY
Brillo/Data/Point.hs view
@@ -20,11 +20,11 @@       +-------+ P1 @ -}-pointInBox-  :: Point-  -> Point-  -> Point-  -> Bool+pointInBox ::+  Point ->+  Point ->+  Point ->+  Bool pointInBox (x0, y0) (x1, y1) (x2, y2) =   x0 >= min x1 x2     && x0 <= max x1 x2
Brillo/Data/ViewState.hs view
@@ -136,12 +136,12 @@   -- | Check if the provided key combination is some brillo viewport command.-isCommand-  :: Map Command [(Key, Maybe Modifiers)]-  -> Command-  -> Key-  -> Modifiers-  -> Bool+isCommand ::+  Map Command [(Key, Maybe Modifiers)] ->+  Command ->+  Key ->+  Modifiers ->+  Bool isCommand commands c key keyMods   | Just csMatch <- Map.lookup c commands =       or $ map (isCommand2 c key keyMods) csMatch@@ -170,10 +170,11 @@ data ViewState   = ViewState   { viewStateCommands :: !(Map Command [(Key, Maybe Modifiers)])-  -- ^ The command list for the viewport controller.-  --      These can be safely overwridden at any time by deleting-  --      or adding entries to the list.-  --      Entries at the front of the list take precedence.+  {- ^ The command list for the viewport controller.+     These can be safely overwridden at any time by deleting+     or adding entries to the list.+     Entries at the front of the list take precedence.+  -}   , viewStateScaleStep :: !Float   -- ^ How much to scale the world by for each step of the mouse wheel.   , viewStateRotateFactor :: !Float@@ -181,14 +182,17 @@   , viewStateScaleFactor :: !Float   -- ^ Ratio to scale the world by for each pixel of y motion.   , viewStateTranslateMark :: !(Maybe (Float, Float))-  -- ^ During viewport translation,-  --      where the mouse was clicked on the window to start the translate.+  {- ^ During viewport translation,+     where the mouse was clicked on the window to start the translate.+  -}   , viewStateRotateMark :: !(Maybe (Float, Float))-  -- ^ During viewport rotation,-  --      where the mouse was clicked on the window to starte the rotate.+  {- ^ During viewport rotation,+     where the mouse was clicked on the window to starte the rotate.+  -}   , viewStateScaleMark :: !(Maybe (Float, Float))-  -- ^ During viewport scale,-  --      where the mouse was clicked on the window to start the scale.+  {- ^ During viewport scale,+     where the mouse was clicked on the window to start the scale.+  -}   , viewStateViewPort :: ViewPort   -- ^ The current viewport.   }@@ -301,8 +305,9 @@   motionScale (viewStateScaleMark viewState) pos viewState     `mplus` motionTranslate (viewStateTranslateMark viewState) pos viewState     `mplus` motionRotate (viewStateRotateMark viewState) pos viewState-updateViewStateWithEventMaybe (EventResize _) _ =-  Nothing+updateViewStateWithEventMaybe (EventDrop _) _ = Nothing+updateViewStateWithEventMaybe (EventPick _) _ = Nothing+updateViewStateWithEventMaybe (EventResize _) _ = Nothing   -- | Zoom in a `ViewState` by the scale step.@@ -347,11 +352,11 @@   -- | Apply a translation to the `ViewState`.-motionTranslate-  :: Maybe (Float, Float) -- Location of first mark.-  -> (Float, Float) -- Current position.-  -> ViewState-  -> Maybe ViewState+motionTranslate ::+  Maybe (Float, Float) -> -- Location of first mark.+  (Float, Float) -> -- Current position.+  ViewState ->+  Maybe ViewState motionTranslate Nothing _ _ = Nothing motionTranslate (Just (markX, markY)) (posX, posY) viewState =   Just $@@ -371,11 +376,11 @@   -- | Apply a rotation to the `ViewState`.-motionRotate-  :: Maybe (Float, Float) -- Location of first mark.-  -> (Float, Float) -- Current position.-  -> ViewState-  -> Maybe ViewState+motionRotate ::+  Maybe (Float, Float) -> -- Location of first mark.+  (Float, Float) -> -- Current position.+  ViewState ->+  Maybe ViewState motionRotate Nothing _ _ = Nothing motionRotate (Just (markX, _markY)) (posX, posY) viewState =   Just $@@ -391,11 +396,11 @@   -- | Apply a scale to the `ViewState`.-motionScale-  :: Maybe (Float, Float) -- Location of first mark.-  -> (Float, Float) -- Current position.-  -> ViewState-  -> Maybe ViewState+motionScale ::+  Maybe (Float, Float) -> -- Location of first mark.+  (Float, Float) -> -- Current position.+  ViewState ->+  Maybe ViewState motionScale Nothing _ _ = Nothing motionScale (Just (_markX, markY)) (posX, posY) viewState =   Just $
+ Brillo/Geometry.hs view
@@ -0,0 +1,9 @@+module Brillo.Geometry (+  module Brillo.Geometry.Angle,+  module Brillo.Geometry.Line,+)+where++import Brillo.Geometry.Angle+import Brillo.Geometry.Line+
Brillo/Geometry/Line.hs view
@@ -33,16 +33,16 @@   -- | Check if line segment (P1-P2) clears a box (P3-P4) by being well outside it.-segClearsBox-  :: Point-  -- ^ P1 First point of segment.-  -> Point-  -- ^ P2 Second point of segment.-  -> Point-  -- ^ P3 Lower left point of box.-  -> Point-  -- ^ P4 Upper right point of box.-  -> Bool+segClearsBox ::+  -- | P1 First point of segment.+  Point ->+  -- | P2 Second point of segment.+  Point ->+  -- | P3 Lower left point of box.+  Point ->+  -- | P4 Upper right point of box.+  Point ->+  Bool segClearsBox (x1, y1) (x2, y2) (xa, ya) (xb, yb)   | x1 < xa, x2 < xa = True   | x1 > xb, x2 > xb = True@@ -54,15 +54,15 @@ {-| Given an infinite line which intersects `P1` and `P1`,      return the point on that line that is closest to `P3` -}-closestPointOnLine-  :: Point-  -- ^ `P1`-  -> Point-  -- ^ `P2`-  -> Point-  -- ^ `P3`-  -> Point-  -- ^ the point on the line P1-P2 that is closest to `P3`+closestPointOnLine ::+  -- | `P1`+  Point ->+  -- | `P2`+  Point ->+  -- | `P3`+  Point ->+  -- | the point on the line P1-P2 that is closest to `P3`+  Point {-# INLINE closestPointOnLine #-} closestPointOnLine p1 p2 p3 =   p1 Pt.+ (u `mulSV` (p2 Pt.- p1))@@ -92,14 +92,14 @@ @ -} {-# INLINE closestPointOnLineParam #-}-closestPointOnLineParam-  :: Point-  -- ^ `P1`-  -> Point-  -- ^ `P2`-  -> Point-  -- ^ `P3`-  -> Float+closestPointOnLineParam ::+  -- | `P1`+  Point ->+  -- | `P2`+  Point ->+  -- | `P3`+  Point ->+  Float closestPointOnLineParam p1 p2 p3 =   (p3 Pt.- p1) `dotV` (p2 Pt.- p1)     / (p2 Pt.- p1) `dotV` (p2 Pt.- p1)@@ -121,16 +121,16 @@     /     \\ @ -}-intersectLineLine-  :: Point-  -- ^ `P1`-  -> Point-  -- ^ `P2`-  -> Point-  -- ^ `P3`-  -> Point-  -- ^ `P4`-  -> Maybe Point+intersectLineLine ::+  -- | `P1`+  Point ->+  -- | `P2`+  Point ->+  -- | `P3`+  Point ->+  -- | `P4`+  Point ->+  Maybe Point intersectLineLine (x1, y1) (x2, y2) (x3, y3) (x4, y4) =   let dx12 = x1 - x2       dx34 = x3 - x4@@ -157,16 +157,16 @@ {-| Get the point where a segment @P1-P2@ crosses an infinite line @P3-P4@,   if any. -}-intersectSegLine-  :: Point-  -- ^ `P1`-  -> Point-  -- ^ `P2`-  -> Point-  -- ^ `P3`-  -> Point-  -- ^ `P4`-  -> Maybe Point+intersectSegLine ::+  -- | `P1`+  Point ->+  -- | `P2`+  Point ->+  -- | `P3`+  Point ->+  -- | `P4`+  Point ->+  Maybe Point intersectSegLine p1 p2 p3 p4   -- TODO: merge closest point check with intersection, reuse subterms.   | Just p0 <- intersectLineLine p1 p2 p3 p4@@ -187,14 +187,14 @@         P2 + @ -}-intersectSegHorzLine-  :: Point-  -- ^ P1 First point of segment.-  -> Point-  -- ^ P2 Second point of segment.-  -> Float-  -- ^ y value of line.-  -> Maybe Point+intersectSegHorzLine ::+  -- | P1 First point of segment.+  Point ->+  -- | P2 Second point of segment.+  Point ->+  -- | y value of line.+  Float ->+  Maybe Point intersectSegHorzLine (x1, y1) (x2, y2) y0   -- seg is on line   | y1 == y0, y2 == y0 = Nothing@@ -226,14 +226,14 @@              | x0 @ -}-intersectSegVertLine-  :: Point-  -- ^ P1 First point of segment.-  -> Point-  -- ^ P2 Second point of segment.-  -> Float-  -- ^ x value of line.-  -> Maybe Point+intersectSegVertLine ::+  -- | P1 First point of segment.+  Point ->+  -- | P2 Second point of segment.+  Point ->+  -- | x value of line.+  Float ->+  Maybe Point intersectSegVertLine (x1, y1) (x2, y2) x0   -- seg is on line   | x1 == x0, x2 == x0 = Nothing@@ -258,16 +258,16 @@ {-| Get the point where a segment @P1-P2@ crosses another segement @P3-P4@,   if any. -}-intersectSegSeg-  :: Point-  -- ^ `P1`-  -> Point-  -- ^ `P2`-  -> Point-  -- ^ `P3`-  -> Point-  -- ^ `P4`-  -> Maybe Point+intersectSegSeg ::+  -- | `P1`+  Point ->+  -- | `P2`+  Point ->+  -- | `P3`+  Point ->+  -- | `P4`+  Point ->+  Maybe Point intersectSegSeg p1 p2 p3 p4   -- TODO: merge closest point checks with intersection, reuse subterms.   | Just p0 <- intersectLineLine p1 p2 p3 p4@@ -290,19 +290,19 @@          P1 + @ -}-intersectSegHorzSeg-  :: Point-  -- ^ P1 First point of segment.-  -> Point-  -- ^ P2 Second point of segment.-  -> Float-  -- ^ (y3) y value of horizontal segment.-  -> Float-  -- ^ (xa) Leftmost x value of horizontal segment.-  -> Float-  -- ^ (xb) Rightmost x value of horizontal segment.-  -> Maybe Point-  -- ^ (x3, y3) Intersection point, if any.+intersectSegHorzSeg ::+  -- | P1 First point of segment.+  Point ->+  -- | P2 Second point of segment.+  Point ->+  -- | (y3) y value of horizontal segment.+  Float ->+  -- | (xa) Leftmost x value of horizontal segment.+  Float ->+  -- | (xb) Rightmost x value of horizontal segment.+  Float ->+  -- | (x3, y3) Intersection point, if any.+  Maybe Point intersectSegHorzSeg p1@(x1, y1) p2@(x2, y2) y0 xa xb   | segClearsBox p1 p2 (xa, y0) (xb, y0) =       Nothing@@ -327,19 +327,19 @@               + (x3, ya) @ -}-intersectSegVertSeg-  :: Point-  -- ^ P1 First point of segment.-  -> Point-  -- ^ P2 Second point of segment.-  -> Float-  -- ^ (x3) x value of vertical segment-  -> Float-  -- ^ (ya) Lowest y value of vertical segment.-  -> Float-  -- ^ (yb) Highest y value of vertical segment.-  -> Maybe Point-  -- ^ (x3, y3) Intersection point, if any.+intersectSegVertSeg ::+  -- | P1 First point of segment.+  Point ->+  -- | P2 Second point of segment.+  Point ->+  -- | (x3) x value of vertical segment+  Float ->+  -- | (ya) Lowest y value of vertical segment.+  Float ->+  -- | (yb) Highest y value of vertical segment.+  Float ->+  -- | (x3, y3) Intersection point, if any.+  Maybe Point intersectSegVertSeg p1@(x1, y1) p2@(x2, y2) x0 ya yb   | segClearsBox p1 p2 (x0, ya) (x0, yb) =       Nothing
Brillo/Interface/Environment.hs view
@@ -1,9 +1,12 @@ module Brillo.Interface.Environment where  import Data.IORef (newIORef)+import Data.Text qualified as T +import Brillo.Data.FileDialog (FileDialog) import Brillo.Internals.Interface.Backend (defaultBackendState) import Brillo.Internals.Interface.Backend.Types qualified as Backend.Types+import Brillo.Internals.TinyFileDialogs qualified as TinyFileDialogs   {-| Get the size of the screen, in pixels.@@ -16,3 +19,38 @@   backendStateRef <- newIORef defaultBackendState   Backend.Types.initializeBackend backendStateRef False   Backend.Types.getScreenSize backendStateRef+++{-| Open a file dialog to select files/directories.++  Returns a list of paths, or `Nothing` if the dialog was cancelled.+-}+openFileDialog :: FileDialog -> IO (Maybe [FilePath])+openFileDialog fileDialog = do+  backendStateRef <- newIORef defaultBackendState+  Backend.Types.initializeBackend backendStateRef False+  Backend.Types.openFileDialog backendStateRef fileDialog+++{-| Open a save file dialog.++  Returns the selected file path, or `Nothing` if the dialog was cancelled.+-}+saveFileDialog ::+  -- | Dialog title+  T.Text ->+  -- | Default file path+  T.Text ->+  -- | Filter patterns like @[\"*.svg\", \"*.png\"]@+  [T.Text] ->+  -- | Filter description like @\"Image files\"@+  T.Text ->+  IO (Maybe FilePath)+saveFileDialog title defaultPath filterPatterns filterDescription = do+  result <-+    TinyFileDialogs.saveFileDialog+      title+      defaultPath+      filterPatterns+      filterDescription+  pure $ fmap T.unpack result
Brillo/Interface/IO/Animate.hs view
@@ -21,17 +21,18 @@    Once the window is open you can use the same commands as with @display@. -}-animateIO-  :: Display-  -- ^ Display mode.-  -> Color-  -- ^ Background color.-  -> (Float -> IO Picture)-  -- ^ Function to produce the next frame of animation.-  --      It is passed the time in seconds since the program started.-  -> (Controller -> IO ())-  -- ^ Callback to take the display controller.-  -> IO ()+animateIO ::+  -- | Display mode.+  Display ->+  -- | Background color.+  Color ->+  {-| Function to produce the next frame of animation.+     It is passed the time in seconds since the program started.+  -}+  (Float -> IO Picture) ->+  -- | Callback to take the display controller.+  (Controller -> IO ()) ->+  IO () animateIO   display   backColor@@ -47,17 +48,18 @@   -- | Like `animateIO` but don't allow the display to be panned around.-animateFixedIO-  :: Display-  -- ^ Display mode.-  -> Color-  -- ^ Background color.-  -> (Float -> IO Picture)-  -- ^ Function to produce the next frame of animation.-  --      It is passed the time in seconds since the program started.-  -> (Controller -> IO ())-  -- ^ Callback to take the display controller.-  -> IO ()+animateFixedIO ::+  -- | Display mode.+  Display ->+  -- | Background color.+  Color ->+  {-| Function to produce the next frame of animation.+     It is passed the time in seconds since the program started.+  -}+  (Float -> IO Picture) ->+  -- | Callback to take the display controller.+  (Controller -> IO ()) ->+  IO () animateFixedIO   display   backColor
Brillo/Interface/IO/Display.hs view
@@ -5,11 +5,13 @@   module Brillo.Data.Color,   displayIO,   Controller (..),+  CursorShape (..), ) where  import Brillo.Data.Color import Brillo.Data.Controller+import Brillo.Data.Cursor import Brillo.Data.Display import Brillo.Data.Picture import Brillo.Internals.Interface.Backend@@ -50,16 +52,16 @@     storing your current picture in an IORef, passing an action that just     reads this IORef, and forking a new thread that watches your files for updates. -}-displayIO-  :: Display-  -- ^ Display mode.-  -> Color-  -- ^ Background color.-  -> IO Picture-  -- ^ Action to produce the current picture.-  -> (Controller -> IO ())-  -- ^ Callback to take the display controller.-  -> IO ()+displayIO ::+  -- | Display mode.+  Display ->+  -- | Background color.+  Color ->+  -- | Action to produce the current picture.+  IO Picture ->+  -- | Callback to take the display controller.+  (Controller -> IO ()) ->+  IO () displayIO dis backColor makePicture eatController =   displayWithBackend     defaultBackendState
Brillo/Interface/IO/Game.hs view
@@ -25,24 +25,25 @@   -- | Play a game in a window, using IO actions to build the pictures.-playIO-  :: forall world-   . Display-  -- ^ Display mode.-  -> Color-  -- ^ Background color.-  -> Int-  -- ^ Number of simulation steps to take for each second of real time.-  -> world-  -- ^ The initial world.-  -> (world -> IO Picture)-  -- ^ An action to convert the world a picture.-  -> (Event -> world -> IO world)-  -- ^ A function to handle input events.-  -> (Float -> world -> IO world)-  -- ^ A function to step the world one iteration.-  --   It is passed the period of time (in seconds) needing to be advanced.-  -> IO ()+playIO ::+  forall world.+  -- | Display mode.+  Display ->+  -- | Background color.+  Color ->+  -- | Number of simulation steps to take for each second of real time.+  Int ->+  -- | The initial world.+  world ->+  -- | An action to convert the world a picture.+  (world -> IO Picture) ->+  -- | A function to handle input events.+  (Event -> world -> IO world) ->+  {-| A function to step the world one iteration.+  It is passed the period of time (in seconds) needing to be advanced.+  -}+  (Float -> world -> IO world) ->+  IO () playIO   display   backColor
Brillo/Interface/IO/Interact.hs view
@@ -5,6 +5,7 @@   module Brillo.Data.Color,   interactIO,   Controller (..),+  CursorShape (..),   Event (..),   Key (..),   SpecialKey (..),@@ -16,6 +17,7 @@  import Brillo.Data.Color import Brillo.Data.Controller+import Brillo.Data.Cursor import Brillo.Data.Display import Brillo.Data.Picture import Brillo.Internals.Interface.Backend@@ -27,20 +29,20 @@    Similar to `displayIO`, except that you manage your own events. -}-interactIO-  :: Display-  -- ^ Display mode.-  -> Color-  -- ^ Background color.-  -> world-  -- ^ Initial world state.-  -> (world -> IO Picture)-  -- ^ A function to produce the current picture.-  -> (Event -> world -> IO world)-  -- ^ A function to handle input events.-  -> (Controller -> IO ())-  -- ^ Callback to take the display controller.-  -> IO ()+interactIO ::+  -- | Display mode.+  Display ->+  -- | Background color.+  Color ->+  -- | Initial world state.+  world ->+  -- | A function to produce the current picture.+  (world -> IO Picture) ->+  -- | A function to handle input events.+  (Event -> world -> IO world) ->+  -- | Callback to take the display controller.+  (Controller -> IO ()) ->+  IO () interactIO dis backColor worldInit makePicture handleEvent eatController =   interactWithBackend     defaultBackendState
Brillo/Interface/IO/Simulate.hs view
@@ -24,21 +24,22 @@ import Brillo.Internals.Interface.Simulate  -simulateIO-  :: forall model-   . Display-  -- ^ Display mode.-  -> Color-  -- ^ Background color.-  -> Int-  -- ^ Number of simulation steps to take for each second of real time.-  -> model-  -- ^ The initial model.-  -> (model -> IO Picture)-  -- ^ A function to convert the model to a picture.-  -> (ViewPort -> Float -> model -> IO model)-  -- ^ A function to step the model one iteration. It is passed the-  --     current viewport and the amount of time for this simulation-  --     step (in seconds).-  -> IO ()+simulateIO ::+  forall model.+  -- | Display mode.+  Display ->+  -- | Background color.+  Color ->+  -- | Number of simulation steps to take for each second of real time.+  Int ->+  -- | The initial model.+  model ->+  -- | A function to convert the model to a picture.+  (model -> IO Picture) ->+  {-| A function to step the model one iteration. It is passed the+    current viewport and the amount of time for this simulation+    step (in seconds).+  -}+  (ViewPort -> Float -> model -> IO model) ->+  IO () simulateIO = simulateWithBackendIO defaultBackendState
Brillo/Interface/Pure/Animate.hs view
@@ -18,15 +18,16 @@    Once the window is open you can use the same commands as with `display`. -}-animate-  :: Display-  -- ^ Display mode.-  -> Color-  -- ^ Background color.-  -> (Float -> Picture)-  -- ^ Function to produce the next frame of animation.-  --      It is passed the time in seconds since the program started.-  -> IO ()+animate ::+  -- | Display mode.+  Display ->+  -- | Background color.+  Color ->+  {-| Function to produce the next frame of animation.+     It is passed the time in seconds since the program started.+  -}+  (Float -> Picture) ->+  IO () animate display backColor frameFun =   animateWithBackendIO     defaultBackendState
Brillo/Interface/Pure/Display.hs view
@@ -15,14 +15,14 @@   -- | Open a new window and display the given picture.-display-  :: Display-  -- ^ Display mode.-  -> Color-  -- ^ Background color.-  -> Picture-  -- ^ The picture to draw.-  -> IO ()+display ::+  -- | Display mode.+  Display ->+  -- | Background color.+  Color ->+  -- | The picture to draw.+  Picture ->+  IO () display dis backColor picture =   displayWithBackend     defaultBackendState
Brillo/Interface/Pure/Game.hs view
@@ -28,23 +28,24 @@   -- | Play a game in a window. Like `simulate`, but you manage your own input events.-play-  :: Display-  -- ^ Display mode.-  -> Color-  -- ^ Background color.-  -> Int-  -- ^ Number of simulation steps to take for each second of real time.-  -> world-  -- ^ The initial world.-  -> (world -> Picture)-  -- ^ A function to convert the world a picture.-  -> (Event -> world -> world)-  -- ^ A function to handle input events.-  -> (Float -> world -> world)-  -- ^ A function to step the world one iteration.-  --   It is passed the period of time (in seconds) needing to be advanced.-  -> IO ()+play ::+  -- | Display mode.+  Display ->+  -- | Background color.+  Color ->+  -- | Number of simulation steps to take for each second of real time.+  Int ->+  -- | The initial world.+  world ->+  -- | A function to convert the world a picture.+  (world -> Picture) ->+  -- | A function to handle input events.+  (Event -> world -> world) ->+  {-| A function to step the world one iteration.+  It is passed the period of time (in seconds) needing to be advanced.+  -}+  (Float -> world -> world) ->+  IO () play   display   backColor
Brillo/Interface/Pure/Simulate.hs view
@@ -30,22 +30,23 @@    Once the window is open you can use the same commands as with `display`. -}-simulate-  :: Display-  -- ^ Display mode.-  -> Color-  -- ^ Background color.-  -> Int-  -- ^ Number of simulation steps to take for each second of real time.-  -> model-  -- ^ The initial model.-  -> (model -> Picture)-  -- ^ A function to convert the model to a picture.-  -> (ViewPort -> Float -> model -> model)-  -- ^ A function to step the model one iteration. It is passed the-  --     current viewport and the amount of time for this simulation-  --     step (in seconds).-  -> IO ()+simulate ::+  -- | Display mode.+  Display ->+  -- | Background color.+  Color ->+  -- | Number of simulation steps to take for each second of real time.+  Int ->+  -- | The initial model.+  model ->+  -- | A function to convert the model to a picture.+  (model -> Picture) ->+  {-| A function to step the model one iteration. It is passed the+    current viewport and the amount of time for this simulation+    step (in seconds).+  -}+  (ViewPort -> Float -> model -> model) ->+  IO () simulate   display   backColor
Brillo/Internals/Interface/Animate.hs view
@@ -22,22 +22,23 @@ import System.Mem  -animateWithBackendIO-  :: (Backend a)-  => a-  -- ^ Initial State of the backend-  -> Bool-  -- ^ Whether to allow the image to be panned around.-  -> Display-  -- ^ Display mode.-  -> Color-  -- ^ Background color.-  -> (Float -> IO Picture)-  -- ^ Function to produce the next frame of animation.-  --     It is passed the time in seconds since the program started.-  -> (Controller -> IO ())-  -- ^ Eat the controller.-  -> IO ()+animateWithBackendIO ::+  (Backend a) =>+  -- | Initial State of the backend+  a ->+  -- | Whether to allow the image to be panned around.+  Bool ->+  -- | Display mode.+  Display ->+  -- | Background color.+  Color ->+  {-| Function to produce the next frame of animation.+    It is passed the time in seconds since the program started.+  -}+  (Float -> IO Picture) ->+  -- | Eat the controller.+  (Controller -> IO ()) ->+  IO () animateWithBackendIO   backend   pannable@@ -85,8 +86,8 @@             , callback_viewState_reshape             ]               ++ ( if pannable-                    then [callback_viewState_keyMouse viewSR]-                    else []+                     then [callback_viewState_keyMouse viewSR]+                     else []                  )        createWindow backend display backColor callbacks $@@ -103,6 +104,9 @@                       let viewState' = viewState{viewStateViewPort = port'}                       writeIORef viewSR viewState'                       postRedisplay backendRef+              , controllerSetCursor =+                  \cursorShape ->+                    setCursor backendRef cursorShape               }  
Brillo/Internals/Interface/Animate/State.hs view
@@ -22,8 +22,9 @@   -- ^ The time when we entered the display callback for the current frame.   , stateDisplayTimeLast :: !Double   , stateDisplayTimeClamp :: !Double-  -- ^ Clamp the minimum time between frames to this value (in seconds)-  --      Setting this to < 10ms probably isn't worthwhile.+  {- ^ Clamp the minimum time between frames to this value (in seconds)+     Setting this to < 10ms probably isn't worthwhile.+  -}   , stateGateTimeStart :: !Double   -- ^ The time when the last call to the users render function finished.   , stateGateTimeEnd :: !Double
Brillo/Internals/Interface/Backend/GLFW.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedRecordDot #-} {-# OPTIONS_HADDOCK hide #-}  -- | Support for using GLFW as the window manager backend.@@ -8,13 +9,30 @@ import Control.Concurrent (threadDelay) import Control.Exception qualified as X import Control.Monad (unless, when)+import Data.Functor ((<&>)) import Data.IORef (IORef, modifyIORef', readIORef, writeIORef)-import Data.Maybe (fromJust)+import Data.Text qualified as T+import GHC.Desugar ((>>>)) import Graphics.Rendering.OpenGL (($=)) import Graphics.Rendering.OpenGL qualified as GL import Graphics.UI.GLFW qualified as GLFW -import Brillo.Internals.Interface.Backend.Types+import Brillo.Data.Cursor (CursorShape (..))+import Brillo.Data.FileDialog (FileDialog (..), SelectionMode (..))+import Brillo.Internals.Interface.Backend.Types (+  Backend (..),+  Callback (..),+  Display (..),+  Key (..),+  KeyState (..),+  Modifiers (..),+  MouseButton (..),+  SpecialKey (..),+ )+import Brillo.Internals.TinyFileDialogs as TinyFileDialogs+import Data.List (singleton)+import Data.Map.Strict (Map)+import Data.Map.Strict qualified as Map   -- | State of the GLFW backend library.@@ -34,6 +52,8 @@   -- ^ Action perforrmed when idling   , optWinHdl :: Maybe GLFW.Window   -- ^ The Window Handle+  , cursorCache :: Map CursorShape GLFW.Cursor+  -- ^ Cache of created cursors   }  @@ -48,6 +68,7 @@     , display = return ()     , idle = return ()     , optWinHdl = Nothing+    , cursorCache = Map.empty     }  @@ -68,15 +89,22 @@   installDisplayCallback = installDisplayCallbackGLFW   installWindowCloseCallback = installWindowCloseCallbackGLFW   installReshapeCallback = installReshapeCallbackGLFW+  installRefreshCallback = installRefreshCallbackGLFW   installKeyMouseCallback = installKeyMouseCallbackGLFW   installMotionCallback = installMotionCallbackGLFW+  installDropCallback = installDropCallbackGLFW   installIdleCallback = installIdleCallbackGLFW   runMainLoop = runMainLoopGLFW   postRedisplay = postRedisplayGLFW-  getWindowDimensions = (\ref -> windowHandle ref >>= \win -> GLFW.getWindowSize win)+  getWindowDimensions ref = windowHandle ref >>= \win -> GLFW.getWindowSize win   getScreenSize = getScreenSizeGLFW-  elapsedTime = (\_ -> GLFW.getTime >>= \mt -> return $ fromJust mt)-  sleep = (\_ sec -> threadDelay (floor (sec * 1000000.0))) -- GLFW.sleep sec)+  openFileDialog = openFileDialogGLFW+  elapsedTime _ =+    GLFW.getTime >>= \mt -> case mt of+      Nothing -> error "GLFW.getTime returned Nothing. Is GLFW initialized?"+      Just t -> return t+  sleep _ sec = threadDelay (floor (sec * 1000000.0)) -- GLFW.sleep sec)+  setCursor = setCursorGLFW   -- Initialise -----------------------------------------------------------------@@ -93,8 +121,8 @@     glfwVersion <- GLFW.getVersion      when debug $-      putStr $-        "  glfwVersion        = " ++ show glfwVersion ++ "\n"+      putStrLn $+        "  glfwVersion        = " ++ show glfwVersion   -- Exit -----------------------------------------------------------------------@@ -106,54 +134,116 @@   GLFW.setWindowShouldClose win True  +-- Cursor ---------------------------------------------------------------------++-- | Set the cursor shape for the GLFW window.+setCursorGLFW :: IORef GLFWState -> CursorShape -> IO ()+setCursorGLFW stateRef shape = do+  state <- readIORef stateRef+  win <- windowHandle stateRef++  case shape of+    CursorHidden -> do+      -- Hide cursor using GLFW's cursor input mode+      GLFW.setCursorInputMode win GLFW.CursorInputMode'Hidden+    _ -> do+      -- Ensure cursor is visible+      GLFW.setCursorInputMode win GLFW.CursorInputMode'Normal++      -- Check if cursor is already cached+      case Map.lookup shape (cursorCache state) of+        Just cursor -> do+          GLFW.setCursor win cursor+        Nothing -> do+          -- Create and cache the cursor+          let stdCursor = cursorShapeToGLFW shape+          cursor <- GLFW.createStandardCursor stdCursor+          -- Cache the cursor for future use+          modifyIORef' stateRef $ \s ->+            s{cursorCache = Map.insert shape cursor (cursorCache s)}+          GLFW.setCursor win cursor+++-- | Convert Brillo CursorShape to GLFW StandardCursorShape.+cursorShapeToGLFW :: CursorShape -> GLFW.StandardCursorShape+cursorShapeToGLFW shape = case shape of+  CursorArrow -> GLFW.StandardCursorShape'Arrow+  CursorIBeam -> GLFW.StandardCursorShape'IBeam+  CursorCrosshair -> GLFW.StandardCursorShape'Crosshair+  CursorHand -> GLFW.StandardCursorShape'Hand+  CursorResizeH -> GLFW.StandardCursorShape'HResize+  CursorResizeV -> GLFW.StandardCursorShape'VResize+  CursorHidden -> GLFW.StandardCursorShape'Arrow -- Not used, handled separately++ -- Open Window ----------------------------------------------------------------  -- | Open a new window.-openWindowGLFW-  :: IORef GLFWState-  -> Display-  -> IO ()+openWindowGLFW ::+  IORef GLFWState ->+  Display ->+  IO () openWindowGLFW ref (InWindow title (sizeX, sizeY) pos) =   do     win <-       GLFW.createWindow         sizeX         sizeY-        title+        (T.unpack title)         Nothing         Nothing -    modifyIORef' ref (\s -> s{optWinHdl = win})-    uncurry (GLFW.setWindowPos (fromJust win)) pos-    GLFW.makeContextCurrent win+    case win of+      Nothing ->+        error $+          "GLFW.createWindow failed to create a "+            ++ show sizeX+            ++ "x"+            ++ show sizeY+            ++ " window."+            ++ " Check that your GPU drivers support OpenGL."+      Just w -> do+        modifyIORef' ref (\s -> s{optWinHdl = win})+        uncurry (GLFW.setWindowPos w) pos+        GLFW.makeContextCurrent win -    -- Try to enable sync-to-vertical-refresh by setting the number-    -- of buffer swaps per vertical refresh to 1.-    GLFW.swapInterval 1+        -- Try to enable sync-to-vertical-refresh by setting the number+        -- of buffer swaps per vertical refresh to 1.+        GLFW.swapInterval 1 openWindowGLFW ref FullScreen =   do     mon <- GLFW.getPrimaryMonitor-    vmode <- GLFW.getVideoMode (fromJust mon)--    let sizeX = GLFW.videoModeWidth (fromJust vmode)-    let sizeY = GLFW.videoModeHeight (fromJust vmode)+    case mon of+      Nothing ->+        error "GLFW.getPrimaryMonitor returned Nothing. No monitor detected."+      Just m -> do+        vmode <- GLFW.getVideoMode m+        case vmode of+          Nothing ->+            error "GLFW.getVideoMode returned Nothing for primary monitor."+          Just vm -> do+            let sizeX = GLFW.videoModeWidth vm+            let sizeY = GLFW.videoModeHeight vm -    win <--      GLFW.createWindow-        sizeX-        sizeY-        ""-        mon-        Nothing+            win <-+              GLFW.createWindow+                sizeX+                sizeY+                ""+                mon+                Nothing -    modifyIORef' ref (\s -> s{optWinHdl = win})-    GLFW.makeContextCurrent win+            case win of+              Nothing ->+                error "GLFW.createWindow failed to create a fullscreen window."+              Just w -> do+                modifyIORef' ref (\s -> s{optWinHdl = win})+                GLFW.makeContextCurrent win -    -- Try to enable sync-to-vertical-refresh by setting the number-    -- of buffer swaps per vertical refresh to 1.-    GLFW.swapInterval 1-    -- GLFW.enableMouseCursor-    GLFW.setCursorInputMode (fromJust win) GLFW.CursorInputMode'Normal+                -- Try to enable sync-to-vertical-refresh by setting the number+                -- of buffer swaps per vertical refresh to 1.+                GLFW.swapInterval 1+                GLFW.setCursorInputMode w GLFW.CursorInputMode'Normal   windowHandle :: IORef GLFWState -> IO GLFW.Window@@ -166,14 +256,43 @@ getScreenSizeGLFW :: IORef GLFWState -> IO (Int, Int) getScreenSizeGLFW _state = do   monitor <- GLFW.getPrimaryMonitor-  vmode <- GLFW.getVideoMode (fromJust monitor)+  case monitor of+    Nothing ->+      error "GLFW.getPrimaryMonitor returned Nothing. No monitor detected."+    Just m -> do+      vmode <- GLFW.getVideoMode m+      case vmode of+        Nothing ->+          error "GLFW.getVideoMode returned Nothing for primary monitor."+        Just vm -> do+          let sizeX = GLFW.videoModeWidth vm+          let sizeY = GLFW.videoModeHeight vm+          pure (sizeX, sizeY) -  let sizeX = GLFW.videoModeWidth (fromJust vmode)-  let sizeY = GLFW.videoModeHeight (fromJust vmode) -  pure (sizeX, sizeY)+-- | Open a file dialog. Return `Nothing` if the user cancels the dialog.+openFileDialogGLFW :: IORef GLFWState -> FileDialog -> IO (Maybe [FilePath])+openFileDialogGLFW _state fileDialog = do+  case fileDialog.selectionMode of+    SingleDirectorySelect -> do+      dirPathMb <-+        TinyFileDialogs.selectFolderDialog+          fileDialog.title+          fileDialog.defaultPath +      return (dirPathMb <&> T.unpack >>> singleton)+    _ -> do+      filePathsMb <-+        TinyFileDialogs.openFileDialog+          fileDialog.title+          fileDialog.defaultPath+          fileDialog.filterPatterns+          fileDialog.filterDescription+          (fileDialog.selectionMode == MultiFileSelect) +      return $ filePathsMb <&> (<&> T.unpack)++ -- Dump State -----------------------------------------------------------------  -- | Print out the internal GLFW state.@@ -234,8 +353,8 @@ -- Display Callback -----------------------------------------------------------  -- | Callback for when GLFW needs us to redraw the contents of the window.-installDisplayCallbackGLFW-  :: IORef GLFWState -> [Callback] -> IO ()+installDisplayCallbackGLFW ::+  IORef GLFWState -> [Callback] -> IO () installDisplayCallbackGLFW stateRef callbacks =   modifyIORef' stateRef $ \s ->     s@@ -243,28 +362,25 @@       }  -callbackDisplay-  :: IORef GLFWState-  -> [Callback]-  -> IO ()-callbackDisplay stateRef callbacks =-  do-    -- clear the display-    GL.clear [GL.ColorBuffer, GL.DepthBuffer]-    GL.color $ GL.Color4 0 0 0 (1 :: GL.GLfloat)--    -- set the OpenGL viewport to account for any HiDPI discrepancy-    (width, height) <- windowHandle stateRef >>= GLFW.getFramebufferSize-    GL.viewport-      $= ( GL.Position 0 0-         , GL.Size (fromIntegral width) (fromIntegral height)-         )+callbackDisplay ::+  IORef GLFWState ->+  [Callback] ->+  IO ()+callbackDisplay stateRef callbacks = do+  -- clear the display+  GL.clear [GL.ColorBuffer, GL.DepthBuffer]+  GL.color $ GL.Color4 0 0 0 (1 :: GL.GLfloat) -    -- get the display callbacks from the chain-    let funs = [f stateRef | (Display f) <- callbacks]-    sequence_ funs+  -- set the OpenGL viewport to account for any HiDPI discrepancy+  (width, height) <- windowHandle stateRef >>= GLFW.getFramebufferSize+  GL.viewport+    $= ( GL.Position 0 0+       , GL.Size (fromIntegral width) (fromIntegral height)+       ) -    return ()+  -- get the display callbacks from the chain+  let funs = [f stateRef | (Display f) <- callbacks]+  sequence_ funs   -- Close Callback -------------------------------------------------------------@@ -272,8 +388,8 @@ {-| Callback for when the user closes the window.   We can do some cleanup here. -}-installWindowCloseCallbackGLFW-  :: IORef GLFWState -> IO ()+installWindowCloseCallbackGLFW ::+  IORef GLFWState -> IO () installWindowCloseCallbackGLFW ref =   do     win <- windowHandle ref@@ -287,25 +403,57 @@ -- Reshape --------------------------------------------------------------------  -- | Callback for when the user reshapes the window.-installReshapeCallbackGLFW-  :: IORef GLFWState -> [Callback] -> IO ()+installReshapeCallbackGLFW ::+  IORef GLFWState -> [Callback] -> IO () installReshapeCallbackGLFW stateRef callbacks =   do     win <- windowHandle stateRef     GLFW.setWindowSizeCallback win (Just $ callbackReshape stateRef callbacks)  -callbackReshape-  :: IORef GLFWState-  -> [Callback]-  -> GLFW.WindowSizeCallback -- = Window -> Int -> Int -> IO ()-callbackReshape glfwState callbacks _win sizeX sizeY =-  sequence_ $-    map-      (\f -> f (sizeX, sizeY))-      [f glfwState | Reshape f <- callbacks]+callbackReshape ::+  IORef GLFWState ->+  [Callback] ->+  GLFW.WindowSizeCallback -- = Window -> Int -> Int -> IO ()+callbackReshape stateRef callbacks _win sizeX sizeY =+  -- Call all reshape callbacks (which will update world state, viewport, etc.)+  -- Note: The actual redraw during live resize is handled by callbackRefresh,+  -- since on macOS the window size callback doesn't fire during drag.+  mapM_+    (\f -> f (sizeX, sizeY))+    ([f stateRef | Reshape f <- callbacks])  +-- Refresh -----------------------------------------------------------------------++-- | Callback for when the window needs to be refreshed (e.g., during live resize on macOS).+installRefreshCallbackGLFW ::+  IORef GLFWState -> [Callback] -> IO ()+installRefreshCallbackGLFW stateRef callbacks = do+  win <- windowHandle stateRef+  GLFW.setWindowRefreshCallback win (Just $ callbackRefresh stateRef callbacks)+++callbackRefresh ::+  IORef GLFWState ->+  [Callback] ->+  GLFW.WindowRefreshCallback -- = Window -> IO ()+callbackRefresh stateRef callbacks win = do+  -- Get the current window size and fire reshape callbacks+  -- This ensures EventResize is sent during live resize on macOS+  (sizeX, sizeY) <- GLFW.getWindowSize win+  mapM_+    (\f -> f (sizeX, sizeY))+    ([f stateRef | Reshape f <- callbacks])++  -- Run idle callbacks to advance simulations/animations+  callbackIdle stateRef callbacks++  -- Redraw and present the frame+  callbackDisplay stateRef callbacks+  GLFW.swapBuffers win++ -- KeyMouse -----------------------------------------------------------------------  {-| Callbacks for when the user presses a key or moves / clicks the mouse.@@ -315,10 +463,10 @@   slot for character keys, arrow keys, mouse buttons and mouse wheel movement,   while GLFW provides a single slot for each. -}-installKeyMouseCallbackGLFW-  :: IORef GLFWState-  -> [Callback]-  -> IO ()+installKeyMouseCallbackGLFW ::+  IORef GLFWState ->+  [Callback] ->+  IO () installKeyMouseCallbackGLFW stateRef callbacks =   do     win <- windowHandle stateRef@@ -326,38 +474,105 @@     GLFW.setCharCallback win (Just $ callbackChar stateRef callbacks)     GLFW.setMouseButtonCallback win (Just $ callbackMouseButton stateRef callbacks)     GLFW.setScrollCallback win (Just $ callbackMouseWheel stateRef callbacks)+    GLFW.setDropCallback win (Just $ callbackDrop stateRef callbacks)   -- GLFW calls this on a non-character keyboard action.-callbackKeyboard-  :: IORef GLFWState-  -> [Callback]-  -> GLFW.KeyCallback -- = Window -> Key -> Int -> KeyState -> ModifierKeys -> IO ()+callbackKeyboard ::+  IORef GLFWState ->+  [Callback] ->+  GLFW.KeyCallback -- = Window -> Key -> Int -> KeyState -> ModifierKeys -> IO ()   -- -> GLFW.Key -> Bool   -- -> IO ()+callbackKeyboard _stateRef _callbacks _win _key _scancode GLFW.KeyState'Repeating _modifiers =+  return () callbackKeyboard stateRef callbacks _win key _scancode keystateglfw _modifiers =   do     let keystate = keystateglfw == GLFW.KeyState'Pressed-    (modsSet, GLFWState mods pos _ _ _ _ _) <-+    (modsSet, GLFWState mods pos _ _ _ _ _ _) <-       setModifiers stateRef key keystate     let key' = fromGLFW key     let keystate' = if keystate then Down else Up     let isCharKey (Char _) = True         isCharKey _ = False +    -- Only process key presses for keys that won't generate character events+    -- or process any key release (since char callback only handles presses)+    let shouldProcess = not keystate || not (isCharacterKey key)+     -- Call the Brillo KeyMouse actions with the new state.-    unless (modsSet || isCharKey key' && keystate) $-      sequence_ $-        map-          (\f -> f key' keystate' mods pos)-          [f stateRef | KeyMouse f <- callbacks]+    unless (modsSet || isCharKey key' && keystate || not shouldProcess) $+      mapM_+        (\f -> f key' keystate' mods pos)+        ([f stateRef | KeyMouse f <- callbacks])  -setModifiers-  :: IORef GLFWState-  -> GLFW.Key-  -> Bool-  -> IO (Bool, GLFWState)+{-| Check if a GLFW key will generate a character callback event+These are keys that produce printable characters and will trigger both+key and character callbacks, causing duplicate events+-}+isCharacterKey :: GLFW.Key -> Bool+isCharacterKey key = case key of+  -- Alphabetic keys+  GLFW.Key'A -> True+  GLFW.Key'B -> True+  GLFW.Key'C -> True+  GLFW.Key'D -> True+  GLFW.Key'E -> True+  GLFW.Key'F -> True+  GLFW.Key'G -> True+  GLFW.Key'H -> True+  GLFW.Key'I -> True+  GLFW.Key'J -> True+  GLFW.Key'K -> True+  GLFW.Key'L -> True+  GLFW.Key'M -> True+  GLFW.Key'N -> True+  GLFW.Key'O -> True+  GLFW.Key'P -> True+  GLFW.Key'Q -> True+  GLFW.Key'R -> True+  GLFW.Key'S -> True+  GLFW.Key'T -> True+  GLFW.Key'U -> True+  GLFW.Key'V -> True+  GLFW.Key'W -> True+  GLFW.Key'X -> True+  GLFW.Key'Y -> True+  GLFW.Key'Z -> True+  -- Number keys+  GLFW.Key'0 -> True+  GLFW.Key'1 -> True+  GLFW.Key'2 -> True+  GLFW.Key'3 -> True+  GLFW.Key'4 -> True+  GLFW.Key'5 -> True+  GLFW.Key'6 -> True+  GLFW.Key'7 -> True+  GLFW.Key'8 -> True+  GLFW.Key'9 -> True+  -- Printable symbols+  GLFW.Key'Space -> True+  GLFW.Key'Apostrophe -> True+  GLFW.Key'Comma -> True+  GLFW.Key'Minus -> True+  GLFW.Key'Period -> True+  GLFW.Key'Slash -> True+  GLFW.Key'Semicolon -> True+  GLFW.Key'Equal -> True+  GLFW.Key'LeftBracket -> True+  GLFW.Key'Backslash -> True+  GLFW.Key'RightBracket -> True+  GLFW.Key'GraveAccent -> True+  -- All other keys (function keys, arrows, etc.) don't generate char events+  _ -> False+++setModifiers ::+  IORef GLFWState ->+  GLFW.Key ->+  Bool ->+  IO (Bool, GLFWState) setModifiers stateRef key pressed =   do     glfwState <- readIORef stateRef@@ -368,7 +583,7 @@           GLFW.Key'LeftAlt -> mods{alt = if pressed then Down else Up}           _ -> mods -    if (mods' /= mods)+    if mods' /= mods       then do         let glfwState' = glfwState{modifiers = mods'}         writeIORef stateRef glfwState'@@ -377,17 +592,17 @@   -- GLFW calls this on a when the user presses or releases a character key.-callbackChar-  :: IORef GLFWState-  -> [Callback]-  -> GLFW.CharCallback+callbackChar ::+  IORef GLFWState ->+  [Callback] ->+  GLFW.CharCallback -- Window -> Char -> IO () -- -> Char -> Bool -> IO ()  callbackChar stateRef callbacks _win char -- keystate   =   do-    (GLFWState mods pos _ _ _ _ _) <- readIORef stateRef+    (GLFWState mods pos _ _ _ _ _ _) <- readIORef stateRef     let key' = charToSpecial char     -- TODO: is this correct? GLFW does not provide the keystate     -- in a character callback, here we asume that its pressed@@ -402,54 +617,51 @@     let keystate' = if keystate then Down else Up      -- Call all the Brillo KeyMouse actions with the new state.-    sequence_ $-      map-        (\f -> f key' keystate' mods pos)-        [f stateRef | KeyMouse f <- callbacks]+    mapM_+      (\f -> f key' keystate' mods pos)+      ([f stateRef | KeyMouse f <- callbacks])   -- GLFW calls on this when the user clicks or releases a mouse button.-callbackMouseButton-  :: IORef GLFWState-  -> [Callback]-  -> GLFW.MouseButtonCallback -- = Window -> MouseButton -> MouseButtonState -> ModifierKeys -> IO ()+callbackMouseButton ::+  IORef GLFWState ->+  [Callback] ->+  GLFW.MouseButtonCallback -- = Window -> MouseButton -> MouseButtonState -> ModifierKeys -> IO () callbackMouseButton stateRef callbacks _win key keystate _modifier =   do-    (GLFWState mods pos _ _ _ _ _) <- readIORef stateRef+    (GLFWState mods pos _ _ _ _ _ _) <- readIORef stateRef     let key' = fromGLFW key     let keystate' = if keystate == GLFW.MouseButtonState'Pressed then Down else Up      -- Call all the Brillo KeyMouse actions with the new state.-    sequence_ $-      map-        (\f -> f key' keystate' mods pos)-        [f stateRef | KeyMouse f <- callbacks]+    mapM_+      (\f -> f key' keystate' mods pos)+      ([f stateRef | KeyMouse f <- callbacks])   -- GLFW calls on this when the user moves the mouse wheel.-callbackMouseWheel-  :: IORef GLFWState-  -> [Callback]-  -> GLFW.ScrollCallback+callbackMouseWheel ::+  IORef GLFWState ->+  [Callback] ->+  GLFW.ScrollCallback -- -> Int -- -> IO () -- ScrollCallback = Window -> Double -> Double -> IO () callbackMouseWheel stateRef callbacks _win x _y =   do     (key, keystate) <- setMouseWheel stateRef (floor x)-    (GLFWState mods pos _ _ _ _ _) <- readIORef stateRef+    (GLFWState mods pos _ _ _ _ _ _) <- readIORef stateRef      -- Call all the Brillo KeyMouse actions with the new state.-    sequence_ $-      map-        (\f -> f key keystate mods pos)-        [f stateRef | KeyMouse f <- callbacks]+    mapM_+      (\f -> f key keystate mods pos)+      ([f stateRef | KeyMouse f <- callbacks])  -setMouseWheel-  :: IORef GLFWState-  -> Int-  -> IO (Key, KeyState)+setMouseWheel ::+  IORef GLFWState ->+  Int ->+  IO (Key, KeyState) setMouseWheel stateRef w =   do     glfwState <- readIORef stateRef@@ -460,41 +672,63 @@       EQ -> return (SpecialKey KeyUnknown, Up)  +-- | GLFW calls this when the user drops files/directories onto the window+callbackDrop ::+  IORef GLFWState ->+  [Callback] ->+  GLFW.DropCallback+callbackDrop stateRef callbacks _win paths = do+  mapM_+    (\f -> f paths)+    ([f stateRef | Drop f <- callbacks])++ -- Motion Callback ------------------------------------------------------------  -- | Callback for when the user moves the mouse.-installMotionCallbackGLFW-  :: IORef GLFWState-  -> [Callback]-  -> IO ()+installMotionCallbackGLFW ::+  IORef GLFWState ->+  [Callback] ->+  IO () installMotionCallbackGLFW stateRef callbacks =   do     win <- windowHandle stateRef     GLFW.setCursorPosCallback win (Just $ callbackMotion stateRef callbacks)  +-- Drop Paths Callback ---------------------------------------------------------++-- | Callback for when the user drops files/directories onto the window.+installDropCallbackGLFW ::+  IORef GLFWState ->+  [Callback] ->+  IO ()+installDropCallbackGLFW stateRef callbacks = do+  win <- windowHandle stateRef+  GLFW.setDropCallback win (Just $ callbackDrop stateRef callbacks)++ -- CursorPosCallback = Window -> Double -> Double -> IO () -callbackMotion-  :: IORef GLFWState-  -> [Callback]-  -> GLFW.CursorPosCallback+callbackMotion ::+  IORef GLFWState ->+  [Callback] ->+  GLFW.CursorPosCallback callbackMotion stateRef callbacks _win x y =   do     pos <- setMousePos stateRef (floor x) (floor y)      -- Call all the Brillo Motion actions with the new state.-    sequence_ $-      map-        (\f -> f pos)-        [f stateRef | Motion f <- callbacks]+    mapM_+      (\f -> f pos)+      ([f stateRef | Motion f <- callbacks])  -setMousePos-  :: IORef GLFWState-  -> Int-  -> Int-  -> IO (Int, Int)+setMousePos ::+  IORef GLFWState ->+  Int ->+  Int ->+  IO (Int, Int) setMousePos stateRef x y =   do     let pos = (x, y)@@ -512,10 +746,10 @@ {-| Callback for when GLFW has finished its jobs and it's time for us to do   something for our application. -}-installIdleCallbackGLFW-  :: IORef GLFWState-  -> [Callback]-  -> IO ()+installIdleCallbackGLFW ::+  IORef GLFWState ->+  [Callback] ->+  IO () installIdleCallbackGLFW stateRef callbacks =   modifyIORef' stateRef $ \s ->     s@@ -523,10 +757,10 @@       }  -callbackIdle-  :: IORef GLFWState-  -> [Callback]-  -> IO ()+callbackIdle ::+  IORef GLFWState ->+  [Callback] ->+  IO () callbackIdle stateRef callbacks =   sequence_ $     [f stateRef | Idle f <- callbacks]@@ -585,9 +819,9 @@   -- Redisplay -------------------------------------------------------------------postRedisplayGLFW-  :: IORef GLFWState-  -> IO ()+postRedisplayGLFW ::+  IORef GLFWState ->+  IO () postRedisplayGLFW stateRef =   modifyIORef' stateRef $ \s ->     s@@ -696,7 +930,7 @@   binding GLFW-b 0.2.* -} charToSpecial :: Char -> Key-charToSpecial c = case (fromEnum c) of+charToSpecial c = case fromEnum c of   32 -> SpecialKey KeySpace   63232 -> SpecialKey KeyUp   63233 -> SpecialKey KeyDown
Brillo/Internals/Interface/Backend/Types.hs view
@@ -3,12 +3,15 @@  module Brillo.Internals.Interface.Backend.Types (   module Brillo.Internals.Interface.Backend.Types,+  module Brillo.Data.Cursor,   module Brillo.Data.Display, ) where +import Brillo.Data.Cursor import Brillo.Data.Display-import Data.IORef+import Brillo.Data.FileDialog (FileDialog)+import Data.IORef (IORef)   {-| The functions every backend window managed backend needs to support.@@ -21,14 +24,16 @@   the backend needs to have OpenGL in a state where it's able to accept them. -} class Backend a where-  -- | Initialize the state used by the backend.-  -- If you don't use any state, make a Unit-like type.+  {-| Initialize the state used by the backend.+  If you don't use any state, make a Unit-like type.+  -}   initBackendState :: a  -  -- | Perform any initialization that needs to happen before opening a window-  --   The Boolean flag indicates if any debug information should be printed to-  --   the terminal+  {-| Perform any initialization that needs to happen before opening a window+  The Boolean flag indicates if any debug information should be printed to+  the terminal+  -}   initializeBackend :: IORef a -> Bool -> IO ()  @@ -56,6 +61,10 @@   installReshapeCallback :: IORef a -> [Callback] -> IO ()  +  -- | Install the refresh callback (called during live resize on macOS).+  installRefreshCallback :: IORef a -> [Callback] -> IO ()++   -- | Install the keymouse press callbacks.   installKeyMouseCallback :: IORef a -> [Callback] -> IO () @@ -64,6 +73,10 @@   installMotionCallback :: IORef a -> [Callback] -> IO ()  +  -- | Install the file/directory drop callbacks.+  installDropCallback :: IORef a -> [Callback] -> IO ()++   -- | Install the idle callbacks.   installIdleCallback :: IORef a -> [Callback] -> IO () @@ -84,8 +97,13 @@   getScreenSize :: IORef a -> IO (Int, Int)  -  -- | Function that reports the time elapsed since the application started.-  --   (in seconds)+  -- | Open a file dialog to pick files/directories.+  openFileDialog :: IORef a -> FileDialog -> IO (Maybe [FilePath])+++  {-| Function that reports the time elapsed since the application started.+  (in seconds)+  -}   elapsedTime :: IORef a -> IO Double  @@ -93,6 +111,10 @@   sleep :: IORef a -> Double -> IO ()  +  -- | Set the cursor shape for the window.+  setCursor :: IORef a -> CursorShape -> IO ()++ -- The callbacks should work for all backends. We pass a reference to the -- backend state so that the callbacks have access to the class dictionary and -- can thus call the appropriate backend functions.@@ -104,17 +126,29 @@  -- | Arguments: KeyType, Key Up \/ Down, Ctrl \/ Alt \/ Shift pressed, latest mouse location. type KeyboardMouseCallback =-  forall a. (Backend a) => IORef a -> Key -> KeyState -> Modifiers -> (Int, Int) -> IO ()+  forall a.+  (Backend a) =>+  IORef a ->+  Key ->+  KeyState ->+  Modifiers ->+  (Int, Int) ->+  IO ()  +-- | No arguments.+type IdleCallback =+  forall a. (Backend a) => IORef a -> IO ()++ -- | Arguments: (PosX,PosY) in pixels. type MotionCallback =   forall a. (Backend a) => IORef a -> (Int, Int) -> IO ()  --- | No arguments.-type IdleCallback =-  forall a. (Backend a) => IORef a -> IO ()+-- | Arguments: [FilePath] of dropped files.+type DropCallback =+  forall a. (Backend a) => IORef a -> [FilePath] -> IO ()   -- | Arguments: (Width,Height) in pixels.@@ -128,6 +162,7 @@   | KeyMouse KeyboardMouseCallback   | Idle IdleCallback   | Motion MotionCallback+  | Drop DropCallback   | Reshape ReshapeCallback  
Brillo/Internals/Interface/Callback.hs view
@@ -6,6 +6,7 @@   DisplayCallback,   KeyboardMouseCallback,   MotionCallback,+  DropCallback,   IdleCallback,   ReshapeCallback, )
Brillo/Internals/Interface/Display.hs view
@@ -18,19 +18,19 @@ import System.Mem  -displayWithBackend-  :: (Backend a)-  => a-  -- ^ Initial state of the backend.-  -> Display-  -- ^ Display config.-  -> Color-  -- ^ Background color.-  -> IO Picture-  -- ^ Make the picture to draw.-  -> (Controller -> IO ())-  -- ^ Eat the controller-  -> IO ()+displayWithBackend ::+  (Backend a) =>+  -- | Initial state of the backend.+  a ->+  -- | Display config.+  Display ->+  -- | Background color.+  Color ->+  -- | Make the picture to draw.+  IO Picture ->+  -- | Eat the controller+  (Controller -> IO ()) ->+  IO () displayWithBackend   backend   displayMode@@ -85,4 +85,7 @@                       let viewState' = viewState{viewStateViewPort = port'}                       writeIORef viewSR viewState'                       postRedisplay backendRef+              , controllerSetCursor =+                  \cursorShape ->+                    setCursor backendRef cursorShape               }
Brillo/Internals/Interface/Event.hs view
@@ -4,6 +4,8 @@   Event (..),   keyMouseEvent,   motionEvent,+  dropEvent,+  pickEvent, ) where @@ -16,38 +18,60 @@   = EventKey Key KeyState Modifiers (Float, Float)   | EventMotion (Float, Float)   | EventResize (Int, Int)+  | EventDrop [FilePath]+  | EventPick [FilePath]   deriving (Eq, Show)  -keyMouseEvent-  :: forall a-   . (Backend a)-  => IORef a-  -> Key-  -> KeyState-  -> Modifiers-  -> (Int, Int)-  -> IO Event+keyMouseEvent ::+  forall a.+  (Backend a) =>+  IORef a ->+  Key ->+  KeyState ->+  Modifiers ->+  (Int, Int) ->+  IO Event keyMouseEvent backendRef key keyState modifiers pos =   EventKey key keyState modifiers <$> convertPoint backendRef pos  -motionEvent-  :: forall a-   . (Backend a)-  => IORef a-  -> (Int, Int)-  -> IO Event+motionEvent ::+  forall a.+  (Backend a) =>+  IORef a ->+  (Int, Int) ->+  IO Event motionEvent backendRef pos =   EventMotion <$> convertPoint backendRef pos  -convertPoint-  :: forall a-   . (Backend a)-  => IORef a-  -> (Int, Int)-  -> IO (Float, Float)+dropEvent ::+  forall a.+  (Backend a) =>+  IORef a ->+  [FilePath] ->+  IO Event+dropEvent _backendRef paths =+  return $ EventDrop paths+++pickEvent ::+  forall a.+  (Backend a) =>+  IORef a ->+  [FilePath] ->+  IO Event+pickEvent _backendRef paths =+  return $ EventPick paths+++convertPoint ::+  forall a.+  (Backend a) =>+  IORef a ->+  (Int, Int) ->+  IO (Float, Float) convertPoint backendRef pos =   do     (sizeX_, sizeY_) <- getWindowDimensions backendRef
Brillo/Internals/Interface/Game.hs view
@@ -1,5 +1,9 @@ {-# LANGUAGE RankNTypes #-}+{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-} +{-# HLINT ignore "Use camelCase" #-}+{-# HLINT ignore "Use list comprehension" #-}+ module Brillo.Internals.Interface.Game (   playWithBackendIO,   Event (..),@@ -24,29 +28,30 @@ import System.Mem  -playWithBackendIO-  :: forall world a-   . (Backend a)-  => a-  -- ^ Initial state of the backend-  -> Display-  -- ^ Display mode.-  -> Color-  -- ^ Background color.-  -> Int-  -- ^ Number of simulation steps to take for each second of real time.-  -> world-  -- ^ The initial world.-  -> (world -> IO Picture)-  -- ^ A function to convert the world to a picture.-  -> (Event -> world -> IO world)-  -- ^ A function to handle input events.-  -> (Float -> world -> IO world)-  -- ^ A function to step the world one iteration.-  --   It is passed the period of time (in seconds) needing to be advanced.-  -> Bool-  -- ^ Whether to use the callback_exit or not.-  -> IO ()+playWithBackendIO ::+  forall world a.+  (Backend a) =>+  -- | Initial state of the backend+  a ->+  -- | Display mode.+  Display ->+  -- | Background color.+  Color ->+  -- | Number of simulation steps to take for each second of real time.+  Int ->+  -- | The initial world.+  world ->+  -- | A function to convert the world to a picture.+  (world -> IO Picture) ->+  -- | A function to handle input events.+  (Event -> world -> IO world) ->+  {-| A function to step the world one iteration.+  It is passed the period of time (in seconds) needing to be advanced.+  -}+  (Float -> world -> IO world) ->+  -- | Whether to use the callback_exit or not.+  Bool ->+  IO () playWithBackendIO   backend   display@@ -105,11 +110,12 @@                     animateSR                     (readIORef viewSR)                     worldSR-                    (\_ -> worldAdvance)+                    (const worldAdvance)                     singleStepTime                 )             , callback_keyMouse worldSR viewSR worldHandleEvent             , callback_motion worldSR worldHandleEvent+            , callback_drop worldSR worldHandleEvent             , callback_reshape worldSR worldHandleEvent             ] @@ -125,22 +131,22 @@   -- | Callback for KeyMouse events.-callback_keyMouse-  :: IORef world-  -- ^ ref to world state-  -> IORef ViewPort-  -> (Event -> world -> IO world)-  -- ^ fn to handle input events-  -> Callback+callback_keyMouse ::+  -- | ref to world state+  IORef world ->+  IORef ViewPort ->+  -- | fn to handle input events+  (Event -> world -> IO world) ->+  Callback callback_keyMouse worldRef viewRef eventFn =   KeyMouse (handle_keyMouse worldRef viewRef eventFn)  -handle_keyMouse-  :: IORef a-  -> t-  -> (Event -> a -> IO a)-  -> KeyboardMouseCallback+handle_keyMouse ::+  IORef a ->+  t ->+  (Event -> a -> IO a) ->+  KeyboardMouseCallback handle_keyMouse worldRef _ eventFn backendRef key keyState keyMods pos =   do     ev <- keyMouseEvent backendRef key keyState keyMods pos@@ -150,20 +156,20 @@   -- | Callback for Motion events.-callback_motion-  :: IORef world-  -- ^ ref to world state-  -> (Event -> world -> IO world)-  -- ^ fn to handle input events-  -> Callback+callback_motion ::+  -- | ref to world state+  IORef world ->+  -- | fn to handle input events+  (Event -> world -> IO world) ->+  Callback callback_motion worldRef eventFn =   Motion (handle_motion worldRef eventFn)  -handle_motion-  :: IORef a-  -> (Event -> a -> IO a)-  -> MotionCallback+handle_motion ::+  IORef a ->+  (Event -> a -> IO a) ->+  MotionCallback handle_motion worldRef eventFn backendRef pos =   do     ev <- motionEvent backendRef pos@@ -172,19 +178,38 @@     writeIORef worldRef world'  +callback_drop ::+  IORef world ->+  (Event -> world -> IO world) ->+  Callback+callback_drop worldRef eventFn =+  Drop (handle_drop worldRef eventFn)+++handle_drop ::+  IORef a ->+  (Event -> a -> IO a) ->+  DropCallback+handle_drop worldRef eventFn backendRef paths = do+  ev <- dropEvent backendRef paths+  world <- readIORef worldRef+  world' <- eventFn ev world+  writeIORef worldRef world'++ -- | Callback for Handle reshape event.-callback_reshape-  :: IORef world-  -> (Event -> world -> IO world)-  -> Callback+callback_reshape ::+  IORef world ->+  (Event -> world -> IO world) ->+  Callback callback_reshape worldRef eventFN =   Reshape (handle_reshape worldRef eventFN)  -handle_reshape-  :: IORef world-  -> (Event -> world -> IO world)-  -> ReshapeCallback+handle_reshape ::+  IORef world ->+  (Event -> world -> IO world) ->+  ReshapeCallback handle_reshape worldRef eventFn stateRef (width, height) =   do     world <- readIORef worldRef
Brillo/Internals/Interface/Interact.hs view
@@ -1,5 +1,8 @@ {-# LANGUAGE RankNTypes #-}+{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-} +{-# HLINT ignore "Use camelCase" #-}+ module Brillo.Internals.Interface.Interact (interactWithBackend) where @@ -18,23 +21,23 @@ import System.Mem  -interactWithBackend-  :: (Backend a)-  => a-  -- ^ Initial state of the backend.-  -> Display-  -- ^ Display config.-  -> Color-  -- ^ Background color.-  -> world-  -- ^ The initial world.-  -> (world -> IO Picture)-  -- ^ A function to produce the current picture.-  -> (Event -> world -> IO world)-  -- ^ A function to handle input events.-  -> (Controller -> IO ())-  -- ^ Eat the controller-  -> IO ()+interactWithBackend ::+  (Backend a) =>+  -- | Initial state of the backend.+  a ->+  -- | Display config.+  Display ->+  -- | Background color.+  Color ->+  -- | The initial world.+  world ->+  -- | A function to produce the current picture.+  (world -> IO Picture) ->+  -- | A function to handle input events.+  (Event -> world -> IO world) ->+  -- | Eat the controller+  (Controller -> IO ()) ->+  IO () interactWithBackend   backend   displayMode@@ -74,6 +77,7 @@             , -- Viewport control with mouse               callback_keyMouse worldSR viewSR worldHandleEvent             , callback_motion worldSR worldHandleEvent+            , callback_drop worldSR worldHandleEvent             , callback_reshape worldSR worldHandleEvent             ] @@ -94,26 +98,29 @@                       let viewState' = viewState{viewStateViewPort = port'}                       writeIORef viewSR viewState'                       postRedisplay backendRef+              , controllerSetCursor =+                  \cursorShape ->+                    setCursor backendRef cursorShape               }   -- | Callback for KeyMouse events.-callback_keyMouse-  :: IORef world-  -- ^ ref to world state-  -> IORef ViewState-  -> (Event -> world -> IO world)-  -- ^ fn to handle input events-  -> Callback+callback_keyMouse ::+  -- | ref to world state+  IORef world ->+  IORef ViewState ->+  -- | fn to handle input events+  (Event -> world -> IO world) ->+  Callback callback_keyMouse worldRef viewRef eventFn =   KeyMouse (handle_keyMouse worldRef viewRef eventFn)  -handle_keyMouse-  :: IORef a-  -> t-  -> (Event -> a -> IO a)-  -> KeyboardMouseCallback+handle_keyMouse ::+  IORef a ->+  t ->+  (Event -> a -> IO a) ->+  KeyboardMouseCallback handle_keyMouse worldRef _ eventFn backendRef key keyState keyMods pos =   do     ev <- keyMouseEvent backendRef key keyState keyMods pos@@ -124,20 +131,20 @@   -- | Callback for Motion events.-callback_motion-  :: IORef world-  -- ^ ref to world state-  -> (Event -> world -> IO world)-  -- ^ fn to handle input events-  -> Callback+callback_motion ::+  -- | ref to world state+  IORef world ->+  -- | fn to handle input events+  (Event -> world -> IO world) ->+  Callback callback_motion worldRef eventFn =   Motion (handle_motion worldRef eventFn)  -handle_motion-  :: IORef a-  -> (Event -> a -> IO a)-  -> MotionCallback+handle_motion ::+  IORef a ->+  (Event -> a -> IO a) ->+  MotionCallback handle_motion worldRef eventFn backendRef pos =   do     ev <- motionEvent backendRef pos@@ -147,19 +154,38 @@     postRedisplay backendRef  +callback_drop ::+  IORef world ->+  (Event -> world -> IO world) ->+  Callback+callback_drop worldRef eventFn =+  Drop (handle_drop worldRef eventFn)+++handle_drop ::+  IORef a ->+  (Event -> a -> IO a) ->+  DropCallback+handle_drop worldRef eventFn backendRef paths = do+  ev <- dropEvent backendRef paths+  world <- readIORef worldRef+  world' <- eventFn ev world+  writeIORef worldRef world'++ -- | Callback for Handle reshape event.-callback_reshape-  :: IORef world-  -> (Event -> world -> IO world)-  -> Callback+callback_reshape ::+  IORef world ->+  (Event -> world -> IO world) ->+  Callback callback_reshape worldRef eventFN =   Reshape (handle_reshape worldRef eventFN)  -handle_reshape-  :: IORef world-  -> (Event -> world -> IO world)-  -> ReshapeCallback+handle_reshape ::+  IORef world ->+  (Event -> world -> IO world) ->+  ReshapeCallback handle_reshape worldRef eventFn backendRef (width, height) =   do     world <- readIORef worldRef
Brillo/Internals/Interface/Simulate.hs view
@@ -24,26 +24,27 @@ import System.Mem  -simulateWithBackendIO-  :: forall model a-   . (Backend a)-  => a-  -- ^ Initial state of the backend-  -> Display-  -- ^ Display mode.-  -> Color-  -- ^ Background color.-  -> Int-  -- ^ Number of simulation steps to take for each second of real time.-  -> model-  -- ^ The initial model.-  -> (model -> IO Picture)-  -- ^ A function to convert the model to a picture.-  -> (ViewPort -> Float -> model -> IO model)-  -- ^ A function to step the model one iteration. It is passed the-  --     current viewport and the amount of time for this simulation-  --     step (in seconds).-  -> IO ()+simulateWithBackendIO ::+  forall model a.+  (Backend a) =>+  -- | Initial state of the backend+  a ->+  -- | Display mode.+  Display ->+  -- | Background color.+  Color ->+  -- | Number of simulation steps to take for each second of real time.+  Int ->+  -- | The initial model.+  model ->+  -- | A function to convert the model to a picture.+  (model -> IO Picture) ->+  {-| A function to step the model one iteration. It is passed the+    current viewport and the amount of time for this simulation+    step (in seconds).+  -}+  (ViewPort -> Float -> model -> IO model) ->+  IO () simulateWithBackendIO   backend   display
Brillo/Internals/Interface/Simulate/Idle.hs view
@@ -17,36 +17,38 @@ {-| The graphics library calls back on this function when it's finished drawing      and it's time to do some computation. -}-callback_simulate_idle-  :: IORef SM.State-  -- ^ the simulation state-  -> IORef AN.State-  -- ^ the animation statea-  -> IO ViewPort-  -- ^ action to get the 'ViewPort'.  We don't use an 'IORef'-  -- directly because sometimes we hold a ref to a 'ViewPort' (in-  -- Game) and sometimes a ref to a 'ViewState'.-  -> IORef world-  -- ^ the current world-  -> (ViewPort -> Float -> world -> IO world)-  -- ^ fn to advance the world-  -> Float-  -- ^ how much time to advance world by-  --      in single step mode-  -> IdleCallback+callback_simulate_idle ::+  -- | the simulation state+  IORef SM.State ->+  -- | the animation statea+  IORef AN.State ->+  {-| action to get the 'ViewPort'.  We don't use an 'IORef'+  directly because sometimes we hold a ref to a 'ViewPort' (in+  Game) and sometimes a ref to a 'ViewState'.+  -}+  IO ViewPort ->+  -- | the current world+  IORef world ->+  -- | fn to advance the world+  (ViewPort -> Float -> world -> IO world) ->+  {-| how much time to advance world by+     in single step mode+  -}+  Float ->+  IdleCallback callback_simulate_idle simSR animateSR viewSA worldSR worldAdvance _singleStepTime backendRef =   {-# SCC "callbackIdle" #-}   do simulate_run simSR animateSR viewSA worldSR worldAdvance backendRef   -- take the number of steps specified by controlWarp-simulate_run-  :: IORef SM.State-  -> IORef AN.State-  -> IO ViewPort-  -> IORef world-  -> (ViewPort -> Float -> world -> IO world)-  -> IdleCallback+simulate_run ::+  IORef SM.State ->+  IORef AN.State ->+  IO ViewPort ->+  IORef world ->+  (ViewPort -> Float -> world -> IO world) ->+  IdleCallback simulate_run simSR _ viewSA worldSR worldAdvance backendRef =   do     viewS <- viewSA
Brillo/Internals/Interface/ViewState/KeyMouse.hs view
@@ -13,9 +13,9 @@ {-| Callback to handle keyboard and mouse button events      for controlling the 'ViewState'. -}-callback_viewState_keyMouse-  :: IORef ViewState-  -> Callback+callback_viewState_keyMouse ::+  IORef ViewState ->+  Callback callback_viewState_keyMouse viewStateRef =   KeyMouse (viewState_keyMouse viewStateRef) 
Brillo/Internals/Interface/ViewState/Motion.hs view
@@ -14,9 +14,9 @@ {-| Callback to handle keyboard and mouse button events      for controlling the viewport. -}-callback_viewState_motion-  :: IORef ViewState-  -> Callback+callback_viewState_motion ::+  IORef ViewState ->+  Callback callback_viewState_motion portRef =   Motion (viewState_motion portRef) 
Brillo/Internals/Interface/Window.hs view
@@ -15,18 +15,19 @@   -- | Open a window and use the supplied callbacks to handle window events.-createWindow-  :: (Backend a)-  => a-  -> Display-  -> Color-  -- ^ Color to use when clearing.-  -> [Callback]-  -- ^ Callbacks to use.-  -> (IORef a -> IO ())-  -- ^ Give the backend back to the caller before-  --   entering the main loop.-  -> IO ()+createWindow ::+  (Backend a) =>+  a ->+  Display ->+  -- | Color to use when clearing.+  Color ->+  -- | Callbacks to use.+  [Callback] ->+  {-| Give the backend back to the caller before+  entering the main loop.+  -}+  (IORef a -> IO ()) ->+  IO () createWindow   backend   display@@ -57,8 +58,10 @@       installDisplayCallback backendStateRef callbacks       installWindowCloseCallback backendStateRef       installReshapeCallback backendStateRef callbacks+      installRefreshCallback backendStateRef callbacks       installKeyMouseCallback backendStateRef callbacks       installMotionCallback backendStateRef callbacks+      installDropCallback backendStateRef callbacks       installIdleCallback backendStateRef callbacks        -- we don't need the depth buffer for 2d.@@ -77,11 +80,10 @@       eatBackend backendStateRef        when debug $-        do putStr $ "* entering mainloop..\n"+        do putStr "* entering mainloop..\n"        -- Start the main backend loop       runMainLoop backendStateRef        when debug $-        putStr $-          "* all done\n"+        putStr "* all done\n"
+ Brillo/Internals/TinyFileDialogs.chs view
@@ -0,0 +1,224 @@+{-# LANGUAGE LambdaCase #-}++module Brillo.Internals.TinyFileDialogs (+  -- * Functions+  beep,+  notifyPopup,+  messageBox,+  inputBox,+  saveFileDialog,+  openFileDialog,+  selectFolderDialog,+  colorChooser,+  -- * Message box options+  IconType(..),+  MessageBox,+  OK(..),+  OKCancel(..),+  YesNo(..),+  YesNoCancel(..),+)+where++import Prelude (+  Applicative (pure),+  Bool (..),+  Bounded,+  Enum,+  Eq ((==)),+  fromIntegral,+  Functor (fmap),+  id,+  Int,+  IO,+  maxBound,+  Maybe (Just, Nothing),+  minBound,+  Ord,+  Read,+  return,+  Semigroup ((<>)),+  show,+  Show,+  (.),+  (<>),+  (>>),+  (>>=),+  ($),+ )++import Data.List (lookup)+import Data.Char (toLower)+import Data.Text qualified as T+import Foreign (Ptr, Word8, nullPtr, peekArray, withArray, withArrayLen, withMany)+import Foreign.C (CInt, CString, CUChar)+import System.Exit (die)++#ifdef WINDOWS+import Data.ByteString qualified as B+import Data.Text.Encoding qualified as TE+#else+import Foreign.C (peekCString, withCString)+#endif++#include "tinyfiledialogs.h"++{#context prefix = "tinyfd_" #}++withCText :: T.Text -> (CString -> IO a) -> IO a+#ifdef WINDOWS+withCText = B.useAsCString . TE.encodeUtf8+#else+withCText = withCString . T.unpack+#endif++withCShowLower :: (Show a) => a -> (CString -> IO b) -> IO b+withCShowLower = withCText . T.pack . fmap toLower . show++withCMaybeText :: Maybe T.Text -> (CString -> IO a) -> IO a+withCMaybeText mt f = case mt of+  Nothing -> f nullPtr+  Just t  -> withCText t f++peekMaybeText :: CString -> IO (Maybe T.Text)+peekMaybeText cstr = if cstr == nullPtr+  then pure Nothing+#ifdef WINDOWS+  else fmap (Just . TE.decodeUtf8) $ B.packCString cstr+#else+  else fmap (Just . T.pack) $ peekCString cstr+#endif++peekMaybeTextMultiple :: CString -> IO (Maybe [T.Text])+peekMaybeTextMultiple = fmap (fmap $ T.splitOn (T.singleton '|')) . peekMaybeText++withCTexts :: [T.Text] -> ((CInt, Ptr CString) -> IO a) -> IO a+withCTexts ts f = withMany withCText ts $ \ptrs ->+  withArrayLen ptrs $ \len ptr -> f (fromIntegral len, ptr)++class (Enum a, Bounded a) => MessageBox a where+  messageBoxType :: a -> T.Text+  messageBoxValue :: a -> Int++data IconType = Info | Warning | Error | Question+  deriving (Eq, Ord, Show, Read, Enum, Bounded)++data OK = OK+  deriving (Eq, Ord, Show, Read, Enum, Bounded)++instance MessageBox OK where+  messageBoxType _ = T.pack "ok"+  messageBoxValue OK = 1++data OKCancel = OC_OK | OC_Cancel+  deriving (Eq, Ord, Show, Read, Enum, Bounded)++instance MessageBox OKCancel where+  messageBoxType _ = T.pack "okcancel"+  messageBoxValue OC_Cancel = 0+  messageBoxValue OC_OK     = 1++data YesNo = YN_Yes | YN_No+  deriving (Eq, Ord, Show, Read, Enum, Bounded)++instance MessageBox YesNo where+  messageBoxType _ = T.pack "yesno"+  messageBoxValue YN_No  = 0+  messageBoxValue YN_Yes = 1++data YesNoCancel = YNC_Yes | YNC_No | YNC_Cancel+  deriving (Eq, Ord, Show, Read, Enum, Bounded)++instance MessageBox YesNoCancel where+  messageBoxType _ = T.pack "yesnocancel"+  messageBoxValue YNC_Cancel = 0+  messageBoxValue YNC_Yes    = 1+  messageBoxValue YNC_No     = 2++{#fun messageBox as c_messageBox+  { withCText*      `T.Text'     -- ^ title+  , withCText*      `T.Text'     -- ^ message, may contain @\\n@ and @\\t@+  , withCText*      `T.Text'     -- ^ @"ok" "okcancel" "yesno" "yesnocancel"@+  , withCShowLower* `IconType'   -- ^ 'Info', 'Warning', 'Error', 'Question'+  ,                 `Int'        -- ^ default button: 0 for cancel/no, 1 for ok/yes, 2 for no in yesnocancel+  } -> `Int' -- ^ 0 for cancel/no, 1 for ok/yes, 2 for no in yesnocancel+#}++{#fun notifyPopup+  { withCText*      `T.Text'   -- ^ title+  , withCText*      `T.Text'   -- ^ message, may contain @\\n@ and @\\t@+  , withCShowLower* `IconType' -- ^ 'Info', 'Warning', 'Error'+  } -> `()'+#}++{#fun beep {} -> `()' #}++messageBox+  :: (MessageBox a)+  => T.Text -- ^ title+  -> T.Text -- ^ message, may contain @\\n@ and @\\t@+  -> IconType -- ^ 'Info', 'Warning', 'Error', 'Question'+  -> a -- ^ default button+  -> IO a+messageBox ttl msg icon dflt = do+  n <- c_messageBox ttl msg (messageBoxType dflt) icon (messageBoxValue dflt)+  case lookup n [ (messageBoxValue x, x) | x <- [minBound .. maxBound] ] of+    Just x  -> pure x+    Nothing -> die $ "TinyFileDialogs.messageBox: "+      <> "internal error; unrecognized return value " <> show n++{#fun inputBox+  { withCText*      `T.Text'       -- ^ title+  , withCText*      `T.Text'       -- ^ message, may NOT contain @\\n@ and @\\t@ on windows+  , withCMaybeText* `Maybe T.Text' -- ^ default input, if 'Nothing' it's a passwordBox+  } -> `Maybe T.Text' peekMaybeText* -- ^ returns 'Nothing' on cancel+#}++{#fun saveFileDialog+  { withCText*  `T.Text'    -- ^ title+  , withCText*  `T.Text'    -- ^ default path and file+  , withCTexts* `[T.Text]'& -- ^ filter patterns, @["*.jpg","*.png"]@+  , withCText*  `T.Text'    -- ^ single filter description, @"text files"@+  } -> `Maybe T.Text' peekMaybeText* -- ^ returns 'Nothing' on cancel+#}++{#fun openFileDialog+  { withCText*  `T.Text'    -- ^ title+  , withCText*  `T.Text'    -- ^ default path and file+  , withCTexts* `[T.Text]'& -- ^ filter patterns, @["*.jpg","*.png"]@+  , withCText*  `T.Text'    -- ^ single filter description, @"text files"@+  ,             `Bool'      -- ^ allow multiple selects+  } -> `Maybe [T.Text]' peekMaybeTextMultiple* -- ^ returns 'Nothing' on cancel+#}++{#fun selectFolderDialog+  { withCText* `T.Text' -- ^ title+  , withCText* `T.Text' -- ^ default path+  } -> `Maybe T.Text' peekMaybeText* -- ^ returns 'Nothing' on cancel+#}++{#fun colorChooser as c_colorChooser+  { withCText*      `T.Text'+  , withCMaybeText* `Maybe T.Text'+  , id              `Ptr CUChar'+  , id              `Ptr CUChar'+  } -> `Maybe T.Text' peekMaybeText* -- ^ returns 'Nothing' on cancel+#}++withColor :: (Word8, Word8, Word8) -> (Ptr CUChar -> IO a) -> IO a+withColor (r, g, b) = withArray $ fmap fromIntegral [r, g, b]++colorChooser+  :: T.Text                           -- ^ title+  -> (Word8, Word8, Word8)            -- ^ default RGB color+  -> IO (Maybe (Word8, Word8, Word8)) -- ^ returns 'Nothing' on cancel+colorChooser title color = withColor color $ \ptr -> do+  res <- c_colorChooser title Nothing ptr ptr+  case res of+    Nothing -> pure Nothing+    Just _  -> fmap+      ((\case+        [r, g, b] -> Just (r, g, b)+        _        -> Nothing+        ) . fmap fromIntegral)+      (peekArray 3 ptr)
brillo.cabal view
@@ -1,14 +1,14 @@-cabal-version: 3.0-name:          brillo-version:       1.13.3-license:       MIT-license-file:  LICENSE-author:        Ben Lippmeier, Adrian Sieber-maintainer:    brillo@ad-si.com-build-type:    Simple-stability:     stable-category:      Graphics-homepage:      https://github.com/ad-si/Brillo+cabal-version:      3.0+name:               brillo+version:            2.0.0+license:            MIT+license-file:       LICENSE+author:             Ben Lippmeier, Adrian Sieber+maintainer:         brillo@ad-si.com+build-type:         Simple+stability:          stable+category:           Graphics+homepage:           https://github.com/ad-si/Brillo description:   Brillo hides the pain of drawing simple vector graphics   behind a nice data type and a few display functions.@@ -19,35 +19,53 @@ synopsis:   Painless 2D vector graphics, animations, and simulations powered by GLFW +tested-with:+  GHC ==9.4.8 || ==9.6.7 || ==9.8.4 || ==9.10.3 || ==9.12.2++extra-source-files: cbits/tinyfiledialogs.h+ source-repository head   type:     git   location: https://github.com/ad-si/Brillo  library-  default-language: GHC2021+  default-language:   GHC2021   build-depends:-    , base              >=4.8    && <5-    , bmp               >=1.2    && <1.3-    , brillo-rendering  >=1.13.3 && <1.15-    , bytestring        >=0.11   && <0.12-    , containers        >=0.5    && <0.7-    , ghc-prim-    , GLFW-b            >=3.3    && <4-    , OpenGL            >=2.12   && <3.1+    , base              >=4.8  && <5+    , brillo-rendering  >=2.0  && <3.0+    , containers        >=0.5  && <0.9+    , GLFW-b            >=3.3  && <4+    , OpenGL            >=2.12 && <3.1+    , text              >=2.0  && <2.2 -  ghc-options:      -O2 -Wall+  build-tool-depends: c2hs:c2hs+  include-dirs:       cbits/+  c-sources:          cbits/tinyfiledialogs.c+  cc-options:         -DHSFORCECONSOLE=0+  ghc-options:        -O2 -Wall++  if os(windows)+    extra-libraries:+      comdlg32+      ole32+      shell32+      user32+   exposed-modules:     Brillo     Brillo.Data.Bitmap     Brillo.Data.Color     Brillo.Data.Controller+    Brillo.Data.Cursor     Brillo.Data.Display+    Brillo.Data.FileDialog     Brillo.Data.Picture     Brillo.Data.Point     Brillo.Data.Point.Arithmetic     Brillo.Data.Vector     Brillo.Data.ViewPort     Brillo.Data.ViewState+    Brillo.Geometry     Brillo.Geometry.Angle     Brillo.Geometry.Line     Brillo.Interface.Environment@@ -83,5 +101,4 @@     Brillo.Internals.Interface.ViewState.Motion     Brillo.Internals.Interface.ViewState.Reshape     Brillo.Internals.Interface.Window--  cpp-options:      -DWITHGLFW+    Brillo.Internals.TinyFileDialogs
+ cbits/tinyfiledialogs.c view
@@ -0,0 +1,8401 @@+/* SPDX-License-Identifier: Zlib+Copyright (c) 2014 - 2025 Guillaume Vareille http://ysengrin.com+	 ________________________________________________________________+	|                                                                |+	| 100% compatible C C++  ->  You can rename this .c file as .cpp |+	| 100% compatible C C++  ->  You can rename this .c file as .cpp |+	| 100% compatible C C++  ->  You can rename this .c file as .cpp |+	| 100% compatible C C++  ->  You can rename this .c file as .cpp |+	| 100% compatible C C++  ->  You can rename this .c file as .cpp |+	|________________________________________________________________|++********* TINY FILE DIALOGS OFFICIAL WEBSITE IS ON SOURCEFORGE *********+  _________+ /         \ tinyfiledialogs.c v3.21.2 [Oct 25, 2025] zlib licence+ |tiny file| Unique code file created [November 9, 2014]+ | dialogs |+ \____  ___/ http://tinyfiledialogs.sourceforge.net+	  \|     git clone http://git.code.sf.net/p/tinyfiledialogs/code tinyfd+			  ____________________________________________+			 |                                            |+			 |   email: tinyfiledialogs at ysengrin.com   |+			 |____________________________________________|+  _________________________________________________________________________________+ |                                                                                 |+ | the windows only wchar_t UTF-16 prototypes are at the bottom of the header file |+ |_________________________________________________________________________________|+  _________________________________________________________+ |                                                         |+ | on windows: - since v3.6 char is UTF-8 by default       |+ |             - if you want MBCS set tinyfd_winUtf8 to 0  |+ |             - functions like fopen expect MBCS          |+ |_________________________________________________________|++If you like tinyfiledialogs, please upvote my stackoverflow answer+https://stackoverflow.com/a/47651444++- License -+This software is provided 'as-is', without any express or implied+warranty.  In no event will the authors be held liable for any damages+arising from the use of this software.+Permission is granted to anyone to use this software for any purpose,+including commercial applications, and to alter it and redistribute it+freely, subject to the following restrictions:+1. The origin of this software must not be misrepresented; you must not+claim that you wrote the original software.  If you use this software+in a product, an acknowledgment in the product documentation would be+appreciated but is not required.+2. Altered source versions must be plainly marked as such, and must not be+misrepresented as being the original software.+3. This notice may not be removed or altered from any source distribution.++     __________________________________________+    |  ______________________________________  |+    | |                                      | |+    | | DO NOT USE USER INPUT IN THE DIALOGS | |+    | |______________________________________| |+    |__________________________________________|+*/+++#if defined(__GNUC__) || defined(__clang__)+#ifndef _GNU_SOURCE+ #define _GNU_SOURCE /* used only to resolve symbolic links. Can be commented out */+ #ifndef _POSIX_C_SOURCE+  #ifdef __NetBSD__+    #define _POSIX_C_SOURCE 200112L+    #define _XOPEN_SOURCE 600 /* https://pubs.opengroup.org/onlinepubs/007904875/functions/xsh_chap02_02.html */+  #elif defined(__FreeBSD__)+    #define _POSIX_C_SOURCE 199506L /* 199506L is enough for freebsd for realpath() */+  #elif defined(__illumos__) || defined(__solaris__)+    #define _POSIX_C_SOURCE 200112L /* illumos/solaris needs 200112L for realpath() */+  #else+    #define _POSIX_C_SOURCE 2 /* to accept POSIX 2 in old ANSI C standards */+  #endif+ #endif+#endif+#endif++#include <stdio.h>+#include <stdlib.h>+#include <string.h>+#include <ctype.h>+#include <sys/stat.h>++#ifdef _WIN32+ #ifdef __BORLANDC__+  #define _getch getch+ #endif+ #ifndef _WIN32_WINNT+  #define _WIN32_WINNT 0x0500+ #endif+ #include <windows.h>+ #include <commdlg.h>+ #include <shlobj.h>+ #include <conio.h>+ #include <direct.h>+ #define TINYFD_NOCCSUNICODE+ #define TINYFD_SLASH "\\"+#else+ #include <limits.h>+ #include <unistd.h>+ #include <dirent.h> /* on old systems try <sys/dir.h> instead */+ #include <termios.h>+ #include <sys/utsname.h>+ #include <signal.h> /* on old systems try <sys/signal.h> instead */+ #define TINYFD_SLASH "/"+#endif /* _WIN32 */++#include "tinyfiledialogs.h"++#define MAX_PATH_OR_CMD 1024 /* _MAX_PATH or MAX_PATH */++#ifndef MAX_MULTIPLE_FILES+#define MAX_MULTIPLE_FILES 1024+#endif+#define LOW_MULTIPLE_FILES 32++char tinyfd_version[8] = "3.21.2";++/******************************************************************************************************/+/**************************************** UTF-8 on Windows ********************************************/+/******************************************************************************************************/+#ifdef _WIN32+/* if you want to use UTF-8 ( instead of the UTF-16/wchar_t functions at the end of tinyfiledialogs.h )+Make sure your code is really prepared for UTF-8 (on windows, functions like fopen() expect MBCS and not UTF-8) */+int tinyfd_winUtf8 = 1; /* on windows char strings can be 1:UTF-8(default) or 0:MBCS */+/* for MBCS change this to 0, here or in your code */+#endif+/******************************************************************************************************/+/******************************************************************************************************/+/******************************************************************************************************/++int tinyfd_verbose = 0 ; /* on unix: prints the command line calls */+int tinyfd_silent = 1 ; /* 1 (default) or 0 : on unix, hide errors and warnings from called dialogs */++/* Curses dialogs are difficult to use, on windows they are only ascii and uses the unix backslah */+int tinyfd_allowCursesDialogs = 0 ; /* 0 (default) or 1 */+int tinyfd_forceConsole = 0 ; /* 0 (default) or 1 */+/* for unix & windows: 0 (graphic mode) or 1 (console mode).+0: try to use a graphic solution, if it fails then it uses console mode.+1: forces all dialogs into console mode even when the X server is present.+   it can use the package dialog or dialog.exe.+   on windows it only make sense for console applications */++/* int tinyfd_assumeGraphicDisplay = 0; */ /* 0 (default) or 1  */+/* some systems don't set the environment variable DISPLAY even when a graphic display is present.+set this to 1 to tell tinyfiledialogs to assume the existence of a graphic display */+++char tinyfd_response[1024];+/* if you pass "tinyfd_query" as aTitle,+the functions will not display the dialogs+but return 0 for console mode, 1 for graphic mode.+tinyfd_response is then filled with the retain solution.+possible values for tinyfd_response are (all lowercase)+for graphic mode:+  windows_wchar windows applescript kdialog zenity zenity3 yad matedialog+  shellementary qarma shanty boxer python2-tkinter python3-tkinter python-dbus+  perl-dbus gxmessage gmessage xmessage xdialog gdialog dunst+for console mode:+  dialog whiptail basicinput no_solution */++static int gWarningDisplayed = 0 ;+static char gTitle[]="missing software! (we will try basic console input)";++#ifdef _WIN32+char tinyfd_needs[] = "\+ ___________\n\+/           \\ \n\+| tiny file |\n\+|  dialogs  |\n\+\\_____  ____/\n\+      \\|\+\ntiny file dialogs on Windows needs:\+\n   a graphic display\+\nor dialog.exe (curses console mode  ** Disabled by default **)\+\nor a console for basic input";+#else+char tinyfd_needs[] = "\+ ___________\n\+/           \\ \n\+| tiny file |\n\+|  dialogs  |\n\+\\_____  ____/\n\+      \\|\+\ntiny file dialogs on UNIX needs:\+\n   applescript or kdialog or yad or Xdialog\+\nor zenity (or matedialog, shellementary, qarma, shanty, boxer)\+\nor python (2 or 3) + tkinter + python-dbus (optional)\+\nor dialog (opens console if needed) ** Disabled by default **\+\nor xterm + bash (opens console for basic input)\+\nor existing console for basic input.";++#endif+++#ifdef _MSC_VER+#pragma warning(disable:4996) /* allows usage of strncpy, strcpy, strcat, sprintf, fopen */+#pragma warning(disable:4100) /* allows usage of strncpy, strcpy, strcat, sprintf, fopen */+#pragma warning(disable:4706) /* allows usage of strncpy, strcpy, strcat, sprintf, fopen */+#endif+++static int getenvDISPLAY(void)+{+	/* return tinyfd_assumeGraphicDisplay || getenv("DISPLAY") || getenv("WAYLAND_DISPLAY") ; */+	static int lReturnValue = -1 ;++	if ( lReturnValue < 0 )+	{+		lReturnValue = 0 ;+		if ( getenv("DISPLAY") ) lReturnValue += 1 ;+		if ( getenv("WAYLAND_DISPLAY") ) lReturnValue += 2 ;+	}++	return lReturnValue ;+}+++static char * getCurDir(void)+{+		static char lCurDir[MAX_PATH_OR_CMD];+		return getcwd(lCurDir, sizeof(lCurDir));+}+++static char * getPathWithoutFinalSlash(+		char * aoDestination, /* make sure it is allocated, use _MAX_PATH */+		char const * aSource) /* aoDestination and aSource can be the same */+{+		char const * lTmp ;+		if ( aSource )+		{+				lTmp = strrchr(aSource, '/');+				if (!lTmp)+				{+						lTmp = strrchr(aSource, '\\');+				}+				if (lTmp)+				{+						strncpy(aoDestination, aSource, lTmp - aSource );+						aoDestination[lTmp - aSource] = '\0';+				}+				else+				{+						* aoDestination = '\0';+				}+		}+		else+		{+				* aoDestination = '\0';+		}+		return aoDestination;+}+++static char * getLastName(+		char * aoDestination, /* make sure it is allocated */+		char const * aSource)+{+		/* copy the last name after '/' or '\' */+		char const * lTmp ;+		if ( aSource )+		{+				lTmp = strrchr(aSource, '/');+				if (!lTmp)+				{+						lTmp = strrchr(aSource, '\\');+				}+				if (lTmp)+				{+						strcpy(aoDestination, lTmp + 1);+				}+				else+				{+						strcpy(aoDestination, aSource);+				}+		}+		else+		{+				* aoDestination = '\0';+		}+		return aoDestination;+}+++static void ensureFinalSlash( char * aioString )+{+		if ( aioString && strlen( aioString ) )+		{+				char * lastcar = aioString + strlen( aioString ) - 1 ;+				if ( strncmp( lastcar , TINYFD_SLASH , 1 ) )+				{+						strcat( lastcar , TINYFD_SLASH ) ;+				}+		}+}+++static void Hex2RGB( char const aHexRGB[8] , unsigned char aoResultRGB[3] )+{+		char lColorChannel[8] ;+		if ( aoResultRGB )+		{+				if ( aHexRGB )+				{+						strcpy(lColorChannel, aHexRGB ) ;+						aoResultRGB[2] = (unsigned char)strtoul(lColorChannel+5,NULL,16);+						lColorChannel[5] = '\0';+						aoResultRGB[1] = (unsigned char)strtoul(lColorChannel+3,NULL,16);+						lColorChannel[3] = '\0';+						aoResultRGB[0] = (unsigned char)strtoul(lColorChannel+1,NULL,16);+/* printf("%d %d %d\n", aoResultRGB[0], aoResultRGB[1], aoResultRGB[2]); */+				}+				else+				{+						aoResultRGB[0]=0;+						aoResultRGB[1]=0;+						aoResultRGB[2]=0;+				}+		}+}+++static void RGB2Hex( unsigned char const aRGB[3], char aoResultHexRGB[8] )+{+	if ( aoResultHexRGB )+	{+		if ( aRGB )+		{+#if (defined(__cplusplus ) && __cplusplus >= 201103L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__clang__)+			sprintf(aoResultHexRGB, "#%02hhx%02hhx%02hhx", aRGB[0], aRGB[1], aRGB[2]);+#else+			sprintf(aoResultHexRGB, "#%02hx%02hx%02hx", aRGB[0], aRGB[1], aRGB[2]);+#endif+		 /*printf("aoResultHexRGB %s\n", aoResultHexRGB);*/+		}+		else+		{+			aoResultHexRGB[0]=0;+			aoResultHexRGB[1]=0;+			aoResultHexRGB[2]=0;+		}+	}+}+++void tfd_replaceSubStr( char const * aSource, char const * aOldSubStr,+						char const * aNewSubStr, char * aoDestination )+{+	char const * pOccurence ;+	char const * p ;+	char const * lNewSubStr = "" ;+	size_t lOldSubLen = strlen( aOldSubStr ) ;++	if ( ! aSource )+	{+		* aoDestination = '\0' ;+		return ;+	}+	if ( ! aOldSubStr )+	{+		strcpy( aoDestination , aSource ) ;+		return ;+	}+	if ( aNewSubStr )+	{+		lNewSubStr = aNewSubStr ;+	}+	p = aSource ;+	* aoDestination = '\0' ;+	while ( ( pOccurence = strstr( p , aOldSubStr ) ) != NULL )+	{+		strncat( aoDestination , p , pOccurence - p ) ;+		strcat( aoDestination , lNewSubStr ) ;+		p = pOccurence + lOldSubLen ;+	}+	strcat( aoDestination , p ) ;+}+++static int filenameValid( char const * aFileNameWithoutPath )+{+	if ( ! aFileNameWithoutPath+	  || ! strlen(aFileNameWithoutPath)+	  || strpbrk(aFileNameWithoutPath , "\\/:*?\"<>|") )+	{+		return 0 ;+	}+	return 1 ;+}++#ifndef _WIN32++static int fileExists( char const * aFilePathAndName )+{+	FILE * lIn ;+	if ( ! aFilePathAndName || ! strlen(aFilePathAndName) )+	{+			return 0 ;+	}+	lIn = ( FILE * ) fopen( aFilePathAndName , "r" ) ;+	if ( ! lIn )+	{+			return 0 ;+	}+	fclose( lIn ) ;+	return 1 ;+}++#endif+++static void wipefile(char const * aFilename)+{+	int i;+	struct stat st;+	FILE * lIn;++	if (stat(aFilename, &st) == 0)+	{+		lIn = ( FILE * ) fopen(aFilename, "w") ;+		if ( lIn )+		{+			for (i = 0; i < st.st_size; i++)+			{+					fputc('A', lIn);+			}+			fclose(lIn);+		}+	}+}+++int tfd_quoteDetected(char const * aString)+{+	char const * p;++	if (!aString) return 0;++	p = aString;+	if ( strchr(p, '\''))+	{+		return 1;+	}++	if ( strchr(p, '\"'))+	{+		return 1;+	}++	if ( strchr(p, '`'))+	{+		return 1;+	}++/*	p = aString;+	while ((p = strchr(p, '$')))+	{+		p ++ ;+		if ( ( * p == '(' ) || ( * p == '_' ) || isalpha( * p) ) return 1 ;+	}*/++	return 0;+}+++char const * tinyfd_getGlobalChar(char const * aCharVariableName) /* to be called from C# (you don't need this in C or C++) */+{+	if (!aCharVariableName || !strlen(aCharVariableName)) return NULL;+	else if (!strcmp(aCharVariableName, "tinyfd_version")) return tinyfd_version;+	else if (!strcmp(aCharVariableName, "tinyfd_needs")) return tinyfd_needs;+	else if (!strcmp(aCharVariableName, "tinyfd_response")) return tinyfd_response;+	else return NULL ;+}+++int tinyfd_getGlobalInt(char const * aIntVariableName) /* to be called from C# (you don't need this in C or C++) */+{+	if ( !aIntVariableName || !strlen(aIntVariableName) ) return -1 ;+	else if ( !strcmp(aIntVariableName, "tinyfd_verbose") ) return tinyfd_verbose ;+	else if ( !strcmp(aIntVariableName, "tinyfd_silent") ) return tinyfd_silent ;+	else if ( !strcmp(aIntVariableName, "tinyfd_allowCursesDialogs") ) return tinyfd_allowCursesDialogs ;+	else if ( !strcmp(aIntVariableName, "tinyfd_forceConsole") ) return tinyfd_forceConsole ;+	/* else if ( !strcmp(aIntVariableName, "tinyfd_assumeGraphicDisplay") ) return tinyfd_assumeGraphicDisplay ; */+#ifdef _WIN32+	else if ( !strcmp(aIntVariableName, "tinyfd_winUtf8") ) return tinyfd_winUtf8 ;+#endif+	else return -1;+}+++int tinyfd_setGlobalInt(char const * aIntVariableName, int aValue) /* to be called from C# (you don't need this in C or C++) */+{+	if (!aIntVariableName || !strlen(aIntVariableName)) return -1 ;+	else if (!strcmp(aIntVariableName, "tinyfd_verbose")) { tinyfd_verbose = aValue; return tinyfd_verbose; }+	else if (!strcmp(aIntVariableName, "tinyfd_silent")) { tinyfd_silent = aValue; return tinyfd_silent; }+	else if (!strcmp(aIntVariableName, "tinyfd_allowCursesDialogs")) { tinyfd_allowCursesDialogs = aValue; return tinyfd_allowCursesDialogs; }+	else if (!strcmp(aIntVariableName, "tinyfd_forceConsole")) { tinyfd_forceConsole = aValue; return tinyfd_forceConsole; }+	/* else if (!strcmp(aIntVariableName, "tinyfd_assumeGraphicDisplay")) { tinyfd_assumeGraphicDisplay = aValue; return tinyfd_assumeGraphicDisplay; } */+#ifdef _WIN32+	else if (!strcmp(aIntVariableName, "tinyfd_winUtf8")) { tinyfd_winUtf8 = aValue; return tinyfd_winUtf8; }+#endif+	else return -1;+}+++#ifdef _WIN32++static int powershellPresent(void)+{ /*only on vista and above (or installed on xp)*/+	static int lPowershellPresent = -1;+	char lBuff[MAX_PATH_OR_CMD];+	FILE* lIn;+	char const* lString = "powershell.exe";++	if (lPowershellPresent < 0)+	{+		lIn = ( FILE * ) _popen("where powershell.exe", "r") ;+		if (! lIn )+		{+			lPowershellPresent = 0;+			return 0;+		}+		while (fgets(lBuff, sizeof(lBuff), lIn) != NULL)+		{+		}+		_pclose(lIn);+		if (lBuff[strlen(lBuff) - 1] == '\n')+		{+			lBuff[strlen(lBuff) - 1] = '\0';+		}+		if (strcmp(lBuff + strlen(lBuff) - strlen(lString), lString))+		{+			lPowershellPresent = 0;+		}+		else+		{+			lPowershellPresent = 1;+		}+	}+	return lPowershellPresent;+}++static int windowsVersion(void)+{+#if ( !defined(__MINGW32__) || defined(__MINGW64_VERSION_MAJOR) ) && !defined(__DMC__)+	typedef LONG NTSTATUS  ;+	typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);+	HMODULE hMod;+	RtlGetVersionPtr lFxPtr;+	RTL_OSVERSIONINFOW lRovi = { 0 };++	hMod = GetModuleHandleW(L"ntdll.dll");+	if (hMod) {+		lFxPtr = (RtlGetVersionPtr)GetProcAddress(hMod, "RtlGetVersion");+		if (lFxPtr)+		{+			lRovi.dwOSVersionInfoSize = sizeof(lRovi);+			if (!lFxPtr(&lRovi))+			{+				return lRovi.dwMajorVersion;+			}+		}+	}+#endif+	if (powershellPresent()) return 6; /*minimum is vista or installed on xp*/+	return 0;+}+++static void replaceChr(char * aString, char aOldChr, char aNewChr)+{+	char * p;++	if (!aString) return;+	if (aOldChr == aNewChr) return;++	p = aString;+	while ((p = strchr(p, aOldChr)))+	{+		*p = aNewChr;+		p++;+	}+	return;+}+++#if !defined(WC_ERR_INVALID_CHARS)+/* undefined prior to Vista, so not yet in MINGW header file */+#define WC_ERR_INVALID_CHARS 0x00000000 /* 0x00000080 for MINGW maybe ? */+#endif++static int sizeUtf16From8(char const * aUtf8string)+{+	return MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,+			aUtf8string, -1, NULL, 0);+}+++static int sizeUtf16FromMbcs(char const * aMbcsString)+{+	return MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS,+			aMbcsString, -1, NULL, 0);+}+++static int sizeUtf8(wchar_t const * aUtf16string)+{+	return WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,+			aUtf16string, -1, NULL, 0, NULL, NULL);+}+++static int sizeMbcs(wchar_t const * aMbcsString)+{+	int lRes = WideCharToMultiByte(CP_ACP, 0,+			aMbcsString, -1, NULL, 0, NULL, NULL);+	/* DWORD licic = GetLastError(); */+	return lRes;+}+++wchar_t* tinyfd_mbcsTo16(char const* aMbcsString)+{+	static wchar_t* lMbcsString = NULL;+	int lSize;++	free(lMbcsString);+	if (!aMbcsString) { lMbcsString = NULL; return NULL; }+	lSize = sizeUtf16FromMbcs(aMbcsString);+	if (lSize)+	{+		lMbcsString = (wchar_t*) malloc(lSize * sizeof(wchar_t));+		lSize = MultiByteToWideChar(CP_ACP, 0, aMbcsString, -1, lMbcsString, lSize);+	}+	else wcscpy(lMbcsString, L"");+	return lMbcsString;+}+++wchar_t * tinyfd_utf8to16(char const * aUtf8string)+{+	static wchar_t * lUtf16string = NULL;+	int lSize;++	free(lUtf16string);+	if (!aUtf8string) {lUtf16string = NULL; return NULL;}+	lSize = sizeUtf16From8(aUtf8string);+	if (lSize)+	{+		lUtf16string = (wchar_t*) malloc(lSize * sizeof(wchar_t));+		lSize = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,+			aUtf8string, -1, lUtf16string, lSize);+		return lUtf16string;+	}+	else+	{+		/* let's try mbcs anyway */+		lUtf16string = NULL;+		return tinyfd_mbcsTo16(aUtf8string);+	}+}+++char * tinyfd_utf16toMbcs(wchar_t const * aUtf16string)+{+	static char * lMbcsString = NULL;+	int lSize;++	free(lMbcsString);+	if (!aUtf16string) { lMbcsString = NULL; return NULL; }+	lSize = sizeMbcs(aUtf16string);+	if (lSize)+	{+		lMbcsString = (char*) malloc(lSize);+		lSize = WideCharToMultiByte(CP_ACP, 0, aUtf16string, -1, lMbcsString, lSize, NULL, NULL);+	}+	else strcpy(lMbcsString, "");+		return lMbcsString;+}+++char * tinyfd_utf8toMbcs(char const * aUtf8string)+{+	wchar_t const * lUtf16string;+	lUtf16string = tinyfd_utf8to16(aUtf8string);+	return tinyfd_utf16toMbcs(lUtf16string);+}+++char * tinyfd_utf16to8(wchar_t const * aUtf16string)+{+	static char * lUtf8string = NULL;+	int lSize;++	free(lUtf8string);+	if (!aUtf16string) { lUtf8string = NULL; return NULL; }+	lSize = sizeUtf8(aUtf16string);+	if (lSize)+	{+		lUtf8string = (char*) malloc(lSize);+		lSize = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, aUtf16string, -1, lUtf8string, lSize, NULL, NULL);+	}+	else strcpy(lUtf8string, "");+	return lUtf8string;+}+++char * tinyfd_mbcsTo8(char const * aMbcsString)+{+	wchar_t const * lUtf16string;+	lUtf16string = tinyfd_mbcsTo16(aMbcsString);+	return tinyfd_utf16to8(lUtf16string);+}+++void tinyfd_beep(void)+{+	if (windowsVersion() > 5) Beep(440, 300);+	else MessageBeep(MB_OK);+}+++static void wipefileW(wchar_t const * aFilename)+{+	int i;+	FILE * lIn;+#if (defined(__MINGW32_MAJOR_VERSION) && !defined(__MINGW64__) && (__MINGW32_MAJOR_VERSION <= 3)) || defined(__BORLANDC__) || defined(__WATCOMC__) || defined(__DMC__)+	struct _stat st;+	if (_wstat(aFilename, &st) == 0)+#else+	struct __stat64 st;+	if (_wstat64(aFilename, &st) == 0)+#endif+	{+		lIn = ( FILE * ) _wfopen(aFilename, L"w");+		if ( lIn )+		{+			for (i = 0; i < st.st_size; i++)+			{+				fputc('A', lIn);+			}+			fclose(lIn);+		}+	}+}+++static wchar_t * getPathWithoutFinalSlashW(+		wchar_t * aoDestination, /* make sure it is allocated, use _MAX_PATH */+		wchar_t const * aSource) /* aoDestination and aSource can be the same */+{+	wchar_t const * lTmp;+	if (aSource)+	{+		lTmp = wcsrchr(aSource, L'/');+		if (!lTmp)+		{+			lTmp = wcsrchr(aSource, L'\\');+		}+		if (lTmp)+		{+			wcsncpy(aoDestination, aSource, lTmp - aSource);+			aoDestination[lTmp - aSource] = L'\0';+		}+		else+		{+			*aoDestination = L'\0';+		}+	}+	else+	{+		*aoDestination = L'\0';+	}+	return aoDestination;+}+++static wchar_t * getLastNameW(+		wchar_t * aoDestination, /* make sure it is allocated */+		wchar_t const * aSource)+{+		/* copy the last name after '/' or '\' */+		wchar_t const * lTmp;+		if (aSource)+		{+				lTmp = wcsrchr(aSource, L'/');+				if (!lTmp)+				{+						lTmp = wcsrchr(aSource, L'\\');+				}+				if (lTmp)+				{+						wcscpy(aoDestination, lTmp + 1);+				}+				else+				{+						wcscpy(aoDestination, aSource);+				}+		}+		else+		{+				*aoDestination = L'\0';+		}+		return aoDestination;+}+++static void Hex2RGBW(wchar_t const aHexRGB[8], unsigned char aoResultRGB[3])+{+		wchar_t lColorChannel[8];+		if (aoResultRGB)+		{+				if (aHexRGB)+				{+						wcscpy(lColorChannel, aHexRGB);+						aoResultRGB[2] = (unsigned char)wcstoul(lColorChannel + 5, NULL, 16);+						lColorChannel[5] = '\0';+						aoResultRGB[1] = (unsigned char)wcstoul(lColorChannel + 3, NULL, 16);+						lColorChannel[3] = '\0';+						aoResultRGB[0] = (unsigned char)wcstoul(lColorChannel + 1, NULL, 16);+						/* printf("%d %d %d\n", aoResultRGB[0], aoResultRGB[1], aoResultRGB[2]); */+				}+				else+				{+						aoResultRGB[0] = 0;+						aoResultRGB[1] = 0;+						aoResultRGB[2] = 0;+				}+		}+}+++static void RGB2HexW( unsigned char const aRGB[3], wchar_t aoResultHexRGB[8])+{+#if (defined(__cplusplus ) && __cplusplus >= 201103L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__clang__)+	wchar_t const * const lPrintFormat = L"#%02hhx%02hhx%02hhx";+#else+	wchar_t const * const lPrintFormat = L"#%02hx%02hx%02hx";+#endif++	if (aoResultHexRGB)+	{+		if (aRGB)+		{+			/* wprintf(L"aoResultHexRGB %s\n", aoResultHexRGB); */+#if !defined(__BORLANDC__) && !defined(__TINYC__) && !(defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR))+			swprintf(aoResultHexRGB, 8, lPrintFormat, aRGB[0], aRGB[1], aRGB[2]);+#else+			swprintf(aoResultHexRGB, lPrintFormat, aRGB[0], aRGB[1], aRGB[2]);+#endif+		}+		else+		{+			aoResultHexRGB[0] = 0;+			aoResultHexRGB[1] = 0;+			aoResultHexRGB[2] = 0;+		}+	}+}+++static int dirExists(char const * aDirPath)+{+#if (defined(__MINGW32_MAJOR_VERSION) && !defined(__MINGW64__) && (__MINGW32_MAJOR_VERSION <= 3)) || defined(__BORLANDC__) || defined(__WATCOMC__) || defined(__DMC__)+	struct _stat lInfo;+#else+	struct __stat64 lInfo;+#endif+		wchar_t * lTmpWChar;+		int lStatRet;+				size_t lDirLen;++				if (!aDirPath)+						return 0;+				lDirLen = strlen(aDirPath);+				if (!lDirLen)+						return 1;+				if ( (lDirLen == 2) && (aDirPath[1] == ':') )+						return 1;++		if (tinyfd_winUtf8)+		{+						lTmpWChar = tinyfd_utf8to16(aDirPath);+#if (defined(__MINGW32_MAJOR_VERSION) && !defined(__MINGW64__) && (__MINGW32_MAJOR_VERSION <= 3)) || defined(__BORLANDC__) || defined(__WATCOMC__)+			lStatRet = _wstat(lTmpWChar, &lInfo);+#else+			lStatRet = _wstat64(lTmpWChar, &lInfo);+#endif+			if (lStatRet != 0)+					return 0;+			else if (lInfo.st_mode & S_IFDIR)+					return 1;+			else+						return 0;+		}+#if (defined(__MINGW32_MAJOR_VERSION) && !defined(__MINGW64__) && (__MINGW32_MAJOR_VERSION <= 3)) || defined(__BORLANDC__) || defined(__WATCOMC__)+		else if (_stat(aDirPath, &lInfo) != 0)+#else+		else if (_stat64(aDirPath, &lInfo) != 0)+#endif+				return 0;+		else if (lInfo.st_mode & S_IFDIR)+				return 1;+		else+				return 0;+}+++static int fileExists(char const * aFilePathAndName)+{+#if (defined(__MINGW32_MAJOR_VERSION) && !defined(__MINGW64__) && (__MINGW32_MAJOR_VERSION <= 3)) || defined(__BORLANDC__) || defined(__WATCOMC__) || defined(__DMC__)+	struct _stat lInfo;+#else+	struct __stat64 lInfo;+#endif+		wchar_t * lTmpWChar;+		int lStatRet;+		FILE * lIn;++		if (!aFilePathAndName || !strlen(aFilePathAndName))+		{+				return 0;+		}++		if (tinyfd_winUtf8)+		{+						lTmpWChar = tinyfd_utf8to16(aFilePathAndName);+#if (defined(__MINGW32_MAJOR_VERSION) && !defined(__MINGW64__) && (__MINGW32_MAJOR_VERSION <= 3)) || defined(__BORLANDC__) || defined(__WATCOMC__)+			lStatRet = _wstat(lTmpWChar, &lInfo);+#else+			lStatRet = _wstat64(lTmpWChar, &lInfo);+#endif++			if (lStatRet != 0)+					return 0;+			else if (lInfo.st_mode & _S_IFREG)+					return 1;+			else+					return 0;+		}+		else+		{+				lIn = ( FILE * ) fopen(aFilePathAndName, "r");+				if (!lIn)+				{+						return 0;+				}+				fclose(lIn);+				return 1;+		}+}++static void replaceWchar(wchar_t * aString,+		wchar_t aOldChr,+		wchar_t aNewChr)+{+		wchar_t * p;++		if (!aString)+		{+				return ;+		}++		if (aOldChr == aNewChr)+		{+				return ;+		}++		p = aString;+		while ((p = wcsrchr(p, aOldChr)))+		{+				*p = aNewChr;+#ifdef TINYFD_NOCCSUNICODE+				p++;+#endif+				p++;+		}+		return ;+}+++static int quoteDetectedW(wchar_t const * aString)+{+		wchar_t const * p;++		if (!aString) return 0;++		p = aString;+		while ((p = wcsrchr(p, L'\'')))+		{+				return 1;+		}++		p = aString;+		while ((p = wcsrchr(p, L'\"')))+		{+				return 1;+		}++		return 0;+}++#endif /* _WIN32 */++/* source and destination can be the same or ovelap*/+static char * ensureFilesExist(char * aDestination,+		char const * aSourcePathsAndNames)+{+		char * lDestination = aDestination;+		char const * p;+		char const * p2;+		size_t lLen;++		if (!aSourcePathsAndNames)+		{+				return NULL;+		}+		lLen = strlen(aSourcePathsAndNames);+		if (!lLen)+		{+				return NULL;+		}++		p = aSourcePathsAndNames;+		while ((p2 = strchr(p, '|')) != NULL)+		{+				lLen = p2 - p;+				memmove(lDestination, p, lLen);+				lDestination[lLen] = '\0';+				if (fileExists(lDestination))+				{+						lDestination += lLen;+						*lDestination = '|';+						lDestination++;+				}+				p = p2 + 1;+		}+		if (fileExists(p))+		{+				lLen = strlen(p);+				memmove(lDestination, p, lLen);+				lDestination[lLen] = '\0';+		}+		else+		{+				*(lDestination - 1) = '\0';+		}+		return aDestination;+}++#ifdef _WIN32++static int __stdcall EnumThreadWndProc(HWND hwnd, LPARAM lParam)+{+		wchar_t lTitleName[MAX_PATH];+		wchar_t const* lDialogTitle = (wchar_t const *) lParam;++		GetWindowTextW(hwnd, lTitleName, MAX_PATH);+		/* wprintf(L"lTitleName %ls \n", lTitleName);  */++		if (wcscmp(lDialogTitle, lTitleName) == 0)+		{+				SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);+				return 0;+		}+		return 1;+}+++static void hiddenConsoleW(wchar_t const * aString, wchar_t const * aDialogTitle, int aInFront)+{+		STARTUPINFOW StartupInfo;+		PROCESS_INFORMATION ProcessInfo;++		if (!aString || !wcslen(aString) ) return;++		memset(&StartupInfo, 0, sizeof(StartupInfo));+		StartupInfo.cb = sizeof(STARTUPINFOW);+		StartupInfo.dwFlags = STARTF_USESHOWWINDOW;+		StartupInfo.wShowWindow = SW_HIDE;++		if (!CreateProcessW(NULL, (LPWSTR)aString, NULL, NULL, FALSE,+								CREATE_NEW_CONSOLE, NULL, NULL,+								&StartupInfo, &ProcessInfo))+		{+				return; /* GetLastError(); */+		}++		WaitForInputIdle(ProcessInfo.hProcess, INFINITE);+		if (aInFront)+		{+				while (EnumWindows(EnumThreadWndProc, (LPARAM)aDialogTitle)) {}+		}+		WaitForSingleObject(ProcessInfo.hProcess, INFINITE);+		CloseHandle(ProcessInfo.hThread);+		CloseHandle(ProcessInfo.hProcess);+}+++int tinyfd_messageBoxW(+		wchar_t const * aTitle, /* NULL or "" */+		wchar_t const * aMessage, /* NULL or ""  may contain \n and \t */+		wchar_t const * aDialogType, /* "ok" "okcancel" "yesno" "yesnocancel" */+		wchar_t const * aIconType, /* "info" "warning" "error" "question" */+		int aDefaultButton) /* 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel */+{+		int lBoxReturnValue;+		UINT aCode;++		if (aTitle&&!wcscmp(aTitle, L"tinyfd_query")){ strcpy(tinyfd_response, "windows_wchar"); return 1; }++		/*if (quoteDetectedW(aTitle)) return tinyfd_messageBoxW(L"INVALID TITLE WITH QUOTES", aMessage, aDialogType, aIconType, aDefaultButton);+		if (quoteDetectedW(aMessage)) return tinyfd_messageBoxW(aTitle, L"INVALID MESSAGE WITH QUOTES", aDialogType, aIconType, aDefaultButton);*/++		if (aIconType && !wcscmp(L"warning", aIconType))+		{+				aCode = MB_ICONWARNING;+		}+		else if (aIconType && !wcscmp(L"error", aIconType))+		{+				aCode = MB_ICONERROR;+		}+		else if (aIconType && !wcscmp(L"question", aIconType))+		{+				aCode = MB_ICONQUESTION;+		}+		else+		{+				aCode = MB_ICONINFORMATION;+		}++		if (aDialogType && !wcscmp(L"okcancel", aDialogType))+		{+				aCode += MB_OKCANCEL;+				if (!aDefaultButton)+				{+						aCode += MB_DEFBUTTON2;+				}+		}+		else if (aDialogType && !wcscmp(L"yesno", aDialogType))+		{+				aCode += MB_YESNO;+				if (!aDefaultButton)+				{+						aCode += MB_DEFBUTTON2;+				}+		}+		else if (aDialogType && !wcscmp(L"yesnocancel", aDialogType))+		{+			aCode += MB_YESNOCANCEL;+			if (aDefaultButton == 1)+			{+				aCode += MB_DEFBUTTON1;+			}+			else if (aDefaultButton == 2)+			{+				aCode += MB_DEFBUTTON2;+			}+			else+			{+				aCode += MB_DEFBUTTON3;+			}+		}+		else+		{+				aCode += MB_OK;+		}++		aCode += MB_TOPMOST;++		lBoxReturnValue = MessageBoxW(GetForegroundWindow(), aMessage, aTitle, aCode);++		if ( (lBoxReturnValue == IDNO) && (aDialogType && !wcscmp(L"yesnocancel", aDialogType)) )+		{+			return 2;+		}+		else if ( (lBoxReturnValue == IDOK) || (lBoxReturnValue == IDYES) )+		{+			return 1;+		}+		else+		{+			return 0;+		}+}+++/* int tinyfd_notifyPopupW_ORIGINAL(+		wchar_t const * aTitle,+		wchar_t const * aMessage,+		wchar_t const * aIconType)+{+		wchar_t * lDialogString;+		size_t lTitleLen;+		size_t lMessageLen;+		size_t lDialogStringLen;++		if (aTitle && !wcscmp(aTitle, L"tinyfd_query")) { strcpy(tinyfd_response, "windows_wchar"); return 1; }++		if (quoteDetectedW(aTitle)) return tinyfd_notifyPopupW(L"INVALID TITLE WITH QUOTES", aMessage, aIconType);+		if (quoteDetectedW(aMessage)) return tinyfd_notifyPopupW(aTitle, L"INVALID MESSAGE WITH QUOTES", aIconType);++		lTitleLen = aTitle ? wcslen(aTitle) : 0;+		lMessageLen = aMessage ? wcslen(aMessage) : 0;+		lDialogStringLen = 3 * MAX_PATH_OR_CMD + lTitleLen + lMessageLen;+		lDialogString = (wchar_t *) malloc(2 * lDialogStringLen);+		if (!lDialogString) return 0;++		wcscpy(lDialogString, L"powershell.exe -executionpolicy bypass -command \"\+function Show-BalloonTip {\+[cmdletbinding()] \+param( \+[string]$Title = ' ', \+[string]$Message = ' ', \+[ValidateSet('info', 'warning', 'error')] \+[string]$IconType = 'info');\+[system.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms') | Out-Null ; \+$balloon = New-Object System.Windows.Forms.NotifyIcon ; \+$path = Get-Process -id $pid | Select-Object -ExpandProperty Path ; \+$icon = [System.Drawing.Icon]::ExtractAssociatedIcon($path) ;");++		wcscat(lDialogString, L"\+$balloon.Icon = $icon ; \+$balloon.BalloonTipIcon = $IconType ; \+$balloon.BalloonTipText = $Message ; \+$balloon.BalloonTipTitle = $Title ; \+$balloon.Text = 'tinyfiledialogs' ; \+$balloon.Visible = $true ; \+$balloon.ShowBalloonTip(5000)};\+Show-BalloonTip");++		if (aTitle && wcslen(aTitle))+		{+				wcscat(lDialogString, L" -Title '");+				wcscat(lDialogString, aTitle);+				wcscat(lDialogString, L"'");+		}+		if (aMessage && wcslen(aMessage))+		{+				wcscat(lDialogString, L" -Message '");+				wcscat(lDialogString, aMessage);+				wcscat(lDialogString, L"'");+		}+		if (aMessage && wcslen(aIconType))+		{+				wcscat(lDialogString, L" -IconType '");+				wcscat(lDialogString, aIconType);+				wcscat(lDialogString, L"'");+		}+		wcscat(lDialogString, L"\"");++		hiddenConsoleW(lDialogString, aTitle, 0);+		free(lDialogString);+		return 1;+}*/+++/* return has only meaning for tinyfd_query */+int tinyfd_notifyPopupW(+	wchar_t const* aTitle, /* NULL or L"" */+	wchar_t const* aMessage, /* NULL or L"" may contain \n \t */+	wchar_t const* aIconType) /* L"info" L"warning" L"error" */+{+	wchar_t* lDialogString;+	size_t lTitleLen;+	size_t lMessageLen;+	size_t lDialogStringLen;++	FILE* lIn;++	if (aTitle && !wcscmp(aTitle, L"tinyfd_query")) { strcpy(tinyfd_response, "windows_wchar"); return 1; }++	if (quoteDetectedW(aTitle)) return tinyfd_notifyPopupW(L"INVALID TITLE WITH QUOTES", aMessage, aIconType);+	if (quoteDetectedW(aMessage)) return tinyfd_notifyPopupW(aTitle, L"INVALID MESSAGE WITH QUOTES", aIconType);++	lTitleLen = aTitle ? wcslen(aTitle) : 0;+	lMessageLen = aMessage ? wcslen(aMessage) : 0;+	lDialogStringLen = 3 * MAX_PATH_OR_CMD + lTitleLen + lMessageLen;+	lDialogString = (wchar_t*)malloc(2 * lDialogStringLen);+	if (!lDialogString) return 0;++	swprintf(lDialogString,+#if !defined(__BORLANDC__) && !defined(__TINYC__) && !(defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR))+		lDialogStringLen,+#endif+		L"%ls\\tinyfd.hta", _wgetenv(L"TEMP"));++	lIn = ( FILE * ) _wfopen(lDialogString, L"w");+	if (!lIn)+	{+		free(lDialogString);+		return 0;+	}++	wcscpy(lDialogString, L"\n\+<html>\n\+<head>\n\+<title>");+	if ( aTitle && wcslen(aTitle) ) wcscat(lDialogString, aTitle);+	wcscat(lDialogString, L"</title>\n\+</head>\n\+<HTA:APPLICATION\n\+SysMenu = 'no'\n\+ID = 'tinyfdHTA'\n\+APPLICATIONNAME = 'tinyfd_notifyPopup'\n\+MINIMIZEBUTTON = 'no'\n\+MAXIMIZEBUTTON = 'no'\n\+BORDER = 'dialog'\n\+SCROLL = 'no'\n\+SINGLEINSTANCE = 'yes'\n\+WINDOWSTATE = 'hidden'>\n\+<script language = 'VBScript'>\n\+intWidth = Screen.Width/4\n\+intHeight = Screen.Height/10\n\+ResizeTo intWidth, intHeight\n\+MoveTo Screen.Width * .7, Screen.Height * .8\n\+result = 0\n\+Sub Window_onLoad\n\+idTimer = window.setTimeout(\"PausedSection\", 3000, \"VBScript\")\n\+End Sub\n");++	wcscat(lDialogString, L"\n\+Sub PausedSection\n\+window.Close\n\+End Sub\n\+</script>\n\+<body style = 'background-color:#EEEEEE' onkeypress = 'vbs:Default_Buttons' align = 'top'>\n\+<table width = '100%' height = '80%' align = 'center' border = '0'>\n\+<tr border = '0'>\n\+<td align = 'left' valign = 'middle' style='Font-Family:Arial'>\n");++	wcscat(lDialogString, aMessage ? aMessage : L"");++	wcscat(lDialogString, L"\n\+</body>\n\+</html>\n\+");++	fputws(lDialogString, lIn);+	fclose(lIn);++	if (aTitle && wcslen(aTitle))+	{+		wcscat(lDialogString, L" -Title '");+		wcscat(lDialogString, aTitle);+		wcscat(lDialogString, L"'");+	}+	if (aMessage && wcslen(aMessage))+	{+		wcscat(lDialogString, L" -Message '");+		wcscat(lDialogString, aMessage);+		wcscat(lDialogString, L"'");+	}+	if (aMessage && wcslen(aIconType))+	{+		wcscat(lDialogString, L" -IconType '");+		wcscat(lDialogString, aIconType);+		wcscat(lDialogString, L"'");+	}+	wcscat(lDialogString, L"\"");++	/* wprintf ( L"lDialogString: %ls\n" , lDialogString ) ; */+	wcscpy(lDialogString,+		L"cmd.exe /c mshta.exe \"%TEMP%\\tinyfd.hta\"");++	hiddenConsoleW(lDialogString, aTitle, 0);+	free(lDialogString);+	return 1;+}+++wchar_t * tinyfd_inputBoxW(+		wchar_t const * aTitle, /* NULL or L"" */+		wchar_t const * aMessage, /* NULL or L"" (\n and \t have no effect) */+		wchar_t const * aDefaultInput) /* L"" , if NULL it's a passwordBox */+{+		static wchar_t lBuff[MAX_PATH_OR_CMD];+		wchar_t * lDialogString;+		FILE * lIn;+		FILE * lFile;+		int lResult;+		size_t lTitleLen;+		size_t lMessageLen;+		size_t lDialogStringLen;++		if (aTitle&&!wcscmp(aTitle, L"tinyfd_query")){ strcpy(tinyfd_response, "windows_wchar"); return (wchar_t *)1; }++		if (quoteDetectedW(aTitle)) return tinyfd_inputBoxW(L"INVALID TITLE WITH QUOTES", aMessage, aDefaultInput);+		if (quoteDetectedW(aMessage)) return tinyfd_inputBoxW(aTitle, L"INVALID MESSAGE WITH QUOTES", aDefaultInput);+		if (quoteDetectedW(aDefaultInput)) return tinyfd_inputBoxW(aTitle, aMessage, L"INVALID DEFAULT_INPUT WITH QUOTES: use the GRAVE ACCENT \\x60 instead.");++		lTitleLen =  aTitle ? wcslen(aTitle) : 0 ;+		lMessageLen =  aMessage ? wcslen(aMessage) : 0 ;+		lDialogStringLen = 3 * MAX_PATH_OR_CMD + lTitleLen + lMessageLen;+		lDialogString = (wchar_t *) malloc(2 * lDialogStringLen);++		if (aDefaultInput)+		{+						swprintf(lDialogString,+#if !defined(__BORLANDC__) && !defined(__TINYC__) && !(defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR))+				lDialogStringLen,+#endif+				L"%ls\\tinyfd.vbs", _wgetenv(L"TEMP"));+		}+		else+		{+				swprintf(lDialogString,+#if !defined(__BORLANDC__) && !defined(__TINYC__) && !(defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR))+						lDialogStringLen,+#endif+				L"%ls\\tinyfd.hta", _wgetenv(L"TEMP"));+		}+		lIn = ( FILE * ) _wfopen(lDialogString, L"w");+		if (!lIn)+		{+				free(lDialogString);+				return NULL;+		}++		if ( aDefaultInput )+		{+				wcscpy(lDialogString, L"Dim result:result=InputBox(\"");+				if (aMessage && wcslen(aMessage))+				{+										wcscpy(lBuff, aMessage);+										replaceWchar(lBuff, L'\n', L' ');+										wcscat(lDialogString, lBuff);+				}+				wcscat(lDialogString, L"\",\"");+				if (aTitle) wcscat(lDialogString, aTitle);+				wcscat(lDialogString, L"\",\"");++				if (aDefaultInput && wcslen(aDefaultInput))+				{+										wcscpy(lBuff, aDefaultInput);+										replaceWchar(lBuff, L'\n', L' ');+										wcscat(lDialogString, lBuff);+				}+				wcscat(lDialogString, L"\"):If IsEmpty(result) then:WScript.Echo 0");+				wcscat(lDialogString, L":Else: WScript.Echo \"1\" & result : End If");+		}+		else+		{+				wcscpy(lDialogString, L"\n\+<html>\n\+<head>\n\+<title>");+				if (aTitle) wcscat(lDialogString, aTitle);+				wcscat(lDialogString, L"</title>\n\+</head>\n\+<HTA:APPLICATION\n\+ID = 'tinyfdHTA'\n\+APPLICATIONNAME = 'tinyfd_inputBox'\n\+MINIMIZEBUTTON = 'no'\n\+MAXIMIZEBUTTON = 'no'\n\+BORDER = 'dialog'\n\+SCROLL = 'no'\n\+SINGLEINSTANCE = 'yes'\n\+WINDOWSTATE = 'hidden'>\n\+\n\+<script language = 'VBScript'>\n\+\n\+intWidth = Screen.Width/4\n\+intHeight = Screen.Height/6\n\+ResizeTo intWidth, intHeight\n\+MoveTo((Screen.Width/2)-(intWidth/2)),((Screen.Height/2)-(intHeight/2))\n\+result = 0\n\+\n\+Sub Window_onLoad\n\+txt_input.Focus\n\+End Sub\n\+\n");++				wcscat(lDialogString, L"\+Sub Window_onUnload\n\+Set objFSO = CreateObject(\"Scripting.FileSystemObject\")\n\+Set oShell = CreateObject(\"WScript.Shell\")\n\+strTempFolder = oShell.ExpandEnvironmentStrings(\"%TEMP%\")\n\+Set objFile = objFSO.CreateTextFile(strTempFolder & \"\\tinyfd.txt\",True,True)\n\+If result = 1 Then\n\+objFile.Write 1 & txt_input.Value\n\+Else\n\+objFile.Write 0\n\+End If\n\+objFile.Close\n\+End Sub\n\+\n\+Sub Run_ProgramOK\n\+result = 1\n\+window.Close\n\+End Sub\n\+\n\+Sub Run_ProgramCancel\n\+window.Close\n\+End Sub\n\+\n");++				wcscat(lDialogString, L"Sub Default_Buttons\n\+If Window.Event.KeyCode = 13 Then\n\+btn_OK.Click\n\+ElseIf Window.Event.KeyCode = 27 Then\n\+btn_Cancel.Click\n\+End If\n\+End Sub\n\+\n\+</script>\n\+<body style = 'background-color:#EEEEEE' onkeypress = 'vbs:Default_Buttons' align = 'top'>\n\+<table width = '100%' height = '80%' align = 'center' border = '0'>\n\+<tr border = '0'>\n\+<td align = 'left' valign = 'middle' style='Font-Family:Arial'>\n");++				wcscat(lDialogString, aMessage ? aMessage : L"");++				wcscat(lDialogString, L"\n\+</td>\n\+<td align = 'right' valign = 'middle' style = 'margin-top: 0em'>\n\+<table  align = 'right' style = 'margin-right: 0em;'>\n\+<tr align = 'right' style = 'margin-top: 5em;'>\n\+<input type = 'button' value = 'OK' name = 'btn_OK' onClick = 'vbs:Run_ProgramOK' style = 'width: 5em; margin-top: 2em;'><br>\n\+<input type = 'button' value = 'Cancel' name = 'btn_Cancel' onClick = 'vbs:Run_ProgramCancel' style = 'width: 5em;'><br><br>\n\+</tr>\n\+</table>\n\+</td>\n\+</tr>\n\+</table>\n");++				wcscat(lDialogString, L"<table width = '100%' height = '100%' align = 'center' border = '0'>\n\+<tr>\n\+<td align = 'left' valign = 'top'>\n\+<input type = 'password' id = 'txt_input'\n\+name = 'txt_input' value = '' style = 'float:left;width:100%' ><BR>\n\+</td>\n\+</tr>\n\+</table>\n\+</body>\n\+</html>\n\+"               ) ;+		}+		fputws(lDialogString, lIn);+		fclose(lIn);++		if (aDefaultInput)+		{+				swprintf(lDialogString,+#if !defined(__BORLANDC__) && !defined(__TINYC__) && !(defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR))+						lDialogStringLen,+#endif+						L"%ls\\tinyfd.txt",_wgetenv(L"TEMP"));++#ifdef TINYFD_NOCCSUNICODE+								lFile = _wfopen(lDialogString, L"w");+								fputc(0xFF, lFile);+								fputc(0xFE, lFile);+#else+								lFile = _wfopen(lDialogString, L"wt, ccs=UNICODE"); /*or ccs=UTF-16LE*/+#endif+								fclose(lFile);++				wcscpy(lDialogString, L"cmd.exe /c cscript.exe //U //Nologo ");+				wcscat(lDialogString, L"\"%TEMP%\\tinyfd.vbs\" ");+				wcscat(lDialogString, L">> \"%TEMP%\\tinyfd.txt\"");+		}+		else+		{+				wcscpy(lDialogString,+						L"cmd.exe /c mshta.exe \"%TEMP%\\tinyfd.hta\"");+		}++		/* wprintf ( "lDialogString: %ls\n" , lDialogString ) ; */++		hiddenConsoleW(lDialogString, aTitle, 1);++		swprintf(lDialogString,+#if !defined(__BORLANDC__) && !defined(__TINYC__) && !(defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR))+				lDialogStringLen,+#endif+				L"%ls\\tinyfd.txt", _wgetenv(L"TEMP"));+		/* wprintf(L"lDialogString: %ls\n", lDialogString); */+#ifdef TINYFD_NOCCSUNICODE+		lIn = ( FILE * ) _wfopen(lDialogString, L"r") ;+#else+		lIn = ( FILE * ) _wfopen(lDialogString, L"rt, ccs=UNICODE") /*or ccs=UTF-16LE*/+#endif+		if ( ! lIn )+		{+			_wremove(lDialogString);+			free(lDialogString);+			return NULL;+		}++		memset(lBuff, 0, MAX_PATH_OR_CMD * sizeof(wchar_t) );++#ifdef TINYFD_NOCCSUNICODE+				fgets((char *)lBuff, 2*MAX_PATH_OR_CMD, lIn);+#else+				fgetws(lBuff, MAX_PATH_OR_CMD, lIn);+#endif+				fclose(lIn);+				wipefileW(lDialogString);+				_wremove(lDialogString);++				if (aDefaultInput)+				{+						swprintf(lDialogString,+#if !defined(__BORLANDC__) && !defined(__TINYC__) && !(defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR))+						lDialogStringLen,+#endif+						L"%ls\\tinyfd.vbs", _wgetenv(L"TEMP"));+		}+		else+		{+				swprintf(lDialogString,+#if !defined(__BORLANDC__) && !defined(__TINYC__) && !(defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR))+						lDialogStringLen,+#endif+						L"%ls\\tinyfd.hta", _wgetenv(L"TEMP"));+		}+		_wremove(lDialogString);+		free(lDialogString);+		/* wprintf( L"lBuff: %ls\n" , lBuff ) ; */+#ifdef TINYFD_NOCCSUNICODE+				lResult = !wcsncmp(lBuff+1, L"1", 1);+#else+				lResult = !wcsncmp(lBuff, L"1", 1);+#endif++		/* printf( "lResult: %d \n" , lResult ) ; */+		if (!lResult)+		{+			return NULL ;+		}++		/* wprintf( "lBuff+1: %ls\n" , lBuff+1 ) ; */++#ifdef TINYFD_NOCCSUNICODE+				if (aDefaultInput)+				{+						lDialogStringLen = wcslen(lBuff) ;+						lBuff[lDialogStringLen - 1] = L'\0';+						lBuff[lDialogStringLen - 2] = L'\0';+				}+				return lBuff + 2;+#else+				if (aDefaultInput) lBuff[wcslen(lBuff) - 1] = L'\0';+				return lBuff + 1;+#endif+}+++wchar_t * tinyfd_saveFileDialogW(+		wchar_t const * aTitle, /* NULL or "" */+		wchar_t const * aDefaultPathAndOrFile, /* NULL or "" */+		int aNumOfFilterPatterns, /* 0 */+		wchar_t const * const * aFilterPatterns, /* NULL or {"*.jpg","*.png"} */+		wchar_t const * aSingleFilterDescription) /* NULL or "image files" */+{+		static wchar_t lBuff[MAX_PATH_OR_CMD];+		wchar_t lDirname[MAX_PATH_OR_CMD];+		wchar_t lDialogString[MAX_PATH_OR_CMD];+		wchar_t lFilterPatterns[MAX_PATH_OR_CMD] = L"";+		wchar_t * p;+		wchar_t * lRetval;+				wchar_t const * ldefExt = NULL;+				int i;+		HRESULT lHResult;+		OPENFILENAMEW ofn = {0};++		if (aTitle&&!wcscmp(aTitle, L"tinyfd_query")){ strcpy(tinyfd_response, "windows_wchar"); return (wchar_t *)1; }++		/*if (quoteDetectedW(aTitle)) return tinyfd_saveFileDialogW(L"INVALID TITLE WITH QUOTES", aDefaultPathAndOrFile, aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription);+		if (quoteDetectedW(aDefaultPathAndOrFile)) return tinyfd_saveFileDialogW(aTitle, L"INVALID DEFAULT_PATH WITH QUOTES", aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription);+		if (quoteDetectedW(aSingleFilterDescription)) return tinyfd_saveFileDialogW(aTitle, aDefaultPathAndOrFile, aNumOfFilterPatterns, aFilterPatterns, L"INVALID FILTER_DESCRIPTION WITH QUOTES");+		for (i = 0; i < aNumOfFilterPatterns; i++)+		{+				if (quoteDetectedW(aFilterPatterns[i])) return tinyfd_saveFileDialogW(L"INVALID FILTER_PATTERN WITH QUOTES: use the GRAVE ACCENT \\x60 instead.", aDefaultPathAndOrFile, 0, NULL, NULL);+		}*/++		lHResult = CoInitializeEx(NULL, 0);++		getPathWithoutFinalSlashW(lDirname, aDefaultPathAndOrFile);+		getLastNameW(lBuff, aDefaultPathAndOrFile);++		if (aNumOfFilterPatterns > 0)+		{+						ldefExt = aFilterPatterns[0];++				if (aSingleFilterDescription && wcslen(aSingleFilterDescription))+				{+						wcscpy(lFilterPatterns, aSingleFilterDescription);+						wcscat(lFilterPatterns, L"\n");+				}+				wcscat(lFilterPatterns, aFilterPatterns[0]);+				for (i = 1; i < aNumOfFilterPatterns; i++)+				{+						wcscat(lFilterPatterns, L";");+						wcscat(lFilterPatterns, aFilterPatterns[i]);+				}+				wcscat(lFilterPatterns, L"\n");+				if (!(aSingleFilterDescription && wcslen(aSingleFilterDescription)))+				{+						wcscpy(lDialogString, lFilterPatterns);+						wcscat(lFilterPatterns, lDialogString);+				}+				wcscat(lFilterPatterns, L"All Files\n*.*\n");+				p = lFilterPatterns;+				while ((p = wcschr(p, L'\n')) != NULL)+				{+						*p = L'\0';+						p++;+				}+		}++		ofn.lStructSize = sizeof(OPENFILENAMEW);+		ofn.hwndOwner = GetForegroundWindow();+		ofn.hInstance = 0;+		ofn.lpstrFilter = wcslen(lFilterPatterns) ? lFilterPatterns : NULL;+		ofn.lpstrCustomFilter = NULL;+		ofn.nMaxCustFilter = 0;+		ofn.nFilterIndex = 1;+		ofn.lpstrFile = lBuff;++		ofn.nMaxFile = MAX_PATH_OR_CMD;+		ofn.lpstrFileTitle = NULL;+		ofn.nMaxFileTitle = MAX_PATH_OR_CMD/2;+		ofn.lpstrInitialDir = wcslen(lDirname) ? lDirname : NULL;+		ofn.lpstrTitle = aTitle && wcslen(aTitle) ? aTitle : NULL;+		ofn.Flags = OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST ;+		ofn.nFileOffset = 0;+		ofn.nFileExtension = 0;+				ofn.lpstrDefExt = ldefExt;+		ofn.lCustData = 0L;+		ofn.lpfnHook = NULL;+		ofn.lpTemplateName = NULL;++		if (GetSaveFileNameW(&ofn) == 0)+		{+				lRetval = NULL;+		}+		else+		{+				lRetval = lBuff;+		}++		if (lHResult == S_OK || lHResult == S_FALSE)+		{+				CoUninitialize();+		}+		return lRetval;+}+++wchar_t * tinyfd_openFileDialogW(+		wchar_t const * aTitle, /* NULL or "" */+		wchar_t const * aDefaultPathAndOrFile, /* NULL or "" */+		int aNumOfFilterPatterns, /* 0 */+		wchar_t const * const * aFilterPatterns, /* NULL or {"*.jpg","*.png"} */+		wchar_t const * aSingleFilterDescription, /* NULL or "image files" */+		int aAllowMultipleSelects) /* 0 or 1 ; -1 to free allocated memory and return */+{+		size_t lLengths[MAX_MULTIPLE_FILES];+		wchar_t lDirname[MAX_PATH_OR_CMD];+		wchar_t lFilterPatterns[MAX_PATH_OR_CMD] = L"";+		wchar_t lDialogString[MAX_PATH_OR_CMD];+		wchar_t * lPointers[MAX_MULTIPLE_FILES+1];+		wchar_t * p;+		int i, j;+		size_t lBuffLen;+		DWORD lFullBuffLen;+		HRESULT lHResult;+		OPENFILENAMEW ofn = { 0 };+		static wchar_t * lBuff = NULL;++		free(lBuff);+		lBuff = NULL;+		if (aAllowMultipleSelects < 0) return (wchar_t *)0;++		if (aTitle&&!wcscmp(aTitle, L"tinyfd_query")){ strcpy(tinyfd_response, "windows_wchar"); return (wchar_t *)1; }++		/*if (quoteDetectedW(aTitle)) return tinyfd_openFileDialogW(L"INVALID TITLE WITH QUOTES", aDefaultPathAndOrFile, aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription, aAllowMultipleSelects);+		if (quoteDetectedW(aDefaultPathAndOrFile)) return tinyfd_openFileDialogW(aTitle, L"INVALID DEFAULT_PATH WITH QUOTES", aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription, aAllowMultipleSelects);+		if (quoteDetectedW(aSingleFilterDescription)) return tinyfd_openFileDialogW(aTitle, aDefaultPathAndOrFile, aNumOfFilterPatterns, aFilterPatterns, L"INVALID FILTER_DESCRIPTION WITH QUOTES", aAllowMultipleSelects);+		for (i = 0; i < aNumOfFilterPatterns; i++)+		{+				if (quoteDetectedW(aFilterPatterns[i])) return tinyfd_openFileDialogW(L"INVALID FILTER_PATTERN WITH QUOTES: use the GRAVE ACCENT \\x60 instead.", aDefaultPathAndOrFile, 0, NULL, NULL, aAllowMultipleSelects);+		}*/++		if (aAllowMultipleSelects)+		{+				lFullBuffLen = MAX_MULTIPLE_FILES * MAX_PATH_OR_CMD + 1;+				lBuff = (wchar_t*) malloc(lFullBuffLen * sizeof(wchar_t));+				if (!lBuff)+				{+						lFullBuffLen = LOW_MULTIPLE_FILES * MAX_PATH_OR_CMD + 1;+						lBuff = (wchar_t*) malloc( lFullBuffLen * sizeof(wchar_t));+				}+		}+		else+		{+				lFullBuffLen = MAX_PATH_OR_CMD + 1;+				lBuff = (wchar_t*) malloc(lFullBuffLen * sizeof(wchar_t));+		}+		if (!lBuff) return NULL;++		lHResult = CoInitializeEx(NULL, 0);++		getPathWithoutFinalSlashW(lDirname, aDefaultPathAndOrFile);+		getLastNameW(lBuff, aDefaultPathAndOrFile);++		if (aNumOfFilterPatterns > 0)+		{+			if (aSingleFilterDescription && wcslen(aSingleFilterDescription))+			{+				wcscpy(lFilterPatterns, aSingleFilterDescription);+				wcscat(lFilterPatterns, L"\n");+			}+			wcscat(lFilterPatterns, aFilterPatterns[0]);+			for (i = 1; i < aNumOfFilterPatterns; i++)+			{+				wcscat(lFilterPatterns, L";");+				wcscat(lFilterPatterns, aFilterPatterns[i]);+			}+			wcscat(lFilterPatterns, L"\n");+			if (!(aSingleFilterDescription && wcslen(aSingleFilterDescription)))+			{+				wcscpy(lDialogString, lFilterPatterns);+				wcscat(lFilterPatterns, lDialogString);+			}+			wcscat(lFilterPatterns, L"All Files\n*.*\n");+			p = lFilterPatterns;+			while ((p = wcschr(p, L'\n')) != NULL)+			{+				*p = L'\0';+				p++;+			}+		}++		ofn.lStructSize = sizeof(OPENFILENAME);+		ofn.hwndOwner = GetForegroundWindow();+		ofn.hInstance = 0;+		ofn.lpstrFilter = wcslen(lFilterPatterns) ? lFilterPatterns : NULL;+		ofn.lpstrCustomFilter = NULL;+		ofn.nMaxCustFilter = 0;+		ofn.nFilterIndex = 1;+		ofn.lpstrFile = lBuff;+		ofn.nMaxFile = lFullBuffLen;+		ofn.lpstrFileTitle = NULL;+		ofn.nMaxFileTitle = MAX_PATH_OR_CMD / 2;+		ofn.lpstrInitialDir = wcslen(lDirname) ? lDirname : NULL;+		ofn.lpstrTitle = aTitle && wcslen(aTitle) ? aTitle : NULL;+		ofn.Flags = OFN_EXPLORER | OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;+		ofn.nFileOffset = 0;+		ofn.nFileExtension = 0;+		ofn.lpstrDefExt = NULL;+		ofn.lCustData = 0L;+		ofn.lpfnHook = NULL;+		ofn.lpTemplateName = NULL;++		if (aAllowMultipleSelects)+		{+			ofn.Flags |= OFN_ALLOWMULTISELECT;+		}++		if (GetOpenFileNameW(&ofn) == 0)+		{+			free(lBuff);+			lBuff = NULL;+		}+		else+		{+			lBuffLen = wcslen(lBuff);+			lPointers[0] = lBuff + lBuffLen + 1;+			if (aAllowMultipleSelects && (lPointers[0][0] != L'\0'))+			{+				i = 0;+				do+				{+					lLengths[i] = wcslen(lPointers[i]);+					lPointers[i + 1] = lPointers[i] + lLengths[i] + 1;+					i++;+				} while (lPointers[i][0] != L'\0' && i < MAX_MULTIPLE_FILES );++				if (i > MAX_MULTIPLE_FILES)+				{+						free(lBuff);+						lBuff = NULL;+				}+				else+				{+						i--;+						p = lBuff + lFullBuffLen - 1;+						*p = L'\0';+						for (j = i; j >= 0; j--)+						{+								p -= lLengths[j];+								memmove(p, lPointers[j], lLengths[j] * sizeof(wchar_t));+								p--;+								*p = L'\\';+								p -= lBuffLen;+								memmove(p, lBuff, lBuffLen*sizeof(wchar_t));+								p--;+								*p = L'|';+						}+						p++;+						wcscpy(lBuff, p);+						lBuffLen = wcslen(lBuff);+				}+			}+			if (lBuff) lBuff = (wchar_t*)(realloc(lBuff, (lBuffLen + 1) * sizeof(wchar_t)));+		}++		if (lHResult == S_OK || lHResult == S_FALSE)+		{+			CoUninitialize();+		}++		return lBuff;+}+++BOOL CALLBACK BrowseCallbackProcW_enum(HWND hWndChild, LPARAM lParam)+{+	wchar_t buf[255];+	(void)lParam;+	GetClassNameW(hWndChild, buf, sizeof(buf));+	if (wcscmp(buf, L"SysTreeView32") == 0)+	{+		HTREEITEM hNode = TreeView_GetSelection(hWndChild);+		TreeView_EnsureVisible(hWndChild, hNode);+		return FALSE;+	}+	return TRUE;+}+++static int __stdcall BrowseCallbackProcW(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData)+{+	(void)lp;+	switch (uMsg)+	{+		case BFFM_INITIALIZED:+			SendMessage(hwnd, BFFM_SETSELECTIONW, TRUE, (LPARAM)pData);+			break;+		case BFFM_SELCHANGED:+			EnumChildWindows(hwnd, BrowseCallbackProcW_enum, 0);+	}+	return 0;+}++wchar_t * tinyfd_selectFolderDialogW(+		wchar_t const * aTitle, /* NULL or "" */+		wchar_t const * aDefaultPath) /* NULL or "" */+{+		static wchar_t lBuff[MAX_PATH_OR_CMD];+		wchar_t * lRetval;++		BROWSEINFOW bInfo;+		LPITEMIDLIST lpItem;+		HRESULT lHResult;++		if (aTitle&&!wcscmp(aTitle, L"tinyfd_query")){ strcpy(tinyfd_response, "windows_wchar"); return (wchar_t *)1; }++		/*if (quoteDetectedW(aTitle)) return tinyfd_selectFolderDialogW(L"INVALID TITLE WITH QUOTES", aDefaultPath);+		if (quoteDetectedW(aDefaultPath)) return tinyfd_selectFolderDialogW(aTitle, L"INVALID DEFAULT_PATH WITH QUOTES");*/++		lHResult = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);++		bInfo.hwndOwner = GetForegroundWindow();+		bInfo.pidlRoot = NULL;+		bInfo.pszDisplayName = lBuff;+		bInfo.lpszTitle = aTitle && wcslen(aTitle) ? aTitle : NULL;+		if (lHResult == S_OK || lHResult == S_FALSE)+		{+#ifndef __DMC__+				bInfo.ulFlags = BIF_USENEWUI;+#endif+		}+		bInfo.lpfn = BrowseCallbackProcW;+		bInfo.lParam = (LPARAM)aDefaultPath;+		bInfo.iImage = -1;++		lpItem = SHBrowseForFolderW(&bInfo);+		if (!lpItem)+				{+						lRetval = NULL;+				}+				else+		{+				SHGetPathFromIDListW(lpItem, lBuff);+								lRetval = lBuff ;+		}++		if (lHResult == S_OK || lHResult == S_FALSE)+		{+				CoUninitialize();+		}+				return lRetval;+}+++wchar_t * tinyfd_colorChooserW(+		wchar_t const * aTitle, /* NULL or "" */+		wchar_t const * aDefaultHexRGB, /* NULL or "#FF0000"*/+		unsigned char const aDefaultRGB[3], /* { 0 , 255 , 255 } */+		unsigned char aoResultRGB[3]) /* { 0 , 0 , 0 } */+{+		static wchar_t lResultHexRGB[8];+		CHOOSECOLORW cc;+		COLORREF crCustColors[16];+		unsigned char lDefaultRGB[3];+		int lRet;++		HRESULT lHResult;++		if (aTitle&&!wcscmp(aTitle, L"tinyfd_query")){ strcpy(tinyfd_response, "windows_wchar"); return (wchar_t *)1; }++		/*if (quoteDetectedW(aTitle)) return tinyfd_colorChooserW(L"INVALID TITLE WITH QUOTES", aDefaultHexRGB, aDefaultRGB, aoResultRGB);+		if (quoteDetectedW(aDefaultHexRGB)) return tinyfd_colorChooserW(aTitle, L"INVALID DEFAULT_HEX_RGB WITH QUOTES: use the GRAVE ACCENT \\x60 instead.", aDefaultRGB, aoResultRGB);*/++		lHResult = CoInitializeEx(NULL, 0);++		if ( aDefaultHexRGB && wcslen(aDefaultHexRGB) )+		{+				Hex2RGBW(aDefaultHexRGB, lDefaultRGB);+		}+		else+		{+				lDefaultRGB[0] = aDefaultRGB[0];+				lDefaultRGB[1] = aDefaultRGB[1];+				lDefaultRGB[2] = aDefaultRGB[2];+		}++		/* we can't use aTitle */+		cc.lStructSize = sizeof(CHOOSECOLOR);+		cc.hwndOwner = GetForegroundWindow();+		cc.hInstance = NULL;+		cc.rgbResult = RGB(lDefaultRGB[0], lDefaultRGB[1], lDefaultRGB[2]);+		cc.lpCustColors = crCustColors;+		cc.Flags = CC_RGBINIT | CC_FULLOPEN | CC_ANYCOLOR ;+		cc.lCustData = 0;+		cc.lpfnHook = NULL;+		cc.lpTemplateName = NULL;++		lRet = ChooseColorW(&cc);++		if (!lRet)+		{+				return NULL;+		}++		aoResultRGB[0] = GetRValue(cc.rgbResult);+		aoResultRGB[1] = GetGValue(cc.rgbResult);+		aoResultRGB[2] = GetBValue(cc.rgbResult);++		RGB2HexW(aoResultRGB, lResultHexRGB);++		if (lHResult == S_OK || lHResult == S_FALSE)+		{+				CoUninitialize();+		}++		return lResultHexRGB;+}+++static int messageBoxWinGui(+		char const * aTitle, /* NULL or "" */+		char const * aMessage, /* NULL or ""  may contain \n and \t */+		char const * aDialogType, /* "ok" "okcancel" "yesno" "yesnocancel" */+		char const * aIconType, /* "info" "warning" "error" "question" */+		int aDefaultButton) /* 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel */+{+		int lIntRetVal;+		wchar_t lTitle[128] = L"";+		wchar_t * lMessage = NULL;+		wchar_t lDialogType[16] = L"";+		wchar_t lIconType[16] = L"";+		wchar_t * lTmpWChar;++		if (aTitle)+		{+				if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aTitle);+				else lTmpWChar = tinyfd_mbcsTo16(aTitle);+				wcscpy(lTitle, lTmpWChar);+		}+		if (aMessage)+		{+				if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aMessage);+				else lTmpWChar = tinyfd_mbcsTo16(aMessage);+				lMessage = (wchar_t *) malloc((wcslen(lTmpWChar) + 1)* sizeof(wchar_t));+				if (lMessage) wcscpy(lMessage, lTmpWChar);+		}+		if (aDialogType)+		{+				if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aDialogType);+				else lTmpWChar = tinyfd_mbcsTo16(aDialogType);+				wcscpy(lDialogType, lTmpWChar);+		}+		if (aIconType)+		{+				if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aIconType);+				else lTmpWChar = tinyfd_mbcsTo16(aIconType);+				wcscpy(lIconType, lTmpWChar);+		}++		lIntRetVal = tinyfd_messageBoxW(lTitle, lMessage, lDialogType, lIconType, aDefaultButton);++		free(lMessage);++		return lIntRetVal;+}+++static int notifyWinGui(+		char const * aTitle, /* NULL or "" */+		char const * aMessage, /* NULL or "" may NOT contain \n nor \t */+		char const * aIconType)+{+		wchar_t lTitle[128] = L"";+		wchar_t * lMessage = NULL;+		wchar_t lIconType[16] = L"";+		wchar_t * lTmpWChar;++		if (aTitle)+		{+				if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aTitle);+				else lTmpWChar = tinyfd_mbcsTo16(aTitle);+				wcscpy(lTitle, lTmpWChar);+		}+		if (aMessage)+		{+				if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aMessage);+				else lTmpWChar = tinyfd_mbcsTo16(aMessage);+				lMessage = (wchar_t *) malloc((wcslen(lTmpWChar) + 1)* sizeof(wchar_t));+		if (lMessage) wcscpy(lMessage, lTmpWChar);+		}+		if (aIconType)+		{+				if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aIconType);+				else lTmpWChar = tinyfd_mbcsTo16(aIconType);+				wcscpy(lIconType, lTmpWChar);+		}++		tinyfd_notifyPopupW(lTitle, lMessage, lIconType);++		free(lMessage);++		return 1;+}+++static int inputBoxWinGui(+		char * aoBuff,+		char const * aTitle, /* NULL or "" */+		char const * aMessage, /* NULL or "" may NOT contain \n nor \t */+		char const * aDefaultInput) /* "" , if NULL it's a passwordBox */+{+		wchar_t lTitle[128] = L"";+		wchar_t * lMessage = NULL;+		wchar_t lDefaultInput[MAX_PATH_OR_CMD] = L"";+		wchar_t * lTmpWChar;+		char * lTmpChar;++		if (aTitle)+		{+				if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aTitle);+				else lTmpWChar = tinyfd_mbcsTo16(aTitle);+				wcscpy(lTitle, lTmpWChar);+		}+		if (aMessage)+		{+				if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aMessage);+				else lTmpWChar = tinyfd_mbcsTo16(aMessage);+				lMessage = (wchar_t *) malloc((wcslen(lTmpWChar) + 1)* sizeof(wchar_t));+	  if (lMessage) wcscpy(lMessage, lTmpWChar);+		}+		if (aDefaultInput)+		{+				if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aDefaultInput);+				else lTmpWChar = tinyfd_mbcsTo16(aDefaultInput);+				wcscpy(lDefaultInput, lTmpWChar);+		lTmpWChar = tinyfd_inputBoxW(lTitle, lMessage, lDefaultInput);+		}+	else lTmpWChar = tinyfd_inputBoxW(lTitle, lMessage, NULL);++		free(lMessage);++		if (!lTmpWChar)+		{+				aoBuff[0] = '\0';+				return 0;+		}++		if (tinyfd_winUtf8) lTmpChar = tinyfd_utf16to8(lTmpWChar);+		else lTmpChar = tinyfd_utf16toMbcs(lTmpWChar);++		strcpy(aoBuff, lTmpChar);++		return 1;+}+++static char * saveFileDialogWinGui(+		char * aoBuff,+		char const * aTitle, /* NULL or "" */+		char const * aDefaultPathAndOrFile, /* NULL or "" */+		int aNumOfFilterPatterns, /* 0 */+		char const * const * aFilterPatterns, /* NULL or {"*.jpg","*.png"} */+		char const * aSingleFilterDescription) /* NULL or "image files" */+{+		wchar_t lTitle[128] = L"";+		wchar_t lDefaultPathAndFile[MAX_PATH_OR_CMD] = L"";+		wchar_t lSingleFilterDescription[128] = L"";+		wchar_t * * lFilterPatterns;+		wchar_t * lTmpWChar;+		char * lTmpChar;+		int i;++		lFilterPatterns = (wchar_t **) malloc(aNumOfFilterPatterns*sizeof(wchar_t *));+		for (i = 0; i < aNumOfFilterPatterns; i++)+		{+				if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aFilterPatterns[i]);+				else lTmpWChar = tinyfd_mbcsTo16(aFilterPatterns[i]);+				lFilterPatterns[i] = (wchar_t *) malloc((wcslen(lTmpWChar) + 1) * sizeof(wchar_t *));+				if (lFilterPatterns[i]) wcscpy(lFilterPatterns[i], lTmpWChar);+		}++		if (aTitle)+		{+				if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aTitle);+				else lTmpWChar = tinyfd_mbcsTo16(aTitle);+				wcscpy(lTitle, lTmpWChar);+		}+		if (aDefaultPathAndOrFile)+		{+				if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aDefaultPathAndOrFile);+				else lTmpWChar = tinyfd_mbcsTo16(aDefaultPathAndOrFile);+				wcscpy(lDefaultPathAndFile, lTmpWChar);+		}+		if (aSingleFilterDescription)+		{+				if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aSingleFilterDescription);+				else lTmpWChar = tinyfd_mbcsTo16(aSingleFilterDescription);+				wcscpy(lSingleFilterDescription, lTmpWChar);+		}++		lTmpWChar = tinyfd_saveFileDialogW(+				lTitle,+				lDefaultPathAndFile,+				aNumOfFilterPatterns,+				(wchar_t const**) lFilterPatterns, /*stupid cast for gcc*/+				lSingleFilterDescription);++		for (i = 0; i < aNumOfFilterPatterns; i++)+		{+				free(lFilterPatterns[i]);+		}+		free(lFilterPatterns);++		if (!lTmpWChar)+		{+				return NULL;+		}++		if (tinyfd_winUtf8) lTmpChar = tinyfd_utf16to8(lTmpWChar);+		else lTmpChar = tinyfd_utf16toMbcs(lTmpWChar);+		strcpy(aoBuff, lTmpChar);+		if (tinyfd_winUtf8) (void)tinyfd_utf16to8(NULL);+		else (void)tinyfd_utf16toMbcs(NULL);++		return aoBuff;+}+++static char * openFileDialogWinGui(+		char const * aTitle, /*  NULL or "" */+		char const * aDefaultPathAndOrFile, /*  NULL or "" */+		int aNumOfFilterPatterns, /* 0 */+		char const * const * aFilterPatterns, /* NULL or {"*.jpg","*.png"} */+		char const * aSingleFilterDescription, /* NULL or "image files" */+		int aAllowMultipleSelects) /* 0 or 1 */+{+		wchar_t lTitle[128] = L"";+		wchar_t lDefaultPathAndFile[MAX_PATH_OR_CMD] = L"";+		wchar_t lSingleFilterDescription[128] = L"";+		wchar_t * * lFilterPatterns;+		wchar_t * lTmpWChar;+		char * lTmpChar;+		int i;++		lFilterPatterns = (wchar_t * *) malloc(aNumOfFilterPatterns*sizeof(wchar_t *));+		for (i = 0; i < aNumOfFilterPatterns; i++)+		{+				if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aFilterPatterns[i]);+				else lTmpWChar = tinyfd_mbcsTo16(aFilterPatterns[i]);+				lFilterPatterns[i] = (wchar_t *) malloc((wcslen(lTmpWChar) + 1)*sizeof(wchar_t *));+	  if (lFilterPatterns[i]) wcscpy(lFilterPatterns[i], lTmpWChar);+		}++		if (aTitle)+		{+				if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aTitle);+				else lTmpWChar = tinyfd_mbcsTo16(aTitle);+				wcscpy(lTitle, lTmpWChar);+		}+		if (aDefaultPathAndOrFile)+		{+				if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aDefaultPathAndOrFile);+				else lTmpWChar = tinyfd_mbcsTo16(aDefaultPathAndOrFile);+				wcscpy(lDefaultPathAndFile, lTmpWChar);+		}+		if (aSingleFilterDescription)+		{+				if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aSingleFilterDescription);+				else lTmpWChar = tinyfd_mbcsTo16(aSingleFilterDescription);+				wcscpy(lSingleFilterDescription, lTmpWChar);+		}++		lTmpWChar = tinyfd_openFileDialogW(+				lTitle,+				lDefaultPathAndFile,+				aNumOfFilterPatterns,+				(wchar_t const**) lFilterPatterns, /*stupid cast for gcc*/+				lSingleFilterDescription,+				aAllowMultipleSelects);++		for (i = 0; i < aNumOfFilterPatterns; i++)+		{+				free(lFilterPatterns[i]);+		}+		free(lFilterPatterns);++		if (!lTmpWChar) return NULL;++		if (tinyfd_winUtf8) lTmpChar = tinyfd_utf16to8(lTmpWChar);+		else lTmpChar = tinyfd_utf16toMbcs(lTmpWChar);+		(void)tinyfd_openFileDialogW(NULL, NULL, 0, NULL, NULL, -1);++		return lTmpChar;+}+++static char * selectFolderDialogWinGui(+		char * aoBuff,+		char const * aTitle, /*  NULL or "" */+		char const * aDefaultPath) /* NULL or "" */+{+		wchar_t lTitle[128] = L"";+		wchar_t lDefaultPath[MAX_PATH_OR_CMD] = L"";+		wchar_t * lTmpWChar;+		char * lTmpChar;++		if (aTitle)+		{+				if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aTitle);+				else lTmpWChar = tinyfd_mbcsTo16(aTitle);+				wcscpy(lTitle, lTmpWChar);+		}+		if (aDefaultPath)+		{+				if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aDefaultPath);+				else lTmpWChar = tinyfd_mbcsTo16(aDefaultPath);+				wcscpy(lDefaultPath, lTmpWChar);+		}++		lTmpWChar = tinyfd_selectFolderDialogW(+				lTitle,+				lDefaultPath);++		if (!lTmpWChar)+		{+				return NULL;+		}++		if (tinyfd_winUtf8) lTmpChar = tinyfd_utf16to8(lTmpWChar);+		else lTmpChar = tinyfd_utf16toMbcs(lTmpWChar);+		strcpy(aoBuff, lTmpChar);++		return aoBuff;+}+++static char * colorChooserWinGui(+		char const * aTitle, /* NULL or "" */+		char const * aDefaultHexRGB, /* NULL or "#FF0000"*/+		unsigned char const aDefaultRGB[3], /* { 0 , 255 , 255 } */+		unsigned char aoResultRGB[3]) /* { 0 , 0 , 0 } */+{+		static char lResultHexRGB[8];++		wchar_t lTitle[128];+		wchar_t * lTmpWChar;+		char * lTmpChar;+		wchar_t lDefaultHexRGB[16] = L"";++				if (aTitle)+				{+						if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aTitle);+						else lTmpWChar = tinyfd_mbcsTo16(aTitle);+						wcscpy(lTitle, lTmpWChar);+				}+				if (aDefaultHexRGB)+				{+						if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aDefaultHexRGB);+						else lTmpWChar = tinyfd_mbcsTo16(aDefaultHexRGB);+						wcscpy(lDefaultHexRGB, lTmpWChar);+				}++		lTmpWChar = tinyfd_colorChooserW(+				lTitle,+				lDefaultHexRGB,+				aDefaultRGB,+				aoResultRGB );++		if (!lTmpWChar)+		{+				return NULL;+		}++				if (tinyfd_winUtf8) lTmpChar = tinyfd_utf16to8(lTmpWChar);+				else lTmpChar = tinyfd_utf16toMbcs(lTmpWChar);+				strcpy(lResultHexRGB, lTmpChar);++		return lResultHexRGB;+}+++static int dialogPresent(void)+{+	static int lDialogPresent = -1 ;+	char lBuff[MAX_PATH_OR_CMD] ;+	FILE * lIn ;+	char const * lString = "dialog.exe";+	if (!tinyfd_allowCursesDialogs) return 0;+	if (lDialogPresent < 0)+	{+		lIn = ( FILE * ) _popen("where dialog.exe", "r");+		if ( ! lIn )+		{+				lDialogPresent = 0 ;+				return 0 ;+		}+		while ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL )+		{}+		_pclose( lIn ) ;+		if ( lBuff[strlen( lBuff ) -1] == '\n' )+		{+				lBuff[strlen( lBuff ) -1] = '\0' ;+		}+		if ( strcmp(lBuff+strlen(lBuff)-strlen(lString),lString) )+		{+				lDialogPresent = 0 ;+		}+		else+		{+				lDialogPresent = 1 ;+		}+	}+	return lDialogPresent;+}+++static int messageBoxWinConsole(+	char const * aTitle , /* NULL or "" */+	char const * aMessage , /* NULL or ""  may contain \n and \t */+	char const * aDialogType , /* "ok" "okcancel" "yesno" "yesnocancel" */+	char const * aIconType , /* "info" "warning" "error" "question" */+	int aDefaultButton ) /* 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel */+{+		char lDialogString[MAX_PATH_OR_CMD];+		char lDialogFile[MAX_PATH_OR_CMD];+		FILE * lIn;+		char lBuff[MAX_PATH_OR_CMD] = "";+		(void)aIconType;++				strcpy(lDialogString, "dialog ");+				if (aTitle && strlen(aTitle))+		{+				strcat(lDialogString, "--title \"") ;+				strcat(lDialogString, aTitle) ;+				strcat(lDialogString, "\" ") ;+		}++		if ( aDialogType && ( !strcmp( "okcancel" , aDialogType )+				|| !strcmp("yesno", aDialogType) || !strcmp("yesnocancel", aDialogType) ) )+		{+				strcat(lDialogString, "--backtitle \"") ;+				strcat(lDialogString, "tab: move focus") ;+				strcat(lDialogString, "\" ") ;+		}++		if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) )+		{+				if ( ! aDefaultButton )+				{+						strcat( lDialogString , "--defaultno " ) ;+				}+				strcat( lDialogString ,+								"--yes-label \"Ok\" --no-label \"Cancel\" --yesno " ) ;+		}+		else if ( aDialogType && ! strcmp( "yesno" , aDialogType ) )+		{+				if ( ! aDefaultButton )+				{+						strcat( lDialogString , "--defaultno " ) ;+				}+				strcat( lDialogString , "--yesno " ) ;+		}+		else if (aDialogType && !strcmp("yesnocancel", aDialogType))+		{+				if (!aDefaultButton)+				{+						strcat(lDialogString, "--defaultno ");+				}+				strcat(lDialogString, "--menu ");+		}+		else+		{+				strcat( lDialogString , "--msgbox " ) ;+		}++		strcat( lDialogString , "\"" ) ;+		if ( aMessage && strlen(aMessage) )+		{+				tfd_replaceSubStr( aMessage , "\n" , "\\n" , lBuff ) ;+				strcat(lDialogString, lBuff) ;+				lBuff[0]='\0';+		}+		strcat(lDialogString, "\" ");++		if (aDialogType && !strcmp("yesnocancel", aDialogType))+		{+				strcat(lDialogString, "0 60 0 Yes \"\" No \"\"");+				strcat(lDialogString, "2>>");+		}+		else+		{+				strcat(lDialogString, "10 60");+				strcat(lDialogString, " && echo 1 > ");+		}++		strcpy(lDialogFile, getenv("TEMP"));+		strcat(lDialogFile, "\\tinyfd.txt");+		strcat(lDialogString, lDialogFile);++		/*if (tinyfd_verbose) printf( "lDialogString: %s\n" , lDialogString ) ;*/+		system( lDialogString ) ;++		lIn = ( FILE * ) fopen(lDialogFile, "r");+		if ( ! lIn )+		{+				remove(lDialogFile);+				return 0 ;+		}+				while (fgets(lBuff, sizeof(lBuff), lIn) != NULL)+		{}+		fclose(lIn);+		remove(lDialogFile);+	if ( lBuff[strlen( lBuff ) -1] == '\n' )+	{+		lBuff[strlen( lBuff ) -1] = '\0' ;+	}++		/* if (tinyfd_verbose) printf("lBuff: %s\n", lBuff); */+		if ( ! strlen(lBuff) )+		{+				return 0;+		}++		if (aDialogType && !strcmp("yesnocancel", aDialogType))+		{+				if (lBuff[0] == 'Y') return 1;+				else return 2;+		}++		return 1;+}+++static int inputBoxWinConsole(+		char * aoBuff ,+		char const * aTitle , /* NULL or "" */+		char const * aMessage , /* NULL or "" may NOT contain \n nor \t */+		char const * aDefaultInput ) /* "" , if NULL it's a passwordBox */+{+		char lDialogString[MAX_PATH_OR_CMD];+		char lDialogFile[MAX_PATH_OR_CMD];+		FILE * lIn;+		int lResult;++		strcpy(lDialogFile, getenv("TEMP"));+		strcat(lDialogFile, "\\tinyfd.txt");+		strcpy(lDialogString , "echo|set /p=1 >" ) ;+		strcat(lDialogString, lDialogFile);+		strcat( lDialogString , " & " ) ;++		strcat( lDialogString , "dialog " ) ;+		if ( aTitle && strlen(aTitle) )+		{+				strcat(lDialogString, "--title \"") ;+				strcat(lDialogString, aTitle) ;+				strcat(lDialogString, "\" ") ;+		}++		strcat(lDialogString, "--backtitle \"") ;+		strcat(lDialogString, "tab: move focus") ;+		if ( ! aDefaultInput )+		{+				strcat(lDialogString, " (sometimes nothing, no blink nor star, is shown in text field)") ;+		}++		strcat(lDialogString, "\" ") ;++		if ( ! aDefaultInput )+		{+				strcat( lDialogString , "--insecure --passwordbox" ) ;+		}+		else+		{+				strcat( lDialogString , "--inputbox" ) ;+		}+		strcat( lDialogString , " \"" ) ;+		if ( aMessage && strlen(aMessage) )+		{+				strcat(lDialogString, aMessage) ;+		}+		strcat(lDialogString,"\" 10 60 ") ;+		if ( aDefaultInput && strlen(aDefaultInput) )+		{+				strcat(lDialogString, "\"") ;+				strcat(lDialogString, aDefaultInput) ;+				strcat(lDialogString, "\" ") ;+		}++		strcat(lDialogString, "2>>");+		strcpy(lDialogFile, getenv("TEMP"));+		strcat(lDialogFile, "\\tinyfd.txt");+		strcat(lDialogString, lDialogFile);+		strcat(lDialogString, " || echo 0 > ");+		strcat(lDialogString, lDialogFile);++		/* printf( "lDialogString: %s\n" , lDialogString ) ; */+		system( lDialogString ) ;++		lIn = ( FILE * ) fopen(lDialogFile, "r");+		if ( ! lIn )+		{+				remove(lDialogFile);+								aoBuff[0] = '\0';+								return 0;+		}+		while (fgets(aoBuff, MAX_PATH_OR_CMD, lIn) != NULL)+		{}+		fclose(lIn);++		wipefile(lDialogFile);+		remove(lDialogFile);+	if ( aoBuff[strlen( aoBuff ) -1] == '\n' )+	{+		aoBuff[strlen( aoBuff ) -1] = '\0' ;+	}+		/* printf( "aoBuff: %s\n" , aoBuff ) ; */++		/* printf( "aoBuff: %s len: %lu \n" , aoBuff , strlen(aoBuff) ) ; */+	lResult =  strncmp( aoBuff , "1" , 1) ? 0 : 1 ;+		/* printf( "lResult: %d \n" , lResult ) ; */+		if ( ! lResult )+		{+				aoBuff[0] = '\0';+				return 0 ;+		}+		/* printf( "aoBuff+1: %s\n" , aoBuff+1 ) ; */+		strcpy(aoBuff, aoBuff+3);+		return 1;+}+++static char * saveFileDialogWinConsole(+		char * aoBuff ,+		char const * aTitle , /* NULL or "" */+		char const * aDefaultPathAndOrFile ) /* NULL or "" */+{+		char lDialogString[MAX_PATH_OR_CMD];+		char lPathAndFile[MAX_PATH_OR_CMD] = "";+		FILE * lIn;++		strcpy( lDialogString , "dialog " ) ;+		if ( aTitle && strlen(aTitle) )+		{+				strcat(lDialogString, "--title \"") ;+				strcat(lDialogString, aTitle) ;+				strcat(lDialogString, "\" ") ;+		}++		strcat(lDialogString, "--backtitle \"") ;+		strcat(lDialogString,+				"tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY") ;+		strcat(lDialogString, "\" ") ;++		strcat( lDialogString , "--fselect \"" ) ;+		if ( aDefaultPathAndOrFile && strlen(aDefaultPathAndOrFile) )+		{+				/* dialog.exe uses unix separators even on windows */+				strcpy(lPathAndFile, aDefaultPathAndOrFile);+				replaceChr( lPathAndFile , '\\' , '/' ) ;+		}++		/* dialog.exe needs at least one separator */+		if ( ! strchr(lPathAndFile, '/') )+		{+				strcat(lDialogString, "./") ;+		}+		strcat(lDialogString, lPathAndFile) ;+		strcat(lDialogString, "\" 0 60 2>");+		strcpy(lPathAndFile, getenv("TEMP"));+		strcat(lPathAndFile, "\\tinyfd.txt");+		strcat(lDialogString, lPathAndFile);++		/* printf( "lDialogString: %s\n" , lDialogString ) ; */+		system( lDialogString ) ;++		lIn = ( FILE * ) fopen(lPathAndFile, "r");+		if ( ! lIn )+		{+				remove(lPathAndFile);+				return NULL;+		}+		while (fgets(aoBuff, MAX_PATH_OR_CMD, lIn) != NULL)+		{}+		fclose(lIn);+		remove(lPathAndFile);+		replaceChr( aoBuff , '/' , '\\' ) ;+		/* printf( "aoBuff: %s\n" , aoBuff ) ; */+		getLastName(lDialogString,aoBuff);+		if ( ! strlen(lDialogString) )+		{+				return NULL;+		}+		return aoBuff;+}+++static char * openFileDialogWinConsole(+		char const * aTitle , /*  NULL or "" */+		char const * aDefaultPathAndOrFile ) /*  NULL or "" */+{+		char lFilterPatterns[MAX_PATH_OR_CMD] = "";+		char lDialogString[MAX_PATH_OR_CMD] ;+		FILE * lIn;++				static char aoBuff[MAX_PATH_OR_CMD];++		strcpy( lDialogString , "dialog " ) ;+		if ( aTitle && strlen(aTitle) )+		{+				strcat(lDialogString, "--title \"") ;+				strcat(lDialogString, aTitle) ;+				strcat(lDialogString, "\" ") ;+		}++		strcat(lDialogString, "--backtitle \"") ;+		strcat(lDialogString,+				"tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY") ;+		strcat(lDialogString, "\" ") ;++		strcat( lDialogString , "--fselect \"" ) ;+		if ( aDefaultPathAndOrFile && strlen(aDefaultPathAndOrFile) )+		{+				/* dialog.exe uses unix separators even on windows */+				strcpy(lFilterPatterns, aDefaultPathAndOrFile);+				replaceChr( lFilterPatterns , '\\' , '/' ) ;+		}++		/* dialog.exe needs at least one separator */+		if ( ! strchr(lFilterPatterns, '/') )+		{+				strcat(lDialogString, "./") ;+		}+		strcat(lDialogString, lFilterPatterns) ;+		strcat(lDialogString, "\" 0 60 2>");+		strcpy(lFilterPatterns, getenv("TEMP"));+		strcat(lFilterPatterns, "\\tinyfd.txt");+		strcat(lDialogString, lFilterPatterns);++		/* printf( "lDialogString: %s\n" , lDialogString ) ; */+		system( lDialogString ) ;++		lIn = ( FILE * ) fopen(lFilterPatterns, "r");+		if ( ! lIn )+		{+				remove(lFilterPatterns);+				return NULL;+		}+		while (fgets(aoBuff, MAX_PATH_OR_CMD, lIn) != NULL)+		{}+		fclose(lIn);+		remove(lFilterPatterns);+		replaceChr( aoBuff , '/' , '\\' ) ;+		/* printf( "aoBuff: %s\n" , aoBuff ) ; */+		return aoBuff;+}+++static char * selectFolderDialogWinConsole(+		char * aoBuff ,+		char const * aTitle , /*  NULL or "" */+		char const * aDefaultPath ) /* NULL or "" */+{+		char lDialogString[MAX_PATH_OR_CMD] ;+		char lString[MAX_PATH_OR_CMD] ;+		FILE * lIn ;++		strcpy( lDialogString , "dialog " ) ;+		if ( aTitle && strlen(aTitle) )+		{+				strcat(lDialogString, "--title \"") ;+				strcat(lDialogString, aTitle) ;+				strcat(lDialogString, "\" ") ;+		}++		strcat(lDialogString, "--backtitle \"") ;+		strcat(lDialogString,+				"tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY") ;+		strcat(lDialogString, "\" ") ;++		strcat( lDialogString , "--dselect \"" ) ;+		if ( aDefaultPath && strlen(aDefaultPath) )+		{+				/* dialog.exe uses unix separators even on windows */+				strcpy(lString, aDefaultPath) ;+				ensureFinalSlash(lString);+				replaceChr( lString , '\\' , '/' ) ;+				strcat(lDialogString, lString) ;+		}+		else+		{+				/* dialog.exe needs at least one separator */+				strcat(lDialogString, "./") ;+		}+		strcat(lDialogString, "\" 0 60 2>");+		strcpy(lString, getenv("TEMP"));+		strcat(lString, "\\tinyfd.txt");+		strcat(lDialogString, lString);++		/* printf( "lDialogString: %s\n" , lDialogString ) ; */+		system( lDialogString ) ;++		lIn = ( FILE * ) fopen(lString, "r") ;+		if ( ! lIn )+		{+				remove(lString);+				return NULL;+		}+		while (fgets(aoBuff, MAX_PATH_OR_CMD, lIn) != NULL)+		{}+		fclose(lIn);+		remove(lString);+		replaceChr( aoBuff , '/' , '\\' ) ;+		/* printf( "aoBuff: %s\n" , aoBuff ) ; */+		return aoBuff;+}++static void writeUtf8( char const * aUtf8String )+{+		unsigned long lNum;+		void * lConsoleHandle;+		wchar_t * lTmpWChar;++		lConsoleHandle = GetStdHandle(STD_OUTPUT_HANDLE);+		lTmpWChar = tinyfd_utf8to16(aUtf8String);+		(void)WriteConsoleW(lConsoleHandle, lTmpWChar, (DWORD) wcslen(lTmpWChar), &lNum, NULL);+}+++int tinyfd_messageBox(+		char const * aTitle, /* NULL or "" */+		char const * aMessage, /* NULL or ""  may contain \n and \t */+		char const * aDialogType, /* "ok" "okcancel" "yesno" "yesnocancel" */+		char const * aIconType, /* "info" "warning" "error" "question" */+		int aDefaultButton) /* 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel */+{+		char lChar;+		UINT lOriginalCP = 0;+		UINT lOriginalOutputCP = 0;++		if (tfd_quoteDetected(aTitle)) return tinyfd_messageBox("INVALID TITLE WITH QUOTES", aMessage, aDialogType, aIconType, aDefaultButton);+		if (tfd_quoteDetected(aMessage)) return tinyfd_messageBox(aTitle, "INVALID MESSAGE WITH QUOTES", aDialogType, aIconType, aDefaultButton);++		if ((!tinyfd_forceConsole || !(GetConsoleWindow() || dialogPresent()))+				&& (!getenv("SSH_CLIENT") || getenvDISPLAY()))+		{+				if (aTitle&&!strcmp(aTitle, "tinyfd_query")){ strcpy(tinyfd_response, "windows"); return 1; }+				return messageBoxWinGui(aTitle, aMessage, aDialogType, aIconType, aDefaultButton);+		}+		else if (dialogPresent())+		{+				if (aTitle&&!strcmp(aTitle, "tinyfd_query")){ strcpy(tinyfd_response, "dialog"); return 0; }+				return messageBoxWinConsole(+						aTitle, aMessage, aDialogType, aIconType, aDefaultButton);+		}+		else+		{+				if (!tinyfd_winUtf8)+				{+						lOriginalCP = GetConsoleCP();+						lOriginalOutputCP = GetConsoleOutputCP();+						(void)SetConsoleCP(GetACP());+						(void)SetConsoleOutputCP(GetACP());+				}++				if (aTitle&&!strcmp(aTitle, "tinyfd_query")){ strcpy(tinyfd_response, "basicinput"); return 0; }+				if (!gWarningDisplayed && !tinyfd_forceConsole)+				{+						gWarningDisplayed = 1;+						printf("\n\n%s\n", gTitle);+						printf("%s\n\n", tinyfd_needs);+				}++				if (aTitle && strlen(aTitle))+				{+						printf("\n");+						if (tinyfd_winUtf8) writeUtf8(aTitle);+						else printf("%s", aTitle);+						printf("\n\n");+				}+				if (aDialogType && !strcmp("yesno", aDialogType))+				{+						do+						{+								if (aMessage && strlen(aMessage))+								{+										if (tinyfd_winUtf8) writeUtf8(aMessage);+										else printf("%s", aMessage);+										printf("\n");+								}+								printf("y/n: ");+								lChar = (char)tolower(_getch());+								printf("\n\n");+						} while (lChar != 'y' && lChar != 'n');+						if (!tinyfd_winUtf8) { (void)SetConsoleCP(lOriginalCP); (void)SetConsoleOutputCP(lOriginalOutputCP); }+						return lChar == 'y' ? 1 : 0;+				}+				else if (aDialogType && !strcmp("okcancel", aDialogType))+				{+						do+						{+								if (aMessage && strlen(aMessage))+								{+										if (tinyfd_winUtf8) writeUtf8(aMessage);+										else printf("%s", aMessage);+										printf("\n");+								}+								printf("[O]kay/[C]ancel: ");+								lChar = (char)tolower(_getch());+								printf("\n\n");+						} while (lChar != 'o' && lChar != 'c');+						if (!tinyfd_winUtf8) { (void)SetConsoleCP(lOriginalCP); (void)SetConsoleOutputCP(lOriginalOutputCP); }+						return lChar == 'o' ? 1 : 0;+				}+				else if (aDialogType && !strcmp("yesnocancel", aDialogType))+				{+						do+						{+								if (aMessage && strlen(aMessage))+								{+										if (tinyfd_winUtf8) writeUtf8(aMessage);+										else printf("%s", aMessage);+										printf("\n");+								}+								printf("[Y]es/[N]o/[C]ancel: ");+								lChar = (char)tolower(_getch());+								printf("\n\n");+						} while (lChar != 'y' && lChar != 'n' && lChar != 'c');+						if (!tinyfd_winUtf8) { (void)SetConsoleCP(lOriginalCP); (void)SetConsoleOutputCP(lOriginalOutputCP); }+						return (lChar == 'y') ? 1 : (lChar == 'n') ? 2 : 0;+				}+				else+				{+						if (aMessage && strlen(aMessage))+						{+								if (tinyfd_winUtf8) writeUtf8(aMessage);+								else printf("%s", aMessage);+								printf("\n\n");+						}+						printf("press enter to continue "); fflush(stdout);+						lChar = (char)_getch();+						printf("\n\n");+						if (!tinyfd_winUtf8) { (void)SetConsoleCP(lOriginalCP); (void)SetConsoleOutputCP(lOriginalOutputCP); }+						return 1;+				}+		}+}+++/* return has only meaning for tinyfd_query */+int tinyfd_notifyPopup(+		char const * aTitle, /* NULL or "" */+		char const * aMessage , /* NULL or "" may contain \n \t */+		char const * aIconType ) /* "info" "warning" "error" */+{+	if (tfd_quoteDetected(aTitle)) return tinyfd_notifyPopup("INVALID TITLE WITH QUOTES", aMessage, aIconType);+	if (tfd_quoteDetected(aMessage)) return tinyfd_notifyPopup(aTitle, "INVALID MESSAGE WITH QUOTES", aIconType);++	if ( powershellPresent()+		&& ( !tinyfd_forceConsole || !(GetConsoleWindow() || dialogPresent()) )+		&& (!getenv("SSH_CLIENT") || getenvDISPLAY()) )+	{+		if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return 1;}+		return notifyWinGui(aTitle, aMessage, aIconType);+	}+	else return tinyfd_messageBox(aTitle, aMessage, "ok" , aIconType, 0);+}+++/* returns NULL on cancel */+char * tinyfd_inputBox(+		char const * aTitle , /* NULL or "" */+		char const * aMessage , /* NULL or "" (\n and \t have no effect) */+		char const * aDefaultInput ) /* "" , if NULL it's a passwordBox */+{+		static char lBuff[MAX_PATH_OR_CMD] = "";+		char * lEOF;++		DWORD mode = 0;+		HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);++		unsigned long lNum;+		void * lConsoleHandle;+		char * lTmpChar;+		wchar_t lBuffW[1024];++		UINT lOriginalCP = 0;+		UINT lOriginalOutputCP = 0;++		if (!aTitle && !aMessage && !aDefaultInput) return lBuff; /* now I can fill lBuff from outside */++		if (tfd_quoteDetected(aTitle)) return tinyfd_inputBox("INVALID TITLE WITH QUOTES", aMessage, aDefaultInput);+		if (tfd_quoteDetected(aMessage)) return tinyfd_inputBox(aTitle, "INVALID MESSAGE WITH QUOTES", aDefaultInput);+		if (tfd_quoteDetected(aDefaultInput)) return tinyfd_inputBox(aTitle, aMessage, "INVALID DEFAULT_INPUT WITH QUOTES: use the GRAVE ACCENT \\x60 instead.");++	mode = 0;+	hStdin = GetStdHandle(STD_INPUT_HANDLE);++	if ((!tinyfd_forceConsole || !(+			GetConsoleWindow() ||+			dialogPresent()))+						&& (!getenv("SSH_CLIENT") || getenvDISPLAY()))+	{+		if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return (char *)1;}+		lBuff[0]='\0';+				if (inputBoxWinGui(lBuff, aTitle, aMessage, aDefaultInput)) return lBuff;+				else return NULL;+		}+	else if ( dialogPresent() )+	{+		if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char *)0;}+		lBuff[0]='\0';+				if (inputBoxWinConsole(lBuff, aTitle, aMessage, aDefaultInput) ) return lBuff;+				else return NULL;+		}+	else+	{+	  if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return (char *)0;}+	  lBuff[0]='\0';+	  if (!gWarningDisplayed && !tinyfd_forceConsole)+	  {+		  gWarningDisplayed = 1 ;+		  printf("\n\n%s\n", gTitle);+		  printf("%s\n\n", tinyfd_needs);+	  }++	  if (!tinyfd_winUtf8)+	  {+			  lOriginalCP = GetConsoleCP();+			  lOriginalOutputCP = GetConsoleOutputCP();+			  (void)SetConsoleCP(GetACP());+			  (void)SetConsoleOutputCP(GetACP());+	  }++	  if (aTitle && strlen(aTitle))+	  {+				printf("\n");+				if (tinyfd_winUtf8) writeUtf8(aTitle);+				else printf("%s", aTitle);+				printf("\n\n");+		  }+	  if ( aMessage && strlen(aMessage) )+	  {+				if (tinyfd_winUtf8) writeUtf8(aMessage);+				else printf("%s", aMessage);+				printf("\n");+	  }+	  printf("(ctrl-Z + enter to cancel): "); fflush(stdout);+	  if ( ! aDefaultInput )+	  {+				  (void) GetConsoleMode(hStdin, &mode);+				  (void) SetConsoleMode(hStdin, mode & (~ENABLE_ECHO_INPUT));+	  }+		  if (tinyfd_winUtf8)+		  {+				lConsoleHandle = GetStdHandle(STD_INPUT_HANDLE);+				(void) ReadConsoleW(lConsoleHandle, lBuffW, MAX_PATH_OR_CMD, &lNum, NULL);+				if (!aDefaultInput)+				{+						(void)SetConsoleMode(hStdin, mode);+						printf("\n");+				}+				lBuffW[lNum] = '\0';+				if (lBuffW[wcslen(lBuffW) - 1] == '\n') lBuffW[wcslen(lBuffW) - 1] = '\0';+				if (lBuffW[wcslen(lBuffW) - 1] == '\r') lBuffW[wcslen(lBuffW) - 1] = '\0';+				lTmpChar = tinyfd_utf16to8(lBuffW);+				if (lTmpChar)+				{+						strcpy(lBuff, lTmpChar);+						return lBuff;+				}+				else+						return NULL;+		  }+		  else+		  {+				  lEOF = fgets(lBuff, MAX_PATH_OR_CMD, stdin);+				  if (!aDefaultInput)+				  {+						  (void)SetConsoleMode(hStdin, mode);+						  printf("\n");+				  }++				  if (!tinyfd_winUtf8)+				  {+						  (void)SetConsoleCP(lOriginalCP);+						  (void)SetConsoleOutputCP(lOriginalOutputCP);+				  }++				  if (!lEOF)+				  {+						  return NULL;+				  }+				  printf("\n");+				  if (strchr(lBuff, 27))+				  {+						  return NULL;+				  }+				  if (lBuff[strlen(lBuff) - 1] == '\n')+				  {+						  lBuff[strlen(lBuff) - 1] = '\0';+				  }+				  return lBuff;+				}+		}+}+++char * tinyfd_saveFileDialog(+		char const * aTitle , /* NULL or "" */+		char const * aDefaultPathAndOrFile , /* NULL or "" */+		int aNumOfFilterPatterns , /* 0 */+		char const * const * aFilterPatterns , /* NULL or {"*.jpg","*.png"} */+		char const * aSingleFilterDescription ) /* NULL or "image files" */+{+		static char lBuff[MAX_PATH_OR_CMD] ;+		char lString[MAX_PATH_OR_CMD] ;+		char * p ;+				char * lPointerInputBox;+				int i;++		lBuff[0]='\0';++		if ( ! aFilterPatterns ) aNumOfFilterPatterns = 0 ;+				if (tfd_quoteDetected(aTitle)) return tinyfd_saveFileDialog("INVALID TITLE WITH QUOTES", aDefaultPathAndOrFile, aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription);+				if (tfd_quoteDetected(aDefaultPathAndOrFile)) return tinyfd_saveFileDialog(aTitle, "INVALID DEFAULT_PATH WITH QUOTES", aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription);+				if (tfd_quoteDetected(aSingleFilterDescription)) return tinyfd_saveFileDialog(aTitle, aDefaultPathAndOrFile, aNumOfFilterPatterns, aFilterPatterns, "INVALID FILTER_DESCRIPTION WITH QUOTES");+				for (i = 0; i < aNumOfFilterPatterns; i++)+				{+						if (tfd_quoteDetected(aFilterPatterns[i])) return tinyfd_saveFileDialog("INVALID FILTER_PATTERN WITH QUOTES: use the GRAVE ACCENT \\x60 instead.", aDefaultPathAndOrFile, 0, NULL, NULL);+				}+++				if ( ( !tinyfd_forceConsole || !( GetConsoleWindow() || dialogPresent() ) )+						&& (!getenv("SSH_CLIENT") || getenvDISPLAY()))+		{+			if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return (char *)1;}+			p = saveFileDialogWinGui(lBuff,+								aTitle, aDefaultPathAndOrFile, aNumOfFilterPatterns, (char const * const *)aFilterPatterns, aSingleFilterDescription);+		}+				else if (dialogPresent())+				{+						if (aTitle&&!strcmp(aTitle, "tinyfd_query")){ strcpy(tinyfd_response, "dialog"); return (char *)0; }+						p = saveFileDialogWinConsole(lBuff, aTitle, aDefaultPathAndOrFile);+				}+				else+				{+						if (aTitle&&!strcmp(aTitle, "tinyfd_query")){ strcpy(tinyfd_response, "basicinput"); return (char *)0; }+						strcpy(lBuff, "Save file in ");+						strcat(lBuff, getCurDir());++						lPointerInputBox = tinyfd_inputBox(NULL,NULL,NULL); /* obtain a pointer on the current content of tinyfd_inputBox */+						if (lPointerInputBox) strcpy(lString, lPointerInputBox); /* preserve the current content of tinyfd_inputBox */+						p = tinyfd_inputBox(aTitle, lBuff, "");+						if (p) strcpy(lBuff, p); else lBuff[0] = '\0';+						if (lPointerInputBox) strcpy(lPointerInputBox, lString); /* restore its previous content to tinyfd_inputBox */+						p = lBuff;+				}++		if ( ! p || ! strlen( p )  )+		{+				return NULL;+		}+		getPathWithoutFinalSlash( lString , p ) ;+		if ( strlen( lString ) && ! dirExists( lString ) )+		{+				return NULL ;+		}+		getLastName(lString,p);+		if ( ! filenameValid(lString) )+		{+				return NULL;+		}+		return p ;+}+++/* in case of multiple files, the separator is | */+char * tinyfd_openFileDialog(+	char const * aTitle , /* NULL or "" */+		char const * aDefaultPathAndOrFile, /* NULL or "" */+	int aNumOfFilterPatterns , /* 0 */+		char const * const * aFilterPatterns, /* NULL or {"*.jpg","*.png"} */+		char const * aSingleFilterDescription, /* NULL or "image files" */+	int aAllowMultipleSelects ) /* 0 or 1 */+{+	static char lBuff[MAX_PATH_OR_CMD];+	char lString[MAX_PATH_OR_CMD];+		char * p;+		char * lPointerInputBox;+		int i;++	if ( ! aFilterPatterns ) aNumOfFilterPatterns = 0 ;+		if (tfd_quoteDetected(aTitle)) return tinyfd_openFileDialog("INVALID TITLE WITH QUOTES", aDefaultPathAndOrFile, aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription, aAllowMultipleSelects);+		if (tfd_quoteDetected(aDefaultPathAndOrFile)) return tinyfd_openFileDialog(aTitle, "INVALID DEFAULT_PATH WITH QUOTES", aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription, aAllowMultipleSelects);+		if (tfd_quoteDetected(aSingleFilterDescription)) return tinyfd_openFileDialog(aTitle, aDefaultPathAndOrFile, aNumOfFilterPatterns, aFilterPatterns, "INVALID FILTER_DESCRIPTION WITH QUOTES", aAllowMultipleSelects);+		for (i = 0; i < aNumOfFilterPatterns; i++)+		{+				if (tfd_quoteDetected(aFilterPatterns[i])) return tinyfd_openFileDialog("INVALID FILTER_PATTERN WITH QUOTES: use the GRAVE ACCENT \\x60 instead.", aDefaultPathAndOrFile, 0, NULL, NULL, aAllowMultipleSelects);+		}++	if ( ( !tinyfd_forceConsole || !( GetConsoleWindow() || dialogPresent() ) )+				&& (!getenv("SSH_CLIENT") || getenvDISPLAY()))+		{+				if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return (char *)1;}+				p = openFileDialogWinGui( aTitle, aDefaultPathAndOrFile, aNumOfFilterPatterns,+										(char const * const *)aFilterPatterns, aSingleFilterDescription, aAllowMultipleSelects);+		}+				else if (dialogPresent())+				{+						if (aTitle&&!strcmp(aTitle, "tinyfd_query")){ strcpy(tinyfd_response, "dialog"); return (char *)0; }+						p = openFileDialogWinConsole(aTitle, aDefaultPathAndOrFile);+				}+				else+				{+						if (aTitle&&!strcmp(aTitle, "tinyfd_query")){ strcpy(tinyfd_response, "basicinput"); return (char *)0; }+						strcpy(lBuff, "Open file from ");+						strcat(lBuff, getCurDir());+						lPointerInputBox = tinyfd_inputBox(NULL, NULL, NULL); /* obtain a pointer on the current content of tinyfd_inputBox */+						if (lPointerInputBox) strcpy(lString, lPointerInputBox); /* preserve the current content of tinyfd_inputBox */+						p = tinyfd_inputBox(aTitle, lBuff, "");+						if (p) strcpy(lBuff, p); else lBuff[0] = '\0';+						if (lPointerInputBox) strcpy(lPointerInputBox, lString); /* restore its previous content to tinyfd_inputBox */+						p = lBuff;+				}++		if ( ! p || ! strlen( p )  )+		{+				return NULL;+		}+		if ( aAllowMultipleSelects && strchr(p, '|') )+		{+				p = ensureFilesExist( (char *) p , p ) ;+		}+		else if ( ! fileExists(p) )+		{+				return NULL ;+		}+		/* printf( "lBuff3: %s\n" , p ) ; */+		return p ;+}+++char * tinyfd_selectFolderDialog(+		char const * aTitle , /* NULL or "" */+		char const * aDefaultPath ) /* NULL or "" */+{+		static char lBuff[MAX_PATH_OR_CMD];+		char * p;+		char * lPointerInputBox;+		char lString[MAX_PATH_OR_CMD];++		if (tfd_quoteDetected(aTitle)) return tinyfd_selectFolderDialog("INVALID TITLE WITH QUOTES", aDefaultPath);+		if (tfd_quoteDetected(aDefaultPath)) return tinyfd_selectFolderDialog(aTitle, "INVALID DEFAULT_PATH WITH QUOTES");++	if ( ( !tinyfd_forceConsole || !( GetConsoleWindow() || dialogPresent() ) )+				&& (!getenv("SSH_CLIENT") || getenvDISPLAY()))+		{+				if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return (char *)1;}+				p = selectFolderDialogWinGui(lBuff, aTitle, aDefaultPath);+		}+				else+				if (dialogPresent())+				{+						if (aTitle&&!strcmp(aTitle, "tinyfd_query")){ strcpy(tinyfd_response, "dialog"); return (char *)0; }+						p = selectFolderDialogWinConsole(lBuff, aTitle, aDefaultPath);+				}+				else+				{+						if (aTitle&&!strcmp(aTitle, "tinyfd_query")){ strcpy(tinyfd_response, "basicinput"); return (char *)0; }+						strcpy(lBuff, "Select folder from ");+						strcat(lBuff, getCurDir());+						lPointerInputBox = tinyfd_inputBox(NULL, NULL, NULL); /* obtain a pointer on the current content of tinyfd_inputBox */+						if (lPointerInputBox) strcpy(lString, lPointerInputBox); /* preserve the current content of tinyfd_inputBox */+						p = tinyfd_inputBox(aTitle, lBuff, "");+						if (p) strcpy(lBuff, p); else lBuff[0] = '\0';+						if (lPointerInputBox) strcpy(lPointerInputBox, lString); /* restore its previous content to tinyfd_inputBox */+						p = lBuff;+				}++		if ( ! p || ! strlen( p ) || ! dirExists( p ) )+		{+				return NULL ;+		}+		return p ;+}+++/* aDefaultRGB is used only if aDefaultHexRGB is absent */+/* aDefaultRGB and aoResultRGB can be the same array */+/* returns NULL on cancel */+/* returns the hexcolor as a string "#FF0000" */+/* aoResultRGB also contains the result */+char * tinyfd_colorChooser(+		char const * aTitle, /* NULL or "" */+		char const * aDefaultHexRGB, /* NULL or "" or "#FF0000"*/+		unsigned char const aDefaultRGB[3], /* { 0 , 255 , 255 } */+		unsigned char aoResultRGB[3]) /* { 0 , 0 , 0 } */+{+	static char lDefaultHexRGB[16];+	int i;+	char * p ;+	char * lPointerInputBox;+	char lString[MAX_PATH_OR_CMD];++	lDefaultHexRGB[0] = '\0';++	if (tfd_quoteDetected(aTitle)) return tinyfd_colorChooser("INVALID TITLE WITH QUOTES", aDefaultHexRGB, aDefaultRGB, aoResultRGB);+	if (tfd_quoteDetected(aDefaultHexRGB)) return tinyfd_colorChooser(aTitle, "INVALID DEFAULT_HEX_RGB WITH QUOTES: use the GRAVE ACCENT \\x60 instead.", aDefaultRGB, aoResultRGB);++	if ( (!tinyfd_forceConsole || !( GetConsoleWindow() || dialogPresent()) )+				&& (!getenv("SSH_CLIENT") || getenvDISPLAY()))+	{+				if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return (char *)1;}+				p = colorChooserWinGui(aTitle, aDefaultHexRGB, aDefaultRGB, aoResultRGB);+		if (p)+		{+			strcpy(lDefaultHexRGB, p);+			return lDefaultHexRGB;+		}+		return NULL;+	}+		else if (dialogPresent())+		{+				if (aTitle&&!strcmp(aTitle, "tinyfd_query")){ strcpy(tinyfd_response, "dialog"); return (char *)0; }+		}+		else+		{+				if (aTitle&&!strcmp(aTitle, "tinyfd_query")){ strcpy(tinyfd_response, "basicinput"); return (char *)0; }+		}++		if (aDefaultHexRGB && (strlen(aDefaultHexRGB)==7) )+		{+				strncpy(lDefaultHexRGB, aDefaultHexRGB,7);+				lDefaultHexRGB[7]='\0';+		}+		else+		{+				RGB2Hex(aDefaultRGB, lDefaultHexRGB);+		}++		lPointerInputBox = tinyfd_inputBox(NULL, NULL, NULL); /* obtain a pointer on the current content of tinyfd_inputBox */+		if (lPointerInputBox) strcpy(lString, lPointerInputBox); /* preserve the current content of tinyfd_inputBox */+		p = tinyfd_inputBox(aTitle, "Enter hex rgb color (i.e. #f5ca20)", lDefaultHexRGB);++	if ( !p || (strlen(p) != 7) || (p[0] != '#') )+	{+		return NULL ;+	}+	for ( i = 1 ; i < 7 ; i ++ )+	{+		if ( ! isxdigit( (int) p[i] ) )+		{+				return NULL ;+		}+	}+	Hex2RGB(p,aoResultRGB);++	strcpy(lDefaultHexRGB, p);++	if (lPointerInputBox) strcpy(lPointerInputBox, lString); /* restore its previous content to tinyfd_inputBox */++	return lDefaultHexRGB;+}+++#else /* unix */++static char gPython2Name[16];+static char gPython3Name[16];+static char gPythonName[16];++int tfd_isDarwin(void)+{+	static int lIsDarwin = -1 ;+	struct utsname lUtsname ;+	if ( lIsDarwin < 0 ) lIsDarwin = !uname(&lUtsname) && !strcmp(lUtsname.sysname,"Darwin") ;+	return lIsDarwin ;+}+++int tfd_isHaiku(void)+{+	static int lIsHaiku = -1 ;+	struct utsname lUtsname ;+	if ( lIsHaiku < 0 ) lIsHaiku = !uname(&lUtsname) && !strcmp(lUtsname.sysname,"Haiku") ;+	return lIsHaiku ;+}+++static int dirExists( char const * aDirPath )+{+	DIR * lDir ;+	if ( ! aDirPath || ! strlen( aDirPath ) )+		return 0 ;++	lDir = opendir( aDirPath ) ;+	if ( ! lDir )+		return 0 ;+	+	closedir( lDir ) ;+	+	return 1 ;+}+++static int detectPresence( char const * aExecutable )+{+   char lBuff[MAX_PATH_OR_CMD] ;+   char lTestedString[MAX_PATH_OR_CMD] = "command -v " ;+   FILE * lIn ;+#ifdef _GNU_SOURCE+   char* lAllocatedCharString;+   int lSubstringUndetected;+#endif++   strcat( lTestedString , aExecutable ) ;+   strcat( lTestedString, " 2>/dev/null ");+   lIn = ( FILE * ) popen( lTestedString , "r" ) ;+   if ( ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL )+	&& ( ! strchr( lBuff , ':' ) ) && ( strncmp(lBuff, "no ", 3) ) )+   {   /* present */+	  pclose( lIn ) ;++#ifdef _GNU_SOURCE /*to bypass this, just comment out "#define _GNU_SOURCE" at the top of the file*/+	  if ( lBuff[strlen( lBuff ) -1] == '\n' ) lBuff[strlen( lBuff ) -1] = '\0' ;+	  lAllocatedCharString = realpath(lBuff,NULL); /*same as canonicalize_file_name*/+	  lSubstringUndetected = ! strstr(lAllocatedCharString, aExecutable);+	  free(lAllocatedCharString);+	  if (lSubstringUndetected)+	  {+		 if (tinyfd_verbose) printf("detectPresence %s %d\n", aExecutable, 0);+		 return 0;+	  }+#endif /*_GNU_SOURCE*/++	  if (tinyfd_verbose) printf("detectPresence %s %d\n", aExecutable, 1);+	  return 1 ;+   }+   else+   {+	  pclose( lIn ) ;+	  if (tinyfd_verbose) printf("detectPresence %s %d\n", aExecutable, 0);+	  return 0 ;+   }+}+++static char * getVersion( char const * aExecutable ) /*version must be first numeral*/+{+	static char lBuff[MAX_PATH_OR_CMD] ;+	char lTestedString[MAX_PATH_OR_CMD] ;+	FILE * lIn ;+	char * lTmp ;++	strcpy( lTestedString , aExecutable ) ;+	strcat( lTestedString , " --version" ) ;++	lIn = ( FILE * ) popen( lTestedString , "r" ) ;+	lTmp = fgets( lBuff , sizeof( lBuff ) , lIn ) ;+	pclose( lIn ) ;++	lTmp += strcspn(lTmp,"0123456789");+	 /* printf("lTmp:%s\n", lTmp); */+	return lTmp ;+}+++static int * getMajorMinorPatch( char const * aExecutable )+{+		static int lArray[3] ;+		char * lTmp ;++		lTmp = (char *) getVersion(aExecutable);+		lArray[0] = atoi( strtok(lTmp," ,.-") ) ;+		/* printf("lArray0 %d\n", lArray[0]); */+		lArray[1] = atoi( strtok(0," ,.-") ) ;+		/* printf("lArray1 %d\n", lArray[1]); */+		lArray[2] = atoi( strtok(0," ,.-") ) ;+		/* printf("lArray2 %d\n", lArray[2]); */++		if ( !lArray[0] && !lArray[1] && !lArray[2] ) return NULL;+		return lArray ;+}+++static int tryCommand( char const * aCommand )+{+	char lBuff[MAX_PATH_OR_CMD] ;+	FILE * lIn ;++	lIn = ( FILE * ) popen( aCommand , "r" ) ;+	if ( fgets( lBuff , sizeof( lBuff ) , lIn ) == NULL )+	{       /* present */+			pclose( lIn ) ;+			return 1 ;+	}+	else+	{+			pclose( lIn ) ;+			return 0 ;+	}++}+++static int isTerminalRunning(void)+{+		static int lIsTerminalRunning = -1 ;+		if ( lIsTerminalRunning < 0 )+		{+				lIsTerminalRunning = isatty(1);+				if (tinyfd_verbose) printf("isTerminalRunning %d\n", lIsTerminalRunning );+		}+		return lIsTerminalRunning;+}+++static char * dialogNameOnly(void)+{+		static char lDialogName[128] = "*" ;+		if ( lDialogName[0] == '*' )+		{+				if (!tinyfd_allowCursesDialogs)+				{+						strcpy(lDialogName , "" );+				}+				else if ( tfd_isDarwin() && * strcpy(lDialogName , "/opt/local/bin/dialog" )+						&& detectPresence( lDialogName ) )+				{}+				else if ( * strcpy(lDialogName , "dialog" )+						&& detectPresence( lDialogName ) )+				{}+				else+				{+						strcpy(lDialogName , "" );+				}+		}+		return lDialogName ;+}+++int isDialogVersionBetter09b(void)+{+		char const * lDialogName ;+		char * lVersion ;+		int lMajor ;+		int lMinor ;+		int lDate ;+		int lResult ;+		char * lMinorP ;+		char * lLetter ;+		char lBuff[128] ;++		/*char lTest[128] = " 0.9b-20031126" ;*/++		lDialogName = dialogNameOnly() ;+		if ( ! strlen(lDialogName) || !(lVersion = (char *) getVersion(lDialogName)) ) return 0 ;+		/*lVersion = lTest ;*/+		/*printf("lVersion %s\n", lVersion);*/+		strcpy(lBuff,lVersion);+		lMajor = atoi( strtok(lVersion," ,.-") ) ;+		/*printf("lMajor %d\n", lMajor);*/+		lMinorP = strtok(0," ,.-abcdefghijklmnopqrstuvxyz");+		lMinor = atoi( lMinorP ) ;+		/*printf("lMinor %d\n", lMinor );*/+		lDate = atoi( strtok(0," ,.-") ) ;+		if (lDate<0) lDate = - lDate;+		/*printf("lDate %d\n", lDate);*/+		lLetter = lMinorP + strlen(lMinorP) ;+		strcpy(lVersion,lBuff);+		strtok(lLetter," ,.-");+		/*printf("lLetter %s\n", lLetter);*/+		lResult = (lMajor > 0) || ( ( lMinor == 9 ) && (*lLetter == 'b') && (lDate >= 20031126) );+		/*printf("lResult %d\n", lResult);*/+		return lResult;+}+++static int whiptailPresentOnly(void)+{+		static int lWhiptailPresent = -1 ;+				if (!tinyfd_allowCursesDialogs) return 0;+		if ( lWhiptailPresent < 0 )+		{+				lWhiptailPresent = detectPresence( "whiptail" ) ;+		}+		return lWhiptailPresent ;+}+++static char * terminalName(void)+{+		static char lTerminalName[128] = "*" ;+		char lShellName[64] = "*" ;+		int * lArray;++		if ( lTerminalName[0] == '*' )+		{+				if ( detectPresence( "bash" ) )+				{+						strcpy(lShellName , "bash -c " ) ; /*good for basic input*/+				}+								else if ( strlen(dialogNameOnly()) || whiptailPresentOnly() )+								{+												strcpy(lShellName , "sh -c " ) ; /*good enough for dialog & whiptail*/+								}+								else+								{+										strcpy(lTerminalName , "" ) ;+										return NULL ;+								}++				if ( tfd_isDarwin() )+				{+										if ( * strcpy(lTerminalName , "/opt/X11/bin/xterm" )+					  && detectPresence( lTerminalName ) )+						{+								strcat(lTerminalName , " -fa 'DejaVu Sans Mono' -fs 10 -title tinyfiledialogs -e " ) ;+								strcat(lTerminalName , lShellName ) ;+						}+						else+						{+								strcpy(lTerminalName , "" ) ;+						}+				}+				else if ( * strcpy(lTerminalName,"xterm") /*good (small without parameters)*/+						&& detectPresence(lTerminalName) )+				{+						strcat(lTerminalName , " -fa 'DejaVu Sans Mono' -fs 10 -title tinyfiledialogs -e " ) ;+						strcat(lTerminalName , lShellName ) ;+				}+				else if ( * strcpy(lTerminalName,"terminator") /*good*/+						  && detectPresence(lTerminalName) )+				{+						strcat(lTerminalName , " -x " ) ;+						strcat(lTerminalName , lShellName ) ;+				}+				else if ( * strcpy(lTerminalName,"lxterminal") /*good*/+						  && detectPresence(lTerminalName) )+				{+						strcat(lTerminalName , " -e " ) ;+						strcat(lTerminalName , lShellName ) ;+				}+				else if ( * strcpy(lTerminalName,"konsole") /*good*/+						  && detectPresence(lTerminalName) )+				{+						strcat(lTerminalName , " -e " ) ;+						strcat(lTerminalName , lShellName ) ;+				}+				else if ( * strcpy(lTerminalName,"kterm") /*good*/+						  && detectPresence(lTerminalName) )+				{+						strcat(lTerminalName , " -e " ) ;+						strcat(lTerminalName , lShellName ) ;+				}+				else if ( * strcpy(lTerminalName,"tilix") /*good*/+						  && detectPresence(lTerminalName) )+				{+						strcat(lTerminalName , " -e " ) ;+						strcat(lTerminalName , lShellName ) ;+				}+				else if ( * strcpy(lTerminalName,"xfce4-terminal") /*good*/+						  && detectPresence(lTerminalName) )+				{+						strcat(lTerminalName , " -x " ) ;+						strcat(lTerminalName , lShellName ) ;+				}+				else if ( * strcpy(lTerminalName,"mate-terminal") /*good*/+						  && detectPresence(lTerminalName) )+				{+						strcat(lTerminalName , " -x " ) ;+						strcat(lTerminalName , lShellName ) ;+				}+				else if ( * strcpy(lTerminalName,"Eterm") /*good*/+						  && detectPresence(lTerminalName) )+				{+						strcat(lTerminalName , " -e " ) ;+						strcat(lTerminalName , lShellName ) ;+				}+				else if ( * strcpy(lTerminalName,"evilvte") /*good*/+						  && detectPresence(lTerminalName) )+				{+						strcat(lTerminalName , " -e " ) ;+						strcat(lTerminalName , lShellName ) ;+				}+				else if ( * strcpy(lTerminalName,"pterm") /*good (only letters)*/+						  && detectPresence(lTerminalName) )+				{+						strcat(lTerminalName , " -e " ) ;+						strcat(lTerminalName , lShellName ) ;+				}+								else if ( * strcpy(lTerminalName,"gnome-terminal")+				&& detectPresence(lTerminalName) && (lArray = getMajorMinorPatch(lTerminalName))+								&& ((lArray[0]<3) || (lArray[0]==3 && lArray[1]<=6)) )+				{+						strcat(lTerminalName , " --disable-factory -x " ) ;+						strcat(lTerminalName , lShellName ) ;+				}+				else+				{+						strcpy(lTerminalName , "" ) ;+				}+				/* bad: koi rxterm guake tilda vala-terminal qterminal kgx+				aterm Terminal terminology sakura lilyterm weston-terminal+				roxterm termit xvt rxvt mrxvt urxvt */+		}+		if ( strlen(lTerminalName) )+		{+				return lTerminalName ;+		}+		else+		{+				return NULL ;+		}+}+++static char * dialogName(void)+{+	char * lDialogName ;+	lDialogName = dialogNameOnly( ) ;+		if ( strlen(lDialogName) && ( isTerminalRunning() || terminalName() ) )+		{+				return lDialogName ;+		}+		else+		{+				return NULL ;+		}+}+++static int whiptailPresent(void)+{+		int lWhiptailPresent ;+	lWhiptailPresent = whiptailPresentOnly( ) ;+		if ( lWhiptailPresent && ( isTerminalRunning() || terminalName() ) )+		{+				return lWhiptailPresent ;+		}+		else+		{+				return 0 ;+		}+}+++static int graphicMode(void)+{+	static int lGraphicMode = -1 ;+	+	if ( lGraphicMode < 0 )+	{+		if ( getenvDISPLAY() ) lGraphicMode = 1 ;+		else if ( tfd_isDarwin() || tfd_isHaiku() )+		{+			if ( getenv("SSH_TTY") ) lGraphicMode = 0 ;+			else lGraphicMode = 1 ;+		}+		else lGraphicMode = 0 ;+	}+	/*printf("lGraphicMode %d %d\n", lGraphicMode);*/+	if ( tinyfd_forceConsole && ( isTerminalRunning() || terminalName() ) ) return 0 ;	+	+	return lGraphicMode ;+}+++static int ffplayPresent(void)+{+   static int lFFplayPresent = -1;+   if (lFFplayPresent < 0)+   {+	  lFFplayPresent = detectPresence("ffplay");+   }+   return lFFplayPresent;+}+++static int pactlPresent( void )+{+	static int lPactlPresent = -1 ;+	char lBuff [256] ;+	FILE * lIn ;++	if ( lPactlPresent < 0 )+	{+		lPactlPresent = detectPresence("pactl") ;+		if ( lPactlPresent )+		{+			lIn = ( FILE * ) popen( "pactl info | grep -iF pulseaudio" , "r" ) ;+			if ( ! (fgets( lBuff , sizeof( lBuff ) , lIn ) && ! strstr(lBuff, "PipeWire") ) )+			{+				lPactlPresent = 0 ;+			}+			pclose( lIn ) ;+			if (tinyfd_verbose) printf("is pactl valid ? %d\n", lPactlPresent);+		}+	}+	return lPactlPresent ;+}+++static int speakertestPresent(void)+{+		static int lSpeakertestPresent = -1 ;+		if ( lSpeakertestPresent < 0 )+		{+				lSpeakertestPresent = detectPresence("speaker-test") ;+		}+		return lSpeakertestPresent ;+}+++static int playPresent(void) /* play is part of sox */+{+   static int lPlayPresent = -1;+   if (lPlayPresent < 0)+   {+	  lPlayPresent = detectPresence("sox"); /*if sox is present, play is ready*/+   }+   return lPlayPresent;+}+++static int beepexePresent(void)+{+   static int lBeepexePresent = -1;+   if (lBeepexePresent < 0)+   {+	  lBeepexePresent = detectPresence("beep.exe");+   }+   return lBeepexePresent;+}+++/*static int beepPresent(void)+{+		static int lBeepPresent = -1 ;+		if ( lBeepPresent < 0 )+		{+				lBeepPresent = detectPresence("beep") ;+		}+		return lBeepPresent ;+}*/+++static int playsoundPresent(void) /* playsound is part of pipewire */+{+    static int lPlaysoundPresent = -1 ;+    if (lPlaysoundPresent < 0)+    {+        lPlaysoundPresent = detectPresence("playsound_simple");+        if ( lPlaysoundPresent && ! fileExists("/usr/share/sounds/freedesktop/stereo/bell.oga") )+        {+            lPlaysoundPresent = 0 ;+        }+    }+    return lPlaysoundPresent;+}+++static int paplayPresent(void) /* playsound is part of pipewire */+{+    static int lPaplayPresent = -1 ;+    if (lPaplayPresent < 0)+    {+        lPaplayPresent = detectPresence("paplay");+        if ( lPaplayPresent && ! fileExists("/usr/share/sounds/freedesktop/stereo/bell.oga") )+        {+            lPaplayPresent = 0 ;+        }+    }+    return lPaplayPresent;+}+++static int xmessagePresent(void)+{+		static int lXmessagePresent = -1 ;+		if ( lXmessagePresent < 0 )+		{+				lXmessagePresent = detectPresence("xmessage");/*if not tty,not on osxpath*/+		}+		return lXmessagePresent && graphicMode( ) ;+}+++static int gxmessagePresent(void)+{+	static int lGxmessagePresent = -1 ;+	if ( lGxmessagePresent < 0 )+	{+		lGxmessagePresent = detectPresence("gxmessage") ;+	}+	return lGxmessagePresent && graphicMode( ) ;+}+++static int gmessagePresent(void)+{+		static int lGmessagePresent = -1 ;+		if ( lGmessagePresent < 0 )+		{+				lGmessagePresent = detectPresence("gmessage") ;+		}+		return lGmessagePresent && graphicMode( ) ;+}+++static int notifysendPresent(void)+{+	static int lNotifysendPresent = -1 ;+	if ( lNotifysendPresent < 0 )+	{+		lNotifysendPresent = detectPresence("notify-send") ;+	}+	return lNotifysendPresent && graphicMode( ) ;+}+++static int notifyPresent(void)+{+	static int lNotifyPresent = -1 ;+	if ( lNotifyPresent < 0 )+	{+		lNotifyPresent = detectPresence("notify") ;+	}+	return lNotifyPresent && graphicMode( ) ;+}+++static int perlPresent(void)+{+   static int lPerlPresent = -1 ;+   char lBuff[MAX_PATH_OR_CMD] ;+   FILE * lIn ;++   if ( lPerlPresent < 0 )+   {+	  lPerlPresent = detectPresence("perl") ;+	  if (lPerlPresent)+	  {+		 lIn = ( FILE * ) popen("perl -MNet::DBus -e \"Net::DBus->session->get_service('org.freedesktop.Notifications')\" 2>&1", "r");+		 if (fgets(lBuff, sizeof(lBuff), lIn) == NULL)+		 {+			lPerlPresent = 2;+		 }+		 pclose(lIn);+		 if (tinyfd_verbose) printf("perl-dbus %d\n", lPerlPresent);+	  }+   }+   return graphicMode() ? lPerlPresent : 0 ;+}+++static int afplayPresent(void)+{+		static int lAfplayPresent = -1 ;+		char lBuff[MAX_PATH_OR_CMD] ;+		FILE * lIn ;++		if ( lAfplayPresent < 0 )+		{+				lAfplayPresent = detectPresence("afplay") ;+				if ( lAfplayPresent )+				{+						lIn = ( FILE * ) popen( "test -e /System/Library/Sounds/Ping.aiff || echo Ping" , "r" ) ;+						if ( fgets( lBuff , sizeof( lBuff ) , lIn ) == NULL )+						{+								lAfplayPresent = 2 ;+						}+						pclose( lIn ) ;+						if (tinyfd_verbose) printf("afplay %d\n", lAfplayPresent);+				}+		}+		return graphicMode() ? lAfplayPresent : 0 ;+}+++static int xdialogPresent(void)+{+	static int lXdialogPresent = -1 ;+	if ( lXdialogPresent < 0 )+	{+		lXdialogPresent = detectPresence("Xdialog") ;+	}+	return lXdialogPresent && graphicMode( ) ;+}+++static int gdialogPresent(void)+{+	static int lGdialoglPresent = -1 ;+	if ( lGdialoglPresent < 0 )+	{+		lGdialoglPresent = detectPresence( "gdialog" ) ;+	}+	return lGdialoglPresent && graphicMode( ) ;+}+++static int osascriptPresent(void)+{+	static int lOsascriptPresent = -1 ;+	if ( lOsascriptPresent < 0 )+	{+				gWarningDisplayed |= !!getenv("SSH_TTY");+				lOsascriptPresent = detectPresence( "osascript" ) ;+	}+		return lOsascriptPresent && graphicMode() && !getenv("SSH_TTY") ;+}+++static int dunstifyPresent(void)+{+	static int lDunstifyPresent = -1 ;+		static char lBuff[MAX_PATH_OR_CMD] ;+		FILE * lIn ;+		char * lTmp ;++	if ( lDunstifyPresent < 0 )+	{+		lDunstifyPresent = detectPresence( "dunstify" ) ;+		if ( lDunstifyPresent )+		{+			lIn = ( FILE * ) popen( "dunstify -s" , "r" ) ;+			lTmp = fgets( lBuff , sizeof( lBuff ) , lIn ) ;+			pclose( lIn ) ;+			/* printf("lTmp:%s\n", lTmp); */+			lDunstifyPresent = strstr(lTmp,"name:dunst\n") ? 1 : 0 ;+			if (tinyfd_verbose) printf("lDunstifyPresent %d\n", lDunstifyPresent);+		}+	}+	return lDunstifyPresent && graphicMode( ) ;+}+++static int dunstPresent(void)+{+	static int lDunstPresent = -1 ;+		static char lBuff[MAX_PATH_OR_CMD] ;+		FILE * lIn ;+		char * lTmp ;++	if ( lDunstPresent < 0 )+	{+		lDunstPresent = detectPresence( "dunst" ) ;+		if ( lDunstPresent )+		{+			lIn = ( FILE * ) popen( "ps -e | grep dunst | grep -v grep" , "r" ) ; /* add "| wc -l" to receive the number of lines */+			lTmp = fgets( lBuff , sizeof( lBuff ) , lIn ) ;+			pclose( lIn ) ;+			/* if ( lTmp ) printf("lTmp:%s\n", lTmp); */+			if ( lTmp ) lDunstPresent = 1 ;+			else lDunstPresent = 0 ;+			if (tinyfd_verbose) printf("lDunstPresent %d\n", lDunstPresent);+		}+	}+	return lDunstPresent && graphicMode( ) ;+}+++int tfd_shantyPresent(void)+{+	static int lShantyPresent = -1 ;+	if ( lShantyPresent < 0 )+	{+		lShantyPresent = detectPresence("shanty") ;+	}+	/*printf("lShantyPresent %d\n", lShantyPresent);*/+	return lShantyPresent && graphicMode( ) ;+}+++int tfd_boxerPresent(void)+{+	static int lBoxerPresent = -1 ;+	if ( lBoxerPresent < 0 )+	{+		lBoxerPresent = detectPresence("boxer") ;+	}+	/*printf("lBoxerPresent %d\n", lBoxerPresent);*/+	return lBoxerPresent && graphicMode( ) ;+}+++int tfd_qarmaPresent(void)+{+	static int lQarmaPresent = -1 ;+	if ( lQarmaPresent < 0 )+	{+		lQarmaPresent = detectPresence("qarma") ;+	}+	return lQarmaPresent && graphicMode( ) ;+}+++int tfd_matedialogPresent(void)+{+	static int lMatedialogPresent = -1 ;+	if ( lMatedialogPresent < 0 )+	{+		lMatedialogPresent = detectPresence("matedialog") ;+	}+	return lMatedialogPresent && graphicMode( ) ;+}+++int tfd_shellementaryPresent(void)+{+	static int lShellementaryPresent = -1 ;+	if ( lShellementaryPresent < 0 )+	{+		lShellementaryPresent = 0 ; /*detectPresence("shellementary"); shellementary is not ready yet */+	}+	return lShellementaryPresent && graphicMode( ) ;+}+++int tfd_xpropPresent(void)+{+	static int lXpropReady = 0 ;+	static int lXpropDetected = -1 ;+	char lBuff[MAX_PATH_OR_CMD] ;+	FILE * lIn ;++	if ( lXpropDetected < 0 )+	{+		if ( getenvDISPLAY() & 1 ) lXpropDetected = detectPresence("xprop") ; /* bitwise & */+		else lXpropDetected = 0 ;+	}++	if ( ! lXpropDetected ) return 0 ;++	if ( ! lXpropReady )+	{	/* xwayland Debian issue reported by Kay F. Jahnke and solved with his help */+		lIn = ( FILE * ) popen( "xprop -root 32x '	$0' _NET_ACTIVE_WINDOW" , "r" ) ;+		if ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL )+		{+			if ( ! strstr( lBuff , "not found" ) )+			{+				if (tinyfd_verbose) printf("xprop is ready\n");+				lXpropReady = 1 ;+			}+		}+		pclose( lIn ) ;+	}+	return graphicMode() ? lXpropReady : 0 ;+}+++int tfd_yadPresent(void)+{+	static int lYadPresent = -1;+	if (lYadPresent < 0)+	{+		lYadPresent = detectPresence("yad");+	}+	return lYadPresent && graphicMode();+}+++int tfd_zenityPresent(void)+{+	static int lZenityPresent = -1 ;+	if ( lZenityPresent < 0 )+	{+		lZenityPresent = detectPresence("zenity") ;+	}+	return lZenityPresent && graphicMode( ) ;+}+++int tfd_zenityVersion(void)+{+	static int lZenityVersion = -1 ;+	char lBuff[MAX_PATH_OR_CMD] ;+	FILE * lIn ;++	if ( lZenityVersion < 0 )+	{+		if ( tfd_zenityPresent() )+		{+			lIn = ( FILE * ) popen( "zenity --version" , "r" ) ;+			if ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL )+			{+				lZenityVersion = 1000 * atoi( lBuff ) ;+				lZenityVersion += atoi( strtok( lBuff, "." ) + 2 ) ;+				/* 3018 , 3010 , 3000, 2032 was 5 , 4 , 3, 2 */ +			}+			pclose( lIn ) ;+		}+		else lZenityVersion = 0 ;+		if (tinyfd_verbose) printf("lZenityVersion %d\n", lZenityVersion ) ;	+	}+	return graphicMode() ? lZenityVersion : 0 ;+}+++int tfd_kdialogPresent(void)+{+    static int lKdialogPresent = -1 ;+    char lBuff[MAX_PATH_OR_CMD] ;+    FILE * lIn ;+    char * lDesktop;++    if ( lKdialogPresent < 0 )+    {+        lDesktop = getenv("XDG_SESSION_DESKTOP");+        if ( !lDesktop  || ( strcmp(lDesktop, "KDE") && strcmp(lDesktop, "lxqt") ) )+        {+            if ( tfd_zenityPresent() )+            {+                lKdialogPresent = 0 ;+                return lKdialogPresent ;+            }+        }++        lKdialogPresent = detectPresence("kdialog") ;+        if ( lKdialogPresent && !getenv("SSH_TTY") )+        {+            lIn = ( FILE * ) popen( "kdialog --attach 2>&1" , "r" ) ;+            if ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL )+            {+                if ( ! strstr( "Unknown" , lBuff ) )+                {+                    lKdialogPresent = 2 ;+                    if (tinyfd_verbose) printf("kdialog-attach %d\n", lKdialogPresent);+                }+            }+            pclose( lIn ) ;++            if (lKdialogPresent == 2)+            {+                lKdialogPresent = 1 ;+                lIn = ( FILE * ) popen( "kdialog --passivepopup 2>&1" , "r" ) ;+                if ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL )+                {+                    if ( ! strstr( "Unknown" , lBuff ) )+                    {+                        lKdialogPresent = 2 ;+                        if (tinyfd_verbose) printf("kdialog-popup %d\n", lKdialogPresent);+                    }+                }+                pclose( lIn ) ;+            }+        }+    }+    return graphicMode() ? lKdialogPresent : 0 ;+}+++static int osx9orBetter(void)+{+		static int lOsx9orBetter = -1 ;+		char lBuff[MAX_PATH_OR_CMD] ;+		FILE * lIn ;+		int V,v;++		if ( lOsx9orBetter < 0 )+		{+				lOsx9orBetter = 0 ;+				lIn = ( FILE * ) popen( "osascript -e 'set osver to system version of (system info)'" , "r" ) ;+				V = 0 ;+				if ( ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL )+						&& ( 2 == sscanf(lBuff, "%d.%d", &V, &v) ) )+				{+						V = V * 100 + v;+						if ( V >= 1009 )+						{+								lOsx9orBetter = 1 ;+						}+				}+				pclose( lIn ) ;+				if (tinyfd_verbose) printf("Osx10 = %d, %d = %s\n", lOsx9orBetter, V, lBuff) ;+		}+		return lOsx9orBetter ;+}+++static int python3Present(void)+{+		static int lPython3Present = -1 ;++		if ( lPython3Present < 0 )+		{+				lPython3Present = 0 ;+				strcpy(gPython3Name , "python3" ) ;+				if ( detectPresence(gPython3Name) ) lPython3Present = 1;+				if (tinyfd_verbose) printf("lPython3Present %d\n", lPython3Present) ;+				if (tinyfd_verbose) printf("gPython3Name %s\n", gPython3Name) ;+		}+				return lPython3Present ;+}+++static int python2Present(void)+{+		static int lPython2Present = -1 ;++		if ( lPython2Present < 0 )+		{+				lPython2Present = 0 ;+				strcpy(gPython2Name , "python2" ) ;+				if ( detectPresence(gPython2Name) ) lPython2Present = 1;+				if (tinyfd_verbose) printf("lPython2Present %d\n", lPython2Present) ;+				if (tinyfd_verbose) printf("gPython2Name %s\n", gPython2Name) ;+		}+		return lPython2Present ;+}+++static int tkinter3Present(void)+{+		static int lTkinter3Present = -1 ;+		char lPythonCommand[256];+		char lPythonParams[128] =+				"-S -c \"try:\n\timport tkinter;\nexcept:\n\tprint(0);\"";++		if ( lTkinter3Present < 0 )+		{+				lTkinter3Present = 0 ;+				if ( python3Present() )+				{+						sprintf( lPythonCommand , "%s %s" , gPython3Name , lPythonParams ) ;+						lTkinter3Present = tryCommand(lPythonCommand) ;+				}+				if (tinyfd_verbose) printf("lTkinter3Present %d\n", lTkinter3Present) ;+		}+				return lTkinter3Present && graphicMode() && !(tfd_isDarwin() && getenv("SSH_TTY") );+}+++static int tkinter2Present(void)+{+		static int lTkinter2Present = -1 ;+		char lPythonCommand[256];+		char lPythonParams[128] =+				"-S -c \"try:\n\timport Tkinter;\nexcept:\n\tprint 0;\"";++		if ( lTkinter2Present < 0 )+		{+				lTkinter2Present = 0 ;+				if ( python2Present() )+				{+						sprintf( lPythonCommand , "%s %s" , gPython2Name , lPythonParams ) ;+						lTkinter2Present = tryCommand(lPythonCommand) ;+				}+				if (tinyfd_verbose) printf("lTkinter2Present %d graphicMode %d \n", lTkinter2Present, graphicMode() ) ;+		}+		return lTkinter2Present && graphicMode() && !(tfd_isDarwin() && getenv("SSH_TTY") );+}+++static int pythonDbusPresent(void)+{+	static int lPythonDbusPresent = -1 ;+		char lPythonCommand[384];+		char lPythonParams[256] =+"-c \"try:\n\timport dbus;bus=dbus.SessionBus();\+notif=bus.get_object('org.freedesktop.Notifications','/org/freedesktop/Notifications');\+notify=dbus.Interface(notif,'org.freedesktop.Notifications');\nexcept:\n\tprint(0);\"";++		if (lPythonDbusPresent < 0 )+		{+		   lPythonDbusPresent = 0 ;+				if ( python2Present() )+				{+						strcpy(gPythonName , gPython2Name ) ;+						sprintf( lPythonCommand , "%s %s" , gPythonName , lPythonParams ) ;+						lPythonDbusPresent = tryCommand(lPythonCommand) ;+				}++				if ( !lPythonDbusPresent && python3Present() )+				{+						strcpy(gPythonName , gPython3Name ) ;+						sprintf( lPythonCommand , "%s %s" , gPythonName , lPythonParams ) ;+						lPythonDbusPresent = tryCommand(lPythonCommand) ;+				}++				if (tinyfd_verbose) printf("lPythonDbusPresent %d\n", lPythonDbusPresent) ;+				if (tinyfd_verbose) printf("gPythonName %s\n", gPythonName) ;+		}+		return lPythonDbusPresent && graphicMode() && !(tfd_isDarwin() && getenv("SSH_TTY") );+}+++static void sigHandler(int signum)+{+    FILE * lIn = ( FILE * ) popen( "pactl unload-module module-sine" , "r" );+    if ( lIn )+    {+        pclose( lIn ) ;+    }+    if (tinyfd_verbose) printf("tinyfiledialogs caught signal %d\n", signum);+}+++void tinyfd_beep(void)+{+    char lDialogString[256] ;+    FILE * lIn ;++    if ( pactlPresent() )+    {+        signal(SIGINT, sigHandler);+        strcpy( lDialogString ,+            "thnum=$(pactl load-module module-sine frequency=440);sleep .3;pactl unload-module $thnum" ) ;+    }+    else if ( osascriptPresent() )+    {+        if ( afplayPresent() >= 2 )+        {+            strcpy( lDialogString , "afplay /System/Library/Sounds/Ping.aiff") ;+        }+        else+        {+            strcpy( lDialogString , "osascript -e 'tell application \"System Events\" to beep'") ;+        }+    }+    else if ( speakertestPresent() )+    {+        /*strcpy( lDialogString , "timeout -k .3 .3 speaker-test --frequency 440 --test sine > /dev/tty" ) ;*/+        strcpy( lDialogString , "( speaker-test -t sine -f 440 > /dev/tty )& pid=$!;sleep .5; kill -9 $pid" ) ; /*.3 was too short for mac g3*/+    }+    else if ( ffplayPresent() )+    {+        strcpy(lDialogString, "ffplay -f lavfi -i sine=f=440:d=0.15 -autoexit -nodisp" );+    }+    else if (playPresent()) /* play is part of sox */+    {+        strcpy(lDialogString, "play -q -n synth .3 sine 440");+    }+    else if ( playsoundPresent() )+    {+        strcpy( lDialogString , "playsound_simple /usr/share/sounds/freedesktop/stereo/bell.oga") ;+    }+    else if ( paplayPresent() )+    {+        strcpy( lDialogString , "paplay /usr/share/sounds/freedesktop/stereo/bell.oga") ;+    }+    else if (beepexePresent())+    {+        strcpy(lDialogString, "beep.exe 440 300");+    }+    /*else if ( beepPresent() )+    {+        strcpy( lDialogString , "beep -f 440 -l 300" ) ;+    }*/+    else+    {+        strcpy( lDialogString , "printf '\\a' > /dev/tty" ) ;+    }++    if (tinyfd_verbose) printf( "lDialogString: %s\n" , lDialogString ) ;++	lIn = ( FILE * ) popen( lDialogString , "r" ) ;+    if ( lIn )+    {+		pclose( lIn ) ;+    }++    if ( pactlPresent() )+    {+            signal(SIGINT, SIG_DFL);+    }+}+++int tinyfd_messageBox(+		char const * aTitle , /* NULL or "" */+		char const * aMessage , /* NULL or ""  may contain \n and \t */+		char const * aDialogType , /* "ok" "okcancel" "yesno" "yesnocancel" */+		char const * aIconType , /* "info" "warning" "error" "question" */+		int aDefaultButton ) /* 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel */+{+	char lBuff[MAX_PATH_OR_CMD] ;+	char lBuff2[MAX_PATH_OR_CMD] ;+	char * lDialogString = NULL ;+	char * lpDialogString;+	FILE * lIn ;+	int lWasGraphicDialog = 0 ;+	int lWasXterm = 0 ;+	int lResult ;+	char lChar ;+	struct termios infoOri;+	struct termios info;+	size_t lTitleLen ;+	size_t lMessageLen ;++	lBuff[0]='\0';++	if (tfd_quoteDetected(aTitle)) return tinyfd_messageBox("INVALID TITLE WITH QUOTES", aMessage, aDialogType, aIconType, aDefaultButton);+	if (tfd_quoteDetected(aMessage)) return tinyfd_messageBox(aTitle, "INVALID MESSAGE WITH QUOTES", aDialogType, aIconType, aDefaultButton);++	lTitleLen =  aTitle ? strlen(aTitle) : 0 ;+	lMessageLen =  aMessage ? strlen(aMessage) : 0 ;+	if ( !aTitle || strcmp(aTitle,"tinyfd_query") )+	{+			lDialogString = (char *) malloc( MAX_PATH_OR_CMD + lTitleLen + lMessageLen );+	}++	if ( osascriptPresent( ) )+	{+		if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return 1;}++		strcpy( lDialogString , "osascript ");+		if ( ! osx9orBetter() ) strcat( lDialogString , " -e 'tell application \"System Events\"' -e 'Activate'");+		strcat( lDialogString , " -e 'try' -e 'set {vButton} to {button returned} of ( display dialog \"") ;+		if ( aMessage && strlen(aMessage) )+		{+				strcat(lDialogString, aMessage) ;+		}+		strcat(lDialogString, "\" ") ;+		if ( aTitle && strlen(aTitle) )+		{+				strcat(lDialogString, "with title \"") ;+				strcat(lDialogString, aTitle) ;+				strcat(lDialogString, "\" ") ;+		}+		strcat(lDialogString, "with icon ") ;+		if ( aIconType && ! strcmp( "error" , aIconType ) )+		{+				strcat(lDialogString, "stop " ) ;+		}+		else if ( aIconType && ! strcmp( "warning" , aIconType ) )+		{+				strcat(lDialogString, "caution " ) ;+		}+		else /* question or info */+		{+				strcat(lDialogString, "note " ) ;+		}+		if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) )+		{+				if ( ! aDefaultButton )+				{+						strcat( lDialogString ,"default button \"Cancel\" " ) ;+				}+		}+		else if ( aDialogType && ! strcmp( "yesno" , aDialogType ) )+		{+				strcat( lDialogString ,"buttons {\"No\", \"Yes\"} " ) ;+				if (aDefaultButton)+				{+						strcat( lDialogString ,"default button \"Yes\" " ) ;+				}+				else+				{+						strcat( lDialogString ,"default button \"No\" " ) ;+				}+				strcat( lDialogString ,"cancel button \"No\"" ) ;+		}+		else if ( aDialogType && ! strcmp( "yesnocancel" , aDialogType ) )+		{+				strcat( lDialogString ,"buttons {\"No\", \"Yes\", \"Cancel\"} " ) ;+				switch (aDefaultButton)+				{+						case 1: strcat( lDialogString ,"default button \"Yes\" " ) ; break;+						case 2: strcat( lDialogString ,"default button \"No\" " ) ; break;+						case 0: strcat( lDialogString ,"default button \"Cancel\" " ) ; break;+				}+				strcat( lDialogString ,"cancel button \"Cancel\"" ) ;+		}+		else+		{+				strcat( lDialogString ,"buttons {\"OK\"} " ) ;+				strcat( lDialogString ,"default button \"OK\" " ) ;+		}+		strcat( lDialogString, ")' ") ;++		strcat( lDialogString,+"-e 'if vButton is \"Yes\" then' -e 'return 1' \+-e 'else if vButton is \"OK\" then' -e 'return 1' \+-e 'else if vButton is \"No\" then' -e 'return 2' \+-e 'else' -e 'return 0' -e 'end if' " );++		strcat( lDialogString, "-e 'on error number -128' " ) ;+		strcat( lDialogString, "-e '0' " );++		strcat( lDialogString, "-e 'end try'") ;+		if ( ! osx9orBetter() ) strcat( lDialogString, " -e 'end tell'") ;+	}+	else if ( tfd_kdialogPresent() )+	{+				if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return 1;}++				strcpy( lDialogString , "kdialog" ) ;+				if ( (tfd_kdialogPresent() == 2) && tfd_xpropPresent() )+				{+						strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */+				}++				strcat( lDialogString , " --" ) ;+				if ( aDialogType && ( ! strcmp( "okcancel" , aDialogType )+						|| ! strcmp( "yesno" , aDialogType ) || ! strcmp( "yesnocancel" , aDialogType ) ) )+				{+						if ( aIconType && ( ! strcmp( "warning" , aIconType )+								|| ! strcmp( "error" , aIconType ) ) )+						{+								strcat( lDialogString , "warning" ) ;+						}+						if ( ! strcmp( "yesnocancel" , aDialogType ) )+						{+								strcat( lDialogString , "yesnocancel" ) ;+						}+						else+						{+								strcat( lDialogString , "yesno" ) ;+						}+				}+				else if ( aIconType && ! strcmp( "error" , aIconType ) )+				{+						strcat( lDialogString , "error" ) ;+				}+				else if ( aIconType && ! strcmp( "warning" , aIconType ) )+				{+						strcat( lDialogString , "sorry" ) ;+				}+				else+				{+						strcat( lDialogString , "msgbox" ) ;+				}+				strcat( lDialogString , " \"" ) ;+				if ( aMessage )+				{+						strcat( lDialogString , aMessage ) ;+				}+				strcat( lDialogString , "\"" ) ;+				if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) )+				{+						strcat( lDialogString ,+								" --yes-label Ok --no-label Cancel" ) ;+				}+				if ( aTitle && strlen(aTitle) )+				{+						strcat(lDialogString, " --title \"") ;+						strcat(lDialogString, aTitle) ;+						strcat(lDialogString, "\"") ;+				}++				if ( ! strcmp( "yesnocancel" , aDialogType ) )+				{+						strcat( lDialogString , "; x=$? ;if [ $x = 0 ] ;then echo 1;elif [ $x = 1 ] ;then echo 2;else echo 0;fi");+				}+				else+				{+						strcat( lDialogString , ";if [ $? = 0 ];then echo 1;else echo 0;fi");+				}+		}+		else if ( tfd_zenityPresent() || tfd_matedialogPresent() || tfd_shellementaryPresent()+				|| tfd_qarmaPresent() || tfd_shantyPresent() || tfd_boxerPresent() )+		{+				if ( tfd_zenityPresent() )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return 1;}+						strcpy( lDialogString , "szAnswer=$(zenity" ) ;+						if ( (tfd_zenityVersion() >= 3010) && !getenv("SSH_TTY") && tfd_xpropPresent() )+						{+								strcat(lDialogString, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */+						}+				}+				else if ( tfd_matedialogPresent() )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return 1;}+						strcpy( lDialogString , "szAnswer=$(matedialog" ) ;+				}+				else if ( tfd_shellementaryPresent() )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"shellementary");return 1;}+						strcpy( lDialogString , "szAnswer=$(shellementary" ) ;+				}+				else if ( tfd_qarmaPresent() )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"qarma");return 1;}+						strcpy( lDialogString , "szAnswer=$(qarma" ) ;+						if ( !getenv("SSH_TTY") && tfd_xpropPresent() )+						{+								strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */+						}+				}+				else if ( tfd_boxerPresent() )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"boxer");return 1;}+						strcpy( lDialogString , "szAnswer=$(boxer" ) ;+				}+				else if ( tfd_shantyPresent() )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"shanty");return 1;}+						strcpy( lDialogString , "szAnswer=$(shanty" ) ;+				}+				else {}++				strcat(lDialogString, " --");++				if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) )+				{+								strcat( lDialogString ,+												"question --ok-label=Ok --cancel-label=Cancel" ) ;+				}+				else if ( aDialogType && ! strcmp( "yesno" , aDialogType ) )+				{+								strcat( lDialogString , "question" ) ;+				}+				else if ( aDialogType && ! strcmp( "yesnocancel" , aDialogType ) )+				{+						strcat( lDialogString , "list --column \"\" --hide-header \"Yes\" \"No\"" ) ;+				}+				else if ( aIconType && ! strcmp( "error" , aIconType ) )+				{+					strcat( lDialogString , "error" ) ;+				}+				else if ( aIconType && ! strcmp( "warning" , aIconType ) )+				{+					strcat( lDialogString , "warning" ) ;+				}+				else+				{+					strcat( lDialogString , "info" ) ;+				}++				strcat(lDialogString, " --title=\"");+				if ( aTitle && strlen(aTitle) ) strcat(lDialogString, aTitle) ;+				strcat(lDialogString, "\"");++				if (strcmp("yesnocancel", aDialogType)) strcat(lDialogString, " --no-wrap");++				strcat(lDialogString, " --text=\"") ;+				if (aMessage && strlen(aMessage)) strcat(lDialogString, aMessage) ;+				strcat(lDialogString, "\"") ;++				if ( (tfd_zenityVersion()>=3000) || tfd_boxerPresent() )+				{+					if ( (tfd_zenityVersion()>=3900) || tfd_boxerPresent() ) strcat( lDialogString , " --icon=dialog-" ) ;+					else strcat( lDialogString , " --icon-name=dialog-" ) ;++					if ( aIconType && (! strcmp( "question" , aIconType )+					  || ! strcmp( "error" , aIconType )+					  || ! strcmp( "warning" , aIconType ) ) )+					{+						strcat( lDialogString , aIconType ) ;+					}+					else+					{+						strcat( lDialogString , "information" ) ;+					}+				}++				if (tinyfd_silent) strcat( lDialogString , " 2>/dev/null ");++				if ( ! strcmp( "yesnocancel" , aDialogType ) )+				{+						strcat( lDialogString ,+");if [ $? = 1 ];then echo 0;elif [ $szAnswer = \"No\" ];then echo 2;else echo 1;fi");+				}+				else+				{+					strcat( lDialogString , ");if [ $? = 0 ];then echo 1;else echo 0;fi");+				}+	  }++	  else if (tfd_yadPresent())+	  {+		 if (aTitle && !strcmp(aTitle, "tinyfd_query")) { strcpy(tinyfd_response, "yad"); return 1; }+		 strcpy(lDialogString, "szAnswer=$(yad --");+		 if (aDialogType && !strcmp("ok", aDialogType))+		 {+			strcat(lDialogString,"button=Ok:1");+		 }+		 else if (aDialogType && !strcmp("okcancel", aDialogType))+		 {+			strcat(lDialogString,"button=Ok:1 --button=Cancel:0");+		 }+		 else if (aDialogType && !strcmp("yesno", aDialogType))+		 {+			strcat(lDialogString, "button=Yes:1 --button=No:0");+		 }+		 else if (aDialogType && !strcmp("yesnocancel", aDialogType))+		 {+			strcat(lDialogString, "button=Yes:1 --button=No:2 --button=Cancel:0");+		 }+		 else if (aIconType && !strcmp("error", aIconType))+		 {+			strcat(lDialogString, "error");+		 }+		 else if (aIconType && !strcmp("warning", aIconType))+		 {+			strcat(lDialogString, "warning");+		 }+		 else+		 {+			strcat(lDialogString, "info");+		 }+		 if (aTitle && strlen(aTitle))+		 {+			strcat(lDialogString, " --title=\"");+			strcat(lDialogString, aTitle);+			strcat(lDialogString, "\"");+		 }+		 if (aMessage && strlen(aMessage))+		 {+			strcat(lDialogString, " --text=\"");+			strcat(lDialogString, aMessage);+			strcat(lDialogString, "\"");+		 }++		 strcat(lDialogString, " --image=dialog-");+		 if (aIconType && (!strcmp("question", aIconType)+			|| !strcmp("error", aIconType)+			|| !strcmp("warning", aIconType)))+		 {+			strcat(lDialogString, aIconType);+		 }+		 else+		 {+			strcat(lDialogString, "information");+		 }++		 if (tinyfd_silent) strcat(lDialogString, " 2>/dev/null ");+		 strcat(lDialogString,");echo $?");+	  }++	  else if ( !gxmessagePresent() && !gmessagePresent() && !gdialogPresent() && !xdialogPresent() && tkinter3Present() )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python3-tkinter");return 1;}++						strcpy( lDialogString , gPython3Name ) ;+						strcat( lDialogString ,+								" -S -c \"import tkinter;from tkinter import messagebox;root=tkinter.Tk();root.withdraw();");++						strcat( lDialogString ,"res=messagebox." ) ;+						if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) )+						{+								strcat( lDialogString , "askokcancel(" ) ;+								if ( aDefaultButton )+								{+										strcat( lDialogString , "default=messagebox.OK," ) ;+								}+								else+								{+										strcat( lDialogString , "default=messagebox.CANCEL," ) ;+								}+						}+						else if ( aDialogType && ! strcmp( "yesno" , aDialogType ) )+						{+								strcat( lDialogString , "askyesno(" ) ;+								if ( aDefaultButton )+								{+										strcat( lDialogString , "default=messagebox.YES," ) ;+								}+								else+								{+										strcat( lDialogString , "default=messagebox.NO," ) ;+								}+						}+						else if ( aDialogType && ! strcmp( "yesnocancel" , aDialogType ) )+						{+								strcat( lDialogString , "askyesnocancel(" ) ;+								switch ( aDefaultButton )+								{+								case 1: strcat( lDialogString , "default=messagebox.YES," ); break;+								case 2: strcat( lDialogString , "default=messagebox.NO," ); break;+								case 0: strcat( lDialogString , "default=messagebox.CANCEL," ); break;+								}+						}+						else+						{+								strcat( lDialogString , "showinfo(" ) ;+						}++						strcat( lDialogString , "icon='" ) ;+						if ( aIconType && (! strcmp( "question" , aIconType )+								|| ! strcmp( "error" , aIconType )+								|| ! strcmp( "warning" , aIconType ) ) )+						{+								strcat( lDialogString , aIconType ) ;+						}+						else+						{+								strcat( lDialogString , "info" ) ;+						}++						strcat(lDialogString, "',") ;+						if ( aTitle && strlen(aTitle) )+						{+								strcat(lDialogString, "title='") ;+								strcat(lDialogString, aTitle) ;+								strcat(lDialogString, "',") ;+						}+						if ( aMessage && strlen(aMessage) )+						{+								strcat(lDialogString, "message='") ;+								lpDialogString = lDialogString + strlen(lDialogString);+								tfd_replaceSubStr( aMessage , "\n" , "\\n" , lpDialogString ) ;+								strcat(lDialogString, "'") ;+						}++						if ( aDialogType && ! strcmp( "yesnocancel" , aDialogType ) )+						{+								strcat(lDialogString, ");\n\+if res is None :\n\tprint(0)\n\+elif res is False :\n\tprint(2)\n\+else :\n\tprint (1)\n\"" ) ;+						}+						else+						{+								strcat(lDialogString, ");\n\+if res is False :\n\tprint(0)\n\+else :\n\tprint(1)\n\"" ) ;+						}+				}+				else if ( !gxmessagePresent() && !gmessagePresent() && !gdialogPresent() && !xdialogPresent() && tkinter2Present() )+		{+				if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python2-tkinter");return 1;}+								strcpy( lDialogString , "export PYTHONIOENCODING=utf-8;" ) ;+								strcat( lDialogString , gPython2Name ) ;+								if ( ! isTerminalRunning( ) && tfd_isDarwin( ) )+				{+						strcat( lDialogString , " -i" ) ;  /* for osx without console */+				}++				strcat( lDialogString ,+" -S -c \"import Tkinter,tkMessageBox;root=Tkinter.Tk();root.withdraw();");++				if ( tfd_isDarwin( ) )+				{+						strcat( lDialogString ,+"import os;os.system('''/usr/bin/osascript -e 'tell app \\\"Finder\\\" to set \+frontmost of process \\\"Python\\\" to true' ''');");+				}++				strcat( lDialogString ,"res=tkMessageBox." ) ;+				if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) )+				{+				  strcat( lDialogString , "askokcancel(" ) ;+				  if ( aDefaultButton )+						{+								strcat( lDialogString , "default=tkMessageBox.OK," ) ;+						}+						else+						{+								strcat( lDialogString , "default=tkMessageBox.CANCEL," ) ;+						}+				}+				else if ( aDialogType && ! strcmp( "yesno" , aDialogType ) )+				{+						strcat( lDialogString , "askyesno(" ) ;+						if ( aDefaultButton )+						{+								strcat( lDialogString , "default=tkMessageBox.YES," ) ;+						}+						else+						{+								strcat( lDialogString , "default=tkMessageBox.NO," ) ;+						}+				}+				else if ( aDialogType && ! strcmp( "yesnocancel" , aDialogType ) )+				{+						strcat( lDialogString , "askyesnocancel(" ) ;+						switch ( aDefaultButton )+						{+								case 1: strcat( lDialogString , "default=tkMessageBox.YES," ); break;+								case 2: strcat( lDialogString , "default=tkMessageBox.NO," ); break;+								case 0: strcat( lDialogString , "default=tkMessageBox.CANCEL," ); break;+						}+				}+				else+				{+								strcat( lDialogString , "showinfo(" ) ;+				}++				strcat( lDialogString , "icon='" ) ;+				if ( aIconType && (! strcmp( "question" , aIconType )+				  || ! strcmp( "error" , aIconType )+				  || ! strcmp( "warning" , aIconType ) ) )+				{+								strcat( lDialogString , aIconType ) ;+				}+				else+				{+								strcat( lDialogString , "info" ) ;+				}++				strcat(lDialogString, "',") ;+				if ( aTitle && strlen(aTitle) )+				{+								strcat(lDialogString, "title='") ;+								strcat(lDialogString, aTitle) ;+								strcat(lDialogString, "',") ;+				}+				if ( aMessage && strlen(aMessage) )+				{+						strcat(lDialogString, "message='") ;+						lpDialogString = lDialogString + strlen(lDialogString);+						tfd_replaceSubStr( aMessage , "\n" , "\\n" , lpDialogString ) ;+						strcat(lDialogString, "'") ;+				}++				if ( aDialogType && ! strcmp( "yesnocancel" , aDialogType ) )+				{+						strcat(lDialogString, ");\n\+if res is None :\n\tprint 0\n\+elif res is False :\n\tprint 2\n\+else :\n\tprint 1\n\"" ) ;+				}+				else+				{+						strcat(lDialogString, ");\n\+if res is False :\n\tprint 0\n\+else :\n\tprint 1\n\"" ) ;+				}+	}+		else if ( gxmessagePresent() || gmessagePresent() || (!gdialogPresent() && !xdialogPresent() && xmessagePresent()) )+		{+				if ( gxmessagePresent() )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"gxmessage");return 1;}+						strcpy( lDialogString , "gxmessage");+				}+				else if ( gmessagePresent() )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"gmessage");return 1;}+						strcpy( lDialogString , "gmessage");+				}+				else+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xmessage");return 1;}+						strcpy( lDialogString , "xmessage");+				}++				if ( aDialogType && ! strcmp("okcancel" , aDialogType) )+				{+						strcat( lDialogString , " -buttons Ok:1,Cancel:0");+						switch ( aDefaultButton )+						{+								case 1: strcat( lDialogString , " -default Ok"); break;+								case 0: strcat( lDialogString , " -default Cancel"); break;+						}+				}+				else if ( aDialogType && ! strcmp("yesno" , aDialogType) )+				{+						strcat( lDialogString , " -buttons Yes:1,No:0");+						switch ( aDefaultButton )+						{+								case 1: strcat( lDialogString , " -default Yes"); break;+								case 0: strcat( lDialogString , " -default No"); break;+						}+				}+				else if ( aDialogType && ! strcmp("yesnocancel" , aDialogType) )+				{+						strcat( lDialogString , " -buttons Yes:1,No:2,Cancel:0");+						switch ( aDefaultButton )+						{+								case 1: strcat( lDialogString , " -default Yes"); break;+								case 2: strcat( lDialogString , " -default No"); break;+								case 0: strcat( lDialogString , " -default Cancel"); break;+						}+				}+				else+				{+						strcat( lDialogString , " -buttons Ok:1");+						strcat( lDialogString , " -default Ok");+				}++				strcat( lDialogString , " -center \"");+				if ( aMessage && strlen(aMessage) )+				{+						strcat( lDialogString , aMessage ) ;+				}+				strcat(lDialogString, "\"" ) ;+				if ( aTitle && strlen(aTitle) )+				{+						strcat( lDialogString , " -title  \"");+						strcat( lDialogString , aTitle ) ;+						strcat( lDialogString, "\"" ) ;+				}+				strcat( lDialogString , " ; echo $? ");+		}+		else if ( xdialogPresent() || gdialogPresent() || dialogName() || whiptailPresent() )+		{+				if ( gdialogPresent( ) )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"gdialog");return 1;}+						lWasGraphicDialog = 1 ;+						strcpy( lDialogString , "(gdialog " ) ;+				}+				else if ( xdialogPresent( ) )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xdialog");return 1;}+						lWasGraphicDialog = 1 ;+						strcpy( lDialogString , "(Xdialog " ) ;+				}+				else if ( dialogName( ) )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return 0;}+						if ( isTerminalRunning( ) )+						{+								strcpy( lDialogString , "(dialog " ) ;+						}+						else+						{+								lWasXterm = 1 ;+								strcpy( lDialogString , terminalName() ) ;+								strcat( lDialogString , "'(" ) ;+								strcat( lDialogString , dialogName() ) ;+								strcat( lDialogString , " " ) ;+						}+				}+				else if ( isTerminalRunning( ) )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"whiptail");return 0;}+						strcpy( lDialogString , "(whiptail " ) ;+				}+				else+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"whiptail");return 0;}+						lWasXterm = 1 ;+						strcpy( lDialogString , terminalName() ) ;+						strcat( lDialogString , "'(whiptail " ) ;+				}++				if ( aTitle && strlen(aTitle) )+				{+						strcat(lDialogString, "--title \"") ;+						strcat(lDialogString, aTitle) ;+						strcat(lDialogString, "\" ") ;+				}++				if ( !xdialogPresent() && !gdialogPresent() )+				{+						if ( aDialogType && ( !strcmp( "okcancel" , aDialogType ) || !strcmp( "yesno" , aDialogType )+								|| !strcmp( "yesnocancel" , aDialogType ) ) )+						{+								strcat(lDialogString, "--backtitle \"") ;+								strcat(lDialogString, "tab: move focus") ;+								strcat(lDialogString, "\" ") ;+						}+				}++				if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) )+				{+						if ( ! aDefaultButton )+						{+								strcat( lDialogString , "--defaultno " ) ;+						}+						strcat( lDialogString ,+										"--yes-label \"Ok\" --no-label \"Cancel\" --yesno " ) ;+				}+				else if ( aDialogType && ! strcmp( "yesno" , aDialogType ) )+				{+						if ( ! aDefaultButton )+						{+								strcat( lDialogString , "--defaultno " ) ;+						}+						strcat( lDialogString , "--yesno " ) ;+				}+				else if (aDialogType && !strcmp("yesnocancel", aDialogType))+				{+						if (!aDefaultButton)+						{+								strcat(lDialogString, "--defaultno ");+						}+						strcat(lDialogString, "--menu ");+				}+				else+				{+						strcat( lDialogString , "--msgbox " ) ;++				}+				strcat( lDialogString , "\"" ) ;+				if ( aMessage && strlen(aMessage) )+				{+						strcat(lDialogString, aMessage) ;+				}+				strcat(lDialogString, "\" ");++				if ( lWasGraphicDialog )+				{+						if (aDialogType && !strcmp("yesnocancel", aDialogType))+						{+								strcat(lDialogString,"0 60 0 Yes \"\" No \"\") 2>/tmp/tinyfd.txt;\+if [ $? = 0 ];then tinyfdBool=1;else tinyfdBool=0;fi;\+tinyfdRes=$(cat /tmp/tinyfd.txt);echo $tinyfdBool$tinyfdRes") ;+						}+						else+						{+								strcat(lDialogString,+								   "10 60 ) 2>&1;if [ $? = 0 ];then echo 1;else echo 0;fi");+						}+				}+				else+				{+						if (aDialogType && !strcmp("yesnocancel", aDialogType))+						{+								strcat(lDialogString,"0 60 0 Yes \"\" No \"\" >/dev/tty ) 2>/tmp/tinyfd.txt;\+				if [ $? = 0 ];then tinyfdBool=1;else tinyfdBool=0;fi;\+				tinyfdRes=$(cat /tmp/tinyfd.txt);echo $tinyfdBool$tinyfdRes") ;++								if ( lWasXterm )+								{+										strcat(lDialogString," >/tmp/tinyfd0.txt';cat /tmp/tinyfd0.txt");+								}+								else+								{+										strcat(lDialogString, "; clear >/dev/tty") ;+								}+						}+						else+						{+								strcat(lDialogString, "10 60 >/dev/tty) 2>&1;if [ $? = 0 ];");+								if ( lWasXterm )+								{+										strcat( lDialogString ,+"then\n\techo 1\nelse\n\techo 0\nfi >/tmp/tinyfd.txt';cat /tmp/tinyfd.txt;rm /tmp/tinyfd.txt");+								}+								else+								{+								   strcat(lDialogString,+												  "then echo 1;else echo 0;fi;clear >/dev/tty");+								}+						}+				}+		}+		else if (  !isTerminalRunning() && terminalName() )+		{+				if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return 0;}+				strcpy( lDialogString , terminalName() ) ;+				strcat( lDialogString , "'" ) ;+				if ( !gWarningDisplayed && !tinyfd_forceConsole)+				{+						gWarningDisplayed = 1 ;+						strcat( lDialogString , "echo \"" ) ;+						strcat( lDialogString, gTitle) ;+						strcat( lDialogString , "\";" ) ;+						strcat( lDialogString , "echo \"" ) ;+						strcat( lDialogString, tinyfd_needs) ;+						strcat( lDialogString , "\";echo;echo;" ) ;+				}+				if ( aTitle && strlen(aTitle) )+				{+						strcat( lDialogString , "echo \"" ) ;+						strcat( lDialogString, aTitle) ;+						strcat( lDialogString , "\";echo;" ) ;+				}+				if ( aMessage && strlen(aMessage) )+				{+						strcat( lDialogString , "echo \"" ) ;+						strcat( lDialogString, aMessage) ;+						strcat( lDialogString , "\"; " ) ;+				}+				if ( aDialogType && !strcmp("yesno",aDialogType) )+				{+						strcat( lDialogString , "echo -n \"y/n: \"; " ) ;+						strcat( lDialogString , "stty sane -echo;" ) ;+						strcat( lDialogString ,+								"answer=$( while ! head -c 1 | grep -i [ny];do true ;done);");+						strcat( lDialogString ,+								"if echo \"$answer\" | grep -iq \"^y\";then\n");+						strcat( lDialogString , "\techo 1\nelse\n\techo 0\nfi" ) ;+				}+				else if ( aDialogType && !strcmp("okcancel",aDialogType) )+				{+						strcat( lDialogString , "echo -n \"[O]kay/[C]ancel: \"; " ) ;+						strcat( lDialogString , "stty sane -echo;" ) ;+						strcat( lDialogString ,+								"answer=$( while ! head -c 1 | grep -i [oc];do true ;done);");+						strcat( lDialogString ,+								"if echo \"$answer\" | grep -iq \"^o\";then\n");+						strcat( lDialogString , "\techo 1\nelse\n\techo 0\nfi" ) ;+				}+				else if ( aDialogType && !strcmp("yesnocancel",aDialogType) )+				{+						strcat( lDialogString , "echo -n \"[Y]es/[N]o/[C]ancel: \"; " ) ;+						strcat( lDialogString , "stty sane -echo;" ) ;+						strcat( lDialogString ,+								"answer=$( while ! head -c 1 | grep -i [nyc];do true ;done);");+						strcat( lDialogString ,+								"if echo \"$answer\" | grep -iq \"^y\";then\n\techo 1\n");+						strcat( lDialogString , "elif echo \"$answer\" | grep -iq \"^n\";then\n\techo 2\n" ) ;+						strcat( lDialogString , "else\n\techo 0\nfi" ) ;+				}+				else+				{+						strcat(lDialogString , "echo -n \"press enter to continue \"; ");+						strcat( lDialogString , "stty sane -echo;" ) ;+						strcat( lDialogString ,+								"answer=$( while ! head -c 1;do true ;done);echo 1");+				}+				strcat( lDialogString ,+						" >/tmp/tinyfd.txt';cat /tmp/tinyfd.txt;rm /tmp/tinyfd.txt");+		}+		else if ( !isTerminalRunning() && pythonDbusPresent() && !strcmp("ok" , aDialogType) )+		{+				if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python-dbus");return 1;}+				strcpy( lDialogString , gPythonName ) ;+				strcat( lDialogString ," -c \"import dbus;bus=dbus.SessionBus();");+				strcat( lDialogString ,"notif=bus.get_object('org.freedesktop.Notifications','/org/freedesktop/Notifications');" ) ;+				strcat( lDialogString ,"notify=dbus.Interface(notif,'org.freedesktop.Notifications');" ) ;+				strcat( lDialogString ,"notify.Notify('',0,'" ) ;+				if ( aIconType && strlen(aIconType) )+				{+						strcat( lDialogString , aIconType ) ;+				}+				strcat(lDialogString, "','") ;+				if ( aTitle && strlen(aTitle) )+				{+						strcat(lDialogString, aTitle) ;+				}+				strcat(lDialogString, "','") ;+				if ( aMessage && strlen(aMessage) )+				{+						lpDialogString = lDialogString + strlen(lDialogString);+						tfd_replaceSubStr( aMessage , "\n" , "\\n" , lpDialogString ) ;+				}+				strcat(lDialogString, "','','',5000)\"") ;+		}+		else if ( !isTerminalRunning() && (perlPresent() >= 2)  && !strcmp("ok" , aDialogType) )+		{+				if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"perl-dbus");return 1;}++								strcpy( lDialogString ,  "perl -e \"use Net::DBus;\+my \\$sessionBus = Net::DBus->session;\+my \\$notificationsService = \\$sessionBus->get_service('org.freedesktop.Notifications');\+my \\$notificationsObject = \\$notificationsService->get_object('/org/freedesktop/Notifications',\+'org.freedesktop.Notifications');");++								sprintf( lDialogString + strlen(lDialogString),+"my \\$notificationId;\\$notificationId = \\$notificationsObject->Notify(shift, 0, '%s', '%s', '%s', [], {}, -1);\" ",+														aIconType?aIconType:"", aTitle?aTitle:"", aMessage?aMessage:"" ) ;+		}+		else if ( !isTerminalRunning() && notifysendPresent() && !strcmp("ok" , aDialogType) )+		{++			if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"notifysend");return 1;}+			strcpy( lDialogString , "notify-send" ) ;+			if ( aIconType && strlen(aIconType) )+			{+					strcat( lDialogString , " -i '" ) ;+					strcat( lDialogString , aIconType ) ;+					strcat( lDialogString , "'" ) ;+			}+			strcat( lDialogString , " \"" ) ;+			if ( aTitle && strlen(aTitle) )+			{+					strcat(lDialogString, aTitle) ;+					strcat( lDialogString , " | " ) ;+			}+			if ( aMessage && strlen(aMessage) )+			{+				tfd_replaceSubStr( aMessage , "\n\t" , " |  " , lBuff ) ;+				tfd_replaceSubStr( lBuff , "\n" , " | " , lBuff2 ) ;+				tfd_replaceSubStr( lBuff2 , "\t" , "  " , lBuff ) ;+				strcat(lDialogString, lBuff) ;+			}+			strcat( lDialogString , "\"" ) ;+		}+		else if ( !isTerminalRunning() && notifyPresent() && !strcmp("ok" , aDialogType) )+		{++			if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"notify");return 1;}+			strcpy( lDialogString , "notify" ) ;+			if ( aIconType && strlen(aIconType) )+			{+				strcat( lDialogString , " --icon '" ) ;+				strcat( lDialogString , aIconType ) ;+				strcat( lDialogString , "'" ) ;+			}+			if ( aTitle && strlen(aTitle) )+			{+				strcat( lDialogString , " --title '" ) ;+				strcat( lDialogString , aTitle ) ;+				strcat( lDialogString , "'" ) ;+			}+			strcat( lDialogString , " \"" ) ;+			if ( aMessage && strlen(aMessage) )+			{+				tfd_replaceSubStr( aMessage , "\n\t" , " |  " , lBuff ) ;+				tfd_replaceSubStr( lBuff , "\n" , " | " , lBuff2 ) ;+				tfd_replaceSubStr( lBuff2 , "\t" , "  " , lBuff ) ;+				strcat(lDialogString, lBuff) ;+			}+			else strcat(lDialogString, "''");+			strcat( lDialogString , "\"" ) ;+		}+		else+		{+				if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return 0;}+				if ( !gWarningDisplayed && !tinyfd_forceConsole)+				{+						gWarningDisplayed = 1 ;+						printf("\n\n%s\n", gTitle);+						printf("%s\n\n", tinyfd_needs);+				}+				if ( aTitle && strlen(aTitle) )+				{+						printf("\n%s\n", aTitle);+				}++				tcgetattr(0, &infoOri);+				tcgetattr(0, &info);+				info.c_lflag &= ~ICANON;+				info.c_cc[VMIN] = 1;+				info.c_cc[VTIME] = 0;+				tcsetattr(0, TCSANOW, &info);+				if ( aDialogType && !strcmp("yesno",aDialogType) )+				{+						do+						{+								if ( aMessage && strlen(aMessage) )+								{+										printf("\n%s\n",aMessage);+								}+								printf("y/n: "); fflush(stdout);+								lChar = (char) tolower( getchar() ) ;+								printf("\n\n");+						}+						while ( lChar != 'y' && lChar != 'n' );+						lResult = lChar == 'y' ? 1 : 0 ;+				}+				else if ( aDialogType && !strcmp("okcancel",aDialogType) )+				{+						do+						{+								if ( aMessage && strlen(aMessage) )+								{+										printf("\n%s\n",aMessage);+								}+								printf("[O]kay/[C]ancel: "); fflush(stdout);+								lChar = (char) tolower( getchar() ) ;+								printf("\n\n");+						}+						while ( lChar != 'o' && lChar != 'c' );+						lResult = lChar == 'o' ? 1 : 0 ;+				}+				else if ( aDialogType && !strcmp("yesnocancel",aDialogType) )+				{+						do+						{+								if ( aMessage && strlen(aMessage) )+								{+										printf("\n%s\n",aMessage);+								}+								printf("[Y]es/[N]o/[C]ancel: "); fflush(stdout);+								lChar = (char) tolower( getchar() ) ;+								printf("\n\n");+						}+						while ( lChar != 'y' && lChar != 'n' && lChar != 'c' );+						lResult = (lChar == 'y') ? 1 : (lChar == 'n') ? 2 : 0 ;+				}+				else+				{+						if ( aMessage && strlen(aMessage) )+						{+								printf("\n%s\n\n",aMessage);+						}+						printf("press enter to continue "); fflush(stdout);+						(void) getchar() ;+						printf("\n\n");+						lResult = 1 ;+				}+				tcsetattr(0, TCSANOW, &infoOri);+				free(lDialogString);+				return lResult ;+		}++		if (tinyfd_verbose) printf( "lDialogString: %s\n" , lDialogString ) ;++		lIn = ( FILE * ) popen( lDialogString , "r" ) ;+		if ( ! lIn )+		{+				free(lDialogString);+				return 0 ;+		}+		while ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL )+		{}++		pclose( lIn ) ;++		/* printf( "lBuff: %s len: %lu \n" , lBuff , strlen(lBuff) ) ; */+		if ( strlen( lBuff ) && lBuff[strlen( lBuff ) -1] == '\n' )+		{+				lBuff[strlen( lBuff ) -1] = '\0' ;+		}+		/* printf( "lBuff1: %s len: %lu \n" , lBuff , strlen(lBuff) ) ; */++		if (aDialogType && !strcmp("yesnocancel", aDialogType))+		{+				if ( lBuff[0]=='1' )+				{+						if ( !strcmp( lBuff+1 , "Yes" )) strcpy(lBuff,"1");+						else if ( !strcmp( lBuff+1 , "No" )) strcpy(lBuff,"2");+				}+		}+		/* printf( "lBuff2: %s len: %lu \n" , lBuff , strlen(lBuff) ) ; */++		lResult =  !strcmp( lBuff , "2" ) ? 2 : !strcmp( lBuff , "1" ) ? 1 : 0;++		/* printf( "lResult: %d\n" , lResult ) ; */+		free(lDialogString);+		return lResult ;+}+++/* return has only meaning for tinyfd_query */+int tinyfd_notifyPopup(+		char const * aTitle , /* NULL or "" */+		char const * aMessage , /* NULL or ""  may contain \n and \t */+		char const * aIconType ) /* "info" "warning" "error" */+{+	char lBuff[MAX_PATH_OR_CMD];+	char lBuff2[MAX_PATH_OR_CMD];+		char * lDialogString = NULL ;+		char * lpDialogString ;+		FILE * lIn ;+		size_t lTitleLen ;+		size_t lMessageLen ;++		if (tfd_quoteDetected(aTitle)) return tinyfd_notifyPopup("INVALID TITLE WITH QUOTES", aMessage, aIconType);+		if (tfd_quoteDetected(aMessage)) return tinyfd_notifyPopup(aTitle, "INVALID MESSAGE WITH QUOTES", aIconType);++		if ( getenv("SSH_TTY") && !dunstifyPresent() && !dunstPresent() )+		{+			return tinyfd_messageBox(aTitle, aMessage, "ok", aIconType, 0);+		}++		lTitleLen =  aTitle ? strlen(aTitle) : 0 ;+		lMessageLen =  aMessage ? strlen(aMessage) : 0 ;+		if ( !aTitle || strcmp(aTitle,"tinyfd_query") )+		{+			lDialogString = (char *) malloc( MAX_PATH_OR_CMD + lTitleLen + lMessageLen );+		}++		if ( getenv("SSH_TTY") )+		{+			if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dunst");return 1;}+			strcpy( lDialogString , "notify-send \"" ) ;+			if ( aTitle && strlen(aTitle) )+			{+				strcat( lDialogString , aTitle ) ;+				strcat( lDialogString , "\" \"" ) ;+			}+			if ( aMessage && strlen(aMessage) )+			{+				strcat(lDialogString, aMessage) ;+			}+			strcat( lDialogString , "\"" ) ;+		}+		else if ( osascriptPresent( ) )+		{+				if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return 1;}++				strcpy( lDialogString , "osascript ");+				if ( ! osx9orBetter() ) strcat( lDialogString , " -e 'tell application \"System Events\"' -e 'Activate'");+				strcat( lDialogString , " -e 'try' -e 'display notification \"") ;+				if ( aMessage && strlen(aMessage) )+				{+						strcat(lDialogString, aMessage) ;+				}+				strcat(lDialogString, " \" ") ;+				if ( aTitle && strlen(aTitle) )+				{+						strcat(lDialogString, "with title \"") ;+						strcat(lDialogString, aTitle) ;+						strcat(lDialogString, "\" ") ;+				}++				strcat( lDialogString, "' -e 'end try'") ;+				if ( ! osx9orBetter() ) strcat( lDialogString, " -e 'end tell'") ;+		}+		else if ( tfd_kdialogPresent() )+		{+				if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return 1;}+				strcpy( lDialogString , "kdialog" ) ;++				if ( aIconType && strlen(aIconType) )+				{+						strcat( lDialogString , " --icon '" ) ;+						strcat( lDialogString , aIconType ) ;+						strcat( lDialogString , "'" ) ;+				}+				if ( aTitle && strlen(aTitle) )+				{+						strcat( lDialogString , " --title \"" ) ;+						strcat( lDialogString , aTitle ) ;+						strcat( lDialogString , "\"" ) ;+				}++				strcat( lDialogString , " --passivepopup" ) ;+				strcat( lDialogString , " \"" ) ;+				if ( aMessage )+				{+						strcat( lDialogString , aMessage ) ;+				}+				strcat( lDialogString , " \" 5" ) ;+		}+		else if ( tfd_yadPresent() )+		{+			if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"yad");return 1;}+			strcpy( lDialogString , "yad --notification");++			if ( aIconType && strlen( aIconType ) )+			{+					strcat( lDialogString , " --image=\"");+					strcat( lDialogString , aIconType ) ;+					strcat( lDialogString , "\"" ) ;+			}++			strcat( lDialogString , " --text=\"" ) ;+			if ( aTitle && strlen(aTitle) )+			{+					strcat(lDialogString, aTitle) ;+					strcat(lDialogString, "\n") ;+			}+			if ( aMessage && strlen( aMessage ) )+			{+					strcat( lDialogString , aMessage ) ;+			}+			strcat( lDialogString , " \"" ) ;+		}+		else if ( perlPresent() >= 2 )+		{+				if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"perl-dbus");return 1;}++				strcpy( lDialogString , "perl -e \"use Net::DBus;\+my \\$sessionBus = Net::DBus->session;\+my \\$notificationsService = \\$sessionBus->get_service('org.freedesktop.Notifications');\+my \\$notificationsObject = \\$notificationsService->get_object('/org/freedesktop/Notifications',\+'org.freedesktop.Notifications');");++				sprintf( lDialogString + strlen(lDialogString) ,+"my \\$notificationId;\\$notificationId = \\$notificationsObject->Notify(shift, 0, '%s', '%s', '%s', [], {}, -1);\" ",+aIconType?aIconType:"", aTitle?aTitle:"", aMessage?aMessage:"" ) ;+		}+		else if ( pythonDbusPresent( ) )+		{+				if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python-dbus");return 1;}+				strcpy( lDialogString , gPythonName ) ;+				strcat( lDialogString ," -c \"import dbus;bus=dbus.SessionBus();");+				strcat( lDialogString ,"notif=bus.get_object('org.freedesktop.Notifications','/org/freedesktop/Notifications');" ) ;+				strcat( lDialogString ,"notify=dbus.Interface(notif,'org.freedesktop.Notifications');" ) ;+				strcat( lDialogString ,"notify.Notify('',0,'" ) ;+				if ( aIconType && strlen(aIconType) )+				{+						strcat( lDialogString , aIconType ) ;+				}+				strcat(lDialogString, "','") ;+				if ( aTitle && strlen(aTitle) )+				{+						strcat(lDialogString, aTitle) ;+				}+				strcat(lDialogString, "','") ;+				if ( aMessage && strlen(aMessage) )+				{+						lpDialogString = lDialogString + strlen(lDialogString);+						tfd_replaceSubStr( aMessage , "\n" , "\\n" , lpDialogString ) ;+				}+				strcat(lDialogString, "','','',5000)\"") ;+		}+		else if ( notifysendPresent() )+		{+			if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"notifysend");return 1;}+			strcpy( lDialogString , "notify-send" ) ;+			if ( aIconType && strlen(aIconType) )+			{+					strcat( lDialogString , " -i '" ) ;+					strcat( lDialogString , aIconType ) ;+					strcat( lDialogString , "'" ) ;+			}+			strcat( lDialogString , " \"" ) ;+			if ( aTitle && strlen(aTitle) )+			{+					strcat(lDialogString, aTitle) ;+					strcat( lDialogString , " | " ) ;+			}+			if ( aMessage && strlen(aMessage) )+			{+				tfd_replaceSubStr( aMessage , "\n\t" , " |  " , lBuff ) ;+				tfd_replaceSubStr( lBuff , "\n" , " | " , lBuff2 ) ;+				tfd_replaceSubStr( lBuff2 , "\t" , "  " , lBuff ) ;+				strcat(lDialogString, lBuff) ;+			}+			strcat( lDialogString , "\"" ) ;+		}+		else if ( notifyPresent() ) /* haiku */+		{+			if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"notify");return 1;}+			strcpy( lDialogString , "notify" ) ;+			if ( aIconType && strlen(aIconType) )+			{+				strcat( lDialogString , " --icon '" ) ;+				strcat( lDialogString , aIconType ) ;+				strcat( lDialogString , "'" ) ;+			}+			if ( aTitle && strlen(aTitle) )+			{+				strcat( lDialogString , " --title '" ) ;+				strcat(lDialogString, aTitle) ;+				strcat( lDialogString , "'" ) ;+			}+			strcat( lDialogString , " \"" ) ;+			if ( aMessage && strlen(aMessage) )+			{+				tfd_replaceSubStr( aMessage , "\n\t" , " |  " , lBuff ) ;+				tfd_replaceSubStr( lBuff , "\n" , " | " , lBuff2 ) ;+				tfd_replaceSubStr( lBuff2 , "\t" , "  " , lBuff ) ;+				strcat(lDialogString, lBuff) ;+			}+			else strcat(lDialogString, "''" ) ;+			strcat( lDialogString , "\"" ) ;+		}+		else if ( tfd_zenityVersion()>=3018 )+		{+				/* zenity 2.32 & 3.14 has the notification but with a bug: it doesnt return from it */+				/* zenity 3.8 show the notification as an alert ok cancel box */+				/* zenity 3.44 doesn't have the notification (3.42 has it) */+				if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return 1;}+				strcpy( lDialogString , "zenity --notification");+				+				if ( aIconType && strlen( aIconType ) )+				{+						strcat( lDialogString , " --window-icon '");+						strcat( lDialogString , aIconType ) ;+						strcat( lDialogString , "'" ) ;+				}++				strcat( lDialogString , " --text \"" ) ;+				if ( aTitle && strlen(aTitle) )+				{+						strcat(lDialogString, aTitle) ;+						strcat(lDialogString, "\n") ;+				}+				if ( aMessage && strlen( aMessage ) )+				{+						strcat( lDialogString , aMessage ) ;+				}+				strcat( lDialogString , " \"" ) ;+		}+		else+		{+			if (lDialogString) free(lDialogString);+			return tinyfd_messageBox(aTitle, aMessage, "ok", aIconType, 0);+		}++		if (tinyfd_verbose) printf( "lDialogString: %s\n" , lDialogString ) ;++		lIn = ( FILE * ) popen( lDialogString , "r" ) ;+		if ( ! lIn )+		{+				free(lDialogString);+				return 0 ;+		}++		pclose( lIn ) ;+		free(lDialogString);+		return 1;+}+++/* returns NULL on cancel */+char * tinyfd_inputBox(+		char const * aTitle , /* NULL or "" */+		char const * aMessage , /* NULL or "" (\n and \t have no effect) */+		char const * aDefaultInput ) /* "" , if NULL it's a passwordBox */+{+		static char lBuff[MAX_PATH_OR_CMD];+		char * lDialogString = NULL;+		char * lpDialogString;+		FILE * lIn ;+		int lResult ;+		int lWasGdialog = 0 ;+		int lWasGraphicDialog = 0 ;+		int lWasXterm = 0 ;+		int lWasBasicXterm = 0 ;+		struct termios oldt ;+		struct termios newt ;+		char * lEOF;+		size_t lTitleLen ;+		size_t lMessageLen ;++		if (!aTitle && !aMessage && !aDefaultInput) return lBuff; /* now I can fill lBuff from outside */++		lBuff[0]='\0';++		if (tfd_quoteDetected(aTitle)) return tinyfd_inputBox("INVALID TITLE WITH QUOTES", aMessage, aDefaultInput);+		if (tfd_quoteDetected(aMessage)) return tinyfd_inputBox(aTitle, "INVALID MESSAGE WITH QUOTES", aDefaultInput);+		if (tfd_quoteDetected(aDefaultInput)) return tinyfd_inputBox(aTitle, aMessage, "INVALID DEFAULT_INPUT WITH QUOTES: use the GRAVE ACCENT \\x60 instead.");++		lTitleLen =  aTitle ? strlen(aTitle) : 0 ;+		lMessageLen =  aMessage ? strlen(aMessage) : 0 ;+		if ( !aTitle || strcmp(aTitle,"tinyfd_query") )+		{+			lDialogString = (char *) malloc( MAX_PATH_OR_CMD + lTitleLen + lMessageLen );+		}++		if ( osascriptPresent( ) )+		{+			if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return (char *)1;}+			strcpy( lDialogString , "osascript ");+			if ( ! osx9orBetter() ) strcat( lDialogString , " -e 'tell application \"System Events\"' -e 'Activate'");+			strcat( lDialogString , " -e 'try' -e 'display dialog \"") ;+			if ( aMessage && strlen(aMessage) )+			{+					strcat(lDialogString, aMessage) ;+			}+			strcat(lDialogString, "\" ") ;+			strcat(lDialogString, "default answer \"") ;+			if ( aDefaultInput && strlen(aDefaultInput) )+			{+					strcat(lDialogString, aDefaultInput) ;+			}+			strcat(lDialogString, "\" ") ;+			if ( ! aDefaultInput )+			{+					strcat(lDialogString, "hidden answer true ") ;+			}+			if ( aTitle && strlen(aTitle) )+			{+					strcat(lDialogString, "with title \"") ;+					strcat(lDialogString, aTitle) ;+					strcat(lDialogString, "\" ") ;+			}+			strcat(lDialogString, "with icon note' ") ;+			strcat(lDialogString, "-e '\"1\" & text returned of result' " );+			strcat(lDialogString, "-e 'on error number -128' " ) ;+			strcat(lDialogString, "-e '0' " );+			strcat(lDialogString, "-e 'end try'") ;+			if ( ! osx9orBetter() ) strcat(lDialogString, " -e 'end tell'") ;+		}+		else if ( tfd_kdialogPresent() )+		{+			if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return (char *)1;}+			strcpy( lDialogString , "szAnswer=$(kdialog" ) ;++							if ( (tfd_kdialogPresent() == 2) && tfd_xpropPresent() )+			{+					strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */+			}++			if ( ! aDefaultInput )+			{+					strcat(lDialogString, " --password ") ;+			}+			else+			{+					strcat(lDialogString, " --inputbox ") ;++			}+			strcat(lDialogString, "\"") ;+			if ( aMessage && strlen(aMessage) )+			{+					strcat(lDialogString, aMessage ) ;+			}+			strcat(lDialogString , "\" \"" ) ;+			if ( aDefaultInput && strlen(aDefaultInput) )+			{+					strcat(lDialogString, aDefaultInput ) ;+			}+			strcat(lDialogString , "\"" ) ;+			if ( aTitle && strlen(aTitle) )+			{+					strcat(lDialogString, " --title \"") ;+					strcat(lDialogString, aTitle) ;+					strcat(lDialogString, "\"") ;+			}+			strcat( lDialogString ,+					");if [ $? = 0 ];then echo 1$szAnswer;else echo 0$szAnswer;fi");+		}+		else if ( tfd_zenityPresent() || tfd_matedialogPresent() || tfd_shellementaryPresent()+				|| tfd_qarmaPresent() || tfd_shantyPresent() || tfd_boxerPresent() )+		{+			if ( tfd_zenityPresent() )+			{+					if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return (char *)1;}+					strcpy( lDialogString , "szAnswer=$(zenity" ) ;+					if ( (tfd_zenityVersion() >= 3010) && !getenv("SSH_TTY") && tfd_xpropPresent() )+					{+							strcat( lDialogString, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */+					}+			}+			else if ( tfd_matedialogPresent() )+			{+					if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return (char *)1;}+					strcpy( lDialogString ,  "szAnswer=$(matedialog" ) ;+			}+			else if ( tfd_shellementaryPresent() )+			{+					if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"shellementary");return (char *)1;}+					strcpy( lDialogString , "szAnswer=$(shellementary" ) ;+			}+			else if ( tfd_qarmaPresent() )+			{+					if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"qarma");return (char *)1;}+					strcpy( lDialogString ,  "szAnswer=$(qarma" ) ;+					if ( !getenv("SSH_TTY") && tfd_xpropPresent() )+					{+							strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */+					}+			}+			else if ( tfd_boxerPresent() )+			{+					if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"boxer");return (char *)1;}+					strcpy( lDialogString ,  "szAnswer=$(boxer" ) ;+			}+			else if ( tfd_shantyPresent() )+			{+					if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"shanty");return (char *)1;}+					strcpy( lDialogString ,  "szAnswer=$(shanty" ) ;+			}+			else {}+			+			strcat( lDialogString ," --entry" ) ;++			strcat(lDialogString, " --title=\"") ;+			if (aTitle && strlen(aTitle)) strcat(lDialogString, aTitle) ;+			strcat(lDialogString, "\"") ;++			strcat(lDialogString, " --text=\"") ;+			if (aMessage && strlen(aMessage)) strcat(lDialogString, aMessage) ;+			strcat(lDialogString, "\"") ;++			if ( aDefaultInput )+			{+					strcat(lDialogString, " --entry-text=\"") ;+					strcat(lDialogString, aDefaultInput) ;+					strcat(lDialogString, "\"") ;+			}+			else+			{+					strcat(lDialogString, " --hide-text") ;+			}+			if (tinyfd_silent) strcat( lDialogString , " 2>/dev/null ");+			strcat( lDialogString ,+							");if [ $? = 0 ];then echo 1$szAnswer;else echo 0$szAnswer;fi");+		}+		else if (tfd_yadPresent())+		{+		   if (aTitle && !strcmp(aTitle, "tinyfd_query")) { strcpy(tinyfd_response, "yad"); return (char*)1; }+		   strcpy(lDialogString, "szAnswer=$(yad --entry");+		   if (aTitle && strlen(aTitle))+		   {+			  strcat(lDialogString, " --title=\"");+			  strcat(lDialogString, aTitle);+			  strcat(lDialogString, "\"");+		   }+		   if (aMessage && strlen(aMessage))+		   {+			  strcat(lDialogString, " --text=\"");+			  strcat(lDialogString, aMessage);+			  strcat(lDialogString, "\"");+		   }+		   if (aDefaultInput && strlen(aDefaultInput))+		   {+			  strcat(lDialogString, " --entry-text=\"");+			  strcat(lDialogString, aDefaultInput);+			  strcat(lDialogString, "\"");+		   }+		   else+		   {+			  strcat(lDialogString, " --hide-text");+		   }+		   if (tinyfd_silent) strcat(lDialogString, " 2>/dev/null ");+		   strcat(lDialogString,+			  ");if [ $? = 0 ];then echo 1$szAnswer;else echo 0$szAnswer;fi");+		}+		else if ( gxmessagePresent() || gmessagePresent() )+		{+				if ( gxmessagePresent() ) {+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"gxmessage");return (char *)1;}+						strcpy( lDialogString , "szAnswer=$(gxmessage -buttons Ok:1,Cancel:0 -center \"");+				}+				else+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"gmessage");return (char *)1;}+						strcpy( lDialogString , "szAnswer=$(gmessage -buttons Ok:1,Cancel:0 -center \"");+				}++				if ( aMessage && strlen(aMessage) )+				{+						strcat( lDialogString , aMessage ) ;+				}+				strcat(lDialogString, "\"" ) ;+				if ( aTitle && strlen(aTitle) )+				{+						strcat( lDialogString , " -title  \"");+						strcat( lDialogString , aTitle ) ;+						strcat(lDialogString, "\" " ) ;+				}+				strcat(lDialogString, " -entrytext \"" ) ;+				if ( aDefaultInput && strlen(aDefaultInput) )+				{+						strcat( lDialogString , aDefaultInput ) ;+				}+				strcat(lDialogString, "\"" ) ;+				strcat( lDialogString , ");echo $?$szAnswer");+		}+				else if ( !gdialogPresent() && !xdialogPresent() && tkinter3Present( ) )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python3-tkinter");return (char *)1;}+						strcpy( lDialogString , gPython3Name ) ;+						strcat( lDialogString ,+								" -S -c \"import tkinter; from tkinter import simpledialog;root=tkinter.Tk();root.withdraw();");+						strcat( lDialogString ,"res=simpledialog.askstring(" ) ;+						if ( aTitle && strlen(aTitle) )+						{+								strcat(lDialogString, "title='") ;+								strcat(lDialogString, aTitle) ;+								strcat(lDialogString, "',") ;+						}+						if ( aMessage && strlen(aMessage) )+						{++								strcat(lDialogString, "prompt='") ;+								lpDialogString = lDialogString + strlen(lDialogString);+								tfd_replaceSubStr( aMessage , "\n" , "\\n" , lpDialogString ) ;+								strcat(lDialogString, "',") ;+						}+						if ( aDefaultInput )+						{+								if ( strlen(aDefaultInput) )+								{+										strcat(lDialogString, "initialvalue='") ;+										strcat(lDialogString, aDefaultInput) ;+										strcat(lDialogString, "',") ;+								}+						}+						else+						{+								strcat(lDialogString, "show='*'") ;+						}+						strcat(lDialogString, ");\nif res is None :\n\tprint(0)");+						strcat(lDialogString, "\nelse :\n\tprint('1'+res)\n\"" ) ;+				}+				else if ( !gdialogPresent() && !xdialogPresent() && tkinter2Present( ) )+		{+				if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python2-tkinter");return (char *)1;}+								strcpy( lDialogString , "export PYTHONIOENCODING=utf-8;" ) ;+								strcat( lDialogString , gPython2Name ) ;+								if ( ! isTerminalRunning( ) && tfd_isDarwin( ) )+				{+				strcat( lDialogString , " -i" ) ;  /* for osx without console */+				}++								strcat( lDialogString ,+										" -S -c \"import Tkinter,tkSimpleDialog;root=Tkinter.Tk();root.withdraw();");++				if ( tfd_isDarwin( ) )+				{+						strcat( lDialogString ,+"import os;os.system('''/usr/bin/osascript -e 'tell app \\\"Finder\\\" to set \+frontmost of process \\\"Python\\\" to true' ''');");+				}++				strcat( lDialogString ,"res=tkSimpleDialog.askstring(" ) ;+				if ( aTitle && strlen(aTitle) )+				{+						strcat(lDialogString, "title='") ;+						strcat(lDialogString, aTitle) ;+						strcat(lDialogString, "',") ;+				}+				if ( aMessage && strlen(aMessage) )+				{++						strcat(lDialogString, "prompt='") ;+						lpDialogString = lDialogString + strlen(lDialogString);+						tfd_replaceSubStr( aMessage , "\n" , "\\n" , lpDialogString ) ;+						strcat(lDialogString, "',") ;+				}+				if ( aDefaultInput )+				{+						if ( strlen(aDefaultInput) )+						{+								strcat(lDialogString, "initialvalue='") ;+								strcat(lDialogString, aDefaultInput) ;+								strcat(lDialogString, "',") ;+						}+				}+				else+				{+						strcat(lDialogString, "show='*'") ;+				}+				strcat(lDialogString, ");\nif res is None :\n\tprint 0");+				strcat(lDialogString, "\nelse :\n\tprint '1'+res\n\"" ) ;+		}+		else if ( gdialogPresent() || xdialogPresent() || dialogName() || whiptailPresent() )+		{+				if ( gdialogPresent( ) )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"gdialog");return (char *)1;}+						lWasGraphicDialog = 1 ;+						lWasGdialog = 1 ;+						strcpy( lDialogString , "(gdialog " ) ;+				}+				else if ( xdialogPresent( ) )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xdialog");return (char *)1;}+						lWasGraphicDialog = 1 ;+						strcpy( lDialogString , "(Xdialog " ) ;+				}+				else if ( dialogName( ) )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char *)0;}+						if ( isTerminalRunning( ) )+						{+								strcpy( lDialogString , "(dialog " ) ;+						}+						else+						{+								lWasXterm = 1 ;+								strcpy( lDialogString , terminalName() ) ;+								strcat( lDialogString , "'(" ) ;+								strcat( lDialogString , dialogName() ) ;+								strcat( lDialogString , " " ) ;+						}+				}+				else if ( isTerminalRunning( ) )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"whiptail");return (char *)0;}+						strcpy( lDialogString , "(whiptail " ) ;+				}+				else+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"whiptail");return (char *)0;}+						lWasXterm = 1 ;+						strcpy( lDialogString , terminalName() ) ;+						strcat( lDialogString , "'(whiptail " ) ;+				}++				if ( aTitle && strlen(aTitle) )+				{+						strcat(lDialogString, "--title \"") ;+						strcat(lDialogString, aTitle) ;+						strcat(lDialogString, "\" ") ;+				}++				if ( !xdialogPresent() && !gdialogPresent() )+				{+						strcat(lDialogString, "--backtitle \"") ;+						strcat(lDialogString, "tab: move focus") ;+						if ( ! aDefaultInput && !lWasGdialog )+						{+								strcat(lDialogString, " (sometimes nothing, no blink nor star, is shown in text field)") ;+						}+						strcat(lDialogString, "\" ") ;+				}++				if ( aDefaultInput || lWasGdialog )+				{+						strcat( lDialogString , "--inputbox" ) ;+				}+				else+				{+						if ( !lWasGraphicDialog && dialogName() && isDialogVersionBetter09b() )+						{+								strcat( lDialogString , "--insecure " ) ;+						}+						strcat( lDialogString , "--passwordbox" ) ;+				}+				strcat( lDialogString , " \"" ) ;+				if ( aMessage && strlen(aMessage) )+				{+						strcat(lDialogString, aMessage) ;+				}+				strcat(lDialogString,"\" 10 60 ") ;+				if ( aDefaultInput && strlen(aDefaultInput) )+				{+						strcat(lDialogString, "\"") ;+						strcat(lDialogString, aDefaultInput) ;+						strcat(lDialogString, "\" ") ;+				}+				if ( lWasGraphicDialog )+				{+						strcat(lDialogString,") 2>/tmp/tinyfd.txt;\+		if [ $? = 0 ];then tinyfdBool=1;else tinyfdBool=0;fi;\+		tinyfdRes=$(cat /tmp/tinyfd.txt);echo $tinyfdBool$tinyfdRes") ;+				}+				else+				{+						strcat(lDialogString,">/dev/tty ) 2>/tmp/tinyfd.txt;\+		if [ $? = 0 ];then tinyfdBool=1;else tinyfdBool=0;fi;\+		tinyfdRes=$(cat /tmp/tinyfd.txt);echo $tinyfdBool$tinyfdRes") ;++						if ( lWasXterm )+						{+				strcat(lDialogString," >/tmp/tinyfd0.txt';cat /tmp/tinyfd0.txt");+						}+						else+						{+								strcat(lDialogString, "; clear >/dev/tty") ;+						}+				}+		}+		else if ( ! isTerminalRunning( ) && terminalName() )+		{+				if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return (char *)0;}+				lWasBasicXterm = 1 ;+				strcpy( lDialogString , terminalName() ) ;+				strcat( lDialogString , "'" ) ;+				if ( !gWarningDisplayed && !tinyfd_forceConsole)+				{+										gWarningDisplayed = 1 ;+										tinyfd_messageBox(gTitle,tinyfd_needs,"ok","warning",0);+				}+				if ( aTitle && strlen(aTitle) && !tinyfd_forceConsole)+				{+						strcat( lDialogString , "echo \"" ) ;+						strcat( lDialogString, aTitle) ;+						strcat( lDialogString , "\";echo;" ) ;+				}++				strcat( lDialogString , "echo \"" ) ;+				if ( aMessage && strlen(aMessage) )+				{+						strcat( lDialogString, aMessage) ;+				}+				strcat( lDialogString , "\";read " ) ;+				if ( ! aDefaultInput )+				{+						strcat( lDialogString , "-s " ) ;+				}+				strcat( lDialogString , "-p \"" ) ;+				strcat( lDialogString , "(esc+enter to cancel): \" ANSWER " ) ;+				strcat( lDialogString , ";echo 1$ANSWER >/tmp/tinyfd.txt';" ) ;+				strcat( lDialogString , "cat -v /tmp/tinyfd.txt");+		}+		else if ( !gWarningDisplayed && ! isTerminalRunning( ) && ! terminalName() ) {+						gWarningDisplayed = 1 ;+						tinyfd_messageBox(gTitle,tinyfd_needs,"ok","warning",0);+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"no_solution");return (char *)0;}+						free(lDialogString);+						return NULL;+		}+		else+		{+				if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return (char *)0;}+				if ( !gWarningDisplayed && !tinyfd_forceConsole)+				{+						gWarningDisplayed = 1 ;+						tinyfd_messageBox(gTitle,tinyfd_needs,"ok","warning",0);+				}+				if ( aTitle && strlen(aTitle) )+				{+						printf("\n%s\n", aTitle);+				}+				if ( aMessage && strlen(aMessage) )+				{+						printf("\n%s\n",aMessage);+				}+				printf("(esc+enter to cancel): "); fflush(stdout);+				if ( ! aDefaultInput )+				{+						tcgetattr(STDIN_FILENO, & oldt) ;+						newt = oldt ;+						newt.c_lflag &= ~ECHO ;+						tcsetattr(STDIN_FILENO, TCSANOW, & newt);+				}++				lEOF = fgets(lBuff, MAX_PATH_OR_CMD, stdin);+				/* printf("lbuff<%c><%d>\n",lBuff[0],lBuff[0]); */+				if ( ! lEOF  || (lBuff[0] == '\0') )+				{+						free(lDialogString);+						return NULL;+				}++				if ( lBuff[0] == '\n' )+				{+						lEOF = fgets(lBuff, MAX_PATH_OR_CMD, stdin);+						/* printf("lbuff<%c><%d>\n",lBuff[0],lBuff[0]); */+						if ( ! lEOF  || (lBuff[0] == '\0') )+						{+								free(lDialogString);+								return NULL;+						}+				}++				if ( ! aDefaultInput )+				{+						tcsetattr(STDIN_FILENO, TCSANOW, & oldt);+						printf("\n");+				}+				printf("\n");+				if ( strchr(lBuff,27) )+				{+						free(lDialogString);+						return NULL ;+				}+				if ( lBuff[strlen( lBuff ) -1] == '\n' )+				{+						lBuff[strlen( lBuff ) -1] = '\0' ;+				}+				free(lDialogString);+				return lBuff ;+		}++		if (tinyfd_verbose) printf( "lDialogString: %s\n" , lDialogString ) ;+		lIn = ( FILE * ) popen( lDialogString , "r" );+		if ( ! lIn  )+		{+				if ( fileExists("/tmp/tinyfd.txt") )+				{+						wipefile("/tmp/tinyfd.txt");+						remove("/tmp/tinyfd.txt");+				}+				if ( fileExists("/tmp/tinyfd0.txt") )+				{+						wipefile("/tmp/tinyfd0.txt");+						remove("/tmp/tinyfd0.txt");+				}+				free(lDialogString);+				return NULL ;+		}+		while ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL )+		{}++		pclose( lIn ) ;++		if ( fileExists("/tmp/tinyfd.txt") )+		{+				wipefile("/tmp/tinyfd.txt");+				remove("/tmp/tinyfd.txt");+		}+		if ( fileExists("/tmp/tinyfd0.txt") )+		{+				wipefile("/tmp/tinyfd0.txt");+				remove("/tmp/tinyfd0.txt");+		}++		/* printf( "len Buff: %lu\n" , strlen(lBuff) ) ; */+		/* printf( "lBuff0: %s\n" , lBuff ) ; */+		if ( strlen( lBuff ) && lBuff[strlen( lBuff ) -1] == '\n' )+		{+				lBuff[strlen( lBuff ) -1] = '\0' ;+		}+		/* printf( "lBuff1: %s len: %lu \n" , lBuff , strlen(lBuff) ) ; */+		if ( lWasBasicXterm )+		{+				if ( strstr(lBuff,"^[") ) /* esc was pressed */+				{+						free(lDialogString);+						return NULL ;+				}+		}++		lResult =  strncmp( lBuff , "1" , 1) ? 0 : 1 ;+		/* printf( "lResult: %d \n" , lResult ) ; */+		if ( ! lResult )+		{+				free(lDialogString);+				return NULL ;+		}++		/* printf( "lBuff+1: %s\n" , lBuff+1 ) ; */+		free(lDialogString);+		return lBuff+1 ;+}+++char * tinyfd_saveFileDialog(+        char const * aTitle , /* NULL or "" */+        char const * aDefaultPathAndOrFile , /* NULL or "" , ends with / to set only a directory */+        int aNumOfFilterPatterns , /* 0 */+        char const * const * aFilterPatterns , /* NULL or {"*.txt","*.doc"} */+        char const * aSingleFilterDescription ) /* NULL or "text files" */+{+		static char lBuff[MAX_PATH_OR_CMD] ;+		static char lLastDirectory[MAX_PATH_OR_CMD] = "$PWD" ;++		char lDialogString[MAX_PATH_OR_CMD] ;+		char lString[MAX_PATH_OR_CMD] ;+		int i ;+		int lWasGraphicDialog = 0 ;+		int lWasXterm = 0 ;+		char * p ;+        char * lPointerInputBox ;+		FILE * lIn ;+		lBuff[0]='\0';++		if ( ! aFilterPatterns ) aNumOfFilterPatterns = 0 ;+				if (tfd_quoteDetected(aTitle)) return tinyfd_saveFileDialog("INVALID TITLE WITH QUOTES", aDefaultPathAndOrFile, aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription);+				if (tfd_quoteDetected(aDefaultPathAndOrFile)) return tinyfd_saveFileDialog(aTitle, "INVALID DEFAULT_PATH WITH QUOTES", aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription);+				if (tfd_quoteDetected(aSingleFilterDescription)) return tinyfd_saveFileDialog(aTitle, aDefaultPathAndOrFile, aNumOfFilterPatterns, aFilterPatterns, "INVALID FILTER_DESCRIPTION WITH QUOTES");+				for (i = 0; i < aNumOfFilterPatterns; i++)+				{+						if (tfd_quoteDetected(aFilterPatterns[i])) return tinyfd_saveFileDialog("INVALID FILTER_PATTERN WITH QUOTES: use the GRAVE ACCENT \\x60 instead.", aDefaultPathAndOrFile, 0, NULL, NULL);+				}++		if ( osascriptPresent( ) )+		{+				if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return (char *)1;}+				strcpy( lDialogString , "osascript ");+				if ( ! osx9orBetter() ) strcat( lDialogString , " -e 'tell application \"Finder\"' -e 'Activate'");+				strcat( lDialogString , " -e 'try' -e 'POSIX path of ( choose file name " );+				if ( aTitle && strlen(aTitle) )+				{+						strcat(lDialogString, "with prompt \"") ;+						strcat(lDialogString, aTitle) ;+						strcat(lDialogString, "\" ") ;+				}+				getPathWithoutFinalSlash( lString , aDefaultPathAndOrFile ) ;+				if ( strlen(lString) )+				{+						strcat(lDialogString, "default location \"") ;+						strcat(lDialogString, lString ) ;+						strcat(lDialogString , "\" " ) ;+				}+				getLastName( lString , aDefaultPathAndOrFile ) ;+				if ( strlen(lString) )+				{+						strcat(lDialogString, "default name \"") ;+						strcat(lDialogString, lString ) ;+						strcat(lDialogString , "\" " ) ;+				}+				strcat( lDialogString , ")' " ) ;+				strcat(lDialogString, "-e 'on error number -128' " ) ;+				strcat(lDialogString, "-e 'end try'") ;+				if ( ! osx9orBetter() ) strcat( lDialogString, " -e 'end tell'") ;+		}+		else if ( tfd_kdialogPresent() )+		{+				if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return (char *)1;}++				strcpy( lDialogString , "kdialog" ) ;+                if ( (tfd_kdialogPresent() == 2) && tfd_xpropPresent() )+				{+                    strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */+				}+				strcat( lDialogString , " --getsavefilename " ) ;++				if ( aDefaultPathAndOrFile && strlen(aDefaultPathAndOrFile) )+				{+					strcat(lDialogString, "\"") ;+					if ( aDefaultPathAndOrFile[0] != '/' )+					{+						strcat(lDialogString, lLastDirectory) ;+						strcat(lDialogString , "/" ) ;+					}+					strcat(lDialogString, aDefaultPathAndOrFile ) ;+					strcat(lDialogString , "\"" ) ;+				}+				else+				{+					strcat(lDialogString, "\"") ;+					strcat(lDialogString, lLastDirectory) ;+					strcat(lDialogString , "/" ) ;+					strcat(lDialogString, "\"") ;+				}++				if ( aNumOfFilterPatterns > 0 )+				{+						strcat(lDialogString , " \"" ) ;+                        strcat( lDialogString , aFilterPatterns[0] ) ;+						for ( i = 1 ; i < aNumOfFilterPatterns ; i ++ )+						{+														strcat( lDialogString , " " ) ;+														strcat( lDialogString , aFilterPatterns[i] ) ;+						}+						if ( aSingleFilterDescription && strlen(aSingleFilterDescription) )+						{+								strcat( lDialogString , " | " ) ;+								strcat( lDialogString , aSingleFilterDescription ) ;+						}+						strcat( lDialogString , "\"" ) ;+				}+				if ( aTitle && strlen(aTitle) )+				{+						strcat(lDialogString, " --title \"") ;+						strcat(lDialogString, aTitle) ;+						strcat(lDialogString, "\"") ;+				}+		}+		else if ( tfd_zenityPresent() || tfd_matedialogPresent() || tfd_shellementaryPresent()+				|| tfd_qarmaPresent() || tfd_shantyPresent() || tfd_boxerPresent() )+		{+				if ( tfd_zenityPresent() )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return (char *)1;}+						strcpy( lDialogString , "zenity" ) ;+						if ( (tfd_zenityVersion() >= 3010) && !getenv("SSH_TTY") && tfd_xpropPresent() )+						{+								strcat( lDialogString, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */+						}+				}+				else if ( tfd_matedialogPresent() )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return (char *)1;}+						strcpy( lDialogString , "matedialog" ) ;+				}+				else if ( tfd_shellementaryPresent() )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"shellementary");return (char *)1;}+						strcpy( lDialogString , "shellementary" ) ;+				}+				else if ( tfd_qarmaPresent() )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"qarma");return (char *)1;}+						strcpy( lDialogString , "qarma" ) ;+												if ( !getenv("SSH_TTY") && tfd_xpropPresent() )+						{+								strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */+						}+				}+				else if ( tfd_boxerPresent() )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"boxer");return (char *)1;}+						strcpy( lDialogString , "boxer" ) ;+				}+				else if ( tfd_shantyPresent() )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"shanty");return (char *)1;}+						strcpy( lDialogString , "shanty" ) ;+				}+				else {}+				+				strcat(lDialogString, " --file-selection --save --confirm-overwrite" ) ;++				strcat(lDialogString, " --title=\"") ;+				if (aTitle && strlen(aTitle)) strcat(lDialogString, aTitle) ;+				strcat(lDialogString, "\"") ;++				if ( aDefaultPathAndOrFile && strlen(aDefaultPathAndOrFile) )+				{+						strcat(lDialogString, " --filename=\"") ;+						strcat(lDialogString, aDefaultPathAndOrFile) ;+						strcat(lDialogString, "\"") ;+				}+				if ( aNumOfFilterPatterns > 0 )+				{+						strcat( lDialogString , " --file-filter='" ) ;+						if ( aSingleFilterDescription && strlen(aSingleFilterDescription) )+						{+								strcat( lDialogString , aSingleFilterDescription ) ;+								strcat( lDialogString , " | " ) ;+						}+						strcat( lDialogString , aFilterPatterns[0] ) ;+						for ( i = 1 ; i < aNumOfFilterPatterns ; i ++ )+						{+							strcat( lDialogString , " " ) ;+							strcat( lDialogString , aFilterPatterns[i] ) ;+						}+						strcat( lDialogString , "' --file-filter='All files | *'" ) ;+				}+				if (tinyfd_silent) strcat( lDialogString , " 2>/dev/null ");+		}+		else if (tfd_yadPresent())+		{+		   if (aTitle && !strcmp(aTitle, "tinyfd_query")) { strcpy(tinyfd_response, "yad"); return (char*)1; }+		   strcpy(lDialogString, "yad --file --save --confirm-overwrite");+		   if (aTitle && strlen(aTitle))+		   {+			  strcat(lDialogString, " --title=\"");+			  strcat(lDialogString, aTitle);+			  strcat(lDialogString, "\"");+		   }+		   if (aDefaultPathAndOrFile && strlen(aDefaultPathAndOrFile))+		   {+			  strcat(lDialogString, " --filename=\"");+			  strcat(lDialogString, aDefaultPathAndOrFile);+			  strcat(lDialogString, "\"");+		   }+		   if (aNumOfFilterPatterns > 0)+		   {+				strcat(lDialogString, " --file-filter='");+				if (aSingleFilterDescription && strlen(aSingleFilterDescription))+				{+					strcat(lDialogString, aSingleFilterDescription);+					strcat(lDialogString, " | ");+				}+				strcat(lDialogString, aFilterPatterns[0]);+				for ( i = 1 ; i < aNumOfFilterPatterns; i++)+				{+					strcat(lDialogString, " ");+					strcat(lDialogString, aFilterPatterns[i]);+				}+				strcat(lDialogString, "' --file-filter='All files | *'");+		   }+		   if (tinyfd_silent) strcat(lDialogString, " 2>/dev/null ");+	  }+	  else if ( !xdialogPresent() && tkinter3Present( ) )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python3-tkinter");return (char *)1;}+						strcpy( lDialogString , gPython3Name ) ;+						strcat( lDialogString ,+								" -S -c \"import tkinter;from tkinter import filedialog;root=tkinter.Tk();root.withdraw();");+						strcat( lDialogString , "res=filedialog.asksaveasfilename(");+						if ( aTitle && strlen(aTitle) )+						{+								strcat(lDialogString, "title='") ;+								strcat(lDialogString, aTitle) ;+								strcat(lDialogString, "',") ;+						}+						if ( aDefaultPathAndOrFile && strlen(aDefaultPathAndOrFile) )+						{+								getPathWithoutFinalSlash( lString , aDefaultPathAndOrFile ) ;+								if ( strlen(lString) )+								{+										strcat(lDialogString, "initialdir='") ;+										strcat(lDialogString, lString ) ;+										strcat(lDialogString , "'," ) ;+								}+								getLastName( lString , aDefaultPathAndOrFile ) ;+								if ( strlen(lString) )+								{+										strcat(lDialogString, "initialfile='") ;+										strcat(lDialogString, lString ) ;+										strcat(lDialogString , "'," ) ;+								}+						}+						if ( ( aNumOfFilterPatterns > 1 )+								|| ( (aNumOfFilterPatterns == 1) /* test because poor osx behaviour */+								&& ( aFilterPatterns[0][strlen(aFilterPatterns[0])-1] != '*' ) ) )+						{+								strcat(lDialogString , "filetypes=(" ) ;+								strcat( lDialogString , "('" ) ;+								if ( aSingleFilterDescription && strlen(aSingleFilterDescription) )+								{+										strcat( lDialogString , aSingleFilterDescription ) ;+								}+								strcat( lDialogString , "',(" ) ;+								for ( i = 0 ; i < aNumOfFilterPatterns ; i ++ )+								{+										strcat( lDialogString , "'" ) ;+										strcat( lDialogString , aFilterPatterns[i] ) ;+										strcat( lDialogString , "'," ) ;+								}+								strcat( lDialogString , "))," ) ;+								strcat( lDialogString , "('All files','*'))" ) ;+						}+						strcat( lDialogString, ");\nif not isinstance(res, tuple):\n\tprint(res)\n\"" ) ;+				}+				else if ( !xdialogPresent() && tkinter2Present( ) )+		{+				if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python2-tkinter");return (char *)1;}+								strcpy( lDialogString , "export PYTHONIOENCODING=utf-8;" ) ;+								strcat( lDialogString , gPython2Name ) ;+								if ( ! isTerminalRunning( ) && tfd_isDarwin( ))+				{+				strcat( lDialogString , " -i" ) ;  /* for osx without console */+				}+			strcat( lDialogString ,+" -S -c \"import Tkinter,tkFileDialog;root=Tkinter.Tk();root.withdraw();");++		if ( tfd_isDarwin( ) )+		{+                strcat( lDialogString ,+"import os;os.system('''/usr/bin/osascript -e 'tell app \\\"Finder\\\" to set \+frontmost of process \\\"Python\\\" to true' ''');");+				}++				strcat( lDialogString , "res=tkFileDialog.asksaveasfilename(");+				if ( aTitle && strlen(aTitle) )+				{+						strcat(lDialogString, "title='") ;+						strcat(lDialogString, aTitle) ;+						strcat(lDialogString, "',") ;+				}+			if ( aDefaultPathAndOrFile && strlen(aDefaultPathAndOrFile) )+			{+						getPathWithoutFinalSlash( lString , aDefaultPathAndOrFile ) ;+						if ( strlen(lString) )+						{+								strcat(lDialogString, "initialdir='") ;+								strcat(lDialogString, lString ) ;+								strcat(lDialogString , "'," ) ;+						}+						getLastName( lString , aDefaultPathAndOrFile ) ;+						if ( strlen(lString) )+						{+								strcat(lDialogString, "initialfile='") ;+								strcat(lDialogString, lString ) ;+								strcat(lDialogString , "'," ) ;+						}+				}+			if ( ( aNumOfFilterPatterns > 1 )+				  || ( (aNumOfFilterPatterns == 1) /* test because poor osx behaviour */+						&& ( aFilterPatterns[0][strlen(aFilterPatterns[0])-1] != '*' ) ) )+			{+						strcat(lDialogString , "filetypes=(" ) ;+						strcat( lDialogString , "('" ) ;+						if ( aSingleFilterDescription && strlen(aSingleFilterDescription) )+						{+								strcat( lDialogString , aSingleFilterDescription ) ;+						}+						strcat( lDialogString , "',(" ) ;+						for ( i = 0 ; i < aNumOfFilterPatterns ; i ++ )+						{+								strcat( lDialogString , "'" ) ;+								strcat( lDialogString , aFilterPatterns[i] ) ;+								strcat( lDialogString , "'," ) ;+						}+						strcat( lDialogString , "))," ) ;+						strcat( lDialogString , "('All files','*'))" ) ;+			}+						strcat( lDialogString, ");\nif not isinstance(res, tuple):\n\tprint res \n\"" ) ;+				}+		else if ( xdialogPresent() || dialogName() )+		{+				if ( xdialogPresent( ) )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xdialog");return (char *)1;}+						lWasGraphicDialog = 1 ;+						strcpy( lDialogString , "(Xdialog " ) ;+				}+				else if ( isTerminalRunning( ) )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char *)0;}+						strcpy( lDialogString , "(dialog " ) ;+				}+				else+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char *)0;}+						lWasXterm = 1 ;+						strcpy( lDialogString , terminalName() ) ;+						strcat( lDialogString , "'(" ) ;+						strcat( lDialogString , dialogName() ) ;+						strcat( lDialogString , " " ) ;+				}++				if ( aTitle && strlen(aTitle) )+				{+						strcat(lDialogString, "--title \"") ;+						strcat(lDialogString, aTitle) ;+						strcat(lDialogString, "\" ") ;+				}++				if ( !xdialogPresent() && !gdialogPresent() )+				{+						strcat(lDialogString, "--backtitle \"") ;+						strcat(lDialogString,+								"tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY") ;+						strcat(lDialogString, "\" ") ;+				}++				strcat( lDialogString , "--fselect \"" ) ;+				if ( aDefaultPathAndOrFile && strlen(aDefaultPathAndOrFile) )+				{+						if ( ! strchr(aDefaultPathAndOrFile, '/') )+						{+								strcat(lDialogString, "./") ;+						}+						strcat(lDialogString, aDefaultPathAndOrFile) ;+				}+				else if ( ! isTerminalRunning( ) && !lWasGraphicDialog )+				{+						strcat(lDialogString, getenv("HOME")) ;+						strcat(lDialogString, "/") ;+				}+				else+				{+						strcat(lDialogString, "./") ;+				}++				if ( lWasGraphicDialog )+				{+						strcat(lDialogString, "\" 0 60 ) 2>&1 ") ;+				}+				else+				{+						strcat(lDialogString, "\" 0 60  >/dev/tty) ") ;+						if ( lWasXterm )+						{+						  strcat( lDialogString ,+								"2>/tmp/tinyfd.txt';cat /tmp/tinyfd.txt;rm /tmp/tinyfd.txt");+						}+						else+						{+								strcat(lDialogString, "2>&1 ; clear >/dev/tty") ;+						}+				}+		}+		else+		{+				if (aTitle&&!strcmp(aTitle,"tinyfd_query")){return tinyfd_inputBox(aTitle,NULL,NULL);}+								strcpy(lBuff, "Save file in ");+								strcat(lBuff, getCurDir());+								lPointerInputBox = tinyfd_inputBox(NULL, NULL, NULL); /* obtain a pointer on the current content of tinyfd_inputBox */+								if (lPointerInputBox) strcpy(lString, lPointerInputBox); /* preserve the current content of tinyfd_inputBox */+								p = tinyfd_inputBox(aTitle, lBuff, "");+								if (p) strcpy(lBuff, p); else lBuff[0] = '\0';+								if (lPointerInputBox) strcpy(lPointerInputBox, lString); /* restore its previous content to tinyfd_inputBox */+								p = lBuff;++								getPathWithoutFinalSlash( lString , p ) ;+				if ( strlen( lString ) && ! dirExists( lString ) )+				{+						return NULL ;+				}+				getLastName(lString,p);+				if ( ! strlen(lString) )+				{+						return NULL;+				}+				return p ;+		}++	if (tinyfd_verbose) printf( "lDialogString: %s\n" , lDialogString ) ;+	+	lIn = ( FILE * ) popen( lDialogString , "r" ) ;+	if ( ! lIn )+	{+		return NULL ;+	}+	while ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL )+	{}+	pclose( lIn ) ;+	if ( strlen(lBuff) && lBuff[strlen( lBuff ) -1] == '\n' )+	{+		lBuff[strlen( lBuff ) -1] = '\0' ;+	}+    /* printf( "lBuff: %s\n" , lBuff ) ; */+    if ( ! strlen(lBuff) )+    {+            return NULL;+    }++	getPathWithoutFinalSlash( lString , lBuff ) ;+	if ( strlen( lString ) && ! dirExists( lString ) )+	{+		return NULL ;+	}+	strcpy(lLastDirectory, lString) ;++    getLastName(lString,lBuff);+    if ( ! filenameValid(lString) )+    {+            return NULL;+    }+	return lBuff ;+}+++/* in case of multiple files, the separator is | */+char * tinyfd_openFileDialog(+    char const * aTitle , /* NULL or "" */+    char const * aDefaultPathAndOrFile , /* NULL or "" , ends with / to set only a directory */+	int aNumOfFilterPatterns , /* 0 */+    char const * const * aFilterPatterns , /* NULL or {"*.jpg","*.png"} */+	char const * aSingleFilterDescription , /* NULL or "image files" */+	int aAllowMultipleSelects ) /* 0 or 1 */+{+    static char * lBuff = NULL;+    static char lLastDirectory[MAX_PATH_OR_CMD] = "$PWD" ;++    char lDialogString[MAX_PATH_OR_CMD] ;+    char lString[MAX_PATH_OR_CMD] ;+    int i ;+    FILE * lIn ;+    char * p ;+    char * lPointerInputBox ;+    size_t lFullBuffLen ;+    int lWasKdialog = 0 ;+    int lWasGraphicDialog = 0 ;+    int lWasXterm = 0 ;++		if ( ! aFilterPatterns ) aNumOfFilterPatterns = 0 ;+				if (tfd_quoteDetected(aTitle)) return tinyfd_openFileDialog("INVALID TITLE WITH QUOTES", aDefaultPathAndOrFile, aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription, aAllowMultipleSelects);+				if (tfd_quoteDetected(aDefaultPathAndOrFile)) return tinyfd_openFileDialog(aTitle, "INVALID DEFAULT_PATH WITH QUOTES", aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription, aAllowMultipleSelects);+				if (tfd_quoteDetected(aSingleFilterDescription)) return tinyfd_openFileDialog(aTitle, aDefaultPathAndOrFile, aNumOfFilterPatterns, aFilterPatterns, "INVALID FILTER_DESCRIPTION WITH QUOTES", aAllowMultipleSelects);+				for (i = 0; i < aNumOfFilterPatterns; i++)+				{+						if (tfd_quoteDetected(aFilterPatterns[i])) return tinyfd_openFileDialog("INVALID FILTER_PATTERN WITH QUOTES: use the GRAVE ACCENT \\x60 instead.", aDefaultPathAndOrFile, 0, NULL, NULL, aAllowMultipleSelects);+				}++				free(lBuff);+				if (aTitle&&!strcmp(aTitle,"tinyfd_query"))+				{+						lBuff = NULL;+				}+				else+				{+						if (aAllowMultipleSelects)+						{+								lFullBuffLen = MAX_MULTIPLE_FILES * MAX_PATH_OR_CMD + 1;+								lBuff = (char *) malloc(lFullBuffLen * sizeof(char));+								if (!lBuff)+								{+										lFullBuffLen = LOW_MULTIPLE_FILES * MAX_PATH_OR_CMD + 1;+										lBuff = (char *) malloc( lFullBuffLen * sizeof(char));+								}+						}+						else+						{+								lFullBuffLen = MAX_PATH_OR_CMD + 1;+								lBuff = (char *) malloc(lFullBuffLen * sizeof(char));+						}+						if (!lBuff) return NULL;+						lBuff[0]='\0';+				}++		if ( osascriptPresent( ) )+		{+				if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return (char *)1;}+				strcpy( lDialogString , "osascript ");+				if ( ! osx9orBetter() ) strcat( lDialogString , " -e 'tell application \"System Events\"' -e 'Activate'");+				strcat( lDialogString , " -e 'try' -e '" );+	if ( ! aAllowMultipleSelects )+	{+++						strcat( lDialogString , "POSIX path of ( " );+				}+				else+				{+						strcat( lDialogString , "set mylist to " );+				}+				strcat( lDialogString , "choose file " );+			if ( aTitle && strlen(aTitle) )+			{+						strcat(lDialogString, "with prompt \"") ;+						strcat(lDialogString, aTitle) ;+						strcat(lDialogString, "\" ") ;+			}+				getPathWithoutFinalSlash( lString , aDefaultPathAndOrFile ) ;+				if ( strlen(lString) )+				{+						strcat(lDialogString, "default location \"") ;+						strcat(lDialogString, lString ) ;+						strcat(lDialogString , "\" " ) ;+				}+				if ( aNumOfFilterPatterns > 0 )+				{+						strcat(lDialogString , "of type {\"" );+						strcat( lDialogString , aFilterPatterns[0] + 2 ) ;+						strcat( lDialogString , "\"" ) ;+						for ( i = 1 ; i < aNumOfFilterPatterns ; i ++ )+						{+								strcat( lDialogString , ",\"" ) ;+								strcat( lDialogString , aFilterPatterns[i] + 2) ;+								strcat( lDialogString , "\"" ) ;+						}+						strcat( lDialogString , "} " ) ;+				}+				if ( aAllowMultipleSelects )+				{+						strcat( lDialogString , "multiple selections allowed true ' " ) ;+						strcat( lDialogString ,+										"-e 'set mystring to POSIX path of item 1 of mylist' " );+						strcat( lDialogString ,+										"-e 'repeat with  i from 2 to the count of mylist' " );+						strcat( lDialogString , "-e 'set mystring to mystring & \"|\"' " );+						strcat( lDialogString ,+						"-e 'set mystring to mystring & POSIX path of item i of mylist' " );+						strcat( lDialogString , "-e 'end repeat' " );+						strcat( lDialogString , "-e 'mystring' " );+				}+				else+				{+						strcat( lDialogString , ")' " ) ;+				}+				strcat(lDialogString, "-e 'on error number -128' " ) ;+				strcat(lDialogString, "-e 'end try'") ;+				if ( ! osx9orBetter() ) strcat( lDialogString, " -e 'end tell'") ;+		}+		else if ( tfd_kdialogPresent() )+		{+				if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return (char *)1;}+				lWasKdialog = 1 ;++				strcpy( lDialogString , "kdialog" ) ;+								if ( (tfd_kdialogPresent() == 2) && tfd_xpropPresent() )+				{+						strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */+				}+				strcat( lDialogString , " --getopenfilename " ) ;++				if ( aDefaultPathAndOrFile && strlen(aDefaultPathAndOrFile) )+				{+					strcat(lDialogString, "\"") ;+					if ( aDefaultPathAndOrFile[0] != '/' )+					{+						strcat(lDialogString, lLastDirectory) ;+						strcat(lDialogString , "/" ) ;+					}+					strcat(lDialogString, aDefaultPathAndOrFile ) ;+					strcat(lDialogString , "\"" ) ;+				}+				else+				{+					strcat(lDialogString, "\"") ;+					strcat(lDialogString, lLastDirectory) ;+					strcat(lDialogString , "/" ) ;+					strcat(lDialogString, "\"") ;+				}++				if ( aNumOfFilterPatterns > 0 )+				{+						strcat(lDialogString , " \"" ) ;+												strcat( lDialogString , aFilterPatterns[0] ) ;+						for ( i = 1 ; i < aNumOfFilterPatterns ; i ++ )+						{+														strcat( lDialogString , " " ) ;+														strcat( lDialogString , aFilterPatterns[i] ) ;+						}+						if ( aSingleFilterDescription && strlen(aSingleFilterDescription) )+						{+								strcat( lDialogString , " | " ) ;+								strcat( lDialogString , aSingleFilterDescription ) ;+						}+						strcat( lDialogString , "\"" ) ;+				}+				if ( aAllowMultipleSelects )+				{+						strcat( lDialogString , " --multiple --separate-output" ) ;+				}+				if ( aTitle && strlen(aTitle) )+				{+						strcat(lDialogString, " --title \"") ;+						strcat(lDialogString, aTitle) ;+						strcat(lDialogString, "\"") ;+				}+		}+		else if ( tfd_zenityPresent() || tfd_matedialogPresent() || tfd_shellementaryPresent()+				|| tfd_qarmaPresent() || tfd_shantyPresent() || tfd_boxerPresent() )+		{+				if ( tfd_zenityPresent() )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return (char *)1;}+						strcpy( lDialogString , "zenity" ) ;+						if ( (tfd_zenityVersion() >= 3010) && !getenv("SSH_TTY") && tfd_xpropPresent() )+						{+							strcat( lDialogString, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */+						}+				}+				else if ( tfd_matedialogPresent() )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return (char *)1;}+						strcpy( lDialogString , "matedialog" ) ;+				}+				else if ( tfd_shellementaryPresent() )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"shellementary");return (char *)1;}+						strcpy( lDialogString , "shellementary" ) ;+				}+				else if ( tfd_qarmaPresent() )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"qarma");return (char *)1;}+						strcpy( lDialogString , "qarma" ) ;+												if ( !getenv("SSH_TTY") && tfd_xpropPresent() )+						{+								strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */+						}+				}+				else if ( tfd_boxerPresent() )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"boxer");return (char *)1;}+						strcpy( lDialogString , "boxer" ) ;+				}+				else if ( tfd_shantyPresent() )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"shanty");return (char *)1;}+						strcpy( lDialogString , "shanty" ) ;+				}+				else {}+				+				strcat( lDialogString , " --file-selection" ) ;++				if ( aAllowMultipleSelects )+				{+						strcat( lDialogString , " --multiple" ) ;+				}++				strcat(lDialogString, " --title=\"") ;+				if (aTitle && strlen(aTitle)) strcat(lDialogString, aTitle) ;+				strcat(lDialogString, "\"") ;++				if ( aDefaultPathAndOrFile && strlen(aDefaultPathAndOrFile) )+				{+						strcat(lDialogString, " --filename=\"") ;+						strcat(lDialogString, aDefaultPathAndOrFile) ;+						strcat(lDialogString, "\"") ;+				}+				if ( aNumOfFilterPatterns > 0 )+				{+						strcat( lDialogString , " --file-filter='" ) ;+						if ( aSingleFilterDescription && strlen(aSingleFilterDescription) )+						{+								strcat( lDialogString , aSingleFilterDescription ) ;+								strcat( lDialogString , " | " ) ;+						}+						strcat( lDialogString , aFilterPatterns[0] ) ;+						for ( i = 1 ; i < aNumOfFilterPatterns ; i ++ )+						{+							strcat( lDialogString , " " ) ;+							strcat( lDialogString , aFilterPatterns[i] ) ;+						}+						strcat( lDialogString , "' --file-filter='All files | *'" ) ;+				}+				if (tinyfd_silent) strcat( lDialogString , " 2>/dev/null ");+		}+		else if (tfd_yadPresent())+		{+		   if (aTitle && !strcmp(aTitle, "tinyfd_query")) { strcpy(tinyfd_response, "yad"); return (char*)1; }+		   strcpy(lDialogString, "yad --file");+		   if (aAllowMultipleSelects)+		   {+			  strcat(lDialogString, " --multiple");+		   }+		   if (aTitle && strlen(aTitle))+		   {+			  strcat(lDialogString, " --title=\"");+			  strcat(lDialogString, aTitle);+			  strcat(lDialogString, "\"");+		   }+		   if (aDefaultPathAndOrFile && strlen(aDefaultPathAndOrFile))+		   {+			  strcat(lDialogString, " --filename=\"");+			  strcat(lDialogString, aDefaultPathAndOrFile);+			  strcat(lDialogString, "\"");+		   }+			if (aNumOfFilterPatterns > 0)+			{+				strcat(lDialogString, " --file-filter='");+				if (aSingleFilterDescription && strlen(aSingleFilterDescription))+				{+					strcat(lDialogString, aSingleFilterDescription);+					strcat(lDialogString, " | ");+				}+				strcat(lDialogString, aFilterPatterns[0]);+				for ( i = 1 ; i < aNumOfFilterPatterns; i++)+				{+				 strcat(lDialogString, " ");+				 strcat(lDialogString, aFilterPatterns[i]);+				}+				strcat(lDialogString, "' --file-filter='All files | *'");+			}+			if (tinyfd_silent) strcat(lDialogString, " 2>/dev/null ");+		}+		else if ( tkinter3Present( ) )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python3-tkinter");return (char *)1;}+						strcpy( lDialogString , gPython3Name ) ;+						strcat( lDialogString ,+								" -S -c \"import tkinter;from tkinter import filedialog;root=tkinter.Tk();root.withdraw();");+						strcat( lDialogString , "lFiles=filedialog.askopenfilename(");+						if ( aAllowMultipleSelects )+						{+								strcat( lDialogString , "multiple=1," ) ;+						}+						if ( aTitle && strlen(aTitle) )+						{+								strcat(lDialogString, "title='") ;+								strcat(lDialogString, aTitle) ;+								strcat(lDialogString, "',") ;+						}+						if ( aDefaultPathAndOrFile && strlen(aDefaultPathAndOrFile) )+						{+								getPathWithoutFinalSlash( lString , aDefaultPathAndOrFile ) ;+								if ( strlen(lString) )+								{+										strcat(lDialogString, "initialdir='") ;+										strcat(lDialogString, lString ) ;+										strcat(lDialogString , "'," ) ;+								}+								getLastName( lString , aDefaultPathAndOrFile ) ;+								if ( strlen(lString) )+								{+										strcat(lDialogString, "initialfile='") ;+										strcat(lDialogString, lString ) ;+										strcat(lDialogString , "'," ) ;+								}+						}+						if ( ( aNumOfFilterPatterns > 1 )+								|| ( ( aNumOfFilterPatterns == 1 ) /*test because poor osx behaviour*/+								&& ( aFilterPatterns[0][strlen(aFilterPatterns[0])-1] != '*' ) ) )+						{+								strcat(lDialogString , "filetypes=(" ) ;+								strcat( lDialogString , "('" ) ;+								if ( aSingleFilterDescription && strlen(aSingleFilterDescription) )+								{+										strcat( lDialogString , aSingleFilterDescription ) ;+								}+								strcat( lDialogString , "',(" ) ;+								for ( i = 0 ; i < aNumOfFilterPatterns ; i ++ )+								{+										strcat( lDialogString , "'" ) ;+										strcat( lDialogString , aFilterPatterns[i] ) ;+										strcat( lDialogString , "'," ) ;+								}+								strcat( lDialogString , "))," ) ;+								strcat( lDialogString , "('All files','*'))" ) ;+						}+						strcat( lDialogString , ");\+\nif not isinstance(lFiles, tuple):\n\tprint(lFiles)\nelse:\+\n\tlFilesString=''\n\tfor lFile in lFiles:\n\t\tlFilesString+=str(lFile)+'|'\+\n\tprint(lFilesString[:-1])\n\"" ) ;+				}+				else if ( tkinter2Present( ) )+		{+				if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python2-tkinter");return (char *)1;}+								strcpy( lDialogString , "export PYTHONIOENCODING=utf-8;" ) ;+								strcat( lDialogString , gPython2Name ) ;+								if ( ! isTerminalRunning( ) && tfd_isDarwin( ) )+				{+				strcat( lDialogString , " -i" ) ;  /* for osx without console */+				}+				strcat( lDialogString ,+" -S -c \"import Tkinter,tkFileDialog;root=Tkinter.Tk();root.withdraw();");++		if ( tfd_isDarwin( ) )+		{+						strcat( lDialogString ,+"import os;os.system('''/usr/bin/osascript -e 'tell app \\\"Finder\\\" to set \+frontmost of process \\\"Python\\\" to true' ''');");+				}+				strcat( lDialogString , "lFiles=tkFileDialog.askopenfilename(");+	if ( aAllowMultipleSelects )+	{+						strcat( lDialogString , "multiple=1," ) ;+	}+	if ( aTitle && strlen(aTitle) )+	{+						strcat(lDialogString, "title='") ;+						strcat(lDialogString, aTitle) ;+						strcat(lDialogString, "',") ;+	}+	if ( aDefaultPathAndOrFile && strlen(aDefaultPathAndOrFile) )+	{+						getPathWithoutFinalSlash( lString , aDefaultPathAndOrFile ) ;+						if ( strlen(lString) )+						{+								strcat(lDialogString, "initialdir='") ;+								strcat(lDialogString, lString ) ;+								strcat(lDialogString , "'," ) ;+						}+						getLastName( lString , aDefaultPathAndOrFile ) ;+						if ( strlen(lString) )+						{+								strcat(lDialogString, "initialfile='") ;+								strcat(lDialogString, lString ) ;+								strcat(lDialogString , "'," ) ;+						}+				}+				if ( ( aNumOfFilterPatterns > 1 )+						|| ( ( aNumOfFilterPatterns == 1 ) /*test because poor osx behaviour*/+								&& ( aFilterPatterns[0][strlen(aFilterPatterns[0])-1] != '*' ) ) )+				{+						strcat(lDialogString , "filetypes=(" ) ;+						strcat( lDialogString , "('" ) ;+						if ( aSingleFilterDescription && strlen(aSingleFilterDescription) )+						{+								strcat( lDialogString , aSingleFilterDescription ) ;+						}+						strcat( lDialogString , "',(" ) ;+						for ( i = 0 ; i < aNumOfFilterPatterns ; i ++ )+						{+								strcat( lDialogString , "'" ) ;+								strcat( lDialogString , aFilterPatterns[i] ) ;+								strcat( lDialogString , "'," ) ;+						}+						strcat( lDialogString , "))," ) ;+						strcat( lDialogString , "('All files','*'))" ) ;+				}+				strcat( lDialogString , ");\+\nif not isinstance(lFiles, tuple):\n\tprint lFiles\nelse:\+\n\tlFilesString=''\n\tfor lFile in lFiles:\n\t\tlFilesString+=str(lFile)+'|'\+\n\tprint lFilesString[:-1]\n\"" ) ;+		}+		else if ( xdialogPresent() || dialogName() )+		{+				if ( xdialogPresent( ) )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xdialog");return (char *)1;}+						lWasGraphicDialog = 1 ;+						strcpy( lDialogString , "(Xdialog " ) ;+				}+				else if ( isTerminalRunning( ) )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char *)0;}+						strcpy( lDialogString , "(dialog " ) ;+				}+				else+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char *)0;}+						lWasXterm = 1 ;+						strcpy( lDialogString , terminalName() ) ;+						strcat( lDialogString , "'(" ) ;+						strcat( lDialogString , dialogName() ) ;+						strcat( lDialogString , " " ) ;+				}++				if ( aTitle && strlen(aTitle) )+				{+						strcat(lDialogString, "--title \"") ;+						strcat(lDialogString, aTitle) ;+						strcat(lDialogString, "\" ") ;+				}++				if ( !xdialogPresent() && !gdialogPresent() )+				{+						strcat(lDialogString, "--backtitle \"") ;+						strcat(lDialogString,+								"tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY") ;+						strcat(lDialogString, "\" ") ;+				}++				strcat( lDialogString , "--fselect \"" ) ;+				if ( aDefaultPathAndOrFile && strlen(aDefaultPathAndOrFile) )+				{+						if ( ! strchr(aDefaultPathAndOrFile, '/') )+						{+								strcat(lDialogString, "./") ;+						}+						strcat(lDialogString, aDefaultPathAndOrFile) ;+				}+				else if ( ! isTerminalRunning( ) && !lWasGraphicDialog )+				{+						strcat(lDialogString, getenv("HOME")) ;+						strcat(lDialogString, "/");+				}+				else+				{+						strcat(lDialogString, "./") ;+				}++				if ( lWasGraphicDialog )+				{+						strcat(lDialogString, "\" 0 60 ) 2>&1 ") ;+				}+				else+				{+						strcat(lDialogString, "\" 0 60  >/dev/tty) ") ;+						if ( lWasXterm )+						{+								strcat( lDialogString ,+								"2>/tmp/tinyfd.txt';cat /tmp/tinyfd.txt;rm /tmp/tinyfd.txt");+						}+						else+						{+								strcat(lDialogString, "2>&1 ; clear >/dev/tty") ;+						}+				}+		}+		else+		{+			if (aTitle&&!strcmp(aTitle,"tinyfd_query")){return tinyfd_inputBox(aTitle,NULL,NULL);}+								strcpy(lBuff, "Open file from ");+								strcat(lBuff, getCurDir());+								lPointerInputBox = tinyfd_inputBox(NULL, NULL, NULL); /* obtain a pointer on the current content of tinyfd_inputBox */+								if (lPointerInputBox) strcpy(lDialogString, lPointerInputBox); /* preserve the current content of tinyfd_inputBox */+								p = tinyfd_inputBox(aTitle, lBuff, "");+								if ( p ) strcpy(lBuff, p); else lBuff[0] = '\0';+								if (lPointerInputBox) strcpy(lPointerInputBox, lDialogString); /* restore its previous content to tinyfd_inputBox */+			if ( ! fileExists(lBuff) )+			{+										free(lBuff);+										lBuff = NULL;+			}+								else+								{+										lBuff = (char *)( realloc( lBuff, (strlen(lBuff)+1) * sizeof(char)));+								}+								return lBuff ;+		}++	if (tinyfd_verbose) printf( "lDialogString: %s\n" , lDialogString ) ;+	lIn = ( FILE * ) popen( lDialogString , "r" ) ;+	if ( ! lIn )+	{+				free(lBuff);+				lBuff = NULL;+				return NULL ;+	}+		lBuff[0]='\0';+		p = lBuff;+		while ( fgets( p , sizeof( lBuff ) , lIn ) != NULL )+		{+				p += strlen( p );+		}+	pclose( lIn ) ;++	if ( strlen( lBuff ) && lBuff[strlen( lBuff ) -1] == '\n' )+	{+		lBuff[strlen( lBuff ) -1] = '\0' ;+	}+	/* printf( "strlen lBuff: %d\n" , strlen( lBuff ) ) ; */+		if ( lWasKdialog && aAllowMultipleSelects )+		{+				p = lBuff ;+				while ( ( p = strchr( p , '\n' ) ) )+						* p = '|' ;+		}+		/* printf( "lBuff2: %s\n" , lBuff ) ; */+		if ( ! strlen( lBuff )  )+		{+						free(lBuff);+						lBuff = NULL;+						return NULL;+		}+		if ( aAllowMultipleSelects && strchr(lBuff, '|') )+		{+            if( ! ensureFilesExist( lBuff , lBuff ) )+            {+                    free(lBuff);+                    lBuff = NULL;+                    return NULL;+            }+		}+		else if ( !fileExists(lBuff) )+		{+						free(lBuff);+						lBuff = NULL;+						return NULL;+        }++        p = strrchr(lBuff, '|');+        if ( !p ) p = lBuff ;+        else p ++ ;+        getPathWithoutFinalSlash( lString , p ) ;+        /* printf( "lString [%lu]: %s\n" , strlen(lString) , lString ) ; */+        if ( strlen( lString ) && ! dirExists( lString ) )+        {+            return NULL ;+        }+        strcpy(lLastDirectory, lString) ;++        lBuff = (char *)( realloc( lBuff, (strlen(lBuff)+1) * sizeof(char)));++        /*printf( "lBuff3 [%lu]: %s\n" , strlen(lBuff) , lBuff ) ; */+        return lBuff ;+}+++char * tinyfd_selectFolderDialog(+    char const * aTitle , /* "" */+    char const * aDefaultPath ) /* "" */+{+    static char lBuff[MAX_PATH_OR_CMD] ;+    static char lLastDirectory[MAX_PATH_OR_CMD] = "$PWD" ;++    char lDialogString[MAX_PATH_OR_CMD] ;+    FILE * lIn ;+    char * p ;+    char * lPointerInputBox ;+    int lWasGraphicDialog = 0 ;+    int lWasXterm = 0 ;+    lBuff[0]='\0';++    if (tfd_quoteDetected(aTitle)) return tinyfd_selectFolderDialog("INVALID TITLE WITH QUOTES", aDefaultPath);+    if (tfd_quoteDetected(aDefaultPath)) return tinyfd_selectFolderDialog(aTitle, "INVALID DEFAULT_PATH WITH QUOTES");++		if ( osascriptPresent( ))+		{+				if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return (char *)1;}+				strcpy( lDialogString , "osascript ");+				if ( ! osx9orBetter() ) strcat( lDialogString , " -e 'tell application \"System Events\"' -e 'Activate'");+				strcat( lDialogString , " -e 'try' -e 'POSIX path of ( choose folder ");+				if ( aTitle && strlen(aTitle) )+				{+				strcat(lDialogString, "with prompt \"") ;+				strcat(lDialogString, aTitle) ;+				strcat(lDialogString, "\" ") ;+				}+				if ( aDefaultPath && strlen(aDefaultPath) )+				{+						strcat(lDialogString, "default location \"") ;+						strcat(lDialogString, aDefaultPath ) ;+						strcat(lDialogString , "\" " ) ;+				}+				strcat( lDialogString , ")' " ) ;+				strcat(lDialogString, "-e 'on error number -128' " ) ;+				strcat(lDialogString, "-e 'end try'") ;+				if ( ! osx9orBetter() ) strcat( lDialogString, " -e 'end tell'") ;+		}+		else if ( tfd_kdialogPresent() )+		{+				if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return (char *)1;}+				strcpy( lDialogString , "kdialog" ) ;+								if ( (tfd_kdialogPresent() == 2) && tfd_xpropPresent() )+				{+						strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */+				}+				strcat( lDialogString , " --getexistingdirectory " ) ;++				if ( aDefaultPath && strlen(aDefaultPath) )+				{+						strcat(lDialogString, "\"") ;+						if ( aDefaultPath[0] != '/' )+						{+							strcat(lDialogString, lLastDirectory) ;+							strcat(lDialogString , "/" ) ;+						}+						strcat(lDialogString, aDefaultPath ) ;+						strcat(lDialogString , "\"" ) ;+				}+				else+				{+						strcat(lDialogString, "\"") ;+						strcat(lDialogString, lLastDirectory) ;+						strcat(lDialogString , "/" ) ;+						strcat(lDialogString, "\"") ;+				}++				if ( aTitle && strlen(aTitle) )+				{+						strcat(lDialogString, " --title \"") ;+						strcat(lDialogString, aTitle) ;+						strcat(lDialogString, "\"") ;+				}+		}+		else if ( tfd_zenityPresent() || tfd_matedialogPresent() || tfd_shellementaryPresent()+				|| tfd_qarmaPresent() || tfd_shantyPresent() || tfd_boxerPresent() )+		{+				if ( tfd_zenityPresent() )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return (char *)1;}+						strcpy( lDialogString , "zenity" ) ;+						if ( (tfd_zenityVersion() >= 3010) && !getenv("SSH_TTY") && tfd_xpropPresent() )+						{+								strcat( lDialogString, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */+						}+				}+				else if ( tfd_matedialogPresent() )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return (char *)1;}+						strcpy( lDialogString , "matedialog" ) ;+				}+				else if ( tfd_shellementaryPresent() )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"shellementary");return (char *)1;}+						strcpy( lDialogString , "shellementary" ) ;+				}+				else if ( tfd_qarmaPresent() )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"qarma");return (char *)1;}+						strcpy( lDialogString , "qarma" ) ;+						if ( !getenv("SSH_TTY") && tfd_xpropPresent() )+						{+								strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */+						}+				}+				else if ( tfd_boxerPresent() )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"boxer");return (char *)1;}+						strcpy( lDialogString , "boxer" ) ;+				}+				else if ( tfd_shantyPresent() )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"shanty");return (char *)1;}+						strcpy( lDialogString , "shanty" ) ;+				}+				else {}++				strcat( lDialogString , " --file-selection --directory" ) ;++				strcat(lDialogString, " --title=\"") ;+				if (aTitle && strlen(aTitle)) strcat(lDialogString, aTitle) ;+				strcat(lDialogString, "\"") ;++				if ( aDefaultPath && strlen(aDefaultPath) )+				{+						strcat(lDialogString, " --filename=\"") ;+						strcat(lDialogString, aDefaultPath) ;+						strcat(lDialogString, "\"") ;+				}+				if (tinyfd_silent) strcat( lDialogString , " 2>/dev/null ");+		}+		else if (tfd_yadPresent())+		{+		   if (aTitle && !strcmp(aTitle, "tinyfd_query")) { strcpy(tinyfd_response, "yad"); return (char*)1; }+		   strcpy(lDialogString, "yad --file --directory");+		   if (aTitle && strlen(aTitle))+		   {+			  strcat(lDialogString, " --title=\"");+			  strcat(lDialogString, aTitle);+			  strcat(lDialogString, "\"");+		   }+		   if (aDefaultPath && strlen(aDefaultPath))+		   {+			  strcat(lDialogString, " --filename=\"");+			  strcat(lDialogString, aDefaultPath);+			  strcat(lDialogString, "\"");+		   }+		   if (tinyfd_silent) strcat(lDialogString, " 2>/dev/null ");+	  }+	  else if ( !xdialogPresent() && tkinter3Present( ) )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python3-tkinter");return (char *)1;}+						strcpy( lDialogString , gPython3Name ) ;+						strcat( lDialogString ,+								" -S -c \"import tkinter;from tkinter import filedialog;root=tkinter.Tk();root.withdraw();");+						strcat( lDialogString , "res=filedialog.askdirectory(");+						if ( aTitle && strlen(aTitle) )+						{+								strcat(lDialogString, "title='") ;+								strcat(lDialogString, aTitle) ;+								strcat(lDialogString, "',") ;+						}+						if ( aDefaultPath && strlen(aDefaultPath) )+						{+								strcat(lDialogString, "initialdir='") ;+								strcat(lDialogString, aDefaultPath ) ;+								strcat(lDialogString , "'" ) ;+						}+						strcat( lDialogString, ");\nif not isinstance(res, tuple):\n\tprint(res)\n\"" ) ;+				}+				else if ( !xdialogPresent() && tkinter2Present( ) )+		{+				if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python2-tkinter");return (char *)1;}+								strcpy( lDialogString , "export PYTHONIOENCODING=utf-8;" ) ;+				strcat( lDialogString , gPython2Name ) ;+				if ( ! isTerminalRunning( ) && tfd_isDarwin( ) )+				{+				strcat( lDialogString , " -i" ) ;  /* for osx without console */+				}+		strcat( lDialogString ,+" -S -c \"import Tkinter,tkFileDialog;root=Tkinter.Tk();root.withdraw();");++		if ( tfd_isDarwin( ) )+		{+						strcat( lDialogString ,+"import os;os.system('''/usr/bin/osascript -e 'tell app \\\"Finder\\\" to set \+frontmost of process \\\"Python\\\" to true' ''');");+				}++				strcat( lDialogString , "print tkFileDialog.askdirectory(");+			if ( aTitle && strlen(aTitle) )+			{+						strcat(lDialogString, "title='") ;+						strcat(lDialogString, aTitle) ;+						strcat(lDialogString, "',") ;+			}+		if ( aDefaultPath && strlen(aDefaultPath) )+		{+								strcat(lDialogString, "initialdir='") ;+								strcat(lDialogString, aDefaultPath ) ;+								strcat(lDialogString , "'" ) ;+				}+				strcat( lDialogString , ")\"" ) ;+		}+		else if ( xdialogPresent() || dialogName() )+		{+				if ( xdialogPresent( ) )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xdialog");return (char *)1;}+						lWasGraphicDialog = 1 ;+						strcpy( lDialogString , "(Xdialog " ) ;+				}+				else if ( isTerminalRunning( ) )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char *)0;}+						strcpy( lDialogString , "(dialog " ) ;+				}+				else+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char *)0;}+						lWasXterm = 1 ;+						strcpy( lDialogString , terminalName() ) ;+						strcat( lDialogString , "'(" ) ;+						strcat( lDialogString , dialogName() ) ;+						strcat( lDialogString , " " ) ;+				}++				if ( aTitle && strlen(aTitle) )+				{+						strcat(lDialogString, "--title \"") ;+						strcat(lDialogString, aTitle) ;+						strcat(lDialogString, "\" ") ;+				}++				if ( !xdialogPresent() && !gdialogPresent() )+				{+						strcat(lDialogString, "--backtitle \"") ;+						strcat(lDialogString,+								"tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY") ;+						strcat(lDialogString, "\" ") ;+				}++				strcat( lDialogString , "--dselect \"" ) ;+				if ( aDefaultPath && strlen(aDefaultPath) )+				{+						strcat(lDialogString, aDefaultPath) ;+						ensureFinalSlash(lDialogString);+				}+				else if ( ! isTerminalRunning( ) && !lWasGraphicDialog )+				{+						strcat(lDialogString, getenv("HOME")) ;+						strcat(lDialogString, "/");+				}+				else+				{+						strcat(lDialogString, "./") ;+				}++				if ( lWasGraphicDialog )+				{+						strcat(lDialogString, "\" 0 60 ) 2>&1 ") ;+				}+				else+				{+						strcat(lDialogString, "\" 0 60  >/dev/tty) ") ;+						if ( lWasXterm )+						{+						  strcat( lDialogString ,+								"2>/tmp/tinyfd.txt';cat /tmp/tinyfd.txt;rm /tmp/tinyfd.txt");+						}+						else+						{+								strcat(lDialogString, "2>&1 ; clear >/dev/tty") ;+						}+				}+		}+		else+		{+				if (aTitle&&!strcmp(aTitle,"tinyfd_query")){return tinyfd_inputBox(aTitle,NULL,NULL);}+								strcpy(lBuff, "Select folder from ");+								strcat(lBuff, getCurDir());+								lPointerInputBox = tinyfd_inputBox(NULL, NULL, NULL); /* obtain a pointer on the current content of tinyfd_inputBox */+								if (lPointerInputBox) strcpy(lDialogString, lPointerInputBox); /* preserve the current content of tinyfd_inputBox */+								p = tinyfd_inputBox(aTitle, lBuff, "");+								if (p) strcpy(lBuff, p); else lBuff[0] = '\0';+								if (lPointerInputBox) strcpy(lPointerInputBox, lDialogString); /* restore its previous content to tinyfd_inputBox */+								p = lBuff;++				if ( !p || ! strlen( p ) || ! dirExists( p ) )+				{+						return NULL ;+				}+				return p ;+		}+	if (tinyfd_verbose) printf( "lDialogString: %s\n" , lDialogString ) ;+	lIn = ( FILE * ) popen( lDialogString , "r" ) ;+	if ( ! lIn )+	{+		return NULL ;+	}+    while ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL )+    {}+    pclose( lIn ) ;+	if ( strlen( lBuff ) && lBuff[strlen( lBuff ) -1] == '\n' )+	{+		lBuff[strlen( lBuff ) -1] = '\0' ;+	}+    /* printf( "lBuff: %s\n" , lBuff ) ; */+    if ( ! strlen( lBuff ) || ! dirExists( lBuff ) )+    {+            return NULL ;+    }++	getPathWithoutFinalSlash( lLastDirectory , lBuff ) ;++    return lBuff ;+}+++/* aDefaultRGB is used only if aDefaultHexRGB is absent */+/* aDefaultRGB and aoResultRGB can be the same array */+/* returns NULL on cancel */+/* returns the hexcolor as a string "#FF0000" */+/* aoResultRGB also contains the result */+char * tinyfd_colorChooser(+		char const * aTitle , /* NULL or "" */+		char const * aDefaultHexRGB , /* NULL or "#FF0000"*/+		unsigned char const aDefaultRGB[3] , /* { 0 , 255 , 255 } */+		unsigned char aoResultRGB[3] ) /* { 0 , 0 , 0 } */+{+		static char lDefaultHexRGB[16];+		char lBuff[128] ;++		char lTmp[128] ;+#if !((defined(__cplusplus ) && __cplusplus >= 201103L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__clang__))+				char * lTmp2 ;+#endif+		char lDialogString[MAX_PATH_OR_CMD] ;+		unsigned char lDefaultRGB[3];+		char * p;+				char * lPointerInputBox;+		FILE * lIn ;+		int i ;+		int lWasZenity2032 = 0 ;+		int lWasOsascript = 0 ;+		int lWasXdialog = 0 ;+		lBuff[0]='\0';++				if (tfd_quoteDetected(aTitle)) return tinyfd_colorChooser("INVALID TITLE WITH QUOTES", aDefaultHexRGB, aDefaultRGB, aoResultRGB);+				if (tfd_quoteDetected(aDefaultHexRGB)) return tinyfd_colorChooser(aTitle, "INVALID DEFAULT_HEX_RGB WITH QUOTES: use the GRAVE ACCENT \\x60 instead.", aDefaultRGB, aoResultRGB);++				if (aDefaultHexRGB && (strlen(aDefaultHexRGB)==7) )+				{+						Hex2RGB(aDefaultHexRGB, lDefaultRGB);+			strcpy(lDefaultHexRGB, aDefaultHexRGB);+				}+				else+				{+						lDefaultRGB[0] = aDefaultRGB[0];+						lDefaultRGB[1] = aDefaultRGB[1];+						lDefaultRGB[2] = aDefaultRGB[2];+			RGB2Hex(aDefaultRGB, lDefaultHexRGB);+				}++		if ( osascriptPresent( ) )+		{+				if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return (char *)1;}+				lWasOsascript = 1 ;+				strcpy( lDialogString , "osascript");++				if ( ! osx9orBetter() )+				{+						strcat( lDialogString , " -e 'tell application \"System Events\"' -e 'Activate'");+						strcat( lDialogString , " -e 'try' -e 'set mycolor to choose color default color {");+				}+				else+				{+						strcat( lDialogString ,+" -e 'try' -e 'tell app (path to frontmost application as Unicode text) \+to set mycolor to choose color default color {");+				}++				sprintf(lTmp, "%d", 256 * lDefaultRGB[0] ) ;+				strcat(lDialogString, lTmp ) ;+				strcat(lDialogString, "," ) ;+				sprintf(lTmp, "%d", 256 * lDefaultRGB[1] ) ;+				strcat(lDialogString, lTmp ) ;+				strcat(lDialogString, "," ) ;+				sprintf(lTmp, "%d", 256 * lDefaultRGB[2] ) ;+				strcat(lDialogString, lTmp ) ;+				strcat(lDialogString, "}' " ) ;+				strcat( lDialogString ,+"-e 'set mystring to ((item 1 of mycolor) div 256 as integer) as string' " );+				strcat( lDialogString ,+"-e 'repeat with i from 2 to the count of mycolor' " );+				strcat( lDialogString ,+"-e 'set mystring to mystring & \" \" & ((item i of mycolor) div 256 as integer) as string' " );+				strcat( lDialogString , "-e 'end repeat' " );+				strcat( lDialogString , "-e 'mystring' ");+				strcat(lDialogString, "-e 'on error number -128' " ) ;+				strcat(lDialogString, "-e 'end try'") ;+				if ( ! osx9orBetter() ) strcat( lDialogString, " -e 'end tell'") ;+		}+		else if ( tfd_kdialogPresent() )+		{+				if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return (char *)1;}+				strcpy( lDialogString , "kdialog" ) ;+								if ( (tfd_kdialogPresent() == 2) && tfd_xpropPresent() )+				{+						strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */+				}+				sprintf( lDialogString + strlen(lDialogString) , " --getcolor --default '%s'" , lDefaultHexRGB ) ;++				if ( aTitle && strlen(aTitle) )+				{+						strcat(lDialogString, " --title \"") ;+						strcat(lDialogString, aTitle) ;+						strcat(lDialogString, "\"") ;+				}+		}+		else if ( (tfd_zenityVersion()>=2032) || tfd_matedialogPresent() || tfd_shellementaryPresent()+				|| tfd_qarmaPresent() || tfd_shantyPresent() || tfd_boxerPresent() )+		{+				lWasZenity2032 = 1 ;+				if ( tfd_zenityVersion()>=2032 )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity3");return (char *)1;}+						strcpy( lDialogString , "zenity" );+						if ( (tfd_zenityVersion() >=3010) && !getenv("SSH_TTY") && tfd_xpropPresent() )+						{+								strcat( lDialogString, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */+						}+				}+				else if ( tfd_matedialogPresent() )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return (char *)1;}+						strcpy( lDialogString , "matedialog" ) ;+				}+				else if ( tfd_shellementaryPresent() )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"shellementary");return (char *)1;}+						strcpy( lDialogString , "shellementary" ) ;+				}+				else if ( tfd_qarmaPresent() )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"qarma");return (char *)1;}+						strcpy( lDialogString , "qarma" ) ;+												if ( !getenv("SSH_TTY") && tfd_xpropPresent() )+						{+								strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */+						}+				}+				else if ( tfd_boxerPresent() )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"boxer");return (char *)1;}+						strcpy( lDialogString , "boxer" ) ;+				}+				else if ( tfd_shantyPresent() )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"shanty");return (char *)1;}+						strcpy( lDialogString , "shanty" ) ;+				}+				else {}+				+				strcat( lDialogString , " --color-selection" ) ;+				if ( tfd_zenityVersion()>=2032 || tfd_matedialogPresent() || tfd_shellementaryPresent() || tfd_qarmaPresent() || tfd_boxerPresent() )+				{+					strcat( lDialogString , " --show-palette" ) ;+				}+				sprintf( lDialogString + strlen(lDialogString), " --color=%s" , lDefaultHexRGB ) ;++				strcat(lDialogString, " --title=\"") ;+				if (aTitle && strlen(aTitle)) strcat(lDialogString, aTitle) ;+				strcat(lDialogString, "\"") ;++				if (tinyfd_silent) strcat( lDialogString , " 2>/dev/null ");+		}+		else if (tfd_yadPresent())+		{+		   if (aTitle && !strcmp(aTitle, "tinyfd_query")) { strcpy(tinyfd_response, "yad"); return (char*)1; }+		   strcpy(lDialogString, "yad --color");+		   sprintf(lDialogString + strlen(lDialogString), " --init-color=%s", lDefaultHexRGB);+		   if (aTitle && strlen(aTitle))+		   {+			  strcat(lDialogString, " --title=\"");+			  strcat(lDialogString, aTitle);+			  strcat(lDialogString, "\"");+		   }+		   if (tinyfd_silent) strcat(lDialogString, " 2>/dev/null ");+		}+		else if ( xdialogPresent() )+		{+				if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xdialog");return (char *)1;}+				lWasXdialog = 1 ;+				strcpy( lDialogString , "Xdialog --colorsel \"" ) ;+				if ( aTitle && strlen(aTitle) )+				{+						strcat(lDialogString, aTitle) ;+				}+				strcat(lDialogString, "\" 0 60 ") ;+#if (defined(__cplusplus ) && __cplusplus >= 201103L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__clang__)+								sprintf(lTmp,"%hhu %hhu %hhu",lDefaultRGB[0],lDefaultRGB[1],lDefaultRGB[2]);+#else+				sprintf(lTmp,"%hu %hu %hu",lDefaultRGB[0],lDefaultRGB[1],lDefaultRGB[2]);+#endif+				strcat(lDialogString, lTmp) ;+				strcat(lDialogString, " 2>&1");+		}+				else if ( tkinter3Present( ) )+				{+						if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python3-tkinter");return (char *)1;}+						strcpy( lDialogString , gPython3Name ) ;+						strcat( lDialogString ,+								" -S -c \"import tkinter;from tkinter import colorchooser;root=tkinter.Tk();root.withdraw();");+						strcat( lDialogString , "res=colorchooser.askcolor(color='" ) ;+						strcat(lDialogString, lDefaultHexRGB ) ;+						strcat(lDialogString, "'") ;++						if ( aTitle && strlen(aTitle) )+						{+								strcat(lDialogString, ",title='") ;+								strcat(lDialogString, aTitle) ;+								strcat(lDialogString, "'") ;+						}+						strcat( lDialogString , ");\+\nif res[1] is not None:\n\tprint(res[1])\"" ) ;+				}+				else if ( tkinter2Present( ) )+				{+				if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python2-tkinter");return (char *)1;}+								strcpy( lDialogString , "export PYTHONIOENCODING=utf-8;" ) ;+								strcat( lDialogString , gPython2Name ) ;+								if ( ! isTerminalRunning( ) && tfd_isDarwin( ) )+				{+				strcat( lDialogString , " -i" ) ;  /* for osx without console */+				}++				strcat( lDialogString ,+" -S -c \"import Tkinter,tkColorChooser;root=Tkinter.Tk();root.withdraw();");++				if ( tfd_isDarwin( ) )+				{+						strcat( lDialogString ,+"import os;os.system('''osascript -e 'tell app \\\"Finder\\\" to set \+frontmost of process \\\"Python\\\" to true' ''');");+				}++				strcat( lDialogString , "res=tkColorChooser.askcolor(color='" ) ;+				strcat(lDialogString, lDefaultHexRGB ) ;+				strcat(lDialogString, "'") ;+++			if ( aTitle && strlen(aTitle) )+			{+						strcat(lDialogString, ",title='") ;+						strcat(lDialogString, aTitle) ;+						strcat(lDialogString, "'") ;+			}+				strcat( lDialogString , ");\+\nif res[1] is not None:\n\tprint res[1]\"" ) ;+		}+		else+		{+				if (aTitle&&!strcmp(aTitle,"tinyfd_query")){return tinyfd_inputBox(aTitle,NULL,NULL);}+								lPointerInputBox = tinyfd_inputBox(NULL, NULL, NULL); /* obtain a pointer on the current content of tinyfd_inputBox */+								if (lPointerInputBox) strcpy(lDialogString, lPointerInputBox); /* preserve the current content of tinyfd_inputBox */+								p = tinyfd_inputBox(aTitle, "Enter hex rgb color (i.e. #f5ca20)", lDefaultHexRGB);++				if ( !p || (strlen(p) != 7) || (p[0] != '#') )+				{+						return NULL ;+				}+				for ( i = 1 ; i < 7 ; i ++ )+				{+						if ( ! isxdigit( (int) p[i] ) )+						{+								return NULL ;+						}+				}+								Hex2RGB(p,aoResultRGB);+								strcpy(lDefaultHexRGB, p);+								if (lPointerInputBox) strcpy(lPointerInputBox, lDialogString); /* restore its previous content to tinyfd_inputBox */+								return lDefaultHexRGB;+		}++		if (tinyfd_verbose) printf( "lDialogString: %s\n" , lDialogString ) ;+		lIn = ( FILE * ) popen( lDialogString , "r" ) ;+		if ( ! lIn )+		{+				return NULL ;+	}+		while ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL )+		{+		}+		pclose( lIn ) ;+	if ( ! strlen( lBuff ) )+	{+		return NULL ;+	}+		/* printf( "len Buff: %lu\n" , strlen(lBuff) ) ; */+		/* printf( "lBuff0: %s\n" , lBuff ) ; */+	if ( lBuff[strlen( lBuff ) -1] == '\n' )+	{+		lBuff[strlen( lBuff ) -1] = '\0' ;+	}++	if ( lWasZenity2032 )+	{+				if ( lBuff[0] == '#' )+				{+						if ( strlen(lBuff)>7 )+						{+								lBuff[3]=lBuff[5];+								lBuff[4]=lBuff[6];+								lBuff[5]=lBuff[9];+								lBuff[6]=lBuff[10];+								lBuff[7]='\0';+						}+				Hex2RGB(lBuff,aoResultRGB);+				}+				else if ( lBuff[3] == '(' ) {+#if (defined(__cplusplus ) && __cplusplus >= 201103L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__clang__)+	sscanf(lBuff,"rgb(%hhu,%hhu,%hhu", & aoResultRGB[0], & aoResultRGB[1],& aoResultRGB[2]);+#else+	aoResultRGB[0] = (unsigned char) strtol(lBuff+4, & lTmp2, 10 );+	aoResultRGB[1] = (unsigned char) strtol(lTmp2+1, & lTmp2, 10 );+	aoResultRGB[2] = (unsigned char) strtol(lTmp2+1, NULL, 10 );+#endif+	RGB2Hex(aoResultRGB,lBuff);+				}+				else if ( lBuff[4] == '(' ) {+#if (defined(__cplusplus ) && __cplusplus >= 201103L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__clang__)+	sscanf(lBuff,"rgba(%hhu,%hhu,%hhu",  & aoResultRGB[0], & aoResultRGB[1],& aoResultRGB[2]);+#else+	aoResultRGB[0] = (unsigned char) strtol(lBuff+5, & lTmp2, 10 );+	aoResultRGB[1] = (unsigned char) strtol(lTmp2+1, & lTmp2, 10 );+	aoResultRGB[2] = (unsigned char) strtol(lTmp2+1, NULL, 10 );+#endif+	RGB2Hex(aoResultRGB,lBuff);+				}+	}+	else if ( lWasOsascript || lWasXdialog )+	{+				/* printf( "lBuff: %s\n" , lBuff ) ; */+#if (defined(__cplusplus ) && __cplusplus >= 201103L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__clang__)+	sscanf(lBuff,"%hhu %hhu %hhu", & aoResultRGB[0], & aoResultRGB[1],& aoResultRGB[2]);+#else+	aoResultRGB[0] = (unsigned char) strtol(lBuff, & lTmp2, 10 );+	aoResultRGB[1] = (unsigned char) strtol(lTmp2+1, & lTmp2, 10 );+	aoResultRGB[2] = (unsigned char) strtol(lTmp2+1, NULL, 10 );+#endif+	RGB2Hex(aoResultRGB,lBuff);+	}+	else+	{+				Hex2RGB(lBuff,aoResultRGB);+	}+	/* printf("%d %d %d\n", aoResultRGB[0],aoResultRGB[1],aoResultRGB[2]); */+	/* printf( "lBuff: %s\n" , lBuff ) ; */++		strcpy(lDefaultHexRGB,lBuff);+		return lDefaultHexRGB ;+}++#endif /* _WIN32 */+++/* Modified prototypes for R */++void tfd_messageBox(+	char const * aTitle ,+	char const * aMessage ,+	char const * aDialogType ,+	char const * aIconType ,+	int * aiDefaultButton )+{+	* aiDefaultButton = tinyfd_messageBox( aTitle , aMessage , aDialogType , aIconType , * aiDefaultButton ) ;+}+++void tfd_inputBox(+	char const * aTitle ,+	char const * aMessage ,+	char * * aiDefaultInput )+{+	char * lReturnedInput ;+	if ( ! strcmp( * aiDefaultInput , "NULL") )  lReturnedInput = tinyfd_inputBox( aTitle , aMessage , NULL ) ;+	else lReturnedInput = tinyfd_inputBox( aTitle , aMessage , * aiDefaultInput ) ;++	if ( lReturnedInput ) strcpy ( * aiDefaultInput , lReturnedInput ) ;+	else strcpy ( * aiDefaultInput , "NULL" ) ;+}+++void tfd_saveFileDialog(+	char const * aTitle ,+	char * * aiDefaultPathAndFile ,+	int const * aNumOfFilterPatterns ,+	char const * const * aFilterPatterns ,+	char const * aSingleFilterDescription )+{+	char * lSavefile ;++	/* printf( "aFilterPatterns %s\n" , aFilterPatterns [0]); */++	lSavefile = tinyfd_saveFileDialog( aTitle , * aiDefaultPathAndFile , * aNumOfFilterPatterns ,+										aFilterPatterns, aSingleFilterDescription ) ;+	if ( lSavefile ) strcpy ( * aiDefaultPathAndFile , lSavefile ) ;+	else strcpy ( * aiDefaultPathAndFile , "NULL" ) ;+}+++void tfd_openFileDialog(+	char const * aTitle ,+	char * * aiDefaultPathAndFile ,+	int const * aNumOfFilterPatterns ,+	char const * const * aFilterPatterns ,+	char const * aSingleFilterDescription ,+	int const * aAllowMultipleSelects )+{+	char * lOpenfile ;++	/* printf( "aFilterPatterns %s\n" , aFilterPatterns [0]); */++	lOpenfile = tinyfd_openFileDialog( aTitle , * aiDefaultPathAndFile , * aNumOfFilterPatterns ,+									aFilterPatterns , aSingleFilterDescription , * aAllowMultipleSelects ) ;++	if ( lOpenfile ) strcpy ( * aiDefaultPathAndFile , lOpenfile ) ;+	else strcpy ( * aiDefaultPathAndFile , "NULL" ) ;+}+++void tfd_selectFolderDialog(+	char const * aTitle ,+	char * * aiDefaultPath )+{+	char * lSelectedfolder ;+	lSelectedfolder = tinyfd_selectFolderDialog( aTitle, * aiDefaultPath ) ;+	if ( lSelectedfolder ) strcpy ( * aiDefaultPath , lSelectedfolder ) ;+	else strcpy ( * aiDefaultPath , "NULL" ) ;+}+++void tfd_colorChooser(+	char const * aTitle ,+	char * * aiDefaultHexRGB )+{+	unsigned char const aDefaultRGB [ 3 ] = {128,128,128} ;+	unsigned char aoResultRGB [ 3 ] =  {128,128,128} ;+	char * lChosenColor ;+	lChosenColor = tinyfd_colorChooser( aTitle, * aiDefaultHexRGB, aDefaultRGB, aoResultRGB ) ;+	if ( lChosenColor ) strcpy ( * aiDefaultHexRGB , lChosenColor ) ;+	else strcpy ( * aiDefaultHexRGB , "NULL" ) ;+}++/* end of Modified prototypes for R */++++/*+int main( int argc , char * argv[] )+{+char const * lTmp;+char const * lTheSaveFileName;+char const * lTheOpenFileName;+char const * lTheSelectFolderName;+char const * lTheHexColor;+char const * lWillBeGraphicMode;+unsigned char lRgbColor[3];+FILE * lIn;+char lBuffer[1024];+char lString[1024];+char const * lFilterPatterns[2] = { "*.txt", "*.text" };++tinyfd_verbose = argc - 1;+tinyfd_silent = 1;++lWillBeGraphicMode = tinyfd_inputBox("tinyfd_query", NULL, NULL);++strcpy(lBuffer, "v");+strcat(lBuffer, tinyfd_version);+if (lWillBeGraphicMode)+{+	strcat(lBuffer, "\ngraphic mode: ");+}+else+{+	strcat(lBuffer, "\nconsole mode: ");+}+strcat(lBuffer, tinyfd_response);+strcat(lBuffer, "\n");+strcat(lBuffer, tinyfd_needs+78);+strcpy(lString, "tinyfiledialogs");+tinyfd_messageBox(lString, lBuffer, "ok", "info", 0);++tinyfd_notifyPopup("the title", "the message\n\tfrom outer-space", "info");++if (lWillBeGraphicMode && !tinyfd_forceConsole)+{+		tinyfd_forceConsole = ! tinyfd_messageBox("Hello World",+				"graphic dialogs [yes] / console mode [no]?",+				"yesno", "question", 1);+}++lTmp = tinyfd_inputBox(+		"a password box", "your password will be revealed", NULL);++if (!lTmp) return 1;++strcpy(lString, lTmp);++lTheSaveFileName = tinyfd_saveFileDialog(+		"let us save this password",+		"passwordFile.txt",+		2,+		lFilterPatterns,+		NULL);++if (!lTheSaveFileName)+{+		tinyfd_messageBox(+				"Error",+				"Save file name is NULL",+				"ok",+				"error",+				1);+		return 1;+}++lIn = fopen(lTheSaveFileName, "w");+if (!lIn)+{+		tinyfd_messageBox(+				"Error",+				"Can not open this file in write mode",+				"ok",+				"error",+				1);+		return 1;+}+fputs(lString, lIn);+fclose(lIn);++lTheOpenFileName = tinyfd_openFileDialog(+		"let us read the password back",+		"",+		2,+		lFilterPatterns,+		NULL,+		0);++if (!lTheOpenFileName)+{+		tinyfd_messageBox(+				"Error",+				"Open file name is NULL",+				"ok",+				"error",+				1);+		return 1;+}++lIn = fopen(lTheOpenFileName, "r");++if (!lIn)+{+		tinyfd_messageBox(+				"Error",+				"Can not open this file in read mode",+				"ok",+				"error",+				1);+		return(1);+}+lBuffer[0] = '\0';+fgets(lBuffer, sizeof(lBuffer), lIn);+fclose(lIn);++tinyfd_messageBox("your password is",+		lBuffer, "ok", "info", 1);++lTheSelectFolderName = tinyfd_selectFolderDialog(+		"let us just select a directory", NULL);++if (!lTheSelectFolderName)+{+		tinyfd_messageBox(+				"Error",+				"Select folder name is NULL",+				"ok",+				"error",+				1);+		return 1;+}++tinyfd_messageBox("The selected folder is",+		lTheSelectFolderName, "ok", "info", 1);++lTheHexColor = tinyfd_colorChooser(+		"choose a nice color",+		"#FF0077",+		lRgbColor,+		lRgbColor);++if (!lTheHexColor)+{+		tinyfd_messageBox(+				"Error",+				"hexcolor is NULL",+				"ok",+				"error",+				1);+		return 1;+}++tinyfd_messageBox("The selected hexcolor is",+		lTheHexColor, "ok", "info", 1);++		tinyfd_beep();++		return 0;+}+*/++#ifdef _MSC_VER+#pragma warning(default:4996)+#pragma warning(default:4100)+#pragma warning(default:4706)+#endif
+ cbits/tinyfiledialogs.h view
@@ -0,0 +1,315 @@+/* SPDX-License-Identifier: Zlib+Copyright (c) 2014 - 2025 Guillaume Vareille http://ysengrin.com+	 ____________________________________________________________________+	|                                                                    |+	| 100% compatible C C++  ->  You can rename tinfiledialogs.c as .cpp |+	|____________________________________________________________________|++********* TINY FILE DIALOGS OFFICIAL WEBSITE IS ON SOURCEFORGE *********+  _________+ /         \ tinyfiledialogs.h v3.21.2 [Oct 25, 2025]+ |tiny file| Unique header file created [November 9, 2014]+ | dialogs |+ \____  ___/ http://tinyfiledialogs.sourceforge.net+      \|     git clone http://git.code.sf.net/p/tinyfiledialogs/code tinyfd+ ____________________________________________+|                                            |+|   email: tinyfiledialogs at ysengrin.com   |+|____________________________________________|+ ________________________________________________________________________________+|  ____________________________________________________________________________  |+| |                                                                            | |+| |  - in tinyfiledialogs, char is UTF-8 by default (since v3.6)               | |+| |                                                                            | |+| | on windows:                                                                | |+| |  - for UTF-16, use the wchar_t functions at the bottom of the header file  | |+| |                                                                            | |+| |  - _wfopen() requires wchar_t                                              | |+| |  - fopen() uses char but expects ASCII or MBCS (not UTF-8)                 | |+| |  - if you want char to be MBCS: set tinyfd_winUtf8 to 0                    | |+| |                                                                            | |+| |  - alternatively, tinyfiledialogs provides                                 | |+| |                        functions to convert between UTF-8, UTF-16 and MBCS | |+| |____________________________________________________________________________| |+|________________________________________________________________________________|++If you like tinyfiledialogs, please upvote my stackoverflow answer+https://stackoverflow.com/a/47651444++- License -+This software is provided 'as-is', without any express or implied+warranty.  In no event will the authors be held liable for any damages+arising from the use of this software.+Permission is granted to anyone to use this software for any purpose,+including commercial applications, and to alter it and redistribute it+freely, subject to the following restrictions:+1. The origin of this software must not be misrepresented; you must not+claim that you wrote the original software.  If you use this software+in a product, an acknowledgment in the product documentation would be+appreciated but is not required.+2. Altered source versions must be plainly marked as such, and must not be+misrepresented as being the original software.+3. This notice may not be removed or altered from any source distribution.++     __________________________________________+    |  ______________________________________  |+    | |                                      | |+    | | DO NOT USE USER INPUT IN THE DIALOGS | |+    | |______________________________________| |+    |__________________________________________|+*/++#ifndef TINYFILEDIALOGS_H+#define TINYFILEDIALOGS_H++#ifdef	__cplusplus+extern "C" {+#endif++/******************************************************************************************************/+/**************************************** UTF-8 on Windows ********************************************/+/******************************************************************************************************/+#ifdef _WIN32+#include <stddef.h> /* for wchar_t */+/* On windows, if you want to use UTF-8 ( instead of the UTF-16/wchar_t functions at the end of this file )+Make sure your code is really prepared for UTF-8 (on windows, functions like fopen() expect MBCS and not UTF-8) */+extern int tinyfd_winUtf8; /* on windows char strings can be 1:UTF-8(default) or 0:MBCS */+/* for MBCS change this to 0, in tinyfiledialogs.c or in your code */++/* Here are some functions to help you convert between UTF-16 UTF-8 MBSC */+char * tinyfd_utf8toMbcs(const char * aUtf8string);+char * tinyfd_utf16toMbcs(const wchar_t * aUtf16string);+wchar_t * tinyfd_mbcsTo16(const char * aMbcsString);+char * tinyfd_mbcsTo8(const char * aMbcsString);+wchar_t * tinyfd_utf8to16(const char * aUtf8string);+char * tinyfd_utf16to8(const wchar_t * aUtf16string);+#endif+/******************************************************************************************************/+/******************************************************************************************************/+/******************************************************************************************************/++/************* 3 funtions for C# (you don't need this in C or C++) : */+const char * tinyfd_getGlobalChar(const char * aCharVariableName); /* returns NULL on error */+int tinyfd_getGlobalInt(const char * aIntVariableName); /* returns -1 on error */+int tinyfd_setGlobalInt(const char * aIntVariableName, int aValue); /* returns -1 on error */+/* aCharVariableName: "tinyfd_version" "tinyfd_needs" "tinyfd_response"+   aIntVariableName : "tinyfd_verbose" "tinyfd_silent" "tinyfd_allowCursesDialogs"+				      "tinyfd_forceConsole" "tinyfd_assumeGraphicDisplay" "tinyfd_winUtf8"+**************/++extern char tinyfd_version[8]; /* contains tinyfd current version number */+extern char tinyfd_needs[]; /* info about requirements */+extern int tinyfd_verbose; /* 0 (default) or 1 : on unix, prints the command line calls */+extern int tinyfd_silent; /* 1 (default) or 0 : on unix, hide errors and warnings from called dialogs */++/** Curses dialogs are difficult to use and counter-intuitive.+On windows they are only ascii and still uses the unix backslash ! **/+extern int tinyfd_allowCursesDialogs; /* 0 (default) or 1 */++extern int tinyfd_forceConsole;  /* 0 (default) or 1 */+/* for unix & windows: 0 (graphic mode) or 1 (console mode).+0: try to use a graphic solution, if it fails then it uses console mode.+1: forces all dialogs into console mode even when an X server is present.+   if enabled, it can use the package Dialog or dialog.exe.+   on windows it only make sense for console applications */++/* extern int tinyfd_assumeGraphicDisplay; */ /* 0 (default) or 1  */+/* some systems don't set the environment variable DISPLAY even when a graphic display is present.+set this to 1 to tell tinyfiledialogs to assume the existence of a graphic display */++extern char tinyfd_response[1024];+/* if you pass "tinyfd_query" as aTitle,+the functions will not display the dialogs+but will return 0 for console mode, 1 for graphic mode.+tinyfd_response is then filled with the retain solution.+possible values for tinyfd_response are (all lowercase)+for graphic mode:+  windows_wchar windows applescript kdialog zenity zenity3 yad matedialog+  shellementary qarma shanty boxer python2-tkinter python3-tkinter python-dbus+  perl-dbus gxmessage gmessage xmessage xdialog gdialog dunst+for console mode:+  dialog whiptail basicinput no_solution */++void tinyfd_beep(void);++int tinyfd_notifyPopup(+	const char * aTitle, /* NULL or "" */+	const char * aMessage, /* NULL or "" may contain \n \t */+	const char * aIconType); /* "info" "warning" "error" */+		/* return has only meaning for tinyfd_query */++int tinyfd_messageBox(+	const char * aTitle , /* NULL or "" */+	const char * aMessage , /* NULL or "" may contain \n \t */+	const char * aDialogType , /* "ok" "okcancel" "yesno" "yesnocancel" */+	const char * aIconType , /* "info" "warning" "error" "question" */+	int aDefaultButton ) ;+		/* 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel */++char * tinyfd_inputBox(+	const char * aTitle , /* NULL or "" */+	const char * aMessage , /* NULL or "" (\n and \t have no effect) */+	const char * aDefaultInput ) ;  /* NULL = passwordBox, "" = inputbox */+		/* returns NULL on cancel */++char * tinyfd_saveFileDialog(+	const char * aTitle , /* NULL or "" */+	const char * aDefaultPathAndOrFile , /* NULL or "" , ends with / to set only a directory */+	int aNumOfFilterPatterns , /* 0  (1 in the following example) */+	const char * const * aFilterPatterns , /* NULL or const char * lFilterPatterns[1]={"*.txt"} */+	const char * aSingleFilterDescription ) ; /* NULL or "text files" */+		/* returns NULL on cancel */++char * tinyfd_openFileDialog(+	const char * aTitle, /* NULL or "" */+	const char * aDefaultPathAndOrFile, /* NULL or "" , ends with / to set only a directory */+	int aNumOfFilterPatterns , /* 0 (2 in the following example) */+	const char * const * aFilterPatterns, /* NULL or const char * lFilterPatterns[2]={"*.png","*.jpg"}; */+	const char * aSingleFilterDescription, /* NULL or "image files" */+	int aAllowMultipleSelects ) ; /* 0 or 1 */+		/* in case of multiple files, the separator is | */+		/* returns NULL on cancel */++char * tinyfd_selectFolderDialog(+	const char * aTitle, /* NULL or "" */+	const char * aDefaultPath); /* NULL or "" */+		/* returns NULL on cancel */++char * tinyfd_colorChooser(+	const char * aTitle, /* NULL or "" */+	const char * aDefaultHexRGB, /* NULL or "" or "#FF0000" */+	const unsigned char aDefaultRGB[3] , /* unsigned char lDefaultRGB[3] = { 0 , 128 , 255 }; */+	unsigned char aoResultRGB[3] ) ; /* unsigned char lResultRGB[3]; */+		/* aDefaultRGB is used only if aDefaultHexRGB is absent */+		/* aDefaultRGB and aoResultRGB can be the same array */+		/* returns NULL on cancel */+		/* returns the hexcolor as a string "#FF0000" */+		/* aoResultRGB also contains the result */+++/************ WINDOWS ONLY SECTION ************************/+#ifdef _WIN32++/* windows only - utf-16 version */+int tinyfd_notifyPopupW(+	const wchar_t * aTitle, /* NULL or L"" */+	const wchar_t * aMessage, /* NULL or L"" may contain \n \t */+	const wchar_t * aIconType); /* L"info" L"warning" L"error" */++/* windows only - utf-16 version */+int tinyfd_messageBoxW(+	const wchar_t * aTitle, /* NULL or L"" */+	const wchar_t * aMessage, /* NULL or L"" may contain \n \t */+	const wchar_t * aDialogType, /* L"ok" L"okcancel" L"yesno" */+	const wchar_t * aIconType, /* L"info" L"warning" L"error" L"question" */+	int aDefaultButton ); /* 0 for cancel/no , 1 for ok/yes */+		/* returns 0 for cancel/no , 1 for ok/yes */++/* windows only - utf-16 version */+wchar_t * tinyfd_inputBoxW(+	const wchar_t * aTitle, /* NULL or L"" */+	const wchar_t * aMessage, /* NULL or L"" (\n nor \t not respected) */+	const wchar_t * aDefaultInput); /* NULL passwordBox, L"" inputbox */++/* windows only - utf-16 version */+wchar_t * tinyfd_saveFileDialogW(+	const wchar_t * aTitle, /* NULL or L"" */+	const wchar_t * aDefaultPathAndOrFile, /* NULL or L"" , ends with / to set only a directory */+	int aNumOfFilterPatterns, /* 0 (1 in the following example) */+	const wchar_t * const * aFilterPatterns, /* NULL or const wchar_t * lFilterPatterns[1]={L"*.txt"} */+	const wchar_t * aSingleFilterDescription); /* NULL or L"text files" */+		/* returns NULL on cancel */++/* windows only - utf-16 version */+wchar_t * tinyfd_openFileDialogW(+	const wchar_t * aTitle, /* NULL or L"" */+	const wchar_t * aDefaultPathAndOrFile, /* NULL or L"" , ends with / to set only a directory */+	int aNumOfFilterPatterns , /* 0 (2 in the following example) */+	const wchar_t * const * aFilterPatterns, /* NULL or const wchar_t * lFilterPatterns[2]={L"*.png","*.jpg"} */+	const wchar_t * aSingleFilterDescription, /* NULL or L"image files" */+	int aAllowMultipleSelects ) ; /* 0 or 1 */+		/* in case of multiple files, the separator is | */+		/* returns NULL on cancel */++/* windows only - utf-16 version */+wchar_t * tinyfd_selectFolderDialogW(+	const wchar_t * aTitle, /* NULL or L"" */+	const wchar_t * aDefaultPath); /* NULL or L"" */+		/* returns NULL on cancel */++/* windows only - utf-16 version */+wchar_t * tinyfd_colorChooserW(+	const wchar_t * aTitle, /* NULL or L"" */+	const wchar_t * aDefaultHexRGB, /* NULL or L"#FF0000" */+	const unsigned char aDefaultRGB[3], /* unsigned char lDefaultRGB[3] = { 0 , 128 , 255 }; */+	unsigned char aoResultRGB[3]); /* unsigned char lResultRGB[3]; */+		/* returns the hexcolor as a string L"#FF0000" */+		/* aoResultRGB also contains the result */+		/* aDefaultRGB is used only if aDefaultHexRGB is NULL */+		/* aDefaultRGB and aoResultRGB can be the same array */+		/* returns NULL on cancel */++#endif /*_WIN32 */++#ifdef	__cplusplus+} /*extern "C"*/+#endif++#endif /* TINYFILEDIALOGS_H */++/*+ ________________________________________________________________________________+|  ____________________________________________________________________________  |+| |                                                                            | |+| | on windows:                                                                | |+| |  - for UTF-16, use the wchar_t functions at the bottom of the header file  | |+| |  - _wfopen() requires wchar_t                                              | |+| |                                                                            | |+| |  - in tinyfiledialogs, char is UTF-8 by default (since v3.6)               | |+| |  - but fopen() expects MBCS (not UTF-8)                                    | |+| |  - if you want char to be MBCS: set tinyfd_winUtf8 to 0                    | |+| |                                                                            | |+| |  - alternatively, tinyfiledialogs provides                                 | |+| |                        functions to convert between UTF-8, UTF-16 and MBCS | |+| |____________________________________________________________________________| |+|________________________________________________________________________________|++- This is not for ios nor android (it works in termux though).+- The files can be renamed with extension ".cpp" as the code is 100% compatible C C+++  (just comment out << extern "C" >> in the header file)+- Windows is fully supported from XP to 10 (maybe even older versions)+- C# & LUA via dll, see files in the folder EXTRAS+- OSX supported from 10.4 to latest (maybe even older versions)+- Do not use " and ' as the dialogs will be displayed with a warning+  instead of the title, message, etc...+- There's one file filter only, it may contain several patterns.+- If no filter description is provided,+  the list of patterns will become the description.+- On windows link against Comdlg32.lib and Ole32.lib+  (on windows the no linking claim is a lie)+- On unix: it tries command line calls, so no such need (NO LINKING).+- On unix you need one of the following:+  applescript, kdialog, zenity, matedialog, shellementary, qarma, shanty, boxer,+  yad, python (2 or 3)/tkinter/python-dbus (optional), Xdialog+  or curses dialogs (opens terminal if running without console).+- One of those is already included on most (if not all) desktops.+- In the absence of those it will use gdialog, gxmessage or whiptail+  with a textinputbox. If nothing is found, it switches to basic console input,+  it opens a console if needed (requires xterm + bash).+- for curses dialogs you must set tinyfd_allowCursesDialogs=1+- You can query the type of dialog that will be used (pass "tinyfd_query" as aTitle)+- String memory is preallocated statically for all the returned values.+- File and path names are tested before return, they should be valid.+- tinyfd_forceConsole=1; at run time, forces dialogs into console mode.+- On windows, console mode only make sense for console applications.+- On windows, console mode is not implemented for wchar_T UTF-16.+- Mutiple selects are not possible in console mode.+- The package dialog must be installed to run in curses dialogs in console mode.+  It is already installed on most unix systems.+- On osx, the package dialog can be installed via+  http://macappstore.org/dialog or http://macports.org+- On windows, for curses dialogs console mode,+  dialog.exe should be copied somewhere on your executable path.+  It can be found at the bottom of the following page:+  http://andrear.altervista.org/home/cdialog.php+*/