From b95ea4b2a273eee3c796db75f8ba6f98d9cd9030 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 14 Sep 2016 23:37:52 +0200 Subject: [PATCH] rFXGen 1.0 -IN PROGRESS- Almost ready... --- tools/rFXGen/SOON.txt | 0 tools/rFXGen/external/tinyfiledialogs.c | 5619 +++++++++++++++++++++++ tools/rFXGen/external/tinyfiledialogs.h | 289 ++ tools/rFXGen/rfxgen.c | 1595 +++++++ tools/rFXGen/twitter.bmp | Bin 0 -> 822 bytes tools/rFXGen/twitter.h | 1 + 6 files changed, 7504 insertions(+) delete mode 100644 tools/rFXGen/SOON.txt create mode 100644 tools/rFXGen/external/tinyfiledialogs.c create mode 100644 tools/rFXGen/external/tinyfiledialogs.h create mode 100644 tools/rFXGen/rfxgen.c create mode 100644 tools/rFXGen/twitter.bmp create mode 100644 tools/rFXGen/twitter.h diff --git a/tools/rFXGen/SOON.txt b/tools/rFXGen/SOON.txt deleted file mode 100644 index e69de29..0000000 diff --git a/tools/rFXGen/external/tinyfiledialogs.c b/tools/rFXGen/external/tinyfiledialogs.c new file mode 100644 index 0000000..9d26186 --- /dev/null +++ b/tools/rFXGen/external/tinyfiledialogs.c @@ -0,0 +1,5619 @@ +/* + _________ +/ \ tinyfiledialogs.c v2.5.8 [September 13, 2016] zlib licence +|tiny file| Unique code file of "tiny file dialogs" created [November 9, 2014] +| dialogs | Copyright (c) 2014 - 2016 Guillaume Vareille http://ysengrin.com +\____ ___/ http://tinyfiledialogs.sourceforge.net + \| mailto:tinyfiledialogs@ysengrin.com + +A big thank you to Don Heyse http://ldglite.sf.net for + his code contributions, bug corrections & thorough testing! + + git://git.code.sf.net/p/tinyfiledialogs/code + +Please + 1) let me know + - if you are including tiny file dialogs, + I'll be happy to add your link to the list of projects using it. + - If you are using it on different hardware / OS / compiler. + 2) Be the first to leave a review on Sourceforge. Thanks. + +tiny file dialogs (cross-platform C C++) +InputBox PasswordBox MessageBox ColorPicker +OpenFileDialog SaveFileDialog SelectFolderDialog +Native dialog library for WINDOWS MAC OSX GTK+ QT CONSOLE & more + +A single C file (add it to your C or C++ project) with 6 boxes: +- message / question +- input / password +- save file +- open file & multiple files +- select folder +- color picker. + +Complements OpenGL GLFW GLUT GLUI VTK SFML SDL Ogre Unity ION +CEGUI MathGL CPW GLOW IMGUI GLT NGL STB & GUI less programs + +NO INIT +NO MAIN LOOP + +The dialogs can be forced into console mode + +Windows [MBCS + UTF-8 + UTF-16] +- native code & some vbs create the graphic dialogs +- enhanced console mode can use dialog.exe from +http://andrear.altervista.org/home/cdialog.php +- basic console input + +Unix [UTF-8] (command line call attempts) +- applescript +- zenity / matedialog +- kdialog +- Xdialog +- python2 tkinter +- dialog (opens a console if needed) +- basic console input +The same executable can run across desktops & distributions + +tested with C & C++ compilers +on VisualStudio MinGW Mac Linux Bsd Solaris Minix Raspbian C# fortran (iso_c) +using Gnome Kde Enlightenment Mate Cinnamon Unity +Lxde Lxqt Xfce WindowMaker IceWm Cde Jds OpenBox + +- 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. +*/ + +#include +#include +#include +#include + +#include "tinyfiledialogs.h" +/* #define TINYFD_NOLIB //*/ + +#define _MAX_FNAME 256 // Ray +#define _MAX_EXT 256 // Ray + +#ifdef _WIN32 + #ifndef _WIN32_WINNT + #define _WIN32_WINNT 0x0500 + #endif + #ifndef TINYFD_NOLIB + #include + #include + #endif + #include + #include + /*#include */ + #define SLASH "\\" + /* tinyfd_winUtf8 is not ready yet, do not modify */ + int tinyfd_winUtf8 = 0 ; /* on windows string char can be 0:MBSC or 1:UTF-8 */ +#else + #include + #include + #include /* on old systems try instead */ + #include + #include + #define SLASH "/" +#endif /* _WIN32 */ + +#define MAX_PATH_OR_CMD 1024 /* _MAX_PATH or MAX_PATH */ +#define MAX_MULTIPLE_FILES 32 + +char tinyfd_version [8] = "2.5.8"; + +#if defined(TINYFD_NOLIB) && defined(_WIN32) +int tinyfd_forceConsole = 1 ; +#else +int tinyfd_forceConsole = 0 ; /* 0 (default) or 1 */ +#endif +/* 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, + if the package dialog (and a console is present) or dialog.exe is installed. + on windows it only make sense for console applications */ + +char tinyfd_response[1024]; +/* if you pass "tinyfd_query" as aTitle, +the functions will not display the dialogs +but and 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 the graphic mode: + windows applescript zenity zenity3 matedialog kdialog + xdialog tkinter gdialog gxmessage xmessage +for the console mode: + dialog whiptail basicinput */ + +#if defined(TINYFD_NOLIB) && defined(_WIN32) +static int gWarningDisplayed = 1 ; +#else +static int gWarningDisplayed = 0 ; +#endif + +static char gTitle[]="missing software! (so we switch to basic console input)"; + +static char gAsciiArt[] ="\ + ___________\n\ +/ \\ \n\ +| tiny file |\n\ +| dialogs |\n\ +\\_____ ____/\n\ + \\|"; + +#ifdef _WIN32 +static char gMessageWin[] = "tiny file dialogs on Windows needs:\n\t\ + a graphic display\nor\tdialog.exe (enhanced console mode)\ + \nor\ta console for basic input"; +#else +static char gMessageUnix[] = "tiny file dialogs on UNIX needs:\n\tapplescript\ +\nor\tzenity (version 3 for the color chooser)\ +\nor\tmatedialog\nor\tkdialog\ +\nor\tXdialog\nor\tpython 2 with tkinter\ +\nor\tdialog (opens a console if needed)\ +\nor\twhiptail, gdialog, gxmessage or xmessage (really?)\ +\nor\tit will open a console (if needed) for basic input (you had it comming!)"; +#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 char * getPathWithoutFinalSlash( + char * const aoDestination, /* make sure it is allocated, use _MAX_PATH */ + char const * 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 * const aoDestination, /* make sure it is allocated */ + char const * 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 * const aioString ) +{ + if ( aioString && strlen ( aioString ) ) + { + char * lastcar = aioString + strlen ( aioString ) - 1 ; + if ( strncmp ( lastcar , SLASH , 1 ) ) + { + strcat ( lastcar , 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(__GNUC__) && defined(_WIN32) + sprintf(aoResultHexRGB, "#%02hx%02hx%02hx", +#else + sprintf(aoResultHexRGB, "#%02hhx%02hhx%02hhx", +#endif + aRGB[0], aRGB[1], aRGB[2]); + /* printf("aoResultHexRGB %s\n", aoResultHexRGB); //*/ + } + else + { + aoResultHexRGB[0]=0; + aoResultHexRGB[1]=0; + aoResultHexRGB[2]=0; + } + } +} + + +static void replaceSubStr ( char const * const aSource , + char const * const aOldSubStr , + char const * const aNewSubStr , + char * const aoDestination ) +{ + char const * pOccurence ; + char const * p ; + char const * lNewSubStr = "" ; + int 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 * const aFileNameWithoutPath ) +{ + if ( ! aFileNameWithoutPath + || ! strlen(aFileNameWithoutPath) + || strpbrk(aFileNameWithoutPath , "\\/:*?\"<>|") ) + { + return 0 ; + } + return 1 ; +} + + +static int fileExists( char const * const aFilePathAndName ) +{ + FILE * lIn ; + if ( ! aFilePathAndName || ! strlen(aFilePathAndName) ) + { + return 0 ; + } + lIn = fopen( aFilePathAndName , "r" ) ; + if ( ! lIn ) + { + + return 0 ; + } + fclose ( lIn ) ; + return 1 ; +} + + +/* source and destination can be the same or ovelap*/ +static char const * ensureFilesExist( char * const aDestination , + char const * const aSourcePathsAndNames) +{ + char * lDestination = aDestination ; + char const * p ; + char const * p2 ; + int 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 replaceChr ( char * const aString , + char const aOldChr , + char const aNewChr ) +{ + char * p ; + int lRes = 0 ; + + if ( ! aString ) + { + return 0 ; + } + + if ( aOldChr == aNewChr ) + { + return 0 ; + } + + p = aString ; + while ( (p = strchr ( p , aOldChr )) ) + { + * p = aNewChr ; + p ++ ; + lRes = 1 ; + } + return lRes ; +} + + +static int dirExists ( char const * const aDirPath ) +{ + struct stat lInfo; + if ( ! aDirPath || ! strlen ( aDirPath ) ) + return 0 ; + if ( stat ( aDirPath , & lInfo ) != 0 ) + return 0 ; + else if ( lInfo.st_mode & S_IFDIR ) + return 1 ; + else + return 0 ; +} + + +static wchar_t * getPathWithoutFinalSlashW( + wchar_t * const aoDestination, /* make sure it is allocated, use _MAX_PATH */ + wchar_t const * 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 * const aoDestination, /* make sure it is allocated */ + wchar_t const * 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 (aoResultHexRGB) + { + if (aRGB) + { +#if defined(__GNUC__) && __GNUC__ < 5 +swprintf(aoResultHexRGB, L"#%02hhx%02hhx%02hhx", aRGB[0], aRGB[1], aRGB[2]); +#else +swprintf(aoResultHexRGB, 8, L"#%02hhx%02hhx%02hhx", aRGB[0], aRGB[1], aRGB[2]); +#endif + /* wprintf(L"aoResultHexRGB %s\n", aoResultHexRGB); //*/ + } + else + { + aoResultHexRGB[0] = 0; + aoResultHexRGB[1] = 0; + aoResultHexRGB[2] = 0; + } + } +} + +#ifndef TINYFD_NOLIB + +#if !defined(WC_ERR_INVALID_CHARS) +/* undefined prior to Vista, so not yet in MINGW header file */ +#define WC_ERR_INVALID_CHARS 0x00000080 +#endif + + +static int sizeUtf16(char const * const aUtf8string) +{ + return MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, + aUtf8string, -1, NULL, 0); +} + + +static int sizeUtf8(wchar_t const * const aUtf16string) +{ + return WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, + aUtf16string, -1, NULL, 0, NULL, NULL); +} + + +static wchar_t * utf8to16(char const * const aUtf8string) +{ + wchar_t * lUtf16string ; + int lSize = sizeUtf16(aUtf8string); + lUtf16string = (wchar_t *) malloc( lSize * sizeof(wchar_t) ); + lSize = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, + aUtf8string, -1, lUtf16string, lSize); + if (lSize == 0) + { + free(lUtf16string); + return NULL; + } + return lUtf16string; +} + + +static char * utf16to8(wchar_t const * const aUtf16string) +{ + char * lUtf8string ; + int lSize = sizeUtf8(aUtf16string); + lUtf8string = (char *) malloc( lSize ); + lSize = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, + aUtf16string, -1, lUtf8string, lSize, NULL, NULL); + if (lSize == 0) + { + free(lUtf8string); + return NULL; + } + return lUtf8string; +} + + +static void runSilentA(char const * const aString) +{ + STARTUPINFOA StartupInfo; + PROCESS_INFORMATION ProcessInfo; + char * lArgs; + char * pEnvCMD = NULL; + char * pDefaultCMD = "CMD.EXE"; + ULONG rc; + int lStringLen = 0; + + memset(&StartupInfo, 0, sizeof(StartupInfo)); + StartupInfo.cb = sizeof(STARTUPINFOA); + StartupInfo.dwFlags = STARTF_USESHOWWINDOW; + StartupInfo.wShowWindow = SW_HIDE; + + if ( aString ) + { + lStringLen = strlen(aString); + } + lArgs = (char *) malloc( MAX_PATH_OR_CMD + lStringLen ); + + pEnvCMD = getenv("COMSPEC"); + + if (pEnvCMD){ + + strcpy(lArgs, pEnvCMD); + } + else{ + strcpy(lArgs, pDefaultCMD); + } + + /* c to execute then terminate the command window */ + strcat(lArgs, " /c "); + + /* application and parameters to run from the command window */ + strcat(lArgs, aString); + + if (!CreateProcessA(NULL, lArgs, NULL, NULL, FALSE, + CREATE_NEW_CONSOLE, NULL, NULL, + &StartupInfo, &ProcessInfo)) + { + free(lArgs); + return; /* GetLastError(); */ + } + + WaitForSingleObject(ProcessInfo.hProcess, INFINITE); + if (!GetExitCodeProcess(ProcessInfo.hProcess, &rc)) + rc = 0; + + CloseHandle(ProcessInfo.hThread); + CloseHandle(ProcessInfo.hProcess); + + free(lArgs); + return; /* rc */ +} + + +static void runSilentW(wchar_t const * const aString) +{ + STARTUPINFOW StartupInfo; + PROCESS_INFORMATION ProcessInfo; + ULONG rc; + wchar_t * lArgs; + wchar_t * pEnvCMD; + wchar_t * pDefaultCMD = L"CMD.EXE"; //powershell.exe + int lStringLen = 0; + + memset(&StartupInfo, 0, sizeof(StartupInfo)); + StartupInfo.cb = sizeof(STARTUPINFOW); + StartupInfo.dwFlags = STARTF_USESHOWWINDOW; + StartupInfo.wShowWindow = SW_HIDE; + + if ( aString ) + { + lStringLen = wcslen(aString); + } + lArgs = (wchar_t *) malloc( (MAX_PATH_OR_CMD + lStringLen) * sizeof(wchar_t) ); + + pEnvCMD = utf8to16( getenv("COMSPEC") ); + if (pEnvCMD) + { + wcscpy(lArgs, pEnvCMD); + free(pEnvCMD); + } + else + { + wcscpy(lArgs, pDefaultCMD); + } + + /* c to execute then terminate the command window */ + wcscat(lArgs, L" /c "); + + /* application and parameters to run from the command window */ + wcscat(lArgs, aString); + + if (!CreateProcessW(NULL, lArgs, NULL, NULL, FALSE, + CREATE_NEW_CONSOLE, NULL, NULL, + &StartupInfo, &ProcessInfo)) + { + free(lArgs); + return; /* GetLastError(); */ + } + + WaitForSingleObject(ProcessInfo.hProcess, INFINITE); + if (!GetExitCodeProcess(ProcessInfo.hProcess, &rc)) + { + rc = 0; + } + + CloseHandle(ProcessInfo.hThread); + CloseHandle(ProcessInfo.hProcess); + + free(lArgs); + return; /* rc */ +} + + + +int tinyfd_messageBoxW( + wchar_t const * const aTitle, /* NULL or "" */ + wchar_t const * const aMessage, /* NULL or "" may contain \n and \t */ + wchar_t const * const aDialogType, /* "ok" "okcancel" "yesno" */ + wchar_t const * const aIconType, /* "info" "warning" "error" "question" */ + int const aDefaultButton) /* 0 for cancel/no , 1 for ok/yes */ +{ + int lBoxReturnValue; + UINT aCode; + + 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 + { + aCode += MB_OK; + } + + lBoxReturnValue = MessageBoxW(NULL, aMessage, aTitle, aCode); + if (((aDialogType + && wcscmp(L"okcancel", aDialogType) + && wcscmp(L"yesno", aDialogType))) + || (lBoxReturnValue == IDOK) + || (lBoxReturnValue == IDYES)) + { + return 1; + } + else + { + return 0; + } +} + + +static int messageBoxWinGui8( + char const * const aTitle, /* NULL or "" */ + char const * const aMessage, /* NULL or "" may contain \n and \t */ + char const * const aDialogType, /* "ok" "okcancel" "yesno" */ + char const * const aIconType, /* "info" "warning" "error" "question" */ + int const aDefaultButton) /* 0 for cancel/no , 1 for ok/yes */ +{ + int lIntRetVal; + wchar_t * lTitle; + wchar_t * lMessage; + wchar_t * lDialogType; + wchar_t * lIconType; + + lTitle = utf8to16(aTitle); + lMessage = utf8to16(aMessage); + lDialogType = utf8to16(aDialogType); + lIconType = utf8to16(aIconType); + + lIntRetVal = tinyfd_messageBoxW(lTitle, lMessage, + lDialogType, lIconType, aDefaultButton ); + + free(lTitle); + free(lMessage); + free(lDialogType); + free(lIconType); + + return lIntRetVal ; +} + +#endif /* TINYFD_NOLIB */ + +static char const * inputBoxWinGui( + char * const aoBuff , + char const * const aTitle , /* NULL or "" */ + char const * const aMessage , /* NULL or "" may NOT contain \n nor \t */ + char const * const aDefaultInput ) /* "" , if NULL it's a passwordBox */ +{ + char * lDialogString; + FILE * lIn; + int lResult; + int lTitleLen; + int lMessageLen; + +#ifndef TINYFD_NOLIB + wchar_t * lDialogStringW; +#endif + + lTitleLen = aTitle ? strlen(aTitle) : 0 ; + lMessageLen = aMessage ? strlen(aMessage) : 0 ; + lDialogString = (char *)malloc(3 * MAX_PATH_OR_CMD + lTitleLen + lMessageLen); + + if (aDefaultInput) + { + sprintf(lDialogString, "%s\\AppData\\Local\\Temp\\tinyfd.vbs", + getenv("USERPROFILE")); + } + else + { + sprintf(lDialogString, "%s\\AppData\\Local\\Temp\\tinyfd.hta", + getenv("USERPROFILE")); + } + lIn = fopen(lDialogString, "w"); + if (!lIn) + { + free(lDialogString); + return NULL; + } + + if ( aDefaultInput ) + { + strcpy(lDialogString, "Dim result:result=InputBox(\""); + if (aMessage && strlen(aMessage)) + { + strcat(lDialogString, aMessage); + } + strcat(lDialogString, "\",\""); + if (aTitle && strlen(aTitle)) + { + strcat(lDialogString, aTitle); + } + strcat(lDialogString, "\",\""); + if (aDefaultInput && strlen(aDefaultInput)) + { + strcat(lDialogString, aDefaultInput); + } + strcat(lDialogString, "\"):If IsEmpty(result) then:WScript.Echo 0"); + strcat(lDialogString, ":Else: WScript.Echo \"1\" & result : End If"); + } + else + { + sprintf(lDialogString, "\n\ +\n\ +\n\ +%s\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ +%s\n\ +\n\ +\n\ +\n\ +
\n\ +

\n\ +\n\ +
\n\ +
\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ +
\n\ +
\n\ +\n\ +\n\ +" , aTitle ? aTitle : "", aMessage ? aMessage : "") ; + } + fputs(lDialogString, lIn); + fclose(lIn); + + strcpy(lDialogString, ""); + +#ifndef TINYFD_NOLIB + if ( aDefaultInput && !GetConsoleWindow()) + { + strcat(lDialogString, "powershell -WindowStyle Hidden -Command \""); + } +#endif + + if (aDefaultInput) + { + strcat(lDialogString, + "cscript.exe %USERPROFILE%\\AppData\\Local\\Temp\\tinyfd.vbs"); + } + else + { + strcat(lDialogString, + "mshta.exe %USERPROFILE%\\AppData\\Local\\Temp\\tinyfd.hta"); + } + +#ifndef TINYFD_NOLIB + if (aDefaultInput && !GetConsoleWindow()) + { + strcat(lDialogString, "\""); + } +#endif + + /* printf ( "lDialogString: %s\n" , lDialogString ) ; //*/ + +#ifndef TINYFD_NOLIB + if ( ! aDefaultInput ) + { + if (tinyfd_winUtf8) + { + lDialogStringW = utf8to16(lDialogString); + runSilentW(lDialogStringW); + free(lDialogStringW); + } + else + { + runSilentA(lDialogString); + } + } + else +#endif /* TINYFD_NOLIB */ + { + if (!(lIn = _popen(lDialogString, "r"))) + { + free(lDialogString); + return NULL; + } + while (fgets(aoBuff, MAX_PATH_OR_CMD, lIn) != NULL) + { + } + _pclose(lIn); + if (aoBuff[strlen(aoBuff) - 1] == '\n') + { + aoBuff[strlen(aoBuff) - 1] = '\0'; + } + } + + if (aDefaultInput) + { + sprintf(lDialogString, "%s\\AppData\\Local\\Temp\\tinyfd.vbs", + getenv("USERPROFILE")); + } + else + { + sprintf(lDialogString, "%s\\AppData\\Local\\Temp\\tinyfd.txt", + getenv("USERPROFILE")); + if (!(lIn = fopen(lDialogString, "r"))) + { + remove(lDialogString); + sprintf(lDialogString, "%s\\AppData\\Local\\Temp\\tinyfd.hta", + getenv("USERPROFILE")); + free(lDialogString); + return NULL; + } + while (fgets(aoBuff, MAX_PATH_OR_CMD, lIn) != NULL) + {} + fclose(lIn); + remove(lDialogString); + sprintf(lDialogString, "%s\\AppData\\Local\\Temp\\tinyfd.hta", + getenv("USERPROFILE")); + } + remove(lDialogString); + free(lDialogString); + /* printf ( "aoBuff: %s\n" , aoBuff ) ; //*/ + lResult = strncmp(aoBuff, "1", 1) ? 0 : 1; + /* printf ( "lResult: %d \n" , lResult ) ; //*/ + if (!lResult) + { + return NULL ; + } + /* printf ( "aoBuff+1: %s\n" , aoBuff+1 ) ; //*/ + return aoBuff + 1; +} + +#ifndef TINYFD_NOLIB + +wchar_t const * tinyfd_saveFileDialogW( + wchar_t const * const aTitle, /* NULL or "" */ + wchar_t const * const aDefaultPathAndFile, /* NULL or "" */ + int const aNumOfFilterPatterns, /* 0 */ + wchar_t const * const * const aFilterPatterns, /* NULL or {"*.jpg","*.png"} */ + wchar_t const * 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; + int i; + OPENFILENAMEW ofn; + HRESULT lHResult; + + lHResult = CoInitializeEx(NULL, 0); + + getPathWithoutFinalSlashW(lDirname, aDefaultPathAndFile); + getLastNameW(lBuff, aDefaultPathAndFile); + + 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(OPENFILENAMEW); + ofn.hwndOwner = 0; + ofn.hInstance = 0; + ofn.lpstrFilter = lFilterPatterns; + ofn.lpstrCustomFilter = NULL; + ofn.nMaxCustFilter = 0; + ofn.nFilterIndex = 1; + ofn.lpstrFile = lBuff; + + ofn.nMaxFile = MAX_PATH_OR_CMD; + ofn.lpstrFileTitle = NULL; + ofn.nMaxFileTitle = _MAX_FNAME + _MAX_EXT; + ofn.lpstrInitialDir = lDirname; + ofn.lpstrTitle = aTitle; + ofn.Flags = OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR; + ofn.nFileOffset = 0; + ofn.nFileExtension = 0; + ofn.lpstrDefExt = NULL; + 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; +} + + +static char const * saveFileDialogWinGui8( + char * const aoBuff, + char const * const aTitle, /* NULL or "" */ + char const * const aDefaultPathAndFile, /* NULL or "" */ + int const aNumOfFilterPatterns, /* 0 */ + char const * const * const aFilterPatterns, /* NULL or {"*.jpg","*.png"} */ + char const * const aSingleFilterDescription) /* NULL or "image files" */ +{ + wchar_t * lTitle; + wchar_t * lDefaultPathAndFile; + wchar_t * lSingleFilterDescription; + wchar_t * * lFilterPatterns; + wchar_t const * lTmpWChar; + char * lTmpChar; + int i ; + + lFilterPatterns = (wchar_t **) malloc(aNumOfFilterPatterns*sizeof(wchar_t *)); + for (i = 0; i < aNumOfFilterPatterns; i++) + { + lFilterPatterns[i] = utf8to16(aFilterPatterns[i]); + } + + lTitle = utf8to16(aTitle); + lDefaultPathAndFile = utf8to16(aDefaultPathAndFile); + lSingleFilterDescription = utf8to16(aSingleFilterDescription); + + lTmpWChar = tinyfd_saveFileDialogW( + lTitle, + lDefaultPathAndFile, + aNumOfFilterPatterns, + (wchar_t const** ) /*stupid cast for gcc*/ + lFilterPatterns, + lSingleFilterDescription); + + free(lTitle); + free(lDefaultPathAndFile); + free(lSingleFilterDescription); + for (i = 0; i < aNumOfFilterPatterns; i++) + { + free(lFilterPatterns[i]); + } + free(lFilterPatterns); + + if (!lTmpWChar) + { + return NULL; + } + + lTmpChar = utf16to8(lTmpWChar); + strcpy(aoBuff, lTmpChar); + free(lTmpChar); + + return aoBuff; +} + + +wchar_t const * tinyfd_openFileDialogW( + wchar_t const * const aTitle, /* NULL or "" */ + wchar_t const * const aDefaultPathAndFile, /* NULL or "" */ + int const aNumOfFilterPatterns, /* 0 */ + wchar_t const * const * const aFilterPatterns, /* NULL or {"*.jpg","*.png"} */ + wchar_t const * const aSingleFilterDescription, /* NULL or "image files" */ + int const aAllowMultipleSelects) /* 0 or 1 */ +{ + static wchar_t lBuff[MAX_MULTIPLE_FILES*MAX_PATH_OR_CMD]; + + 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]; + wchar_t * lRetval, * p; + int i, j; + OPENFILENAMEW ofn; + size_t lBuffLen; + HRESULT lHResult; + + lHResult = CoInitializeEx(NULL, 0); + + getPathWithoutFinalSlashW(lDirname, aDefaultPathAndFile); + getLastNameW(lBuff, aDefaultPathAndFile); + + 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 = 0; + ofn.hInstance = 0; + ofn.lpstrFilter = lFilterPatterns; + ofn.lpstrCustomFilter = NULL; + ofn.nMaxCustFilter = 0; + ofn.nFilterIndex = 1; + ofn.lpstrFile = lBuff; + ofn.nMaxFile = MAX_PATH_OR_CMD; + ofn.lpstrFileTitle = NULL; + ofn.nMaxFileTitle = _MAX_FNAME + _MAX_EXT; + ofn.lpstrInitialDir = lDirname; + ofn.lpstrTitle = aTitle; + ofn.Flags = OFN_EXPLORER | OFN_NOCHANGEDIR; + 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) + { + lRetval = NULL; + } + else + { + lBuffLen = wcslen(lBuff); + lPointers[0] = lBuff + lBuffLen + 1; + if (!aAllowMultipleSelects || (lPointers[0][0] == L'\0')) + { + lRetval = lBuff; + } + else + { + i = 0; + do + { + lLengths[i] = wcslen(lPointers[i]); + lPointers[i + 1] = lPointers[i] + lLengths[i] + 1; + i++; + } while (lPointers[i][0] != L'\0'); + i--; + p = lBuff + MAX_MULTIPLE_FILES*MAX_PATH_OR_CMD - 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++; + lRetval = p; + } + } + + if (lHResult == S_OK || lHResult == S_FALSE) + { + CoUninitialize(); + } + return lRetval; +} + + +static char const * openFileDialogWinGui8( + char * const aoBuff, + char const * const aTitle, /* NULL or "" */ + char const * const aDefaultPathAndFile, /* NULL or "" */ + int const aNumOfFilterPatterns, /* 0 */ + char const * const * const aFilterPatterns, /* NULL or {"*.jpg","*.png"} */ + char const * const aSingleFilterDescription, /* NULL or "image files" */ + int const aAllowMultipleSelects) /* 0 or 1 */ +{ + wchar_t * lTitle; + wchar_t * lDefaultPathAndFile; + wchar_t * lSingleFilterDescription; + wchar_t * * lFilterPatterns; + wchar_t const * lTmpWChar; + char * lTmpChar; + int i; + + lFilterPatterns = (wchar_t * *) malloc(aNumOfFilterPatterns*sizeof(wchar_t *)); + for (i = 0; i < aNumOfFilterPatterns; i++) + { + lFilterPatterns[i] = utf8to16(aFilterPatterns[i]); + } + + lTitle = utf8to16(aTitle); + lDefaultPathAndFile = utf8to16(aDefaultPathAndFile); + lSingleFilterDescription = utf8to16(aSingleFilterDescription); + + lTmpWChar = tinyfd_openFileDialogW( + lTitle, + lDefaultPathAndFile, + aNumOfFilterPatterns, + (wchar_t const**) /*stupid cast for gcc*/ + lFilterPatterns, + lSingleFilterDescription, + aAllowMultipleSelects); + + free(lTitle); + free(lDefaultPathAndFile); + free(lSingleFilterDescription); + for (i = 0; i < aNumOfFilterPatterns; i++) + { + free(lFilterPatterns[i]); + } + free(lFilterPatterns); + + if (!lTmpWChar) + { + return NULL; + } + + lTmpChar = utf16to8(lTmpWChar); + strcpy(aoBuff, lTmpChar); + free(lTmpChar); + + return aoBuff; +} + + +wchar_t const * tinyfd_selectFolderDialogW( + wchar_t const * const aTitle, /* NULL or "" */ + wchar_t const * const aDefaultPath) /* NULL or "" */ +{ + static wchar_t lBuff[MAX_PATH_OR_CMD]; + + BROWSEINFOW bInfo; + LPITEMIDLIST lpItem; + HRESULT lHResult; + + lHResult = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + /* we can't use aDefaultPath */ + bInfo.hwndOwner = 0; + bInfo.pidlRoot = NULL; + bInfo.pszDisplayName = lBuff; + bInfo.lpszTitle = aTitle; + bInfo.ulFlags = BIF_USENEWUI; + bInfo.lpfn = NULL; + bInfo.lParam = 0; + bInfo.iImage = -1; + + lpItem = SHBrowseForFolderW(&bInfo); + if (lpItem) + { + SHGetPathFromIDListW(lpItem, lBuff); + } + + if (lHResult == S_OK || lHResult == S_FALSE) + { + CoUninitialize(); + } + return lBuff; +} + + +static char const * selectFolderDialogWinGui8 ( + char * const aoBuff , + char const * const aTitle , /* NULL or "" */ + char const * const aDefaultPath ) /* NULL or "" */ +{ + wchar_t * lTitle; + wchar_t * lDefaultPath; + wchar_t const * lTmpWChar; + char * lTmpChar; + + lTitle = utf8to16(aTitle); + lDefaultPath = utf8to16(aDefaultPath); + + lTmpWChar = tinyfd_selectFolderDialogW( + lTitle, + lDefaultPath); + + free(lTitle); + free(lDefaultPath); + if (!lTmpWChar) + { + return NULL; + } + + lTmpChar = utf16to8(lTmpWChar); + strcpy(aoBuff, lTmpChar); + free(lTmpChar); + + return aoBuff; +} + + +wchar_t const * tinyfd_colorChooserW( + wchar_t const * const aTitle, /* NULL or "" */ + wchar_t const * 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; + //lHResult = CoInitializeEx(NULL, 0); + + if (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 = NULL; + cc.hInstance = NULL; + cc.rgbResult = RGB(lDefaultRGB[0], lDefaultRGB[1], lDefaultRGB[2]); + cc.lpCustColors = crCustColors; + cc.Flags = CC_RGBINIT | CC_FULLOPEN; + 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 char const * colorChooserWinGui8( + char const * const aTitle, /* NULL or "" */ + char const * 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; + wchar_t * lDefaultHexRGB; + wchar_t const * lTmpWChar; + char * lTmpChar; + + lTitle = utf8to16(aTitle); + lDefaultHexRGB = utf8to16(aDefaultHexRGB); + + lTmpWChar = tinyfd_colorChooserW( + lTitle, + lDefaultHexRGB, + aDefaultRGB, + aoResultRGB ); + + free(lTitle); + free(lDefaultHexRGB); + if (!lTmpWChar) + { + return NULL; + } + + lTmpChar = utf16to8(lTmpWChar); + strcpy(lResultHexRGB, lTmpChar); + free(lTmpChar); + + return lResultHexRGB; +} + + +static int messageBoxWinGuiA ( + char const * const aTitle , /* NULL or "" */ + char const * const aMessage , /* NULL or "" may contain \n and \t */ + char const * const aDialogType , /* "ok" "okcancel" "yesno" */ + char const * const aIconType , /* "info" "warning" "error" "question" */ + int const aDefaultButton ) /* 0 for cancel/no , 1 for ok/yes */ +{ + int lBoxReturnValue; + UINT aCode ; + + if ( aIconType && ! strcmp( "warning" , aIconType ) ) + { + aCode = MB_ICONWARNING ; + } + else if ( aIconType && ! strcmp("error", aIconType)) + { + aCode = MB_ICONERROR ; + } + else if ( aIconType && ! strcmp("question", aIconType)) + { + aCode = MB_ICONQUESTION ; + } + else + { + aCode = MB_ICONINFORMATION ; + } + + if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) ) + { + aCode += MB_OKCANCEL ; + if ( ! aDefaultButton ) + { + aCode += MB_DEFBUTTON2 ; + } + } + else if ( aDialogType && ! strcmp( "yesno" , aDialogType ) ) + { + aCode += MB_YESNO ; + if ( ! aDefaultButton ) + { + aCode += MB_DEFBUTTON2 ; + } + } + else + { + aCode += MB_OK ; + } + + lBoxReturnValue = MessageBoxA(NULL, aMessage, aTitle, aCode); + if ( ( ( aDialogType + && strcmp("okcancel", aDialogType) + && strcmp("yesno", aDialogType) ) ) + || (lBoxReturnValue == IDOK) + || (lBoxReturnValue == IDYES) ) + { + return 1 ; + } + else + { + return 0 ; + } +} + + +static char const * saveFileDialogWinGuiA ( + char * const aoBuff , + char const * const aTitle , /* NULL or "" */ + char const * const aDefaultPathAndFile , /* NULL or "" */ + int const aNumOfFilterPatterns , /* 0 */ + char const * const * const aFilterPatterns , /* NULL or {"*.jpg","*.png"} */ + char const * const aSingleFilterDescription ) /* NULL or "image files" */ +{ + char lDirname [MAX_PATH_OR_CMD] ; + char lDialogString[MAX_PATH_OR_CMD]; + char lFilterPatterns[MAX_PATH_OR_CMD] = ""; + int i ; + char * p; + OPENFILENAMEA ofn ; + char * lRetval; + HRESULT lHResult; + + lHResult = CoInitializeEx(NULL,0); + + getPathWithoutFinalSlash(lDirname, aDefaultPathAndFile); + getLastName(aoBuff, aDefaultPathAndFile); + + if (aNumOfFilterPatterns > 0) + { + if ( aSingleFilterDescription && strlen(aSingleFilterDescription) ) + { + strcpy(lFilterPatterns, aSingleFilterDescription); + strcat(lFilterPatterns, "\n"); + } + strcat(lFilterPatterns, aFilterPatterns[0]); + for (i = 1; i < aNumOfFilterPatterns; i++) + { + strcat(lFilterPatterns, ";"); + strcat(lFilterPatterns, aFilterPatterns[i]); + } + strcat(lFilterPatterns, "\n"); + if ( ! (aSingleFilterDescription && strlen(aSingleFilterDescription) ) ) + { + strcpy(lDialogString, lFilterPatterns); + strcat(lFilterPatterns, lDialogString); + } + strcat(lFilterPatterns, "All Files\n*.*\n"); + p = lFilterPatterns; + while ((p = strchr(p, '\n')) != NULL) + { + *p = '\0'; + p ++ ; + } + } + + ofn.lStructSize = sizeof(OPENFILENAME) ; + ofn.hwndOwner = 0 ; + ofn.hInstance = 0 ; + ofn.lpstrFilter = lFilterPatterns ; + ofn.lpstrCustomFilter = NULL ; + ofn.nMaxCustFilter = 0 ; + ofn.nFilterIndex = 1 ; + ofn.lpstrFile = aoBuff; + + ofn.nMaxFile = MAX_PATH_OR_CMD ; + ofn.lpstrFileTitle = NULL ; + ofn.nMaxFileTitle = _MAX_FNAME + _MAX_EXT ; + ofn.lpstrInitialDir = lDirname; + ofn.lpstrTitle = aTitle ; + ofn.Flags = OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR ; + ofn.nFileOffset = 0 ; + ofn.nFileExtension = 0 ; + ofn.lpstrDefExt = NULL ; + ofn.lCustData = 0L ; + ofn.lpfnHook = NULL ; + ofn.lpTemplateName = NULL ; + + if ( GetSaveFileNameA ( & ofn ) == 0 ) + { + lRetval = NULL ; + } + else + { + lRetval = aoBuff ; + } + + if (lHResult==S_OK || lHResult==S_FALSE) + { + CoUninitialize(); + } + return lRetval ; +} + + +static char const * openFileDialogWinGuiA ( + char * const aoBuff , + char const * const aTitle , /* NULL or "" */ + char const * const aDefaultPathAndFile , /* NULL or "" */ + int const aNumOfFilterPatterns , /* 0 */ + char const * const * const aFilterPatterns , /* NULL or {"*.jpg","*.png"} */ + char const * const aSingleFilterDescription , /* NULL or "image files" */ + int const aAllowMultipleSelects ) /* 0 or 1 */ +{ + char lDirname [MAX_PATH_OR_CMD] ; + char lFilterPatterns[MAX_PATH_OR_CMD] = ""; + char lDialogString[MAX_PATH_OR_CMD] ; + char * lPointers[MAX_MULTIPLE_FILES]; + size_t lLengths[MAX_MULTIPLE_FILES]; + int i , j ; + char * p; + OPENFILENAMEA ofn; + size_t lBuffLen ; + char * lRetval; + HRESULT lHResult; + + lHResult = CoInitializeEx(NULL,0); + + getPathWithoutFinalSlash(lDirname, aDefaultPathAndFile); + getLastName(aoBuff, aDefaultPathAndFile); + + if (aNumOfFilterPatterns > 0) + { + if ( aSingleFilterDescription && strlen(aSingleFilterDescription) ) + { + strcpy(lFilterPatterns, aSingleFilterDescription); + strcat(lFilterPatterns, "\n"); + } + strcat(lFilterPatterns, aFilterPatterns[0]); + for (i = 1; i < aNumOfFilterPatterns; i++) + { + strcat(lFilterPatterns, ";"); + strcat(lFilterPatterns, aFilterPatterns[i]); + } + strcat(lFilterPatterns, "\n"); + if ( ! (aSingleFilterDescription && strlen(aSingleFilterDescription) ) ) + { + strcpy(lDialogString, lFilterPatterns); + strcat(lFilterPatterns, lDialogString); + } + strcat(lFilterPatterns, "All Files\n*.*\n"); + p = lFilterPatterns; + while ((p = strchr(p, '\n')) != NULL) + { + *p = '\0'; + p ++ ; + } + } + + ofn.lStructSize = sizeof ( OPENFILENAME ) ; + ofn.hwndOwner = 0 ; + ofn.hInstance = 0 ; + ofn.lpstrFilter = lFilterPatterns; + ofn.lpstrCustomFilter = NULL ; + ofn.nMaxCustFilter = 0 ; + ofn.nFilterIndex = 1 ; + ofn.lpstrFile = aoBuff ; + ofn.nMaxFile = MAX_PATH_OR_CMD ; + ofn.lpstrFileTitle = NULL ; + ofn.nMaxFileTitle = _MAX_FNAME + _MAX_EXT ; + ofn.lpstrInitialDir = lDirname ; + ofn.lpstrTitle = aTitle ; + ofn.Flags = OFN_EXPLORER | OFN_NOCHANGEDIR ; + 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 ( GetOpenFileNameA ( & ofn ) == 0 ) + { + lRetval = NULL ; + } + else + { + lBuffLen = strlen(aoBuff) ; + lPointers[0] = aoBuff + lBuffLen + 1 ; + if ( !aAllowMultipleSelects || (lPointers[0][0] == '\0') ) + { + lRetval = aoBuff ; + } + else + { + i = 0 ; + do + { + lLengths[i] = strlen(lPointers[i]); + lPointers[i+1] = lPointers[i] + lLengths[i] + 1 ; + i ++ ; + } + while ( lPointers[i][0] != '\0' ); + i--; + p = aoBuff + MAX_MULTIPLE_FILES*MAX_PATH_OR_CMD - 1 ; + * p = '\0'; + for ( j = i ; j >=0 ; j-- ) + { + p -= lLengths[j]; + memmove(p, lPointers[j], lLengths[j]); + p--; + *p = '\\'; + p -= lBuffLen ; + memmove(p, aoBuff, lBuffLen); + p--; + *p = '|'; + } + p++; + lRetval = p ; + } + } + + if (lHResult==S_OK || lHResult==S_FALSE) + { + CoUninitialize(); + } + return lRetval; +} + + +static char const * selectFolderDialogWinGuiA ( + char * const aoBuff , + char const * const aTitle , /* NULL or "" */ + char const * const aDefaultPath ) /* NULL or "" */ +{ + BROWSEINFOA bInfo ; + LPITEMIDLIST lpItem ; + HRESULT lHResult; + + lHResult = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + /* we can't use aDefaultPath */ + bInfo.hwndOwner = 0 ; + bInfo.pidlRoot = NULL ; + bInfo.pszDisplayName = aoBuff ; + bInfo.lpszTitle = aTitle ; + bInfo.ulFlags = BIF_USENEWUI; + bInfo.lpfn = NULL ; + bInfo.lParam = 0 ; + bInfo.iImage = -1 ; + + lpItem = SHBrowseForFolderA ( & bInfo ) ; + if ( lpItem ) + { + SHGetPathFromIDListA ( lpItem , aoBuff ) ; + } + + if (lHResult==S_OK || lHResult==S_FALSE) + { + CoUninitialize(); + } + return aoBuff ; +} + + +static char const * colorChooserWinGuiA( + char const * const aTitle, /* NULL or "" */ + char const * const aDefaultHexRGB, /* NULL or "#FF0000"*/ + unsigned char const aDefaultRGB[3], /* { 0 , 255 , 255 } */ + unsigned char aoResultRGB[3]) /* { 0 , 0 , 0 } */ +{ + static char lResultHexRGB[8]; + + CHOOSECOLORA cc; + COLORREF crCustColors[16]; + unsigned char lDefaultRGB[3]; + int lRet; + + if ( aDefaultHexRGB ) + { + Hex2RGB(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 = NULL ; + cc.hInstance = NULL ; + cc.rgbResult = RGB(lDefaultRGB[0], lDefaultRGB[1], lDefaultRGB[2]); + cc.lpCustColors = crCustColors; + cc.Flags = CC_RGBINIT | CC_FULLOPEN; + cc.lCustData = 0; + cc.lpfnHook = NULL; + cc.lpTemplateName = NULL; + + lRet = ChooseColorA(&cc); + + if ( ! lRet ) + { + return NULL; + } + + aoResultRGB[0] = GetRValue(cc.rgbResult); + aoResultRGB[1] = GetGValue(cc.rgbResult); + aoResultRGB[2] = GetBValue(cc.rgbResult); + + RGB2Hex(aoResultRGB, lResultHexRGB); + + return lResultHexRGB; +} + +#endif /* TINYFD_NOLIB */ + +static int dialogPresent ( ) +{ + static int lDialogPresent = -1 ; + char lBuff [MAX_PATH_OR_CMD] ; + FILE * lIn ; + char const * lString = "dialog.exe"; + if ( lDialogPresent < 0 ) + { + if (!(lIn = _popen("where dialog.exe","r"))) + { + 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 * const aTitle , /* NULL or "" */ + char const * const aMessage , /* NULL or "" may contain \n and \t */ + char const * const aDialogType , /* "ok" "okcancel" "yesno" */ + char const * const aIconType , /* "info" "warning" "error" "question" */ + int const aDefaultButton ) /* 0 for cancel/no , 1 for ok/yes */ +{ + char lDialogString[MAX_PATH_OR_CMD]; + char lDialogFile[MAX_PATH_OR_CMD]; + FILE * lIn; + char lBuff [MAX_PATH_OR_CMD] = ""; + + strcpy ( lDialogString , "dialog " ) ; + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, "--title \"") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\" ") ; + } + + if ( aDialogType && ( !strcmp( "okcancel" , aDialogType ) || !strcmp( "yesno" , 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 + { + strcat ( lDialogString , "--msgbox " ) ; + } + + strcat ( lDialogString , "\"" ) ; + if ( aMessage && strlen(aMessage) ) + { + replaceSubStr ( aMessage , "\n" , "\\n" , lBuff ) ; + strcat(lDialogString, lBuff) ; + lBuff[0]='\0'; + } + + strcat(lDialogString, "\" 10 60"); + strcat(lDialogString, " && echo 1 > "); + + strcpy(lDialogFile, getenv("USERPROFILE")); + strcat(lDialogFile, "\\AppData\\Local\\Temp\\tinyfd.txt"); + strcat(lDialogString, lDialogFile); + + /* printf ( "lDialogString: %s\n" , lDialogString ) ; //*/ + system ( lDialogString ) ; + + if (!(lIn = fopen(lDialogFile, "r"))) + { + 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' ; + } + /* printf ( "lBuff: %s\n" , lBuff ) ; //*/ + if ( ! strlen(lBuff) ) + { + return 0; + } + return 1; +} + + +static char const * inputBoxWinConsole( + char * const aoBuff , + char const * const aTitle , /* NULL or "" */ + char const * const aMessage , /* NULL or "" may NOT contain \n nor \t */ + char const * 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("USERPROFILE")); + strcat(lDialogFile, "\\AppData\\Local\\Temp\\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") ; + strcat(lDialogString, "\" ") ; + + if ( ! aDefaultInput ) + { + strcat ( lDialogString , "--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("USERPROFILE")); + strcat(lDialogFile, "\\AppData\\Local\\Temp\\tinyfd.txt"); + strcat(lDialogString, lDialogFile); + strcat(lDialogString, " || echo 0 > "); + strcat(lDialogString, lDialogFile); + + /* printf ( "lDialogString: %s\n" , lDialogString ) ; //*/ + system ( lDialogString ) ; + + if (!(lIn = fopen(lDialogFile, "r"))) + { + remove(lDialogFile); + return 0 ; + } + while (fgets(aoBuff, MAX_PATH_OR_CMD, lIn) != NULL) + {} + fclose(lIn); + 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 ) + { + return NULL ; + } + /* printf ( "aoBuff+1: %s\n" , aoBuff+1 ) ; //*/ + return aoBuff+3 ; +} + + +static char const * saveFileDialogWinConsole ( + char * const aoBuff , + char const * const aTitle , /* NULL or "" */ + char const * const aDefaultPathAndFile ) /* 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 | spacebar -> select | / -> populate | enter -> ok input line") ; + strcat(lDialogString, "\" ") ; + + strcat ( lDialogString , "--fselect \"" ) ; + if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) + { + /* dialog.exe uses unix separators even on windows */ + strcpy(lPathAndFile, aDefaultPathAndFile); + 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("USERPROFILE")); + strcat(lPathAndFile, "\\AppData\\Local\\Temp\\tinyfd.txt"); + strcat(lDialogString, lPathAndFile); + + /* printf ( "lDialogString: %s\n" , lDialogString ) ; //*/ + system ( lDialogString ) ; + + if (!(lIn = fopen(lPathAndFile, "r"))) + { + 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 const * openFileDialogWinConsole ( + char * const aoBuff , + char const * const aTitle , /* NULL or "" */ + char const * const aDefaultPathAndFile , /* NULL or "" */ + int const aAllowMultipleSelects ) /* 0 or 1 */ +{ + char lFilterPatterns[MAX_PATH_OR_CMD] = ""; + char lDialogString[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 | spacebar -> select | / -> populate | enter -> ok input line") ; + strcat(lDialogString, "\" ") ; + + strcat ( lDialogString , "--fselect \"" ) ; + if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) + { + /* dialog.exe uses unix separators even on windows */ + strcpy(lFilterPatterns, aDefaultPathAndFile); + 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("USERPROFILE")); + strcat(lFilterPatterns, "\\AppData\\Local\\Temp\\tinyfd.txt"); + strcat(lDialogString, lFilterPatterns); + + /* printf ( "lDialogString: %s\n" , lDialogString ) ; //*/ + system ( lDialogString ) ; + + if (!(lIn = fopen(lFilterPatterns, "r"))) + { + 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 const * selectFolderDialogWinConsole ( + char * const aoBuff , + char const * const aTitle , /* NULL or "" */ + char const * 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 | spacebar -> select | / -> populate | enter -> ok input line") ; + 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("USERPROFILE")); + strcat(lString, "\\AppData\\Local\\Temp\\tinyfd.txt"); + strcat(lDialogString, lString); + + /* printf ( "lDialogString: %s\n" , lDialogString ) ; //*/ + system ( lDialogString ) ; + + if (!(lIn = fopen(lString, "r"))) + { + 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; +} + + +/* returns 0 for cancel/no , 1 for ok/yes */ +int tinyfd_messageBox ( + char const * const aTitle , /* NULL or "" */ + char const * const aMessage , /* NULL or "" may contain \n and \t */ + char const * const aDialogType , /* "ok" "okcancel" "yesno" */ + char const * const aIconType , /* "info" "warning" "error" "question" */ + int const aDefaultButton ) /* 0 for cancel/no , 1 for ok/yes */ +{ + char lChar ; + +#ifndef TINYFD_NOLIB + if ((!tinyfd_forceConsole || !(GetConsoleWindow() || dialogPresent())) + && (!getenv("SSH_CLIENT") || getenv("DISPLAY"))) + { + if (aTitle&&!strcmp(aTitle, "tinyfd_query")){ strcpy(tinyfd_response, "windows"); return 1; } + if (tinyfd_winUtf8) + { + return messageBoxWinGui8( + aTitle, aMessage, aDialogType, aIconType, aDefaultButton); + } + else + { + return messageBoxWinGuiA( + aTitle, aMessage, aDialogType, aIconType, aDefaultButton); + } + } + else +#endif /* TINYFD_NOLIB */ + if ( dialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return 0;} + return messageBoxWinConsole( + aTitle,aMessage,aDialogType,aIconType,aDefaultButton); + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return 0;} + if (!gWarningDisplayed && !tinyfd_forceConsole ) + { + gWarningDisplayed = 1; + printf("\n\n%s", gAsciiArt); + printf("\n%s\n", gTitle); + printf("%s\n\n\n", gMessageWin); + } + if ( aTitle && strlen(aTitle) ) + { + printf ("%s\n\n", aTitle); + } + if ( aDialogType && !strcmp("yesno",aDialogType) ) + { + do + { + if ( aMessage && strlen(aMessage) ) + { + printf("%s\n",aMessage); + } + printf("y/n: "); + lChar = (char) tolower ( _getch() ) ; + printf("\n\n"); + } + while ( lChar != 'y' && lChar != 'n' ) ; + return lChar == 'y' ? 1 : 0 ; + } + else if ( aDialogType && !strcmp("okcancel",aDialogType) ) + { + do + { + if ( aMessage && strlen(aMessage) ) + { + printf("%s\n",aMessage); + } + printf("[O]kay/[C]ancel: "); + lChar = (char) tolower ( _getch() ) ; + printf("\n\n"); + } + while ( lChar != 'o' && lChar != 'c' ) ; + return lChar == 'o' ? 1 : 0 ; + } + else + { + if ( aMessage && strlen(aMessage) ) + { + printf("%s\n\n",aMessage); + } + printf("press enter to continue "); + lChar = (char) _getch() ; + printf("\n\n"); + return 1 ; + } + } +} + + +/* returns NULL on cancel */ +char const * tinyfd_inputBox( + char const * const aTitle , /* NULL or "" */ + char const * const aMessage , /* NULL or "" may NOT contain \n nor \t */ + char const * const aDefaultInput ) /* "" , if NULL it's a passwordBox */ +{ + static char lBuff [MAX_PATH_OR_CMD] ; + char * lEOF; + +#ifndef TINYFD_NOLIB + DWORD mode = 0; + HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); +#endif /* TINYFD_NOLIB */ + + if ((!tinyfd_forceConsole || !( +#ifndef TINYFD_NOLIB + GetConsoleWindow() || +#endif /* TINYFD_NOLIB */ + dialogPresent())) + && ( !getenv("SSH_CLIENT") || getenv("DISPLAY") ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return (char const *)1;} + lBuff[0]='\0'; + return inputBoxWinGui(lBuff,aTitle,aMessage,aDefaultInput); + } + else if ( dialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)0;} + lBuff[0]='\0'; + return inputBoxWinConsole(lBuff,aTitle,aMessage,aDefaultInput); + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return (char const *)0;} + lBuff[0]='\0'; + if (!gWarningDisplayed && !tinyfd_forceConsole) + { + gWarningDisplayed = 1 ; + printf("\n\n%s", gAsciiArt); + printf("\n%s\n", gTitle); + printf("%s\n\n\n", gMessageWin); + } + if ( aTitle && strlen(aTitle) ) + { + printf ("%s\n\n", aTitle); + } + if ( aMessage && strlen(aMessage) ) + { + printf("%s\n",aMessage); + } + printf("(ctrl-Z + enter to cancel): "); +#ifndef TINYFD_NOLIB + if ( ! aDefaultInput ) + { + GetConsoleMode(hStdin,&mode); + SetConsoleMode(hStdin,mode & (~ENABLE_ECHO_INPUT) ); + } +#endif /* TINYFD_NOLIB */ + lEOF = fgets(lBuff, MAX_PATH_OR_CMD, stdin); + if ( ! lEOF ) + { + return NULL; + } +#ifndef TINYFD_NOLIB + if ( ! aDefaultInput ) + { + SetConsoleMode(hStdin,mode); + printf ("\n"); + } +#endif /* TINYFD_NOLIB */ + printf ("\n"); + if ( strchr(lBuff,27) ) + { + return NULL ; + } + if ( lBuff[strlen ( lBuff ) -1] == '\n' ) + { + lBuff[strlen ( lBuff ) -1] = '\0' ; + } + return lBuff ; + } +} + + +char const * tinyfd_saveFileDialog ( + char const * const aTitle , /* NULL or "" */ + char const * const aDefaultPathAndFile , /* NULL or "" */ + int const aNumOfFilterPatterns , /* 0 */ + char const * const * const aFilterPatterns , /* NULL or {"*.jpg","*.png"} */ + char const * const aSingleFilterDescription ) /* NULL or "image files" */ +{ + static char lBuff [MAX_PATH_OR_CMD] ; + char lString[MAX_PATH_OR_CMD] ; + char const * p ; + lBuff[0]='\0'; +#ifndef TINYFD_NOLIB + if ( ( !tinyfd_forceConsole || !( GetConsoleWindow() || dialogPresent() ) ) + && ( !getenv("SSH_CLIENT") || getenv("DISPLAY") ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return (char const *)1;} + if (tinyfd_winUtf8) + { + p = saveFileDialogWinGui8(lBuff, + aTitle, aDefaultPathAndFile, aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription); + } + else + { + p = saveFileDialogWinGuiA(lBuff, + aTitle, aDefaultPathAndFile, aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription); + } + } + else +#endif /* TINYFD_NOLIB */ + if ( dialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)0;} + p = saveFileDialogWinConsole(lBuff,aTitle,aDefaultPathAndFile); + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return (char const *)0;} + p = tinyfd_inputBox(aTitle, "Save file",""); + } + + 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 const * tinyfd_openFileDialog ( + char const * const aTitle , /* NULL or "" */ + char const * const aDefaultPathAndFile , /* NULL or "" */ + int const aNumOfFilterPatterns , /* 0 */ + char const * const * const aFilterPatterns , /* NULL or {"*.jpg","*.png"} */ + char const * const aSingleFilterDescription , /* NULL or "image files" */ + int const aAllowMultipleSelects ) /* 0 or 1 */ +{ + static char lBuff[MAX_MULTIPLE_FILES*MAX_PATH_OR_CMD]; + char const * p ; +#ifndef TINYFD_NOLIB + if ( ( !tinyfd_forceConsole || !( GetConsoleWindow() || dialogPresent() ) ) + && ( !getenv("SSH_CLIENT") || getenv("DISPLAY") ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return (char const *)1;} + if (tinyfd_winUtf8) + { + p = openFileDialogWinGui8(lBuff, + aTitle, aDefaultPathAndFile, aNumOfFilterPatterns, + aFilterPatterns, aSingleFilterDescription, aAllowMultipleSelects); + } + else + { + p = openFileDialogWinGuiA(lBuff, + aTitle, aDefaultPathAndFile, aNumOfFilterPatterns, + aFilterPatterns, aSingleFilterDescription, aAllowMultipleSelects); + } + } + else +#endif /* TINYFD_NOLIB */ + if ( dialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)0;} + p = openFileDialogWinConsole(lBuff, + aTitle,aDefaultPathAndFile,aAllowMultipleSelects); + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return (char const *)0;} + p = tinyfd_inputBox(aTitle, "Open file",""); + } + + if ( ! p || ! strlen ( p ) ) + { + return NULL; + } + if ( aAllowMultipleSelects && strchr(p, '|') ) + { + p = ensureFilesExist( lBuff , p ) ; + } + else if ( ! fileExists (p) ) + { + return NULL ; + } + /* printf ( "lBuff3: %s\n" , p ) ; //*/ + return p ; +} + + +char const * tinyfd_selectFolderDialog ( + char const * const aTitle , /* NULL or "" */ + char const * const aDefaultPath ) /* NULL or "" */ +{ + static char lBuff [MAX_PATH_OR_CMD] ; + char const * p ; +#ifndef TINYFD_NOLIB + if ( ( !tinyfd_forceConsole || !( GetConsoleWindow() || dialogPresent() ) ) + && ( !getenv("SSH_CLIENT") || getenv("DISPLAY") ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return (char const *)1;} + if (tinyfd_winUtf8) + { + p = selectFolderDialogWinGui8(lBuff, aTitle, aDefaultPath); + } + else + { + p = selectFolderDialogWinGuiA(lBuff, aTitle, aDefaultPath); + } + } + else +#endif /* TINYFD_NOLIB */ + if ( dialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)0;} + p = selectFolderDialogWinConsole(lBuff,aTitle,aDefaultPath); + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return (char const *)0;} + p = tinyfd_inputBox(aTitle, "Select folder",""); + } + + if ( ! p || ! strlen ( p ) || ! dirExists ( p ) ) + { + return NULL ; + } + return p ; +} + + +/* returns the hexcolor as a string "#FF0000" */ +/* aoResultRGB also contains the result */ +/* aDefaultRGB is used only if aDefaultHexRGB is NULL */ +/* aDefaultRGB and aoResultRGB can be the same array */ +char const * tinyfd_colorChooser( + char const * const aTitle, /* NULL or "" */ + char const * const aDefaultHexRGB, /* NULL or "#FF0000"*/ + unsigned char const aDefaultRGB[3], /* { 0 , 255 , 255 } */ + unsigned char aoResultRGB[3]) /* { 0 , 0 , 0 } */ +{ + char lDefaultHexRGB[8]; + char * lpDefaultHexRGB; + int i; + char const * p ; + +#ifndef TINYFD_NOLIB + if ( (!tinyfd_forceConsole || !( GetConsoleWindow() || dialogPresent()) ) + && (!getenv("SSH_CLIENT") || getenv("DISPLAY")) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return (char const *)1;} + if (tinyfd_winUtf8) + { + return colorChooserWinGui8( + aTitle, aDefaultHexRGB, aDefaultRGB, aoResultRGB); + } + else + { + return colorChooserWinGuiA( + aTitle, aDefaultHexRGB, aDefaultRGB, aoResultRGB); + } + } + else +#endif /* TINYFD_NOLIB */ + if ( aDefaultHexRGB ) + { + lpDefaultHexRGB = (char *) aDefaultHexRGB ; + } + else + { + RGB2Hex( aDefaultRGB , lDefaultHexRGB ) ; + lpDefaultHexRGB = (char *) lDefaultHexRGB ; + } + p = tinyfd_inputBox(aTitle, + "Enter hex rgb color (i.e. #f5ca20)",lpDefaultHexRGB); + if (aTitle&&!strcmp(aTitle,"tinyfd_query")) return p; + + if ( !p || (strlen(p) != 7) || (p[0] != '#') ) + { + return NULL ; + } + for ( i = 1 ; i < 7 ; i ++ ) + { + if ( ! isxdigit( p[i] ) ) + { + return NULL ; + } + } + Hex2RGB(p,aoResultRGB); + return p ; +} + +#else /* unix */ + +static char gPython2Name[16]; + +static int isDarwin ( ) +{ + static int lsIsDarwin = -1 ; + struct utsname lUtsname ; + if ( lsIsDarwin < 0 ) + { + lsIsDarwin = !uname(&lUtsname) && !strcmp(lUtsname.sysname,"Darwin") ; + } + return lsIsDarwin ; +} + + +static int dirExists ( char const * 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 * const aExecutable ) +{ + char lBuff [MAX_PATH_OR_CMD] ; + char lTestedString [MAX_PATH_OR_CMD] = "which " ; + FILE * lIn ; + + strcat ( lTestedString , aExecutable ) ; + lIn = popen ( lTestedString , "r" ) ; + if ( ( fgets ( lBuff , sizeof ( lBuff ) , lIn ) != NULL ) + && ( ! strchr ( lBuff , ':' ) ) ) + { /* present */ + pclose ( lIn ) ; + return 1 ; + } + else + { + pclose ( lIn ) ; + return 0 ; + } +} + + +static int tryCommand ( char const * const aCommand ) +{ + char lBuff [MAX_PATH_OR_CMD] ; + FILE * lIn ; + + lIn = popen ( aCommand , "r" ) ; + if ( fgets ( lBuff , sizeof ( lBuff ) , lIn ) == NULL ) + { /* present */ + pclose ( lIn ) ; + return 1 ; + } + else + { + pclose ( lIn ) ; + return 0 ; + } + +} + + +static char const * terminalName ( ) +{ + static char lTerminalName[64] = "*" ; + if ( lTerminalName[0] == '*' ) + { + if ( isDarwin() ) + { + if ( strcpy(lTerminalName , "/opt/X11/bin/xterm" ) + && detectPresence ( lTerminalName ) ) + { + strcat(lTerminalName , " -e bash -c " ) ; + } + else + { + strcpy(lTerminalName , "" ) ; + } + } + else if ( strcpy(lTerminalName,"terminator") /*good*/ + && detectPresence(lTerminalName) ) + { + strcat(lTerminalName , " -x bash -c " ) ; + } + else if ( strcpy(lTerminalName,"lxterminal") /*good*/ + && detectPresence(lTerminalName) ) + { + strcat(lTerminalName , " -e bash -c " ) ; + } + else if ( strcpy(lTerminalName,"mate-terminal") /*good*/ + && detectPresence(lTerminalName) ) + { + strcat(lTerminalName , " -x bash -c " ) ; + } + else if ( strcpy(lTerminalName,"konsole") + && detectPresence(lTerminalName) ) + { + strcat(lTerminalName , " -e bash -c " ) ; + } + else if ( strcpy(lTerminalName,"rxvt") /*good*/ + && detectPresence(lTerminalName) ) + { + strcat(lTerminalName , " -e bash -c " ) ; + } + else if ( strcpy(lTerminalName,"urxvt") /*good*/ + && detectPresence(lTerminalName) ) + { + strcat(lTerminalName , " -e bash -c " ) ; + } + else if ( strcpy(lTerminalName,"mrxvt") /*good*/ + && detectPresence(lTerminalName) ) + { + strcat(lTerminalName , " -e bash -c " ) ; + } + else if ( strcpy(lTerminalName,"evilvte") /*good*/ + && detectPresence(lTerminalName) ) + { + strcat(lTerminalName , " -e bash -c " ) ; + } + else if ( strcpy(lTerminalName,"termit") /*good*/ + && detectPresence(lTerminalName) ) + { + strcat(lTerminalName , " -e bash -c " ) ; + } + else if ( strcpy(lTerminalName,"kterm") /*good*/ + && detectPresence(lTerminalName) ) + { + strcat(lTerminalName , " -e bash -c " ) ; + } + else if ( strcpy(lTerminalName,"roxterm") /*good*/ + && detectPresence(lTerminalName) ) + { + strcat(lTerminalName , " -e bash -c " ) ; + } + else if ( strcpy(lTerminalName,"xterm") /*good small*/ + && detectPresence(lTerminalName) ) + { + strcat(lTerminalName , " -e bash -c " ) ; + } + else if ( strcpy(lTerminalName,"lxterm") /*good small*/ + && detectPresence(lTerminalName) ) + { + strcat(lTerminalName , " -e bash -c " ) ; + } + else if ( strcpy(lTerminalName,"xvt") /*good B&W*/ + && detectPresence(lTerminalName) ) + { + strcat(lTerminalName , " -e bash -c " ) ; + } + else if ( strcpy(lTerminalName,"pterm") /*good only letters*/ + && detectPresence(lTerminalName) ) + { + strcat(lTerminalName , " -e bash -c " ) ; + } + else if ( strcpy(lTerminalName,"x-terminal-emulator") /*alias*/ + && detectPresence(lTerminalName) ) + { + strcat(lTerminalName , " -e bash -c " ) ; + } + else if ( strcpy(lTerminalName,"$TERM") /*alias*/ + && detectPresence(lTerminalName) ) + { + strcat(lTerminalName , " -x bash -c " ) ; + } + else + { + strcpy(lTerminalName , "" ) ; + } + /* bad: koi8rxterm xfce4-terminal gnome-terminal guake tilda + vala-terminal Eterm aterm Terminal terminology sakura lilyterm*/ + } + if ( strlen(lTerminalName) ) + { + return lTerminalName ; + } + else + { + return NULL ; + } +} + + +static char const * dialogName ( ) +{ + static char lDialogName[64] = "*" ; + if ( lDialogName[0] == '*' ) + { + if ( isDarwin() && strcpy(lDialogName , "/opt/local/bin/dialog" ) + && detectPresence ( lDialogName ) ) + {} + else if ( strcpy(lDialogName , "dialog" ) + && detectPresence ( lDialogName ) ) + {} + else + { + strcpy(lDialogName , "" ) ; + } + } + if ( strlen(lDialogName) && ( isatty(1) || terminalName() ) ) + { + return lDialogName ; + } + else + { + return NULL ; + } +} + + +static int whiptailPresent ( ) +{ + static int lWhiptailPresent = -1 ; + if ( lWhiptailPresent < 0 ) + { + lWhiptailPresent = detectPresence ( "whiptail" ) ; + } + return lWhiptailPresent && ( isatty(1) || terminalName() ) ; +} + + +static int graphicMode() +{ + return !( tinyfd_forceConsole && (isatty(1) || terminalName()) ) + && ( getenv ( "DISPLAY" ) || isDarwin() ) ; +} + + +static int xmessagePresent ( ) +{ + static int lXmessagePresent = -1 ; + if ( lXmessagePresent < 0 ) + { + lXmessagePresent = detectPresence("xmessage");/*if not tty,not on osxpath*/ + } + return lXmessagePresent && graphicMode ( ) ; +} + + +static int gxmessagePresent ( ) +{ + static int lGxmessagePresent = -1 ; + if ( lGxmessagePresent < 0 ) + { + lGxmessagePresent = detectPresence("gxmessage") ; + } + return lGxmessagePresent && graphicMode ( ) ; +} + + +static int notifysendPresent ( ) +{ + static int lNotifysendPresent = -1 ; + if ( lNotifysendPresent < 0 ) + { + lNotifysendPresent = detectPresence("notify-send") ; + } + return lNotifysendPresent && graphicMode ( ) ; +} + + +static int xdialogPresent ( ) +{ + static int lXdialogPresent = -1 ; + if ( lXdialogPresent < 0 ) + { + lXdialogPresent = detectPresence("Xdialog") ; + } + return lXdialogPresent && graphicMode ( ) ; +} + + +static int gdialogPresent ( ) +{ + static int lGdialoglPresent = -1 ; + if ( lGdialoglPresent < 0 ) + { + lGdialoglPresent = detectPresence ( "gdialog" ) ; + } + return lGdialoglPresent && graphicMode ( ) ; +} + + +static int osascriptPresent ( ) +{ + static int lOsascriptPresent = -1 ; + if ( lOsascriptPresent < 0 ) + { + lOsascriptPresent = detectPresence ( "osascript" ) ; + } + return lOsascriptPresent && graphicMode ( ) ; +} + + +static int kdialogPresent ( ) +{ + static int lKdialogPresent = -1 ; + if ( lKdialogPresent < 0 ) + { + lKdialogPresent = detectPresence("kdialog") ; + } + return lKdialogPresent && graphicMode ( ) ; +} + + +static int matedialogPresent ( ) +{ + static int lMatedialogPresent = -1 ; + if ( lMatedialogPresent < 0 ) + { + lMatedialogPresent = detectPresence("matedialog") ; + } + return lMatedialogPresent && graphicMode ( ) ; +} + + +static int zenityPresent ( ) +{ + static int lZenityPresent = -1 ; + if ( lZenityPresent < 0 ) + { + lZenityPresent = detectPresence("zenity") ; + } + return lZenityPresent && graphicMode ( ) ; +} + + +static int osx9orBetter ( ) +{ + static int lOsx9orBetter = -1 ; + char lBuff [MAX_PATH_OR_CMD] ; + FILE * lIn ; + int V,v; + + if ( lOsx9orBetter < 0 ) + { + lOsx9orBetter = 0 ; + lIn = popen ( "osascript -e 'set osver to system version of (system info)'" , "r" ) ; + 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 ) ; + /* printf ("Osx10 = %d, %d = <%s>\n", lOsx9orBetter, V, lBuff) ; //*/ + } + return lOsx9orBetter ; +} + + + +static int zenity3Present ( ) +{ + static int lZenity3Present = -1 ; + char lBuff [MAX_PATH_OR_CMD] ; + FILE * lIn ; + + if ( lZenity3Present < 0 ) + { + if ( ! zenityPresent() ) + { + lZenity3Present = 0 ; + } + else + { + lIn = popen ( "zenity --version" , "r" ) ; + if ( ( fgets ( lBuff , sizeof ( lBuff ) , lIn ) != NULL ) + && ( atoi(lBuff) >= 3 ) + && ( atoi(strtok(lBuff,".")+1) >= 0 ) ) + { + lZenity3Present = 1 ; + } + else + { + lZenity3Present = 0 ; + } + pclose ( lIn ) ; + } + } + return lZenity3Present && graphicMode ( ) ; +} + + +static int tkinter2Present ( ) +{ + static int lTkinter2Present = -1 ; + char lPythonCommand[256]; + char lPythonParams[256] = +"-c \"try:\n\timport Tkinter;\nexcept:\n\tprint(0);\""; + int i; + + if ( lTkinter2Present < 0 ) + { + strcpy(gPython2Name , "python" ) ; + sprintf ( lPythonCommand , "%s %s" , gPython2Name , lPythonParams ) ; + lTkinter2Present = tryCommand(lPythonCommand); + if ( ! lTkinter2Present ) + { + strcpy(gPython2Name , "python2" ) ; + if ( detectPresence(gPython2Name) ) + { + sprintf ( lPythonCommand , "%s %s" , gPython2Name , lPythonParams ) ; + lTkinter2Present = tryCommand(lPythonCommand); + } + else + { + for ( i = 9 ; i >= 0 ; i -- ) + { + sprintf ( gPython2Name , "python2.%d" , i ) ; + if ( detectPresence(gPython2Name) ) + { + sprintf ( lPythonCommand , "%s %s" , gPython2Name , lPythonParams ) ; + lTkinter2Present = tryCommand(lPythonCommand); + break ; + } + } + } + } + } + /* printf ("gPython2Name %s\n", gPython2Name) ; //*/ + return lTkinter2Present && graphicMode ( ) ; +} + + +/* returns 0 for cancel/no , 1 for ok/yes */ +int tinyfd_messageBox ( + char const * const aTitle , /* NULL or "" */ + char const * const aMessage , /* NULL or "" may contain \n and \t */ + char const * const aDialogType , /* "ok" "okcancel" "yesno"*/ + char const * const aIconType , /* "info" "warning" "error" "question" */ + int const aDefaultButton ) /* 0 for cancel/no , 1 for ok/yes */ +{ + char lBuff [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; + int lTitleLen ; + int lMessageLen ; + + lBuff[0]='\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 ( 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 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 + { + strcat ( lDialogString ,"buttons {\"OK\"} " ) ; + strcat ( lDialogString ,"default button \"OK\" " ) ; + } + strcat ( lDialogString, "' ") ; + strcat ( lDialogString, "-e '1' " ); + 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 ( zenityPresent() || matedialogPresent() ) + { + if ( zenityPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return 1;} + strcpy ( lDialogString , "zenity --" ) ; + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return 1;} + strcpy ( lDialogString , "matedialog --" ) ; + } + + 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 ( 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, "\"") ; + } + if ( zenity3Present ( ) ) + { + strcat ( lDialogString , " --icon-name=dialog-" ) ; + if ( aIconType && (! strcmp( "question" , aIconType ) + || ! strcmp( "error" , aIconType ) + || ! strcmp( "warning" , aIconType ) ) ) + { + strcat ( lDialogString , aIconType ) ; + } + else + { + strcat ( lDialogString , "information" ) ; + } + } + strcat ( lDialogString , ";if [ $? = 0 ];then echo 1;else echo 0;fi"); + } + else if ( kdialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return 1;} + + strcpy ( lDialogString , "kdialog --" ) ; + if ( aDialogType && ( ! strcmp( "okcancel" , aDialogType ) + || ! strcmp( "yesno" , aDialogType ) ) ) + { + if ( aIconType && ( ! strcmp( "warning" , aIconType ) + || ! strcmp( "error" , aIconType ) ) ) + { + strcat ( lDialogString , "warning" ) ; + } + 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, "\"") ; + } + strcat ( lDialogString , ";if [ $? = 0 ];then echo 1;else echo 0;fi"); + } + else if ( ! xdialogPresent() && tkinter2Present ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"tkinter");return 1;} + + strcpy ( lDialogString , gPython2Name ) ; + if ( ! isatty ( 1 ) && isDarwin ( ) ) + { + strcat ( lDialogString , " -i" ) ; /* for osx without console */ + } + + strcat ( lDialogString , +" -c \"import Tkinter,tkMessageBox;root=Tkinter.Tk();root.withdraw();"); + + if ( 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 + { + 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); + replaceSubStr ( aMessage , "\n" , "\\n" , lpDialogString ) ; + //replaceSubStr ( aMessage , "\n" , "\\n" , lBuff ) ; + //strcat(lDialogString, lBuff) ; + //lBuff[0]='\0'; + strcat(lDialogString, "'") ; + } + strcat(lDialogString, ");\n\ +if res==False :\n\tprint 0\n\ +else :\n\tprint 1\n\"" ) ; + } + else if (!xdialogPresent() && !gdialogPresent() && gxmessagePresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"gxmessage");return 1;} + + strcpy ( lDialogString , "gxmessage"); + + if ( aDialogType && ! strcmp("okcancel" , aDialogType) ) + { + strcat ( lDialogString , " -buttons Ok:1,Cancel:0"); + } + else if ( aDialogType && ! strcmp("yesno" , aDialogType) ) + { + strcat ( lDialogString , " -buttons Yes:1,No:0"); + } + + 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() && notifysendPresent() + && strcmp("okcancel" , aDialogType) + && strcmp("yesno" , aDialogType) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"notify");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 (!xdialogPresent() && !gdialogPresent() && xmessagePresent() + && strcmp("okcancel" , aDialogType) + && strcmp("yesno" , aDialogType) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xmessage");return 1;} + + strcpy ( lDialogString , "xmessage -center \""); + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\n\n" ) ; + } + if ( aMessage && strlen(aMessage) ) + { + strcat(lDialogString, aMessage) ; + } + strcat(lDialogString, "\"" ) ; + } + else if ( xdialogPresent() || gdialogPresent() + || dialogName() || whiptailPresent() ) + { + if ( xdialogPresent ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xdialog");return 1;} + lWasGraphicDialog = 1 ; + strcpy ( lDialogString , "(Xdialog " ) ; + } + else if ( gdialogPresent ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"gdialog");return 1;} + lWasGraphicDialog = 1 ; + strcpy ( lDialogString , "(gdialog " ) ; + } + else if ( dialogName ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return 0;} + if ( isatty ( 1 ) ) + { + strcpy ( lDialogString , "(dialog " ) ; + } + else + { + lWasXterm = 1 ; + strcpy ( lDialogString , terminalName() ) ; + strcat ( lDialogString , "'(" ) ; + strcat ( lDialogString , dialogName() ) ; + strcat ( lDialogString , " " ) ; + } + } + else if ( isatty ( 1 ) ) + { + 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 ) ) ) + { + 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 + { + strcat ( lDialogString , "--msgbox " ) ; + + } + strcat ( lDialogString , "\"" ) ; + if ( aMessage && strlen(aMessage) ) + { + strcat(lDialogString, aMessage) ; + } + + if ( lWasGraphicDialog ) + { + strcat(lDialogString, + "\" 10 60 ) 2>&1;if [ $? = 0 ];then echo 1;else echo 0;fi"); + } + 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 ( ! isatty ( 1 ) && 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, gAsciiArt) ; + strcat ( lDialogString , " \";" ) ; + strcat ( lDialogString , "echo \"" ) ; + strcat ( lDialogString, gTitle) ; + strcat ( lDialogString , "\";" ) ; + strcat ( lDialogString , "echo \"" ) ; + strcat ( lDialogString, gMessageUnix) ; + 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 raw -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 raw -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 + { + strcat(lDialogString , "echo -n \"press enter to continue \"; "); + strcat ( lDialogString , "stty raw -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 (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return 0;} + if ( !gWarningDisplayed && !tinyfd_forceConsole) + { + gWarningDisplayed = 1 ; + printf("\n\n%s", gAsciiArt); + printf ("\n%s\n", gTitle); + printf ("%s\n\n\n", gMessageUnix); + } + if ( aTitle && strlen(aTitle) ) + { + printf ("%s\n\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("%s\n",aMessage); + } + printf("y/n: "); fflush(stdout); + lChar = 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("%s\n",aMessage); + } + printf("[O]kay/[C]ancel: "); fflush(stdout); + lChar = tolower ( getchar() ) ; + printf("\n\n"); + } + while ( lChar != 'o' && lChar != 'c' ); + lResult = lChar == 'o' ? 1 : 0 ; + } + else + { + if ( aMessage && strlen(aMessage) ) + { + printf("%s\n\n",aMessage); + } + printf("press enter to continue "); fflush(stdout); + getchar() ; + printf("\n\n"); + lResult = 1 ; + } + tcsetattr(0, TCSANOW, &infoOri); + free(lDialogString); + return lResult ; + } + + /* printf ( "lDialogString: %s\n" , lDialogString ) ; //*/ + if ( ! ( lIn = popen ( lDialogString , "r" ) ) ) + { + free(lDialogString); + return 0 ; + } + while ( fgets ( lBuff , sizeof ( lBuff ) , lIn ) != NULL ) + {} + + pclose ( lIn ) ; + + /* printf ( "lBuff: %s len: %lu \n" , lBuff , strlen(lBuff) ) ; //*/ + if ( lBuff[strlen ( lBuff ) -1] == '\n' ) + { + lBuff[strlen ( lBuff ) -1] = '\0' ; + } + /* printf ( "lBuff1: %s len: %lu \n" , lBuff , strlen(lBuff) ) ; //*/ + + lResult = strcmp ( lBuff , "1" ) ? 0 : 1 ; + /* printf ( "lResult: %d\n" , lResult ) ; //*/ + free(lDialogString); + return lResult ; +} + + +/* returns NULL on cancel */ +char const * tinyfd_inputBox( + char const * const aTitle , /* NULL or "" */ + char const * const aMessage , /* NULL or "" may NOT contain \n nor \t */ + char const * 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; + int lTitleLen ; + int lMessageLen ; + + lBuff[0]='\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 ( osascriptPresent ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return (char const *)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 ( zenityPresent() || matedialogPresent() ) + { + if ( zenityPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return (char const *)1;} + strcpy ( lDialogString , "szAnswer=$(zenity --entry" ) ; + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return (char const *)1;} + strcpy ( lDialogString , "szAnswer=$(matedialog --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") ; + } + strcat ( lDialogString , + ");if [ $? = 0 ];then echo 1$szAnswer;else echo 0$szAnswer;fi"); + } + else if ( kdialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return (char const *)1;} + strcpy ( lDialogString , "szAnswer=$(kdialog" ) ; + 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 ( ! xdialogPresent() && tkinter2Present ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"tkinter");return (char const *)1;} + strcpy ( lDialogString , gPython2Name ) ; + if ( ! isatty ( 1 ) && isDarwin ( ) ) + { + strcat ( lDialogString , " -i" ) ; /* for osx without console */ + } + + strcat ( lDialogString , +" -c \"import Tkinter,tkSimpleDialog;root=Tkinter.Tk();root.withdraw();"); + + if ( 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); + replaceSubStr ( aMessage , "\n" , "\\n" , lpDialogString ) ; + //replaceSubStr ( aMessage , "\n" , "\\n" , lBuff ) ; + //strcat(lDialogString, lBuff) ; + //lBuff[0]='\0'; + 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 (!xdialogPresent() && !gdialogPresent() && gxmessagePresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"gxmessage");return (char const *)1;} + strcpy ( lDialogString , "szAnswer=$(gxmessage -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 ( xdialogPresent() || gdialogPresent() + || dialogName() || whiptailPresent() ) + { + if ( xdialogPresent ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xdialog");return (char const *)1;} + lWasGraphicDialog = 1 ; + strcpy ( lDialogString , "(Xdialog " ) ; + } + else if ( gdialogPresent ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"gdialog");return (char const *)1;} + lWasGraphicDialog = 1 ; + lWasGdialog = 1 ; + strcpy ( lDialogString , "(gdialog " ) ; + } + else if ( dialogName ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)0;} + if ( isatty ( 1 ) ) + { + strcpy ( lDialogString , "(dialog " ) ; + } + else + { + lWasXterm = 1 ; + strcpy ( lDialogString , terminalName() ) ; + strcat ( lDialogString , "'(" ) ; + strcat ( lDialogString , dialogName() ) ; + strcat ( lDialogString , " " ) ; + } + } + else if ( isatty ( 1 ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"whiptail");return (char const *)0;} + strcpy ( lDialogString , "(whiptail " ) ; + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"whiptail");return (char const *)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") ; + strcat(lDialogString, "\" ") ; + } + + if ( aDefaultInput || lWasGdialog ) + { + strcat ( lDialogString , "--inputbox" ) ; + } + else + { + 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);\ + rm /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);\ + rm /tmp/tinyfd.txt;echo $tinyfdBool$tinyfdRes") ; + if ( lWasXterm ) + { + strcat ( lDialogString , + " >/tmp/tinyfd0.txt';cat /tmp/tinyfd0.txt;rm /tmp/tinyfd0.txt"); + } + else + { + strcat(lDialogString, "; clear >/dev/tty") ; + } + } + } + else if ( ! isatty ( 1 ) && terminalName() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return (char const *)0;} + lWasBasicXterm = 1 ; + strcpy ( lDialogString , terminalName() ) ; + strcat ( lDialogString , "'" ) ; + if ( !gWarningDisplayed && !tinyfd_forceConsole) + { + gWarningDisplayed = 1 ; + strcat ( lDialogString , "echo \"" ) ; + strcat ( lDialogString, gAsciiArt) ; + strcat ( lDialogString , "\";" ) ; + strcat ( lDialogString , "echo \"" ) ; + strcat ( lDialogString, gTitle) ; + strcat ( lDialogString , "\";" ) ; + strcat ( lDialogString , "echo \"" ) ; + strcat ( lDialogString, gMessageUnix) ; + strcat ( lDialogString , "\";echo;echo;" ) ; + } + 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;rm /tmp/tinyfd.txt"); + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return (char const *)0;} + if ( !gWarningDisplayed && !tinyfd_forceConsole) + { + gWarningDisplayed = 1 ; + printf ("\n\n%s", gAsciiArt); + printf ("\n%s\n", gTitle); + printf ("%s\n\n\n", gMessageUnix); + } + if ( aTitle && strlen(aTitle) ) + { + printf ("%s\n\n", aTitle); + } + if ( aMessage && strlen(aMessage) ) + { + printf("%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 ; + } + + /* printf ( "lDialogString: %s\n" , lDialogString ) ; //*/ + if ( ! ( lIn = popen ( lDialogString , "r" ) ) ) + { + free(lDialogString); + return NULL ; + } + while ( fgets ( lBuff , sizeof ( lBuff ) , lIn ) != NULL ) + {} + + pclose ( lIn ) ; + + /* printf ( "len Buff: %lu\n" , strlen(lBuff) ) ; //*/ + /* printf ( "lBuff0: %s\n" , lBuff ) ; //*/ + if ( 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 const * tinyfd_saveFileDialog ( + char const * const aTitle , /* NULL or "" */ + char const * const aDefaultPathAndFile , /* NULL or "" */ + int const aNumOfFilterPatterns , /* 0 */ + char const * const * const aFilterPatterns , /* NULL or {"*.jpg","*.png"} */ + char const * const aSingleFilterDescription ) /* NULL or "image files" */ +{ + + static char lBuff [MAX_PATH_OR_CMD] ; + char lDialogString [MAX_PATH_OR_CMD] ; + char lString [MAX_PATH_OR_CMD] ; + int i ; + int lWasGraphicDialog = 0 ; + int lWasXterm = 0 ; + char const * p ; + FILE * lIn ; + lBuff[0]='\0'; + + if ( osascriptPresent ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return (char const *)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 , aDefaultPathAndFile ) ; + if ( strlen(lString) ) + { + strcat(lDialogString, "default location \"") ; + strcat(lDialogString, lString ) ; + strcat(lDialogString , "\" " ) ; + } + getLastName ( lString , aDefaultPathAndFile ) ; + 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 ( zenityPresent() || matedialogPresent() ) + { + if ( zenityPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return (char const *)1;} + strcpy ( lDialogString , "zenity" ) ; + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return (char const *)1;} + strcpy ( lDialogString , "matedialog" ) ; + + } + strcat(lDialogString, " --file-selection --save --confirm-overwrite" ) ; + + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, " --title=\"") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\"") ; + } + if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) + { + strcat(lDialogString, " --filename=\"") ; + strcat(lDialogString, aDefaultPathAndFile) ; + strcat(lDialogString, "\"") ; + } + if ( aNumOfFilterPatterns > 0 ) + { + strcat ( lDialogString , " --file-filter='" ) ; + if ( aSingleFilterDescription && strlen(aSingleFilterDescription) ) + { + strcat ( lDialogString , aSingleFilterDescription ) ; + strcat ( lDialogString , " | " ) ; + } + for ( i = 0 ; i < aNumOfFilterPatterns ; i ++ ) + { + strcat ( lDialogString , aFilterPatterns [i] ) ; + strcat ( lDialogString , " " ) ; + } + strcat ( lDialogString , "' --file-filter='All files | *'" ) ; + } + } + else if ( kdialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return (char const *)1;} + strcpy ( lDialogString , "kdialog --getsavefilename" ) ; + if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) + { + strcat(lDialogString, " \"") ; + strcat(lDialogString, aDefaultPathAndFile ) ; + strcat(lDialogString , "\"" ) ; + } + else + { + strcat(lDialogString, " :" ) ; + } + if ( aNumOfFilterPatterns > 0 ) + { + strcat(lDialogString , " \"" ) ; + for ( i = 0 ; i < aNumOfFilterPatterns ; i ++ ) + { + strcat ( lDialogString , aFilterPatterns [i] ) ; + strcat ( lDialogString , " " ) ; + } + 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 ( ! xdialogPresent() && tkinter2Present ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"tkinter");return (char const *)1;} + strcpy ( lDialogString , gPython2Name ) ; + if ( ! isatty ( 1 ) && isDarwin ( )) + { + strcat ( lDialogString , " -i" ) ; /* for osx without console */ + } + strcat ( lDialogString , +" -c \"import Tkinter,tkFileDialog;root=Tkinter.Tk();root.withdraw();"); + + if ( 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.asksaveasfilename("); + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, "title='") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "',") ; + } + if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) + { + getPathWithoutFinalSlash ( lString , aDefaultPathAndFile ) ; + if ( strlen(lString) ) + { + strcat(lDialogString, "initialdir='") ; + strcat(lDialogString, lString ) ; + strcat(lDialogString , "'," ) ; + } + getLastName ( lString , aDefaultPathAndFile ) ; + 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 , ")\"" ) ; + } + else if ( xdialogPresent() || dialogName() ) + { + if ( xdialogPresent ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xdialog");return (char const *)1;} + lWasGraphicDialog = 1 ; + strcpy ( lDialogString , "(Xdialog " ) ; + } + else if ( isatty ( 1 ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)0;} + strcpy ( lDialogString , "@echo lala;(dialog " ) ; + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)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 | spacebar -> select | / -> populate | enter -> ok input line") ; + strcat(lDialogString, "\" ") ; + } + + strcat ( lDialogString , "--fselect \"" ) ; + if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) + { + if ( ! strchr(aDefaultPathAndFile, '/') ) + { + strcat(lDialogString, "./") ; + } + strcat(lDialogString, aDefaultPathAndFile) ; + } + else if ( ! isatty ( 1 ) && !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);} + p = tinyfd_inputBox ( aTitle , "Save file" , "" ) ; + getPathWithoutFinalSlash ( lString , p ) ; + if ( strlen ( lString ) && ! dirExists ( lString ) ) + { + return NULL ; + } + getLastName(lString,p); + if ( ! strlen(lString) ) + { + return NULL; + } + return p ; + } + + /* printf ( "lDialogString: %s\n" , lDialogString ) ; //*/ + if ( ! ( lIn = popen ( lDialogString , "r" ) ) ) + { + return NULL ; + } + while ( fgets ( lBuff , sizeof ( lBuff ) , lIn ) != NULL ) + {} + pclose ( lIn ) ; + if ( 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 ; + } + getLastName(lString,lBuff); + if ( ! filenameValid(lString) ) + { + return NULL; + } + return lBuff ; +} + + +/* in case of multiple files, the separator is | */ +char const * tinyfd_openFileDialog ( + char const * const aTitle , /* NULL or "" */ + char const * const aDefaultPathAndFile , /* NULL or "" */ + int const aNumOfFilterPatterns , /* 0 */ + char const * const * const aFilterPatterns , /* NULL or {"*.jpg","*.png"} */ + char const * const aSingleFilterDescription , /* NULL or "image files" */ + int const aAllowMultipleSelects ) /* 0 or 1 */ +{ + static char lBuff [MAX_MULTIPLE_FILES*MAX_PATH_OR_CMD] ; + char lDialogString [MAX_PATH_OR_CMD] ; + char lString [MAX_PATH_OR_CMD] ; + int i ; + FILE * lIn ; + char * p ; + char const * p2 ; + int lWasKdialog = 0 ; + int lWasGraphicDialog = 0 ; + int lWasXterm = 0 ; + lBuff[0]='\0'; + + if ( osascriptPresent ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return (char const *)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 , aDefaultPathAndFile ) ; + 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 ( zenityPresent() || matedialogPresent() ) + { + if ( zenityPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return (char const *)1;} + strcpy ( lDialogString , "zenity --file-selection" ) ; + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return (char const *)1;} + strcpy ( lDialogString , "matedialog --file-selection" ) ; + } + + if ( aAllowMultipleSelects ) + { + strcat ( lDialogString , " --multiple" ) ; + } + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, " --title=\"") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\"") ; + } + if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) + { + strcat(lDialogString, " --filename=\"") ; + strcat(lDialogString, aDefaultPathAndFile) ; + strcat(lDialogString, "\"") ; + } + if ( aNumOfFilterPatterns > 0 ) + { + strcat ( lDialogString , " --file-filter='" ) ; + if ( aSingleFilterDescription && strlen(aSingleFilterDescription) ) + { + strcat ( lDialogString , aSingleFilterDescription ) ; + strcat ( lDialogString , " | " ) ; + } + for ( i = 0 ; i < aNumOfFilterPatterns ; i ++ ) + { + strcat ( lDialogString , aFilterPatterns [i] ) ; + strcat ( lDialogString , " " ) ; + } + strcat ( lDialogString , "' --file-filter='All files | *'" ) ; + } + } + else if ( kdialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return (char const *)1;} + lWasKdialog = 1 ; + strcpy ( lDialogString , "kdialog --getopenfilename" ) ; + if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) + { + strcat(lDialogString, " \"") ; + strcat(lDialogString, aDefaultPathAndFile ) ; + + strcat(lDialogString , "\"" ) ; + } + else + { + strcat(lDialogString, " :" ) ; + } + if ( aNumOfFilterPatterns > 0 ) + { + strcat(lDialogString , " \"" ) ; + for ( i = 0 ; i < aNumOfFilterPatterns ; i ++ ) + { + strcat ( lDialogString , aFilterPatterns [i] ) ; + strcat ( lDialogString , " " ) ; + } + 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 ( ! xdialogPresent() && tkinter2Present ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"tkinter");return (char const *)1;} + strcpy ( lDialogString , gPython2Name ) ; + if ( ! isatty ( 1 ) && isDarwin ( ) ) + { + strcat ( lDialogString , " -i" ) ; /* for osx without console */ + } + strcat ( lDialogString , +" -c \"import Tkinter,tkFileDialog;root=Tkinter.Tk();root.withdraw();"); + + if ( 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 ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) + { + getPathWithoutFinalSlash ( lString , aDefaultPathAndFile ) ; + if ( strlen(lString) ) + { + strcat(lDialogString, "initialdir='") ; + strcat(lDialogString, lString ) ; + strcat(lDialogString , "'," ) ; + } + getLastName ( lString , aDefaultPathAndFile ) ; + 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 const *)1;} + lWasGraphicDialog = 1 ; + strcpy ( lDialogString , "(Xdialog " ) ; + } + else if ( isatty ( 1 ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)0;} + strcpy ( lDialogString , "(dialog " ) ; + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)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 | spacebar -> select | / -> populate | enter -> ok input line") ; + strcat(lDialogString, "\" ") ; + } + + strcat ( lDialogString , "--fselect \"" ) ; + if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) + { + if ( ! strchr(aDefaultPathAndFile, '/') ) + { + strcat(lDialogString, "./") ; + } + strcat(lDialogString, aDefaultPathAndFile) ; + } + else if ( ! isatty ( 1 ) && !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);} + p2 = tinyfd_inputBox(aTitle, "Open file",""); + if ( ! fileExists (p2) ) + { + return NULL ; + } + return p2 ; + } + + /* printf ( "lDialogString: %s\n" , lDialogString ) ; //*/ + if ( ! ( lIn = popen ( lDialogString , "r" ) ) ) + { + return NULL ; + } + lBuff[0]='\0'; + p=lBuff; + while ( fgets ( p , sizeof ( lBuff ) , lIn ) != NULL ) + { + p += strlen ( p ); + } + pclose ( lIn ) ; + if ( lBuff[strlen ( lBuff ) -1] == '\n' ) + { + lBuff[strlen ( lBuff ) -1] = '\0' ; + } + /* printf ( "lBuff: %s\n" , lBuff ) ; //*/ + if ( lWasKdialog && aAllowMultipleSelects ) + { + p = lBuff ; + while ( ( p = strchr ( p , '\n' ) ) ) + * p = '|' ; + } + /* printf ( "lBuff2: %s\n" , lBuff ) ; //*/ + if ( ! strlen ( lBuff ) ) + { + return NULL; + } + if ( aAllowMultipleSelects && strchr(lBuff, '|') ) + { + p2 = ensureFilesExist( lBuff , lBuff ) ; + } + else if ( fileExists (lBuff) ) + { + p2 = lBuff ; + } + else + { + return NULL ; + } + /* printf ( "lBuff3: %s\n" , p2 ) ; //*/ + + return p2 ; +} + + +char const * tinyfd_selectFolderDialog ( + char const * const aTitle , /* "" */ + char const * const aDefaultPath ) /* "" */ +{ + static char lBuff [MAX_PATH_OR_CMD] ; + char lDialogString [MAX_PATH_OR_CMD] ; + FILE * lIn ; + char const * p ; + int lWasGraphicDialog = 0 ; + int lWasXterm = 0 ; + lBuff[0]='\0'; + + if ( osascriptPresent ( )) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return (char const *)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 ( zenityPresent() || matedialogPresent() ) + { + if ( zenityPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return (char const *)1;} + strcpy ( lDialogString , "zenity --file-selection --directory" ) ; + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return (char const *)1;} + strcpy ( lDialogString , "matedialog --file-selection --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, "\"") ; + } + } + else if ( kdialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return (char const *)1;} + strcpy ( lDialogString , "kdialog --getexistingdirectory" ) ; + if ( aDefaultPath && strlen(aDefaultPath) ) + { + strcat(lDialogString, " \"") ; + strcat(lDialogString, aDefaultPath ) ; + strcat(lDialogString , "\"" ) ; + } + else + { + strcat(lDialogString, " :" ) ; + } + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, " --title \"") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\"") ; + } + } + else if ( ! xdialogPresent() && tkinter2Present ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"tkinter");return (char const *)1;} + strcpy ( lDialogString , gPython2Name ) ; + if ( ! isatty ( 1 ) && isDarwin ( ) ) + { + strcat ( lDialogString , " -i" ) ; /* for osx without console */ + } + strcat ( lDialogString , +" -c \"import Tkinter,tkFileDialog;root=Tkinter.Tk();root.withdraw();"); + + if ( 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 const *)1;} + lWasGraphicDialog = 1 ; + strcpy ( lDialogString , "(Xdialog " ) ; + } + else if ( isatty ( 1 ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)0;} + strcpy ( lDialogString , "(dialog " ) ; + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)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 | spacebar -> select | / -> populate | enter -> ok input line") ; + strcat(lDialogString, "\" ") ; + } + + strcat ( lDialogString , "--dselect \"" ) ; + if ( aDefaultPath && strlen(aDefaultPath) ) + { + strcat(lDialogString, aDefaultPath) ; + ensureFinalSlash(lDialogString); + } + else if ( ! isatty ( 1 ) && !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);} + p = tinyfd_inputBox(aTitle, "Select folder",""); + if ( !p || ! strlen ( p ) || ! dirExists ( p ) ) + { + return NULL ; + } + return p ; + } + /* printf ( "lDialogString: %s\n" , lDialogString ) ; //*/ + if ( ! ( lIn = popen ( lDialogString , "r" ) ) ) + { + return NULL ; + } + while ( fgets ( lBuff , sizeof ( lBuff ) , lIn ) != NULL ) + {} + pclose ( lIn ) ; + if ( lBuff[strlen ( lBuff ) -1] == '\n' ) + { + lBuff[strlen ( lBuff ) -1] = '\0' ; + } + /* printf ( "lBuff: %s\n" , lBuff ) ; //*/ + if ( ! strlen ( lBuff ) || ! dirExists ( lBuff ) ) + { + return NULL ; + } + return lBuff ; +} + + +/* returns the hexcolor as a string "#FF0000" */ +/* aoResultRGB also contains the result */ +/* aDefaultRGB is used only if aDefaultHexRGB is NULL */ +/* aDefaultRGB and aoResultRGB can be the same array */ +char const * tinyfd_colorChooser( + char const * const aTitle , /* NULL or "" */ + char const * const aDefaultHexRGB , /* NULL or "#FF0000"*/ + unsigned char const aDefaultRGB[3] , /* { 0 , 255 , 255 } */ + unsigned char aoResultRGB[3] ) /* { 0 , 0 , 0 } */ +{ + static char lBuff [16] ; + char lTmp [16] ; + char lDialogString [MAX_PATH_OR_CMD] ; + char lDefaultHexRGB[8]; + char * lpDefaultHexRGB; + unsigned char lDefaultRGB[3]; + char const * p; + FILE * lIn ; + int i ; + int lWasZenity3 = 0 ; + int lWasOsascript = 0 ; + int lWasXdialog = 0 ; + lBuff[0]='\0'; + + if ( aDefaultHexRGB ) + { + Hex2RGB ( aDefaultHexRGB , lDefaultRGB ) ; + lpDefaultHexRGB = (char *) aDefaultHexRGB ; + } + else + { + lDefaultRGB[0]=aDefaultRGB[0]; + lDefaultRGB[1]=aDefaultRGB[1]; + lDefaultRGB[2]=aDefaultRGB[2]; + RGB2Hex( aDefaultRGB , lDefaultHexRGB ) ; + lpDefaultHexRGB = (char *) lDefaultHexRGB ; + } + + if ( osascriptPresent ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return (char const *)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)/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)/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 ( zenity3Present() || matedialogPresent() ) + { + lWasZenity3 = 1 ; + if ( zenity3Present() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity3");return (char const *)1;} + sprintf ( lDialogString , +"zenity --color-selection --show-palette --color=%s" , lpDefaultHexRGB ) ; + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return (char const *)1;} + sprintf ( lDialogString , +"matedialog --color-selection --show-palette --color=%s" , lpDefaultHexRGB ) ; + } + + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, " --title=\"") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\"") ; + } + } + else if ( kdialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return (char const *)1;} + sprintf ( lDialogString , +"kdialog --getcolor --default '%s'" , lpDefaultHexRGB ) ; + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, " --title \"") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\"") ; + } + } + else if ( xdialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xdialog");return (char const *)1;} + lWasXdialog = 1 ; + strcpy ( lDialogString , "Xdialog --colorsel \"" ) ; + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, aTitle) ; + } + strcat(lDialogString, "\" 0 60 ") ; + sprintf(lTmp,"%hhu %hhu %hhu",lDefaultRGB[0], + lDefaultRGB[1],lDefaultRGB[2]); + strcat(lDialogString, lTmp) ; + strcat(lDialogString, " 2>&1"); + } + else if ( tkinter2Present ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"tkinter");return (char const *)1;} + strcpy ( lDialogString , gPython2Name ) ; + if ( ! isatty ( 1 ) && isDarwin ( ) ) + { + strcat ( lDialogString , " -i" ) ; /* for osx without console */ + } + + strcat ( lDialogString , +" -c \"import Tkinter,tkColorChooser;root=Tkinter.Tk();root.withdraw();"); + + if ( 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, lpDefaultHexRGB ) ; + 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);} + p = tinyfd_inputBox(aTitle, + "Enter hex rgb color (i.e. #f5ca20)",lpDefaultHexRGB); + if ( !p || (strlen(p) != 7) || (p[0] != '#') ) + { + return NULL ; + } + for ( i = 1 ; i < 7 ; i ++ ) + { + if ( ! isxdigit( p[i] ) ) + { + return NULL ; + } + } + Hex2RGB(p,aoResultRGB); + return p ; + } + + /* printf ( "lDialogString: %s\n" , lDialogString ) ; //*/ + if ( ! ( lIn = popen ( lDialogString , "r" ) ) ) + { + 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 ( lWasZenity3 ) + { + lBuff[3]=lBuff[5]; + lBuff[4]=lBuff[6]; + lBuff[5]=lBuff[9]; + lBuff[6]=lBuff[10]; + lBuff[7]='\0'; + Hex2RGB(lBuff,aoResultRGB); + } + else if ( lWasOsascript || lWasXdialog ) + { + sscanf(lBuff,"%hhu %hhu %hhu", + & aoResultRGB[0], & aoResultRGB[1],& aoResultRGB[2]); + RGB2Hex(aoResultRGB,lBuff); + } + else + { + Hex2RGB(lBuff,aoResultRGB); + } + /* printf("%d %d %d\n", aoResultRGB[0],aoResultRGB[1],aoResultRGB[2]); //*/ + /* printf ( "lBuff: %s\n" , lBuff ) ; //*/ + return lBuff ; +} + +/* not cross platform - zenity only */ +/* contributed by Attila Dusnoki */ +char const * tinyfd_arrayDialog ( + char const * const aTitle , /* "" */ + int const aNumOfColumns , /* 2 */ + char const * const * const aColumns , /* {"Column 1","Column 2"} */ + int const aNumOfRows , /* 2 */ + char const * const * const aCells ) + /* {"Row1 Col1","Row1 Col2","Row2 Col1","Row2 Col2"} */ +{ + static char lBuff [MAX_PATH_OR_CMD] ; + char lDialogString [MAX_PATH_OR_CMD] ; + FILE * lIn ; + lBuff[0]='\0'; + int i ; + + if ( zenityPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return (char const *)1;} + strcpy ( lDialogString , "zenity --list --print-column=ALL" ) ; + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, " --title=\"") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\"") ; + } + + if ( aColumns && (aNumOfColumns > 0) ) + { + for ( i = 0 ; i < aNumOfColumns ; i ++ ) + { + strcat ( lDialogString , " --column=\"" ) ; + strcat ( lDialogString , aColumns [i] ) ; + strcat ( lDialogString , "\"" ) ; + } + } + + if ( aCells && (aNumOfRows > 0) ) + { + strcat ( lDialogString , " " ) ; + for ( i = 0 ; i < aNumOfRows*aNumOfColumns ; i ++ ) + { + strcat ( lDialogString , "\"" ) ; + strcat ( lDialogString , aCells [i] ) ; + strcat ( lDialogString , "\" " ) ; + } + } + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"");return (char const *)0;} + return NULL ; + } + + /* printf ( "lDialogString: %s\n" , lDialogString ) ; //*/ + if ( ! ( lIn = popen ( lDialogString , "r" ) ) ) + { + return NULL ; + } + while ( fgets ( lBuff , sizeof ( lBuff ) , lIn ) != NULL ) + {} + pclose ( lIn ) ; + if ( lBuff[strlen ( lBuff ) -1] == '\n' ) + { + lBuff[strlen ( lBuff ) -1] = '\0' ; + } + /* printf ( "lBuff: %s\n" , lBuff ) ; //*/ + if ( ! strlen ( lBuff ) ) + { + return NULL ; + } + return lBuff ; +} +#endif /* _WIN32 */ + + +/* +int main(void) +{ +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 lThePassword[1024]; +char const * lFilterPatterns[2] = { "*.txt", "*.text" }; + +lWillBeGraphicMode = tinyfd_inputBox("tinyfd_query", NULL, NULL); + +if (lWillBeGraphicMode) +{ + strcpy(lBuffer, "graphic mode: "); +} +else +{ + strcpy(lBuffer, "console mode: "); +} + +strcat(lBuffer, tinyfd_response); +strcpy(lThePassword, "tinyfiledialogs v"); +strcat(lThePassword, tinyfd_version); +tinyfd_messageBox(lThePassword, lBuffer, "ok", "info", 0); + +if (lWillBeGraphicMode && !tinyfd_forceConsole) +{ + tinyfd_forceConsole = tinyfd_messageBox("Hello World", + "force dialogs into console mode?\ + \n\t(it is better if dialog is installed)", + "yesno", "question", 0); +} + +lTmp = tinyfd_inputBox( + "a password box", "your password will be revealed", NULL); + +if (!lTmp) return 1; + +strcpy(lThePassword, 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(lThePassword, 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); + + return 0; +} +//*/ + +#ifdef _MSC_VER +#pragma warning(default:4996) +#pragma warning(default:4100) +#pragma warning(default:4706) +#endif diff --git a/tools/rFXGen/external/tinyfiledialogs.h b/tools/rFXGen/external/tinyfiledialogs.h new file mode 100644 index 0000000..49844e8 --- /dev/null +++ b/tools/rFXGen/external/tinyfiledialogs.h @@ -0,0 +1,289 @@ +/* + _________ +/ \ tinyfiledialogs.h v2.5.8 [September 13, 2016] zlib licence +|tiny file| Unique header file of "tiny file dialogs" created [November 9, 2014] +| dialogs | Copyright (c) 2014 - 2016 Guillaume Vareille http://ysengrin.com +\____ ___/ http://tinyfiledialogs.sourceforge.net + \| mailto:tinyfiledialogs@ysengrin.com + +A big thank you to Don Heyse http://ldglite.sf.net for + his code contributions, bug corrections & thorough testing! + + git://git.code.sf.net/p/tinyfiledialogs/code + +Please + 1) let me know + - if you are including tiny file dialogs, + I'll be happy to add your link to the list of projects using it. + - If you are using it on different hardware / OS / compiler. + 2) Be the first to leave a review on Sourceforge. Thanks. + +tiny file dialogs (cross-platform C C++) +InputBox PasswordBox MessageBox ColorPicker +OpenFileDialog SaveFileDialog SelectFolderDialog +Native dialog library for WINDOWS MAC OSX GTK+ QT CONSOLE & more + +A single C file (add it to your C or C++ project) with 6 boxes: +- message / question +- input / password +- save file +- open file & multiple files +- select folder +- color picker. + +Complements OpenGL GLFW GLUT GLUI VTK SFML SDL Ogre Unity ION +CEGUI MathGL CPW GLOW IMGUI GLT NGL STB & GUI less programs + +NO INIT +NO MAIN LOOP + +The dialogs can be forced into console mode + +Windows [MBCS + UTF-8 + UTF-16] +- native code & some vbs create the graphic dialogs +- enhanced console mode can use dialog.exe from +http://andrear.altervista.org/home/cdialog.php +- basic console input + +Unix [UTF-8] (command line call attempts) +- applescript +- zenity / matedialog +- kdialog +- Xdialog +- python2 tkinter +- dialog (opens a console if needed) +- basic console input +The same executable can run across desktops & distributions + +tested with C & C++ compilers +on VisualStudio MinGW Mac Linux Bsd Solaris Minix Raspbian C# fortran (iso_c) +using Gnome Kde Enlightenment Mate Cinnamon Unity +Lxde Lxqt Xfce WindowMaker IceWm Cde Jds OpenBox + +- 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. +*/ + +#ifndef TINYFILEDIALOGS_H +#define TINYFILEDIALOGS_H + +/* #define TINYFD_NOLIB //*/ +/* On windows, define TINYFD_NOLIB here +if you don't want to include the code creating the graphic dialogs. +Then you won't need to link against Comdlg32.lib and Ole32.lib */ + +/* if tinydialogs.c is compiled with a C++ compiler rather than with a C compiler +(ie. you change the extension from .c to .cpp), you need to comment out: +extern "C" { +and the corresponding closing bracket near the end of this file: +} +*/ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +extern char tinyfd_version[8]; /* contains tinyfd current version number */ + +#ifdef _WIN32 +/* tinyfd_winUtf8 is not ready yet, do not modify */ +extern int tinyfd_winUtf8; /* 0 (default) or 1 */ +/* on windows string char can be 0:MBSC or 1:UTF-8 (work in progress) +unless your code is really prepared for it, leave this on MBSC. +for UTF-16 choose the functions at the end of this files */ +#endif + +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 the X server is present, + if the package dialog (and a console is present) or dialog.exe is installed. + on windows it only make sense for console applications */ + +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 the graphic mode: + windows applescript zenity zenity3 matedialog kdialog + xdialog tkinter gdialog gxmessage xmessage +for the console mode: + dialog whiptail basicinput */ + +int tinyfd_messageBox ( + char const * const aTitle , /* "" */ + char const * const aMessage , /* "" may contain \n \t */ + char const * const aDialogType , /* "ok" "okcancel" "yesno" */ + char const * const aIconType , /* "info" "warning" "error" "question" */ + int const aDefaultButton ) ; /* 0 for cancel/no , 1 for ok/yes */ + /* returns 0 for cancel/no , 1 for ok/yes */ + +char const * tinyfd_inputBox ( + char const * const aTitle , /* "" */ + char const * const aMessage , /* "" may NOT contain \n \t on windows */ + char const * const aDefaultInput ) ; /* "" , if NULL it's a passwordBox */ + /* returns NULL on cancel */ + +char const * tinyfd_saveFileDialog ( + char const * const aTitle , /* "" */ + char const * const aDefaultPathAndFile , /* "" */ + int const aNumOfFilterPatterns , /* 0 */ + char const * const * const aFilterPatterns , /* NULL | {"*.jpg","*.png"} */ + char const * const aSingleFilterDescription ) ; /* NULL | "text files" */ + /* returns NULL on cancel */ + +char const * tinyfd_openFileDialog ( + char const * const aTitle , /* "" */ + char const * const aDefaultPathAndFile , /* "" */ + int const aNumOfFilterPatterns , /* 0 */ + char const * const * const aFilterPatterns , /* NULL {"*.jpg","*.png"} */ + char const * const aSingleFilterDescription , /* NULL | "image files" */ + int const aAllowMultipleSelects ) ; /* 0 or 1 */ + /* in case of multiple files, the separator is | */ + /* returns NULL on cancel */ + +char const * tinyfd_selectFolderDialog ( + char const * const aTitle , /* "" */ + char const * const aDefaultPath ) ; /* "" */ + /* returns NULL on cancel */ + +char const * tinyfd_colorChooser( + char const * const aTitle , /* "" */ + char const * const aDefaultHexRGB , /* NULL or "#FF0000" */ + unsigned char const aDefaultRGB[3] , /* { 0 , 255 , 255 } */ + unsigned char aoResultRGB[3] ) ; /* { 0 , 0 , 0 } */ + /* returns the hexcolor as a string "#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 */ + + +/************ NOT CROSS PLATFORM SECTION STARTS HERE ************************/ +#ifdef _WIN32 +#ifndef TINYFD_NOLIB + +/* windows only - utf-16 version */ +int tinyfd_messageBoxW( + wchar_t const * const aTitle , + wchar_t const * const aMessage, /* "" may contain \n \t */ + wchar_t const * const aDialogType, /* "ok" "okcancel" "yesno" */ + wchar_t const * const aIconType, /* "info" "warning" "error" "question" */ + int const 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 const * tinyfd_saveFileDialogW( + wchar_t const * const aTitle, /* NULL or "" */ + wchar_t const * const aDefaultPathAndFile, /* NULL or "" */ + int const aNumOfFilterPatterns, /* 0 */ + wchar_t const * const * const aFilterPatterns, /* NULL or {"*.jpg","*.png"} */ + wchar_t const * const aSingleFilterDescription); /* NULL or "image files" */ + /* returns NULL on cancel */ + +/* windows only - utf-16 version */ +wchar_t const * tinyfd_openFileDialogW( + wchar_t const * const aTitle, /* "" */ + wchar_t const * const aDefaultPathAndFile, /* "" */ + int const aNumOfFilterPatterns , /* 0 */ + wchar_t const * const * const aFilterPatterns, /* NULL {"*.jpg","*.png"} */ + wchar_t const * const aSingleFilterDescription, /* NULL | "image files" */ + int const aAllowMultipleSelects ) ; /* 0 or 1 */ + /* in case of multiple files, the separator is | */ + /* returns NULL on cancel */ + +/* windows only - utf-16 version */ + wchar_t const * tinyfd_selectFolderDialogW( + wchar_t const * const aTitle, /* "" */ + wchar_t const * const aDefaultPath); /* "" */ + /* returns NULL on cancel */ + +/* windows only - utf-16 version */ +wchar_t const * tinyfd_colorChooserW( + wchar_t const * const aTitle, /* "" */ + wchar_t const * const aDefaultHexRGB, /* NULL or "#FF0000" */ + unsigned char const aDefaultRGB[3] , /* { 0 , 255 , 255 } */ + unsigned char aoResultRGB[3] ) ; /* { 0 , 0 , 0 } */ + /* returns the hexcolor as a string "#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 /*TINYFD_NOLIB*/ +#else /*_WIN32*/ + +/* unix zenity only */ +char const * tinyfd_arrayDialog( + char const * const aTitle , /* "" */ + int const aNumOfColumns , /* 2 */ + char const * const * const aColumns, /* {"Column 1","Column 2"} */ + int const aNumOfRows, /* 2*/ + char const * const * const aCells); + /* {"Row1 Col1","Row1 Col2","Row2 Col1","Row2 Col2"} */ + +#endif /*_WIN32 */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* TINYFILEDIALOGS_H */ + +/* +- This is not for android nor ios. +- The code is pure C, perfectly compatible with C++. +- The API is Fortran ISO_C_BINDING compliant +- C# via dll, see example file +- AVOID USING " AND ' IN TITLES AND MESSAGES. +- 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. +- char const * filterPatterns[3] = { "*.obj" , "*.stl" , "*.dxf" } ; +- On windows link against Comdlg32.lib and Ole32.lib + This linking is not compulsary for console mode (see above). +- On unix: it tries command line calls, so no such need. +- On unix you need applescript, zenity, matedialog, kdialog, Xdialog, + python2/tkinter or dialog (will open a 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. +- Use windows separators on windows and unix separators on unix. +- String memory is preallocated statically for all the returned values. +- File and path names are tested before return, they are valid. +- If you pass only a path instead of path + filename, + make sure it ends with a separator. +- tinyfd_forceConsole=1; at run time, forces dialogs into console mode. +- On windows, console mode only make sense for console applications. +- Mutiple selects are not allowed in console mode. +- The package dialog must be installed to run in enhanced console mode. + It is already installed on most unix systems. +- On osx, the package dialog can be installed via http://macports.org +- On windows, for enhanced 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 +- If dialog is missing, it will switch to basic console input. +- You can query the type of dialog that will be use. +- The Hello World (and a bit more) is on the sourceforge site: +*/ + diff --git a/tools/rFXGen/rfxgen.c b/tools/rFXGen/rfxgen.c new file mode 100644 index 0000000..06ac7cd --- /dev/null +++ b/tools/rFXGen/rfxgen.c @@ -0,0 +1,1595 @@ +/******************************************************************************************* +* +* rFXGen 1.0 - raylib FX sounds generator (based on Tomas Petterson sfxr) +* +* DEPENDENCIES: +* +* raylib 1.6 - This program uses latest raylib audio module functionality. +* raygui 1.0 - Simple IMGUI library (based on raylib) +* tinyfiledialogs 2.5.8 - Open/save file dialogs, it requires linkage with comdlg32 and ole32 libs. +* +* VERSIONS HISTORY: +* +* 1.0 (XX-Sep-2016) TODO: Release stable version... not yet... +* 0.95 (14-Sep-2016) Reviewed comments and .rfx format +* 0.9 (12-Sep-2016) Defined WaveParams struct and command line functionality +* 0.8 (09-Sep-2016) Added open/save file dialogs using tinyfiledialogs library +* 0.7 (04-Sep-2016) Program variables renaming for consistency, code reorganized +* 0.6 (30-Aug-2016) Interface redesigned (reduced size) and new features added (wave drawing) +* 0.5 (27-Aug-2016) Completed port and adaptation from sfxr (only sound generation and playing) +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2014-2016 Ramon Santamaria (@raysan5) +* +* 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. +* +**********************************************************************************************/ + +#include "raylib.h" + +//#define RAYGUI_STYLE_DEFAULT_DARK +#define RAYGUI_NO_STYLE_SAVE_LOAD // Avoid compiling style load/save code +#define RAYGUI_IMPLEMENTATION +#include "raygui.h" + +#include "external/tinyfiledialogs.h" // Required for native open/save file dialogs + +#include // Required for: sinf(), pow() +#include // Required for: malloc(), free() +#include // Required for: strcmp() +#include // Required for: FILE, fopen(), fread(), fwrite(), ftell(), fseek() fclose() + // NOTE: Used on functions: LoadSound(), SaveSound(), WriteWAV() + +#include "twitter.h" // Twitter icon embedded + +#if defined(_WIN32) + #include + #define GetCurrentDir _getcwd +#else + #include + #define GetCurrentDir getcwd +#endif + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +#define MAX_WAVE_LENGTH 10 // 10 seconds + +#define rnd(n) GetRandomValue(0, n) +#define frnd(range) ((float)rnd(10000)/10000.0f*range) + +// Wave parameters type +typedef struct WaveParams { + + // Wave type (square, sawtooth, sine, noise) + int waveTypeValue; + + // Wave envelope parameters + float attackTimeValue; + float sustainTimeValue; + float sustainPunchValue; + float decayTimeValue; + + // Frequency parameters + float startFrequencyValue; + float minFrequencyValue; + float slideValue; + float deltaSlideValue; + float vibratoDepthValue; + float vibratoSpeedValue; + //float vibratoPhaseDelayValue; + + // Tone change parameters + float changeAmountValue; + float changeSpeedValue; + + // Square wave parameters + float squareDutyValue; + float dutySweepValue; + + // Repeat parameters + float repeatSpeedValue; + + // Phaser parameters + float phaserOffsetValue; + float phaserSweepValue; + + // Filter parameters + float lpfCutoffValue; + float lpfCutoffSweepValue; + float lpfResonanceValue; + float hpfCutoffValue; + float hpfCutoffSweepValue; + //bool filterOn; + +} WaveParams; + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- + +// Volume parameters +static float volumeValue = 0.6f; // Volume +const float masterVolume = 0.15f; // Master volume + +// Export WAV variables +static int wavBitrate = 16; // Wave bitrate (sample size in bits) +static int wavFrequency = 44100; // Wave frequency (sample rate) + +// Wave and Sound variables +static Wave wave; // Stores wave data +static WaveParams params; // Stores wave parameters for generation +static Sound sound; // Stores sound data (OpenAL) + +static char currentPath[256]; // Path to current working folder + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +static void ResetParams(void); // Reset sound parameters +static void GenerateWave(WaveParams params); // Generate wave data and update sound +static void GeneratePlay(void); // Generate wave data and play sound + +static void LoadSoundParams(const char *fileName); // Load sound parameters from file +static void SaveSoundParams(const char *fileName); // Save sound parameters to file +static void SaveWAV(const char *fileName, Wave wave); // Export sound to .wav file +static void DrawWave(Rectangle bounds, Color color); // Draw wave data using lines +static const char *GetExtension(const char *fileName); // Get extension from filename + +// Buttons functions +static void BtnPickupCoin(void); // Generate sound: Pickup/Coin +static void BtnLaserShoot(void); // Generate sound: Laser shoot +static void BtnExplosion(void); // Generate sound: Explosion +static void BtnPowerup(void); // Generate sound: Powerup +static void BtnHitHurt(void); // Generate sound: Hit/Hurt +static void BtnJump(void); // Generate sound: Jump +static void BtnBlipSelect(void); // Generate sound: Blip/Select +static void BtnRandomize(void); // Generate random sound +static void BtnMutate(void);; // Mutate current sound + +static void BtnPlaySound(void); // Play current sound +static void BtnLoadSound(void); // Load sound parameters file +static void BtnSaveSound(void); // Save sound parameters file +static void BtnExportWav(void); // Export current sound as .wav + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(int argc, char *argv[]) +{ + // Command line utility to generate wav files directly from .sfs and .rfx + // NOTE: .sfs uses defaults: sampleRate = 22050, sampleSize = 8, channels = 1; + // .rfx files contain sampleRate, sampleSize and channels information + if (argc > 1) + { + for (int i = 1; i < argc; i++) + { + if ((strcmp(GetExtension(argv[i]), "rfx") == 0) || + (strcmp(GetExtension(argv[i]), "sfs") == 0)) + { + LoadSoundParams(argv[i]); + GenerateWave(params); + + argv[i][strlen(argv[i]) - 3] = 'w'; + argv[i][strlen(argv[i]) - 2] = 'a'; + argv[i][strlen(argv[i]) - 1] = 'v'; + + //printf("output name: %s\n", argv[i]); + + SaveWAV(argv[i], wave); + } + } + + return 0; + } + + // Initialization + //---------------------------------------------------------------------------------------- + int screenWidth = 500; + int screenHeight = 500; + + //SetConfigFlags(FLAG_MSAA_4X_HINT); + InitWindow(screenWidth, screenHeight, "rFXGen - raylib FX Sound Generator"); + + InitAudioDevice(); + + Rectangle paramsRec = { 117, 43, 265, 373 }; + + Image mask; + mask.width = 16; + mask.height = 16; + mask.mipmaps = 1; + mask.format = UNCOMPRESSED_GRAYSCALE; + mask.data = imTwitter; + + Color *pixels = (Color *)malloc(16*16*sizeof(Color)); + for (int i = 0; i < 16*16; i++) pixels[i] = WHITE; + Image twitter = LoadImageEx(pixels, 16, 16); + free(pixels); + + ImageAlphaMask(&twitter, mask); // Add alpha mask to image + ImageFormat(&twitter, UNCOMPRESSED_GRAY_ALPHA); + + Texture2D texTwitter = LoadTextureFromImage(twitter); + UnloadImage(twitter); + + // Label data + //---------------------------------------------------------------------------------------- + Rectangle lblAttackTimeRec = { paramsRec.x + 33, paramsRec.y + 5, 100, 10 }; + Rectangle lblSustainTimeRec = { paramsRec.x + 31, paramsRec.y + 20, 100, 10 }; + Rectangle lblSustainPunchRec = { paramsRec.x + 27, paramsRec.y + 35, 100, 10 }; + Rectangle lblDecayTimeRec = { paramsRec.x + 37, paramsRec.y + 50, 100, 10 }; + + Rectangle lblStartFrequencyRec = { paramsRec.x + 7, paramsRec.y + 66 + 5, 100, 10 }; + Rectangle lblMinFrequencyRec = { paramsRec.x + 27, paramsRec.y + 66 + 20, 100, 10 }; + Rectangle lblSlideRec = { paramsRec.x + 55, paramsRec.y + 66 + 35, 100, 10 }; + Rectangle lblDeltaSlideRec = { paramsRec.x + 35, paramsRec.y + 66 + 50, 100, 10 }; + Rectangle lblVibratoDepthRec = { paramsRec.x + 26, paramsRec.y + 66 + 65, 100, 10 }; + Rectangle lblVibratoSpeedRec = { paramsRec.x + 27, paramsRec.y + 66 + 80, 100, 10 }; + + Rectangle lblChangeAmountRec = { paramsRec.x + 25, paramsRec.y + 162 + 5, 100, 10 }; + Rectangle lblChangeSpeedRec = { paramsRec.x + 30, paramsRec.y + 162 + 20, 100, 10 }; + + Rectangle lblSquareDutyRec = { paramsRec.x + 33, paramsRec.y + 198 + 5, 100, 10 }; + Rectangle lblDutySweepRec = { paramsRec.x + 36, paramsRec.y + 198 + 20, 100, 10 }; + + Rectangle lblRepeatSpeedRec = { paramsRec.x + 29, paramsRec.y + 234 + 5, 100, 10 }; + + Rectangle lblPhaserOffsetRec = { paramsRec.x + 26, paramsRec.y + 255 + 5, 100, 10 }; + Rectangle lblPhaserSweepRec = { paramsRec.x + 29, paramsRec.y + 255 + 20, 100, 10 }; + + Rectangle lblLpfCutoffRec = { paramsRec.x + 37, paramsRec.y + 291 + 5, 100, 10 }; + Rectangle lblLpfCutoffSweepRec = { paramsRec.x + 4, paramsRec.y + 291 + 20, 100, 10 }; + Rectangle lblLpfResonanceRec = { paramsRec.x + 27, paramsRec.y + 291 + 35, 100, 10 }; + Rectangle lblHpfCutoffRec = { paramsRec.x + 36, paramsRec.y + 291 + 50, 100, 10 }; + Rectangle lblHpfCutoffSweepRec = { paramsRec.x + 3, paramsRec.y + 291 + 65, 100, 10 }; + //---------------------------------------------------------------------------------------- + + // SliderBar data + //---------------------------------------------------------------------------------------- + Rectangle sldrAttackTimeRec = { paramsRec.x + 127, paramsRec.y + 5, 100, 10 }; + Rectangle sldrSustainTimeRec = { paramsRec.x + 127, paramsRec.y + 20, 100, 10 }; + Rectangle sldrSustainPunchRec = { paramsRec.x + 127, paramsRec.y + 35, 100, 10 }; + Rectangle sldrDecayTimeRec = { paramsRec.x + 127, paramsRec.y + 50, 100, 10 }; + + Rectangle sldrStartFrequencyRec = { paramsRec.x + 127, paramsRec.y + 66 + 5, 100, 10 }; + Rectangle sldrMinFrequencyRec = { paramsRec.x + 127, paramsRec.y + 66 + 20, 100, 10 }; + Rectangle sldrSlideRec = { paramsRec.x + 127, paramsRec.y + 66 + 35, 100, 10 }; + Rectangle sldrDeltaSlideRec = { paramsRec.x + 127, paramsRec.y + 66 + 50, 100, 10 }; + Rectangle sldrVibratoDepthRec = { paramsRec.x + 127, paramsRec.y + 66 + 65, 100, 10 }; + Rectangle sldrVibratoSpeedRec = { paramsRec.x + 127, paramsRec.y + 66 + 80, 100, 10 }; + + Rectangle sldrChangeAmountRec = { paramsRec.x + 127, paramsRec.y + 162 + 5, 100, 10 }; + Rectangle sldrChangeSpeedRec = { paramsRec.x + 127, paramsRec.y + 162 + 20, 100, 10 }; + + Rectangle sldrSquareDutyRec = { paramsRec.x + 127, paramsRec.y + 198 + 5, 100, 10 }; + Rectangle sldrDutySweepRec = { paramsRec.x + 127, paramsRec.y + 198 + 20, 100, 10 }; + + Rectangle sldrRepeatSpeedRec = { paramsRec.x + 127, paramsRec.y + 234 + 5, 100, 10 }; + + Rectangle sldrPhaserOffsetRec = { paramsRec.x + 127, paramsRec.y + 255 + 5, 100, 10 }; + Rectangle sldrPhaserSweepRec = { paramsRec.x + 127, paramsRec.y + 255 + 20, 100, 10 }; + + Rectangle sldrLpfCutoffRec = { paramsRec.x + 127, paramsRec.y + 291 + 5, 100, 10 }; + Rectangle sldrLpfCutoffSweepRec = { paramsRec.x + 127, paramsRec.y + 291 + 20, 100, 10 }; + Rectangle sldrLpfResonanceRec = { paramsRec.x + 127, paramsRec.y + 291 + 35, 100, 10 }; + Rectangle sldrHpfCutoffRec = { paramsRec.x + 127, paramsRec.y + 291 + 50, 100, 10 }; + Rectangle sldrHpfCutoffSweepRec = { paramsRec.x + 127, paramsRec.y + 291 + 65, 100, 10 }; + + Rectangle sldrVolumeRec = { 394, 65, 92, 10 }; + //---------------------------------------------------------------------------------------- + + // Button data + //---------------------------------------------------------------------------------------- + Rectangle btnPickupCoinRec = { 13, 48, 92, 20 }; + Rectangle btnLaserShootRec = { 13, 73, 92, 20 }; + Rectangle btnExplosionRec = { 13, 98, 92, 20 }; + Rectangle btnPowerupRec = { 13, 123, 92, 20 }; + Rectangle btnHitHurtRec = { 13, 148, 92, 20 }; + Rectangle btnJumpRec = { 13, 173, 92, 20 }; + Rectangle btnBlipSelectRec = { 13, 198, 92, 20 }; + Rectangle btnMutateRec = { 13, 364, 92, 20 }; + Rectangle btnRandomizeRec = { 13, 389, 92, 20 }; + + Rectangle btnPlaySoundRec = { 394, 81, 92, 20 }; + Rectangle btnLoadSoundRec = { 394, 283, 92, 20 }; + Rectangle btnSaveSoundRec = { 394, 307, 92, 20 }; + Rectangle btnExportWavRec = { 394, 389, 92, 20 }; + //---------------------------------------------------------------------------------------- + + // CheckBox data + //---------------------------------------------------------------------------------------- + Rectangle chkboxPlayOnChangeRec = { 394, 115, 10, 10 }; + bool playOnChangeValue = true; + //---------------------------------------------------------------------------------------- + + // ComboBox data + //---------------------------------------------------------------------------------------- + Rectangle comboxFrequencyRec = { 394, 340, 61, 20 }; + Rectangle comboxBitRateRec = { 394, 364, 61, 20 }; + char *comboxFrequencyText[2] = { "44100 Hz", "22050 Hz" }; + char *comboxBitRateText[2] = { "16 bit", "8 bit" }; + int comboxFrequencyValue = 1; + int comboxBitRateValue = 1; + //---------------------------------------------------------------------------------------- + + // ToggleGroup data + //---------------------------------------------------------------------------------------- + Rectangle tgroupWaveTypeRec = { 117, 15, 64, 20 }; + char *tgroupWaveTypeText[4] = { "Square", "Sawtooth", "Sinewave", "Noise" }; + //---------------------------------------------------------------------------------------- + + // Wave default parameters + wave.sampleRate = 44100; + wave.sampleSize = 32; // 32 bit -> float + wave.channels = 1; + + wave.sampleCount = MAX_WAVE_LENGTH*wave.sampleRate*wave.channels; + wave.data = (float *)malloc(wave.sampleCount*sizeof(float)); + + sound = LoadSoundFromWave(wave); + SetSoundVolume(sound, volumeValue); + + ResetParams(); + + Rectangle waveRec = { 13, 421, 473, 50 }; + +#define RENDER_WAVE_TO_TEXTURE +#if defined(RENDER_WAVE_TO_TEXTURE) + // To avoid enabling MSXAAx4, we will render wave to a texture x2 + RenderTexture2D waveTarget = LoadRenderTexture(waveRec.width*2, waveRec.height*2); +#endif + + // Get current directory + // NOTE: Current working directory could not match current executable directory + GetCurrentDir(currentPath, sizeof(currentPath)); + currentPath[strlen(currentPath)] = '\\'; + currentPath[strlen(currentPath) + 1] = '\0'; // Not really required + + SetTargetFPS(60); + //---------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update & Draw + //------------------------------------------------------------------------------------ + if (IsKeyPressed(KEY_SPACE)) PlaySound(sound); + + // Check if mouse is moving sliders to generate new wave data when mouse released + if ((CheckCollisionPointRec(GetMousePosition(), (Rectangle){ 265, 65, 105, 360 })) && (IsMouseButtonReleased(MOUSE_LEFT_BUTTON))) + { + if (playOnChangeValue) GeneratePlay(); + else GenerateWave(params); + } + + BeginDrawing(); + + ClearBackground(GuiBackgroundColor()); + + DrawRectangleLines(paramsRec.x, paramsRec.y - 1, paramsRec.width, paramsRec.height + 1, GuiLinesColor()); + + DrawLine(paramsRec.x, paramsRec.y + 66, paramsRec.x + paramsRec.width, paramsRec.y + 66, GuiLinesColor()); + DrawLine(paramsRec.x, paramsRec.y + 66 + 96, paramsRec.x + paramsRec.width, paramsRec.y + 66 + 96, GuiLinesColor()); + DrawLine(paramsRec.x, paramsRec.y + 162 + 36, paramsRec.x + paramsRec.width, paramsRec.y + 162 + 36, GuiLinesColor()); + DrawLine(paramsRec.x, paramsRec.y + 198 + 36, paramsRec.x + paramsRec.width, paramsRec.y + 198 + 36, GuiLinesColor()); + DrawLine(paramsRec.x, paramsRec.y + 234 + 21, paramsRec.x + paramsRec.width, paramsRec.y + 234 + 21, GuiLinesColor()); + DrawLine(paramsRec.x, paramsRec.y + 291, paramsRec.x + paramsRec.width, paramsRec.y + 291, GuiLinesColor()); + + DrawLine(13, 225, 105, 224, GuiLinesColor()); + DrawLine(13, 358, 105, 358, GuiLinesColor()); + DrawLine(394, 108, 486, 108, GuiLinesColor()); + DrawLine(394, 277, 486, 277, GuiLinesColor()); + DrawLine(394, 334, 486, 334, GuiLinesColor()); + + // Labels + //-------------------------------------------------------------------------------- + DrawText("rFXGen", 28, 19, 20, DARKGRAY); + + GuiLabel(lblAttackTimeRec, "ATTACK TIME"); + GuiLabel(lblSustainTimeRec, "SUSTAIN TIME"); + GuiLabel(lblSustainPunchRec, "SUSTAIN PUNCH"); + GuiLabel(lblDecayTimeRec, "DECAY TIME"); + // --------- + GuiLabel(lblStartFrequencyRec, "START FREQUENCY"); + GuiLabel(lblMinFrequencyRec, "MIN FREQUENCY"); + GuiLabel(lblSlideRec, "SLIDE"); + GuiLabel(lblDeltaSlideRec, "DELTA SLIDE"); + GuiLabel(lblVibratoDepthRec, "VIBRATO DEPTH"); + GuiLabel(lblVibratoSpeedRec, "VIBRATO SPEED"); + // --------- + GuiLabel(lblChangeAmountRec, "CHANGE AMOUNT"); + GuiLabel(lblChangeSpeedRec, "CHANGE SPEED"); + // --------- + GuiLabel(lblSquareDutyRec, "SQUARE DUTY"); + GuiLabel(lblDutySweepRec, "DUTY SWEEP"); + // --------- + GuiLabel(lblRepeatSpeedRec, "REPEAT SPEED"); + // --------- + GuiLabel(lblPhaserOffsetRec, "PHASER OFFSET"); + GuiLabel(lblPhaserSweepRec, "PHASER SWEEP"); + // --------- + GuiLabel(lblLpfCutoffRec, "LPF CUTOFF"); + GuiLabel(lblLpfCutoffSweepRec, "LPF CUTOFF SWEEP"); + GuiLabel(lblLpfResonanceRec, "LPF RESONANCE"); + GuiLabel(lblHpfCutoffRec, "HPF CUTOFF"); + GuiLabel(lblHpfCutoffSweepRec, "HPF CUTOFF SWEEP"); + //-------------------------------------------------------------------------------- + + // Sliders + //-------------------------------------------------------------------------------- + params.attackTimeValue = GuiSliderBar(sldrAttackTimeRec, params.attackTimeValue, 0, 1); + params.sustainTimeValue = GuiSliderBar(sldrSustainTimeRec, params.sustainTimeValue, 0, 1); + params.sustainPunchValue = GuiSliderBar(sldrSustainPunchRec, params.sustainPunchValue, 0, 1); + params.decayTimeValue = GuiSliderBar(sldrDecayTimeRec, params.decayTimeValue, 0, 1); + // -------------- + params.startFrequencyValue = GuiSliderBar(sldrStartFrequencyRec, params.startFrequencyValue, 0, 1); + params.minFrequencyValue = GuiSliderBar(sldrMinFrequencyRec, params.minFrequencyValue, 0, 1); + params.slideValue = GuiSliderBar(sldrSlideRec, params.slideValue, -1, 1); + params.deltaSlideValue = GuiSliderBar(sldrDeltaSlideRec, params.deltaSlideValue, -1, 1); + params.vibratoDepthValue = GuiSliderBar(sldrVibratoDepthRec, params.vibratoDepthValue, 0, 1); + params.vibratoSpeedValue = GuiSliderBar(sldrVibratoSpeedRec, params.vibratoSpeedValue, 0, 1); + // -------------- + params.changeAmountValue = GuiSliderBar(sldrChangeAmountRec, params.changeAmountValue, -1, 1); + params.changeSpeedValue = GuiSliderBar(sldrChangeSpeedRec, params.changeSpeedValue, 0, 1); + // -------------- + params.squareDutyValue = GuiSliderBar(sldrSquareDutyRec, params.squareDutyValue, 0, 1); + params.dutySweepValue = GuiSliderBar(sldrDutySweepRec, params.dutySweepValue, -1, 1); + // -------------- + params.repeatSpeedValue = GuiSliderBar(sldrRepeatSpeedRec, params.repeatSpeedValue, 0, 1); + // --------------- + params.phaserOffsetValue = GuiSliderBar(sldrPhaserOffsetRec, params.phaserOffsetValue, -1, 1); + params.phaserSweepValue = GuiSliderBar(sldrPhaserSweepRec, params.phaserSweepValue, -1, 1); + // --------------- + params.lpfCutoffValue = GuiSliderBar(sldrLpfCutoffRec, params.lpfCutoffValue, 0, 1); + params.lpfCutoffSweepValue = GuiSliderBar(sldrLpfCutoffSweepRec, params.lpfCutoffSweepValue, -1, 1); + params.lpfResonanceValue = GuiSliderBar(sldrLpfResonanceRec, params.lpfResonanceValue, 0, 1); + params.hpfCutoffValue = GuiSliderBar(sldrHpfCutoffRec, params.hpfCutoffValue, 0, 1); + params.hpfCutoffSweepValue = GuiSliderBar(sldrHpfCutoffSweepRec, params.hpfCutoffSweepValue, -1, 1); + // ---------------- + float previousVolumeValue = volumeValue; + volumeValue = GuiSliderBar(sldrVolumeRec, volumeValue, 0, 1); + if (volumeValue != previousVolumeValue) SetSoundVolume(sound, volumeValue); + //-------------------------------------------------------------------------------- + + // Slider values + //-------------------------------------------------------------------------------- + DrawText(FormatText("%.02f", params.attackTimeValue), sldrAttackTimeRec.x + sldrAttackTimeRec.width + 7, sldrAttackTimeRec.y + 1, 10, DARKGRAY); + DrawText(FormatText("%.02f", params.sustainTimeValue), sldrSustainTimeRec.x + sldrSustainTimeRec.width + 7, sldrSustainTimeRec.y + 1, 10, DARKGRAY); + DrawText(FormatText("%.02f", params.sustainPunchValue), sldrSustainPunchRec.x + sldrSustainPunchRec.width + 7, sldrSustainPunchRec.y + 1, 10, DARKGRAY); + DrawText(FormatText("%.02f", params.decayTimeValue), sldrDecayTimeRec.x + sldrDecayTimeRec.width + 7, sldrDecayTimeRec.y + 1, 10, DARKGRAY); + // --------------- + DrawText(FormatText("%.02f", params.startFrequencyValue), sldrStartFrequencyRec.x + sldrStartFrequencyRec.width + 7, sldrStartFrequencyRec.y + 1, 10, DARKGRAY); + DrawText(FormatText("%.02f", params.minFrequencyValue), sldrMinFrequencyRec.x + sldrMinFrequencyRec.width + 7, sldrMinFrequencyRec.y + 1, 10, DARKGRAY); + DrawText(FormatText("%.02f", params.slideValue), sldrSlideRec.x + sldrSlideRec.width + 7, sldrSlideRec.y + 1, 10, DARKGRAY); + DrawText(FormatText("%.02f", params.deltaSlideValue), sldrDeltaSlideRec.x + sldrDeltaSlideRec.width + 7, sldrDeltaSlideRec.y + 1, 10, DARKGRAY); + DrawText(FormatText("%.02f", params.vibratoDepthValue), sldrVibratoDepthRec.x + sldrVibratoDepthRec.width + 7, sldrVibratoDepthRec.y + 1, 10, DARKGRAY); + DrawText(FormatText("%.02f", params.vibratoSpeedValue), sldrVibratoSpeedRec.x + sldrVibratoSpeedRec.width + 7, sldrVibratoSpeedRec.y + 1, 10, DARKGRAY); + // --------------- + DrawText(FormatText("%.02f", params.changeAmountValue), sldrChangeAmountRec.x + sldrChangeAmountRec.width + 7, sldrChangeAmountRec.y + 1, 10, DARKGRAY); + DrawText(FormatText("%.02f", params.changeSpeedValue), sldrChangeSpeedRec.x + sldrChangeSpeedRec.width + 7, sldrChangeSpeedRec.y + 1, 10, DARKGRAY); + // --------------- + DrawText(FormatText("%.02f", params.squareDutyValue), sldrSquareDutyRec.x + sldrSquareDutyRec.width + 7, sldrSquareDutyRec.y + 1, 10, DARKGRAY); + DrawText(FormatText("%.02f", params.dutySweepValue), sldrDutySweepRec.x + sldrDutySweepRec.width + 7, sldrDutySweepRec.y + 1, 10, DARKGRAY); + // --------------- + DrawText(FormatText("%.02f", params.repeatSpeedValue), sldrRepeatSpeedRec.x + sldrRepeatSpeedRec.width + 7, sldrRepeatSpeedRec.y + 1, 10, DARKGRAY); + // --------------- + DrawText(FormatText("%.02f", params.phaserOffsetValue), sldrPhaserOffsetRec.x + sldrPhaserOffsetRec.width + 7, sldrPhaserOffsetRec.y + 1, 10, DARKGRAY); + DrawText(FormatText("%.02f", params.phaserSweepValue), sldrPhaserSweepRec.x + sldrPhaserSweepRec.width + 7, sldrPhaserSweepRec.y + 1, 10, DARKGRAY); + // --------------- + DrawText(FormatText("%.02f", params.lpfCutoffValue), sldrLpfCutoffRec.x + sldrLpfCutoffRec.width + 7, sldrLpfCutoffRec.y + 1, 10, DARKGRAY); + DrawText(FormatText("%.02f", params.lpfCutoffSweepValue), sldrLpfCutoffSweepRec.x + sldrLpfCutoffSweepRec.width + 7, sldrLpfCutoffSweepRec.y + 1, 10, DARKGRAY); + DrawText(FormatText("%.02f", params.lpfResonanceValue), sldrLpfResonanceRec.x + sldrLpfResonanceRec.width + 7, sldrLpfResonanceRec.y + 1, 10, DARKGRAY); + DrawText(FormatText("%.02f", params.hpfCutoffValue), sldrHpfCutoffRec.x + sldrHpfCutoffRec.width + 7, sldrHpfCutoffRec.y + 1, 10, DARKGRAY); + DrawText(FormatText("%.02f", params.hpfCutoffSweepValue), sldrHpfCutoffSweepRec.x + sldrHpfCutoffSweepRec.width + 7, sldrHpfCutoffSweepRec.y + 1, 10, DARKGRAY); + //-------------------------------------------------------------------------------- + + // Buttons + //-------------------------------------------------------------------------------- + if (GuiButton(btnPickupCoinRec, "Pickup/Coin")) BtnPickupCoin(); + if (GuiButton(btnLaserShootRec, "Laser/Shoot")) BtnLaserShoot(); + if (GuiButton(btnExplosionRec, "Explosion")) BtnExplosion(); + if (GuiButton(btnPowerupRec, "Powerup")) BtnPowerup(); + if (GuiButton(btnHitHurtRec, "Hit/Hurt")) BtnHitHurt(); + if (GuiButton(btnJumpRec, "Jump")) BtnJump(); + if (GuiButton(btnBlipSelectRec, "Blip/Select")) BtnBlipSelect(); + if (GuiButton(btnMutateRec, "Mutate")) BtnMutate(); + if (GuiButton(btnRandomizeRec, "Randomize")) BtnRandomize(); + if (GuiButton(btnPlaySoundRec, "Play Sound")) BtnPlaySound(); + if (GuiButton(btnLoadSoundRec, "Load Sound")) BtnLoadSound(); + if (GuiButton(btnSaveSoundRec, "Save Sound")) BtnSaveSound(); + if (GuiButton(btnExportWavRec, "Export .Wav")) BtnExportWav(); + //-------------------------------------------------------------------------------- + + // CheckBox + //-------------------------------------------------------------------------------- + playOnChangeValue = GuiCheckBox(chkboxPlayOnChangeRec, " Play on change", playOnChangeValue); + //-------------------------------------------------------------------------------- + + // ComboBox + //-------------------------------------------------------------------------------- + comboxFrequencyValue = GuiComboBox(comboxFrequencyRec, 2, comboxFrequencyText, comboxFrequencyValue); + comboxBitRateValue = GuiComboBox(comboxBitRateRec, 2, comboxBitRateText, comboxBitRateValue); + + if (comboxFrequencyValue == 0) wavFrequency = 44100; + else if (comboxFrequencyValue == 1) wavFrequency = 22050; + + if (comboxBitRateValue == 0) wavBitrate = 16; + else if (comboxBitRateValue == 1) wavBitrate = 8; + //-------------------------------------------------------------------------------- + + // ToggleGroup + //-------------------------------------------------------------------------------- + int previousWaveTypeValue = params.waveTypeValue; + params.waveTypeValue = GuiToggleGroup(tgroupWaveTypeRec, 4, tgroupWaveTypeText, params.waveTypeValue); + if (params.waveTypeValue != previousWaveTypeValue) GeneratePlay(); + //-------------------------------------------------------------------------------- + + if (volumeValue < 1.0f) DrawText(FormatText("VOLUME: %02i %%", (int)(volumeValue*100.0f)), 394, 49, 10, DARKGRAY); + else DrawText(FormatText("VOLUME: %02i %%", (int)(volumeValue*100.0f)), 394, 49, 10, DARKGRAY); + +#if defined(RENDER_WAVE_TO_TEXTURE) + BeginTextureMode(waveTarget); + DrawWave((Rectangle){ 0, 0, waveTarget.texture.width, waveTarget.texture.height }, MAROON); + EndTextureMode(); + + DrawTextureEx(waveTarget.texture, (Vector2){ waveRec.x, waveRec.y }, 0.0f, 0.5f, WHITE); +#else + DrawWave(waveRec, MAROON); +#endif + DrawRectangleLines(waveRec.x, waveRec.y, waveRec.width, waveRec.height, GuiLinesColor()); + DrawRectangle(waveRec.x, waveRec.y + waveRec.height/2, waveRec.width, 1, LIGHTGRAY); + + // Draw status bar + DrawRectangle(0, screenHeight - 20, screenWidth, 20, Fade(LIGHTGRAY, 0.5f)); + DrawLine(0, screenHeight - 20, screenWidth, screenHeight - 20, LIGHTGRAY); + DrawText("SOUND INFO:", 28, 486, 10, DARKGRAY); + DrawText(FormatText("num samples: %i", wave.sampleCount), 117, 486, 10, DARKGRAY); + DrawText(FormatText("| duration: %i ms", wave.sampleCount*1000/(wave.sampleRate*wave.channels)), 234, 486, 10, DARKGRAY); + DrawText(FormatText("| Wave size: %i bytes", wave.sampleCount*wavBitrate/8), 355, 486, 10, DARKGRAY); + + // Adverts + //DrawText("based on sfxr by\nTomas Pettersson", 18, 240, 10, GRAY); + DrawText("based on sfxr by", 16, 235, 10, GRAY); + DrawText("Tomas Pettersson", 13, 248, 10, GRAY); + + DrawLine(13, 268, 105, 268, GuiLinesColor()); + + DrawText("www/github.com/\nraysan5/raygui", 18, 280, 10, GRAY); + DrawText("www/github.com/\nraysan5/raylib", 18, 318, 10, GRAY); + DrawText("powered by", 394, 149, 10, DARKGRAY); + DrawRectangle(394, 162, 92, 92, BLACK); + DrawRectangle(400, 168, 80, 80, RAYWHITE); + DrawText("raylib", 419, 223, 20, BLACK); + + //DrawText("based on sfxr by", 394, 13, 10, GRAY); + //DrawText("Tomas Pettersson", 392, 25, 10, GRAY); + + DrawText("@raysan5", 421, 21, 10, GRAY); + DrawTexture(texTwitter, 400, 18, Fade(BLACK, 0.4f)); + + //DrawText("@raysan5", 40, 242, 10, GRAY); + //DrawTexture(texTwitter, 20, 240, Fade(BLACK, 0.4f)); + + EndDrawing(); + //------------------------------------------------------------------------------------ + + //if (doPlay) PlaySample(); + } + + // De-Initialization + //---------------------------------------------------------------------------------------- + UnloadSound(sound); + UnloadWave(wave); + +#if defined(RENDER_WAVE_TO_TEXTURE) + UnloadRenderTexture(waveTarget); +#endif + + UnloadTexture(texTwitter); + + CloseAudioDevice(); + + CloseWindow(); // Close window and OpenGL context + //---------------------------------------------------------------------------------------- + + return 0; +} + +//-------------------------------------------------------------------------------------------- +// Module Functions Definitions (local) +//-------------------------------------------------------------------------------------------- +static void ResetParams() +{ + // Wave type + params.waveTypeValue = 0; + + // Wave envelope params + params.attackTimeValue = 0.0f; + params.sustainTimeValue = 0.3f; + params.sustainPunchValue = 0.0f; + params.decayTimeValue = 0.4f; + + // Frequency params + params.startFrequencyValue = 0.3f; + params.minFrequencyValue = 0.0f; + params.slideValue = 0.0f; + params.deltaSlideValue = 0.0f; + params.vibratoDepthValue = 0.0f; + params.vibratoSpeedValue = 0.0f; + //params.vibratoPhaseDelay = 0.0f; + + // Tone change params + params.changeAmountValue = 0.0f; + params.changeSpeedValue = 0.0f; + + // Square wave params + params.squareDutyValue = 0.0f; + params.dutySweepValue = 0.0f; + + // Repeat params + params.repeatSpeedValue = 0.0f; + + // Phaser params + params.phaserOffsetValue = 0.0f; + params.phaserSweepValue = 0.0f; + + // Filter params + params.lpfCutoffValue = 1.0f; + params.lpfCutoffSweepValue = 0.0f; + params.lpfResonanceValue = 0.0f; + params.hpfCutoffValue = 0.0f; + params.hpfCutoffSweepValue = 0.0f; + //params.filterOn = false; +} + +// Generates new wave data and updates sound buffer +// NOTE: Operates on global variables: sound, wave +static void GenerateWave(WaveParams params) +{ + // Configuration parameters for generation + // NOTE: Those parameters are calculated from selected values + int phase; + double fperiod; + double fmaxperiod; + double fslide; + double fdslide; + int period; + float squareDuty; + float squareSlide; + int envelopeStage; + int envelopeTime; + int envelopeLength[3]; + float envelopeVolume; + float fphase; + float fdphase; + int iphase; + float phaserBuffer[1024]; + int ipp; + float noiseBuffer[32]; + float fltp; + float fltdp; + float fltw; + float fltwd; + float fltdmp; + float fltphp; + float flthp; + float flthpd; + float vibratoPhase; + float vibratoSpeed; + float vibratoAmplitude; + int repeatTime; + int repeatLimit; + int arpeggioTime; + int arpeggioLimit; + double arpeggioModulation; + + // Reset sample parameters + //---------------------------------------------------------------------------------------- + phase = 0; + + fperiod = 100.0/(params.startFrequencyValue*params.startFrequencyValue + 0.001); + period = (int)fperiod; + fmaxperiod = 100.0/(params.minFrequencyValue*params.minFrequencyValue + 0.001); + fslide = 1.0 - pow((double)params.slideValue, 3.0)*0.01; + fdslide = -pow((double)params.deltaSlideValue, 3.0)*0.000001; + squareDuty = 0.5f - params.squareDutyValue*0.5f; + squareSlide = -params.dutySweepValue*0.00005f; + + if (params.changeAmountValue >= 0.0f) arpeggioModulation = 1.0 - pow((double)params.changeAmountValue, 2.0)*0.9; + else arpeggioModulation = 1.0 + pow((double)params.changeAmountValue, 2.0)*10.0; + + arpeggioTime = 0; + arpeggioLimit = (int)(pow(1.0f - params.changeSpeedValue, 2.0f)*20000 + 32); + + if (params.changeSpeedValue == 1.0f) arpeggioLimit = 0; + + // Reset filter parameters + fltp = 0.0f; + fltdp = 0.0f; + fltw = pow(params.lpfCutoffValue, 3.0f)*0.1f; + fltwd = 1.0f + params.lpfCutoffSweepValue*0.0001f; + fltdmp = 5.0f/(1.0f + pow(params.lpfResonanceValue, 2.0f)*20.0f)*(0.01f + fltw); + if (fltdmp > 0.8f) fltdmp = 0.8f; + fltphp = 0.0f; + flthp = pow(params.hpfCutoffValue, 2.0f)*0.1f; + flthpd = 1.0 + params.hpfCutoffSweepValue*0.0003f; + + // Reset vibrato + vibratoPhase = 0.0f; + vibratoSpeed = pow(params.vibratoSpeedValue, 2.0f)*0.01f; + vibratoAmplitude = params.vibratoDepthValue*0.5f; + + // Reset envelope + envelopeVolume = 0.0f; + envelopeStage = 0; + envelopeTime = 0; + envelopeLength[0] = (int)(params.attackTimeValue*params.attackTimeValue*100000.0f); + envelopeLength[1] = (int)(params.sustainTimeValue*params.sustainTimeValue*100000.0f); + envelopeLength[2] = (int)(params.decayTimeValue*params.decayTimeValue*100000.0f); + + fphase = pow(params.phaserOffsetValue, 2.0f)*1020.0f; + if (params.phaserOffsetValue < 0.0f) fphase = -fphase; + + fdphase = pow(params.phaserSweepValue, 2.0f)*1.0f; + if (params.phaserSweepValue < 0.0f) fdphase = -fdphase; + + iphase = abs((int)fphase); + ipp = 0; + + for (int i = 0; i < 1024; i++) phaserBuffer[i] = 0.0f; + for (int i = 0; i < 32; i++) noiseBuffer[i] = frnd(2.0f) - 1.0f; // TODO: Review frnd() + + repeatTime = 0; + repeatLimit = (int)(pow(1.0f - params.repeatSpeedValue, 2.0f)*20000 + 32); + + if (params.repeatSpeedValue == 0.0f) repeatLimit = 0; + //---------------------------------------------------------------------------------------- + + bool generatingSample = true; + float *buffer = (float *)wave.data; + + for (int i = 0; i < MAX_WAVE_LENGTH*wave.sampleRate; i++) + { + if (!generatingSample) + { + wave.sampleCount = i; + break; + } + + // Generate sample using selected parameters + //------------------------------------------------------------------------------------ + repeatTime++; + + if (repeatLimit != 0 && repeatTime >= repeatLimit) + { + // Reset sample parameters (only some of them) + repeatTime = 0; + + fperiod = 100.0/(params.startFrequencyValue*params.startFrequencyValue + 0.001); + period = (int)fperiod; + fmaxperiod = 100.0/(params.minFrequencyValue*params.minFrequencyValue + 0.001); + fslide = 1.0 - pow((double)params.slideValue, 3.0)*0.01; + fdslide = -pow((double)params.deltaSlideValue, 3.0)*0.000001; + squareDuty = 0.5f - params.squareDutyValue*0.5f; + squareSlide = -params.dutySweepValue*0.00005f; + + if (params.changeAmountValue >= 0.0f) arpeggioModulation = 1.0 - pow((double)params.changeAmountValue, 2.0)*0.9; + else arpeggioModulation = 1.0 + pow((double)params.changeAmountValue, 2.0)*10.0; + + arpeggioTime = 0; + arpeggioLimit = (int)(pow(1.0f - params.changeSpeedValue, 2.0f)*20000 + 32); + + if (params.changeSpeedValue == 1.0f) arpeggioLimit = 0; + } + + // Frequency envelopes/arpeggios + arpeggioTime++; + + if (arpeggioLimit !=0 && arpeggioTime >= arpeggioLimit) + { + arpeggioLimit = 0; + fperiod *= arpeggioModulation; + } + + fslide += fdslide; + fperiod *= fslide; + + if (fperiod > fmaxperiod) + { + fperiod = fmaxperiod; + + if (params.minFrequencyValue > 0.0f) generatingSample = false; // TODO: minFrequencyValue shouldn't be here... + } + + float rfperiod = fperiod; + + if (vibratoAmplitude > 0.0f) + { + vibratoPhase += vibratoSpeed; + rfperiod = fperiod*(1.0 + sinf(vibratoPhase)*vibratoAmplitude); + } + + period = (int)rfperiod; + + if (period < 8) period=8; + + squareDuty += squareSlide; + + if (squareDuty < 0.0f) squareDuty = 0.0f; + if (squareDuty > 0.5f) squareDuty = 0.5f; + + // Volume envelope + envelopeTime++; + + if (envelopeTime>envelopeLength[envelopeStage]) + { + envelopeTime = 0; + envelopeStage++; + + if (envelopeStage == 3) generatingSample = false; + } + + if (envelopeStage == 0) envelopeVolume = (float)envelopeTime/envelopeLength[0]; + if (envelopeStage == 1) envelopeVolume = 1.0f + pow(1.0f - (float)envelopeTime/envelopeLength[1], 1.0f)*2.0f*params.sustainPunchValue; + if (envelopeStage == 2) envelopeVolume = 1.0f - (float)envelopeTime/envelopeLength[2]; + + // Phaser step + fphase += fdphase; + iphase = abs((int)fphase); + + if (iphase > 1023) iphase = 1023; + + if (flthpd != 0.0f) + { + flthp *= flthpd; + if (flthp < 0.00001f) flthp = 0.00001f; + if (flthp > 0.1f) flthp = 0.1f; + } + + float ssample = 0.0f; + + // Supersampling x8 + for (int si = 0; si < 8; si++) + { + float sample = 0.0f; + phase++; + + if (phase >= period) + { + //phase = 0; + phase %= period; + if (params.waveTypeValue == 3) + for (int i = 0;i < 32; i++) + noiseBuffer[i] = frnd(2.0f) - 1.0f; // TODO: Review frnd() + } + + // base waveform + float fp = (float)phase/period; + + switch (params.waveTypeValue) + { + case 0: // Square wave + { + if (fp < squareDuty) sample = 0.5f; + else sample = -0.5f; + + } break; + case 1: // Sawtooth wave + { + sample = 1.0f - fp*2; + + } break; + case 2: // Sine wave + { + sample = sinf(fp*2*PI); + + } break; + case 3: // Noise wave + { + sample = noiseBuffer[phase*32/period]; + + } break; + default: break; + } + + // LP filter + float pp = fltp; + fltw *= fltwd; + + if (fltw < 0.0f) fltw = 0.0f; + if (fltw > 0.1f) fltw = 0.1f; + if (params.lpfCutoffValue != 1.0f) + { + fltdp += (sample-fltp)*fltw; + fltdp -= fltdp*fltdmp; + } + else + { + fltp = sample; + fltdp = 0.0f; + } + + fltp += fltdp; + + // HP filter + fltphp += fltp - pp; + fltphp -= fltphp*flthp; + sample = fltphp; + + // Phaser + phaserBuffer[ipp&1023] = sample; + sample += phaserBuffer[(ipp - iphase + 1024) & 1023]; + ipp = (ipp + 1) & 1023; + + // Final accumulation and envelope application + ssample += sample*envelopeVolume; + } + + ssample = ssample/8*masterVolume; // TODO: Why masterVolume? + ssample *= 2.0f*volumeValue; + //------------------------------------------------------------------------------------ + + // Accumulate samples in the buffer + if (wave.data != NULL) + { + if (ssample > 1.0f) ssample = 1.0f; + if (ssample < -1.0f) ssample = -1.0f; + + *buffer++ = ssample; + } + } + + // Update sound buffer with new data + UpdateSound(sound, wave.data, wave.sampleCount); +} + +// Generate wave, update sound and play sound +static void GeneratePlay(void) +{ + GenerateWave(params); + PlaySound(sound); +} + +// Load .rfx (rFXGen) or .sfs (sfxr) sound parameters file +static void LoadSoundParams(const char* fileName) +{ + if (strcmp(GetExtension(fileName),"sfs") == 0) + { + FILE *file = fopen(fileName, "rb"); + + // Load .sfs sound parameters + int version = 0; + fread(&version, 1, sizeof(int), file); + + if ((version == 100) || (version == 101) || (version == 102)) + { + fread(¶ms.waveTypeValue, 1, sizeof(int), file); + + volumeValue = 0.5f; + + if (version == 102) fread(&volumeValue, 1, sizeof(float), file); + + fread(¶ms.startFrequencyValue, 1, sizeof(float), file); + fread(¶ms.minFrequencyValue, 1, sizeof(float), file); + fread(¶ms.slideValue, 1, sizeof(float), file); + + if (version >= 101) fread(¶ms.deltaSlideValue, 1, sizeof(float), file); + + fread(¶ms.squareDutyValue, 1, sizeof(float), file); + fread(¶ms.dutySweepValue, 1, sizeof(float), file); + + fread(¶ms.vibratoDepthValue, 1, sizeof(float), file); + fread(¶ms.vibratoSpeedValue, 1, sizeof(float), file); + + float vibratoPhaseDelay = 0.0f; + fread(&vibratoPhaseDelay, 1, sizeof(float), file); // Not used + + fread(¶ms.attackTimeValue, 1, sizeof(float), file); + fread(¶ms.sustainTimeValue, 1, sizeof(float), file); + fread(¶ms.decayTimeValue, 1, sizeof(float), file); + fread(¶ms.sustainPunchValue, 1, sizeof(float), file); + + bool filterOn = false; + fread(&filterOn, 1, sizeof(bool), file); // Not used + + fread(¶ms.lpfResonanceValue, 1, sizeof(float), file); + fread(¶ms.lpfCutoffValue, 1, sizeof(float), file); + fread(¶ms.lpfCutoffSweepValue, 1, sizeof(float), file); + fread(¶ms.hpfCutoffValue, 1, sizeof(float), file); + fread(¶ms.hpfCutoffSweepValue, 1, sizeof(float), file); + + fread(¶ms.phaserOffsetValue, 1, sizeof(float), file); + fread(¶ms.phaserSweepValue, 1, sizeof(float), file); + fread(¶ms.repeatSpeedValue, 1, sizeof(float), file); + + if (version >= 101) + { + fread(¶ms.changeSpeedValue, 1, sizeof(float), file); + fread(¶ms.changeAmountValue, 1, sizeof(float), file); + } + } + else printf("SFS file version not supported\n"); + + fclose(file); + } + else if (strcmp(GetExtension(fileName),"rfx") == 0) + { + FILE *rfxFile = fopen(fileName, "rb"); + + // Load .rfx sound parameters + char signature[4]; + fread(signature, 4, sizeof(char), rfxFile); + + if ((signature[0] == 'r') && + (signature[0] == 'F') && + (signature[0] == 'X') && + (signature[0] == ' ')) + { + int version; + fread(&version, 1, sizeof(int), rfxFile); + + fread(&volumeValue, 1, sizeof(float), rfxFile); + fread(&wavBitrate, 1, sizeof(int), rfxFile); + fread(&wavFrequency, 1, sizeof(int), rfxFile); + + int channels; + fread(&channels, 1, sizeof(int), rfxFile); + + // Read wave parameters struct + fread(¶ms, 1, sizeof(WaveParams), rfxFile); + } + + fclose(rfxFile); + } + +} + +// Save .rfx (rFXGen) or .sfs (sfxr) sound parameters file +static void SaveSoundParams(const char* fileName) +{ + if (strcmp(GetExtension(fileName),"sfs") == 0) + { + FILE *sfsFile = fopen(fileName, "wb"); + + // Save .sfs sound parameters + int version = 102; + fwrite(&version, 1, sizeof(int), sfsFile); + + fwrite(¶ms.waveTypeValue, 1, sizeof(int), sfsFile); + + fwrite(&volumeValue, 1, sizeof(float), sfsFile); + + fwrite(¶ms.startFrequencyValue, 1, sizeof(float), sfsFile); + fwrite(¶ms.minFrequencyValue, 1, sizeof(float), sfsFile); + fwrite(¶ms.slideValue, 1, sizeof(float), sfsFile); + fwrite(¶ms.deltaSlideValue, 1, sizeof(float), sfsFile); + fwrite(¶ms.squareDutyValue, 1, sizeof(float), sfsFile); + fwrite(¶ms.dutySweepValue, 1, sizeof(float), sfsFile); + + fwrite(¶ms.vibratoDepthValue, 1, sizeof(float), sfsFile); + fwrite(¶ms.vibratoSpeedValue, 1, sizeof(float), sfsFile); + + float vibratoPhaseDelay = 0.0f; + fwrite(&vibratoPhaseDelay, 1, sizeof(float), sfsFile); // Not used + + fwrite(¶ms.attackTimeValue, 1, sizeof(float), sfsFile); + fwrite(¶ms.sustainTimeValue, 1, sizeof(float), sfsFile); + fwrite(¶ms.decayTimeValue, 1, sizeof(float), sfsFile); + fwrite(¶ms.sustainPunchValue, 1, sizeof(float), sfsFile); + + bool filterOn = false; + fwrite(&filterOn, 1, sizeof(bool), sfsFile); // Not used + + fwrite(¶ms.lpfResonanceValue, 1, sizeof(float), sfsFile); + fwrite(¶ms.lpfCutoffValue, 1, sizeof(float), sfsFile); + fwrite(¶ms.lpfCutoffSweepValue, 1, sizeof(float), sfsFile); + fwrite(¶ms.hpfCutoffValue, 1, sizeof(float), sfsFile); + fwrite(¶ms.hpfCutoffSweepValue, 1, sizeof(float), sfsFile); + + fwrite(¶ms.phaserOffsetValue, 1, sizeof(float), sfsFile); + fwrite(¶ms.phaserSweepValue, 1, sizeof(float), sfsFile); + + fwrite(¶ms.repeatSpeedValue, 1, sizeof(float), sfsFile); + + fwrite(¶ms.changeSpeedValue, 1, sizeof(float), sfsFile); + fwrite(¶ms.changeAmountValue, 1, sizeof(float), sfsFile); + + fclose(sfsFile); + } + else if (strcmp(GetExtension(fileName),"rfx") == 0) + { + FILE *rfxFile = fopen(fileName, "wb"); + + // Save .rfx sound parameters + char signature[4] = "rFX "; + fwrite(signature, 4, sizeof(char), rfxFile); + + int version = 100; // File version + fwrite(&version, 1, sizeof(int), rfxFile); + + fwrite(&volumeValue, 1, sizeof(float), rfxFile); + fwrite(&wavBitrate, 1, sizeof(int), rfxFile); + fwrite(&wavFrequency, 1, sizeof(int), rfxFile); + + int channels = 1; + fwrite(&channels, 1, sizeof(int), rfxFile); + + fwrite(¶ms, 1, sizeof(WaveParams), rfxFile); + + fclose(rfxFile); + } +} + +// Draw wave data +// NOTE: For proper visualization, MSAA x4 is recommended, alternatively +// it should be rendered to a bigger texture and then scaled down with +// bilinear/trilinear texture filtering +static void DrawWave(Rectangle bounds, Color color) +{ + float sample, sampleNext; + float currentSample = 0.0f; + float sampleIncrement = (float)wave.sampleCount/(float)(bounds.width*2); + float sampleScale = (float)bounds.height; + + for (int i = 1; i < bounds.width*2 - 1; i++) + { + sample = ((float *)wave.data)[(int)currentSample]*sampleScale; + sampleNext = ((float *)wave.data)[(int)(currentSample + sampleIncrement)]*sampleScale; + + if (sample > bounds.height/2) sample = bounds.height/2; + else if (sample < -bounds.height/2) sample = -bounds.height/2; + + if (sampleNext > bounds.height/2) sampleNext = bounds.height/2; + else if (sampleNext < -bounds.height/2) sampleNext = -bounds.height/2; + + DrawLineV((Vector2){ (float)bounds.x + (float)i/2.0f, (float)(bounds.y + bounds.height/2) + sample }, + (Vector2){ (float)bounds.x + (float)i/2.0f, (float)(bounds.y + bounds.height/2) + sampleNext }, color); + + currentSample += sampleIncrement; + } +} + +// Save wave data to WAV file +static void SaveWAV(const char *fileName, Wave wave) +{ + // Basic WAV headers structs + typedef struct { + char chunkID[4]; + int chunkSize; + char format[4]; + } RiffHeader; + + typedef struct { + char subChunkID[4]; + int subChunkSize; + short audioFormat; + short numChannels; + int sampleRate; + int byteRate; + short blockAlign; + short bitsPerSample; + } WaveFormat; + + typedef struct { + char subChunkID[4]; + int subChunkSize; + } WaveData; + + RiffHeader riffHeader; + WaveFormat waveFormat; + WaveData waveData; + + // Fill structs with data + riffHeader.chunkID[0] = 'R'; + riffHeader.chunkID[1] = 'I'; + riffHeader.chunkID[2] = 'F'; + riffHeader.chunkID[3] = 'F'; + riffHeader.chunkSize = 44 - 4 + wave.sampleCount*wave.sampleSize/8; + riffHeader.format[0] = 'W'; + riffHeader.format[1] = 'A'; + riffHeader.format[2] = 'V'; + riffHeader.format[3] = 'E'; + + waveFormat.subChunkID[0] = 'f'; + waveFormat.subChunkID[1] = 'm'; + waveFormat.subChunkID[2] = 't'; + waveFormat.subChunkID[3] = ' '; + waveFormat.subChunkSize = 16; + waveFormat.audioFormat = 1; + waveFormat.numChannels = 1; + waveFormat.sampleRate = wave.sampleRate; + waveFormat.byteRate = wave.sampleRate*wave.sampleSize/8; + waveFormat.blockAlign = wave.sampleSize/8; + waveFormat.bitsPerSample = wave.sampleSize; + + waveData.subChunkID[0] = 'd'; + waveData.subChunkID[1] = 'a'; + waveData.subChunkID[2] = 't'; + waveData.subChunkID[3] = 'a'; + waveData.subChunkSize = wave.sampleCount*wave.sampleSize/8; + + FILE *wavFile = fopen(fileName, "wb"); + + fwrite(&riffHeader, 1, sizeof(RiffHeader), wavFile); + fwrite(&waveFormat, 1, sizeof(WaveFormat), wavFile); + fwrite(&waveData, 1, sizeof(WaveFormat), wavFile); + + fwrite(wave.data, 1, wave.sampleRate*wave.sampleSize/8, wavFile); + + fclose(wavFile); +} + +//-------------------------------------------------------------------------------------------- +// Buttons functions: sound generation +//-------------------------------------------------------------------------------------------- + +// Generate sound: Pickup/Coin +static void BtnPickupCoin(void) +{ + ResetParams(); + + params.startFrequencyValue = 0.4f + frnd(0.5f); + params.attackTimeValue = 0.0f; + params.sustainTimeValue = frnd(0.1f); + params.decayTimeValue = 0.1f + frnd(0.4f); + params.sustainPunchValue = 0.3f + frnd(0.3f); + + if (rnd(1)) + { + params.changeSpeedValue = 0.5f + frnd(0.2f); + params.changeAmountValue = 0.2f + frnd(0.4f); + } + + GeneratePlay(); +} + +// Generate sound: Laser shoot +static void BtnLaserShoot(void) +{ + ResetParams(); + + params.waveTypeValue = rnd(2); + + if (params.waveTypeValue == 2 && rnd(1)) params.waveTypeValue = rnd(1); + + params.startFrequencyValue = 0.5f + frnd(0.5f); + params.minFrequencyValue = params.startFrequencyValue - 0.2f - frnd(0.6f); + + if (params.minFrequencyValue < 0.2f) params.minFrequencyValue = 0.2f; + + params.slideValue = -0.15f - frnd(0.2f); + + if (rnd(2) == 0) + { + params.startFrequencyValue = 0.3f + frnd(0.6f); + params.minFrequencyValue = frnd(0.1f); + params.slideValue = -0.35f - frnd(0.3f); + } + + if (rnd(1)) + { + params.squareDutyValue = frnd(0.5f); + params.dutySweepValue = frnd(0.2f); + } + else + { + params.squareDutyValue = 0.4f + frnd(0.5f); + params.dutySweepValue = -frnd(0.7f); + } + + params.attackTimeValue = 0.0f; + params.sustainTimeValue = 0.1f + frnd(0.2f); + params.decayTimeValue = frnd(0.4f); + + if (rnd(1)) params.sustainPunchValue = frnd(0.3f); + + if (rnd(2) == 0) + { + params.phaserOffsetValue = frnd(0.2f); + params.phaserSweepValue = -frnd(0.2f); + } + + if (rnd(1)) params.hpfCutoffValue = frnd(0.3f); + + GeneratePlay(); +} + +// Generate sound: Explosion +static void BtnExplosion(void) +{ + ResetParams(); + + params.waveTypeValue = 3; + + if (rnd(1)) + { + params.startFrequencyValue = 0.1f + frnd(0.4f); + params.slideValue = -0.1f + frnd(0.4f); + } + else + { + params.startFrequencyValue = 0.2f + frnd(0.7f); + params.slideValue = -0.2f - frnd(0.2f); + } + + params.startFrequencyValue *= params.startFrequencyValue; + + if (rnd(4) == 0) params.slideValue = 0.0f; + if (rnd(2) == 0) params.repeatSpeedValue = 0.3f + frnd(0.5f); + + params.attackTimeValue = 0.0f; + params.sustainTimeValue = 0.1f + frnd(0.3f); + params.decayTimeValue = frnd(0.5f); + + if (rnd(1) == 0) + { + params.phaserOffsetValue = -0.3f + frnd(0.9f); + params.phaserSweepValue = -frnd(0.3f); + } + + params.sustainPunchValue = 0.2f + frnd(0.6f); + + if (rnd(1)) + { + params.vibratoDepthValue = frnd(0.7f); + params.vibratoSpeedValue = frnd(0.6f); + } + + if (rnd(2) == 0) + { + params.changeSpeedValue = 0.6f + frnd(0.3f); + params.changeAmountValue = 0.8f - frnd(1.6f); + } + + GeneratePlay(); +} + +// Generate sound: Powerup +static void BtnPowerup(void) +{ + ResetParams(); + + if (rnd(1)) params.waveTypeValue = 1; + else params.squareDutyValue = frnd(0.6f); + + if (rnd(1)) + { + params.startFrequencyValue = 0.2f + frnd(0.3f); + params.slideValue = 0.1f + frnd(0.4f); + params.repeatSpeedValue = 0.4f + frnd(0.4f); + } + else + { + params.startFrequencyValue = 0.2f + frnd(0.3f); + params.slideValue = 0.05f + frnd(0.2f); + + if (rnd(1)) + { + params.vibratoDepthValue = frnd(0.7f); + params.vibratoSpeedValue = frnd(0.6f); + } + } + + params.attackTimeValue = 0.0f; + params.sustainTimeValue = frnd(0.4f); + params.decayTimeValue = 0.1f + frnd(0.4f); + + GeneratePlay(); +} + +// Generate sound: Hit/Hurt +static void BtnHitHurt(void) +{ + ResetParams(); + + params.waveTypeValue = rnd(2); + if (params.waveTypeValue == 2) params.waveTypeValue = 3; + if (params.waveTypeValue == 0) params.squareDutyValue = frnd(0.6f); + + params.startFrequencyValue = 0.2f + frnd(0.6f); + params.slideValue = -0.3f - frnd(0.4f); + params.attackTimeValue = 0.0f; + params.sustainTimeValue = frnd(0.1f); + params.decayTimeValue = 0.1f + frnd(0.2f); + + if (rnd(1)) params.hpfCutoffValue = frnd(0.3f); + + GeneratePlay(); +} + +// Generate sound: Jump +static void BtnJump(void) +{ + ResetParams(); + + params.waveTypeValue = 0; + params.squareDutyValue = frnd(0.6f); + params.startFrequencyValue = 0.3f + frnd(0.3f); + params.slideValue = 0.1f + frnd(0.2f); + params.attackTimeValue = 0.0f; + params.sustainTimeValue = 0.1f + frnd(0.3f); + params.decayTimeValue = 0.1f + frnd(0.2f); + + if (rnd(1)) params.hpfCutoffValue = frnd(0.3f); + if (rnd(1)) params.lpfCutoffValue = 1.0f - frnd(0.6f); + + GeneratePlay(); +} + +// Generate sound: Blip/Select +static void BtnBlipSelect(void) +{ + ResetParams(); + + params.waveTypeValue = rnd(1); + if (params.waveTypeValue == 0) params.squareDutyValue = frnd(0.6f); + params.startFrequencyValue = 0.2f + frnd(0.4f); + params.attackTimeValue = 0.0f; + params.sustainTimeValue = 0.1f + frnd(0.1f); + params.decayTimeValue = frnd(0.2f); + params.hpfCutoffValue = 0.1f; + + GeneratePlay(); +} + +// Generate random sound +static void BtnRandomize(void) +{ + params.startFrequencyValue = pow(frnd(2.0f) - 1.0f, 2.0f); + + if (rnd(1)) params.startFrequencyValue = pow(frnd(2.0f) - 1.0f, 3.0f)+0.5f; + + params.minFrequencyValue = 0.0f; + params.slideValue = pow(frnd(2.0f) - 1.0f, 5.0f); + + if ((params.startFrequencyValue > 0.7f) && (params.slideValue > 0.2f)) params.slideValue = -params.slideValue; + if ((params.startFrequencyValue < 0.2f) && (params.slideValue < -0.05f)) params.slideValue = -params.slideValue; + + params.deltaSlideValue = pow(frnd(2.0f) - 1.0f, 3.0f); + params.squareDutyValue = frnd(2.0f) - 1.0f; + params.dutySweepValue = pow(frnd(2.0f) - 1.0f, 3.0f); + params.vibratoDepthValue = pow(frnd(2.0f) - 1.0f, 3.0f); + params.vibratoSpeedValue = frnd(2.0f) - 1.0f; + //params.vibratoPhaseDelay = frnd(2.0f) - 1.0f; + params.attackTimeValue = pow(frnd(2.0f) - 1.0f, 3.0f); + params.sustainTimeValue = pow(frnd(2.0f) - 1.0f, 2.0f); + params.decayTimeValue = frnd(2.0f)-1.0f; + params.sustainPunchValue = pow(frnd(0.8f), 2.0f); + + if (params.attackTimeValue + params.sustainTimeValue + params.decayTimeValue < 0.2f) + { + params.sustainTimeValue += 0.2f + frnd(0.3f); + params.decayTimeValue += 0.2f + frnd(0.3f); + } + + params.lpfResonanceValue = frnd(2.0f) - 1.0f; + params.lpfCutoffValue = 1.0f - pow(frnd(1.0f), 3.0f); + params.lpfCutoffSweepValue = pow(frnd(2.0f) - 1.0f, 3.0f); + + if (params.lpfCutoffValue < 0.1f && params.lpfCutoffSweepValue < -0.05f) params.lpfCutoffSweepValue = -params.lpfCutoffSweepValue; + + params.hpfCutoffValue = pow(frnd(1.0f), 5.0f); + params.hpfCutoffSweepValue = pow(frnd(2.0f) - 1.0f, 5.0f); + params.phaserOffsetValue = pow(frnd(2.0f) - 1.0f, 3.0f); + params.phaserSweepValue = pow(frnd(2.0f) - 1.0f, 3.0f); + params.repeatSpeedValue = frnd(2.0f) - 1.0f; + params.changeSpeedValue = frnd(2.0f) - 1.0f; + params.changeAmountValue = frnd(2.0f) - 1.0f; + + GeneratePlay(); +} + +// Mutate current sound +static void BtnMutate(void) +{ + if (rnd(1)) params.startFrequencyValue += frnd(0.1f) - 0.05f; + //if (rnd(1)) params.minFrequencyValue += frnd(0.1f) - 0.05f; + if (rnd(1)) params.slideValue += frnd(0.1f) - 0.05f; + if (rnd(1)) params.deltaSlideValue += frnd(0.1f) - 0.05f; + if (rnd(1)) params.squareDutyValue += frnd(0.1f) - 0.05f; + if (rnd(1)) params.dutySweepValue += frnd(0.1f) - 0.05f; + if (rnd(1)) params.vibratoDepthValue += frnd(0.1f) - 0.05f; + if (rnd(1)) params.vibratoSpeedValue += frnd(0.1f) - 0.05f; + //if (rnd(1)) params.vibratoPhaseDelay += frnd(0.1f) - 0.05f; + if (rnd(1)) params.attackTimeValue += frnd(0.1f) - 0.05f; + if (rnd(1)) params.sustainTimeValue += frnd(0.1f) - 0.05f; + if (rnd(1)) params.decayTimeValue += frnd(0.1f) - 0.05f; + if (rnd(1)) params.sustainPunchValue += frnd(0.1f) - 0.05f; + if (rnd(1)) params.lpfResonanceValue += frnd(0.1f) - 0.05f; + if (rnd(1)) params.lpfCutoffValue += frnd(0.1f) - 0.05f; + if (rnd(1)) params.lpfCutoffSweepValue += frnd(0.1f) - 0.05f; + if (rnd(1)) params.hpfCutoffValue += frnd(0.1f) - 0.05f; + if (rnd(1)) params.hpfCutoffSweepValue += frnd(0.1f) - 0.05f; + if (rnd(1)) params.phaserOffsetValue += frnd(0.1f) - 0.05f; + if (rnd(1)) params.phaserSweepValue += frnd(0.1f) - 0.05f; + if (rnd(1)) params.repeatSpeedValue += frnd(0.1f) - 0.05f; + if (rnd(1)) params.changeSpeedValue += frnd(0.1f) - 0.05f; + if (rnd(1)) params.changeAmountValue += frnd(0.1f) - 0.05f; + + GeneratePlay(); +} + +//-------------------------------------------------------------------------------------------- +// Buttons functions: sound playing and export functions +//-------------------------------------------------------------------------------------------- + +// Play current sound +static void BtnPlaySound(void) { PlaySound(sound); } + +// Load sound parameters file +static void BtnLoadSound(void) +{ + // Open file dialog + const char *filters[] = { "*.rfx", "*.sfs" }; + const char *fileName = tinyfd_openFileDialog("Load sound parameters file", currentPath, 2, filters, "Sound Param Files (*.rfx, *.sfs)", 0); + + if (fileName != NULL) + { + LoadSoundParams(fileName); + //GeneratePlay(); + } +} + +// Save sound parameters file +static void BtnSaveSound(void) +{ + char currrentPathFile[256]; + + // Add sample file name to currentPath + strcpy(currrentPathFile, currentPath); + strcat(currrentPathFile, "sound.rfx\0"); + + // Save file dialog + const char *filters[] = { "*.rfx", "*.sfs" }; + const char *fileName = tinyfd_saveFileDialog("Save sound parameters file", currrentPathFile, 2, filters, "Sound Param Files (*.rfx, *.sfs)"); + + if (fileName != NULL) SaveSoundParams(fileName); +} + +// Export current sound as .wav +static void BtnExportWav(void) +{ + char currrentPathFile[256]; + + // Add sample file name to currentPath + strcpy(currrentPathFile, currentPath); + strcat(currrentPathFile, "sound.wav\0"); + + // Save file dialog + const char *filters[] = { "*.wav" }; + const char *fileName = tinyfd_saveFileDialog("Save wave file", currrentPathFile, 1, filters, "Wave File (*.wav)"); + + Wave ewave = WaveCopy(wave); + WaveFormat(&ewave, wavFrequency, wavBitrate, 1); + + SaveWAV(fileName, ewave); + + UnloadWave(ewave); +} + +//-------------------------------------------------------------------------------------------- +// Helper functions +//-------------------------------------------------------------------------------------------- + +// Get the extension for a filename +static const char *GetExtension(const char *fileName) +{ + const char *dot = strrchr(fileName, '.'); + if (!dot || dot == fileName) return ""; + return (dot + 1); +} diff --git a/tools/rFXGen/twitter.bmp b/tools/rFXGen/twitter.bmp new file mode 100644 index 0000000000000000000000000000000000000000..4dfa7a30230ae82735eb40eafd717887aad9056e GIT binary patch literal 822 zcmd6ju?>Sj3`L1jra7fbNtyC0YLq?_BQOFZ+y*xSBism#zz8=2BP0(DmIX;0#nK($ z|Gq!AAJ6JsEKzaaIc`0t;N(=4l6VE%=(6