├── .editorconfig ├── .gitignore ├── LICENSE ├── README.md ├── dub.sdl └── tinyfiledialogs.d /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | indent_style = space 8 | indent_size = 4 9 | trim_trailing_whitespace = true 10 | max_line_length = 120 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.obj 3 | *.so 4 | *.dylib 5 | *.dll 6 | *.a 7 | *.lib 8 | *.exe 9 | 10 | .dub/ 11 | dub.selections.json 12 | *.~* 13 | *.*~ 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | zlib License 2 | 3 | (C) 2014-2019 Guillaume Vareille 4 | (C) 2019 dayllenger 5 | 6 | This software is provided 'as-is', without any express or implied 7 | warranty. In no event will the authors be held liable for any damages 8 | arising from the use of this software. 9 | 10 | Permission is granted to anyone to use this software for any purpose, 11 | including commercial applications, and to alter it and redistribute it 12 | freely, subject to the following restrictions: 13 | 14 | 1. The origin of this software must not be misrepresented; you must not 15 | claim that you wrote the original software. If you use this software 16 | in a product, an acknowledgment in the product documentation would be 17 | appreciated but is not required. 18 | 2. Altered source versions must be plainly marked as such, and must not be 19 | misrepresented as being the original software. 20 | 3. This notice may not be removed or altered from any source distribution. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## tinyfiledialogs 2 | 3 | Native dialog library for Windows, macOS, GTK+, Qt, console & more. 4 | SSH supported via automatic switch to console mode or X11 forwarding. 5 | 6 | *NOTE: this package may not reflect the latest version of [the original library by its author](https://sourceforge.net/projects/tinyfiledialogs/).* 7 | 8 | *NOTE: the main purpose of this package is to implement native file and color dialogs in [beamui](https://github.com/dayllenger/beamui), so don't expect much support and development.* 9 | 10 |
11 | 12 | Originally pure C89. To build in betterC mode, enable `BC` subconfiguration. 13 | 14 | For examples and info take a look at [the documentation](https://tinyfiledialogs.dpldocs.info/tinyfiledialogs.html). 15 | 16 | Short example of how to open a save dialog and show the name of chosen file in a message box: 17 | ```D 18 | import tinyfiledialogs; 19 | 20 | // pairs of pattern list and optional description 21 | const TFD_Filter[] filters = [ 22 | { ["*.png" ], "PNG image" }, 23 | { ["*.jpg", "*.jpeg" ], "JPEG image" }, 24 | { ["*.tif", "*.tiff" ], "TIFF image" }, 25 | { ["*.tga" ], "TarGA image" }, 26 | ]; 27 | // it blocks until the dialog is closed 28 | const char* filename = tinyfd_saveFileDialog("Save as...", "Untitled.png", filters); 29 | if (filename) 30 | { 31 | // now we can save our file physically 32 | tinyfd_messageBox("The filename is", filename, "ok", "info", 1); 33 | } 34 | ``` 35 | -------------------------------------------------------------------------------- /dub.sdl: -------------------------------------------------------------------------------- 1 | name "tinyfiledialogs" 2 | description "Cross-platform single-file library for native dialogs." 3 | authors "Guillaume Vareille" "dayllenger" 4 | license "Zlib" 5 | 6 | importPaths "." 7 | sourceFiles "tinyfiledialogs.d" 8 | 9 | configuration "default" { 10 | targetType "staticLibrary" 11 | } 12 | configuration "BC" { 13 | buildOptions "betterC" 14 | targetType "staticLibrary" 15 | } 16 | -------------------------------------------------------------------------------- /tinyfiledialogs.d: -------------------------------------------------------------------------------- 1 | /*________ 2 | / \ This is a custom D port of tinyfiledialogs v3.3.9 [Apr 14, 2019] 3 | |tiny file| http://tinyfiledialogs.sourceforge.net 4 | | dialogs | Copyright (c) 2014-2019 Guillaume Vareille 5 | \____ ___/ Copyright (c) 2019 dayllenger 6 | \| 7 | */ 8 | /** 9 | Native dialog library for Windows, macOS, GTK+, Qt, console & more. 10 | SSH supported via automatic switch to console mode or X11 forwarding. 11 | 12 | 8 functions: 13 | $(LIST 14 | * beep 15 | * notify popup (tray) 16 | * message & question 17 | * input & password 18 | * save file 19 | * open file(s) 20 | * select folder 21 | * color picker 22 | ) 23 | Each function is documented with examples. 24 | The dialogs can be forced into console mode. 25 | Supports UTF-8 (except Windows console). 26 | 27 | Windows XP to 10: 28 | $(LIST 29 | * native code & vbs create the graphic dialogs 30 | * enhanced console mode can use dialog.exe from 31 | `http://andrear.altervista.org/home/cdialog.php` 32 | * basic console input 33 | ) 34 | Unix (using command line calls): 35 | $(LIST 36 | * applescript, kdialog, zenity 37 | * python (2 or 3) + tkinter + python-dbus (optional) 38 | * whiptail, dialog (opens a console if needed) 39 | * basic console input 40 | ) 41 | The same executable can run across desktops & distributions. 42 | 43 | Notes 44 | ===== 45 | $(LIST 46 | * This is $(I not) for Android nor iOS. 47 | * The code is betterC compatible, originally pure C89. 48 | * Windows is fully supported from XP to 10 (maybe even older versions). 49 | * OSX supported from 10.4 to latest (maybe even older versions). 50 | * On Windows, it links against comdlg32.lib, ole32.lib, and user32.lib. 51 | * Set TINYFD_NOLIB version if you don't want to include the code creating 52 | graphic dialogs. Then you won't need to link against those libs. 53 | * On Unix, it tries command line calls. 54 | They are already available on most (if not all) desktops. 55 | * In the absence of those it will use gdialog, gxmessage or whiptail 56 | with a textinputbox. 57 | * If nothing is found, it switches to basic console input, 58 | and opens a console if needed (requires xterm + bash). 59 | * On OSX, the package dialog can be installed via 60 | `http://macappstore.org/dialog` or `https://www.macports.org/` 61 | * On Windows, for enhanced console mode, 62 | dialog.exe should be copied somewhere on your executable path. 63 | It can be found at the bottom of the following page: 64 | `http://andrear.altervista.org/home/cdialog.php` 65 | * If dialog is missing, it will switch to basic console input. 66 | * Mutiple selects are not allowed in console mode. 67 | ) 68 | $(LIST 69 | * Avoid using " and ' in titles and messages. 70 | * String memory is preallocated statically for all the returned values. 71 | * Use platform-specific path separators: \ on Windows, / on Unix. 72 | * If you pass only a path instead of path + filename, 73 | make sure it ends with a separator. 74 | * File and path names are tested before return, they are valid. 75 | * You can query the type of dialog that will be used, see [tinyfd_response]. 76 | ) 77 | Thanks for contributions, bug corrections & thorough testing to: 78 | $(LIST 79 | * Don Heyse http://ldglite.sf.net for bug corrections & thorough testing! 80 | * Paul Rouget 81 | ) 82 | */ 83 | /* 84 | zlib License 85 | 86 | This software is provided 'as-is', without any express or implied 87 | warranty. In no event will the authors be held liable for any damages 88 | arising from the use of this software. 89 | 90 | Permission is granted to anyone to use this software for any purpose, 91 | including commercial applications, and to alter it and redistribute it 92 | freely, subject to the following restrictions: 93 | 94 | 1. The origin of this software must not be misrepresented; you must not 95 | claim that you wrote the original software. If you use this software 96 | in a product, an acknowledgment in the product documentation would be 97 | appreciated but is not required. 98 | 2. Altered source versions must be plainly marked as such, and must not be 99 | misrepresented as being the original software. 100 | 3. This notice may not be removed or altered from any source distribution. 101 | */ 102 | module tinyfiledialogs; 103 | 104 | // version = TINYFD_NOLIB; 105 | // version = TINYFD_NOSELECTFOLDERWIN; 106 | version = TINYFD_NOCCSUNICODE; 107 | 108 | extern (C) nothrow @nogc: 109 | 110 | alias c_str = const(char*); 111 | 112 | /// No params, no return value, just beep 113 | void tinyfd_beep() 114 | { 115 | _beep(); 116 | } 117 | 118 | /** Params: 119 | title = C-string or null 120 | message = C-string or null, can be multiline 121 | iconType = "info" "warning" "error" 122 | 123 | Returns: 124 | Return has only meaning for "tinyfd_query". 125 | 126 | Example: 127 | --- 128 | tinyfd_notifyPopup("the title", "the message from outer-space", "info"); 129 | --- 130 | */ 131 | int tinyfd_notifyPopup(c_str title, c_str message, c_str iconType) 132 | { 133 | return _notifyPopup(title, message, iconType); 134 | } 135 | 136 | /** Params: 137 | title = C-string or null 138 | message = C-string or null, can be multiline 139 | dialogType = "ok" "okcancel" "yesno" "yesnocancel" 140 | iconType = "info" "warning" "error" "question" 141 | defaultButton = 0 - cancel/no, 1 - ok/yes, 2 - no in yesnocancel 142 | 143 | Returns: 144 | 0 - cancel/no, 1 - ok/yes, 2 - no in yesnocancel. 145 | 146 | Example: 147 | --- 148 | const int ret = tinyfd_messageBox("Hello World", 149 | "graphic dialogs [yes] / console mode [no]?", 150 | "yesno", "question", 1); 151 | tinyfd_forceConsole = (ret == 0); 152 | --- 153 | */ 154 | int tinyfd_messageBox( 155 | c_str title, 156 | c_str message, 157 | c_str dialogType, 158 | c_str iconType, 159 | int defaultButton, 160 | ) 161 | { 162 | return _messageBox(title, message, dialogType, iconType, defaultButton); 163 | } 164 | 165 | /** Params: 166 | title = C-string or null 167 | message = C-string or null; some platforms (Windows, GTK-based) can't display multiline text 168 | defaultInput = C-string, if null it's a password box 169 | 170 | Returns: 171 | Entered text, `null` on cancel. 172 | 173 | Example: 174 | --- 175 | c_str passwd = tinyfd_inputBox("A password box", "Your password will be revealed", null); 176 | if (passwd) 177 | tinyfd_notifyPopup("Your password is:", passwd, "warning"); 178 | --- 179 | */ 180 | c_str tinyfd_inputBox(c_str title, c_str message, c_str defaultInput) 181 | { 182 | return _inputBox(title, message, defaultInput); 183 | } 184 | 185 | /// Single filter, used in open/save file dialogs 186 | struct TFD_Filter 187 | { 188 | /// Patterns like `["*.jpg", "*.png"]` or MIME types (kdialog only) `["audio/mp3"]` 189 | c_str[] patterns; 190 | /// Description like "Image files". If none, the list of patterns becomes the description 191 | c_str description; 192 | } 193 | 194 | /** Params: 195 | title = C-string or null 196 | defaultPathAndFile = C-string or null 197 | filters = list of file type patterns 198 | 199 | Returns: 200 | Selected file full name, `null` on cancel. 201 | 202 | Example: 203 | --- 204 | const TFD_Filter[] filters = [ 205 | { ["*.css" ], "CSS" }, 206 | { ["*.sass", "*.scss" ], "SASS" }, 207 | ]; 208 | c_str filename = tinyfd_saveFileDialog("Save as...", "style.css", filters); 209 | if (filename) 210 | tinyfd_messageBox("Chosen file is", filename, "ok", "info", 1); 211 | --- 212 | */ 213 | c_str tinyfd_saveFileDialog( 214 | c_str title, 215 | c_str defaultPathAndFile, 216 | const TFD_Filter[] filters, 217 | ) 218 | { 219 | return _saveFileDialog(title, defaultPathAndFile, filters); 220 | } 221 | 222 | /** Params: 223 | title = C-string or null 224 | defaultPathAndFile = C-string or null 225 | filters = list of file type patterns 226 | allowMultipleSelects = does not work on console 227 | 228 | Returns: 229 | Selected file full name, `null` on cancel. In case of multiple files, the separator is |. 230 | 231 | Example: 232 | --- 233 | const TFD_Filter[] filters = [ 234 | { ["*.c" ], "C source code" }, 235 | { ["*.cpp", "*.cc", "*.C", "*.cxx", "*.c++"], "C++ source code" } 236 | ]; 237 | c_str filename = tinyfd_openFileDialog("Open a C/C++ File", null, filters, false); 238 | if (filename) 239 | tinyfd_messageBox("Chosen file is", filename, "ok", "info", 1); 240 | --- 241 | */ 242 | c_str tinyfd_openFileDialog( 243 | c_str title, 244 | c_str defaultPathAndFile, 245 | const TFD_Filter[] filters, 246 | bool allowMultipleSelects, 247 | ) 248 | { 249 | return _openFileDialog(title, defaultPathAndFile, filters, allowMultipleSelects); 250 | } 251 | 252 | /** Params: 253 | title = C-string or null 254 | defaultPath = C-string or null 255 | 256 | Returns: 257 | Selected folder path, `null` on cancel 258 | 259 | Example: 260 | --- 261 | c_str name = tinyfd_selectFolderDialog("Let us just select a directory", null); 262 | if (name) 263 | tinyfd_messageBox("The selected folder is", name, "ok", "info", 1); 264 | --- 265 | */ 266 | c_str tinyfd_selectFolderDialog(c_str title, c_str defaultPath) 267 | { 268 | return _selectFolderDialog(title, defaultPath); 269 | } 270 | 271 | /** Params: 272 | title = C-string or null 273 | defaultHexRGB = default color, C-string or null 274 | defaultRGB = used only if the previous parameter is null 275 | resultRGB = contains the decoded result 276 | 277 | Returns: 278 | The hexcolor as a string like "#FF0000" or `null` on cancel. 279 | 280 | Example: 281 | --- 282 | ubyte[3] rgb; 283 | c_str hexColor = tinyfd_colorChooser("Choose a nice color", "#FF0077", rgb, rgb); 284 | if (hexColor) 285 | tinyfd_messageBox("The selected hexcolor is", hexColor, "ok", "info", 1); 286 | --- 287 | */ 288 | c_str tinyfd_colorChooser( 289 | c_str title, 290 | c_str defaultHexRGB, 291 | ref const ubyte[3] defaultRGB, 292 | ref ubyte[3] resultRGB, 293 | ) 294 | { 295 | return _colorChooser(title, defaultHexRGB, defaultRGB, resultRGB); 296 | } 297 | 298 | /**** Constants and variables ****/ 299 | __gshared: 300 | 301 | /// Contains tinyfd current version number 302 | immutable char[8] tinyfd_version = "3.3.9"; 303 | 304 | /// Info about requirements for a platform 305 | version (Windows) 306 | immutable char[] tinyfd_needs = 307 | ` ___________ 308 | / \ 309 | | tiny file | 310 | | dialogs | 311 | \_____ ____/ 312 | \| 313 | tiny file dialogs on Windows needs: 314 | a graphic display 315 | or dialog.exe (enhanced console mode) 316 | or a console for basic input`; 317 | else 318 | immutable char[] tinyfd_needs = 319 | ` ___________ 320 | / \ 321 | | tiny file | 322 | | dialogs | 323 | \_____ ____/ 324 | \| 325 | tiny file dialogs on UNIX needs: 326 | applescript 327 | or kdialog 328 | or zenity (or matedialog or qarma) 329 | or python (2 or 3) 330 | + tkinter + python-dbus (optional) 331 | or dialog (opens console if needed) 332 | or xterm + bash 333 | (opens console for basic input) 334 | or existing console for basic input`; 335 | 336 | /// On unix, prints the command line calls; default is `false` 337 | bool tinyfd_verbose; 338 | /// On unix, hide errors and warnings from called dialog; default is `true` 339 | bool tinyfd_silent = true; 340 | 341 | /** `false` (default) - try to use a graphic solution, if it fails use console mode. 342 | `true` - force all dialogs into console mode even when an X server is present, 343 | if the package `whiptail` or `dialog` and a console are present in a system, 344 | or `dialog.exe` is installed. 345 | On Windows it only make sense for console applications. 346 | */ 347 | version (Windows) 348 | { 349 | version (TINYFD_NOLIB) 350 | bool tinyfd_forceConsole = true; 351 | else 352 | bool tinyfd_forceConsole; 353 | } 354 | else 355 | bool tinyfd_forceConsole; 356 | 357 | /** If you pass "tinyfd_query" as `title`, the functions will not display 358 | the dialogs but will return 0 for console mode, 1 for graphic mode. 359 | `tinyfd_response` is then filled with the retain solution. 360 | possible values for `tinyfd_response` are (all lowercase), 361 | for graphic mode: `windows` 362 | `applescript kdialog zenity zenity3 matedialog qarma` 363 | `python2-tkinter python3-tkinter python-dbus perl-dbus` 364 | `gxmessage gmessage xmessage xdialog gdialog`, 365 | for console mode: `dialog whiptail basicinput no_solution` 366 | 367 | Example: 368 | --- 369 | import core.stdc.string; 370 | 371 | char[1024] buf = '\0'; 372 | c_str mode = tinyfd_inputBox("tinyfd_query", null, null); 373 | 374 | strcpy(buf.ptr, "v"); 375 | strcat(buf.ptr, tinyfd_version.ptr); 376 | strcat(buf.ptr, "\n"); 377 | if (mode) 378 | strcat(buf.ptr, "graphic mode: "); 379 | else 380 | strcat(buf.ptr, "console mode: "); 381 | strcat(buf.ptr, tinyfd_response.ptr); 382 | strcat(buf.ptr, "\n"); 383 | strcat(buf.ptr, tinyfd_needs.ptr + 78); 384 | tinyfd_messageBox("Hello", buf.ptr, "ok", "info", 0); 385 | --- 386 | */ 387 | char[1024] tinyfd_response = '\0'; 388 | 389 | /**************************** IMPLEMENTATION ****************************/ 390 | 391 | private: 392 | import core.stdc.ctype; 393 | import core.stdc.stdlib; 394 | import core.stdc.string; 395 | 396 | version (Windows) 397 | { 398 | import core.stdc.stdio; 399 | import core.stdc.wchar_; 400 | import core.sys.windows.commdlg; 401 | import core.sys.windows.w32api; 402 | import core.sys.windows.winbase; 403 | import core.sys.windows.wincon : GetConsoleMode, SetConsoleMode, GetConsoleWindow, ENABLE_ECHO_INPUT; 404 | import core.sys.windows.windef; 405 | import core.sys.windows.winnls; 406 | 407 | static assert(_WIN32_WINNT >= 0x0500); 408 | 409 | enum SLASH = "\\"; 410 | 411 | int _getch(); 412 | FILE* _popen(const char* command, const char* mode); 413 | int _pclose(FILE* stream); 414 | 415 | version (TINYFD_NOLIB) 416 | { 417 | bool gWarningDisplayed = true; 418 | } 419 | else 420 | { 421 | version = TINYFD_LIB; 422 | import core.sys.windows.com : COINIT_APARTMENTTHREADED; 423 | import core.sys.windows.wingdi : RGB, GetRValue, GetGValue, GetBValue; 424 | import core.sys.windows.winuser; 425 | 426 | pragma(lib, "comdlg32.lib"); 427 | pragma(lib, "ole32.lib"); 428 | pragma(lib, "user32.lib"); 429 | 430 | FILE* _wfopen(const wchar* filename, const wchar* mode); 431 | int _wremove(const wchar* path); 432 | wchar* _wgetenv(const wchar* varname); 433 | // header versions are not nothrow @nogc 434 | extern(Windows) HRESULT CoInitializeEx(LPVOID, DWORD); 435 | extern(Windows) void CoUninitialize(); 436 | 437 | bool gWarningDisplayed; 438 | } 439 | 440 | version (TINYFD_NOSELECTFOLDERWIN) {} 441 | else 442 | { 443 | version = TINYFD_SELECTFOLDERWIN; 444 | import core.sys.windows.shlobj : BFFM_INITIALIZED, BFFM_SETSELECTIONW, BIF_USENEWUI, 445 | BROWSEINFOW, LPCITEMIDLIST, LPITEMIDLIST, PBROWSEINFOW; 446 | 447 | extern(Windows) LPITEMIDLIST SHBrowseForFolderW(PBROWSEINFOW); 448 | extern(Windows) BOOL SHGetPathFromIDListW(LPCITEMIDLIST, LPWSTR); 449 | } 450 | } 451 | else 452 | { 453 | import core.stdc.limits; 454 | import core.sys.posix.dirent; 455 | import core.sys.posix.signal; 456 | import core.sys.posix.stdio; 457 | import core.sys.posix.sys.stat; 458 | import core.sys.posix.sys.utsname; 459 | import core.sys.posix.termios; 460 | import core.sys.posix.unistd; 461 | 462 | enum SLASH = "/"; 463 | bool gWarningDisplayed; 464 | } 465 | 466 | enum int MAX_PATH_OR_CMD = 1024; /* _MAX_PATH or MAX_PATH */ 467 | enum int MAX_MULTIPLE_FILES = 32; 468 | enum int MAX_PATTERNS = 8; 469 | 470 | immutable char[] gTitle = "missing software! (we will try basic console input)"; 471 | 472 | bool some(const char* str) 473 | { 474 | return str && str[0] != '\0'; 475 | } 476 | 477 | bool wsome(const wchar* str) 478 | { 479 | return str && str[0] != '\0'; 480 | } 481 | 482 | bool eq(const char* s1, const char* s2) 483 | { 484 | if (s1 is s2) 485 | return true; 486 | if (s1 is null || s2 is null) 487 | return false; 488 | return strcmp(s1, s2) == 0; 489 | } 490 | 491 | char lastch(const char* str) 492 | { 493 | if (str) 494 | { 495 | size_t len = strlen(str); 496 | if (len > 0) 497 | return str[len - 1]; 498 | } 499 | return '\0'; 500 | } 501 | 502 | void removeLastNL(char* str) 503 | { 504 | size_t len = strlen(str); 505 | if (len > 0 && str[len - 1] == '\n') 506 | { 507 | str[len - 1] = '\0'; 508 | } 509 | } 510 | 511 | void response(const char* message) 512 | { 513 | strcpy(tinyfd_response.ptr, message); 514 | } 515 | 516 | char* getPathWithoutFinalSlash( 517 | char* aoDestination, /* make sure it is allocated, use _MAX_PATH */ 518 | const char* aSource) /* aoDestination and aSource can be the same */ 519 | { 520 | const(char)* lTmp; 521 | if (aSource) 522 | { 523 | lTmp = strrchr(aSource, '/'); 524 | if (!lTmp) 525 | { 526 | lTmp = strrchr(aSource, '\\'); 527 | } 528 | if (lTmp) 529 | { 530 | strncpy(aoDestination, aSource, lTmp - aSource); 531 | aoDestination[lTmp - aSource] = '\0'; 532 | } 533 | else 534 | { 535 | *aoDestination = '\0'; 536 | } 537 | } 538 | else 539 | { 540 | *aoDestination = '\0'; 541 | } 542 | return aoDestination; 543 | } 544 | 545 | char* getLastName( 546 | char* aoDestination, /* make sure it is allocated */ 547 | const char* aSource) 548 | { 549 | /* copy the last name after '/' or '\' */ 550 | const(char)* lTmp; 551 | if (aSource) 552 | { 553 | lTmp = strrchr(aSource, '/'); 554 | if (!lTmp) 555 | { 556 | lTmp = strrchr(aSource, '\\'); 557 | } 558 | if (lTmp) 559 | { 560 | strcpy(aoDestination, lTmp + 1); 561 | } 562 | else 563 | { 564 | strcpy(aoDestination, aSource); 565 | } 566 | } 567 | else 568 | { 569 | *aoDestination = '\0'; 570 | } 571 | return aoDestination; 572 | } 573 | 574 | void ensureFinalSlash(char* aioString) 575 | { 576 | if (some(aioString)) 577 | { 578 | char* lastcar = aioString + strlen(aioString) - 1; 579 | if (strncmp(lastcar, SLASH, 1)) 580 | { 581 | strcat(lastcar, SLASH); 582 | } 583 | } 584 | } 585 | 586 | uint getValidPatterns(ref const TFD_Filter filter, out bool isMime, out const(char)*[MAX_PATTERNS] output) 587 | { 588 | uint len; 589 | foreach (pt; filter.patterns) 590 | { 591 | if (some(pt)) 592 | { 593 | output[len] = pt; 594 | len++; 595 | if (len == MAX_PATTERNS) 596 | break; 597 | } 598 | } 599 | if (len == 0) 600 | return 0; 601 | 602 | uint mime; 603 | foreach (pt; output[0 .. len]) 604 | { 605 | if (strchr(pt, '/')) 606 | mime++; 607 | else 608 | break; 609 | } 610 | if (mime > 0) 611 | { 612 | // forbid mixing of MIME and usual patterns 613 | if (mime != len) 614 | { 615 | isMime = false; 616 | output[0 .. len] = null; 617 | return 0; 618 | } 619 | isMime = true; 620 | } 621 | return len; 622 | } 623 | 624 | void Hex2RGB(const char* aHexRGB, ref ubyte[3] aoResultRGB) 625 | { 626 | char[8] lColorChannel = '\0'; 627 | if (aHexRGB) 628 | { 629 | strcpy(lColorChannel.ptr, aHexRGB); 630 | aoResultRGB[2] = cast(ubyte)strtoul(lColorChannel.ptr + 5, null, 16); 631 | lColorChannel[5] = '\0'; 632 | aoResultRGB[1] = cast(ubyte)strtoul(lColorChannel.ptr + 3, null, 16); 633 | lColorChannel[3] = '\0'; 634 | aoResultRGB[0] = cast(ubyte)strtoul(lColorChannel.ptr + 1, null, 16); 635 | /* printf("%d %d %d\n", aoResultRGB[0], aoResultRGB[1], aoResultRGB[2]); */ 636 | } 637 | else 638 | { 639 | aoResultRGB[0] = 0; 640 | aoResultRGB[1] = 0; 641 | aoResultRGB[2] = 0; 642 | } 643 | } 644 | 645 | void RGB2Hex(const ubyte[3] aRGB, char* aoResultHexRGB) 646 | { 647 | if (aoResultHexRGB) 648 | { 649 | // NOTE: here was compiler ifdef 650 | sprintf(aoResultHexRGB, "#%02hhx%02hhx%02hhx", aRGB[0], aRGB[1], aRGB[2]); 651 | /* printf("aoResultHexRGB %s\n", aoResultHexRGB); */ 652 | } 653 | } 654 | 655 | void replaceSubStr(const char* aSource, 656 | const char* aOldSubStr, 657 | const char* aNewSubStr, 658 | char* aoDestination) 659 | { 660 | const(char)* pOccurence; 661 | const(char)* p; 662 | const(char)* lNewSubStr = ""; 663 | size_t lOldSubLen = strlen(aOldSubStr); 664 | 665 | if (!aSource) 666 | { 667 | *aoDestination = '\0'; 668 | return; 669 | } 670 | if (!aOldSubStr) 671 | { 672 | strcpy(aoDestination, aSource); 673 | return; 674 | } 675 | if (aNewSubStr) 676 | { 677 | lNewSubStr = aNewSubStr; 678 | } 679 | p = aSource; 680 | *aoDestination = '\0'; 681 | while ((pOccurence = strstr(p, aOldSubStr)) !is null) 682 | { 683 | strncat(aoDestination, p, pOccurence - p); 684 | strcat(aoDestination, lNewSubStr); 685 | p = pOccurence + lOldSubLen; 686 | } 687 | strcat(aoDestination, p); 688 | } 689 | 690 | bool filenameValid(const char* aFileNameWithoutPath) 691 | { 692 | return some(aFileNameWithoutPath) && !strpbrk(aFileNameWithoutPath, "\\/:*?\"<>|"); 693 | } 694 | 695 | void wipefile(const char* aFilename) 696 | { 697 | // file must exist beforehand 698 | FILE* lIn = fopen(aFilename, "w"); 699 | if (lIn) 700 | { 701 | fseek(lIn, 0, SEEK_END); 702 | const size = ftell(lIn); 703 | rewind(lIn); 704 | foreach (i; 0 .. size) 705 | { 706 | fputc('A', lIn); 707 | } 708 | fclose(lIn); 709 | } 710 | } 711 | 712 | /* source and destination can be the same or ovelap*/ 713 | const(char)* ensureFilesExist(char* aDestination, const char* aSourcePathsAndNames) 714 | { 715 | if (!some(aSourcePathsAndNames)) 716 | return null; 717 | 718 | char* lDestination = aDestination; 719 | const(char)* p; 720 | const(char)* p2; 721 | size_t lLen = strlen(aSourcePathsAndNames); 722 | 723 | p = aSourcePathsAndNames; 724 | while ((p2 = strchr(p, '|')) !is null) 725 | { 726 | lLen = p2 - p; 727 | memmove(lDestination, p, lLen); 728 | lDestination[lLen] = '\0'; 729 | if (fileExists(lDestination)) 730 | { 731 | lDestination += lLen; 732 | *lDestination = '|'; 733 | lDestination++; 734 | } 735 | p = p2 + 1; 736 | } 737 | if (fileExists(p)) 738 | { 739 | lLen = strlen(p); 740 | memmove(lDestination, p, lLen); 741 | lDestination[lLen] = '\0'; 742 | } 743 | else 744 | { 745 | *(lDestination - 1) = '\0'; 746 | } 747 | return aDestination; 748 | } 749 | 750 | version (Windows) 751 | { 752 | bool fileExists(const char* aFilePathAndName) 753 | { 754 | if (!some(aFilePathAndName)) 755 | return false; 756 | 757 | wchar* lTmpWChar = utf8to16(aFilePathAndName); 758 | DWORD attribs = GetFileAttributes(lTmpWChar); 759 | free(lTmpWChar); 760 | return !(attribs & FILE_ATTRIBUTE_DEVICE) && !(attribs & FILE_ATTRIBUTE_DIRECTORY); 761 | } 762 | } 763 | else // unix 764 | { 765 | bool fileExists(const char* aFilePathAndName) 766 | { 767 | if (!some(aFilePathAndName)) 768 | return false; 769 | 770 | FILE* lIn = fopen(aFilePathAndName, "r"); 771 | if (!lIn) 772 | return false; 773 | fclose(lIn); 774 | return true; 775 | } 776 | } 777 | 778 | version (Windows) { 779 | 780 | bool replaceChr(char* aString, const char aOldChr, const char aNewChr) 781 | { 782 | if (!aString) 783 | return false; 784 | if (aOldChr == aNewChr) 785 | return false; 786 | 787 | bool ret; 788 | char* p = aString; 789 | while ((p = strchr(p, aOldChr)) !is null) 790 | { 791 | *p = aNewChr; 792 | p++; 793 | ret = true; 794 | } 795 | return ret; 796 | } 797 | 798 | int sizeUtf16(const char* aUtf8string) 799 | { 800 | return MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, 801 | aUtf8string, -1, null, 0); 802 | } 803 | 804 | wchar* utf8to16(const char* str) 805 | { 806 | if (!some(str)) 807 | return null; 808 | 809 | int lSize = sizeUtf16(str); 810 | wchar* ret = cast(wchar*)malloc(lSize * wchar.sizeof); 811 | lSize = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, 812 | str, -1, ret, lSize); 813 | if (lSize == 0) 814 | { 815 | free(ret); 816 | return null; 817 | } 818 | return ret; 819 | } 820 | 821 | bool dirExists(const char* aDirPath) 822 | { 823 | if (!some(aDirPath)) 824 | return false; 825 | size_t lDirLen = strlen(aDirPath); 826 | if (lDirLen == 2 && aDirPath[1] == ':') 827 | return true; 828 | 829 | wchar* lTmpWChar = utf8to16(aDirPath); 830 | DWORD attribs = GetFileAttributes(lTmpWChar); 831 | free(lTmpWChar); 832 | return (attribs & FILE_ATTRIBUTE_DIRECTORY) != 0; 833 | } 834 | 835 | version (TINYFD_NOLIB) { 836 | 837 | void _beep() 838 | { 839 | printf("\a"); 840 | } 841 | 842 | } else { // TINYFD_NOLIB 843 | 844 | void _beep() 845 | { 846 | Beep(440, 300); 847 | } 848 | 849 | void wipefileW(const wchar* aFilename) 850 | { 851 | // file must exist beforehand 852 | FILE* lIn = _wfopen(aFilename, "w"); 853 | if (lIn) 854 | { 855 | fseek(lIn, 0, SEEK_END); 856 | const size = ftell(lIn); 857 | rewind(lIn); 858 | foreach (i; 0 .. size) 859 | { 860 | fputc('A', lIn); 861 | } 862 | fclose(lIn); 863 | } 864 | } 865 | 866 | wchar* getPathWithoutFinalSlashW( 867 | wchar* aoDestination, /* make sure it is allocated, use _MAX_PATH */ 868 | const wchar* aSource) /* aoDestination and aSource can be the same */ 869 | { 870 | const(wchar)* lTmp; 871 | if (aSource) 872 | { 873 | lTmp = wcsrchr(aSource, '/'); 874 | if (!lTmp) 875 | { 876 | lTmp = wcsrchr(aSource, '\\'); 877 | } 878 | if (lTmp) 879 | { 880 | wcsncpy(aoDestination, aSource, lTmp - aSource); 881 | aoDestination[lTmp - aSource] = '\0'; 882 | } 883 | else 884 | { 885 | *aoDestination = '\0'; 886 | } 887 | } 888 | else 889 | { 890 | *aoDestination = '\0'; 891 | } 892 | return aoDestination; 893 | } 894 | 895 | wchar* getLastNameW( 896 | wchar* aoDestination, /* make sure it is allocated */ 897 | const wchar* aSource) 898 | { 899 | /* copy the last name after '/' or '\' */ 900 | const(wchar)* lTmp; 901 | if (aSource) 902 | { 903 | lTmp = wcsrchr(aSource, '/'); 904 | if (!lTmp) 905 | { 906 | lTmp = wcsrchr(aSource, '\\'); 907 | } 908 | if (lTmp) 909 | { 910 | wcscpy(aoDestination, lTmp + 1); 911 | } 912 | else 913 | { 914 | wcscpy(aoDestination, aSource); 915 | } 916 | } 917 | else 918 | { 919 | *aoDestination = '\0'; 920 | } 921 | return aoDestination; 922 | } 923 | 924 | static if (!is(WC_ERR_INVALID_CHARS)) 925 | /* undefined prior to Vista, so not yet in MINGW header file */ 926 | enum DWORD WC_ERR_INVALID_CHARS = 0x00000080; 927 | 928 | int sizeUtf8(const wchar* aUtf16string) 929 | { 930 | return WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, 931 | aUtf16string, -1, null, 0, null, null); 932 | } 933 | 934 | char* utf16to8(const wchar* str) 935 | { 936 | if (!wsome(str)) 937 | return null; 938 | 939 | int lSize = sizeUtf8(str); 940 | char* ret = cast(char*)malloc(lSize); 941 | lSize = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, 942 | str, -1, ret, lSize, null, null); 943 | if (lSize == 0) 944 | { 945 | free(ret); 946 | return null; 947 | } 948 | return ret; 949 | } 950 | 951 | bool replaceWchar(wchar* aString, const wchar aOldChr, const wchar aNewChr) 952 | { 953 | if (!aString) 954 | return false; 955 | if (aOldChr == aNewChr) 956 | return false; 957 | 958 | bool ret; 959 | wchar* p = aString; 960 | while ((p = wcsrchr(p, aOldChr)) !is null) 961 | { 962 | *p = aNewChr; 963 | version (TINYFD_NOCCSUNICODE) 964 | p++; 965 | p++; 966 | ret = true; 967 | } 968 | return ret; 969 | } 970 | 971 | bool willBeGui() 972 | { 973 | return (!tinyfd_forceConsole || !(GetConsoleWindow() || dialogPresent())) && 974 | (!getenv("SSH_CLIENT") || getenv("DISPLAY")); 975 | } 976 | 977 | extern (Windows) int EnumThreadWndProc(HWND hwnd, LPARAM lParam) 978 | { 979 | wchar[MAX_PATH] lTitleName = '\0'; 980 | GetWindowTextW(hwnd, lTitleName.ptr, MAX_PATH); 981 | /* wprintf("lTitleName %ls \n", lTitleName.ptr); */ 982 | if (wcscmp("tinyfiledialogsTopWindow", lTitleName.ptr) == 0) 983 | { 984 | SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); 985 | return 0; 986 | } 987 | return 1; 988 | } 989 | 990 | void hiddenConsoleW(const wchar* aString, const wchar* aDialogTitle, const int aInFront) 991 | { 992 | if (!wsome(aString)) 993 | return; 994 | 995 | STARTUPINFOW StartupInfo; 996 | PROCESS_INFORMATION ProcessInfo; 997 | 998 | StartupInfo.dwFlags = STARTF_USESHOWWINDOW; 999 | StartupInfo.wShowWindow = SW_HIDE; 1000 | 1001 | if (!CreateProcessW(null, cast(LPWSTR)aString, null, null, FALSE, 1002 | CREATE_NEW_CONSOLE, null, null, 1003 | &StartupInfo, &ProcessInfo)) 1004 | { 1005 | return; /* GetLastError(); */ 1006 | } 1007 | 1008 | WaitForInputIdle(ProcessInfo.hProcess, INFINITE); 1009 | if (aInFront) 1010 | { 1011 | while (EnumWindows(&EnumThreadWndProc, 0)) 1012 | { 1013 | } 1014 | SetWindowTextW(GetForegroundWindow(), aDialogTitle); 1015 | } 1016 | WaitForSingleObject(ProcessInfo.hProcess, INFINITE); 1017 | CloseHandle(ProcessInfo.hThread); 1018 | CloseHandle(ProcessInfo.hProcess); 1019 | } 1020 | 1021 | int messageBoxWinGui( 1022 | const char* aTitle, 1023 | const char* aMessage, 1024 | const char* aDialogType, 1025 | const char* aIconType, 1026 | const int aDefaultButton) 1027 | { 1028 | wchar* lTitle = utf8to16(aTitle); 1029 | wchar* lMessage = utf8to16(aMessage); 1030 | 1031 | UINT aCode; 1032 | 1033 | if (eq("warning", aIconType)) 1034 | { 1035 | aCode = MB_ICONWARNING; 1036 | } 1037 | else if (eq("error", aIconType)) 1038 | { 1039 | aCode = MB_ICONERROR; 1040 | } 1041 | else if (eq("question", aIconType)) 1042 | { 1043 | aCode = MB_ICONQUESTION; 1044 | } 1045 | else 1046 | { 1047 | aCode = MB_ICONINFORMATION; 1048 | } 1049 | 1050 | if (eq("okcancel", aDialogType)) 1051 | { 1052 | aCode += MB_OKCANCEL; 1053 | if (!aDefaultButton) 1054 | { 1055 | aCode += MB_DEFBUTTON2; 1056 | } 1057 | } 1058 | else if (eq("yesno", aDialogType)) 1059 | { 1060 | aCode += MB_YESNO; 1061 | if (!aDefaultButton) 1062 | { 1063 | aCode += MB_DEFBUTTON2; 1064 | } 1065 | } 1066 | else 1067 | { 1068 | aCode += MB_OK; 1069 | } 1070 | 1071 | aCode += MB_TOPMOST; 1072 | 1073 | int lBoxReturnValue = MessageBoxW(GetForegroundWindow(), lMessage, lTitle, aCode); 1074 | free(lTitle); 1075 | free(lMessage); 1076 | 1077 | if (eq("ok", aDialogType) || lBoxReturnValue == IDOK || lBoxReturnValue == IDYES) 1078 | return 1; 1079 | else 1080 | return 0; 1081 | } 1082 | 1083 | int notifyWinGui( 1084 | const char* aTitle, 1085 | const char* aMessage, 1086 | const char* aIconType) 1087 | { 1088 | wchar* lTitle = utf8to16(aTitle); 1089 | wchar* lMessage = utf8to16(aMessage); 1090 | wchar* lIconType = utf8to16(aIconType); 1091 | 1092 | size_t lTitleLen = lTitle ? wcslen(lTitle) : 0; 1093 | size_t lMessageLen = lMessage ? wcslen(lMessage) : 0; 1094 | size_t lDialogStringLen = 3 * MAX_PATH_OR_CMD + lTitleLen + lMessageLen; 1095 | wchar* str = cast(wchar*)malloc(lDialogStringLen * wchar.sizeof); 1096 | 1097 | wcscpy(str, "powershell.exe -command \"" ~ 1098 | "function Show-BalloonTip {" ~ 1099 | "[cmdletbinding()] " ~ 1100 | "param( " ~ 1101 | "[string]$Title = ' ', " ~ 1102 | "[string]$Message = ' ', " ~ 1103 | "[ValidateSet('info', 'warning', 'error')] " ~ 1104 | "[string]$IconType = 'info');" ~ 1105 | "[system.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms') | Out-Null ; " ~ 1106 | "$balloon = New-Object System.Windows.Forms.NotifyIcon ; " ~ 1107 | "$path = Get-Process -id $pid | Select-Object -ExpandProperty Path ; " ~ 1108 | "$icon = [System.Drawing.Icon]::ExtractAssociatedIcon($path) ;"); 1109 | 1110 | wcscat(str, 1111 | "$balloon.Icon = $icon ; " ~ 1112 | "$balloon.BalloonTipIcon = $IconType ; " ~ 1113 | "$balloon.BalloonTipText = $Message ; " ~ 1114 | "$balloon.BalloonTipTitle = $Title ; " ~ 1115 | "$balloon.Text = 'lalala' ; " ~ 1116 | "$balloon.Visible = $true ; " ~ 1117 | "$balloon.ShowBalloonTip(5000)};" ~ 1118 | "Show-BalloonTip"); 1119 | 1120 | if (wsome(lTitle)) 1121 | { 1122 | wcscat(str, " -Title '"); 1123 | wcscat(str, lTitle); 1124 | wcscat(str, "'"); 1125 | } 1126 | if (wsome(lMessage)) 1127 | { 1128 | wcscat(str, " -Message '"); 1129 | wcscat(str, lMessage); 1130 | wcscat(str, "'"); 1131 | } 1132 | if (wsome(lIconType)) 1133 | { 1134 | wcscat(str, " -IconType '"); 1135 | wcscat(str, lIconType); 1136 | wcscat(str, "'"); 1137 | } 1138 | wcscat(str, "\""); 1139 | 1140 | /* wprintf ( "str: %ls\n" , str ) ; */ 1141 | 1142 | hiddenConsoleW(str, lTitle, 0); 1143 | 1144 | free(str); 1145 | free(lTitle); 1146 | free(lMessage); 1147 | free(lIconType); 1148 | return 1; 1149 | } 1150 | 1151 | const(wchar)* _inputBoxW( 1152 | const wchar* aTitle, 1153 | const wchar* aMessage, 1154 | const wchar* aDefaultInput) 1155 | { 1156 | static wchar[MAX_PATH_OR_CMD] lBuff = '\0'; 1157 | 1158 | size_t lTitleLen = aTitle ? wcslen(aTitle) : 0; 1159 | size_t lMessageLen = aMessage ? wcslen(aMessage) : 0; 1160 | size_t lDialogStringLen = 3 * MAX_PATH_OR_CMD + lTitleLen + lMessageLen; 1161 | wchar* str = cast(wchar*)malloc(lDialogStringLen * wchar.sizeof); 1162 | 1163 | wcscpy(str, _wgetenv("USERPROFILE")); 1164 | wcscat(str, "\\AppData\\Local\\Temp\\tinyfd."); 1165 | wcscat(str, aDefaultInput ? "vbs" : "hta"); 1166 | 1167 | FILE* lIn = _wfopen(str, "w"); 1168 | if (!lIn) 1169 | { 1170 | free(str); 1171 | return null; 1172 | } 1173 | 1174 | if (aDefaultInput) 1175 | { 1176 | wcscpy(str, "Dim result:result=InputBox(\""); 1177 | if (wsome(aMessage)) 1178 | { 1179 | wcscpy(lBuff.ptr, aMessage); 1180 | replaceWchar(lBuff.ptr, '\n', ' '); 1181 | wcscat(str, lBuff.ptr); 1182 | } 1183 | wcscat(str, "\",\"tinyfiledialogsTopWindow\",\""); 1184 | if (wsome(aDefaultInput)) 1185 | { 1186 | wcscpy(lBuff.ptr, aDefaultInput); 1187 | replaceWchar(lBuff.ptr, '\n', ' '); 1188 | wcscat(str, lBuff.ptr); 1189 | } 1190 | wcscat(str, "\"):If IsEmpty(result) then:WScript.Echo 0"); 1191 | wcscat(str, ":Else: WScript.Echo \"1\" & result : End If"); 1192 | } 1193 | else 1194 | { 1195 | wcscpy(str, ` 1196 | 1197 | 1198 | `); 1199 | 1200 | wcscat(str, "tinyfiledialogsTopWindow"); 1201 | wcscat(str, ` 1202 | 1211 | 1212 | 1258 | 1259 | 1260 | 1261 | 1262 | 1269 | 1277 | 1278 |
1263 | `); 1264 | 1265 | wcscat(str, aMessage ? aMessage : ""); 1266 | 1267 | wcscat(str, ` 1268 | 1270 | 1271 | 1272 |
1273 |

1274 | 1275 |
1276 |
1279 | `); 1280 | 1281 | wcscat(str, ` 1282 | 1283 | 1287 | 1288 |
1284 |
1286 |
1289 | 1290 | 1291 | `); 1292 | } 1293 | fputws(str, lIn); 1294 | fclose(lIn); 1295 | 1296 | if (aDefaultInput) 1297 | { 1298 | wcscpy(str, _wgetenv("USERPROFILE")); 1299 | wcscat(str, "\\AppData\\Local\\Temp\\tinyfd.txt"); 1300 | 1301 | version (TINYFD_NOCCSUNICODE) { 1302 | FILE* lFile = _wfopen(str, "w"); 1303 | fputc(0xFF, lFile); 1304 | fputc(0xFE, lFile); 1305 | } else { 1306 | FILE* lFile = _wfopen(str, "wt, ccs=UNICODE"); /*or ccs=UTF-16LE*/ 1307 | } 1308 | fclose(lFile); 1309 | 1310 | wcscpy(str, "cmd.exe /c cscript.exe //U //Nologo "); 1311 | wcscat(str, "\"%USERPROFILE%\\AppData\\Local\\Temp\\tinyfd.vbs\" "); 1312 | wcscat(str, ">> \"%USERPROFILE%\\AppData\\Local\\Temp\\tinyfd.txt\""); 1313 | } 1314 | else 1315 | { 1316 | wcscpy(str, "cmd.exe /c mshta.exe \"%USERPROFILE%\\AppData\\Local\\Temp\\tinyfd.hta\""); 1317 | } 1318 | 1319 | /* wprintf ( "str: %ls\n" , str ) ; */ 1320 | 1321 | hiddenConsoleW(str, aTitle, 1); 1322 | 1323 | wcscpy(str, _wgetenv("USERPROFILE")); 1324 | wcscat(str, "\\AppData\\Local\\Temp\\tinyfd.txt"); 1325 | /* wprintf("str: %ls\n", str); */ 1326 | version (TINYFD_NOCCSUNICODE) 1327 | const wchar* mode = "r"; 1328 | else 1329 | const wchar* mode = "rt, ccs=UNICODE"; /*or ccs=UTF-16LE*/ 1330 | lIn = _wfopen(str, mode); 1331 | if (!lIn) 1332 | { 1333 | _wremove(str); 1334 | free(str); 1335 | return null; 1336 | } 1337 | 1338 | lBuff = '\0'; 1339 | 1340 | version (TINYFD_NOCCSUNICODE) { 1341 | fgets(cast(char*)lBuff.ptr, 2 * MAX_PATH_OR_CMD, lIn); 1342 | } else { 1343 | fgetws(lBuff.ptr, MAX_PATH_OR_CMD, lIn); 1344 | } 1345 | fclose(lIn); 1346 | wipefileW(str); 1347 | _wremove(str); 1348 | 1349 | wcscpy(str, _wgetenv("USERPROFILE")); 1350 | wcscat(str, "\\AppData\\Local\\Temp\\tinyfd."); 1351 | wcscat(str, aDefaultInput ? "vbs" : "hta"); 1352 | _wremove(str); 1353 | free(str); 1354 | /* wprintf( "lBuff: %ls\n" , lBuff ) ; */ 1355 | version (TINYFD_NOCCSUNICODE) { 1356 | int lResult = !wcsncmp(lBuff.ptr + 1, "1", 1); 1357 | } else { 1358 | int lResult = !wcsncmp(lBuff.ptr, "1", 1); 1359 | } 1360 | 1361 | /* printf( "lResult: %d \n" , lResult ) ; */ 1362 | if (!lResult) 1363 | return null; 1364 | 1365 | /* wprintf( "lBuff+1: %ls\n" , lBuff+1 ) ; */ 1366 | 1367 | version (TINYFD_NOCCSUNICODE) { 1368 | 1369 | if (aDefaultInput) 1370 | { 1371 | lDialogStringLen = wcslen(lBuff.ptr); 1372 | assert(lDialogStringLen >= 2); 1373 | lBuff[lDialogStringLen - 1] = '\0'; 1374 | lBuff[lDialogStringLen - 2] = '\0'; 1375 | } 1376 | return lBuff.ptr + 2; 1377 | 1378 | } else { 1379 | 1380 | if (aDefaultInput) 1381 | { 1382 | lDialogStringLen = wcslen(lBuff.ptr); 1383 | assert(lDialogStringLen > 0); 1384 | lBuff[lDialogStringLen - 1] = '\0'; 1385 | } 1386 | return lBuff.ptr + 1; 1387 | } 1388 | } 1389 | 1390 | const(char)* inputBoxWinGui( 1391 | char* aoBuff, 1392 | const char* aTitle, 1393 | const char* aMessage, 1394 | const char* aDefaultInput) 1395 | { 1396 | wchar* lTitle = utf8to16(aTitle); 1397 | wchar* lMessage = utf8to16(aMessage); 1398 | wchar* lDefaultInput = utf8to16(aDefaultInput); 1399 | 1400 | const(wchar)* lTmpWChar = _inputBoxW(lTitle, lMessage, lDefaultInput); 1401 | 1402 | free(lTitle); 1403 | free(lMessage); 1404 | free(lDefaultInput); 1405 | 1406 | if (!lTmpWChar) 1407 | return null; 1408 | 1409 | char* lTmpChar = utf16to8(lTmpWChar); 1410 | strcpy(aoBuff, lTmpChar); 1411 | free(lTmpChar); 1412 | 1413 | return aoBuff; 1414 | } 1415 | 1416 | wchar* buildFilterString(const TFD_Filter[] filters) 1417 | { 1418 | bool isMime; 1419 | const(char)*[MAX_PATTERNS] patterns; 1420 | char[MAX_PATH_OR_CMD] str = '\0'; 1421 | char[MAX_PATH_OR_CMD] patternsStr = '\0'; 1422 | 1423 | foreach (filter; filters) 1424 | { 1425 | const plen = getValidPatterns(filter, isMime, patterns); 1426 | if (plen == 0 || isMime) 1427 | continue; 1428 | 1429 | patternsStr[0] = '\0'; 1430 | foreach (i, pt; patterns[0 .. plen]) 1431 | { 1432 | if (i != 0) 1433 | strcat(patternsStr.ptr, ";"); 1434 | strcat(patternsStr.ptr, pt); 1435 | } 1436 | 1437 | strcat(str.ptr, some(filter.description) ? filter.description : patternsStr.ptr); 1438 | strcat(str.ptr, "\n"); 1439 | strcat(str.ptr, patternsStr.ptr); 1440 | strcat(str.ptr, "\n"); 1441 | } 1442 | strcat(str.ptr, "All Files\n*.*\n"); 1443 | 1444 | wchar* ret = utf8to16(str.ptr); 1445 | if (!ret) 1446 | return null; 1447 | 1448 | wchar* p = ret; 1449 | while ((p = wcschr(p, '\n')) !is null) 1450 | { 1451 | *p = '\0'; 1452 | p++; 1453 | } 1454 | return ret; 1455 | } 1456 | 1457 | const(char)* saveFileDialogWinGui( 1458 | char* aoBuff, 1459 | const char* aTitle, 1460 | const char* aDefaultPathAndFile, 1461 | const TFD_Filter[] aFilters) 1462 | { 1463 | static wchar[MAX_PATH_OR_CMD] lBuff = '\0'; 1464 | wchar[MAX_PATH_OR_CMD] lDirname = '\0'; 1465 | 1466 | wchar* lTitle = utf8to16(aTitle); 1467 | wchar* lDefaultPathAndFile = utf8to16(aDefaultPathAndFile); 1468 | wchar* lFilterStr = buildFilterString(aFilters); 1469 | 1470 | getPathWithoutFinalSlashW(lDirname.ptr, lDefaultPathAndFile); 1471 | getLastNameW(lBuff.ptr, lDefaultPathAndFile); 1472 | 1473 | HRESULT lHResult = CoInitializeEx(null, 0); 1474 | 1475 | OPENFILENAMEW ofn = {0}; 1476 | ofn.lStructSize = OPENFILENAMEW.sizeof; 1477 | ofn.hwndOwner = GetForegroundWindow(); 1478 | ofn.hInstance = null; 1479 | ofn.lpstrFilter = lFilterStr; 1480 | ofn.lpstrCustomFilter = null; 1481 | ofn.nMaxCustFilter = 0; 1482 | ofn.nFilterIndex = 1; 1483 | ofn.lpstrFile = lBuff.ptr; 1484 | 1485 | ofn.nMaxFile = MAX_PATH_OR_CMD; 1486 | ofn.lpstrFileTitle = null; 1487 | ofn.nMaxFileTitle = MAX_PATH_OR_CMD / 2; 1488 | ofn.lpstrInitialDir = wsome(lDirname.ptr) ? lDirname.ptr : null; 1489 | ofn.lpstrTitle = wsome(lTitle) ? lTitle : null; 1490 | ofn.Flags = OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST; 1491 | ofn.nFileOffset = 0; 1492 | ofn.nFileExtension = 0; 1493 | ofn.lpstrDefExt = null; 1494 | ofn.lCustData = 0; 1495 | ofn.lpfnHook = null; 1496 | ofn.lpTemplateName = null; 1497 | 1498 | wchar* lTmpWChar; 1499 | if (GetSaveFileNameW(&ofn) == 0) 1500 | { 1501 | lTmpWChar = null; 1502 | } 1503 | else 1504 | { 1505 | lTmpWChar = lBuff.ptr; 1506 | } 1507 | 1508 | if (lHResult == S_OK || lHResult == S_FALSE) 1509 | { 1510 | CoUninitialize(); 1511 | } 1512 | 1513 | free(lTitle); 1514 | free(lDefaultPathAndFile); 1515 | free(lFilterStr); 1516 | 1517 | if (!lTmpWChar) 1518 | return null; 1519 | 1520 | char* lTmpChar = utf16to8(lTmpWChar); 1521 | strcpy(aoBuff, lTmpChar); 1522 | free(lTmpChar); 1523 | 1524 | return aoBuff; 1525 | } 1526 | 1527 | const(char)* openFileDialogWinGui( 1528 | char* aoBuff, 1529 | const char* aTitle, 1530 | const char* aDefaultPathAndFile, 1531 | const TFD_Filter[] aFilters, 1532 | const bool aAllowMultipleSelects) 1533 | { 1534 | static wchar[MAX_MULTIPLE_FILES * MAX_PATH_OR_CMD] lBuff = '\0'; 1535 | wchar[MAX_PATH_OR_CMD] lDirname = '\0'; 1536 | 1537 | wchar* lTitle = utf8to16(aTitle); 1538 | wchar* lDefaultPathAndFile = utf8to16(aDefaultPathAndFile); 1539 | wchar* lFilterStr = buildFilterString(aFilters); 1540 | 1541 | getPathWithoutFinalSlashW(lDirname.ptr, lDefaultPathAndFile); 1542 | getLastNameW(lBuff.ptr, lDefaultPathAndFile); 1543 | 1544 | HRESULT lHResult = CoInitializeEx(null, 0); 1545 | 1546 | OPENFILENAMEW ofn = {0}; 1547 | ofn.lStructSize = OPENFILENAME.sizeof; 1548 | ofn.hwndOwner = GetForegroundWindow(); 1549 | ofn.hInstance = null; 1550 | ofn.lpstrFilter = lFilterStr; 1551 | ofn.lpstrCustomFilter = null; 1552 | ofn.nMaxCustFilter = 0; 1553 | ofn.nFilterIndex = 1; 1554 | ofn.lpstrFile = lBuff.ptr; 1555 | ofn.nMaxFile = MAX_PATH_OR_CMD; 1556 | ofn.lpstrFileTitle = null; 1557 | ofn.nMaxFileTitle = MAX_PATH_OR_CMD / 2; 1558 | ofn.lpstrInitialDir = wsome(lDirname.ptr) ? lDirname.ptr : null; 1559 | ofn.lpstrTitle = wsome(lTitle) ? lTitle : null; 1560 | ofn.Flags = OFN_EXPLORER | OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; 1561 | ofn.nFileOffset = 0; 1562 | ofn.nFileExtension = 0; 1563 | ofn.lpstrDefExt = null; 1564 | ofn.lCustData = 0; 1565 | ofn.lpfnHook = null; 1566 | ofn.lpTemplateName = null; 1567 | 1568 | if (aAllowMultipleSelects) 1569 | { 1570 | ofn.Flags |= OFN_ALLOWMULTISELECT; 1571 | } 1572 | 1573 | size_t[MAX_MULTIPLE_FILES] lLengths; 1574 | wchar*[MAX_MULTIPLE_FILES] lPointers; 1575 | wchar* lTmpWChar; 1576 | if (GetOpenFileNameW(&ofn) == 0) 1577 | { 1578 | lTmpWChar = null; 1579 | } 1580 | else 1581 | { 1582 | size_t lBuffLen = wcslen(lBuff.ptr); 1583 | lPointers[0] = lBuff.ptr + lBuffLen + 1; 1584 | if (!aAllowMultipleSelects || (lPointers[0][0] == '\0')) 1585 | { 1586 | lTmpWChar = lBuff.ptr; 1587 | } 1588 | else 1589 | { 1590 | int i; 1591 | do 1592 | { 1593 | lLengths[i] = wcslen(lPointers[i]); 1594 | lPointers[i + 1] = lPointers[i] + lLengths[i] + 1; 1595 | i++; 1596 | } while (lPointers[i][0] != '\0'); 1597 | i--; 1598 | wchar* p = lBuff.ptr + MAX_MULTIPLE_FILES * MAX_PATH_OR_CMD - 1; 1599 | *p = '\0'; 1600 | for (int j = i; j >= 0; j--) 1601 | { 1602 | p -= lLengths[j]; 1603 | memmove(p, lPointers[j], lLengths[j] * wchar.sizeof); 1604 | p--; 1605 | *p = '\\'; 1606 | p -= lBuffLen; 1607 | memmove(p, lBuff.ptr, lBuffLen * wchar.sizeof); 1608 | p--; 1609 | *p = '|'; 1610 | } 1611 | p++; 1612 | lTmpWChar = p; 1613 | } 1614 | } 1615 | 1616 | if (lHResult == S_OK || lHResult == S_FALSE) 1617 | { 1618 | CoUninitialize(); 1619 | } 1620 | 1621 | free(lTitle); 1622 | free(lDefaultPathAndFile); 1623 | free(lFilterStr); 1624 | 1625 | if (!lTmpWChar) 1626 | return null; 1627 | 1628 | char* lTmpChar = utf16to8(lTmpWChar); 1629 | strcpy(aoBuff, lTmpChar); 1630 | free(lTmpChar); 1631 | 1632 | return aoBuff; 1633 | } 1634 | 1635 | version (TINYFD_SELECTFOLDERWIN) { 1636 | 1637 | extern (Windows) int BrowseCallbackProcW(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData) 1638 | { 1639 | if (uMsg == BFFM_INITIALIZED) 1640 | { 1641 | SendMessage(hwnd, BFFM_SETSELECTIONW, TRUE, cast(LPARAM)pData); 1642 | } 1643 | return 0; 1644 | } 1645 | 1646 | const(char)* selectFolderDialogWinGui( 1647 | char* aoBuff, 1648 | const char* aTitle, 1649 | const char* aDefaultPath) 1650 | { 1651 | static wchar[MAX_PATH_OR_CMD] lBuff = '\0'; 1652 | 1653 | wchar* lTitle = utf8to16(aTitle); 1654 | wchar* lDefaultPath = utf8to16(aDefaultPath); 1655 | 1656 | HRESULT lHResult = CoInitializeEx(null, COINIT_APARTMENTTHREADED); 1657 | 1658 | BROWSEINFOW bInfo; 1659 | bInfo.hwndOwner = GetForegroundWindow(); 1660 | bInfo.pidlRoot = null; 1661 | bInfo.pszDisplayName = lBuff.ptr; 1662 | bInfo.lpszTitle = wsome(lTitle) ? lTitle : null; 1663 | if (lHResult == S_OK || lHResult == S_FALSE) 1664 | { 1665 | bInfo.ulFlags = BIF_USENEWUI; 1666 | } 1667 | bInfo.lpfn = &BrowseCallbackProcW; 1668 | bInfo.lParam = cast(LPARAM)lDefaultPath; 1669 | bInfo.iImage = -1; 1670 | 1671 | LPITEMIDLIST lpItem = SHBrowseForFolderW(&bInfo); 1672 | if (lpItem) 1673 | { 1674 | SHGetPathFromIDListW(lpItem, lBuff.ptr); 1675 | } 1676 | 1677 | if (lHResult == S_OK || lHResult == S_FALSE) 1678 | { 1679 | CoUninitialize(); 1680 | } 1681 | 1682 | char* lTmpChar = utf16to8(lBuff.ptr); 1683 | if (lTmpChar) 1684 | { 1685 | strcpy(aoBuff, lTmpChar); 1686 | free(lTmpChar); 1687 | } 1688 | free(lTitle); 1689 | free(lDefaultPath); 1690 | return aoBuff; 1691 | } 1692 | 1693 | } // TINYFD_SELECTFOLDERWIN 1694 | 1695 | const(char)* colorChooserWinGui( 1696 | const char* aTitle, 1697 | const char* aDefaultHexRGB, 1698 | ref const ubyte[3] aDefaultRGB, 1699 | ref ubyte[3] aoResultRGB) 1700 | { 1701 | static char[8] lResultHexRGB = '\0'; 1702 | ubyte[3] lDefaultRGB = aDefaultRGB; 1703 | 1704 | HRESULT lHResult = CoInitializeEx(null, 0); 1705 | 1706 | if (aDefaultHexRGB) 1707 | { 1708 | Hex2RGB(aDefaultHexRGB, lDefaultRGB); 1709 | } 1710 | 1711 | CHOOSECOLORW cc; 1712 | COLORREF[16] crCustColors; 1713 | /* we can't use aTitle */ 1714 | cc.lStructSize = CHOOSECOLOR.sizeof; 1715 | cc.hwndOwner = GetForegroundWindow(); 1716 | cc.hInstance = null; 1717 | cc.rgbResult = RGB(lDefaultRGB[0], lDefaultRGB[1], lDefaultRGB[2]); 1718 | cc.lpCustColors = crCustColors.ptr; 1719 | cc.Flags = CC_RGBINIT | CC_FULLOPEN | CC_ANYCOLOR; 1720 | cc.lCustData = 0; 1721 | cc.lpfnHook = null; 1722 | cc.lpTemplateName = null; 1723 | 1724 | const(char)* ret; 1725 | if (ChooseColorW(&cc)) 1726 | { 1727 | aoResultRGB[0] = GetRValue(cc.rgbResult); 1728 | aoResultRGB[1] = GetGValue(cc.rgbResult); 1729 | aoResultRGB[2] = GetBValue(cc.rgbResult); 1730 | RGB2Hex(aoResultRGB, lResultHexRGB.ptr); 1731 | ret = lResultHexRGB.ptr; 1732 | } 1733 | 1734 | if (lHResult == S_OK || lHResult == S_FALSE) 1735 | { 1736 | CoUninitialize(); 1737 | } 1738 | return ret; 1739 | } 1740 | 1741 | } // TINYFD_NOLIB 1742 | 1743 | int dialogPresent() 1744 | { 1745 | static int ret = -1; 1746 | if (ret < 0) 1747 | { 1748 | FILE* lIn = _popen("where dialog.exe", "r"); 1749 | if (!lIn) 1750 | { 1751 | ret = 0; 1752 | return 0; 1753 | } 1754 | 1755 | char[MAX_PATH_OR_CMD] lBuff = '\0'; 1756 | const char* lString = "dialog.exe"; 1757 | while (fgets(lBuff.ptr, lBuff.sizeof, lIn) !is null) 1758 | { 1759 | } 1760 | _pclose(lIn); 1761 | removeLastNL(lBuff.ptr); 1762 | if (strcmp(lBuff.ptr + strlen(lBuff.ptr) - strlen(lString), lString)) 1763 | ret = 0; 1764 | else 1765 | ret = 1; 1766 | } 1767 | return ret; 1768 | } 1769 | 1770 | int messageBoxWinConsole( 1771 | const char* aTitle, 1772 | const char* aMessage, 1773 | const char* aDialogType, 1774 | const char* aIconType, 1775 | const int aDefaultButton) 1776 | { 1777 | char[MAX_PATH_OR_CMD] str_buf = '\0'; 1778 | char[MAX_PATH_OR_CMD] lDialogFile_buf = '\0'; 1779 | char* str = str_buf.ptr; 1780 | char* lDialogFile = lDialogFile_buf.ptr; 1781 | FILE* lIn; 1782 | char[MAX_PATH_OR_CMD] lBuff = '\0'; 1783 | 1784 | strcpy(str, "dialog "); 1785 | if (some(aTitle)) 1786 | { 1787 | strcat(str, "--title \""); 1788 | strcat(str, aTitle); 1789 | strcat(str, "\" "); 1790 | } 1791 | 1792 | if (eq("okcancel", aDialogType) || eq("yesno", aDialogType) || eq("yesnocancel", aDialogType)) 1793 | { 1794 | strcat(str, "--backtitle \""); 1795 | strcat(str, "tab: move focus"); 1796 | strcat(str, "\" "); 1797 | } 1798 | 1799 | if (eq("okcancel", aDialogType)) 1800 | { 1801 | if (!aDefaultButton) 1802 | { 1803 | strcat(str, "--defaultno "); 1804 | } 1805 | strcat(str, 1806 | "--yes-label \"Ok\" --no-label \"Cancel\" --yesno "); 1807 | } 1808 | else if (eq("yesno", aDialogType)) 1809 | { 1810 | if (!aDefaultButton) 1811 | { 1812 | strcat(str, "--defaultno "); 1813 | } 1814 | strcat(str, "--yesno "); 1815 | } 1816 | else if (eq("yesnocancel", aDialogType)) 1817 | { 1818 | if (!aDefaultButton) 1819 | { 1820 | strcat(str, "--defaultno "); 1821 | } 1822 | strcat(str, "--menu "); 1823 | } 1824 | else 1825 | { 1826 | strcat(str, "--msgbox "); 1827 | } 1828 | 1829 | strcat(str, "\""); 1830 | if (some(aMessage)) 1831 | { 1832 | replaceSubStr(aMessage, "\n", "\\n", lBuff.ptr); 1833 | strcat(str, lBuff.ptr); 1834 | lBuff[0] = '\0'; 1835 | } 1836 | strcat(str, "\" "); 1837 | 1838 | if (eq("yesnocancel", aDialogType)) 1839 | { 1840 | strcat(str, "0 60 0 Yes \"\" No \"\""); 1841 | strcat(str, "2>>"); 1842 | } 1843 | else 1844 | { 1845 | strcat(str, "10 60"); 1846 | strcat(str, " && echo 1 > "); 1847 | } 1848 | 1849 | strcpy(lDialogFile, getenv("USERPROFILE")); 1850 | strcat(lDialogFile, "\\AppData\\Local\\Temp\\tinyfd.txt"); 1851 | strcat(str, lDialogFile); 1852 | 1853 | /*if (tinyfd_verbose) printf( "str: %s\n" , str ) ;*/ 1854 | system(str); 1855 | 1856 | lIn = fopen(lDialogFile, "r"); 1857 | if (!lIn) 1858 | { 1859 | remove(lDialogFile); 1860 | return 0; 1861 | } 1862 | while (fgets(lBuff.ptr, lBuff.sizeof, lIn) !is null) 1863 | { 1864 | } 1865 | fclose(lIn); 1866 | remove(lDialogFile); 1867 | removeLastNL(lBuff.ptr); 1868 | 1869 | /* if (tinyfd_verbose) printf("lBuff: %s\n", lBuff.ptr); */ 1870 | if (!some(lBuff.ptr)) 1871 | { 1872 | return 0; 1873 | } 1874 | 1875 | if (eq("yesnocancel", aDialogType)) 1876 | { 1877 | if (lBuff[0] == 'Y') 1878 | return 1; 1879 | else 1880 | return 2; 1881 | } 1882 | 1883 | return 1; 1884 | } 1885 | 1886 | const(char)* inputBoxWinConsole( 1887 | char* aoBuff, 1888 | const char* aTitle, 1889 | const char* aMessage, 1890 | const char* aDefaultInput) 1891 | { 1892 | char[MAX_PATH_OR_CMD] str_buf = '\0'; 1893 | char[MAX_PATH_OR_CMD] lDialogFile_buf = '\0'; 1894 | char* str = str_buf.ptr; 1895 | char* lDialogFile = lDialogFile_buf.ptr; 1896 | FILE* lIn; 1897 | int lResult; 1898 | 1899 | strcpy(lDialogFile, getenv("USERPROFILE")); 1900 | strcat(lDialogFile, "\\AppData\\Local\\Temp\\tinyfd.txt"); 1901 | strcpy(str, "echo|set /p=1 >"); 1902 | strcat(str, lDialogFile); 1903 | strcat(str, " & "); 1904 | 1905 | strcat(str, "dialog "); 1906 | if (some(aTitle)) 1907 | { 1908 | strcat(str, "--title \""); 1909 | strcat(str, aTitle); 1910 | strcat(str, "\" "); 1911 | } 1912 | 1913 | strcat(str, "--backtitle \""); 1914 | strcat(str, "tab: move focus"); 1915 | if (!aDefaultInput) 1916 | { 1917 | strcat(str, " (sometimes nothing, no blink nor star, is shown in text field)"); 1918 | } 1919 | 1920 | strcat(str, "\" "); 1921 | 1922 | if (!aDefaultInput) 1923 | { 1924 | strcat(str, "--insecure --passwordbox"); 1925 | } 1926 | else 1927 | { 1928 | strcat(str, "--inputbox"); 1929 | } 1930 | strcat(str, " \""); 1931 | if (some(aMessage)) 1932 | { 1933 | strcat(str, aMessage); 1934 | } 1935 | strcat(str, "\" 10 60 "); 1936 | if (some(aDefaultInput)) 1937 | { 1938 | strcat(str, "\""); 1939 | strcat(str, aDefaultInput); 1940 | strcat(str, "\" "); 1941 | } 1942 | 1943 | strcat(str, "2>>"); 1944 | strcpy(lDialogFile, getenv("USERPROFILE")); 1945 | strcat(lDialogFile, "\\AppData\\Local\\Temp\\tinyfd.txt"); 1946 | strcat(str, lDialogFile); 1947 | strcat(str, " || echo 0 > "); 1948 | strcat(str, lDialogFile); 1949 | 1950 | /* printf( "str: %s\n" , str ) ; */ 1951 | system(str); 1952 | 1953 | lIn = fopen(lDialogFile, "r"); 1954 | if (!lIn) 1955 | { 1956 | remove(lDialogFile); 1957 | return null; 1958 | } 1959 | while (fgets(aoBuff, MAX_PATH_OR_CMD, lIn) !is null) 1960 | { 1961 | } 1962 | fclose(lIn); 1963 | 1964 | wipefile(lDialogFile); 1965 | remove(lDialogFile); 1966 | removeLastNL(aoBuff); 1967 | /* printf( "aoBuff: %s\n" , aoBuff ) ; */ 1968 | 1969 | /* printf( "aoBuff: %s len: %lu \n" , aoBuff , strlen(aoBuff) ) ; */ 1970 | lResult = strncmp(aoBuff, "1", 1) ? 0 : 1; 1971 | /* printf( "lResult: %d \n" , lResult ) ; */ 1972 | if (!lResult) 1973 | return null; 1974 | /* printf( "aoBuff+1: %s\n" , aoBuff+1 ) ; */ 1975 | return aoBuff + 3; 1976 | } 1977 | 1978 | const(char)* saveFileDialogWinConsole( 1979 | char* aoBuff, 1980 | const char* aTitle, 1981 | const char* aDefaultPathAndFile) 1982 | { 1983 | char[MAX_PATH_OR_CMD] str_buf = '\0'; 1984 | char[MAX_PATH_OR_CMD] lPathAndFile_buf = '\0'; 1985 | char* str = str_buf.ptr; 1986 | char* lPathAndFile = lPathAndFile_buf.ptr; 1987 | FILE* lIn; 1988 | 1989 | strcpy(str, "dialog "); 1990 | if (some(aTitle)) 1991 | { 1992 | strcat(str, "--title \""); 1993 | strcat(str, aTitle); 1994 | strcat(str, "\" "); 1995 | } 1996 | 1997 | strcat(str, "--backtitle \""); 1998 | strcat(str, 1999 | "tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY"); 2000 | strcat(str, "\" "); 2001 | 2002 | strcat(str, "--fselect \""); 2003 | if (some(aDefaultPathAndFile)) 2004 | { 2005 | /* dialog.exe uses unix separators even on windows */ 2006 | strcpy(lPathAndFile, aDefaultPathAndFile); 2007 | replaceChr(lPathAndFile, '\\', '/'); 2008 | } 2009 | 2010 | /* dialog.exe needs at least one separator */ 2011 | if (!strchr(lPathAndFile, '/')) 2012 | { 2013 | strcat(str, "./"); 2014 | } 2015 | strcat(str, lPathAndFile); 2016 | strcat(str, "\" 0 60 2>"); 2017 | strcpy(lPathAndFile, getenv("USERPROFILE")); 2018 | strcat(lPathAndFile, "\\AppData\\Local\\Temp\\tinyfd.txt"); 2019 | strcat(str, lPathAndFile); 2020 | 2021 | /* printf( "str: %s\n" , str ) ; */ 2022 | system(str); 2023 | 2024 | lIn = fopen(lPathAndFile, "r"); 2025 | if (!lIn) 2026 | { 2027 | remove(lPathAndFile); 2028 | return null; 2029 | } 2030 | while (fgets(aoBuff, MAX_PATH_OR_CMD, lIn) !is null) 2031 | { 2032 | } 2033 | fclose(lIn); 2034 | remove(lPathAndFile); 2035 | replaceChr(aoBuff, '/', '\\'); 2036 | /* printf( "aoBuff: %s\n" , aoBuff ) ; */ 2037 | getLastName(str, aoBuff); 2038 | if (!some(str)) 2039 | return null; 2040 | return aoBuff; 2041 | } 2042 | 2043 | const(char)* openFileDialogWinConsole( 2044 | char* aoBuff, 2045 | const char* aTitle, 2046 | const char* aDefaultPathAndFile, 2047 | const bool aAllowMultipleSelects) 2048 | { 2049 | char[MAX_PATH_OR_CMD] lFilterPatterns = '\0'; 2050 | char[MAX_PATH_OR_CMD] str_buf = '\0'; 2051 | char* str = str_buf.ptr; 2052 | FILE* lIn; 2053 | 2054 | strcpy(str, "dialog "); 2055 | if (some(aTitle)) 2056 | { 2057 | strcat(str, "--title \""); 2058 | strcat(str, aTitle); 2059 | strcat(str, "\" "); 2060 | } 2061 | 2062 | strcat(str, "--backtitle \""); 2063 | strcat(str, 2064 | "tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY"); 2065 | strcat(str, "\" "); 2066 | 2067 | strcat(str, "--fselect \""); 2068 | if (some(aDefaultPathAndFile)) 2069 | { 2070 | /* dialog.exe uses unix separators even on windows */ 2071 | strcpy(lFilterPatterns.ptr, aDefaultPathAndFile); 2072 | replaceChr(lFilterPatterns.ptr, '\\', '/'); 2073 | } 2074 | 2075 | /* dialog.exe needs at least one separator */ 2076 | if (!strchr(lFilterPatterns.ptr, '/')) 2077 | { 2078 | strcat(str, "./"); 2079 | } 2080 | strcat(str, lFilterPatterns.ptr); 2081 | strcat(str, "\" 0 60 2>"); 2082 | strcpy(lFilterPatterns.ptr, getenv("USERPROFILE")); 2083 | strcat(lFilterPatterns.ptr, "\\AppData\\Local\\Temp\\tinyfd.txt"); 2084 | strcat(str, lFilterPatterns.ptr); 2085 | 2086 | /* printf( "str: %s\n" , str ) ; */ 2087 | system(str); 2088 | 2089 | lIn = fopen(lFilterPatterns.ptr, "r"); 2090 | if (!lIn) 2091 | { 2092 | remove(lFilterPatterns.ptr); 2093 | return null; 2094 | } 2095 | while (fgets(aoBuff, MAX_PATH_OR_CMD, lIn) !is null) 2096 | { 2097 | } 2098 | fclose(lIn); 2099 | remove(lFilterPatterns.ptr); 2100 | replaceChr(aoBuff, '/', '\\'); 2101 | /* printf( "aoBuff: %s\n" , aoBuff ) ; */ 2102 | return aoBuff; 2103 | } 2104 | 2105 | const(char)* selectFolderDialogWinConsole( 2106 | char* aoBuff, 2107 | const char* aTitle, 2108 | const char* aDefaultPath) 2109 | { 2110 | char[MAX_PATH_OR_CMD] str_buf = '\0'; 2111 | char[MAX_PATH_OR_CMD] lString_buf = '\0'; 2112 | char* str = str_buf.ptr; 2113 | char* lString = lString_buf.ptr; 2114 | FILE* lIn; 2115 | 2116 | strcpy(str, "dialog "); 2117 | if (some(aTitle)) 2118 | { 2119 | strcat(str, "--title \""); 2120 | strcat(str, aTitle); 2121 | strcat(str, "\" "); 2122 | } 2123 | 2124 | strcat(str, "--backtitle \""); 2125 | strcat(str, 2126 | "tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY"); 2127 | strcat(str, "\" "); 2128 | 2129 | strcat(str, "--dselect \""); 2130 | if (some(aDefaultPath)) 2131 | { 2132 | /* dialog.exe uses unix separators even on windows */ 2133 | strcpy(lString, aDefaultPath); 2134 | ensureFinalSlash(lString); 2135 | replaceChr(lString, '\\', '/'); 2136 | strcat(str, lString); 2137 | } 2138 | else 2139 | { 2140 | /* dialog.exe needs at least one separator */ 2141 | strcat(str, "./"); 2142 | } 2143 | strcat(str, "\" 0 60 2>"); 2144 | strcpy(lString, getenv("USERPROFILE")); 2145 | strcat(lString, "\\AppData\\Local\\Temp\\tinyfd.txt"); 2146 | strcat(str, lString); 2147 | 2148 | /* printf( "str: %s\n" , str ) ; */ 2149 | system(str); 2150 | 2151 | lIn = fopen(lString, "r"); 2152 | if (!lIn) 2153 | { 2154 | remove(lString); 2155 | return null; 2156 | } 2157 | while (fgets(aoBuff, MAX_PATH_OR_CMD, lIn) !is null) 2158 | { 2159 | } 2160 | fclose(lIn); 2161 | remove(lString); 2162 | replaceChr(aoBuff, '/', '\\'); 2163 | /* printf( "aoBuff: %s\n" , aoBuff ) ; */ 2164 | return aoBuff; 2165 | } 2166 | 2167 | int _messageBox( 2168 | const char* aTitle, 2169 | const char* aMessage, 2170 | const char* aDialogType, 2171 | const char* aIconType, 2172 | int aDefaultButton) 2173 | { 2174 | char lChar; 2175 | const bool lQuery = eq(aTitle, "tinyfd_query"); 2176 | 2177 | version (TINYFD_LIB) { 2178 | 2179 | if (willBeGui()) 2180 | { 2181 | if (lQuery) 2182 | { 2183 | response("windows"); 2184 | return 1; 2185 | } 2186 | return messageBoxWinGui( 2187 | aTitle, aMessage, aDialogType, aIconType, aDefaultButton); 2188 | } 2189 | } 2190 | if (dialogPresent()) 2191 | { 2192 | if (lQuery) 2193 | { 2194 | response("dialog"); 2195 | return 0; 2196 | } 2197 | return messageBoxWinConsole( 2198 | aTitle, aMessage, aDialogType, aIconType, aDefaultButton); 2199 | } 2200 | else 2201 | { 2202 | if (lQuery) 2203 | { 2204 | response("basicinput"); 2205 | return 0; 2206 | } 2207 | if (!gWarningDisplayed && !tinyfd_forceConsole) 2208 | { 2209 | gWarningDisplayed = true; 2210 | printf("\n\n%s\n", gTitle.ptr); 2211 | printf("%s\n\n", tinyfd_needs.ptr); 2212 | } 2213 | if (some(aTitle)) 2214 | { 2215 | printf("\n%s\n\n", aTitle); 2216 | } 2217 | if (eq("yesno", aDialogType)) 2218 | { 2219 | do 2220 | { 2221 | if (some(aMessage)) 2222 | { 2223 | printf("%s\n", aMessage); 2224 | } 2225 | printf("y/n: "); 2226 | lChar = cast(char)tolower(_getch()); 2227 | printf("\n\n"); 2228 | } while (lChar != 'y' && lChar != 'n'); 2229 | return lChar == 'y' ? 1 : 0; 2230 | } 2231 | else if (eq("okcancel", aDialogType)) 2232 | { 2233 | do 2234 | { 2235 | if (some(aMessage)) 2236 | { 2237 | printf("%s\n", aMessage); 2238 | } 2239 | printf("[O]kay/[C]ancel: "); 2240 | lChar = cast(char)tolower(_getch()); 2241 | printf("\n\n"); 2242 | } while (lChar != 'o' && lChar != 'c'); 2243 | return lChar == 'o' ? 1 : 0; 2244 | } 2245 | else if (eq("yesnocancel", aDialogType)) 2246 | { 2247 | do 2248 | { 2249 | if (some(aMessage)) 2250 | { 2251 | printf("%s\n", aMessage); 2252 | } 2253 | printf("[Y]es/[N]o/[C]ancel: "); 2254 | lChar = cast(char)tolower(_getch()); 2255 | printf("\n\n"); 2256 | } while (lChar != 'y' && lChar != 'n' && lChar != 'c'); 2257 | return (lChar == 'y') ? 1 : (lChar == 'n') ? 2 : 0; 2258 | } 2259 | else 2260 | { 2261 | if (some(aMessage)) 2262 | { 2263 | printf("%s\n\n", aMessage); 2264 | } 2265 | printf("press enter to continue "); 2266 | lChar = cast(char)_getch(); 2267 | printf("\n\n"); 2268 | return 1; 2269 | } 2270 | } 2271 | } 2272 | 2273 | int _notifyPopup( 2274 | const char* aTitle, 2275 | const char* aMessage, 2276 | const char* aIconType) 2277 | { 2278 | const bool lQuery = eq(aTitle, "tinyfd_query"); 2279 | 2280 | version (TINYFD_LIB) { 2281 | 2282 | if (willBeGui()) 2283 | { 2284 | if (lQuery) 2285 | { 2286 | response("windows"); 2287 | return 1; 2288 | } 2289 | return notifyWinGui(aTitle, aMessage, aIconType); 2290 | } 2291 | } 2292 | return _messageBox(aTitle, aMessage, "ok", aIconType, 0); 2293 | } 2294 | 2295 | const(char*) _inputBox( 2296 | const char* aTitle, 2297 | const char* aMessage, 2298 | const char* aDefaultInput) 2299 | { 2300 | static char[MAX_PATH_OR_CMD] lBuff = '\0'; 2301 | char* lEOF; 2302 | const bool lQuery = eq(aTitle, "tinyfd_query"); 2303 | 2304 | version (TINYFD_LIB) { 2305 | 2306 | DWORD mode = 0; 2307 | HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); 2308 | 2309 | if (willBeGui()) 2310 | { 2311 | if (lQuery) 2312 | { 2313 | response("windows"); 2314 | return cast(const(char)*)1; 2315 | } 2316 | lBuff[0] = '\0'; 2317 | return inputBoxWinGui(lBuff.ptr, aTitle, aMessage, aDefaultInput); 2318 | } 2319 | } 2320 | if (dialogPresent()) 2321 | { 2322 | if (lQuery) 2323 | { 2324 | response("dialog"); 2325 | return cast(const(char)*)0; 2326 | } 2327 | lBuff[0] = '\0'; 2328 | return inputBoxWinConsole(lBuff.ptr, aTitle, aMessage, aDefaultInput); 2329 | } 2330 | else 2331 | { 2332 | if (lQuery) 2333 | { 2334 | response("basicinput"); 2335 | return cast(const(char)*)0; 2336 | } 2337 | lBuff[0] = '\0'; 2338 | if (!gWarningDisplayed && !tinyfd_forceConsole) 2339 | { 2340 | gWarningDisplayed = true; 2341 | printf("\n\n%s\n", gTitle.ptr); 2342 | printf("%s\n\n", tinyfd_needs.ptr); 2343 | } 2344 | if (some(aTitle)) 2345 | { 2346 | printf("\n%s\n\n", aTitle); 2347 | } 2348 | if (some(aMessage)) 2349 | { 2350 | printf("%s\n", aMessage); 2351 | } 2352 | printf("(ctrl-Z + enter to cancel): "); 2353 | version (TINYFD_LIB) 2354 | { 2355 | if (!aDefaultInput) 2356 | { 2357 | GetConsoleMode(hStdin, &mode); 2358 | SetConsoleMode(hStdin, mode & (~ENABLE_ECHO_INPUT)); 2359 | } 2360 | } 2361 | 2362 | lEOF = fgets(lBuff.ptr, lBuff.sizeof, stdin); 2363 | if (!lEOF) 2364 | return null; 2365 | 2366 | version (TINYFD_LIB) 2367 | { 2368 | if (!aDefaultInput) 2369 | { 2370 | SetConsoleMode(hStdin, mode); 2371 | printf("\n"); 2372 | } 2373 | } 2374 | printf("\n"); 2375 | if (strchr(lBuff.ptr, 27)) 2376 | return null; 2377 | removeLastNL(lBuff.ptr); 2378 | return lBuff.ptr; 2379 | } 2380 | } 2381 | 2382 | const(char*) _saveFileDialog( 2383 | const char* aTitle, 2384 | const char* aDefaultPathAndFile, 2385 | const TFD_Filter[] aFilters) 2386 | { 2387 | static char[MAX_PATH_OR_CMD] lBuff = '\0'; 2388 | const bool lQuery = eq(aTitle, "tinyfd_query"); 2389 | char[MAX_PATH_OR_CMD] lString = '\0'; 2390 | const(char)* p; 2391 | lBuff[0] = '\0'; 2392 | 2393 | version (TINYFD_LIB) { 2394 | 2395 | if (willBeGui()) 2396 | { 2397 | if (lQuery) 2398 | { 2399 | response("windows"); 2400 | return cast(const(char)*)1; 2401 | } 2402 | p = saveFileDialogWinGui(lBuff.ptr, aTitle, aDefaultPathAndFile, aFilters); 2403 | goto end; 2404 | } 2405 | } 2406 | if (dialogPresent()) 2407 | { 2408 | if (lQuery) 2409 | { 2410 | response("dialog"); 2411 | return cast(const(char)*)0; 2412 | } 2413 | p = saveFileDialogWinConsole(lBuff.ptr, aTitle, aDefaultPathAndFile); 2414 | } 2415 | else 2416 | { 2417 | if (lQuery) 2418 | { 2419 | response("basicinput"); 2420 | return cast(const(char)*)0; 2421 | } 2422 | p = _inputBox(aTitle, "Save file", ""); 2423 | } 2424 | 2425 | end: 2426 | 2427 | if (!some(p)) 2428 | return null; 2429 | getPathWithoutFinalSlash(lString.ptr, p); 2430 | if (!dirExists(lString.ptr)) 2431 | return null; 2432 | getLastName(lString.ptr, p); 2433 | if (!filenameValid(lString.ptr)) 2434 | return null; 2435 | return p; 2436 | } 2437 | 2438 | const(char*) _openFileDialog( 2439 | const char* aTitle, 2440 | const char* aDefaultPathAndFile, 2441 | const TFD_Filter[] aFilters, 2442 | const bool aAllowMultipleSelects) 2443 | { 2444 | static char[MAX_MULTIPLE_FILES * MAX_PATH_OR_CMD] lBuff = '\0'; 2445 | const bool lQuery = eq(aTitle, "tinyfd_query"); 2446 | const(char)* p; 2447 | 2448 | version (TINYFD_LIB) { 2449 | 2450 | if (willBeGui()) 2451 | { 2452 | if (lQuery) 2453 | { 2454 | response("windows"); 2455 | return cast(const(char)*)1; 2456 | } 2457 | p = openFileDialogWinGui(lBuff.ptr, aTitle, aDefaultPathAndFile, aFilters, aAllowMultipleSelects); 2458 | goto end; 2459 | } 2460 | } 2461 | if (dialogPresent()) 2462 | { 2463 | if (lQuery) 2464 | { 2465 | response("dialog"); 2466 | return cast(const(char)*)0; 2467 | } 2468 | p = openFileDialogWinConsole(lBuff.ptr, 2469 | aTitle, aDefaultPathAndFile, aAllowMultipleSelects); 2470 | } 2471 | else 2472 | { 2473 | if (lQuery) 2474 | { 2475 | response("basicinput"); 2476 | return cast(const(char)*)0; 2477 | } 2478 | p = _inputBox(aTitle, "Open file", ""); 2479 | } 2480 | 2481 | end: 2482 | 2483 | if (!some(p)) 2484 | return null; 2485 | if (aAllowMultipleSelects && strchr(p, '|')) 2486 | p = ensureFilesExist(lBuff.ptr, p); 2487 | else if (!fileExists(p)) 2488 | return null; 2489 | return p; 2490 | } 2491 | 2492 | const(char*) _selectFolderDialog(const char* aTitle, const char* aDefaultPath) 2493 | { 2494 | static char[MAX_PATH_OR_CMD] lBuff = '\0'; 2495 | const bool lQuery = eq(aTitle, "tinyfd_query"); 2496 | const(char)* p; 2497 | 2498 | version (TINYFD_LIB) { 2499 | 2500 | if (willBeGui()) 2501 | { 2502 | if (lQuery) 2503 | { 2504 | response("windows"); 2505 | return cast(const(char)*)1; 2506 | } 2507 | version (TINYFD_SELECTFOLDERWIN) 2508 | { 2509 | p = selectFolderDialogWinGui(lBuff.ptr, aTitle, aDefaultPath); 2510 | return dirExists(p) ? p : null; 2511 | } 2512 | } 2513 | } 2514 | if (dialogPresent()) 2515 | { 2516 | if (lQuery) 2517 | { 2518 | response("dialog"); 2519 | return cast(const(char)*)0; 2520 | } 2521 | p = selectFolderDialogWinConsole(lBuff.ptr, aTitle, aDefaultPath); 2522 | } 2523 | else 2524 | { 2525 | if (lQuery) 2526 | { 2527 | response("basicinput"); 2528 | return cast(const(char)*)0; 2529 | } 2530 | p = _inputBox(aTitle, "Select folder", ""); 2531 | } 2532 | return dirExists(p) ? p : null; 2533 | } 2534 | 2535 | const(char*) _colorChooser( 2536 | const char* aTitle, 2537 | const char* aDefaultHexRGB, 2538 | ref const ubyte[3] aDefaultRGB, 2539 | ref ubyte[3] aoResultRGB) 2540 | { 2541 | const bool lQuery = eq(aTitle, "tinyfd_query"); 2542 | char[8] lDefaultHexRGB = '\0'; 2543 | char* lpDefaultHexRGB; 2544 | const(char)* p; 2545 | 2546 | version (TINYFD_LIB) { 2547 | 2548 | if (willBeGui()) 2549 | { 2550 | if (lQuery) 2551 | { 2552 | response("windows"); 2553 | return cast(const(char)*)1; 2554 | } 2555 | return colorChooserWinGui(aTitle, aDefaultHexRGB, aDefaultRGB, aoResultRGB); 2556 | } 2557 | } 2558 | if (aDefaultHexRGB) 2559 | { 2560 | lpDefaultHexRGB = cast(char*)aDefaultHexRGB; 2561 | } 2562 | else 2563 | { 2564 | RGB2Hex(aDefaultRGB, lDefaultHexRGB.ptr); 2565 | lpDefaultHexRGB = cast(char*)lDefaultHexRGB; 2566 | } 2567 | p = _inputBox(aTitle, "Enter hex rgb color (i.e. #f5ca20)", lpDefaultHexRGB); 2568 | if (lQuery) 2569 | return p; 2570 | 2571 | if (!p || strlen(p) != 7 || p[0] != '#') 2572 | return null; 2573 | 2574 | foreach (i; 1 .. 7) 2575 | { 2576 | if (!isxdigit(p[i])) 2577 | return null; 2578 | } 2579 | Hex2RGB(p, aoResultRGB); 2580 | return p; 2581 | } 2582 | 2583 | } else { // unix 2584 | 2585 | char[16] gPython2Name = '\0'; 2586 | char[16] gPython3Name = '\0'; 2587 | char[16] gPythonName = '\0'; 2588 | 2589 | int isDarwin() 2590 | { 2591 | static int ret = -1; 2592 | utsname lUtsname; 2593 | if (ret < 0) 2594 | { 2595 | ret = !uname(&lUtsname) && eq(lUtsname.sysname.ptr, "Darwin"); 2596 | } 2597 | return ret; 2598 | } 2599 | 2600 | bool dirExists(const char* aDirPath) 2601 | { 2602 | if (!some(aDirPath)) 2603 | return false; 2604 | DIR* lDir = opendir(aDirPath); 2605 | if (!lDir) 2606 | return false; 2607 | closedir(lDir); 2608 | return true; 2609 | } 2610 | 2611 | bool detectPresence(const char* aExecutable) 2612 | { 2613 | char[MAX_PATH_OR_CMD] lBuff = '\0'; 2614 | char[MAX_PATH_OR_CMD] lTestedString = "which "; 2615 | 2616 | strcat(lTestedString.ptr, aExecutable); 2617 | strcat(lTestedString.ptr, " 2>/dev/null "); 2618 | FILE* lIn = popen(lTestedString.ptr, "r"); 2619 | if (fgets(lBuff.ptr, lBuff.sizeof, lIn) !is null && !strchr(lBuff.ptr, ':') && strncmp(lBuff.ptr, "no ", 3)) 2620 | { 2621 | // present 2622 | pclose(lIn); 2623 | if (tinyfd_verbose) 2624 | printf("detectPresence %s %d\n", aExecutable, 1); 2625 | return true; 2626 | } 2627 | else 2628 | { 2629 | pclose(lIn); 2630 | if (tinyfd_verbose) 2631 | printf("detectPresence %s %d\n", aExecutable, 0); 2632 | return false; 2633 | } 2634 | } 2635 | 2636 | const(char)* getVersion(const char* aExecutable) /*version must be first numeral*/ 2637 | { 2638 | static char[MAX_PATH_OR_CMD] lBuff = '\0'; 2639 | char[MAX_PATH_OR_CMD] lTestedString = '\0'; 2640 | FILE* lIn; 2641 | char* lTmp; 2642 | 2643 | strcpy(lTestedString.ptr, aExecutable); 2644 | strcat(lTestedString.ptr, " --version"); 2645 | 2646 | lIn = popen(lTestedString.ptr, "r"); 2647 | lTmp = fgets(lBuff.ptr, lBuff.sizeof, lIn); 2648 | pclose(lIn); 2649 | 2650 | lTmp += strcspn(lTmp, "0123456789"); 2651 | /* printf("lTmp:%s\n", lTmp); */ 2652 | return lTmp; 2653 | } 2654 | 2655 | const(int)* getMajorMinorPatch(const char* aExecutable) 2656 | { 2657 | static int[3] lArray; 2658 | char* lTmp; 2659 | 2660 | lTmp = cast(char*)getVersion(aExecutable); 2661 | lArray[0] = atoi(strtok(lTmp, " ,.-")); 2662 | /* printf("lArray0 %d\n", lArray[0]); */ 2663 | lArray[1] = atoi(strtok(null, " ,.-")); 2664 | /* printf("lArray1 %d\n", lArray[1]); */ 2665 | lArray[2] = atoi(strtok(null, " ,.-")); 2666 | /* printf("lArray2 %d\n", lArray[2]); */ 2667 | 2668 | if (!lArray[0] && !lArray[1] && !lArray[2]) 2669 | return null; 2670 | return lArray.ptr; 2671 | } 2672 | 2673 | bool tryCommand(const char* aCommand) 2674 | { 2675 | char[MAX_PATH_OR_CMD] lBuff = '\0'; 2676 | FILE* lIn = popen(aCommand, "r"); 2677 | const bool present = fgets(lBuff.ptr, lBuff.sizeof, lIn) is null; 2678 | pclose(lIn); 2679 | return present; 2680 | } 2681 | 2682 | int isTerminalRunning() 2683 | { 2684 | static int ret = -1; 2685 | if (ret < 0) 2686 | { 2687 | ret = isatty(1); 2688 | if (tinyfd_verbose) 2689 | printf("isTerminalRunning %d\n", ret); 2690 | } 2691 | return ret; 2692 | } 2693 | 2694 | const(char)* dialogNameOnly() 2695 | { 2696 | static char[128] ret = "*"; 2697 | if (ret[0] == '*') 2698 | { 2699 | if (isDarwin() && strcpy(ret.ptr, "/opt/local/bin/dialog") && detectPresence(ret.ptr)) 2700 | { 2701 | } 2702 | else if (strcpy(ret.ptr, "dialog") && detectPresence(ret.ptr)) 2703 | { 2704 | } 2705 | else 2706 | { 2707 | strcpy(ret.ptr, ""); 2708 | } 2709 | } 2710 | return ret.ptr; 2711 | } 2712 | 2713 | bool isDialogVersionBetter09b() 2714 | { 2715 | const(char)* lDialogName; 2716 | char* lVersion; 2717 | int lMajor; 2718 | int lMinor; 2719 | int lDate; 2720 | int lResult; 2721 | char* lMinorP; 2722 | char* lLetter; 2723 | char[128] lBuff = '\0'; 2724 | 2725 | /*char[128] lTest = " 0.9b-20031126" ;*/ 2726 | 2727 | lDialogName = dialogNameOnly(); 2728 | lVersion = cast(char*)getVersion(lDialogName); 2729 | if (!some(lDialogName) || !lVersion) 2730 | return false; 2731 | /*lVersion = lTest ;*/ 2732 | /*printf("lVersion %s\n", lVersion);*/ 2733 | strcpy(lBuff.ptr, lVersion); 2734 | lMajor = atoi(strtok(lVersion, " ,.-")); 2735 | /*printf("lMajor %d\n", lMajor);*/ 2736 | lMinorP = strtok(null, " ,.-abcdefghijklmnopqrstuvxyz"); 2737 | lMinor = atoi(lMinorP); 2738 | /*printf("lMinor %d\n", lMinor );*/ 2739 | lDate = atoi(strtok(null, " ,.-")); 2740 | if (lDate < 0) 2741 | lDate = -lDate; 2742 | /*printf("lDate %d\n", lDate);*/ 2743 | lLetter = lMinorP + strlen(lMinorP); 2744 | strcpy(lVersion, lBuff.ptr); 2745 | strtok(lLetter, " ,.-"); 2746 | /*printf("lLetter %s\n", lLetter);*/ 2747 | return lMajor > 0 || (lMinor == 9 && *lLetter == 'b' && lDate >= 20031126); 2748 | } 2749 | 2750 | int whiptailPresentOnly() 2751 | { 2752 | static int ret = -1; 2753 | if (ret < 0) 2754 | { 2755 | ret = detectPresence("whiptail"); 2756 | } 2757 | return ret; 2758 | } 2759 | 2760 | const(char)* terminalName() 2761 | { 2762 | static char[128] ret_buf = "*"; 2763 | char[64] shellName_buf = "*"; 2764 | char* ret = ret_buf.ptr; 2765 | char* lShellName = shellName_buf.ptr; 2766 | const(int)* lArray; 2767 | 2768 | if (ret[0] == '*') 2769 | { 2770 | if (detectPresence("bash")) 2771 | { 2772 | strcpy(lShellName, "bash -c "); /*good for basic input*/ 2773 | } 2774 | else if (some(dialogNameOnly()) || whiptailPresentOnly()) 2775 | { 2776 | strcpy(lShellName, "sh -c "); /*good enough for dialog & whiptail*/ 2777 | } 2778 | else 2779 | { 2780 | strcpy(ret, ""); 2781 | return null; 2782 | } 2783 | 2784 | if (isDarwin()) 2785 | { 2786 | if (strcpy(ret, "/opt/X11/bin/xterm") && detectPresence(ret)) 2787 | { 2788 | strcat(ret, " -fa 'DejaVu Sans Mono' -fs 10 -title tinyfiledialogs -e "); 2789 | strcat(ret, lShellName); 2790 | } 2791 | else 2792 | { 2793 | strcpy(ret, ""); 2794 | } 2795 | } 2796 | else if (strcpy(ret, "xterm") /*good (small without parameters)*/ 2797 | && detectPresence(ret)) 2798 | { 2799 | strcat(ret, " -fa 'DejaVu Sans Mono' -fs 10 -title tinyfiledialogs -e "); 2800 | strcat(ret, lShellName); 2801 | } 2802 | else if (strcpy(ret, "terminator") /*good*/ 2803 | && detectPresence(ret)) 2804 | { 2805 | strcat(ret, " -x "); 2806 | strcat(ret, lShellName); 2807 | } 2808 | else if (strcpy(ret, "lxterminal") /*good*/ 2809 | && detectPresence(ret)) 2810 | { 2811 | strcat(ret, " -e "); 2812 | strcat(ret, lShellName); 2813 | } 2814 | else if (strcpy(ret, "konsole") /*good*/ 2815 | && detectPresence(ret)) 2816 | { 2817 | strcat(ret, " -e "); 2818 | strcat(ret, lShellName); 2819 | } 2820 | else if (strcpy(ret, "kterm") /*good*/ 2821 | && detectPresence(ret)) 2822 | { 2823 | strcat(ret, " -e "); 2824 | strcat(ret, lShellName); 2825 | } 2826 | else if (strcpy(ret, "tilix") /*good*/ 2827 | && detectPresence(ret)) 2828 | { 2829 | strcat(ret, " -e "); 2830 | strcat(ret, lShellName); 2831 | } 2832 | else if (strcpy(ret, "xfce4-terminal") /*good*/ 2833 | && detectPresence(ret)) 2834 | { 2835 | strcat(ret, " -x "); 2836 | strcat(ret, lShellName); 2837 | } 2838 | else if (strcpy(ret, "mate-terminal") /*good*/ 2839 | && detectPresence(ret)) 2840 | { 2841 | strcat(ret, " -x "); 2842 | strcat(ret, lShellName); 2843 | } 2844 | else if (strcpy(ret, "Eterm") /*good*/ 2845 | && detectPresence(ret)) 2846 | { 2847 | strcat(ret, " -e "); 2848 | strcat(ret, lShellName); 2849 | } 2850 | else if (strcpy(ret, "evilvte") /*good*/ 2851 | && detectPresence(ret)) 2852 | { 2853 | strcat(ret, " -e "); 2854 | strcat(ret, lShellName); 2855 | } 2856 | else if (strcpy(ret, "pterm") /*good (only letters)*/ 2857 | && detectPresence(ret)) 2858 | { 2859 | strcat(ret, " -e "); 2860 | strcat(ret, lShellName); 2861 | } 2862 | else if (strcpy(ret, "gnome-terminal") && detectPresence(ret)) 2863 | { 2864 | lArray = getMajorMinorPatch(ret); 2865 | if (lArray[0] < 3 || lArray[0] == 3 && lArray[1] <= 6) 2866 | { 2867 | strcat(ret, " --disable-factory -x "); 2868 | strcat(ret, lShellName); 2869 | } 2870 | else 2871 | { 2872 | strcpy(ret, ""); 2873 | } 2874 | } 2875 | else 2876 | { 2877 | strcpy(ret, ""); 2878 | } 2879 | /* bad: koi rxterm guake tilda vala-terminal qterminal 2880 | aterm Terminal terminology sakura lilyterm weston-terminal 2881 | roxterm termit xvt rxvt mrxvt urxvt */ 2882 | } 2883 | return some(ret) ? ret : null; 2884 | } 2885 | 2886 | const(char)* dialogName() 2887 | { 2888 | const(char)* ret; 2889 | ret = dialogNameOnly(); 2890 | if (some(ret) && (isTerminalRunning() || terminalName())) 2891 | { 2892 | return ret; 2893 | } 2894 | else 2895 | return null; 2896 | } 2897 | 2898 | int whiptailPresent() 2899 | { 2900 | int ret; 2901 | ret = whiptailPresentOnly(); 2902 | if (ret && (isTerminalRunning() || terminalName())) 2903 | { 2904 | return ret; 2905 | } 2906 | else 2907 | return 0; 2908 | } 2909 | 2910 | int graphicMode() 2911 | { 2912 | return !(tinyfd_forceConsole && (isTerminalRunning() || terminalName())) && (getenv("DISPLAY") || (isDarwin() && (!getenv("SSH_TTY") || getenv("DISPLAY")))); 2913 | } 2914 | 2915 | int pactlPresent() 2916 | { 2917 | static int ret = -1; 2918 | if (ret < 0) 2919 | { 2920 | ret = detectPresence("pactl"); 2921 | } 2922 | return ret; 2923 | } 2924 | 2925 | int speakertestPresent() 2926 | { 2927 | static int ret = -1; 2928 | if (ret < 0) 2929 | { 2930 | ret = detectPresence("speaker-test"); 2931 | } 2932 | return ret; 2933 | } 2934 | 2935 | int beepexePresent() 2936 | { 2937 | static int ret = -1; 2938 | if (ret < 0) 2939 | { 2940 | ret = detectPresence("beep.exe"); 2941 | } 2942 | return ret; 2943 | } 2944 | 2945 | int xmessagePresent() 2946 | { 2947 | static int ret = -1; 2948 | if (ret < 0) 2949 | { 2950 | ret = detectPresence("xmessage"); /*if not tty,not on osxpath*/ 2951 | } 2952 | return ret && graphicMode(); 2953 | } 2954 | 2955 | int gxmessagePresent() 2956 | { 2957 | static int ret = -1; 2958 | if (ret < 0) 2959 | { 2960 | ret = detectPresence("gxmessage"); 2961 | } 2962 | return ret && graphicMode(); 2963 | } 2964 | 2965 | int gmessagePresent() 2966 | { 2967 | static int ret = -1; 2968 | if (ret < 0) 2969 | { 2970 | ret = detectPresence("gmessage"); 2971 | } 2972 | return ret && graphicMode(); 2973 | } 2974 | 2975 | int notifysendPresent() 2976 | { 2977 | static int ret = -1; 2978 | if (ret < 0) 2979 | { 2980 | ret = detectPresence("notify-send"); 2981 | } 2982 | return ret && graphicMode(); 2983 | } 2984 | 2985 | int perlPresent() 2986 | { 2987 | static int ret = -1; 2988 | char[MAX_PATH_OR_CMD] lBuff = 0; 2989 | FILE* lIn; 2990 | 2991 | if (ret < 0) 2992 | { 2993 | ret = detectPresence("perl"); 2994 | if (ret) 2995 | { 2996 | lIn = popen("perl -MNet::DBus -e \"Net::DBus->session->get_service('org.freedesktop.Notifications')\" 2>&1", "r"); 2997 | if (fgets(lBuff.ptr, lBuff.sizeof, lIn) is null) 2998 | { 2999 | ret = 2; 3000 | } 3001 | pclose(lIn); 3002 | if (tinyfd_verbose) 3003 | printf("perl-dbus %d\n", ret); 3004 | } 3005 | } 3006 | return graphicMode() ? ret : 0; 3007 | } 3008 | 3009 | int afplayPresent() 3010 | { 3011 | static int ret = -1; 3012 | char[MAX_PATH_OR_CMD] lBuff = '\0'; 3013 | FILE* lIn; 3014 | 3015 | if (ret < 0) 3016 | { 3017 | ret = detectPresence("afplay"); 3018 | if (ret) 3019 | { 3020 | lIn = popen("test -e /System/Library/Sounds/Ping.aiff || echo Ping", "r"); 3021 | if (fgets(lBuff.ptr, lBuff.sizeof, lIn) is null) 3022 | { 3023 | ret = 2; 3024 | } 3025 | pclose(lIn); 3026 | if (tinyfd_verbose) 3027 | printf("afplay %d\n", ret); 3028 | } 3029 | } 3030 | return graphicMode() ? ret : 0; 3031 | } 3032 | 3033 | int xdialogPresent() 3034 | { 3035 | static int ret = -1; 3036 | if (ret < 0) 3037 | { 3038 | ret = detectPresence("Xdialog"); 3039 | } 3040 | return ret && graphicMode(); 3041 | } 3042 | 3043 | int gdialogPresent() 3044 | { 3045 | static int ret = -1; 3046 | if (ret < 0) 3047 | { 3048 | ret = detectPresence("gdialog"); 3049 | } 3050 | return ret && graphicMode(); 3051 | } 3052 | 3053 | int osascriptPresent() 3054 | { 3055 | static int ret = -1; 3056 | if (ret < 0) 3057 | { 3058 | gWarningDisplayed |= !!getenv("SSH_TTY"); 3059 | ret = detectPresence("osascript"); 3060 | } 3061 | return ret && graphicMode() && !getenv("SSH_TTY"); 3062 | } 3063 | 3064 | int qarmaPresent() 3065 | { 3066 | static int ret = -1; 3067 | if (ret < 0) 3068 | { 3069 | ret = detectPresence("qarma"); 3070 | } 3071 | return ret && graphicMode(); 3072 | } 3073 | 3074 | int matedialogPresent() 3075 | { 3076 | static int ret = -1; 3077 | if (ret < 0) 3078 | { 3079 | ret = detectPresence("matedialog"); 3080 | } 3081 | return ret && graphicMode(); 3082 | } 3083 | 3084 | int shellementaryPresent() 3085 | { 3086 | static int ret = -1; 3087 | if (ret < 0) 3088 | { 3089 | ret = 0; /*detectPresence("shellementary"); shellementary is not ready yet */ 3090 | } 3091 | return ret && graphicMode(); 3092 | } 3093 | 3094 | int zenityPresent() 3095 | { 3096 | static int ret = -1; 3097 | if (ret < 0) 3098 | { 3099 | ret = detectPresence("zenity"); 3100 | } 3101 | return ret && graphicMode(); 3102 | } 3103 | 3104 | int zenity3Present() 3105 | { 3106 | static int ret = -1; 3107 | char[MAX_PATH_OR_CMD] lBuff = '\0'; 3108 | FILE* lIn; 3109 | int lIntTmp; 3110 | 3111 | if (ret < 0) 3112 | { 3113 | ret = 0; 3114 | if (zenityPresent()) 3115 | { 3116 | lIn = popen("zenity --version", "r"); 3117 | if (fgets(lBuff.ptr, lBuff.sizeof, lIn) !is null) 3118 | { 3119 | if (atoi(lBuff.ptr) >= 3) 3120 | { 3121 | ret = 3; 3122 | lIntTmp = atoi(strtok(lBuff.ptr, ".") + 2); 3123 | if (lIntTmp >= 18) 3124 | { 3125 | ret = 5; 3126 | } 3127 | else if (lIntTmp >= 10) 3128 | { 3129 | ret = 4; 3130 | } 3131 | } 3132 | else if ((atoi(lBuff.ptr) == 2) && (atoi(strtok(lBuff.ptr, ".") + 2) >= 32)) 3133 | { 3134 | ret = 2; 3135 | } 3136 | if (tinyfd_verbose) 3137 | printf("zenity %d\n", ret); 3138 | } 3139 | pclose(lIn); 3140 | } 3141 | } 3142 | return graphicMode() ? ret : 0; 3143 | } 3144 | 3145 | int kdialogPresent() 3146 | { 3147 | static int ret = -1; 3148 | char[MAX_PATH_OR_CMD] lBuff = '\0'; 3149 | FILE* lIn; 3150 | char* lDesktop; 3151 | 3152 | if (ret < 0) 3153 | { 3154 | if (zenityPresent()) 3155 | { 3156 | lDesktop = getenv("XDG_SESSION_DESKTOP"); 3157 | if (!eq(lDesktop, "KDE") && !eq(lDesktop, "lxqt")) 3158 | { 3159 | ret = 0; 3160 | return ret; 3161 | } 3162 | } 3163 | 3164 | ret = detectPresence("kdialog"); 3165 | if (ret && !getenv("SSH_TTY")) 3166 | { 3167 | lIn = popen("kdialog --attach 2>&1", "r"); 3168 | if (fgets(lBuff.ptr, lBuff.sizeof, lIn) !is null) 3169 | { 3170 | if (!strstr("Unknown", lBuff.ptr)) 3171 | { 3172 | ret = 2; 3173 | if (tinyfd_verbose) 3174 | printf("kdialog-attach %d\n", ret); 3175 | } 3176 | } 3177 | pclose(lIn); 3178 | 3179 | if (ret == 2) 3180 | { 3181 | ret = 1; 3182 | lIn = popen("kdialog --passivepopup 2>&1", "r"); 3183 | if (fgets(lBuff.ptr, lBuff.sizeof, lIn) !is null) 3184 | { 3185 | if (!strstr("Unknown", lBuff.ptr)) 3186 | { 3187 | ret = 2; 3188 | if (tinyfd_verbose) 3189 | printf("kdialog-popup %d\n", ret); 3190 | } 3191 | } 3192 | pclose(lIn); 3193 | } 3194 | } 3195 | } 3196 | return graphicMode() ? ret : 0; 3197 | } 3198 | 3199 | int osx9orBetter() 3200 | { 3201 | static int ret = -1; 3202 | char[MAX_PATH_OR_CMD] lBuff = '\0'; 3203 | FILE* lIn; 3204 | int V, v; 3205 | 3206 | if (ret < 0) 3207 | { 3208 | ret = 0; 3209 | lIn = popen("osascript -e 'set osver to system version of (system info)'", "r"); 3210 | if ((fgets(lBuff.ptr, lBuff.sizeof, lIn) !is null) && (2 == sscanf(lBuff.ptr, "%d.%d", &V, &v))) 3211 | { 3212 | V = V * 100 + v; 3213 | if (V >= 1009) 3214 | { 3215 | ret = 1; 3216 | } 3217 | } 3218 | pclose(lIn); 3219 | if (tinyfd_verbose) 3220 | printf("Osx10 = %d, %d = %s\n", ret, V, lBuff.ptr); 3221 | } 3222 | return ret; 3223 | } 3224 | 3225 | int python2Present() 3226 | { 3227 | static int ret = -1; 3228 | 3229 | if (ret < 0) 3230 | { 3231 | ret = 0; 3232 | strcpy(gPython2Name.ptr, "python2"); 3233 | if (detectPresence(gPython2Name.ptr)) 3234 | ret = 1; 3235 | else 3236 | { 3237 | for (int i = 9; i >= 0; i--) 3238 | { 3239 | sprintf(gPython2Name.ptr, "python2.%d", i); 3240 | if (detectPresence(gPython2Name.ptr)) 3241 | { 3242 | ret = 1; 3243 | break; 3244 | } 3245 | } 3246 | /*if ( ! ret ) 3247 | { 3248 | strcpy(gPython2Name , "python" ) ; 3249 | if ( detectPresence(gPython2Name.ptr) ) ret = 1; 3250 | }*/ 3251 | } 3252 | if (tinyfd_verbose) 3253 | printf("python2Present %d\n", ret); 3254 | if (tinyfd_verbose) 3255 | printf("gPython2Name %s\n", gPython2Name.ptr); 3256 | } 3257 | return ret; 3258 | } 3259 | 3260 | int python3Present() 3261 | { 3262 | static int ret = -1; 3263 | 3264 | if (ret < 0) 3265 | { 3266 | ret = 0; 3267 | strcpy(gPython3Name.ptr, "python3"); 3268 | if (detectPresence(gPython3Name.ptr)) 3269 | ret = 1; 3270 | else 3271 | { 3272 | for (int i = 9; i >= 0; i--) 3273 | { 3274 | sprintf(gPython3Name.ptr, "python3.%d", i); 3275 | if (detectPresence(gPython3Name.ptr)) 3276 | { 3277 | ret = 1; 3278 | break; 3279 | } 3280 | } 3281 | /*if ( ! ret ) 3282 | { 3283 | strcpy(gPython3Name , "python" ) ; 3284 | if ( detectPresence(gPython3Name.ptr) ) ret = 1; 3285 | }*/ 3286 | } 3287 | if (tinyfd_verbose) 3288 | printf("python3Present %d\n", ret); 3289 | if (tinyfd_verbose) 3290 | printf("gPython3Name %s\n", gPython3Name.ptr); 3291 | } 3292 | return ret; 3293 | } 3294 | 3295 | int tkinter2Present() 3296 | { 3297 | static int ret = -1; 3298 | char[256] lPythonCommand = '\0'; 3299 | char[256] lPythonParams = 3300 | "-S -c \"try:\n\timport Tkinter;\nexcept:\n\tprint 0;\""; 3301 | 3302 | if (ret < 0) 3303 | { 3304 | ret = 0; 3305 | if (python2Present()) 3306 | { 3307 | sprintf(lPythonCommand.ptr, "%s %s", gPython2Name.ptr, lPythonParams.ptr); 3308 | ret = tryCommand(lPythonCommand.ptr); 3309 | } 3310 | if (tinyfd_verbose) 3311 | printf("tkinter2Present %d\n", ret); 3312 | } 3313 | return ret && graphicMode() && !(isDarwin() && getenv("SSH_TTY")); 3314 | } 3315 | 3316 | int tkinter3Present() 3317 | { 3318 | static int ret = -1; 3319 | char[256] lPythonCommand = '\0'; 3320 | char[256] lPythonParams = 3321 | "-S -c \"try:\n\timport tkinter;\nexcept:\n\tprint(0);\""; 3322 | 3323 | if (ret < 0) 3324 | { 3325 | ret = 0; 3326 | if (python3Present()) 3327 | { 3328 | sprintf(lPythonCommand.ptr, "%s %s", gPython3Name.ptr, lPythonParams.ptr); 3329 | ret = tryCommand(lPythonCommand.ptr); 3330 | } 3331 | if (tinyfd_verbose) 3332 | printf("tkinter3Present %d\n", ret); 3333 | } 3334 | return ret && graphicMode() && !(isDarwin() && getenv("SSH_TTY")); 3335 | } 3336 | 3337 | int pythonDbusPresent() 3338 | { 3339 | static int ret = -1; 3340 | char[256] lPythonCommand = '\0'; 3341 | char[256] lPythonParams = 3342 | `-c "try: 3343 | import dbus 3344 | bus=dbus.SessionBus() 3345 | notif=bus.get_object('org.freedesktop.Notifications','/org/freedesktop/Notifications') 3346 | notify=dbus.Interface(notif,'org.freedesktop.Notifications') 3347 | except: 3348 | print(0)"`; 3349 | 3350 | if (ret < 0) 3351 | { 3352 | ret = 0; 3353 | if (python2Present()) 3354 | { 3355 | strcpy(gPythonName.ptr, gPython2Name.ptr); 3356 | sprintf(lPythonCommand.ptr, "%s %s", gPythonName.ptr, lPythonParams.ptr); 3357 | ret = tryCommand(lPythonCommand.ptr); 3358 | } 3359 | 3360 | if (!ret && python3Present()) 3361 | { 3362 | strcpy(gPythonName.ptr, gPython3Name.ptr); 3363 | sprintf(lPythonCommand.ptr, "%s %s", gPythonName.ptr, lPythonParams.ptr); 3364 | ret = tryCommand(lPythonCommand.ptr); 3365 | } 3366 | 3367 | if (tinyfd_verbose) 3368 | printf("dbusPresent %d\n", ret); 3369 | if (tinyfd_verbose) 3370 | printf("gPythonName %s\n", gPythonName.ptr); 3371 | } 3372 | return ret && graphicMode() && !(isDarwin() && getenv("SSH_TTY")); 3373 | } 3374 | 3375 | void sigHandler(int sig) 3376 | { 3377 | FILE* lIn = popen("pactl unload-module module-sine", "r"); 3378 | if (lIn) 3379 | { 3380 | pclose(lIn); 3381 | } 3382 | } 3383 | 3384 | void _beep() 3385 | { 3386 | char[256] str_buf = '\0'; 3387 | char* str = str_buf.ptr; 3388 | FILE* lIn; 3389 | 3390 | if (osascriptPresent()) 3391 | { 3392 | if (afplayPresent() >= 2) 3393 | { 3394 | strcpy(str, "afplay /System/Library/Sounds/Ping.aiff"); 3395 | } 3396 | else 3397 | { 3398 | strcpy(str, "osascript -e 'tell application \"System Events\" to beep'"); 3399 | } 3400 | } 3401 | else if (pactlPresent()) 3402 | { 3403 | signal(SIGINT, &sigHandler); 3404 | /*strcpy( str , "pactl load-module module-sine frequency=440;sleep .3;pactl unload-module module-sine" ) ;*/ 3405 | strcpy(str, "thnum=$(pactl load-module module-sine frequency=440);sleep .3;pactl unload-module $thnum"); 3406 | } 3407 | else if (speakertestPresent()) 3408 | { 3409 | /*strcpy( str , "timeout -k .3 .3 speaker-test --frequency 440 --test sine > /dev/tty" ) ;*/ 3410 | strcpy(str, "( speaker-test -t sine -f 440 > /dev/tty )& pid=$!;sleep .3; kill -9 $pid"); 3411 | } 3412 | else if (beepexePresent()) 3413 | { 3414 | strcpy(str, "beep.exe 440 300"); 3415 | } 3416 | else 3417 | { 3418 | strcpy(str, "printf '\a' > /dev/tty"); 3419 | } 3420 | 3421 | if (tinyfd_verbose) 3422 | printf("str: %s\n", str); 3423 | 3424 | lIn = popen(str, "r"); 3425 | if (lIn) 3426 | { 3427 | pclose(lIn); 3428 | } 3429 | 3430 | if (pactlPresent()) 3431 | { 3432 | signal(SIGINT, SIG_DFL); 3433 | } 3434 | } 3435 | 3436 | int _messageBox( 3437 | const char* aTitle, 3438 | const char* aMessage, 3439 | const char* aDialogType, 3440 | const char* aIconType, 3441 | int aDefaultButton) 3442 | { 3443 | char[MAX_PATH_OR_CMD] lBuff = '\0'; 3444 | const bool lQuery = eq(aTitle, "tinyfd_query"); 3445 | char* str; 3446 | char* lpDialogString; 3447 | FILE* lIn; 3448 | bool lWasGraphicDialog; 3449 | bool lWasXterm; 3450 | int lResult; 3451 | char lChar; 3452 | termios infoOri; 3453 | termios info; 3454 | size_t lTitleLen; 3455 | size_t lMessageLen; 3456 | 3457 | lBuff[0] = '\0'; 3458 | 3459 | lTitleLen = aTitle ? strlen(aTitle) : 0; 3460 | lMessageLen = aMessage ? strlen(aMessage) : 0; 3461 | if (!aTitle || !lQuery) 3462 | { 3463 | str = cast(char*)malloc(MAX_PATH_OR_CMD + lTitleLen + lMessageLen); 3464 | } 3465 | 3466 | if (osascriptPresent()) 3467 | { 3468 | if (lQuery) 3469 | { 3470 | response("applescript"); 3471 | return 1; 3472 | } 3473 | 3474 | strcpy(str, "osascript "); 3475 | if (!osx9orBetter()) 3476 | strcat(str, " -e 'tell application \"System Events\"' -e 'Activate'"); 3477 | strcat(str, " -e 'try' -e 'set {vButton} to {button returned} of ( display dialog \""); 3478 | if (some(aMessage)) 3479 | { 3480 | strcat(str, aMessage); 3481 | } 3482 | strcat(str, "\" "); 3483 | if (some(aTitle)) 3484 | { 3485 | strcat(str, "with title \""); 3486 | strcat(str, aTitle); 3487 | strcat(str, "\" "); 3488 | } 3489 | strcat(str, "with icon "); 3490 | if (eq("error", aIconType)) 3491 | { 3492 | strcat(str, "stop "); 3493 | } 3494 | else if (eq("warning", aIconType)) 3495 | { 3496 | strcat(str, "caution "); 3497 | } 3498 | else /* question or info */ 3499 | { 3500 | strcat(str, "note "); 3501 | } 3502 | if (eq("okcancel", aDialogType)) 3503 | { 3504 | if (!aDefaultButton) 3505 | { 3506 | strcat(str, "default button \"Cancel\" "); 3507 | } 3508 | } 3509 | else if (eq("yesno", aDialogType)) 3510 | { 3511 | strcat(str, "buttons {\"No\", \"Yes\"} "); 3512 | if (aDefaultButton) 3513 | { 3514 | strcat(str, "default button \"Yes\" "); 3515 | } 3516 | else 3517 | { 3518 | strcat(str, "default button \"No\" "); 3519 | } 3520 | strcat(str, "cancel button \"No\""); 3521 | } 3522 | else if (eq("yesnocancel", aDialogType)) 3523 | { 3524 | strcat(str, "buttons {\"No\", \"Yes\", \"Cancel\"} "); 3525 | switch (aDefaultButton) 3526 | { 3527 | case 1: 3528 | strcat(str, "default button \"Yes\" "); 3529 | break; 3530 | case 2: 3531 | strcat(str, "default button \"No\" "); 3532 | break; 3533 | case 0: 3534 | strcat(str, "default button \"Cancel\" "); 3535 | break; 3536 | default: 3537 | break; 3538 | } 3539 | strcat(str, "cancel button \"Cancel\""); 3540 | } 3541 | else 3542 | { 3543 | strcat(str, "buttons {\"OK\"} "); 3544 | strcat(str, "default button \"OK\" "); 3545 | } 3546 | strcat(str, ")' "); 3547 | 3548 | strcat(str, 3549 | " -e 'if vButton is \"Yes\" then' -e 'return 1'" ~ 3550 | " -e 'else if vButton is \"OK\" then' -e 'return 1'" ~ 3551 | " -e 'else if vButton is \"No\" then' -e 'return 2'" ~ 3552 | " -e 'else' -e 'return 0' -e 'end if' "); 3553 | 3554 | strcat(str, "-e 'on error number -128' "); 3555 | strcat(str, "-e '0' "); 3556 | 3557 | strcat(str, "-e 'end try'"); 3558 | if (!osx9orBetter()) 3559 | strcat(str, " -e 'end tell'"); 3560 | } 3561 | else if (kdialogPresent()) 3562 | { 3563 | if (lQuery) 3564 | { 3565 | response("kdialog"); 3566 | return 1; 3567 | } 3568 | 3569 | strcpy(str, "kdialog"); 3570 | if (kdialogPresent() == 2) 3571 | { 3572 | strcat(str, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ 3573 | } 3574 | 3575 | strcat(str, " --"); 3576 | if (eq("okcancel", aDialogType) || eq("yesno", aDialogType) || eq("yesnocancel", aDialogType)) 3577 | { 3578 | if (eq("warning", aIconType) || eq("error", aIconType)) 3579 | { 3580 | strcat(str, "warning"); 3581 | } 3582 | if (eq("yesnocancel", aDialogType)) 3583 | { 3584 | strcat(str, "yesnocancel"); 3585 | } 3586 | else 3587 | { 3588 | strcat(str, "yesno"); 3589 | } 3590 | } 3591 | else if (eq("error", aIconType)) 3592 | { 3593 | strcat(str, "error"); 3594 | } 3595 | else if (eq("warning", aIconType)) 3596 | { 3597 | strcat(str, "sorry"); 3598 | } 3599 | else 3600 | { 3601 | strcat(str, "msgbox"); 3602 | } 3603 | strcat(str, " \""); 3604 | if (aMessage) 3605 | { 3606 | strcat(str, aMessage); 3607 | } 3608 | strcat(str, "\""); 3609 | if (eq("okcancel", aDialogType)) 3610 | { 3611 | strcat(str, 3612 | " --yes-label Ok --no-label Cancel"); 3613 | } 3614 | if (some(aTitle)) 3615 | { 3616 | strcat(str, " --title \""); 3617 | strcat(str, aTitle); 3618 | strcat(str, "\""); 3619 | } 3620 | 3621 | if (eq("yesnocancel", aDialogType)) 3622 | { 3623 | strcat(str, "; x=$? ;if [ $x = 0 ] ;then echo 1;elif [ $x = 1 ] ;then echo 2;else echo 0;fi"); 3624 | } 3625 | else 3626 | { 3627 | strcat(str, ";if [ $? = 0 ];then echo 1;else echo 0;fi"); 3628 | } 3629 | } 3630 | else if (zenityPresent() || matedialogPresent() || shellementaryPresent() || qarmaPresent()) 3631 | { 3632 | if (zenityPresent()) 3633 | { 3634 | if (lQuery) 3635 | { 3636 | response("zenity"); 3637 | return 1; 3638 | } 3639 | strcpy(str, "szAnswer=$(zenity"); 3640 | if (zenity3Present() >= 4 && !getenv("SSH_TTY")) 3641 | { 3642 | strcat(str, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ 3643 | } 3644 | } 3645 | else if (matedialogPresent()) 3646 | { 3647 | if (lQuery) 3648 | { 3649 | response("matedialog"); 3650 | return 1; 3651 | } 3652 | strcpy(str, "szAnswer=$(matedialog"); 3653 | } 3654 | else if (shellementaryPresent()) 3655 | { 3656 | if (lQuery) 3657 | { 3658 | response("shellementary"); 3659 | return 1; 3660 | } 3661 | strcpy(str, "szAnswer=$(shellementary"); 3662 | } 3663 | else 3664 | { 3665 | if (lQuery) 3666 | { 3667 | response("qarma"); 3668 | return 1; 3669 | } 3670 | strcpy(str, "szAnswer=$(qarma"); 3671 | if (!getenv("SSH_TTY")) 3672 | { 3673 | strcat(str, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ 3674 | } 3675 | } 3676 | strcat(str, " --"); 3677 | 3678 | if (eq("okcancel", aDialogType)) 3679 | { 3680 | strcat(str, "question --ok-label=Ok --cancel-label=Cancel"); 3681 | } 3682 | else if (eq("yesno", aDialogType)) 3683 | { 3684 | strcat(str, "question"); 3685 | } 3686 | else if (eq("yesnocancel", aDialogType)) 3687 | { 3688 | strcat(str, "list --column \"\" --hide-header \"Yes\" \"No\""); 3689 | } 3690 | else if (eq("error", aIconType)) 3691 | { 3692 | strcat(str, "error"); 3693 | } 3694 | else if (eq("warning", aIconType)) 3695 | { 3696 | strcat(str, "warning"); 3697 | } 3698 | else 3699 | { 3700 | strcat(str, "info"); 3701 | } 3702 | if (some(aTitle)) 3703 | { 3704 | strcat(str, " --title=\""); 3705 | strcat(str, aTitle); 3706 | strcat(str, "\""); 3707 | } 3708 | if (some(aMessage)) 3709 | { 3710 | strcat(str, " --no-wrap --text=\""); 3711 | strcat(str, aMessage); 3712 | strcat(str, "\""); 3713 | } 3714 | if ((zenity3Present() >= 3) || (!zenityPresent() && (shellementaryPresent() || qarmaPresent()))) 3715 | { 3716 | strcat(str, " --icon-name=dialog-"); 3717 | if (eq("question", aIconType) || eq("error", aIconType) || eq("warning", aIconType)) 3718 | { 3719 | strcat(str, aIconType); 3720 | } 3721 | else 3722 | { 3723 | strcat(str, "information"); 3724 | } 3725 | } 3726 | 3727 | if (tinyfd_silent) 3728 | strcat(str, " 2>/dev/null "); 3729 | 3730 | if (eq("yesnocancel", aDialogType)) 3731 | { 3732 | strcat(str, 3733 | ");if [ $? = 1 ];then echo 0;elif [ $szAnswer = \"No\" ];then echo 2;else echo 1;fi"); 3734 | } 3735 | else 3736 | { 3737 | strcat(str, ");if [ $? = 0 ];then echo 1;else echo 0;fi"); 3738 | } 3739 | } 3740 | else if (!gxmessagePresent() && !gmessagePresent() && !gdialogPresent() && !xdialogPresent() && tkinter2Present()) 3741 | { 3742 | if (lQuery) 3743 | { 3744 | response("python2-tkinter"); 3745 | return 1; 3746 | } 3747 | 3748 | strcpy(str, gPython2Name.ptr); 3749 | if (!isTerminalRunning() && isDarwin()) 3750 | { 3751 | strcat(str, " -i"); /* for osx without console */ 3752 | } 3753 | 3754 | strcat(str, 3755 | " -S -c \"import Tkinter,tkMessageBox;root=Tkinter.Tk();root.withdraw();"); 3756 | 3757 | if (isDarwin()) 3758 | { 3759 | strcat(str, 3760 | "import os;os.system('''/usr/bin/osascript -e 'tell app \\\"Finder\\\" to set " ~ 3761 | "frontmost of process \\\"Python\\\" to true' ''');"); 3762 | } 3763 | 3764 | strcat(str, "res=tkMessageBox."); 3765 | if (eq("okcancel", aDialogType)) 3766 | { 3767 | strcat(str, "askokcancel("); 3768 | if (aDefaultButton) 3769 | { 3770 | strcat(str, "default=tkMessageBox.OK,"); 3771 | } 3772 | else 3773 | { 3774 | strcat(str, "default=tkMessageBox.CANCEL,"); 3775 | } 3776 | } 3777 | else if (eq("yesno", aDialogType)) 3778 | { 3779 | strcat(str, "askyesno("); 3780 | if (aDefaultButton) 3781 | { 3782 | strcat(str, "default=tkMessageBox.YES,"); 3783 | } 3784 | else 3785 | { 3786 | strcat(str, "default=tkMessageBox.NO,"); 3787 | } 3788 | } 3789 | else if (eq("yesnocancel", aDialogType)) 3790 | { 3791 | strcat(str, "askyesnocancel("); 3792 | switch (aDefaultButton) 3793 | { 3794 | case 1: 3795 | strcat(str, "default=tkMessageBox.YES,"); 3796 | break; 3797 | case 2: 3798 | strcat(str, "default=tkMessageBox.NO,"); 3799 | break; 3800 | case 0: 3801 | strcat(str, "default=tkMessageBox.CANCEL,"); 3802 | break; 3803 | default: 3804 | break; 3805 | } 3806 | } 3807 | else 3808 | { 3809 | strcat(str, "showinfo("); 3810 | } 3811 | 3812 | strcat(str, "icon='"); 3813 | if (eq("question", aIconType) || eq("error", aIconType) || eq("warning", aIconType)) 3814 | { 3815 | strcat(str, aIconType); 3816 | } 3817 | else 3818 | { 3819 | strcat(str, "info"); 3820 | } 3821 | 3822 | strcat(str, "',"); 3823 | if (some(aTitle)) 3824 | { 3825 | strcat(str, "title='"); 3826 | strcat(str, aTitle); 3827 | strcat(str, "',"); 3828 | } 3829 | if (some(aMessage)) 3830 | { 3831 | strcat(str, "message='"); 3832 | lpDialogString = str + strlen(str); 3833 | replaceSubStr(aMessage, "\n", "\\n", lpDialogString); 3834 | strcat(str, "'"); 3835 | } 3836 | 3837 | if (eq("yesnocancel", aDialogType)) 3838 | { 3839 | strcat(str, `); 3840 | if res is None: 3841 | print 0 3842 | elif res is False: 3843 | print 2 3844 | else: 3845 | print 1 3846 | "`); 3847 | } 3848 | else 3849 | { 3850 | strcat(str, `); 3851 | if res is False: 3852 | print 0 3853 | else: 3854 | print 1 3855 | "`); 3856 | } 3857 | } 3858 | else if (!gxmessagePresent() && !gmessagePresent() && !gdialogPresent() && !xdialogPresent() && tkinter3Present()) 3859 | { 3860 | if (lQuery) 3861 | { 3862 | response("python3-tkinter"); 3863 | return 1; 3864 | } 3865 | 3866 | strcpy(str, gPython3Name.ptr); 3867 | strcat(str, 3868 | " -S -c \"import tkinter;from tkinter import messagebox;root=tkinter.Tk();root.withdraw();"); 3869 | 3870 | strcat(str, "res=messagebox."); 3871 | if (eq("okcancel", aDialogType)) 3872 | { 3873 | strcat(str, "askokcancel("); 3874 | if (aDefaultButton) 3875 | { 3876 | strcat(str, "default=messagebox.OK,"); 3877 | } 3878 | else 3879 | { 3880 | strcat(str, "default=messagebox.CANCEL,"); 3881 | } 3882 | } 3883 | else if (eq("yesno", aDialogType)) 3884 | { 3885 | strcat(str, "askyesno("); 3886 | if (aDefaultButton) 3887 | { 3888 | strcat(str, "default=messagebox.YES,"); 3889 | } 3890 | else 3891 | { 3892 | strcat(str, "default=messagebox.NO,"); 3893 | } 3894 | } 3895 | else if (eq("yesnocancel", aDialogType)) 3896 | { 3897 | strcat(str, "askyesnocancel("); 3898 | switch (aDefaultButton) 3899 | { 3900 | case 1: 3901 | strcat(str, "default=messagebox.YES,"); 3902 | break; 3903 | case 2: 3904 | strcat(str, "default=messagebox.NO,"); 3905 | break; 3906 | case 0: 3907 | strcat(str, "default=messagebox.CANCEL,"); 3908 | break; 3909 | default: 3910 | break; 3911 | } 3912 | } 3913 | else 3914 | { 3915 | strcat(str, "showinfo("); 3916 | } 3917 | 3918 | strcat(str, "icon='"); 3919 | if (eq("question", aIconType) || eq("error", aIconType) || eq("warning", aIconType)) 3920 | { 3921 | strcat(str, aIconType); 3922 | } 3923 | else 3924 | { 3925 | strcat(str, "info"); 3926 | } 3927 | 3928 | strcat(str, "',"); 3929 | if (some(aTitle)) 3930 | { 3931 | strcat(str, "title='"); 3932 | strcat(str, aTitle); 3933 | strcat(str, "',"); 3934 | } 3935 | if (some(aMessage)) 3936 | { 3937 | strcat(str, "message='"); 3938 | lpDialogString = str + strlen(str); 3939 | replaceSubStr(aMessage, "\n", "\\n", lpDialogString); 3940 | strcat(str, "'"); 3941 | } 3942 | 3943 | if (eq("yesnocancel", aDialogType)) 3944 | { 3945 | strcat(str, `); 3946 | if res is None: 3947 | print(0) 3948 | elif res is False: 3949 | print(2) 3950 | else: 3951 | print(1) 3952 | "`); 3953 | } 3954 | else 3955 | { 3956 | strcat(str, `); 3957 | if res is False: 3958 | print(0) 3959 | else: 3960 | print(1) 3961 | "`); 3962 | } 3963 | } 3964 | else if (gxmessagePresent() || gmessagePresent() || (!gdialogPresent() && !xdialogPresent() && xmessagePresent())) 3965 | { 3966 | if (gxmessagePresent()) 3967 | { 3968 | if (lQuery) 3969 | { 3970 | response("gxmessage"); 3971 | return 1; 3972 | } 3973 | strcpy(str, "gxmessage"); 3974 | } 3975 | else if (gmessagePresent()) 3976 | { 3977 | if (lQuery) 3978 | { 3979 | response("gmessage"); 3980 | return 1; 3981 | } 3982 | strcpy(str, "gmessage"); 3983 | } 3984 | else 3985 | { 3986 | if (lQuery) 3987 | { 3988 | response("xmessage"); 3989 | return 1; 3990 | } 3991 | strcpy(str, "xmessage"); 3992 | } 3993 | 3994 | if (eq("okcancel", aDialogType)) 3995 | { 3996 | strcat(str, " -buttons Ok:1,Cancel:0"); 3997 | switch (aDefaultButton) 3998 | { 3999 | case 1: 4000 | strcat(str, " -default Ok"); 4001 | break; 4002 | case 0: 4003 | strcat(str, " -default Cancel"); 4004 | break; 4005 | default: 4006 | break; 4007 | } 4008 | } 4009 | else if (eq("yesno", aDialogType)) 4010 | { 4011 | strcat(str, " -buttons Yes:1,No:0"); 4012 | switch (aDefaultButton) 4013 | { 4014 | case 1: 4015 | strcat(str, " -default Yes"); 4016 | break; 4017 | case 0: 4018 | strcat(str, " -default No"); 4019 | break; 4020 | default: 4021 | break; 4022 | } 4023 | } 4024 | else if (eq("yesnocancel", aDialogType)) 4025 | { 4026 | strcat(str, " -buttons Yes:1,No:2,Cancel:0"); 4027 | switch (aDefaultButton) 4028 | { 4029 | case 1: 4030 | strcat(str, " -default Yes"); 4031 | break; 4032 | case 2: 4033 | strcat(str, " -default No"); 4034 | break; 4035 | case 0: 4036 | strcat(str, " -default Cancel"); 4037 | break; 4038 | default: 4039 | break; 4040 | } 4041 | } 4042 | else 4043 | { 4044 | strcat(str, " -buttons Ok:1"); 4045 | strcat(str, " -default Ok"); 4046 | } 4047 | 4048 | strcat(str, " -center \""); 4049 | if (some(aMessage)) 4050 | { 4051 | strcat(str, aMessage); 4052 | } 4053 | strcat(str, "\""); 4054 | if (some(aTitle)) 4055 | { 4056 | strcat(str, " -title \""); 4057 | strcat(str, aTitle); 4058 | strcat(str, "\""); 4059 | } 4060 | strcat(str, " ; echo $? "); 4061 | } 4062 | else if (xdialogPresent() || gdialogPresent() || dialogName() || whiptailPresent()) 4063 | { 4064 | if (gdialogPresent()) 4065 | { 4066 | if (lQuery) 4067 | { 4068 | response("gdialog"); 4069 | return 1; 4070 | } 4071 | lWasGraphicDialog = true; 4072 | strcpy(str, "(gdialog "); 4073 | } 4074 | else if (xdialogPresent()) 4075 | { 4076 | if (lQuery) 4077 | { 4078 | response("xdialog"); 4079 | return 1; 4080 | } 4081 | lWasGraphicDialog = true; 4082 | strcpy(str, "(Xdialog "); 4083 | } 4084 | else if (dialogName()) 4085 | { 4086 | if (lQuery) 4087 | { 4088 | response("dialog"); 4089 | return 0; 4090 | } 4091 | if (isTerminalRunning()) 4092 | { 4093 | strcpy(str, "(dialog "); 4094 | } 4095 | else 4096 | { 4097 | lWasXterm = true; 4098 | strcpy(str, terminalName()); 4099 | strcat(str, "'("); 4100 | strcat(str, dialogName()); 4101 | strcat(str, " "); 4102 | } 4103 | } 4104 | else if (isTerminalRunning()) 4105 | { 4106 | if (lQuery) 4107 | { 4108 | response("whiptail"); 4109 | return 0; 4110 | } 4111 | strcpy(str, "(whiptail "); 4112 | } 4113 | else 4114 | { 4115 | if (lQuery) 4116 | { 4117 | response("whiptail"); 4118 | return 0; 4119 | } 4120 | lWasXterm = true; 4121 | strcpy(str, terminalName()); 4122 | strcat(str, "'(whiptail "); 4123 | } 4124 | 4125 | if (some(aTitle)) 4126 | { 4127 | strcat(str, "--title \""); 4128 | strcat(str, aTitle); 4129 | strcat(str, "\" "); 4130 | } 4131 | 4132 | if (!xdialogPresent() && !gdialogPresent()) 4133 | { 4134 | if (eq("okcancel", aDialogType) || eq("yesno", aDialogType) || eq("yesnocancel", aDialogType)) 4135 | { 4136 | strcat(str, "--backtitle \""); 4137 | strcat(str, "tab: move focus"); 4138 | strcat(str, "\" "); 4139 | } 4140 | } 4141 | 4142 | if (eq("okcancel", aDialogType)) 4143 | { 4144 | if (!aDefaultButton) 4145 | { 4146 | strcat(str, "--defaultno "); 4147 | } 4148 | strcat(str, 4149 | "--yes-label \"Ok\" --no-label \"Cancel\" --yesno "); 4150 | } 4151 | else if (eq("yesno", aDialogType)) 4152 | { 4153 | if (!aDefaultButton) 4154 | { 4155 | strcat(str, "--defaultno "); 4156 | } 4157 | strcat(str, "--yesno "); 4158 | } 4159 | else if (eq("yesnocancel", aDialogType)) 4160 | { 4161 | if (!aDefaultButton) 4162 | { 4163 | strcat(str, "--defaultno "); 4164 | } 4165 | strcat(str, "--menu "); 4166 | } 4167 | else 4168 | { 4169 | strcat(str, "--msgbox "); 4170 | } 4171 | strcat(str, "\""); 4172 | if (some(aMessage)) 4173 | { 4174 | strcat(str, aMessage); 4175 | } 4176 | strcat(str, "\" "); 4177 | 4178 | if (lWasGraphicDialog) 4179 | { 4180 | if (eq("yesnocancel", aDialogType)) 4181 | { 4182 | strcat(str, "0 60 0 Yes \"\" No \"\") 2>/tmp/tinyfd.txt;" ~ 4183 | "if [ $? = 0 ];then tinyfdBool=1;else tinyfdBool=0;fi;" ~ 4184 | "tinyfdRes=$(cat /tmp/tinyfd.txt);echo $tinyfdBool$tinyfdRes"); 4185 | } 4186 | else 4187 | { 4188 | strcat(str, "10 60 ) 2>&1;if [ $? = 0 ];then echo 1;else echo 0;fi"); 4189 | } 4190 | } 4191 | else 4192 | { 4193 | if (eq("yesnocancel", aDialogType)) 4194 | { 4195 | strcat(str, "0 60 0 Yes \"\" No \"\" >/dev/tty ) 2>/tmp/tinyfd.txt;" ~ 4196 | "if [ $? = 0 ];then tinyfdBool=1;else tinyfdBool=0;fi;" ~ 4197 | "tinyfdRes=$(cat /tmp/tinyfd.txt);echo $tinyfdBool$tinyfdRes"); 4198 | 4199 | if (lWasXterm) 4200 | { 4201 | strcat(str, " >/tmp/tinyfd0.txt';cat /tmp/tinyfd0.txt"); 4202 | } 4203 | else 4204 | { 4205 | strcat(str, "; clear >/dev/tty"); 4206 | } 4207 | } 4208 | else 4209 | { 4210 | strcat(str, "10 60 >/dev/tty) 2>&1;if [ $? = 0 ];"); 4211 | if (lWasXterm) 4212 | { 4213 | strcat(str, 4214 | "then\n\techo 1\nelse\n\techo 0\nfi >/tmp/tinyfd.txt';cat /tmp/tinyfd.txt;rm /tmp/tinyfd.txt"); 4215 | } 4216 | else 4217 | { 4218 | strcat(str, 4219 | "then echo 1;else echo 0;fi;clear >/dev/tty"); 4220 | } 4221 | } 4222 | } 4223 | } 4224 | else if (!isTerminalRunning() && terminalName()) 4225 | { 4226 | if (lQuery) 4227 | { 4228 | response("basicinput"); 4229 | return 0; 4230 | } 4231 | strcpy(str, terminalName()); 4232 | strcat(str, "'"); 4233 | if (!gWarningDisplayed && !tinyfd_forceConsole) 4234 | { 4235 | gWarningDisplayed = true; 4236 | strcat(str, "echo \""); 4237 | strcat(str, gTitle.ptr); 4238 | strcat(str, "\";"); 4239 | strcat(str, "echo \""); 4240 | strcat(str, tinyfd_needs.ptr); 4241 | strcat(str, "\";echo;echo;"); 4242 | } 4243 | if (some(aTitle)) 4244 | { 4245 | strcat(str, "echo \""); 4246 | strcat(str, aTitle); 4247 | strcat(str, "\";echo;"); 4248 | } 4249 | if (some(aMessage)) 4250 | { 4251 | strcat(str, "echo \""); 4252 | strcat(str, aMessage); 4253 | strcat(str, "\"; "); 4254 | } 4255 | if (eq("yesno", aDialogType)) 4256 | { 4257 | strcat(str, "echo -n \"y/n: \"; "); 4258 | strcat(str, "stty sane -echo;"); 4259 | strcat(str, 4260 | "answer=$( while ! head -c 1 | grep -i [ny];do true ;done);"); 4261 | strcat(str, 4262 | "if echo \"$answer\" | grep -iq \"^y\";then\n"); 4263 | strcat(str, "\techo 1\nelse\n\techo 0\nfi"); 4264 | } 4265 | else if (eq("okcancel", aDialogType)) 4266 | { 4267 | strcat(str, "echo -n \"[O]kay/[C]ancel: \"; "); 4268 | strcat(str, "stty sane -echo;"); 4269 | strcat(str, 4270 | "answer=$( while ! head -c 1 | grep -i [oc];do true ;done);"); 4271 | strcat(str, 4272 | "if echo \"$answer\" | grep -iq \"^o\";then\n"); 4273 | strcat(str, "\techo 1\nelse\n\techo 0\nfi"); 4274 | } 4275 | else if (eq("yesnocancel", aDialogType)) 4276 | { 4277 | strcat(str, "echo -n \"[Y]es/[N]o/[C]ancel: \"; "); 4278 | strcat(str, "stty sane -echo;"); 4279 | strcat(str, 4280 | "answer=$( while ! head -c 1 | grep -i [nyc];do true ;done);"); 4281 | strcat(str, 4282 | "if echo \"$answer\" | grep -iq \"^y\";then\n\techo 1\n"); 4283 | strcat(str, "elif echo \"$answer\" | grep -iq \"^n\";then\n\techo 2\n"); 4284 | strcat(str, "else\n\techo 0\nfi"); 4285 | } 4286 | else 4287 | { 4288 | strcat(str, "echo -n \"press enter to continue \"; "); 4289 | strcat(str, "stty sane -echo;"); 4290 | strcat(str, 4291 | "answer=$( while ! head -c 1;do true ;done);echo 1"); 4292 | } 4293 | strcat(str, 4294 | " >/tmp/tinyfd.txt';cat /tmp/tinyfd.txt;rm /tmp/tinyfd.txt"); 4295 | } 4296 | else if (!isTerminalRunning() && pythonDbusPresent() && eq("ok", aDialogType)) 4297 | { 4298 | if (lQuery) 4299 | { 4300 | response("python-dbus"); 4301 | return 1; 4302 | } 4303 | strcpy(str, gPythonName.ptr); 4304 | strcat(str, " -c \"import dbus;bus=dbus.SessionBus();"); 4305 | strcat(str, "notif=bus.get_object('org.freedesktop.Notifications','/org/freedesktop/Notifications');"); 4306 | strcat(str, "notify=dbus.Interface(notif,'org.freedesktop.Notifications');"); 4307 | strcat(str, "notify.Notify('',0,'"); 4308 | if (some(aIconType)) 4309 | { 4310 | strcat(str, aIconType); 4311 | } 4312 | strcat(str, "','"); 4313 | if (some(aTitle)) 4314 | { 4315 | strcat(str, aTitle); 4316 | } 4317 | strcat(str, "','"); 4318 | if (some(aMessage)) 4319 | { 4320 | lpDialogString = str + strlen(str); 4321 | replaceSubStr(aMessage, "\n", "\\n", lpDialogString); 4322 | } 4323 | strcat(str, "','','',5000)\""); 4324 | } 4325 | else if (!isTerminalRunning() && (perlPresent() >= 2) && eq("ok", aDialogType)) 4326 | { 4327 | if (lQuery) 4328 | { 4329 | response("perl-dbus"); 4330 | return 1; 4331 | } 4332 | sprintf(str, `perl -e "use Net::DBus; 4333 | my \$sessionBus = Net::DBus->session; 4334 | my \$notificationsService = \$sessionBus->get_service('org.freedesktop.Notifications'); 4335 | my \$notificationsObject = \$notificationsService->get_object('/org/freedesktop/Notifications', 4336 | 'org.freedesktop.Notifications'); 4337 | my \$notificationId;\$notificationId = \$notificationsObject->Notify(shift, 0, '%s', '%s', '%s', [], {}, -1);"`, 4338 | aIconType ? aIconType : "", aTitle ? aTitle : "", aMessage ? aMessage : ""); 4339 | } 4340 | else if (!isTerminalRunning() && notifysendPresent() && eq("ok", aDialogType)) 4341 | { 4342 | 4343 | if (lQuery) 4344 | { 4345 | response("notifysend"); 4346 | return 1; 4347 | } 4348 | strcpy(str, "notify-send"); 4349 | if (some(aIconType)) 4350 | { 4351 | strcat(str, " -i '"); 4352 | strcat(str, aIconType); 4353 | strcat(str, "'"); 4354 | } 4355 | strcat(str, " \""); 4356 | if (some(aTitle)) 4357 | { 4358 | strcat(str, aTitle); 4359 | strcat(str, " | "); 4360 | } 4361 | if (some(aMessage)) 4362 | { 4363 | replaceSubStr(aMessage, "\n\t", " | ", lBuff.ptr); 4364 | replaceSubStr(aMessage, "\n", " | ", lBuff.ptr); 4365 | replaceSubStr(aMessage, "\t", " ", lBuff.ptr); 4366 | strcat(str, lBuff.ptr); 4367 | } 4368 | strcat(str, "\""); 4369 | } 4370 | else 4371 | { 4372 | if (lQuery) 4373 | { 4374 | response("basicinput"); 4375 | return 0; 4376 | } 4377 | if (!gWarningDisplayed && !tinyfd_forceConsole) 4378 | { 4379 | gWarningDisplayed = true; 4380 | printf("\n\n%s\n", gTitle.ptr); 4381 | printf("%s\n\n", tinyfd_needs.ptr); 4382 | } 4383 | if (some(aTitle)) 4384 | { 4385 | printf("\n%s\n", aTitle); 4386 | } 4387 | 4388 | tcgetattr(0, &infoOri); 4389 | tcgetattr(0, &info); 4390 | info.c_lflag &= ~ICANON; 4391 | info.c_cc[VMIN] = 1; 4392 | info.c_cc[VTIME] = 0; 4393 | tcsetattr(0, TCSANOW, &info); 4394 | if (eq("yesno", aDialogType)) 4395 | { 4396 | do 4397 | { 4398 | if (some(aMessage)) 4399 | { 4400 | printf("\n%s\n", aMessage); 4401 | } 4402 | printf("y/n: "); 4403 | fflush(stdout); 4404 | lChar = cast(char)tolower(getchar()); 4405 | printf("\n\n"); 4406 | } while (lChar != 'y' && lChar != 'n'); 4407 | lResult = lChar == 'y' ? 1 : 0; 4408 | } 4409 | else if (eq("okcancel", aDialogType)) 4410 | { 4411 | do 4412 | { 4413 | if (some(aMessage)) 4414 | { 4415 | printf("\n%s\n", aMessage); 4416 | } 4417 | printf("[O]kay/[C]ancel: "); 4418 | fflush(stdout); 4419 | lChar = cast(char)tolower(getchar()); 4420 | printf("\n\n"); 4421 | } while (lChar != 'o' && lChar != 'c'); 4422 | lResult = lChar == 'o' ? 1 : 0; 4423 | } 4424 | else if (eq("yesnocancel", aDialogType)) 4425 | { 4426 | do 4427 | { 4428 | if (some(aMessage)) 4429 | { 4430 | printf("\n%s\n", aMessage); 4431 | } 4432 | printf("[Y]es/[N]o/[C]ancel: "); 4433 | fflush(stdout); 4434 | lChar = cast(char)tolower(getchar()); 4435 | printf("\n\n"); 4436 | } while (lChar != 'y' && lChar != 'n' && lChar != 'c'); 4437 | lResult = (lChar == 'y') ? 1 : (lChar == 'n') ? 2 : 0; 4438 | } 4439 | else 4440 | { 4441 | if (some(aMessage)) 4442 | { 4443 | printf("\n%s\n\n", aMessage); 4444 | } 4445 | printf("press enter to continue "); 4446 | fflush(stdout); 4447 | getchar(); 4448 | printf("\n\n"); 4449 | lResult = 1; 4450 | } 4451 | tcsetattr(0, TCSANOW, &infoOri); 4452 | free(str); 4453 | return lResult; 4454 | } 4455 | 4456 | if (tinyfd_verbose) 4457 | printf("str: %s\n", str); 4458 | 4459 | lIn = popen(str, "r"); 4460 | if (!lIn) 4461 | { 4462 | free(str); 4463 | return 0; 4464 | } 4465 | while (fgets(lBuff.ptr, lBuff.sizeof, lIn) !is null) 4466 | { 4467 | } 4468 | 4469 | pclose(lIn); 4470 | 4471 | /* printf( "lBuff: %s len: %lu \n" , lBuff , strlen(lBuff.ptr) ) ; */ 4472 | removeLastNL(lBuff.ptr); 4473 | /* printf( "lBuff1: %s len: %lu \n" , lBuff , strlen(lBuff.ptr) ) ; */ 4474 | 4475 | if (eq("yesnocancel", aDialogType)) 4476 | { 4477 | if (lBuff[0] == '1') 4478 | { 4479 | if (eq(lBuff.ptr + 1, "Yes")) 4480 | strcpy(lBuff.ptr, "1"); 4481 | else if (eq(lBuff.ptr + 1, "No")) 4482 | strcpy(lBuff.ptr, "2"); 4483 | } 4484 | } 4485 | /* printf( "lBuff2: %s len: %lu \n" , lBuff , strlen(lBuff.ptr) ) ; */ 4486 | 4487 | lResult = eq(lBuff.ptr, "2") ? 2 : eq(lBuff.ptr, "1") ? 1 : 0; 4488 | 4489 | /* printf( "lResult: %d\n" , lResult ) ; */ 4490 | free(str); 4491 | return lResult; 4492 | } 4493 | 4494 | int _notifyPopup( 4495 | const char* aTitle, 4496 | const char* aMessage, 4497 | const char* aIconType) 4498 | { 4499 | char[MAX_PATH_OR_CMD] lBuff = '\0'; 4500 | char* str; 4501 | char* lpDialogString; 4502 | FILE* lIn; 4503 | size_t lTitleLen; 4504 | size_t lMessageLen; 4505 | bool lQuery; 4506 | 4507 | if (getenv("SSH_TTY")) 4508 | { 4509 | return _messageBox(aTitle, aMessage, "ok", aIconType, 0); 4510 | } 4511 | 4512 | lQuery = eq(aTitle, "tinyfd_query"); 4513 | lTitleLen = aTitle ? strlen(aTitle) : 0; 4514 | lMessageLen = aMessage ? strlen(aMessage) : 0; 4515 | if (!aTitle || !lQuery) 4516 | { 4517 | str = cast(char*)malloc(MAX_PATH_OR_CMD + lTitleLen + lMessageLen); 4518 | } 4519 | 4520 | if (osascriptPresent()) 4521 | { 4522 | if (lQuery) 4523 | { 4524 | response("applescript"); 4525 | return 1; 4526 | } 4527 | 4528 | strcpy(str, "osascript "); 4529 | if (!osx9orBetter()) 4530 | strcat(str, " -e 'tell application \"System Events\"' -e 'Activate'"); 4531 | strcat(str, " -e 'try' -e 'display notification \""); 4532 | if (some(aMessage)) 4533 | { 4534 | strcat(str, aMessage); 4535 | } 4536 | strcat(str, " \" "); 4537 | if (some(aTitle)) 4538 | { 4539 | strcat(str, "with title \""); 4540 | strcat(str, aTitle); 4541 | strcat(str, "\" "); 4542 | } 4543 | 4544 | strcat(str, "' -e 'end try'"); 4545 | if (!osx9orBetter()) 4546 | strcat(str, " -e 'end tell'"); 4547 | } 4548 | else if (kdialogPresent()) 4549 | { 4550 | if (lQuery) 4551 | { 4552 | response("kdialog"); 4553 | return 1; 4554 | } 4555 | strcpy(str, "kdialog"); 4556 | 4557 | if (some(aIconType)) 4558 | { 4559 | strcat(str, " --icon '"); 4560 | strcat(str, aIconType); 4561 | strcat(str, "'"); 4562 | } 4563 | if (some(aTitle)) 4564 | { 4565 | strcat(str, " --title \""); 4566 | strcat(str, aTitle); 4567 | strcat(str, "\""); 4568 | } 4569 | 4570 | strcat(str, " --passivepopup"); 4571 | strcat(str, " \""); 4572 | if (aMessage) 4573 | { 4574 | strcat(str, aMessage); 4575 | } 4576 | strcat(str, " \" 5"); 4577 | } 4578 | else if ((zenity3Present() >= 5) || matedialogPresent() || shellementaryPresent() || qarmaPresent()) 4579 | { 4580 | /* zenity 2.32 & 3.14 has the notification but with a bug: it doesnt return from it */ 4581 | /* zenity 3.8 show the notification as an alert ok cancel box */ 4582 | if (zenity3Present() >= 5) 4583 | { 4584 | if (lQuery) 4585 | { 4586 | response("zenity"); 4587 | return 1; 4588 | } 4589 | strcpy(str, "zenity"); 4590 | } 4591 | else if (matedialogPresent()) 4592 | { 4593 | if (lQuery) 4594 | { 4595 | response("matedialog"); 4596 | return 1; 4597 | } 4598 | strcpy(str, "matedialog"); 4599 | } 4600 | else if (shellementaryPresent()) 4601 | { 4602 | if (lQuery) 4603 | { 4604 | response("shellementary"); 4605 | return 1; 4606 | } 4607 | strcpy(str, "shellementary"); 4608 | } 4609 | else 4610 | { 4611 | if (lQuery) 4612 | { 4613 | response("qarma"); 4614 | return 1; 4615 | } 4616 | strcpy(str, "qarma"); 4617 | } 4618 | 4619 | strcat(str, " --notification"); 4620 | 4621 | if (some(aIconType)) 4622 | { 4623 | strcat(str, " --window-icon '"); 4624 | strcat(str, aIconType); 4625 | strcat(str, "'"); 4626 | } 4627 | 4628 | strcat(str, " --text \""); 4629 | if (some(aTitle)) 4630 | { 4631 | strcat(str, aTitle); 4632 | strcat(str, "\n"); 4633 | } 4634 | if (some(aMessage)) 4635 | { 4636 | strcat(str, aMessage); 4637 | } 4638 | strcat(str, " \""); 4639 | } 4640 | else if (perlPresent() >= 2) 4641 | { 4642 | if (lQuery) 4643 | { 4644 | response("perl-dbus"); 4645 | return 1; 4646 | } 4647 | sprintf(str, `perl -e "use Net::DBus; 4648 | my \$sessionBus = Net::DBus->session; 4649 | my \$notificationsService = \$sessionBus->get_service('org.freedesktop.Notifications'); 4650 | my \$notificationsObject = \$notificationsService->get_object('/org/freedesktop/Notifications', 4651 | 'org.freedesktop.Notifications'); 4652 | my \$notificationId;\$notificationId = \$notificationsObject->Notify(shift, 0, '%s', '%s', '%s', [], {}, -1);"`, 4653 | aIconType ? aIconType : "", aTitle ? aTitle : "", aMessage ? aMessage : ""); 4654 | } 4655 | else if (pythonDbusPresent()) 4656 | { 4657 | if (lQuery) 4658 | { 4659 | response("python-dbus"); 4660 | return 1; 4661 | } 4662 | strcpy(str, gPythonName.ptr); 4663 | strcat(str, " -c \"import dbus;bus=dbus.SessionBus();"); 4664 | strcat(str, "notif=bus.get_object('org.freedesktop.Notifications','/org/freedesktop/Notifications');"); 4665 | strcat(str, "notify=dbus.Interface(notif,'org.freedesktop.Notifications');"); 4666 | strcat(str, "notify.Notify('',0,'"); 4667 | if (some(aIconType)) 4668 | { 4669 | strcat(str, aIconType); 4670 | } 4671 | strcat(str, "','"); 4672 | if (some(aTitle)) 4673 | { 4674 | strcat(str, aTitle); 4675 | } 4676 | strcat(str, "','"); 4677 | if (some(aMessage)) 4678 | { 4679 | lpDialogString = str + strlen(str); 4680 | replaceSubStr(aMessage, "\n", "\\n", lpDialogString); 4681 | } 4682 | strcat(str, "','','',5000)\""); 4683 | } 4684 | else if (notifysendPresent()) 4685 | { 4686 | if (lQuery) 4687 | { 4688 | response("notifysend"); 4689 | return 1; 4690 | } 4691 | strcpy(str, "notify-send"); 4692 | if (some(aIconType)) 4693 | { 4694 | strcat(str, " -i '"); 4695 | strcat(str, aIconType); 4696 | strcat(str, "'"); 4697 | } 4698 | strcat(str, " \""); 4699 | if (some(aTitle)) 4700 | { 4701 | strcat(str, aTitle); 4702 | strcat(str, " | "); 4703 | } 4704 | if (some(aMessage)) 4705 | { 4706 | replaceSubStr(aMessage, "\n\t", " | ", lBuff.ptr); 4707 | replaceSubStr(aMessage, "\n", " | ", lBuff.ptr); 4708 | replaceSubStr(aMessage, "\t", " ", lBuff.ptr); 4709 | strcat(str, lBuff.ptr); 4710 | } 4711 | strcat(str, "\""); 4712 | } 4713 | else 4714 | { 4715 | return _messageBox(aTitle, aMessage, "ok", aIconType, 0); 4716 | } 4717 | 4718 | if (tinyfd_verbose) 4719 | printf("str: %s\n", str); 4720 | 4721 | lIn = popen(str, "r"); 4722 | if (!lIn) 4723 | { 4724 | free(str); 4725 | return 0; 4726 | } 4727 | 4728 | pclose(lIn); 4729 | free(str); 4730 | return 1; 4731 | } 4732 | 4733 | const(char*) _inputBox( 4734 | const char* aTitle, 4735 | const char* aMessage, 4736 | const char* aDefaultInput) 4737 | { 4738 | static char[MAX_PATH_OR_CMD] lBuff = '\0'; 4739 | const bool lQuery = eq(aTitle, "tinyfd_query"); 4740 | char* str; 4741 | char* lpDialogString; 4742 | FILE* lIn; 4743 | int lResult; 4744 | bool lWasGdialog; 4745 | bool lWasGraphicDialog; 4746 | bool lWasXterm; 4747 | bool lWasBasicXterm; 4748 | termios oldt; 4749 | termios newt; 4750 | char* lEOF; 4751 | size_t lTitleLen; 4752 | size_t lMessageLen; 4753 | 4754 | lBuff[0] = '\0'; 4755 | 4756 | lTitleLen = aTitle ? strlen(aTitle) : 0; 4757 | lMessageLen = aMessage ? strlen(aMessage) : 0; 4758 | if (!aTitle || !lQuery) 4759 | { 4760 | str = cast(char*)malloc(MAX_PATH_OR_CMD + lTitleLen + lMessageLen); 4761 | } 4762 | 4763 | if (osascriptPresent()) 4764 | { 4765 | if (lQuery) 4766 | { 4767 | response("applescript"); 4768 | return cast(const(char)*)1; 4769 | } 4770 | strcpy(str, "osascript "); 4771 | if (!osx9orBetter()) 4772 | strcat(str, " -e 'tell application \"System Events\"' -e 'Activate'"); 4773 | strcat(str, " -e 'try' -e 'display dialog \""); 4774 | if (some(aMessage)) 4775 | { 4776 | strcat(str, aMessage); 4777 | } 4778 | strcat(str, "\" "); 4779 | strcat(str, "default answer \""); 4780 | if (some(aDefaultInput)) 4781 | { 4782 | strcat(str, aDefaultInput); 4783 | } 4784 | strcat(str, "\" "); 4785 | if (!aDefaultInput) 4786 | { 4787 | strcat(str, "hidden answer true "); 4788 | } 4789 | if (some(aTitle)) 4790 | { 4791 | strcat(str, "with title \""); 4792 | strcat(str, aTitle); 4793 | strcat(str, "\" "); 4794 | } 4795 | strcat(str, "with icon note' "); 4796 | strcat(str, "-e '\"1\" & text returned of result' "); 4797 | strcat(str, "-e 'on error number -128' "); 4798 | strcat(str, "-e '0' "); 4799 | strcat(str, "-e 'end try'"); 4800 | if (!osx9orBetter()) 4801 | strcat(str, " -e 'end tell'"); 4802 | } 4803 | else if (kdialogPresent()) 4804 | { 4805 | if (lQuery) 4806 | { 4807 | response("kdialog"); 4808 | return cast(const(char)*)1; 4809 | } 4810 | strcpy(str, "szAnswer=$(kdialog"); 4811 | 4812 | if (kdialogPresent() == 2) 4813 | { 4814 | strcat(str, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ 4815 | } 4816 | 4817 | if (!aDefaultInput) 4818 | { 4819 | strcat(str, " --password "); 4820 | } 4821 | else 4822 | { 4823 | strcat(str, " --inputbox "); 4824 | } 4825 | strcat(str, "\""); 4826 | if (some(aMessage)) 4827 | { 4828 | strcat(str, aMessage); 4829 | } 4830 | strcat(str, "\" \""); 4831 | if (some(aDefaultInput)) 4832 | { 4833 | strcat(str, aDefaultInput); 4834 | } 4835 | strcat(str, "\""); 4836 | if (some(aTitle)) 4837 | { 4838 | strcat(str, " --title \""); 4839 | strcat(str, aTitle); 4840 | strcat(str, "\""); 4841 | } 4842 | strcat(str, ");if [ $? = 0 ];then echo 1$szAnswer;else echo 0$szAnswer;fi"); 4843 | } 4844 | else if (zenityPresent() || matedialogPresent() || shellementaryPresent() || qarmaPresent()) 4845 | { 4846 | if (zenityPresent()) 4847 | { 4848 | if (lQuery) 4849 | { 4850 | response("zenity"); 4851 | return cast(const(char)*)1; 4852 | } 4853 | strcpy(str, "szAnswer=$(zenity"); 4854 | if (zenity3Present() >= 4 && !getenv("SSH_TTY")) 4855 | { 4856 | strcat(str, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ 4857 | } 4858 | } 4859 | else if (matedialogPresent()) 4860 | { 4861 | if (lQuery) 4862 | { 4863 | response("matedialog"); 4864 | return cast(const(char)*)1; 4865 | } 4866 | strcpy(str, "szAnswer=$(matedialog"); 4867 | } 4868 | else if (shellementaryPresent()) 4869 | { 4870 | if (lQuery) 4871 | { 4872 | response("shellementary"); 4873 | return cast(const(char)*)1; 4874 | } 4875 | strcpy(str, "szAnswer=$(shellementary"); 4876 | } 4877 | else 4878 | { 4879 | if (lQuery) 4880 | { 4881 | response("qarma"); 4882 | return cast(const(char)*)1; 4883 | } 4884 | strcpy(str, "szAnswer=$(qarma"); 4885 | if (!getenv("SSH_TTY")) 4886 | { 4887 | strcat(str, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ 4888 | } 4889 | } 4890 | strcat(str, " --entry"); 4891 | 4892 | if (some(aTitle)) 4893 | { 4894 | strcat(str, " --title=\""); 4895 | strcat(str, aTitle); 4896 | strcat(str, "\""); 4897 | } 4898 | if (some(aMessage)) 4899 | { 4900 | strcat(str, " --text=\""); 4901 | strcat(str, aMessage); 4902 | strcat(str, "\""); 4903 | } 4904 | if (some(aDefaultInput)) 4905 | { 4906 | strcat(str, " --entry-text=\""); 4907 | strcat(str, aDefaultInput); 4908 | strcat(str, "\""); 4909 | } 4910 | else 4911 | { 4912 | strcat(str, " --hide-text"); 4913 | } 4914 | if (tinyfd_silent) 4915 | strcat(str, " 2>/dev/null "); 4916 | strcat(str, ");if [ $? = 0 ];then echo 1$szAnswer;else echo 0$szAnswer;fi"); 4917 | } 4918 | else if (gxmessagePresent() || gmessagePresent()) 4919 | { 4920 | if (gxmessagePresent()) 4921 | { 4922 | if (lQuery) 4923 | { 4924 | response("gxmessage"); 4925 | return cast(const(char)*)1; 4926 | } 4927 | strcpy(str, "szAnswer=$(gxmessage -buttons Ok:1,Cancel:0 -center \""); 4928 | } 4929 | else 4930 | { 4931 | if (lQuery) 4932 | { 4933 | response("gmessage"); 4934 | return cast(const(char)*)1; 4935 | } 4936 | strcpy(str, "szAnswer=$(gmessage -buttons Ok:1,Cancel:0 -center \""); 4937 | } 4938 | 4939 | if (some(aMessage)) 4940 | { 4941 | strcat(str, aMessage); 4942 | } 4943 | strcat(str, "\""); 4944 | if (some(aTitle)) 4945 | { 4946 | strcat(str, " -title \""); 4947 | strcat(str, aTitle); 4948 | strcat(str, "\" "); 4949 | } 4950 | strcat(str, " -entrytext \""); 4951 | if (some(aDefaultInput)) 4952 | { 4953 | strcat(str, aDefaultInput); 4954 | } 4955 | strcat(str, "\""); 4956 | strcat(str, ");echo $?$szAnswer"); 4957 | } 4958 | else if (!gdialogPresent() && !xdialogPresent() && tkinter2Present()) 4959 | { 4960 | if (lQuery) 4961 | { 4962 | response("python2-tkinter"); 4963 | return cast(const(char)*)1; 4964 | } 4965 | strcpy(str, gPython2Name.ptr); 4966 | if (!isTerminalRunning() && isDarwin()) 4967 | { 4968 | strcat(str, " -i"); /* for osx without console */ 4969 | } 4970 | 4971 | strcat(str, 4972 | " -S -c \"import Tkinter,tkSimpleDialog;root=Tkinter.Tk();root.withdraw();"); 4973 | 4974 | if (isDarwin()) 4975 | { 4976 | strcat(str, 4977 | "import os;os.system('''/usr/bin/osascript -e 'tell app \\\"Finder\\\" to set " ~ 4978 | "frontmost of process \\\"Python\\\" to true' ''');"); 4979 | } 4980 | 4981 | strcat(str, "res=tkSimpleDialog.askstring("); 4982 | if (some(aTitle)) 4983 | { 4984 | strcat(str, "title='"); 4985 | strcat(str, aTitle); 4986 | strcat(str, "',"); 4987 | } 4988 | if (some(aMessage)) 4989 | { 4990 | strcat(str, "prompt='"); 4991 | lpDialogString = str + strlen(str); 4992 | replaceSubStr(aMessage, "\n", "\\n", lpDialogString); 4993 | strcat(str, "',"); 4994 | } 4995 | if (aDefaultInput) 4996 | { 4997 | if (some(aDefaultInput)) 4998 | { 4999 | strcat(str, "initialvalue='"); 5000 | strcat(str, aDefaultInput); 5001 | strcat(str, "',"); 5002 | } 5003 | } 5004 | else 5005 | { 5006 | strcat(str, "show='*'"); 5007 | } 5008 | strcat(str, ");\nif res is None :\n\tprint 0"); 5009 | strcat(str, "\nelse :\n\tprint '1'+res\n\""); 5010 | } 5011 | else if (!gdialogPresent() && !xdialogPresent() && tkinter3Present()) 5012 | { 5013 | if (lQuery) 5014 | { 5015 | response("python3-tkinter"); 5016 | return cast(const(char)*)1; 5017 | } 5018 | strcpy(str, gPython3Name.ptr); 5019 | strcat(str, 5020 | " -S -c \"import tkinter; from tkinter import simpledialog;root=tkinter.Tk();root.withdraw();"); 5021 | strcat(str, "res=simpledialog.askstring("); 5022 | if (some(aTitle)) 5023 | { 5024 | strcat(str, "title='"); 5025 | strcat(str, aTitle); 5026 | strcat(str, "',"); 5027 | } 5028 | if (some(aMessage)) 5029 | { 5030 | strcat(str, "prompt='"); 5031 | lpDialogString = str + strlen(str); 5032 | replaceSubStr(aMessage, "\n", "\\n", lpDialogString); 5033 | strcat(str, "',"); 5034 | } 5035 | if (aDefaultInput) 5036 | { 5037 | if (some(aDefaultInput)) 5038 | { 5039 | strcat(str, "initialvalue='"); 5040 | strcat(str, aDefaultInput); 5041 | strcat(str, "',"); 5042 | } 5043 | } 5044 | else 5045 | { 5046 | strcat(str, "show='*'"); 5047 | } 5048 | strcat(str, ");\nif res is None :\n\tprint(0)"); 5049 | strcat(str, "\nelse :\n\tprint('1'+res)\n\""); 5050 | } 5051 | else if (gdialogPresent() || xdialogPresent() || dialogName() || whiptailPresent()) 5052 | { 5053 | if (gdialogPresent()) 5054 | { 5055 | if (lQuery) 5056 | { 5057 | response("gdialog"); 5058 | return cast(const(char)*)1; 5059 | } 5060 | lWasGraphicDialog = true; 5061 | lWasGdialog = true; 5062 | strcpy(str, "(gdialog "); 5063 | } 5064 | else if (xdialogPresent()) 5065 | { 5066 | if (lQuery) 5067 | { 5068 | response("xdialog"); 5069 | return cast(const(char)*)1; 5070 | } 5071 | lWasGraphicDialog = true; 5072 | strcpy(str, "(Xdialog "); 5073 | } 5074 | else if (dialogName()) 5075 | { 5076 | if (lQuery) 5077 | { 5078 | response("dialog"); 5079 | return cast(const(char)*)0; 5080 | } 5081 | if (isTerminalRunning()) 5082 | { 5083 | strcpy(str, "(dialog "); 5084 | } 5085 | else 5086 | { 5087 | lWasXterm = true; 5088 | strcpy(str, terminalName()); 5089 | strcat(str, "'("); 5090 | strcat(str, dialogName()); 5091 | strcat(str, " "); 5092 | } 5093 | } 5094 | else if (isTerminalRunning()) 5095 | { 5096 | if (lQuery) 5097 | { 5098 | response("whiptail"); 5099 | return cast(const(char)*)0; 5100 | } 5101 | strcpy(str, "(whiptail "); 5102 | } 5103 | else 5104 | { 5105 | if (lQuery) 5106 | { 5107 | response("whiptail"); 5108 | return cast(const(char)*)0; 5109 | } 5110 | lWasXterm = true; 5111 | strcpy(str, terminalName()); 5112 | strcat(str, "'(whiptail "); 5113 | } 5114 | 5115 | if (some(aTitle)) 5116 | { 5117 | strcat(str, "--title \""); 5118 | strcat(str, aTitle); 5119 | strcat(str, "\" "); 5120 | } 5121 | 5122 | if (!xdialogPresent() && !gdialogPresent()) 5123 | { 5124 | strcat(str, "--backtitle \""); 5125 | strcat(str, "tab: move focus"); 5126 | if (!aDefaultInput && !lWasGdialog) 5127 | { 5128 | strcat(str, " (sometimes nothing, no blink nor star, is shown in text field)"); 5129 | } 5130 | strcat(str, "\" "); 5131 | } 5132 | 5133 | if (aDefaultInput || lWasGdialog) 5134 | { 5135 | strcat(str, "--inputbox"); 5136 | } 5137 | else 5138 | { 5139 | if (!lWasGraphicDialog && dialogName() && isDialogVersionBetter09b()) 5140 | { 5141 | strcat(str, "--insecure "); 5142 | } 5143 | strcat(str, "--passwordbox"); 5144 | } 5145 | strcat(str, " \""); 5146 | if (some(aMessage)) 5147 | { 5148 | strcat(str, aMessage); 5149 | } 5150 | strcat(str, "\" 10 60 "); 5151 | if (some(aDefaultInput)) 5152 | { 5153 | strcat(str, "\""); 5154 | strcat(str, aDefaultInput); 5155 | strcat(str, "\" "); 5156 | } 5157 | if (lWasGraphicDialog) 5158 | { 5159 | strcat(str, ") 2>/tmp/tinyfd.txt;" ~ 5160 | "if [ $? = 0 ];then tinyfdBool=1;else tinyfdBool=0;fi;" ~ 5161 | "tinyfdRes=$(cat /tmp/tinyfd.txt);echo $tinyfdBool$tinyfdRes"); 5162 | } 5163 | else 5164 | { 5165 | strcat(str, ">/dev/tty ) 2>/tmp/tinyfd.txt;" ~ 5166 | "if [ $? = 0 ];then tinyfdBool=1;else tinyfdBool=0;fi;" ~ 5167 | "tinyfdRes=$(cat /tmp/tinyfd.txt);echo $tinyfdBool$tinyfdRes"); 5168 | 5169 | if (lWasXterm) 5170 | { 5171 | strcat(str, " >/tmp/tinyfd0.txt';cat /tmp/tinyfd0.txt"); 5172 | } 5173 | else 5174 | { 5175 | strcat(str, "; clear >/dev/tty"); 5176 | } 5177 | } 5178 | } 5179 | else if (!isTerminalRunning() && terminalName()) 5180 | { 5181 | if (lQuery) 5182 | { 5183 | response("basicinput"); 5184 | return cast(const(char)*)0; 5185 | } 5186 | lWasBasicXterm = true; 5187 | strcpy(str, terminalName()); 5188 | strcat(str, "'"); 5189 | if (!gWarningDisplayed && !tinyfd_forceConsole) 5190 | { 5191 | gWarningDisplayed = true; 5192 | _messageBox(gTitle.ptr, tinyfd_needs.ptr, "ok", "warning", 0); 5193 | } 5194 | if (some(aTitle) && !tinyfd_forceConsole) 5195 | { 5196 | strcat(str, "echo \""); 5197 | strcat(str, aTitle); 5198 | strcat(str, "\";echo;"); 5199 | } 5200 | 5201 | strcat(str, "echo \""); 5202 | if (some(aMessage)) 5203 | { 5204 | strcat(str, aMessage); 5205 | } 5206 | strcat(str, "\";read "); 5207 | if (!aDefaultInput) 5208 | { 5209 | strcat(str, "-s "); 5210 | } 5211 | strcat(str, "-p \""); 5212 | strcat(str, "(esc+enter to cancel): \" ANSWER "); 5213 | strcat(str, ";echo 1$ANSWER >/tmp/tinyfd.txt';"); 5214 | strcat(str, "cat -v /tmp/tinyfd.txt"); 5215 | } 5216 | else if (!gWarningDisplayed && !isTerminalRunning() && !terminalName()) 5217 | { 5218 | gWarningDisplayed = true; 5219 | _messageBox(gTitle.ptr, tinyfd_needs.ptr, "ok", "warning", 0); 5220 | if (lQuery) 5221 | { 5222 | response("no_solution"); 5223 | return cast(const(char)*)0; 5224 | } 5225 | return null; 5226 | } 5227 | else 5228 | { 5229 | if (lQuery) 5230 | { 5231 | response("basicinput"); 5232 | return cast(const(char)*)0; 5233 | } 5234 | if (!gWarningDisplayed && !tinyfd_forceConsole) 5235 | { 5236 | gWarningDisplayed = true; 5237 | _messageBox(gTitle.ptr, tinyfd_needs.ptr, "ok", "warning", 0); 5238 | } 5239 | if (some(aTitle)) 5240 | { 5241 | printf("\n%s\n", aTitle); 5242 | } 5243 | if (some(aMessage)) 5244 | { 5245 | printf("\n%s\n", aMessage); 5246 | } 5247 | printf("(esc+enter to cancel): "); 5248 | fflush(stdout); 5249 | if (!aDefaultInput) 5250 | { 5251 | tcgetattr(STDIN_FILENO, &oldt); 5252 | newt = oldt; 5253 | newt.c_lflag &= ~ECHO; 5254 | tcsetattr(STDIN_FILENO, TCSANOW, &newt); 5255 | } 5256 | 5257 | lEOF = fgets(lBuff.ptr, MAX_PATH_OR_CMD, stdin); 5258 | /* printf("lbuff<%c><%d>\n",lBuff[0],lBuff[0]); */ 5259 | if (!lEOF || (lBuff[0] == '\0')) 5260 | { 5261 | free(str); 5262 | return null; 5263 | } 5264 | 5265 | if (lBuff[0] == '\n') 5266 | { 5267 | lEOF = fgets(lBuff.ptr, MAX_PATH_OR_CMD, stdin); 5268 | /* printf("lbuff<%c><%d>\n",lBuff[0],lBuff[0]); */ 5269 | if (!lEOF || (lBuff[0] == '\0')) 5270 | { 5271 | free(str); 5272 | return null; 5273 | } 5274 | } 5275 | 5276 | if (!aDefaultInput) 5277 | { 5278 | tcsetattr(STDIN_FILENO, TCSANOW, &oldt); 5279 | printf("\n"); 5280 | } 5281 | printf("\n"); 5282 | if (strchr(lBuff.ptr, 27)) 5283 | { 5284 | free(str); 5285 | return null; 5286 | } 5287 | removeLastNL(lBuff.ptr); 5288 | free(str); 5289 | return lBuff.ptr; 5290 | } 5291 | 5292 | if (tinyfd_verbose) 5293 | printf("str: %s\n", str); 5294 | lIn = popen(str, "r"); 5295 | if (!lIn) 5296 | { 5297 | if (fileExists("/tmp/tinyfd.txt")) 5298 | { 5299 | wipefile("/tmp/tinyfd.txt"); 5300 | remove("/tmp/tinyfd.txt"); 5301 | } 5302 | if (fileExists("/tmp/tinyfd0.txt")) 5303 | { 5304 | wipefile("/tmp/tinyfd0.txt"); 5305 | remove("/tmp/tinyfd0.txt"); 5306 | } 5307 | free(str); 5308 | return null; 5309 | } 5310 | while (fgets(lBuff.ptr, lBuff.sizeof, lIn) !is null) 5311 | { 5312 | } 5313 | 5314 | pclose(lIn); 5315 | 5316 | if (fileExists("/tmp/tinyfd.txt")) 5317 | { 5318 | wipefile("/tmp/tinyfd.txt"); 5319 | remove("/tmp/tinyfd.txt"); 5320 | } 5321 | if (fileExists("/tmp/tinyfd0.txt")) 5322 | { 5323 | wipefile("/tmp/tinyfd0.txt"); 5324 | remove("/tmp/tinyfd0.txt"); 5325 | } 5326 | 5327 | /* printf( "len Buff: %lu\n" , strlen(lBuff.ptr) ) ; */ 5328 | /* printf( "lBuff0: %s\n" , lBuff ) ; */ 5329 | removeLastNL(lBuff.ptr); 5330 | /* printf( "lBuff1: %s len: %lu \n" , lBuff , strlen(lBuff.ptr) ) ; */ 5331 | if (lWasBasicXterm) 5332 | { 5333 | if (strstr(lBuff.ptr, "^[")) /* esc was pressed */ 5334 | { 5335 | free(str); 5336 | return null; 5337 | } 5338 | } 5339 | 5340 | lResult = strncmp(lBuff.ptr, "1", 1) ? 0 : 1; 5341 | /* printf( "lResult: %d \n" , lResult ) ; */ 5342 | if (!lResult) 5343 | { 5344 | free(str); 5345 | return null; 5346 | } 5347 | /* printf( "lBuff+1: %s\n" , lBuff+1 ) ; */ 5348 | free(str); 5349 | 5350 | return lBuff.ptr + 1; 5351 | } 5352 | 5353 | void osascriptAppendFilters(const TFD_Filter[] filters, char* output) 5354 | { 5355 | assert(output); 5356 | 5357 | if (!filters.length) 5358 | return; 5359 | 5360 | bool isMime; 5361 | const(char)*[MAX_PATTERNS] patterns; 5362 | 5363 | foreach (filter; filters) 5364 | { 5365 | const plen = getValidPatterns(filter, isMime, patterns); 5366 | if (plen == 0 || isMime) 5367 | continue; 5368 | 5369 | strcat(output, "of type {\""); 5370 | foreach (i, pt; patterns[0 .. plen]) 5371 | { 5372 | if (strstr(pt, "*.")) 5373 | pt += 2; 5374 | if (*pt == '\0') 5375 | continue; 5376 | 5377 | if (i != 0) 5378 | strcat(output, "\",\""); 5379 | strcat(output, pt); 5380 | } 5381 | strcat(output, "\"} "); 5382 | break; // TODO: can multiple? 5383 | } 5384 | } 5385 | 5386 | void kdialogAppendFilters(const TFD_Filter[] filters, char* output) 5387 | { 5388 | assert(output); 5389 | 5390 | if (!filters.length) 5391 | return; 5392 | 5393 | bool isMime; 5394 | const(char)*[MAX_PATTERNS] patterns; 5395 | char[MAX_PATH_OR_CMD] patternsStr = void; 5396 | int j; 5397 | 5398 | // "Description(p1 p2) | p1(p1) | mime1 mime2" 5399 | strcat(output, " \""); 5400 | foreach (filter; filters) 5401 | { 5402 | const plen = getValidPatterns(filter, isMime, patterns); 5403 | if (plen == 0) 5404 | continue; 5405 | 5406 | patternsStr[0] = '\0'; 5407 | foreach (i, pt; patterns[0 .. plen]) 5408 | { 5409 | if (i != 0) 5410 | strcat(patternsStr.ptr, " "); 5411 | strcat(patternsStr.ptr, pt); 5412 | } 5413 | 5414 | if (j != 0) 5415 | strcat(output, " | "); 5416 | j++; 5417 | 5418 | if (!isMime) 5419 | { 5420 | strcat(output, some(filter.description) ? filter.description : patternsStr.ptr); 5421 | strcat(output, "("); 5422 | } 5423 | strcat(output, patternsStr.ptr); 5424 | if (!isMime) 5425 | { 5426 | strcat(output, ")"); 5427 | } 5428 | } 5429 | strcat(output, "\""); 5430 | // no need to add "All files" 5431 | } 5432 | 5433 | void zenityAppendFilters(const TFD_Filter[] filters, char* output) 5434 | { 5435 | assert(output); 5436 | 5437 | if (!filters.length) 5438 | return; 5439 | 5440 | bool isMime; 5441 | const(char)*[MAX_PATTERNS] patterns; 5442 | 5443 | foreach (filter; filters) 5444 | { 5445 | const plen = getValidPatterns(filter, isMime, patterns); 5446 | if (plen == 0 || isMime) 5447 | continue; 5448 | 5449 | strcat(output, " --file-filter='"); 5450 | if (some(filter.description)) 5451 | { 5452 | strcat(output, filter.description); 5453 | strcat(output, " | "); 5454 | } 5455 | foreach (pt; patterns[0 .. plen]) 5456 | { 5457 | strcat(output, pt); 5458 | strcat(output, " "); 5459 | } 5460 | strcat(output, "'"); 5461 | } 5462 | strcat(output, " --file-filter='All files | *'"); 5463 | } 5464 | 5465 | void tkinterAppendFilters(const TFD_Filter[] filters, char* output) 5466 | { 5467 | assert(output); 5468 | 5469 | if (!filters.length) 5470 | return; 5471 | 5472 | bool isMime; 5473 | const(char)*[MAX_PATTERNS] patterns; 5474 | char placeholder = 'a'; 5475 | 5476 | // filetypes=[('Description', ['p1','p2',]), ('a', 'p3'), ('All files', '*')] 5477 | strcat(output, "filetypes=["); 5478 | foreach (filter; filters) 5479 | { 5480 | const plen = getValidPatterns(filter, isMime, patterns); 5481 | if (plen == 0 || isMime) 5482 | continue; 5483 | if (plen == 1 && lastch(patterns[0]) == '*') 5484 | continue; // poor osx behaviour 5485 | 5486 | strcat(output, "('"); 5487 | if (some(filter.description)) 5488 | { 5489 | strcat(output, filter.description); 5490 | } 5491 | else 5492 | { 5493 | const len = strlen(output); 5494 | output[len] = placeholder; 5495 | output[len + 1] = '\0'; 5496 | placeholder++; 5497 | } 5498 | strcat(output, "', "); 5499 | if (plen > 1) 5500 | { 5501 | strcat(output, "["); 5502 | foreach (pt; patterns[0 .. plen]) 5503 | { 5504 | strcat(output, "'"); 5505 | strcat(output, pt); 5506 | strcat(output, "',"); 5507 | } 5508 | strcat(output, "]"); 5509 | } 5510 | else 5511 | { 5512 | strcat(output, "'"); 5513 | strcat(output, patterns[0]); 5514 | strcat(output, "'"); 5515 | } 5516 | strcat(output, "), "); 5517 | } 5518 | strcat(output, "('All files', '*')]"); 5519 | } 5520 | 5521 | const(char*) _saveFileDialog( 5522 | const char* aTitle, 5523 | const char* aDefaultPathAndFile, 5524 | const TFD_Filter[] aFilters) 5525 | { 5526 | static char[MAX_PATH_OR_CMD] lBuff = '\0'; 5527 | const bool lQuery = eq(aTitle, "tinyfd_query"); 5528 | char[MAX_PATH_OR_CMD] str_buf1 = '\0'; 5529 | char[MAX_PATH_OR_CMD] str_buf2 = '\0'; 5530 | char* str = str_buf1.ptr; 5531 | char* lString = str_buf2.ptr; 5532 | bool lWasGraphicDialog; 5533 | bool lWasXterm; 5534 | const(char)* p; 5535 | FILE* lIn; 5536 | lBuff[0] = '\0'; 5537 | 5538 | if (osascriptPresent()) 5539 | { 5540 | if (lQuery) 5541 | { 5542 | response("applescript"); 5543 | return cast(const(char)*)1; 5544 | } 5545 | strcpy(str, "osascript "); 5546 | if (!osx9orBetter()) 5547 | strcat(str, " -e 'tell application \"Finder\"' -e 'Activate'"); 5548 | strcat(str, " -e 'try' -e 'POSIX path of ( choose file name "); 5549 | if (some(aTitle)) 5550 | { 5551 | strcat(str, "with prompt \""); 5552 | strcat(str, aTitle); 5553 | strcat(str, "\" "); 5554 | } 5555 | getPathWithoutFinalSlash(lString, aDefaultPathAndFile); 5556 | if (some(lString)) 5557 | { 5558 | strcat(str, "default location \""); 5559 | strcat(str, lString); 5560 | strcat(str, "\" "); 5561 | } 5562 | getLastName(lString, aDefaultPathAndFile); 5563 | if (some(lString)) 5564 | { 5565 | strcat(str, "default name \""); 5566 | strcat(str, lString); 5567 | strcat(str, "\" "); 5568 | } 5569 | strcat(str, ")' "); 5570 | strcat(str, "-e 'on error number -128' "); 5571 | strcat(str, "-e 'end try'"); 5572 | if (!osx9orBetter()) 5573 | strcat(str, " -e 'end tell'"); 5574 | } 5575 | else if (kdialogPresent()) 5576 | { 5577 | if (lQuery) 5578 | { 5579 | response("kdialog"); 5580 | return cast(const(char)*)1; 5581 | } 5582 | 5583 | strcpy(str, "kdialog"); 5584 | if (kdialogPresent() == 2) 5585 | { 5586 | strcat(str, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ 5587 | } 5588 | strcat(str, " --getsavefilename "); 5589 | 5590 | if (some(aDefaultPathAndFile)) 5591 | { 5592 | if (aDefaultPathAndFile[0] != '/') 5593 | { 5594 | strcat(str, "$PWD/"); 5595 | } 5596 | strcat(str, "\""); 5597 | strcat(str, aDefaultPathAndFile); 5598 | strcat(str, "\""); 5599 | } 5600 | else 5601 | { 5602 | strcat(str, "$PWD/"); 5603 | } 5604 | 5605 | kdialogAppendFilters(aFilters, str); 5606 | 5607 | if (some(aTitle)) 5608 | { 5609 | strcat(str, " --title \""); 5610 | strcat(str, aTitle); 5611 | strcat(str, "\""); 5612 | } 5613 | } 5614 | else if (zenityPresent() || matedialogPresent() || shellementaryPresent() || qarmaPresent()) 5615 | { 5616 | if (zenityPresent()) 5617 | { 5618 | if (lQuery) 5619 | { 5620 | response("zenity"); 5621 | return cast(const(char)*)1; 5622 | } 5623 | strcpy(str, "zenity"); 5624 | if (zenity3Present() >= 4 && !getenv("SSH_TTY")) 5625 | { 5626 | strcat(str, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ 5627 | } 5628 | } 5629 | else if (matedialogPresent()) 5630 | { 5631 | if (lQuery) 5632 | { 5633 | response("matedialog"); 5634 | return cast(const(char)*)1; 5635 | } 5636 | strcpy(str, "matedialog"); 5637 | } 5638 | else if (shellementaryPresent()) 5639 | { 5640 | if (lQuery) 5641 | { 5642 | response("shellementary"); 5643 | return cast(const(char)*)1; 5644 | } 5645 | strcpy(str, "shellementary"); 5646 | } 5647 | else 5648 | { 5649 | if (lQuery) 5650 | { 5651 | response("qarma"); 5652 | return cast(const(char)*)1; 5653 | } 5654 | strcpy(str, "qarma"); 5655 | if (!getenv("SSH_TTY")) 5656 | { 5657 | strcat(str, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ 5658 | } 5659 | } 5660 | strcat(str, " --file-selection --save --confirm-overwrite"); 5661 | 5662 | if (some(aTitle)) 5663 | { 5664 | strcat(str, " --title=\""); 5665 | strcat(str, aTitle); 5666 | strcat(str, "\""); 5667 | } 5668 | if (some(aDefaultPathAndFile)) 5669 | { 5670 | strcat(str, " --filename=\""); 5671 | strcat(str, aDefaultPathAndFile); 5672 | strcat(str, "\""); 5673 | } 5674 | 5675 | zenityAppendFilters(aFilters, str); 5676 | 5677 | if (tinyfd_silent) 5678 | strcat(str, " 2>/dev/null "); 5679 | } 5680 | else if (!xdialogPresent() && tkinter2Present()) 5681 | { 5682 | if (lQuery) 5683 | { 5684 | response("python2-tkinter"); 5685 | return cast(const(char)*)1; 5686 | } 5687 | strcpy(str, gPython2Name.ptr); 5688 | if (!isTerminalRunning() && isDarwin()) 5689 | { 5690 | strcat(str, " -i"); /* for osx without console */ 5691 | } 5692 | strcat(str, 5693 | " -S -c \"import Tkinter,tkFileDialog;root=Tkinter.Tk();root.withdraw();"); 5694 | 5695 | if (isDarwin()) 5696 | { 5697 | strcat(str, 5698 | "import os;os.system('''/usr/bin/osascript -e 'tell app \\\"Finder\\\" to set " ~ 5699 | "frontmost of process \\\"Python\\\" to true' ''');"); 5700 | } 5701 | 5702 | strcat(str, "print tkFileDialog.asksaveasfilename("); 5703 | if (some(aTitle)) 5704 | { 5705 | strcat(str, "title='"); 5706 | strcat(str, aTitle); 5707 | strcat(str, "',"); 5708 | } 5709 | if (some(aDefaultPathAndFile)) 5710 | { 5711 | getPathWithoutFinalSlash(lString, aDefaultPathAndFile); 5712 | if (some(lString)) 5713 | { 5714 | strcat(str, "initialdir='"); 5715 | strcat(str, lString); 5716 | strcat(str, "',"); 5717 | } 5718 | getLastName(lString, aDefaultPathAndFile); 5719 | if (some(lString)) 5720 | { 5721 | strcat(str, "initialfile='"); 5722 | strcat(str, lString); 5723 | strcat(str, "',"); 5724 | } 5725 | } 5726 | 5727 | tkinterAppendFilters(aFilters, str); 5728 | 5729 | strcat(str, ")\""); 5730 | } 5731 | else if (!xdialogPresent() && tkinter3Present()) 5732 | { 5733 | if (lQuery) 5734 | { 5735 | response("python3-tkinter"); 5736 | return cast(const(char)*)1; 5737 | } 5738 | strcpy(str, gPython3Name.ptr); 5739 | strcat(str, 5740 | " -S -c \"import tkinter;from tkinter import filedialog;root=tkinter.Tk();root.withdraw();"); 5741 | strcat(str, "print( filedialog.asksaveasfilename("); 5742 | if (some(aTitle)) 5743 | { 5744 | strcat(str, "title='"); 5745 | strcat(str, aTitle); 5746 | strcat(str, "',"); 5747 | } 5748 | if (some(aDefaultPathAndFile)) 5749 | { 5750 | getPathWithoutFinalSlash(lString, aDefaultPathAndFile); 5751 | if (some(lString)) 5752 | { 5753 | strcat(str, "initialdir='"); 5754 | strcat(str, lString); 5755 | strcat(str, "',"); 5756 | } 5757 | getLastName(lString, aDefaultPathAndFile); 5758 | if (some(lString)) 5759 | { 5760 | strcat(str, "initialfile='"); 5761 | strcat(str, lString); 5762 | strcat(str, "',"); 5763 | } 5764 | } 5765 | 5766 | tkinterAppendFilters(aFilters, str); 5767 | 5768 | strcat(str, "))\""); 5769 | } 5770 | else if (xdialogPresent() || dialogName()) 5771 | { 5772 | if (xdialogPresent()) 5773 | { 5774 | if (lQuery) 5775 | { 5776 | response("xdialog"); 5777 | return cast(const(char)*)1; 5778 | } 5779 | lWasGraphicDialog = true; 5780 | strcpy(str, "(Xdialog "); 5781 | } 5782 | else if (isTerminalRunning()) 5783 | { 5784 | if (lQuery) 5785 | { 5786 | response("dialog"); 5787 | return cast(const(char)*)0; 5788 | } 5789 | strcpy(str, "(dialog "); 5790 | } 5791 | else 5792 | { 5793 | if (lQuery) 5794 | { 5795 | response("dialog"); 5796 | return cast(const(char)*)0; 5797 | } 5798 | lWasXterm = true; 5799 | strcpy(str, terminalName()); 5800 | strcat(str, "'("); 5801 | strcat(str, dialogName()); 5802 | strcat(str, " "); 5803 | } 5804 | 5805 | if (some(aTitle)) 5806 | { 5807 | strcat(str, "--title \""); 5808 | strcat(str, aTitle); 5809 | strcat(str, "\" "); 5810 | } 5811 | 5812 | if (!xdialogPresent() && !gdialogPresent()) 5813 | { 5814 | strcat(str, "--backtitle \""); 5815 | strcat(str, 5816 | "tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY"); 5817 | strcat(str, "\" "); 5818 | } 5819 | 5820 | strcat(str, "--fselect \""); 5821 | if (some(aDefaultPathAndFile)) 5822 | { 5823 | if (!strchr(aDefaultPathAndFile, '/')) 5824 | { 5825 | strcat(str, "./"); 5826 | } 5827 | strcat(str, aDefaultPathAndFile); 5828 | } 5829 | else if (!isTerminalRunning() && !lWasGraphicDialog) 5830 | { 5831 | strcat(str, getenv("HOME")); 5832 | strcat(str, "/"); 5833 | } 5834 | else 5835 | { 5836 | strcat(str, "./"); 5837 | } 5838 | 5839 | if (lWasGraphicDialog) 5840 | { 5841 | strcat(str, "\" 0 60 ) 2>&1 "); 5842 | } 5843 | else 5844 | { 5845 | strcat(str, "\" 0 60 >/dev/tty) "); 5846 | if (lWasXterm) 5847 | { 5848 | strcat(str, 5849 | "2>/tmp/tinyfd.txt';cat /tmp/tinyfd.txt;rm /tmp/tinyfd.txt"); 5850 | } 5851 | else 5852 | { 5853 | strcat(str, "2>&1 ; clear >/dev/tty"); 5854 | } 5855 | } 5856 | } 5857 | else 5858 | { 5859 | if (lQuery) 5860 | { 5861 | return _inputBox(aTitle, null, null); 5862 | } 5863 | p = _inputBox(aTitle, "Save file", ""); 5864 | getPathWithoutFinalSlash(lString, p); 5865 | if (!dirExists(lString)) 5866 | return null; 5867 | getLastName(lString, p); 5868 | if (!some(lString)) 5869 | return null; 5870 | return p; 5871 | } 5872 | 5873 | if (tinyfd_verbose) 5874 | printf("str: %s\n", str); 5875 | lIn = popen(str, "r"); 5876 | if (!lIn) 5877 | return null; 5878 | while (fgets(lBuff.ptr, lBuff.sizeof, lIn) !is null) 5879 | { 5880 | } 5881 | pclose(lIn); 5882 | removeLastNL(lBuff.ptr); 5883 | /* printf( "lBuff: %s\n" , lBuff ) ; */ 5884 | if (!some(lBuff.ptr)) 5885 | return null; 5886 | getPathWithoutFinalSlash(lString, lBuff.ptr); 5887 | if (!dirExists(lString)) 5888 | return null; 5889 | getLastName(lString, lBuff.ptr); 5890 | if (!filenameValid(lString)) 5891 | return null; 5892 | return lBuff.ptr; 5893 | } 5894 | 5895 | const(char*) _openFileDialog( 5896 | const char* aTitle, 5897 | const char* aDefaultPathAndFile, 5898 | const TFD_Filter[] aFilters, 5899 | const bool aAllowMultipleSelects) 5900 | { 5901 | static char[MAX_MULTIPLE_FILES * MAX_PATH_OR_CMD] lBuff = '\0'; 5902 | const bool lQuery = eq(aTitle, "tinyfd_query"); 5903 | char[MAX_PATH_OR_CMD] str_buf1 = '\0'; 5904 | char[MAX_PATH_OR_CMD] str_buf2 = '\0'; 5905 | char* str = str_buf1.ptr; 5906 | char* lString = str_buf2.ptr; 5907 | FILE* lIn; 5908 | char* p; 5909 | const(char)* p2; 5910 | bool lWasKdialog; 5911 | bool lWasGraphicDialog; 5912 | bool lWasXterm; 5913 | lBuff[0] = '\0'; 5914 | 5915 | if (osascriptPresent()) 5916 | { 5917 | if (lQuery) 5918 | { 5919 | response("applescript"); 5920 | return cast(const(char)*)1; 5921 | } 5922 | strcpy(str, "osascript "); 5923 | if (!osx9orBetter()) 5924 | strcat(str, " -e 'tell application \"System Events\"' -e 'Activate'"); 5925 | strcat(str, " -e 'try' -e '"); 5926 | if (!aAllowMultipleSelects) 5927 | { 5928 | 5929 | strcat(str, "POSIX path of ( "); 5930 | } 5931 | else 5932 | { 5933 | strcat(str, "set mylist to "); 5934 | } 5935 | strcat(str, "choose file "); 5936 | if (some(aTitle)) 5937 | { 5938 | strcat(str, "with prompt \""); 5939 | strcat(str, aTitle); 5940 | strcat(str, "\" "); 5941 | } 5942 | getPathWithoutFinalSlash(lString, aDefaultPathAndFile); 5943 | if (some(lString)) 5944 | { 5945 | strcat(str, "default location \""); 5946 | strcat(str, lString); 5947 | strcat(str, "\" "); 5948 | } 5949 | 5950 | osascriptAppendFilters(aFilters, str); 5951 | 5952 | if (aAllowMultipleSelects) 5953 | { 5954 | strcat(str, "multiple selections allowed true ' "); 5955 | strcat(str, 5956 | "-e 'set mystring to POSIX path of item 1 of mylist' "); 5957 | strcat(str, 5958 | "-e 'repeat with i from 2 to the count of mylist' "); 5959 | strcat(str, "-e 'set mystring to mystring & \"|\"' "); 5960 | strcat(str, 5961 | "-e 'set mystring to mystring & POSIX path of item i of mylist' "); 5962 | strcat(str, "-e 'end repeat' "); 5963 | strcat(str, "-e 'mystring' "); 5964 | } 5965 | else 5966 | { 5967 | strcat(str, ")' "); 5968 | } 5969 | strcat(str, "-e 'on error number -128' "); 5970 | strcat(str, "-e 'end try'"); 5971 | if (!osx9orBetter()) 5972 | strcat(str, " -e 'end tell'"); 5973 | } 5974 | else if (kdialogPresent()) 5975 | { 5976 | if (lQuery) 5977 | { 5978 | response("kdialog"); 5979 | return cast(const(char)*)1; 5980 | } 5981 | lWasKdialog = true; 5982 | 5983 | strcpy(str, "kdialog"); 5984 | if (kdialogPresent() == 2) 5985 | { 5986 | strcat(str, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ 5987 | } 5988 | strcat(str, " --getopenfilename "); 5989 | 5990 | if (some(aDefaultPathAndFile)) 5991 | { 5992 | if (aDefaultPathAndFile[0] != '/') 5993 | { 5994 | strcat(str, "$PWD/"); 5995 | } 5996 | strcat(str, "\""); 5997 | strcat(str, aDefaultPathAndFile); 5998 | strcat(str, "\""); 5999 | } 6000 | else 6001 | { 6002 | strcat(str, "$PWD/"); 6003 | } 6004 | 6005 | kdialogAppendFilters(aFilters, str); 6006 | 6007 | if (aAllowMultipleSelects) 6008 | { 6009 | strcat(str, " --multiple --separate-output"); 6010 | } 6011 | if (some(aTitle)) 6012 | { 6013 | strcat(str, " --title \""); 6014 | strcat(str, aTitle); 6015 | strcat(str, "\""); 6016 | } 6017 | } 6018 | else if (zenityPresent() || matedialogPresent() || shellementaryPresent() || qarmaPresent()) 6019 | { 6020 | if (zenityPresent()) 6021 | { 6022 | if (lQuery) 6023 | { 6024 | response("zenity"); 6025 | return cast(const(char)*)1; 6026 | } 6027 | strcpy(str, "zenity"); 6028 | if (zenity3Present() >= 4 && !getenv("SSH_TTY")) 6029 | { 6030 | strcat(str, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ 6031 | } 6032 | } 6033 | else if (matedialogPresent()) 6034 | { 6035 | if (lQuery) 6036 | { 6037 | response("matedialog"); 6038 | return cast(const(char)*)1; 6039 | } 6040 | strcpy(str, "matedialog"); 6041 | } 6042 | else if (shellementaryPresent()) 6043 | { 6044 | if (lQuery) 6045 | { 6046 | response("shellementary"); 6047 | return cast(const(char)*)1; 6048 | } 6049 | strcpy(str, "shellementary"); 6050 | } 6051 | else 6052 | { 6053 | if (lQuery) 6054 | { 6055 | response("qarma"); 6056 | return cast(const(char)*)1; 6057 | } 6058 | strcpy(str, "qarma"); 6059 | if (!getenv("SSH_TTY")) 6060 | { 6061 | strcat(str, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ 6062 | } 6063 | } 6064 | strcat(str, " --file-selection"); 6065 | 6066 | if (aAllowMultipleSelects) 6067 | { 6068 | strcat(str, " --multiple"); 6069 | } 6070 | if (some(aTitle)) 6071 | { 6072 | strcat(str, " --title=\""); 6073 | strcat(str, aTitle); 6074 | strcat(str, "\""); 6075 | } 6076 | if (some(aDefaultPathAndFile)) 6077 | { 6078 | strcat(str, " --filename=\""); 6079 | strcat(str, aDefaultPathAndFile); 6080 | strcat(str, "\""); 6081 | } 6082 | 6083 | zenityAppendFilters(aFilters, str); 6084 | 6085 | if (tinyfd_silent) 6086 | strcat(str, " 2>/dev/null "); 6087 | } 6088 | else if (tkinter2Present()) 6089 | { 6090 | if (lQuery) 6091 | { 6092 | response("python2-tkinter"); 6093 | return cast(const(char)*)1; 6094 | } 6095 | strcpy(str, gPython2Name.ptr); 6096 | if (!isTerminalRunning() && isDarwin()) 6097 | { 6098 | strcat(str, " -i"); /* for osx without console */ 6099 | } 6100 | strcat(str, 6101 | " -S -c \"import Tkinter,tkFileDialog;root=Tkinter.Tk();root.withdraw();"); 6102 | 6103 | if (isDarwin()) 6104 | { 6105 | strcat(str, 6106 | "import os;os.system('''/usr/bin/osascript -e 'tell app \\\"Finder\\\" to set " ~ 6107 | "frontmost of process \\\"Python\\\" to true' ''');"); 6108 | } 6109 | strcat(str, "lFiles=tkFileDialog.askopenfilename("); 6110 | if (aAllowMultipleSelects) 6111 | { 6112 | strcat(str, "multiple=1,"); 6113 | } 6114 | if (some(aTitle)) 6115 | { 6116 | strcat(str, "title='"); 6117 | strcat(str, aTitle); 6118 | strcat(str, "',"); 6119 | } 6120 | if (some(aDefaultPathAndFile)) 6121 | { 6122 | getPathWithoutFinalSlash(lString, aDefaultPathAndFile); 6123 | if (some(lString)) 6124 | { 6125 | strcat(str, "initialdir='"); 6126 | strcat(str, lString); 6127 | strcat(str, "',"); 6128 | } 6129 | getLastName(lString, aDefaultPathAndFile); 6130 | if (some(lString)) 6131 | { 6132 | strcat(str, "initialfile='"); 6133 | strcat(str, lString); 6134 | strcat(str, "',"); 6135 | } 6136 | } 6137 | 6138 | tkinterAppendFilters(aFilters, str); 6139 | 6140 | strcat(str, `); 6141 | if not isinstance(lFiles, tuple): 6142 | print lFiles 6143 | else: 6144 | lFilesString='' 6145 | for lFile in lFiles: 6146 | lFilesString += str(lFile) + '|' 6147 | print lFilesString[:-1] 6148 | "`); 6149 | } 6150 | else if (tkinter3Present()) 6151 | { 6152 | if (lQuery) 6153 | { 6154 | response("python3-tkinter"); 6155 | return cast(const(char)*)1; 6156 | } 6157 | strcpy(str, gPython3Name.ptr); 6158 | strcat(str, 6159 | " -S -c \"import tkinter;from tkinter import filedialog;root=tkinter.Tk();root.withdraw();"); 6160 | strcat(str, "lFiles=filedialog.askopenfilename("); 6161 | if (aAllowMultipleSelects) 6162 | { 6163 | strcat(str, "multiple=1,"); 6164 | } 6165 | if (some(aTitle)) 6166 | { 6167 | strcat(str, "title='"); 6168 | strcat(str, aTitle); 6169 | strcat(str, "',"); 6170 | } 6171 | if (some(aDefaultPathAndFile)) 6172 | { 6173 | getPathWithoutFinalSlash(lString, aDefaultPathAndFile); 6174 | if (some(lString)) 6175 | { 6176 | strcat(str, "initialdir='"); 6177 | strcat(str, lString); 6178 | strcat(str, "',"); 6179 | } 6180 | getLastName(lString, aDefaultPathAndFile); 6181 | if (some(lString)) 6182 | { 6183 | strcat(str, "initialfile='"); 6184 | strcat(str, lString); 6185 | strcat(str, "',"); 6186 | } 6187 | } 6188 | 6189 | tkinterAppendFilters(aFilters, str); 6190 | 6191 | strcat(str, `); 6192 | if not isinstance(lFiles, tuple): 6193 | print(lFiles) 6194 | else: 6195 | lFilesString = '' 6196 | for lFile in lFiles: 6197 | lFilesString += str(lFile) + '|' 6198 | print(lFilesString[:-1]) 6199 | "`); 6200 | } 6201 | else if (xdialogPresent() || dialogName()) 6202 | { 6203 | if (xdialogPresent()) 6204 | { 6205 | if (lQuery) 6206 | { 6207 | response("xdialog"); 6208 | return cast(const(char)*)1; 6209 | } 6210 | lWasGraphicDialog = true; 6211 | strcpy(str, "(Xdialog "); 6212 | } 6213 | else if (isTerminalRunning()) 6214 | { 6215 | if (lQuery) 6216 | { 6217 | response("dialog"); 6218 | return cast(const(char)*)0; 6219 | } 6220 | strcpy(str, "(dialog "); 6221 | } 6222 | else 6223 | { 6224 | if (lQuery) 6225 | { 6226 | response("dialog"); 6227 | return cast(const(char)*)0; 6228 | } 6229 | lWasXterm = true; 6230 | strcpy(str, terminalName()); 6231 | strcat(str, "'("); 6232 | strcat(str, dialogName()); 6233 | strcat(str, " "); 6234 | } 6235 | 6236 | if (some(aTitle)) 6237 | { 6238 | strcat(str, "--title \""); 6239 | strcat(str, aTitle); 6240 | strcat(str, "\" "); 6241 | } 6242 | 6243 | if (!xdialogPresent() && !gdialogPresent()) 6244 | { 6245 | strcat(str, "--backtitle \""); 6246 | strcat(str, 6247 | "tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY"); 6248 | strcat(str, "\" "); 6249 | } 6250 | 6251 | strcat(str, "--fselect \""); 6252 | if (some(aDefaultPathAndFile)) 6253 | { 6254 | if (!strchr(aDefaultPathAndFile, '/')) 6255 | { 6256 | strcat(str, "./"); 6257 | } 6258 | strcat(str, aDefaultPathAndFile); 6259 | } 6260 | else if (!isTerminalRunning() && !lWasGraphicDialog) 6261 | { 6262 | strcat(str, getenv("HOME")); 6263 | strcat(str, "/"); 6264 | } 6265 | else 6266 | { 6267 | strcat(str, "./"); 6268 | } 6269 | 6270 | if (lWasGraphicDialog) 6271 | { 6272 | strcat(str, "\" 0 60 ) 2>&1 "); 6273 | } 6274 | else 6275 | { 6276 | strcat(str, "\" 0 60 >/dev/tty) "); 6277 | if (lWasXterm) 6278 | { 6279 | strcat(str, 6280 | "2>/tmp/tinyfd.txt';cat /tmp/tinyfd.txt;rm /tmp/tinyfd.txt"); 6281 | } 6282 | else 6283 | { 6284 | strcat(str, "2>&1 ; clear >/dev/tty"); 6285 | } 6286 | } 6287 | } 6288 | else 6289 | { 6290 | if (lQuery) 6291 | { 6292 | return _inputBox(aTitle, null, null); 6293 | } 6294 | p2 = _inputBox(aTitle, "Open file", ""); 6295 | if (!fileExists(p2)) 6296 | return null; 6297 | return p2; 6298 | } 6299 | 6300 | if (tinyfd_verbose) 6301 | printf("str: %s\n", str); 6302 | lIn = popen(str, "r"); 6303 | if (!lIn) 6304 | return null; 6305 | 6306 | lBuff[0] = '\0'; 6307 | p = lBuff.ptr; 6308 | while (fgets(p, lBuff.sizeof, lIn) !is null) 6309 | { 6310 | p += strlen(p); 6311 | } 6312 | pclose(lIn); 6313 | removeLastNL(lBuff.ptr); 6314 | /* printf( "lBuff: %s\n" , lBuff ) ; */ 6315 | if (lWasKdialog && aAllowMultipleSelects) 6316 | { 6317 | p = lBuff.ptr; 6318 | while (p) 6319 | { 6320 | p = strchr(p, '\n'); 6321 | *p = '|'; 6322 | } 6323 | } 6324 | /* printf( "lBuff2: %s\n" , lBuff ) ; */ 6325 | if (!some(lBuff.ptr)) 6326 | return null; 6327 | 6328 | if (aAllowMultipleSelects && strchr(lBuff.ptr, '|')) 6329 | { 6330 | p2 = ensureFilesExist(lBuff.ptr, lBuff.ptr); 6331 | } 6332 | else if (fileExists(lBuff.ptr)) 6333 | { 6334 | p2 = lBuff.ptr; 6335 | } 6336 | else 6337 | return null; 6338 | /* printf( "lBuff3: %s\n" , p2 ) ; */ 6339 | 6340 | return p2; 6341 | } 6342 | 6343 | const(char*) _selectFolderDialog(const char* aTitle, const char* aDefaultPath) 6344 | { 6345 | static char[MAX_PATH_OR_CMD] lBuff = '\0'; 6346 | const bool lQuery = eq(aTitle, "tinyfd_query"); 6347 | char[MAX_PATH_OR_CMD] str_buf = '\0'; 6348 | char* str = str_buf.ptr; 6349 | FILE* lIn; 6350 | const(char)* p; 6351 | bool lWasGraphicDialog; 6352 | bool lWasXterm; 6353 | lBuff[0] = '\0'; 6354 | 6355 | if (osascriptPresent()) 6356 | { 6357 | if (lQuery) 6358 | { 6359 | response("applescript"); 6360 | return cast(const(char)*)1; 6361 | } 6362 | strcpy(str, "osascript "); 6363 | if (!osx9orBetter()) 6364 | strcat(str, " -e 'tell application \"System Events\"' -e 'Activate'"); 6365 | strcat(str, " -e 'try' -e 'POSIX path of ( choose folder "); 6366 | if (some(aTitle)) 6367 | { 6368 | strcat(str, "with prompt \""); 6369 | strcat(str, aTitle); 6370 | strcat(str, "\" "); 6371 | } 6372 | if (some(aDefaultPath)) 6373 | { 6374 | strcat(str, "default location \""); 6375 | strcat(str, aDefaultPath); 6376 | strcat(str, "\" "); 6377 | } 6378 | strcat(str, ")' "); 6379 | strcat(str, "-e 'on error number -128' "); 6380 | strcat(str, "-e 'end try'"); 6381 | if (!osx9orBetter()) 6382 | strcat(str, " -e 'end tell'"); 6383 | } 6384 | else if (kdialogPresent()) 6385 | { 6386 | if (lQuery) 6387 | { 6388 | response("kdialog"); 6389 | return cast(const(char)*)1; 6390 | } 6391 | strcpy(str, "kdialog"); 6392 | if (kdialogPresent() == 2) 6393 | { 6394 | strcat(str, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ 6395 | } 6396 | strcat(str, " --getexistingdirectory "); 6397 | 6398 | if (some(aDefaultPath)) 6399 | { 6400 | if (aDefaultPath[0] != '/') 6401 | { 6402 | strcat(str, "$PWD/"); 6403 | } 6404 | strcat(str, "\""); 6405 | strcat(str, aDefaultPath); 6406 | strcat(str, "\""); 6407 | } 6408 | else 6409 | { 6410 | strcat(str, "$PWD/"); 6411 | } 6412 | 6413 | if (some(aTitle)) 6414 | { 6415 | strcat(str, " --title \""); 6416 | strcat(str, aTitle); 6417 | strcat(str, "\""); 6418 | } 6419 | } 6420 | else if (zenityPresent() || matedialogPresent() || shellementaryPresent() || qarmaPresent()) 6421 | { 6422 | if (zenityPresent()) 6423 | { 6424 | if (lQuery) 6425 | { 6426 | response("zenity"); 6427 | return cast(const(char)*)1; 6428 | } 6429 | strcpy(str, "zenity"); 6430 | if (zenity3Present() >= 4 && !getenv("SSH_TTY")) 6431 | { 6432 | strcat(str, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ 6433 | } 6434 | } 6435 | else if (matedialogPresent()) 6436 | { 6437 | if (lQuery) 6438 | { 6439 | response("matedialog"); 6440 | return cast(const(char)*)1; 6441 | } 6442 | strcpy(str, "matedialog"); 6443 | } 6444 | else if (shellementaryPresent()) 6445 | { 6446 | if (lQuery) 6447 | { 6448 | response("shellementary"); 6449 | return cast(const(char)*)1; 6450 | } 6451 | strcpy(str, "shellementary"); 6452 | } 6453 | else 6454 | { 6455 | if (lQuery) 6456 | { 6457 | response("qarma"); 6458 | return cast(const(char)*)1; 6459 | } 6460 | strcpy(str, "qarma"); 6461 | if (!getenv("SSH_TTY")) 6462 | { 6463 | strcat(str, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ 6464 | } 6465 | } 6466 | strcat(str, " --file-selection --directory"); 6467 | 6468 | if (some(aTitle)) 6469 | { 6470 | strcat(str, " --title=\""); 6471 | strcat(str, aTitle); 6472 | strcat(str, "\""); 6473 | } 6474 | if (some(aDefaultPath)) 6475 | { 6476 | strcat(str, " --filename=\""); 6477 | strcat(str, aDefaultPath); 6478 | strcat(str, "\""); 6479 | } 6480 | if (tinyfd_silent) 6481 | strcat(str, " 2>/dev/null "); 6482 | } 6483 | else if (!xdialogPresent() && tkinter2Present()) 6484 | { 6485 | if (lQuery) 6486 | { 6487 | response("python2-tkinter"); 6488 | return cast(const(char)*)1; 6489 | } 6490 | strcpy(str, gPython2Name.ptr); 6491 | if (!isTerminalRunning() && isDarwin()) 6492 | { 6493 | strcat(str, " -i"); /* for osx without console */ 6494 | } 6495 | strcat(str, 6496 | " -S -c \"import Tkinter,tkFileDialog;root=Tkinter.Tk();root.withdraw();"); 6497 | 6498 | if (isDarwin()) 6499 | { 6500 | strcat(str, 6501 | "import os;os.system('''/usr/bin/osascript -e 'tell app \\\"Finder\\\" to set " ~ 6502 | "frontmost of process \\\"Python\\\" to true' ''');"); 6503 | } 6504 | 6505 | strcat(str, "print tkFileDialog.askdirectory("); 6506 | if (some(aTitle)) 6507 | { 6508 | strcat(str, "title='"); 6509 | strcat(str, aTitle); 6510 | strcat(str, "',"); 6511 | } 6512 | if (some(aDefaultPath)) 6513 | { 6514 | strcat(str, "initialdir='"); 6515 | strcat(str, aDefaultPath); 6516 | strcat(str, "'"); 6517 | } 6518 | strcat(str, ")\""); 6519 | } 6520 | else if (!xdialogPresent() && tkinter3Present()) 6521 | { 6522 | if (lQuery) 6523 | { 6524 | response("python3-tkinter"); 6525 | return cast(const(char)*)1; 6526 | } 6527 | strcpy(str, gPython3Name.ptr); 6528 | strcat(str, 6529 | " -S -c \"import tkinter;from tkinter import filedialog;root=tkinter.Tk();root.withdraw();"); 6530 | strcat(str, "print( filedialog.askdirectory("); 6531 | if (some(aTitle)) 6532 | { 6533 | strcat(str, "title='"); 6534 | strcat(str, aTitle); 6535 | strcat(str, "',"); 6536 | } 6537 | if (some(aDefaultPath)) 6538 | { 6539 | strcat(str, "initialdir='"); 6540 | strcat(str, aDefaultPath); 6541 | strcat(str, "'"); 6542 | } 6543 | strcat(str, ") )\""); 6544 | } 6545 | else if (xdialogPresent() || dialogName()) 6546 | { 6547 | if (xdialogPresent()) 6548 | { 6549 | if (lQuery) 6550 | { 6551 | response("xdialog"); 6552 | return cast(const(char)*)1; 6553 | } 6554 | lWasGraphicDialog = true; 6555 | strcpy(str, "(Xdialog "); 6556 | } 6557 | else if (isTerminalRunning()) 6558 | { 6559 | if (lQuery) 6560 | { 6561 | response("dialog"); 6562 | return cast(const(char)*)0; 6563 | } 6564 | strcpy(str, "(dialog "); 6565 | } 6566 | else 6567 | { 6568 | if (lQuery) 6569 | { 6570 | response("dialog"); 6571 | return cast(const(char)*)0; 6572 | } 6573 | lWasXterm = true; 6574 | strcpy(str, terminalName()); 6575 | strcat(str, "'("); 6576 | strcat(str, dialogName()); 6577 | strcat(str, " "); 6578 | } 6579 | 6580 | if (some(aTitle)) 6581 | { 6582 | strcat(str, "--title \""); 6583 | strcat(str, aTitle); 6584 | strcat(str, "\" "); 6585 | } 6586 | 6587 | if (!xdialogPresent() && !gdialogPresent()) 6588 | { 6589 | strcat(str, "--backtitle \""); 6590 | strcat(str, 6591 | "tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY"); 6592 | strcat(str, "\" "); 6593 | } 6594 | 6595 | strcat(str, "--dselect \""); 6596 | if (some(aDefaultPath)) 6597 | { 6598 | strcat(str, aDefaultPath); 6599 | ensureFinalSlash(str); 6600 | } 6601 | else if (!isTerminalRunning() && !lWasGraphicDialog) 6602 | { 6603 | strcat(str, getenv("HOME")); 6604 | strcat(str, "/"); 6605 | } 6606 | else 6607 | { 6608 | strcat(str, "./"); 6609 | } 6610 | 6611 | if (lWasGraphicDialog) 6612 | { 6613 | strcat(str, "\" 0 60 ) 2>&1 "); 6614 | } 6615 | else 6616 | { 6617 | strcat(str, "\" 0 60 >/dev/tty) "); 6618 | if (lWasXterm) 6619 | { 6620 | strcat(str, 6621 | "2>/tmp/tinyfd.txt';cat /tmp/tinyfd.txt;rm /tmp/tinyfd.txt"); 6622 | } 6623 | else 6624 | { 6625 | strcat(str, "2>&1 ; clear >/dev/tty"); 6626 | } 6627 | } 6628 | } 6629 | else 6630 | { 6631 | if (lQuery) 6632 | { 6633 | return _inputBox(aTitle, null, null); 6634 | } 6635 | p = _inputBox(aTitle, "Select folder", ""); 6636 | if (!dirExists(p)) 6637 | return null; 6638 | return p; 6639 | } 6640 | if (tinyfd_verbose) 6641 | printf("str: %s\n", str); 6642 | lIn = popen(str, "r"); 6643 | if (!lIn) 6644 | return null; 6645 | 6646 | while (fgets(lBuff.ptr, lBuff.sizeof, lIn) !is null) 6647 | { 6648 | } 6649 | pclose(lIn); 6650 | removeLastNL(lBuff.ptr); 6651 | /* printf( "lBuff: %s\n" , lBuff ) ; */ 6652 | if (!dirExists(lBuff.ptr)) 6653 | return null; 6654 | return lBuff.ptr; 6655 | } 6656 | 6657 | const(char*) _colorChooser( 6658 | const char* aTitle, 6659 | const char* aDefaultHexRGB, 6660 | ref const ubyte[3] aDefaultRGB, 6661 | ref ubyte[3] aoResultRGB) 6662 | { 6663 | static char[128] lBuff = '\0'; 6664 | const bool lQuery = eq(aTitle, "tinyfd_query"); 6665 | char[128] tmp_buf = '\0'; 6666 | char[MAX_PATH_OR_CMD] str_buf = '\0'; 6667 | char* lTmp = tmp_buf.ptr; 6668 | char* str = str_buf.ptr; 6669 | char[8] lDefaultHexRGB = '\0'; 6670 | char* lpDefaultHexRGB; 6671 | ubyte[3] lDefaultRGB; 6672 | const(char)* p; 6673 | FILE* lIn; 6674 | bool lWasZenity3; 6675 | bool lWasOsascript; 6676 | bool lWasXdialog; 6677 | lBuff[0] = '\0'; 6678 | 6679 | if (aDefaultHexRGB) 6680 | { 6681 | Hex2RGB(aDefaultHexRGB, lDefaultRGB); 6682 | lpDefaultHexRGB = cast(char*)aDefaultHexRGB; 6683 | } 6684 | else 6685 | { 6686 | lDefaultRGB[0] = aDefaultRGB[0]; 6687 | lDefaultRGB[1] = aDefaultRGB[1]; 6688 | lDefaultRGB[2] = aDefaultRGB[2]; 6689 | RGB2Hex(aDefaultRGB, lDefaultHexRGB.ptr); 6690 | lpDefaultHexRGB = lDefaultHexRGB.ptr; 6691 | } 6692 | 6693 | if (osascriptPresent()) 6694 | { 6695 | if (lQuery) 6696 | { 6697 | response("applescript"); 6698 | return cast(const(char)*)1; 6699 | } 6700 | lWasOsascript = true; 6701 | strcpy(str, "osascript"); 6702 | 6703 | if (!osx9orBetter()) 6704 | { 6705 | strcat(str, " -e 'tell application \"System Events\"' -e 'Activate'"); 6706 | strcat(str, " -e 'try' -e 'set mycolor to choose color default color {"); 6707 | } 6708 | else 6709 | { 6710 | strcat(str, 6711 | " -e 'try' -e 'tell app (path to frontmost application as Unicode text) " ~ 6712 | "to set mycolor to choose color default color {"); 6713 | } 6714 | 6715 | sprintf(lTmp, "%d", 256 * lDefaultRGB[0]); 6716 | strcat(str, lTmp); 6717 | strcat(str, ","); 6718 | sprintf(lTmp, "%d", 256 * lDefaultRGB[1]); 6719 | strcat(str, lTmp); 6720 | strcat(str, ","); 6721 | sprintf(lTmp, "%d", 256 * lDefaultRGB[2]); 6722 | strcat(str, lTmp); 6723 | strcat(str, "}' "); 6724 | strcat(str, 6725 | "-e 'set mystring to ((item 1 of mycolor) div 256 as integer) as string' "); 6726 | strcat(str, 6727 | "-e 'repeat with i from 2 to the count of mycolor' "); 6728 | strcat(str, 6729 | "-e 'set mystring to mystring & \" \" & ((item i of mycolor) div 256 as integer) as string' "); 6730 | strcat(str, "-e 'end repeat' "); 6731 | strcat(str, "-e 'mystring' "); 6732 | strcat(str, "-e 'on error number -128' "); 6733 | strcat(str, "-e 'end try'"); 6734 | if (!osx9orBetter()) 6735 | strcat(str, " -e 'end tell'"); 6736 | } 6737 | else if (kdialogPresent()) 6738 | { 6739 | if (lQuery) 6740 | { 6741 | response("kdialog"); 6742 | return cast(const(char)*)1; 6743 | } 6744 | strcpy(str, "kdialog"); 6745 | if (kdialogPresent() == 2) 6746 | { 6747 | strcat(str, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ 6748 | } 6749 | sprintf(str + strlen(str), " --getcolor --default '%s'", lpDefaultHexRGB); 6750 | 6751 | if (some(aTitle)) 6752 | { 6753 | strcat(str, " --title \""); 6754 | strcat(str, aTitle); 6755 | strcat(str, "\""); 6756 | } 6757 | } 6758 | else if (zenity3Present() || matedialogPresent() || shellementaryPresent() || qarmaPresent()) 6759 | { 6760 | lWasZenity3 = true; 6761 | if (zenity3Present()) 6762 | { 6763 | if (lQuery) 6764 | { 6765 | response("zenity3"); 6766 | return cast(const(char)*)1; 6767 | } 6768 | strcpy(str, "zenity"); 6769 | if (zenity3Present() >= 4 && !getenv("SSH_TTY")) 6770 | { 6771 | strcat(str, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ 6772 | } 6773 | } 6774 | else if (matedialogPresent()) 6775 | { 6776 | if (lQuery) 6777 | { 6778 | response("matedialog"); 6779 | return cast(const(char)*)1; 6780 | } 6781 | strcpy(str, "matedialog"); 6782 | } 6783 | else if (shellementaryPresent()) 6784 | { 6785 | if (lQuery) 6786 | { 6787 | response("shellementary"); 6788 | return cast(const(char)*)1; 6789 | } 6790 | strcpy(str, "shellementary"); 6791 | } 6792 | else 6793 | { 6794 | if (lQuery) 6795 | { 6796 | response("qarma"); 6797 | return cast(const(char)*)1; 6798 | } 6799 | strcpy(str, "qarma"); 6800 | if (!getenv("SSH_TTY")) 6801 | { 6802 | strcat(str, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ 6803 | } 6804 | } 6805 | strcat(str, " --color-selection --show-palette"); 6806 | sprintf(str + strlen(str), " --color=%s", lpDefaultHexRGB); 6807 | 6808 | if (some(aTitle)) 6809 | { 6810 | strcat(str, " --title=\""); 6811 | strcat(str, aTitle); 6812 | strcat(str, "\""); 6813 | } 6814 | if (tinyfd_silent) 6815 | strcat(str, " 2>/dev/null "); 6816 | } 6817 | else if (xdialogPresent()) 6818 | { 6819 | if (lQuery) 6820 | { 6821 | response("xdialog"); 6822 | return cast(const(char)*)1; 6823 | } 6824 | lWasXdialog = true; 6825 | strcpy(str, "Xdialog --colorsel \""); 6826 | if (some(aTitle)) 6827 | { 6828 | strcat(str, aTitle); 6829 | } 6830 | strcat(str, "\" 0 60 "); 6831 | sprintf(lTmp, "%hhu %hhu %hhu", lDefaultRGB[0], lDefaultRGB[1], lDefaultRGB[2]); 6832 | strcat(str, lTmp); 6833 | strcat(str, " 2>&1"); 6834 | } 6835 | else if (tkinter2Present()) 6836 | { 6837 | if (lQuery) 6838 | { 6839 | response("python2-tkinter"); 6840 | return cast(const(char)*)1; 6841 | } 6842 | strcpy(str, gPython2Name.ptr); 6843 | if (!isTerminalRunning() && isDarwin()) 6844 | { 6845 | strcat(str, " -i"); /* for osx without console */ 6846 | } 6847 | 6848 | strcat(str, 6849 | " -S -c \"import Tkinter,tkColorChooser;root=Tkinter.Tk();root.withdraw();"); 6850 | 6851 | if (isDarwin()) 6852 | { 6853 | strcat(str, 6854 | "import os;os.system('''osascript -e 'tell app \\\"Finder\\\" to set " ~ 6855 | "frontmost of process \\\"Python\\\" to true' ''');"); 6856 | } 6857 | 6858 | strcat(str, "res=tkColorChooser.askcolor(color='"); 6859 | strcat(str, lpDefaultHexRGB); 6860 | strcat(str, "'"); 6861 | 6862 | if (some(aTitle)) 6863 | { 6864 | strcat(str, ",title='"); 6865 | strcat(str, aTitle); 6866 | strcat(str, "'"); 6867 | } 6868 | strcat(str, `); 6869 | if res[1] is not None: 6870 | print res[1] 6871 | "`); 6872 | } 6873 | else if (tkinter3Present()) 6874 | { 6875 | if (lQuery) 6876 | { 6877 | response("python3-tkinter"); 6878 | return cast(const(char)*)1; 6879 | } 6880 | strcpy(str, gPython3Name.ptr); 6881 | strcat(str, 6882 | " -S -c \"import tkinter;from tkinter import colorchooser;root=tkinter.Tk();root.withdraw();"); 6883 | strcat(str, "res=colorchooser.askcolor(color='"); 6884 | strcat(str, lpDefaultHexRGB); 6885 | strcat(str, "'"); 6886 | 6887 | if (some(aTitle)) 6888 | { 6889 | strcat(str, ",title='"); 6890 | strcat(str, aTitle); 6891 | strcat(str, "'"); 6892 | } 6893 | strcat(str, `); 6894 | if res[1] is not None: 6895 | print(res[1]) 6896 | "`); 6897 | } 6898 | else 6899 | { 6900 | if (lQuery) 6901 | { 6902 | return _inputBox(aTitle, null, null); 6903 | } 6904 | p = _inputBox(aTitle, "Enter hex rgb color (i.e. #f5ca20)", lpDefaultHexRGB); 6905 | if (!p || strlen(p) != 7 || p[0] != '#') 6906 | return null; 6907 | 6908 | foreach (i; 1 .. 7) 6909 | { 6910 | if (!isxdigit(p[i])) 6911 | return null; 6912 | } 6913 | Hex2RGB(p, aoResultRGB); 6914 | return p; 6915 | } 6916 | 6917 | if (tinyfd_verbose) 6918 | printf("str: %s\n", str); 6919 | lIn = popen(str, "r"); 6920 | if (!lIn) 6921 | return null; 6922 | 6923 | while (fgets(lBuff.ptr, lBuff.sizeof, lIn) !is null) 6924 | { 6925 | } 6926 | pclose(lIn); 6927 | if (!some(lBuff.ptr)) 6928 | return null; 6929 | /* printf( "len Buff: %lu\n" , strlen(lBuff.ptr) ) ; */ 6930 | /* printf( "lBuff0: %s\n" , lBuff ) ; */ 6931 | removeLastNL(lBuff.ptr); 6932 | 6933 | if (lWasZenity3) 6934 | { 6935 | if (lBuff[0] == '#') 6936 | { 6937 | if (strlen(lBuff.ptr) > 7) 6938 | { 6939 | lBuff[3] = lBuff[5]; 6940 | lBuff[4] = lBuff[6]; 6941 | lBuff[5] = lBuff[9]; 6942 | lBuff[6] = lBuff[10]; 6943 | lBuff[7] = '\0'; 6944 | } 6945 | Hex2RGB(lBuff.ptr, aoResultRGB); 6946 | } 6947 | else if (lBuff[3] == '(') 6948 | { 6949 | sscanf(lBuff.ptr, "rgb(%hhu,%hhu,%hhu", 6950 | &aoResultRGB[0], &aoResultRGB[1], &aoResultRGB[2]); 6951 | RGB2Hex(aoResultRGB, lBuff.ptr); 6952 | } 6953 | else if (lBuff[4] == '(') 6954 | { 6955 | sscanf(lBuff.ptr, "rgba(%hhu,%hhu,%hhu", 6956 | &aoResultRGB[0], &aoResultRGB[1], &aoResultRGB[2]); 6957 | RGB2Hex(aoResultRGB, lBuff.ptr); 6958 | } 6959 | } 6960 | else if (lWasOsascript || lWasXdialog) 6961 | { 6962 | /* printf( "lBuff: %s\n" , lBuff ) ; */ 6963 | sscanf(lBuff.ptr, "%hhu %hhu %hhu", 6964 | &aoResultRGB[0], &aoResultRGB[1], &aoResultRGB[2]); 6965 | RGB2Hex(aoResultRGB, lBuff.ptr); 6966 | } 6967 | else 6968 | { 6969 | Hex2RGB(lBuff.ptr, aoResultRGB); 6970 | } 6971 | /* printf("%d %d %d\n", aoResultRGB[0],aoResultRGB[1],aoResultRGB[2]); */ 6972 | /* printf( "lBuff: %s\n" , lBuff ) ; */ 6973 | return lBuff.ptr; 6974 | } 6975 | 6976 | } // windows-unix 6977 | --------------------------------------------------------------------------------