Browse Source

Add commandline conversion for Windows (no-op on other platforms)

- add Fl::args_to_utf8() to convert commandline arguments to UTF-8

This new function closes the gap that previously only Visual Studio
applications converted their commandlines to UTF-8.

Tested with MinGW, MSYS2/MinGW-w64, and Visual Studio (2019).
pull/857/head
Albrecht Schlosser 2 years ago
parent
commit
727bd94560
  1. 3
      FL/Fl.H
  2. 27
      examples/howto-parse-args.cxx
  3. 9
      src/CMakeLists.txt
  4. 73
      src/Fl.cxx
  5. 4
      src/Fl_System_Driver.H
  6. 4
      src/drivers/WinAPI/Fl_WinAPI_System_Driver.H
  7. 34
      src/drivers/WinAPI/Fl_WinAPI_System_Driver.cxx
  8. 55
      src/fl_call_main.c
  9. 5
      test/pixmap_browser.cxx

3
FL/Fl.H

@ -1398,6 +1398,9 @@ public:
static int system(const char *command); static int system(const char *command);
// Convert Windows commandline arguments to UTF-8 (documented in src/Fl.cxx)
static int args_to_utf8(int argc, char ** &argv);
#ifdef FLTK_HAVE_CAIRO #ifdef FLTK_HAVE_CAIRO
/** \defgroup group_cairo Cairo Support Functions and Classes /** \defgroup group_cairo Cairo Support Functions and Classes
@{ @{

27
examples/howto-parse-args.cxx

@ -11,7 +11,16 @@
// usual *nix idiom of "option=value", and provides no validation nor // usual *nix idiom of "option=value", and provides no validation nor
// conversion of the parameter string into ints or floats. // conversion of the parameter string into ints or floats.
// //
// Copyright 1998-2020 by Bill Spitzak and others. // Example 1:
//
// ./howto-parse-args -ti "FLTK is great" -o "FLTK is a great GUI tool"
//
// Example 2: translated to Japanese and simplified Chinese, respectively,
// by a well known internet translation service.
//
// ./howto-parse-args -ti "FLTKは素晴らしいです" -o "FLTK 是一个很棒的 GUI 工具"
//
// Copyright 1998-2023 by Bill Spitzak and others.
// //
// This library is free software. Distribution and use rights are outlined in // This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this // the file "COPYING" which should have been included with this file. If this
@ -42,13 +51,14 @@ char *optionString = 0;
* returns 1 if argv[i] matches on its own, * returns 1 if argv[i] matches on its own,
* returns 0 if argv[i] does not match. * returns 0 if argv[i] does not match.
*/ */
int arg(int argc, char **argv, int &i) int arg(int argc, char **argv, int &i) {
{
if (strcmp("-h", argv[i]) == 0 || strcmp("--help", argv[i]) == 0) { if (strcmp("-h", argv[i]) == 0 || strcmp("--help", argv[i]) == 0) {
helpFlag = 1; helpFlag = 1;
i += 1; i += 1;
return 1; return 1;
} }
if (strcmp("-o", argv[i]) == 0 || strcmp("--option", argv[i]) == 0) { if (strcmp("-o", argv[i]) == 0 || strcmp("--option", argv[i]) == 0) {
if (i < argc-1 && argv[i+1] != 0) { if (i < argc-1 && argv[i+1] != 0) {
optionString = argv[i+1]; optionString = argv[i+1];
@ -59,8 +69,13 @@ int arg(int argc, char **argv, int &i)
return 0; return 0;
} }
int main(int argc, char** argv) int main(int argc, char** argv) {
{
// Convert commandline arguments in 'argv' to UTF-8 on Windows.
// This is a no-op on all other platforms (see documentation).
Fl::args_to_utf8(argc, argv);
int i = 1; int i = 1;
if (Fl::args(argc, argv, i, arg) < argc) if (Fl::args(argc, argv, i, arg) < argc)
// note the concatenated strings to give a single format string! // note the concatenated strings to give a single format string!
@ -84,6 +99,8 @@ int main(int argc, char** argv)
textBox->label(optionString); textBox->label(optionString);
else else
textBox->label("re-run with [-o|--option] text"); textBox->label("re-run with [-o|--option] text");
mainWin->resizable(mainWin);
mainWin->show(argc, argv); mainWin->show(argc, argv);
return Fl::run(); return Fl::run();
} }

9
src/CMakeLists.txt

@ -578,9 +578,14 @@ list (APPEND SHARED_FILES ${HEADER_FILES} ${DRIVER_HEADER_FILES})
set (STATIC_FILES ${SHARED_FILES}) set (STATIC_FILES ${SHARED_FILES})
if (MSVC) # Visual Studio (MSVC) is known to need WinMain() and maybe BORLAND
# needs it as well, hence we include it on all Windows platforms.
# The GNU compilers (MinGW, MSYS2, Cygwin) disable compilation inside
# the source file which is what we finally want and need.
if (WIN32)
list (APPEND STATIC_FILES fl_call_main.c) list (APPEND STATIC_FILES fl_call_main.c)
endif (MSVC) endif ()
####################################################################### #######################################################################

73
src/Fl.cxx

@ -2281,3 +2281,76 @@ FL_EXPORT const char* fl_local_shift = Fl::system_driver()->shift_name();
FL_EXPORT const char* fl_local_meta = Fl::system_driver()->meta_name(); FL_EXPORT const char* fl_local_meta = Fl::system_driver()->meta_name();
FL_EXPORT const char* fl_local_alt = Fl::system_driver()->alt_name(); FL_EXPORT const char* fl_local_alt = Fl::system_driver()->alt_name();
FL_EXPORT const char* fl_local_ctrl = Fl::system_driver()->control_name(); FL_EXPORT const char* fl_local_ctrl = Fl::system_driver()->control_name();
/**
Convert Windows commandline arguments to UTF-8.
\note This function does nothing on other (non-Windows) platforms, hence
you may call it on all platforms or only on Windows by using platform
specific code like <tt>'\#ifdef _WIN32'</tt> etc. - it's your choice.
Calling it on other platforms returns quickly w/o wasting much CPU time.
This function <i>must be called <b>on Windows platforms</b></i> in \c main()
before the array \c argv is used if your program uses any commandline
argument strings (these should be UTF-8 encoded).
This applies also to standard FLTK commandline arguments like
"-name" (class name) and "-title" (window title in the title bar).
Unfortunately Windows \b neither provides commandline arguments in UTF-8
encoding \b nor as Windows "Wide Character" strings in the standard
\c main() and/or the Windows specific \c WinMain() function.
On Windows platforms (no matter which build system) this function calls
a Windows specific function to retrieve commandline arguments as Windows
"Wide Character" strings, converts these strings to an internally allocated
buffer (or multiple buffers) and returns the result in \c argv.
For implementation details please refer to the source code; however these
details may be changed in the future.
Note that \c argv is provided by reference so it can be overwritten.
In the recommended simple form the function overwrites the variable
\c argv and allocates a new array of strings pointed to by \c argv.
You may use this form on all platforms and it is as simple as adding
one line to old programs to make them work with international (UTF-8)
commandline arguments.
\code
int main(int argc, char **argv) {
Fl::args_to_utf8(argc, argv); // add this line
// ... use argc and argv, e.g. for commandline parsing
window->show(argc, argv);
return Fl::run();
}
\endcode
For an example see 'examples/howto-parse-args.cxx' in the FLTK sources.
If you want to retain the original \c argc and \c argv variables the
following slightly longer and more complicated code works as well on
all platforms.
\code
int main(int argc, char **argv) {
char **argvn = argv; // must copy argv to work on all platforms
int argcn = Fl::args_to_utf8(argc, argvn);
// ... use argcn and argvn, e.g. for commandline parsing
window->show(argcn, argvn);
return Fl::run();
}
\endcode
\param[in] argc used only on non-Windows platforms
\param[out] argv modified only on Windows platforms
\returns argument count (always the same as argc)
\since 1.4.0
\internal This function must not open the display, otherwise
commandline processing (e.g. by fluid) would open the display.
OTOH calling it when the display is opened wouldn't work either
for the same reasons ('fluid -c' doesn't open the display).
*/
int Fl::args_to_utf8(int argc, char ** &argv) {
return Fl::system_driver()->args_to_utf8(argc, argv);
}

4
src/Fl_System_Driver.H

@ -110,6 +110,10 @@ public:
virtual int rmdir(const char*) {return -1;} virtual int rmdir(const char*) {return -1;}
virtual int rename(const char* /*f*/, const char * /*n*/) {return -1;} virtual int rename(const char* /*f*/, const char * /*n*/) {return -1;}
// Windows commandline argument conversion to UTF-8.
// Default implementation: no-op, overridden only on Windows
virtual int args_to_utf8(int argc, char ** &argv) { return argc; }
// the default implementation of these utf8... functions should be enough // the default implementation of these utf8... functions should be enough
virtual unsigned utf8towc(const char* src, unsigned srclen, wchar_t* dst, unsigned dstlen); virtual unsigned utf8towc(const char* src, unsigned srclen, wchar_t* dst, unsigned dstlen);
virtual unsigned utf8fromwc(char* dst, unsigned dstlen, const wchar_t* src, unsigned srclen); virtual unsigned utf8fromwc(char* dst, unsigned dstlen, const wchar_t* src, unsigned srclen);

4
src/drivers/WinAPI/Fl_WinAPI_System_Driver.H

@ -61,11 +61,15 @@ public:
int mkdir(const char *fnam, int mode) FL_OVERRIDE; int mkdir(const char *fnam, int mode) FL_OVERRIDE;
int rmdir(const char *fnam) FL_OVERRIDE; int rmdir(const char *fnam) FL_OVERRIDE;
int rename(const char *fnam, const char *newnam) FL_OVERRIDE; int rename(const char *fnam, const char *newnam) FL_OVERRIDE;
// Windows commandline argument conversion to UTF-8
int args_to_utf8(int argc, char ** &argv) FL_OVERRIDE;
// Windows specific UTF-8 conversions
unsigned utf8towc(const char *src, unsigned srclen, wchar_t* dst, unsigned dstlen) FL_OVERRIDE; unsigned utf8towc(const char *src, unsigned srclen, wchar_t* dst, unsigned dstlen) FL_OVERRIDE;
unsigned utf8fromwc(char *dst, unsigned dstlen, const wchar_t* src, unsigned srclen) FL_OVERRIDE; unsigned utf8fromwc(char *dst, unsigned dstlen, const wchar_t* src, unsigned srclen) FL_OVERRIDE;
int utf8locale() FL_OVERRIDE; int utf8locale() FL_OVERRIDE;
unsigned utf8to_mb(const char *src, unsigned srclen, char *dst, unsigned dstlen) FL_OVERRIDE; unsigned utf8to_mb(const char *src, unsigned srclen, char *dst, unsigned dstlen) FL_OVERRIDE;
unsigned utf8from_mb(char *dst, unsigned dstlen, const char *src, unsigned srclen) FL_OVERRIDE; unsigned utf8from_mb(char *dst, unsigned dstlen, const char *src, unsigned srclen) FL_OVERRIDE;
int clocale_vprintf(FILE *output, const char *format, va_list args) FL_OVERRIDE; int clocale_vprintf(FILE *output, const char *format, va_list args) FL_OVERRIDE;
int clocale_vsnprintf(char *output, size_t output_size, const char *format, va_list args) FL_OVERRIDE; int clocale_vsnprintf(char *output, size_t output_size, const char *format, va_list args) FL_OVERRIDE;
int clocale_vsscanf(const char *input, const char *format, va_list args) FL_OVERRIDE; int clocale_vsscanf(const char *input, const char *format, va_list args) FL_OVERRIDE;

34
src/drivers/WinAPI/Fl_WinAPI_System_Driver.cxx

@ -299,6 +299,40 @@ int Fl_WinAPI_System_Driver::rename(const char *fnam, const char *newnam) {
return _wrename(wbuf, wbuf1); return _wrename(wbuf, wbuf1);
} }
// See Fl::args_to_utf8()
int Fl_WinAPI_System_Driver::args_to_utf8(int argc, char ** &argv) {
int i;
char strbuf[2048]; // FIXME: allocate argv and strings dynamically
// Convert the command line arguments to UTF-8
LPWSTR *wideArgv = CommandLineToArgvW(GetCommandLineW(), &argc);
argv = (char **)malloc((argc + 1) * sizeof(char *));
for (i = 0; i < argc; i++) {
int ret = WideCharToMultiByte(CP_UTF8, // CodePage
0, // dwFlags
wideArgv[i], // lpWideCharStr
-1, // cchWideChar
strbuf, // lpMultiByteStr
sizeof(strbuf), // cbMultiByte
NULL, // lpDefaultChar
NULL); // lpUsedDefaultChar
if (!ret)
strbuf[0] = '\0'; // return empty string
argv[i] = fl_strdup(strbuf);
}
argv[argc] = NULL; // required NULL pointer at end of list
// Free the wide character string array
LocalFree(wideArgv);
// Note: the allocated memory or argv[] will not be free'd by the system
// on exit. This does not constitute a memory leak.
return argc;
}
// Two Windows-specific functions fl_utf8_to_locale() and fl_locale_to_utf8() // Two Windows-specific functions fl_utf8_to_locale() and fl_locale_to_utf8()
// from file fl_utf8.cxx are put here for API compatibility // from file fl_utf8.cxx are put here for API compatibility

55
src/fl_call_main.c

@ -22,29 +22,40 @@
* "main()". This will allow you to build a Windows Application * "main()". This will allow you to build a Windows Application
* without any special settings. * without any special settings.
* *
* Because of problems with the Microsoft Visual C++ header files * You cannot have this WinMain() function in a DLL because it would have
* and/or compiler, you cannot have a WinMain function in a DLL. * to call \c main() outside the DLL. Thus, this nifty feature is only
* I don't know why. Thus, this nifty feature is only available * available if you link to the static library.
* if you link to the static library.
* *
* Currently the debug version of this library will create a * However, it is possible to build this module separately so you can
* console window for your application so you can put printf() * use it in progams that link to the shared library.
* statements for debugging or informational purposes. Ultimately *
* we want to update this to always use the parent's console, * Currently the debug version of this library will create a console window
* but at present we have not identified a function or API in * for your application so you can put printf() statements for debugging or
* Microsoft(r) Windows(r) that allows for it. * informational purposes. Ultimately we want to update this to always use
* the parent's console, but at present we have not identified a function
* or API in Microsoft(r) Windows(r) that allows for it.
*/ */
/* /*
* This file is compiled only on Windows platforms (since FLTK 1.4.0). * Notes for FLTK developers:
* Therefore we don't need to test the _WIN32 macro anymore. *
* The _MSC_VER macro is tested to compile it only for Visual Studio * 1) Since FLTK 1.4.0 this file is compiled only on Windows, hence we don't
* platforms because GNU platforms (MinGW, MSYS) don't need it. * need to test the _WIN32 macro.
* 2) This file must not call any FLTK library functions because this would
* not work with /both/ the DLL /and/ the static library (linkage stuff).
* 3) Converting the commandline arguments to UTF-8 is therefore implemented
* here *and* in the library but this seems to be an acceptable compromise.
* 4) (Unless someone finds a better solution, of course. Albrecht)
* 5) The condition "!defined(FL_DLL)" prevents building this in the shared
* library, i.e. "WinMain()" will not be defined in the shared lib (DLL).
* 6) The condition "!defined (__GNUC__)" prevents compilation of this
* module with MinGW, MSYS, and Cygwin which don't use WinMain().
* 7) It is unclear if there are other build systems on Windows that need a
* WinMain() entry point. Earlier comments and code seem to indicate that
* Borland C++ would require it.
*/ */
#if !defined(FL_DLL) && !defined (__GNUC__)
#include <FL/fl_utf8.h> #if !defined(FL_DLL) && !defined (__GNUC__)
#include <FL/fl_string_functions.h>
#include <windows.h> #include <windows.h>
#include <stdio.h> #include <stdio.h>
@ -78,9 +89,13 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
freopen("conout$", "w", stderr); freopen("conout$", "w", stderr);
#endif /* _DEBUG */ #endif /* _DEBUG */
/* Convert the command line arguments to UTF-8 */ /* Get the command line arguments as Windows Wide Character strings */
LPWSTR *wideArgv = CommandLineToArgvW(GetCommandLineW(), &argc); LPWSTR *wideArgv = CommandLineToArgvW(GetCommandLineW(), &argc);
/* Allocate an array of 'argc + 1' string pointers */
argv = (char **)malloc((argc + 1) * sizeof(char *)); argv = (char **)malloc((argc + 1) * sizeof(char *));
/* Convert the command line arguments to UTF-8 */
for (i = 0; i < argc; i++) { for (i = 0; i < argc; i++) {
int ret = WideCharToMultiByte(CP_UTF8, /* CodePage */ int ret = WideCharToMultiByte(CP_UTF8, /* CodePage */
0, /* dwFlags */ 0, /* dwFlags */
@ -90,7 +105,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
sizeof(strbuf), /* cbMultiByte */ sizeof(strbuf), /* cbMultiByte */
NULL, /* lpDefaultChar */ NULL, /* lpDefaultChar */
NULL); /* lpUsedDefaultChar */ NULL); /* lpUsedDefaultChar */
argv[i] = fl_strdup(strbuf); argv[i] = _strdup(strbuf);
} }
argv[argc] = NULL; // required by C standard at end of list argv[argc] = NULL; // required by C standard at end of list
@ -118,6 +133,8 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
} }
#else #else
/* STR# 2973: solves "empty translation unit" error */ /* STR# 2973: solves "empty translation unit" error */
typedef int dummy; typedef int dummy;
#endif /* !defined(FL_DLL) && !defined (__GNUC__) */ #endif /* !defined(FL_DLL) && !defined (__GNUC__) */

5
test/pixmap_browser.cxx

@ -1,7 +1,7 @@
// //
// A shared image test program for the Fast Light Tool Kit (FLTK). // A shared image test program for the Fast Light Tool Kit (FLTK).
// //
// Copyright 1998-2022 by Bill Spitzak and others. // Copyright 1998-2023 by Bill Spitzak and others.
// //
// This library is free software. Distribution and use rights are outlined in // This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this // the file "COPYING" which should have been included with this file. If this
@ -143,7 +143,8 @@ int main(int argc, char **argv) {
setlocale(LC_ALL, ""); // enable multilanguage errors in file chooser setlocale(LC_ALL, ""); // enable multilanguage errors in file chooser
fl_register_images(); fl_register_images();
Fl::args(argc,argv,i,arg); Fl::args_to_utf8(argc, argv); // enable multilanguage commandlines on Windows
Fl::args(argc, argv, i, arg); // parse commandline
if (animate) if (animate)
Fl_GIF_Image::animate = true; // create animated shared .GIF images (e.g. file chooser) Fl_GIF_Image::animate = true; // create animated shared .GIF images (e.g. file chooser)

Loading…
Cancel
Save