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 +1/−2
- Brillo/Data/Color.hs +11/−11
- Brillo/Data/Controller.hs +3/−0
- Brillo/Data/Cursor.hs +21/−0
- Brillo/Data/Display.hs +3/−1
- Brillo/Data/FileDialog.hs +33/−0
- Brillo/Data/Picture.hs +81/−123
- Brillo/Data/Point.hs +5/−5
- Brillo/Data/ViewState.hs +38/−33
- Brillo/Geometry.hs +9/−0
- Brillo/Geometry/Line.hs +99/−99
- Brillo/Interface/Environment.hs +38/−0
- Brillo/Interface/IO/Animate.hs +24/−22
- Brillo/Interface/IO/Display.hs +12/−10
- Brillo/Interface/IO/Game.hs +19/−18
- Brillo/Interface/IO/Interact.hs +16/−14
- Brillo/Interface/IO/Simulate.hs +18/−17
- Brillo/Interface/Pure/Animate.hs +10/−9
- Brillo/Interface/Pure/Display.hs +8/−8
- Brillo/Interface/Pure/Game.hs +18/−17
- Brillo/Interface/Pure/Simulate.hs +17/−16
- Brillo/Internals/Interface/Animate.hs +22/−18
- Brillo/Internals/Interface/Animate/State.hs +3/−2
- Brillo/Internals/Interface/Backend/GLFW.hs +389/−155
- Brillo/Internals/Interface/Backend/Types.hs +47/−12
- Brillo/Internals/Interface/Callback.hs +1/−0
- Brillo/Internals/Interface/Display.hs +16/−13
- Brillo/Internals/Interface/Event.hs +45/−21
- Brillo/Internals/Interface/Game.hs +79/−54
- Brillo/Internals/Interface/Interact.hs +73/−47
- Brillo/Internals/Interface/Simulate.hs +21/−20
- Brillo/Internals/Interface/Simulate/Idle.hs +26/−24
- Brillo/Internals/Interface/ViewState/KeyMouse.hs +3/−3
- Brillo/Internals/Interface/ViewState/Motion.hs +3/−3
- Brillo/Internals/Interface/Window.hs +17/−15
- Brillo/Internals/TinyFileDialogs.chs +224/−0
- brillo.cabal +40/−23
- cbits/tinyfiledialogs.c +8401/−0
- cbits/tinyfiledialogs.h +315/−0
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+*/