summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--FL/Fl.H3
-rw-r--r--examples/howto-parse-args.cxx27
-rw-r--r--src/CMakeLists.txt9
-rw-r--r--src/Fl.cxx73
-rw-r--r--src/Fl_System_Driver.H10
-rw-r--r--src/drivers/WinAPI/Fl_WinAPI_System_Driver.H4
-rw-r--r--src/drivers/WinAPI/Fl_WinAPI_System_Driver.cxx34
-rw-r--r--src/fl_call_main.c55
-rw-r--r--test/pixmap_browser.cxx5
9 files changed, 189 insertions, 31 deletions
diff --git a/FL/Fl.H b/FL/Fl.H
index b52c33c72..3757b65be 100644
--- a/FL/Fl.H
+++ b/FL/Fl.H
@@ -1398,6 +1398,9 @@ public:
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
/** \defgroup group_cairo Cairo Support Functions and Classes
@{
diff --git a/examples/howto-parse-args.cxx b/examples/howto-parse-args.cxx
index 393344128..ef756d09b 100644
--- a/examples/howto-parse-args.cxx
+++ b/examples/howto-parse-args.cxx
@@ -11,7 +11,16 @@
// usual *nix idiom of "option=value", and provides no validation nor
// 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
// 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 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) {
helpFlag = 1;
i += 1;
return 1;
}
+
if (strcmp("-o", argv[i]) == 0 || strcmp("--option", argv[i]) == 0) {
if (i < argc-1 && argv[i+1] != 0) {
optionString = argv[i+1];
@@ -59,8 +69,13 @@ int arg(int argc, char **argv, int &i)
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;
if (Fl::args(argc, argv, i, arg) < argc)
// note the concatenated strings to give a single format string!
@@ -84,6 +99,8 @@ int main(int argc, char** argv)
textBox->label(optionString);
else
textBox->label("re-run with [-o|--option] text");
+
+ mainWin->resizable(mainWin);
mainWin->show(argc, argv);
return Fl::run();
}
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 393e06f6e..a32319889 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -578,9 +578,14 @@ list (APPEND SHARED_FILES ${HEADER_FILES} ${DRIVER_HEADER_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)
-endif (MSVC)
+endif ()
#######################################################################
diff --git a/src/Fl.cxx b/src/Fl.cxx
index 105bc018b..013d4dee1 100644
--- a/src/Fl.cxx
+++ b/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_alt = Fl::system_driver()->alt_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);
+}
diff --git a/src/Fl_System_Driver.H b/src/Fl_System_Driver.H
index e7230f74e..3dad3d754 100644
--- a/src/Fl_System_Driver.H
+++ b/src/Fl_System_Driver.H
@@ -110,6 +110,10 @@ public:
virtual int rmdir(const char*) {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
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);
@@ -124,9 +128,9 @@ public:
virtual int filename_list(const char * /*d*/, dirent ***,
int (* /*sort*/)(struct dirent **, struct dirent **),
char *errmsg=NULL, int errmsg_sz=0) {
- (void)errmsg; (void)errmsg_sz;
- return -1;
- }
+ (void)errmsg; (void)errmsg_sz;
+ return -1;
+ }
// the default implementation of filename_expand() may be enough
virtual int filename_expand(char *to, int tolen, const char *from);
// to implement
diff --git a/src/drivers/WinAPI/Fl_WinAPI_System_Driver.H b/src/drivers/WinAPI/Fl_WinAPI_System_Driver.H
index aae1017e2..016cad9fa 100644
--- a/src/drivers/WinAPI/Fl_WinAPI_System_Driver.H
+++ b/src/drivers/WinAPI/Fl_WinAPI_System_Driver.H
@@ -61,11 +61,15 @@ public:
int mkdir(const char *fnam, int mode) FL_OVERRIDE;
int rmdir(const char *fnam) 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 utf8fromwc(char *dst, unsigned dstlen, const wchar_t* src, unsigned srclen) FL_OVERRIDE;
int utf8locale() 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;
+
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_vsscanf(const char *input, const char *format, va_list args) FL_OVERRIDE;
diff --git a/src/drivers/WinAPI/Fl_WinAPI_System_Driver.cxx b/src/drivers/WinAPI/Fl_WinAPI_System_Driver.cxx
index faf89e979..d7b01465a 100644
--- a/src/drivers/WinAPI/Fl_WinAPI_System_Driver.cxx
+++ b/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);
}
+// 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()
// from file fl_utf8.cxx are put here for API compatibility
diff --git a/src/fl_call_main.c b/src/fl_call_main.c
index 025e7f03c..de05b98d4 100644
--- a/src/fl_call_main.c
+++ b/src/fl_call_main.c
@@ -22,29 +22,40 @@
* "main()". This will allow you to build a Windows Application
* without any special settings.
*
- * Because of problems with the Microsoft Visual C++ header files
- * and/or compiler, you cannot have a WinMain function in a DLL.
- * I don't know why. Thus, this nifty feature is only available
- * if you link to the static library.
+ * You cannot have this WinMain() function in a DLL because it would have
+ * to call \c main() outside the DLL. Thus, this nifty feature is only
+ * available if you link to the static library.
*
- * Currently the debug version of this library will create a
- * console window for your application so you can put printf()
- * statements for debugging or 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.
+ * However, it is possible to build this module separately so you can
+ * use it in progams that link to the shared library.
+ *
+ * Currently the debug version of this library will create a console window
+ * for your application so you can put printf() statements for debugging or
+ * 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).
- * Therefore we don't need to test the _WIN32 macro anymore.
- * The _MSC_VER macro is tested to compile it only for Visual Studio
- * platforms because GNU platforms (MinGW, MSYS) don't need it.
+ * Notes for FLTK developers:
+ *
+ * 1) Since FLTK 1.4.0 this file is compiled only on Windows, hence we don't
+ * 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>
-#include <FL/fl_string_functions.h>
+#if !defined(FL_DLL) && !defined (__GNUC__)
#include <windows.h>
#include <stdio.h>
@@ -78,9 +89,13 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
freopen("conout$", "w", stderr);
#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);
+
+ /* Allocate an array of 'argc + 1' string pointers */
argv = (char **)malloc((argc + 1) * sizeof(char *));
+
+ /* Convert the command line arguments to UTF-8 */
for (i = 0; i < argc; i++) {
int ret = WideCharToMultiByte(CP_UTF8, /* CodePage */
0, /* dwFlags */
@@ -90,7 +105,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
sizeof(strbuf), /* cbMultiByte */
NULL, /* lpDefaultChar */
NULL); /* lpUsedDefaultChar */
- argv[i] = fl_strdup(strbuf);
+ argv[i] = _strdup(strbuf);
}
argv[argc] = NULL; // required by C standard at end of list
@@ -118,6 +133,8 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
}
#else
+
/* STR# 2973: solves "empty translation unit" error */
typedef int dummy;
+
#endif /* !defined(FL_DLL) && !defined (__GNUC__) */
diff --git a/test/pixmap_browser.cxx b/test/pixmap_browser.cxx
index dca13316e..9478540cd 100644
--- a/test/pixmap_browser.cxx
+++ b/test/pixmap_browser.cxx
@@ -1,7 +1,7 @@
//
// 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
// 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
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)
Fl_GIF_Image::animate = true; // create animated shared .GIF images (e.g. file chooser)