Platform-Specific Issues for Euphoria


1. Introduction

Rapid Deployment Software currently supports Euphoria on four different platforms. More platforms will be added in the future.

The first platform is called DOS32, since it depends on the DOS operating system, but with the CPU operating in 32-bit (protected) mode.

The second platform is called WIN32, since the underlying operating system is Microsoft Windows, in particular, the 32-bit version of Windows that is used on Windows 95/98/ME, as well as NT/2000/XP and later systems.

The third platform is Linux. Linux is based on the UNIX operating system. It has recently become very popular on PCs. There are many distributors of Linux, including Red Hat, Debian, Caldera, etc. Linux can be obtained on a CD for a very low price. Linux is an open-source operating system.

The fourth platform is FreeBSD. FreeBSD is also based on the UNIX operating system. It is very popular on Internet server machines. It's also open source.

Users who have purchased the Euphoria source code have ported Euphoria to other platforms, such as HP Unix and Sun Unix.

The Euphoria for DOS32+WIN32 installation file contains two .exe files. The first is called ex.exe. It runs Euphoria programs on the DOS32 platform. The second is exw.exe. It runs Euphoria programs on the WIN32 platform. Euphoria programs that are meant to be run on the WIN32 platform have a .exw file type, while programs that are meant to be run on the DOS32 platform have a .ex file type.

The Euphoria for Linux .tar file contains only exu. It runs Euphoria programs on the Linux platform. Euphoria programs intended for Linux or FreeBSD have a .exu file type.

The FreeBSD version of Euphoria is installed by first installing the Linux version of Euphoria, and then replacing exu, by the version of exu for FreeBSD.

Many Euphoria programs can be run on two, three, or all four platforms without change. The file type should indicate the preferred platform for the program. Any Euphoria interpreter can try to run any Euphoria file, but you might have to specify the full name of the file, including the type, since each interpreter looks for it's own preferred file type (.ex, .exw or .exu).

Sometimes you'll find that the majority of your code will be the same on all platforms, but some small parts will have to be written differently for each platform. Use the platform() built-in function to tell you which platform you are currently running on. Note that platform() returns the same value (3) on both Linux and FreeBSD, since those systems are so similar.


2. The DOS32 Platform

If you are new to programming, you might want to start with ex.exe on the DOS32 platform. You should try to understand the essential core of Euphoria, before you leap into Windows GUI programming. All versions of Windows (even XP), let you open a Command Prompt text window, and run DOS programs.

Euphoria programs run in 32-bit (protected) mode and have access to all of the megabytes of memory on the machine. Many programming languages for DOS limit you to 16-bit real mode. This makes it impossible to access more than 640K of memory at one time. Your machine might have 256Mb of memory, but your program will run out of memory after using just 640K. QBasic is even worse. It limits you to just 160K.

DOS32 programs can flip the screen into either text mode or pixel graphics mode, and Euphoria provides you with library routines for both modes. There is rarely any need to call DOS directly, but you can do this using the dos_interrupt() routine. You can also peek and poke into special memory locations to achieve high-speed graphics and get access to low-level details of the system.

Under DOS32 for Windows 95 and later systems, Euphoria files can have long filenames, and programs can open long filename files for reading and writing, but not for creating a new file.

Under pure DOS, outside of Windows, there is no system swap file so the DOS-extender built in to ex.exe will create one for possible use by your program. This file is created when your Euphoria program starts up under DOS, and is deleted when your program terminates. It starts as a 0-byte file and grows only if actual swapping is needed. It is created in the directory on your hard disk pointed to by the TEMP or TMP environment variable. If neither of these variables have been set, it is created in the directory containing either ex.exe or your bound Euphoria .exe file. You can force it to be created in a particular directory by setting the CAUSEWAY environment variable as follows:

        SET CAUSEWAY=SWAP:path
where   path   is the full path to the directory. You can prevent the creation of a DOS swap file with:
        SET CAUSEWAY=NOVM

When disk swapping activity occurs, your program will run correctly but will slow down. A better approach might be to free up more extended memory by cutting back on SMARTDRV and other programs that reserve large amounts of extended memory for themselves.

When your free disk space is less than the amount of RAM in your machine, no swap file will be created.


3. The WIN32 Platform

Euphoria for WIN32 (exw.exe) has a lot in common with Euphoria for DOS32. With WIN32 you also have access to all of the memory on your machine. Most library routines work the same way on each platform. Many existing DOS32 text mode programs can be run using exw without any change. With exw you can run programs from the command line, and display text on a standard (typically 25 line x 80 column) DOS window. The DOS window is known as the console in Windows terminology. Euphoria makes the transition from DOS32 text mode programming, to WIN32 console programming, trivial. You can add calls to WIN32 C functions and later, if desired, you can create real Windows GUI windows.

A console window will be created automatically when a WIN32 Euphoria program first outputs something to the screen or reads from the keyboard. You will also see a console window when you read standard input or write to standard output, even when these have been redirected to files. The console will disappear when your program finishes execution, or via a call to free_console(). If there is something on the console that you want your user to read, you should prompt him and wait for his input before terminating. To prevent the console from quickly disappearing you might include a statement such as:

        if getc(0) then
        end if
which will wait for the user enter something.

If you want to run Euphoria programs without popping up a new console window use exwc.exe. It uses the current console window, just like a DOS program would when using ex.exe.

Under WIN32, long filenames are fully supported for reading and writing and creating.


3.1 High-Level WIN32 Programming

Thanks to David Cuny, Derek Parnell, Judith Evans and many others, there's a package called Win32Lib that you can use to develop Windows GUI applications in Euphoria. It's remarkably easy to learn and use, and comes with good documentation and many small example programs. You can download Win32Lib and Judith's IDE from the Euphoria Web site. Recently, Andrea Cini has developed a similar, somewhat smaller package called EuWinGUI. It's also available from our site.


3.2 Low-Level WIN32 Programming

To allow access to WIN32 at a lower level, Euphoria provides a mechanism for calling any C function in any WIN32 API .dll file, or indeed in any 32-bit Windows .dll file that you create or someone else creates. There is also a call-back mechanism that lets Windows call your Euphoria routines. Call-backs are necessary when you create a graphical user interface.

To make full use of the WIN32 platform, you need documentation on 32-bit Windows programming, in particular the WIN32 Application Program Interface (API), including the C structures defined by the API. There is a large WIN32.HLP file (c) Microsoft that is available with many programming tools for Windows. There are numerous books available on the subject of WIN32 programming for C/C++. You can adapt most of what you find in those books to the world of Euphoria programming for WIN32. A good book is:

Programming Windows
by Charles Petzold
Microsoft Press

A WIN32 API Windows help file (8 Mb) can be downloaded from Borland's Web site:
        ftp://ftp.borland.com/pub/delphi/techpubs/delphi2/win32.zip

See also the Euphoria Archive Web page - "documentation".


4. The Linux and FreeBSD Platforms

Euphoria for Linux, and Euphoria for FreeBSD share certain features with Euphoria for DOS32, and share other features with Euphoria for WIN32.

As with WIN32 and DOS32, you can write text on a console, or xterm window, in multiple colors and at any line or column position.

Just as in WIN32, you can call C routines in shared libraries and C code can call back to your Euphoria routines.

Euphoria for Linux and FreeBSD do not have integrated support for pixel graphics like DOS32, but Pete Eberlein has created a Euphoria interface to svgalib.

Easy X windows GUI programming is available using either Irv Mullin's EuGTK interface to the GTK GUI library, or wxEuphoria developed by Matt Lewis. wxEuphoria also runs on Windows.

When porting code from DOS or Windows to Linux or FreeBSD, you'll notice the following differences:

  • Some of the numbers assigned to the 16 main colors in graphics.e are different. If you use the constants defined in graphics.e you won't have a problem. If you hard-code your color numbers you will see that blue and red have been switched etc.

  • The key codes for special keys such as Home, End, arrow keys are different, and there are some additional differences when you run under XTERM.

  • The Enter key is code 10 (line-feed) on Linux, where on DOS/Windows it was 13 (carriage-return).

  • Linux and FreeBSD use '/' (slash) on file paths. DOS/Windows uses '\\' (backslash).

  • Highly specialized things such as dos_interrupt() obviously won't work on Linux or FreeBSD.

  • Calls to system() and system_exec() that contain DOS commands will obviously have to be changed to the corresponding Linux or FreeBSD command. e.g. "DEL" becomes "rm", and "MOVE" becomes "mv".


5. Interfacing with C Code (WIN32, Linux, FreeBSD)

On WIN32, Linux and FreeBSD it's possible to interface Euphoria code with C code. Your Euphoria program can call C routines and read and write C variables. C routines can even call ("callback") your Euphoria routines. The C code must reside in a WIN32 dynamic link library (.dll file), or a Linux or FreeBSD shared library (.so file). By interfacing with .dll's and shared libraries, you can access the full programming interface on these systems.

Using the Euphoria to C Translator, you can translate Euphoria routines to C, and compile them into a .dll or .so file. You can pass Euphoria atoms and sequences to these compiled Euphoria routines, and receive Euphoria data as a result. Translated/compiled routines typically run much faster than interpreted routines. For more information see the Translator.


5.1 Calling C Functions

To call a C function in a .dll or .so file you must perform the following steps:

1. Open the .dll or .so file that contains the C function by calling open_dll() contained in euphoria\include\dll.e.
2. Define the C function, by calling define_c_func() or define_c_proc() in dll.e. This tells Euphoria the number and type of the arguments as well as the type of value returned.

Euphoria currently supports all C integer and pointer types as arguments and return values. It also supports floating-point arguments and return values (C double type). It is currently not possible to pass C structures by value or receive a structure as a function result, although you can certainly pass a pointer to a structure and get a pointer to a structure as a return value. Passing C structures by value is rarely required for operating system calls.

Euphoria also supports all forms of Euphoria data - atoms and arbitrarily-complex sequences, as arguments to translated/compiled Euphoria routines.

3. Call the C function by calling c_func() or c_proc().

Example:
include dll.e

atom user32
integer LoadIcon, icon

user32 = open_dll("user32.dll")

-- The name of the routine in user32.dll is "LoadIconA".
-- It takes a pointer and an int as arguments, 
-- and it returns an int.
LoadIcon = define_c_func(user32, "LoadIconA",
                         {C_POINTER, C_INT}, C_INT)

icon = c_func(LoadIcon, {NULL, IDI_APPLICATION})

See library.doc - Calling C Functions for descriptions of c_func(), c_proc(), define_c_func(), define_c_proc(), open_dll() etc. See demo\win32 or demo\linux for example programs.

On Windows there is more than one C calling convention. The Windows API routines all use the __stdcall convention. Most C compilers however have __cdecl as their default. __cdecl allows for variable numbers of arguments to be passed. Euphoria assumes __stdcall, but if you need to call a C routine that uses __cdecl, you can put a '+' sign at the start of the routine name in define_c_proc() and define_c_func(). In the example above, you would have "+LoadIconA", instead of "LoadIconA".

You can examine a .dll file by right-clicking on it, and choosing "QuickView" (if it's on your system). You will see a list of all the C routines that the .dll exports.

To find out which .dll file contains a particular WIN32 C function, run euphoria\demo\win32\dsearch.exw.


5.2 Accessing C Variables

You can get the address of a C variable using define_c_var(). You can then use poke() and peek() to access the value of the variable.


5.3 Accessing C Structures

Many C routines require that you pass pointers to structures. You can simulate C structures using allocated blocks of memory. The address returned by allocate() can be passed as if it were a C pointer.

You can read and write members of C structures using peek() and poke(), or peek4u(), peek4s(), and poke4(). You can allocate space for structures using allocate(). You must calculate the offset of a member of a C structure. This is usually easy, because anything in C that needs 4 bytes will be assigned 4 bytes in the structure. Thus C int's, char's, unsigned int's, pointers to anything, etc. will all take 4 bytes. If the C declaration looks like:

        // Warning C code ahead!

        struct example {
            int a;           // offset  0
            char *b;         // offset  4
            char c;          // offset  8
            long d;          // offset 12
        };

To allocate space for "struct example" you would need:

        atom p

        p = allocate(16) -- size of "struct example"

The address that you get from allocate() is always at least 4-byte aligned. This is useful, since WIN32 structures are supposed to start on a 4-byte boundary. Fields within a C structure that are 4-bytes or more in size must start on a 4-byte boundary in memory. 2-byte fields must start on a 2-byte boundary. To achieve this you may have to leave small gaps within the structure. In practice it is not hard to align most structures since 90% of the fields are 4-byte pointers or 4-byte integers.

You can set the fields using something like:

        poke4(p + 0, a)
        poke4(p + 4, b)
        poke4(p + 8, c)
        poke4(p +12, d)
You can read a field with something like:
        d = peek4(p+12)
Tip:
For readability, make up Euphoria constants for the field offsets. See Example below.

Example:
constant RECT_LEFT = 0,
         RECT_TOP  = 4,
         RECT_RIGHT = 8,
         RECT_BOTTOM = 12
         
atom rect
rect = allocate(16)

poke4(rect + RECT_LEFT,    10)
poke4(rect + RECT_TOP,     20)
poke4(rect + RECT_RIGHT,   90)
poke4(rect + RECT_BOTTOM, 100)
     
-- pass rect as a pointer to a C structure
-- hWnd is a "handle" to the window
if not c_func(InvalidateRect, {hWnd, rect, 1}) then
    puts(2, "InvalidateRect failed\n")
end if
The Euphoria code that accesses C routines and data structures may look a bit ugly, but it will typically form just a small part of your program, especially if you use Win32Lib, EuWinGUI, or Irv Mullin's X Windows library. Most of your program will be written in pure Euphoria, which will give you a big advantage over those forced to code in C.


5.4 Call-backs to your Euphoria routines

When you create a window, the Windows operating system will need to call your Euphoria routine. This is a strange concept for DOS programmers who are used to calling operating system routines, but are not used to having the operating system call their routine. To set this up, you must get a 32-bit "call-back" address for your routine and give it to Windows. For example (taken from demo\win32\window.exw):

        integer id
        atom WndProcAddress
        
        id = routine_id("WndProc") 

        WndProcAddress = call_back(id)
routine_id() uniquely identifies a Euphoria procedure or function by returning a small integer value. This value can be used later to call the routine. You can also use it as an argument to the call_back() function.

In the example above, The 32-bit call-back address, WndProcAddress, can be stored in a C structure and passed to Windows via the RegisterClass() C API function. This gives Windows the ability to call the Euphoria routine, WndProc(), whenever the user performs an action on a certain class of window. Actions include clicking the mouse, typing a key, resizing the window etc. See the window.exw demo program for the whole story.

Note:
It is possible to get a call-back address for any Euphoria routine that meets the following conditions:
  • the routine must be a function, not a procedure
  • it must have from 0 to 9 parameters
  • the parameters should all be of type atom (or integer etc.), not sequence
  • the return value should be an integer value up to 32-bits in size
You can create as many call-back addresses as you like, but you should not call call_back() for the same Euphoria routine multiple times - each call-back address that you create requires a small block of memory.

The values that are passed to your Euphoria routine can be any 32-bit unsigned atoms, i.e. non-negative. Your routine could choose to interpret large positive numbers as negative if that is desirable. For instance, if a C routine tried to pass you -1, it would appear as hex FFFFFFFF. If a value is passed that does not fit the type you have chosen for a given parameter, a Euphoria type-check error may occur (depending on with/without type_check etc.) No error will occur if you declare all parameters as atom.

Normally, as in the case of WndProc() above, Windows initiates these call-backs to your routines. It is also possible for a C routine in any .dll to call one of your Euphoria routines. You just have to declare the C routine properly, and pass it the call-back address.

Here's an example of a WATCOM C routine that takes your call-back address as its only parameter, and then calls your 3-parameter Euphoria routine:

        /* 1-parameter C routine that you call from Euphoria */
        unsigned EXPORT APIENTRY test1(
                 LRESULT CALLBACK (*eu_callback)(unsigned a,
                                                 unsigned b,
                                                 unsigned c))
        {
            /* Your 3-parameter Euphoria routine is called here 
               via eu_callback pointer */
            return (*eu_callback)(111, 222, 333); 
        }
The C declaration above declares test1 as an externally-callable C routine that takes a single parameter. The single parameter is a pointer to a routine that takes 3 unsigned parameters - i.e. your Euphoria routine.

In WATCOM C, "CALLBACK" is the same as "__stdcall". This is the calling convention that's used to call WIN32 API routines, and the C pointer to your Euphoria routine should be declared this way too, or you'll get an error when your Euphoria routine tries to return to your .DLL.

If you need your Euphoria routine to be called using the __cdecl convention, you must code the call to call_back() as:

        myroutineaddr = call_back({'+', id})
The plus sign and braces indicate the __cdecl convention. The simple case, with no braces, is __stdcall.

In the example above, your Euphoria routine will be passed the three values 111, 222 and 333 as arguments. Your routine will return a value to test1. That value will then be immediately returned to the caller of test1 (which could be at some other place in your Euphoria program).

A call-back address can be passed to the Linux or FreeBSD signal() function to specify a Euphoria routine to handle various signals (e.g. SIGTERM). It can also be passed to C routines such as qsort(), to specify a Euphoria comparison function.