summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--test/CMakeLists.txt19
-rw-r--r--test/demo.cxx480
-rw-r--r--test/demo.menu2
3 files changed, 313 insertions, 188 deletions
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index f1265978c..b477d6647 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -15,8 +15,8 @@
#
#######################################################################
-set (EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}/../bin/examples)
-set (TESTFILE_PATH ${EXECUTABLE_OUTPUT_PATH})
+set (EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}/../bin)
+set (TESTFILE_PATH ${CMAKE_CURRENT_BINARY_DIR}/../data)
include (../CMake/FLTK-Functions.cmake)
include (../CMake/fl_create_example.cmake)
@@ -204,13 +204,11 @@ endif (NOT ANDROID)
# We need some support files for the demo programs:
-# Note: this is incomplete as of 11 Feb 2015
-# Todo: currently all files are copied, but some of them need configuration:
-# - demo.menu: fluid can't be started (wrong path)
+# Note: this is incomplete as of July 2020
+# Todo: currently all files are copied, but some of them may need configuration:
# - demo.menu: help_dialog (help_dialog.html) can't find its images (not copied)
-# - maybe more ...
-# create data directory for test and demo programs
+# create data directory for test and demo programs if it doesn't exist
file (MAKE_DIRECTORY ${TESTFILE_PATH})
# copy the test files
@@ -221,13 +219,16 @@ file (COPY
DESTINATION ${TESTFILE_PATH}
)
+# the main test program 'demo' needs additional hints and configurations
+
+target_compile_definitions (demo PRIVATE GENERATED_BY_CMAKE)
+target_compile_definitions (demo PRIVATE CMAKE_SOURCE_PATH="${CMAKE_CURRENT_SOURCE_DIR}")
+
# Apple macOS creates bundles instead of executables and needs a little bit
# more help for demos to run correctly
if (APPLE AND (NOT OPTION_APPLE_X11) AND (NOT OPTION_APPLE_SDL))
- target_compile_definitions (demo PUBLIC USING_XCODE)
-
# make the menu structure part of the app
target_sources (demo PRIVATE demo.menu)
set_target_properties (demo PROPERTIES MACOSX_BUNDLE TRUE RESOURCE demo.menu)
diff --git a/test/demo.cxx b/test/demo.cxx
index e7f9445e1..e02b81def 100644
--- a/test/demo.cxx
+++ b/test/demo.cxx
@@ -14,9 +14,58 @@
// https://www.fltk.org/bugs.php
//
+/*
+ General information on directory structure and file handling.
+
+ The "classic" autotools/make system creates executables in their source
+ folders, i.e. fluid/fluid, test/demo and test/xyz, resp.. The menu file is
+ in folder test/, as is the main demo(.exe) program. In the following text
+ and directory lists all test and demo executables are represented by "demo"
+ and the fluid executable by "fluid", no matter what OS (under Windows: *.exe).
+
+ The CMake build system generates all executables in the build tree and copies
+ the supporting test data files to the build tree as well. This structure is
+ different and needs to be handled separately in this program.
+
+ Additionally, different OS platforms create different types of files, for
+ instance "app bundles" on macOS. All this needs to be considered.
+
+ The overall structure, relative to the FLTK source dir (fltk) and the build
+ tree (build):
+
+ (1) Autotools / Make:
+
+ fltk/fluid fluid (../fluid/fluid)
+ fltk/test demo, demo.menu, working directory, data files
+ fltk/documentation/src images for help_dialog(.html)
+
+ (2) CMake + make (e.g. Unix)
+
+ build/bin fluid, demo
+ build/data demo.menu, working directory, data files
+
+ (3) CMake + Visual Studio (TYPE == build type: Debug, Release, ...)
+
+ build/bin/TYPE fluid, demo
+ build/data demo.menu, working directory, data files
+
+ (4) CMake + macOS + Xcode
+
+ *FIXME* special code to handle bundles: do we need this?
+
+ The built executable 'demo' can also be executed with the menu filename
+ as commandline argument. In this case all the support (data) files are
+ expected to be in the same directory as the menu file or relative paths
+ as needed by the test programs, for instance help_dialog which needs
+ help_dialog.html and related image files.
+*/
+
+#define DEBUG_VARS (1) // 1 = output variables to stderr, 0 = no
+
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
+#include <errno.h>
#if defined __APPLE__
#include <ApplicationServices/ApplicationServices.h>
@@ -29,21 +78,7 @@
#include <FL/Fl_Choice.H>
#include <FL/filename.H>
#include <FL/platform.H>
-
-/* Define a macro to decide whether a trailing 'd' needs to be removed
- from the executable file name. Previous versions of Visual Studio
- added a 'd' to the executable file name ('demod.exe') in Debug
- configurations that needed to be removed.
- This is no longer true with CMake-generated IDE's since FLTK 1.4.
- Just in case we add it again: leave macro DEBUG_EXE_WITH_D defined
- and leave the code using this macro as-is.
-*/
-
-// #if defined(_MSC_VER) && defined(_DEBUG) // Visual Studio in Debug mode
-// # define DEBUG_EXE_WITH_D 1
-// #else
-# define DEBUG_EXE_WITH_D 0
-// #endif
+#include <FL/fl_ask.H> // fl_alert()
/* The form description */
@@ -57,6 +92,40 @@ void doscheme(Fl_Choice *c, void *) {
Fl_Double_Window *form;
Fl_Button *but[9];
+// Allocate space to edit commands and arguments from demo.menu.
+// We "trust demo.menu" that strings don't overflow
+
+char cmdbuf[256]; // commandline w/o arguments
+char params[256]; // commandline arguments
+
+// Global path variables for all platforms and build systems
+// to avoid duplication and dynamic allocation
+
+char src_path [FL_PATH_MAX]; // directory of this souce file
+char app_path [FL_PATH_MAX]; // directory of all demo binaries
+char fluid_path [FL_PATH_MAX]; // binary directory of fluid
+char data_path [FL_PATH_MAX]; // working directory of all demos
+char command [2 * FL_PATH_MAX + 40]; // command to be executed
+
+// platform specific suffix for executable files
+
+#ifdef _WIN32
+const char *suffix = ".exe";
+#elif defined __APPLE__
+const char *suffix = ".app";
+#else
+const char *suffix = "";
+#endif
+
+// debug output function (to stderr or ...)
+
+void debug_var(const char *varname, const char *value) {
+#if (DEBUG_VARS)
+ fprintf(stderr, "%-10s = '%s'\n", varname, value);
+ fflush(stderr); // Windows needs this
+#endif // DEBUG_VARS
+}
+
void create_the_forms() {
Fl_Widget *obj;
form = new Fl_Double_Window(350, 440);
@@ -214,190 +283,145 @@ void dobut(Fl_Widget *, long arg) {
int men = find_menu(stack[stsize-1]);
int n = menus[men].numb;
int bn = but2numb( (int) arg, n-1);
+
+ // menu ?
+
if (menus[men].icommand[bn][0] == '@') {
push_menu(menus[men].icommand[bn]);
+ return;
+ }
+
+ // not a menu: run test/demo/fluid executable
+ // find and separate "command" and "params"
+
+ // skip leading spaces in command
+ char *start_command = menus[men].icommand[bn];
+ while (*start_command == ' ') ++start_command;
+
+ strcpy(cmdbuf, start_command); // here still full command w/params
+
+ // find the space between the command and parameters if one exists
+ char *start_params = strchr(cmdbuf, ' ');
+ if (start_params) {
+ *start_params = '\0'; // terminate command
+ start_params++; // skip space
+ strcpy(params, start_params); // copy parameters
} else {
+ params[0] = '\0'; // empty string
+ }
-#ifdef _WIN32
+ // select application path: either app_path or fluid_path
- STARTUPINFO suInfo; // Process startup information
- PROCESS_INFORMATION prInfo; // Process information
+ const char *path = app_path;
+ if (!strncmp(cmdbuf, "fluid", 5))
+ path = fluid_path;
-# if DEBUG_EXE_WITH_D
- const char *exe = "d.exe"; // exe name with trailing 'd'
-# else
- const char *exe = ".exe"; // exe name w/o trailing 'd'
-# endif
+ // format commandline with optional parameters
- memset(&suInfo, 0, sizeof(suInfo));
- suInfo.cb = sizeof(suInfo);
+#if defined(__APPLE__) // macOS
- int icommand_length = strlen(menus[men].icommand[bn]);
+ if (params[0]) {
+ // we assume that we have only one argument (which is a filename)
+ sprintf(command, "open '%s/%s%s' --args '%s'", path, cmdbuf, suffix, params);
+ } else {
+ sprintf(command, "open '%s/%s%s'", path, cmdbuf, suffix);
+ }
- char* copy_of_icommand = new char[icommand_length+1];
- strcpy(copy_of_icommand,menus[men].icommand[bn]);
+#else // other platforms
- // On Windows the .exe suffix needs to be appended to the command
- // whilst leaving any additional parameters unchanged - this
- // is required to handle the correct conversion of cases such as :
- // `../fluid/fluid valuators.fl' to '../fluid/fluid.exe valuators.fl'.
+ if (params[0])
+ sprintf(command, "%s/%s%s %s", path, cmdbuf, suffix, params);
+ else
+ sprintf(command, "%s/%s%s", path, cmdbuf, suffix);
- // skip leading spaces.
- char* start_command = copy_of_icommand;
- while (*start_command == ' ') ++start_command;
+#endif
- // find the space between the command and parameters if one exists.
- char* start_parameters = strchr(start_command,' ');
+ // finally, execute program (the system specific part)
- char* command = new char[icommand_length+6]; // 6 for extra 'd.exe\0'
+#ifdef _WIN32
- if (start_parameters==NULL) { // no parameters required.
- sprintf(command, "%s%s", start_command, exe);
- } else { // parameters required.
- // break the start_command at the intermediate space between
- // start_command and start_parameters.
- *start_parameters = 0;
- // move start_paremeters to skip over the intermediate space.
- ++start_parameters;
+ STARTUPINFO suInfo; // Process startup information
+ PROCESS_INFORMATION prInfo; // Process information
- sprintf(command, "%s%s %s", start_command, exe, start_parameters);
- }
+ memset(&suInfo, 0, sizeof(suInfo));
+ suInfo.cb = sizeof(suInfo);
- CreateProcess(NULL, command, NULL, NULL, FALSE,
- NORMAL_PRIORITY_CLASS, NULL, NULL, &suInfo, &prInfo);
+ debug_var("Command", command);
- delete[] command;
- delete[] copy_of_icommand;
+ BOOL stat = CreateProcess(NULL, command, NULL, NULL, FALSE,
+ NORMAL_PRIORITY_CLASS, NULL,
+ NULL, &suInfo, &prInfo);
+ if (!stat) {
+ DWORD err = GetLastError();
+ fl_alert("Error starting process, error #%d\n'%s'", err, command);
+ }
#elif defined __APPLE__
- /*
- Starting with version 1.4.0, FLTK uses CMake as the only supported build
- system. On macOS, the app developer is expected to run CMake in a
- directory named './build/Xcode' or './build/Makefiles' to generate the
- build environment.
-
- When building FLTK in the next step, teh macOS app bundles are then
- stored in either:
- './build/Xcode/bin/examples/hello.app/' for Makefiles
- './build/Xcode/bin/examples/Debug/hello.app/' for XCode Debug
- or
- './build/Xcode/bin/examples/Release/hello.app/' as a symbolic
- into the Archive system of macOS
-
- 'Demo' needs to find and run all of these app bundles, some requiring
- an additional file name and path for resource files.
-
- This is my attempt to find the bundles and resources so that Demo.app
- and all its dependencies will run without any further configuration.
- They will stop running however if any of the bundles or rresources
- are moved.
- */
- {
- char src_path[PATH_MAX];
- char app_path[PATH_MAX];
- char app_name[PATH_MAX];
- char command[2*PATH_MAX+2];
- char *cmd = strdup(menus[men].icommand[bn]);
- char *args = strchr(cmd, ' ');
-
- /*
- Get the path to the source code at compile time. This is where the other
- resources are located.
- */
- strcpy(src_path, __FILE__);
- char *src_path_end = (char*)fl_filename_name(src_path);
- if (src_path_end) *src_path_end = 0;
-
- /*
- All example app bundles are in the same directory as 'Demo', so set the
- current dir to the location of Demo.app .
-
- Starting with macOS 10.12, the actual location of the app has a randomized
- path to fix a vulnerability. This still works in Debug mode which is
- */
- {
- app_path[0] = 0;
- CFBundleRef app = CFBundleGetMainBundle();
- CFURLRef url = CFBundleCopyBundleURL(app);
- CFStringRef cc_app_path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
- CFRelease(url);
- CFStringGetCString(cc_app_path, app_path, 2048, kCFStringEncodingUTF8);
- CFRelease(cc_app_path);
- if (app_path[0]) {
- char *app_path_end = (char*)fl_filename_name(app_path);
- if (app_path_end) *app_path_end = 0;
- fl_chdir(app_path);
- }
- }
-
- // extract the executable name from the command in the menu file
- strcpy(app_name, cmd);
- // remove any additioanl command line arguments
- if (args) app_name[args-cmd] = 0;
- // make the file name into a bundle name
- strcat(app_name, ".app");
-
- if (args) {
- if (strcmp(app_name, "../fluid/fluid.app")==0) {
- // CMake -G 'Unix Makefiles' ... : ./bin/fluid.app
- // CMake -G 'Xcode' ... : ./bin/Debug/fluid.app or ./bin/Release/fluid.app
- // so removing the '/example' path segment from the app_path should
- // always work.
- char *examples = strstr(app_path, "/examples");
- if (examples) {
- memmove(examples, examples+9, strlen(examples+9)+1);
- }
- sprintf(command, "open '%sfluid.app' --args '%s%s'", app_path, src_path, args+1);
- } else {
- // we assume that we have only one argument which is a filename, so we add a path
- sprintf(command, "open '%s%s' --args '%s%s'", app_path, app_name, src_path, args+1);
- }
- } else {
- sprintf(command, "open '%s%s'", app_path, app_name);
- }
- system(command);
- free(cmd);
- }
-#else // Non Windows systems.
+ debug_var("Command", command);
+ system(command);
- int icommand_length = strlen(menus[men].icommand[bn]);
- char* command = new char[icommand_length+5]; // 5 for extra './' and ' &\0'
- sprintf(command, "./%s &", menus[men].icommand[bn]);
- if (system(command)==-1) { /* ignore */ }
+#else // other platforms (Unix, Linux)
- delete[] command;
+ strcat(command, " &"); // run in background
+ debug_var("Command", command);
-#endif // _WIN32
+ if (system(command) == -1) {
+ fl_alert("Could not start program, errno = %d\n'%s'", errno, command);
}
+
+#endif // _WIN32
+
}
void doback(Fl_Widget *, void *) {pop_menu();}
void doexit(Fl_Widget *, void *) {exit(0);}
-/* Load the menu file. Returns whether successful. */
-int load_the_menu(char* fname) {
+/*
+ Load the menu file. Returns whether successful.
+
+ New strategy: the menu file *should* be usable as is!
+
+ Old strategy was:
+
+ We have different situations:
+
+ (1) Visual Studio: see main(): nothing to do
+ (2) Standard (autotools/make): nothing to do
+ (3) CMake: see main(): nothing to do
+
+ (4) macOS: ??? *FIXME*
+*/
+int load_the_menu(char *menu) {
FILE *fin = 0;
char line[256], mname[64],iname[64],cname[64];
int i, j;
- fin = fl_fopen(fname,"r");
-#if defined ( __APPLE__ )
+
+ fin = fl_fopen(menu, "r");
+
+// // *FIXME* Albrecht: Do we need this? I don't think so!
+#if (0) // *FIXME* disabled for testing
+
+#if defined (__APPLE__)
if (fin == NULL) {
- // mac os bundle menu detection:
- char* pos = strrchr(fname,'/');
+ // macOS bundle menu detection:
+ char *pos = strrchr(menu, '/');
if (!pos) return 0;
*pos = '\0';
- pos = strrchr(fname,'/');
+ pos = strrchr(menu, '/');
if (!pos) return 0;
- strcpy(pos,"/Resources/demo.menu");
- fin = fl_fopen(fname,"r");
+ strcpy(pos, "/Resources/demo.menu");
+ fin = fl_fopen(menu, "r");
}
-#endif
- if (fin == NULL) {
+#endif // __APPLE __
+
+#endif // *FIXME* disabled for testing
+
+ if (fin == NULL)
return 0;
- }
+
for (;;) {
if (fgets(line,256,fin) == NULL) break;
// remove all carriage returns that Cygwin may have inserted
@@ -436,32 +460,132 @@ int load_the_menu(char* fname) {
return 1;
}
+// Fix '\' in Windows paths (convert to '/') and cut off filename (optional, default)
+void fix_path(char *path, int strip_filename = 1) {
+ if (!path[0])
+ return;
+#ifdef _WIN32 // convert '\' to '/'
+ char *p = path;
+ while (*p) {
+ if (*p == '\\')
+ *p = '/';
+ p++;
+ }
+#endif // _WIN32
+ if (strip_filename) {
+ char *pos = strrchr(path, '/');
+ if (pos)
+ *pos = 0;
+ }
+}
+
int main(int argc, char **argv) {
- fl_putenv("FLTK_DOCDIR=../documentation/html");
- char buf[FL_PATH_MAX];
- strcpy(buf, argv[0]);
-#if DEBUG_EXE_WITH_D
- // MS_Visual Studio appends a 'd' to debugging executables. Remove it.
- fl_filename_setext( buf, "" );
- buf[ strlen(buf)-1 ] = 0;
+ fl_putenv("FLTK_DOCDIR=../documentation/html"); // *FIXME*
+
+ char menu[FL_PATH_MAX];
+
+ // construct app_path for all executable files
+
+#ifdef __APPLE__
+ {
+ // Starting with macOS 10.12, the actual location of the app has a randomized
+ // path to fix a vulnerability.
+ // We need some "Apple magic" ;-) to find the actual path.
+ // Albrecht: is this (still) true and necessary?
+
+ app_path[0] = 0;
+ CFBundleRef app = CFBundleGetMainBundle();
+ CFURLRef url = CFBundleCopyBundleURL(app);
+ CFStringRef cc_app_path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
+ CFRelease(url);
+ CFStringGetCString(cc_app_path, app_path, 2048, kCFStringEncodingUTF8);
+ CFRelease(cc_app_path);
+ }
+#else
+ fl_filename_absolute(app_path, sizeof(app_path), argv[0]);
#endif
- fl_filename_setext(buf,".menu");
- char *fname = buf;
+ fix_path(app_path);
+
+ // construct src_path in case we want to use it (macOS ?)
+
+#if defined(GENERATED_BY_CMAKE)
+ strcpy(src_path, CMAKE_SOURCE_PATH);
+#else
+ strcpy(src_path, app_path);
+#endif
+ fix_path(src_path, 0);
+
+ // fluid's path is the same for CMake builds but not for autoconf/make
+
+ strcpy(fluid_path, app_path);
+
+#if !defined(GENERATED_BY_CMAKE)
+ fix_path(fluid_path); // removes folder name (test)
+ strcat(fluid_path, "/fluid");
+#endif
+
+ // construct data_path for the menu file and all resources (data files)
+ // CMake: replace "/bin/*"" with "/data"
+ // autotools: use app_path directly
+
+ strcpy(data_path, app_path);
+
+#if defined(GENERATED_BY_CMAKE)
+ {
+ char *pos = strstr(data_path, "/bin");
+ if (pos)
+ strcpy(pos, "/data");
+ }
+#endif
+
+ // construct the menu file name, optionally overridden by command args
+ // CMake: use data_path instead of app_path
+
+ const char *fn = fl_filename_name(argv[0]);
+
+#if defined(GENERATED_BY_CMAKE)
+ strcpy(menu, data_path);
+#else
+ strcpy(menu, app_path);
+#endif
+
+ // append "/<exe-file-name>.menu"
+ strcat(menu, "/");
+ strcat(menu, fn);
+ fl_filename_setext(menu, sizeof(menu), ".menu");
+
+ // parse commandline
+
int i = 0;
if (!Fl::args(argc,argv,i) || i < argc-1)
- Fl::fatal("Usage: %s <switches> <menufile>\n%s",argv[0],Fl::help);
- if (i < argc) fname = argv[i];
+ Fl::fatal("Usage: %s <switches> <menufile>\n%s", argv[0], Fl::help);
+ if (i < argc) {
+ // override menu file *and* data path !
+ fl_filename_absolute(menu, sizeof(menu), (const char *)argv[i]);
+ strcpy(data_path, menu);
+ fix_path(data_path);
+ }
+
+#if (DEBUG_VARS)
+ fprintf(stderr, "\n");
+ debug_var("src_path", src_path);
+ debug_var("app_path", app_path);
+ debug_var("fluid_path", fluid_path);
+ debug_var("data_path", data_path);
+ debug_var("Menu file", menu);
+ fprintf(stderr, "\n");
+#endif // DEBUG_VARS
create_the_forms();
- if (!load_the_menu(fname)) Fl::fatal("Can't open %s",fname);
- if (buf != fname)
- strcpy(buf,fname);
- const char *c = fl_filename_name(buf);
- if (c > buf) {
- buf[c-buf] = 0;
- if (fl_chdir(buf) == -1) { /* ignore */ }
- }
+ // note: load_the_menu() *may* change the `menu` buffer contents !
+ if (!load_the_menu(menu))
+ Fl::fatal("Can't open %s", menu);
+
+ // set current work directory to 'app_path'
+
+ if (fl_chdir(data_path) == -1) { /* ignore */ }
+
push_menu("@main");
form->show(argc,argv);
Fl::run();
diff --git a/test/demo.menu b/test/demo.menu
index 2eedfb772..8b325cca6 100644
--- a/test/demo.menu
+++ b/test/demo.menu
@@ -58,7 +58,7 @@
@u:fast && slow widgets:fast_slow
@u:inactive:inactive
-@main:Fluid\n(UI design tool):../fluid/fluid valuators.fl
+@main:Fluid\n(UI design tool):fluid valuators.fl
@main:Cool\nDemos...:@e
@e:X Color\nBrowser:colbrowser