Building Euler
Though Euler needs a C++ compiler, it is a C program in its core and uses global functions and variables instead of classes. Classes are used in the GUI. The Windows API is all that is needed for the user interface and the graphics. The compiler needs GDI+.
There kernel is separated from the GUI, but it will rely on system functions, which must be implemented for an own GUI. The GUI starts a separate thread for the interpreter. For editing of command lines, this thread is halted.
The most recent version was compiled with Visual Studio 2012 Professional. But the same sources should compile with Visual C++ 2010 Express. The following description refers to Visual Studio 2012 in some details.
Compilation
To make a project, create a new empty project for Win32 named "euler".
General Settings
In the General tab, set Character Set to "Not Set". Euler currently is not Unicode aware.
You may also wish to bind statically. This avoids having to install the Visual C++ DLL on the target machine. To achieve this, go to "Code Generation" and change the "Runtime Library" to the static one (switch /MT).
C/C++
Add
WIN32 FLOAT_TEST DLL _WINDOWS _CRT_SECURE_NO_DEPRECATE YACAS CATCH PYTHON
to the preprocessor definitions. For the 64bit project, add WIN64.
On the Code Generation tab, set "Enable Floating Point Exceptions". This allows Euler to catch divisions by 0 etc.
Linker
Go to the *Linker* tab and add
kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib Shlwapi.lib advapi32.lib shell32.lib uuid.lib comctl32.lib winmm.lib gdiplus.lib wininet.lib
I redirect the output in the General Tab to my working directory of Euler. Don't forget the the trailing \\ in the path.
Files
Add the following files to the project.
- *.cpp and *.h in source
- *.cpp, *.h, and *.res in windows
- *.cpp, *.h in string
- *.cpp, *.h in AlgLib
Do not add files from pipes or other directories. You can organize the files in filters, if you like.
Run
Compile with F7. Copy "euler.exe" from the Release directory to the directory, where "euler.cfg" is, unless you changed the output directory. You need the sub-directory "util" there.
The program will not start Maxima, if it is not present. If you have Euler installed, you can use its Maxima. Configure it with a path in the settings. Or you can use another version of Maxima.
Compile the Python DLL
You can simply copy py27.dll from an installation of Euler. The file is installed in the Windows directory. Make sure, you have the correct version for 32bit or 64bit. But you can also compile this DLL yourself. You need to install the correct Version of Python 2.7 for this.
Create a new Win32 project named *py27*, and select "DLL" as the target. The target appears in the Configuration Type field in the General tab of the properties.
In the compiler settings, add "\Python27\include" as "Additional Include Directories". This assumes that you installed Python 2.7 in the root directory of the same drive, your project uses.
To the linker input, add "\Python27\libs\python27.lib".
Copy the "py.dll" file to your Euler directory.
Compile the LPSOLVE DLL
Again, you can copy eulpsolve.dll and lpsolve.dll, or you can compile eulpsolve.dll yourself, and copy lpsolve.dll from the home page of LPSOLVE.
The compilation is similar to the compilation of py27.dll
Porting to Other Systems
In the following sections I explain the various ways the core system communicates with the outside world. There are some things it needs, like command input and output. Other things are recommended, like the graphics output, or support for Maxima. There are some dispensable things too, like asynchronous keyboard input or playing sound files.
The core files are in the root directory of the source directory. The Windows files are in the windows sub-directory.
There is not even a guarantee, that the core system can be compiled on the target system at all. You may have to change some things, like colliding function names, or system constants. If you do so, by all means use a macro switch, or contact me to make the changes in the Windows version too, if possible.
Here are the various things, you can provide. Details are discussed in the following chapters.
- line editing for command input,
- text output,
- synchronous timed keyboard input for wait(),
- asynchronous keyboard for testkey(),
- graphics output,
- synchronous mouse input for mouse(),
- graphics pages,
- anaglyph plots,
- playing sounds in wave format,
- notebook support.
A basic start for a port would be the main file in pipes, which should be included in your source distribution.
To compile the core files for Linux, I used the following command line in the sources directory. I got no more warnings or errors.
g++ -c fno-opertor-names -Wno-write-strings -Wformat=0 *.cpp
The pipes/pipes.cpp compiles on Windows, but will have to be adapted for Linux.
Line Editor
Obviously, Euler needs some way to read commands from the user, or from a stream. The function to provide is
void edit (char *s);
declared in "sysdep.h" in the core directory. The maximal size of the provided space for the string s is MAXLINES bytes (including the trailing 0). You need to check for overflow.
The Windows version uses an own thread for the core computations, and another thread for the interface. The user keyboard and mouse input is asynchronous to the Euler thread, which carries out the computations. The way to handle this is to stop the thread inside the edit() function, and to resume it, once the editing is complete. It can be handled with a wait command for a thread lock, or by simply stopping and resuming the thread.
This design makes it necessary to know the state the system is in, editing or computing. So the interface can reject keyboard commands, if the computation is active. On the other hand, the user can abort the program at any time.
To see how this is done, look into "main.cpp" for the edit() function. The key presses in the text window are handled by editkey() in "edit.cpp" however.
A simple implementation would start the core in a process, and communicate via pipes in some line oriented, buffered way.
The Windows version supports multi-line commands, as in
>1+2+3+4+5+6+7+8+8+9+ ... >10 63 >function f(x) ... $if x>0 then return x $else return x^2 $endif $endfunction >
The first two lines would be executed, even if return was in the second line, and the function would be read completely, if return was is pressed in the function line.
Multi-lines should be implemented by the edit() function to guarantee compatibility with Windows notebooks. They are not necessary for the core files, however. To implement this feature, feed the lines one by one in subsequent calls of edit without user interaction. The triple dots must need not be removed. Otherwise, Euler will be able to parse the interrupted lines. Of course, you can also connect the lines, and send them as one long line. This also applies to function definitions.
Moreover, the Windows version provides a help function. If the user presses the help key (which is the insert key on most system), Euler will complete the command at the cursor position. Each press completes the command in another way. The status line in the Windows interface, will display the possible ways. Use the function
void gethelp (char *start, char *extend);
defined in "help.cpp" to get the status line. The string start contains the command to complete. You should go back from the current cursor position until you find the start of the command, and copy from here to the cursor somewhere. Then invoke gethelp(), providing at least 1024 bytes for the result.
It is not easy to do the replacement with the help key. Have a look into the "edit.cpp" file of Windows to see, how this can be done.
Keyboard Input
There is asynchronous and synchronous keyboard input. The synchronous version is started with the functions
int wait_key (int *scan); int wait_key (int *scan, char *statustext);
declared in "sysdep.h". The first should return a key code, and parse an optional scan code in the polnter variable. The second version is for notebook support. On simple systems, it should be implemented like the first. On notebook systems, it can can display a help text in the status line.
Of course, the key codes to return are characters. Euler uses characters from 32 (blank) on, omitting the escape key (127). Currently, the Euler core has no Unicode support. The scan should handle any of the following constants, declared in "sysdep.h". This is used for tracing Euler functions. The key() command of Euler only uses the code. The other command using wait_key() is shg, which shows the graphics screen, until a key is pressed.
typedef enum { key_none, cursor_up, cursor_down, cursor_left, cursor_right, escape, deletekey, backspace, clear_home, switch_screen, enter, space, line_end, line_start, fk1, fk2, fk3, fk4, fk5, fk6, fk7, fk8, fk9, fk10, word_left, word_right, help } scantyp;
There are two more functions. Both wait for a maximal number of seconds. If the times runs off, or the use presses a key, these commands will return. The wait(delay) command of Euler uses these functions. The second version is for notebook support.
void sys_wait (double delay, int *scan); void sys_wait (double delay, int *scan, char *text);