#include <string.h>
#ifndef __RSXNT__
#include<go32.h>
#endif
#include<sys/exceptn.h>

//#define DEBUG

#define  __DEBUG_MEMORY__
#undef   __DEBUG_MEMORY__

#define __SEAL__
#define __MAIN_INCLUDED__
//#define __CLOCK_INCLUDED__

//#define NO_SCREEN
#ifndef NO_SCREEN
// The startup window detailing dlx loading
#define WANT_FLASHY_STUFF
#endif

#define __DEBUG_FILE__ /* debug version */
#undef __DEBUG_FILE__
#ifndef __DEBUG_FILE__
#define DEBUG_printf(x,...)
#define DEBUG_printf_defined
#endif

#define NEW_ALLEGRO

/* put before allegro.h because rsx version likes to redefine some bits of these */
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef __RSXNT__
/*
At least this compiles with rsxntdj - but since I neither have, nor want, dcomm (nasty evil-empire stuff)
I can't really test it - who knows, it might event work ;-p You would have to make a new exports table
though, I #ifdef'ed the whole lot out because rsx uses all kinds of different low level function names.
*/
#define _POSIX_VDISABLE 1
#define D_OK    0x10
#define PATH_MAX 1024
#endif

#include "allegro.h"

#ifdef __RSXNT__
#undef font
/* a most unsatisfactory #define font (*font) which is trying to force us
to use a pointer for their global variable font (cos its in the dll) 
fortunately we never need it because it blows xxx->font statements sky high
*/
#endif


#define BLENDER_MAP BLENDER_FUNC
#define _trans_blender15 _blender_func15
#define _trans_blender16 _blender_func16
#define _trans_blender24 _blender_func24

/* sadly these _xxx tables no longer look much like what seal expects. They are indexed by the
upper 8 bits of the value readkey returns and we put in keyb->code
*/
#ifndef __RSXNT__
extern unsigned short *_key_ascii_table;
extern unsigned short *_key_capslock_table;
extern unsigned short *_key_shift_table;
extern unsigned short *_key_control_table;
extern unsigned short *_key_altgr_table;
unsigned short *key_ascii_table=(unsigned short *)&_key_ascii_table;
unsigned short *key_capslock_table=(unsigned short *)&_key_capslock_table;
unsigned short *key_shift_table=(unsigned short *)&_key_shift_table;
unsigned short *key_control_table=(unsigned short *)&_key_control_table;
unsigned short *key_altgr_table=(unsigned short *)&_key_altgr_table;
#else
/* you will need to fill these in somehow for rsx */
unsigned short key_ascii_table[256];
unsigned short key_capslock_table[256];
unsigned short key_shift_table[256];
unsigned short key_control_table[256];
unsigned short key_altgr_table[256];
#endif


#define SCANCODE_TO_KEY(c)       ( ((c)<<8) + (int)((key_ascii_table[c]>0x00ff)?0:key_ascii_table[c]) )
#define SCANCODE_TO_CAPS(c)      ( ((c)<<8) + (int)((key_capslock_table[c]>0x00ff)?KB_SHIFT_FLAG:key_capslock_table[c]) )
#define SCANCODE_TO_SHIFT(c)     ( ((c)<<8) + (int)((key_shift_table[c]>0x00ff)?KB_SHIFT_FLAG:key_shift_table[c]) )
#define SCANCODE_TO_CONTROL(c)   ( ((c)<<8) + (int)((key_control_table[c]>0x00ff)?KB_CTRL_FLAG :key_control_table[c]) )
#define SCANCODE_TO_ALTGR(c)     ( ((c)<<8) + (int)((key_altgr_table[c]>0x00ff?)'^':key_altgr_table[c]) )
#define SCANCODE_TO_ALT(c)       ( ((c)<<8) + (int)((key_ascii_table[c]>0x00ff)?KB_ALT_FLAG:0) )

/*
those convert a KEY_xxx number into an ascii char
if keyboard was not_shift'ed, capslock'ed, shift'ed, control'ed or altgr'ed
eg x<<8+table[x] is the value we store in keyb->code
Unfortunately we cant fill them in when we init the keyboard
because the ALT-F1 and ALT-F2 key shift to two different tables. With custom tables
ALT-F2 come 4 extra shifts KB_ACCENT1_FLAG to KB_ACCENT4_FLAG
the keys that cause these like most other things are in the config file as -
they are taken from the file specified or keyboard.dat in the main config file (I think)
"key_escape accent1_key" and "key_escape accent1_flag" and are all 0 in us keyboard
They may also be defined to return an arbitrary keycode I think
ctrl-keypad returns current keyshift state as the 'key' I think

Note that indexing the external char key[keycode] array tells you if any particular key is 
currently up or down (_key[code] I think processes pending transitions in polling mode)

Note that (as with windows) you can enter any key with sequence of ALT-keypad_decimal_ascii_numbers
which comes through as a key value with 0 in the code.

It seems new allegro is giving us a cooked version of the "code"
(see allegro/src/misc/pckeys.c) not the PC keycode that old allegro gave.
The codes are values KEY_A=1 to KEY_MAX=115 the last few from KEY_MODIFIERS=103 up are the shift keys
with all 16 flags filled in as KB_SHIFT_FLAG=0x0001 to KB_ACCENT4_FLAG=0x8000
It seems a shame to throw out all that good stuff, accented keys, japanese keyshifts etc
are all handled properly.
NOTE that ALT-F1 AND ALT-F2 are consumed to switch between keyboard mappings by allegro.
(Which kinda explains why I was getting nowhere - very, very, very slowly - when I was
(trying to) use ALT-F2 to figure out what was going wrong.)
*/

#include "registry.h"

/*#include"object.c"*/
/****************************************************************/
/*                                                              */
/*                          object.c                            */
/*                                                              */
/*                Seal - free desktop environment               */
/*                                                              */
/*                   Copyright (c) 1999,2000                    */
/*                        Michal Stencl                         */
/*                     All Rights Reserved                      */
/*                                                              */
/* mail : stenclpmd@ba.telecom.sk                               */
/* web  : http://www.home.sk/public/seal/                       */
/*                                                              */
/* This file is part of Seal.                                   */
/*                                                              */
/* Seal is free software; you can redistribute it and/or        */
/* modify it under the terms of the GNU General Public License  */
/* as published by the Free Software Foundation; either version */
/* 2, or (at your option) any later version.                    */
/*                                                              */
/* Seal is distributed in the hope that it will be useful, but  */
/* WITHOUT ANY WARRANTY; without even the implied warranty of   */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See    */
/* the GNU General Public License for more details.             */
/*                                                              */
/* You should have received a copy of the GNU General Public    */
/* License along with Seal; see the file COPYING.  If not,      */
/* write to the Free Software Foundation, 675 Mass Ave,         */
/* Cambridge, MA 02139, USA.                                    */
/*                                                              */
/****************************************************************/


#include <pc.h>
#include <bios.h>
#include <time.h>
#include <stdarg.h>
#include"object.h"
/*#include"safmem.c"*/ /* important */
/****************************************************************/
/*                                                              */
/*                           safmem.c                           */
/*                                                              */
/*                Seal - free desktop environment               */
/*                                                              */
/*                    Copyright (c) 1999,2000                   */
/*                         Michal Stencl                        */
/*                     All Rights Reserved                      */
/*                                                              */
/* mail : stenclpmd@ba.telecom.sk                               */
/* web  : http://www.home.sk/public/seal/                       */
/*                                                              */
/* This file is part of Seal.                                   */
/*                                                              */
/* Seal is free software; you can redistribute it and/or        */
/* modify it under the terms of the GNU General Public License  */
/* as published by the Free Software Foundation; either version */
/* 2, or (at your option) any later version.                    */
/*                                                              */
/* Seal is distributed in the hope that it will be useful, but  */
/* WITHOUT ANY WARRANTY; without even the implied warranty of   */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See    */
/* the GNU General Public License for more details.             */
/*                                                              */
/* You should have received a copy of the GNU General Public    */
/* License along with Seal; see the file COPYING.  If not,      */
/* write to the Free Software Foundation, 675 Mass Ave,         */
/* Cambridge, MA 02139, USA.                                    */
/*                                                              */
/****************************************************************/


#ifdef  __DEBUG_MEMORY__
#define MSS
#include"mss.h"
#endif
#include"safmem.h"

void     tag_strcpy ( void *dst, void *src, l_int size )
{
  if ( dst ) {
     clear_type(dst, size);
     if ( src ) strncpy(dst, src, size-1);
  };
};

l_int   tag_stricmp ( void *dst, void *src )
{
  if ( dst && src ) return stricmp(dst, src);
  return 1; /* two null strings match at start I suppose */
};

/* safe malloc */
void   sf_alert ( void )
{
     seal_error(ERR_INFO, "%s.\n\n%s", TXT_NOTENOUGHMEMEMORYFOROPERATION, TXT_PLEASESTOREYOURDATAANDEXITSOMEAPPLICATION);
};

void  *sf_malloc ( size_t size )
{
void *p = malloc(size);
return p;
};
void  sf_free ( void *rec )
{
  if ( rec ) free(rec);
};
#ifdef __RSXNT__
#define cfree(rec) free(rec)
#endif
void  sf_cfree ( void *rec )
{
  if (rec) cfree(rec);
};
void  *sf_calloc ( size_t num_elements, size_t size )
{
   return calloc(num_elements, size);
};
char  *sf_strdup ( const char *source )
{
 return source?strdup(source):NULL;
};
void   *sf_realloc ( void *rec, size_t size )
{
 return rec?realloc(rec, size):sf_malloc(size);
};

/*#include"filter.c"*/ /* important */
/****************************************************************/
/*                                                              */
/*                          filter.c                            */
/*                                                              */
/*                Seal - free desktop environment               */
/*                                                              */
/*                    Copyright (c) 1999,2000                   */
/*                         Michal Stencl                        */
/*                     All Rights Reserved                      */
/*                                                              */
/* mail : stenclpmd@ba.telecom.sk                               */
/* web  : http://www.home.sk/public/seal/                       */
/*                                                              */
/* This file is part of Seal.                                   */
/*                                                              */
/* Seal is free software; you can redistribute it and/or        */
/* modify it under the terms of the GNU General Public License  */
/* as published by the Free Software Foundation; either version */
/* 2, or (at your option) any later version.                    */
/*                                                              */
/* Seal is distributed in the hope that it will be useful, but  */
/* WITHOUT ANY WARRANTY; without even the implied warranty of   */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See    */
/* the GNU General Public License for more details.             */
/*                                                              */
/* You should have received a copy of the GNU General Public    */
/* License along with Seal; see the file COPYING.  If not,      */
/* write to the Free Software Foundation, 675 Mass Ave,         */
/* Cambridge, MA 02139, USA.                                    */
/*                                                              */
/****************************************************************/


#include"filter.h"

/* contains information about all filters that use Seal */
p_filter filter = NULL;

/*
 this function is used, whenever some data from the object comes to the other one.
 it filters data form (ptr), (tag_from) to (tag_to). returns converted value or
 data.
 example :
 l_ptr x = filter(text, DAT_NUMBER, DAT_STRING);
 (x) contains the number ( in l_big format ).
*/

l_ptr  dat_filter ( l_ptr ptr, l_tag tag_to, l_tag tag_from )
{

  /* set filter to the first (filter item) */
  p_filter f = filter;
  /* for all items */
  while ( f ) {
      /* test if can convert to one format to another */
      if ( l_tag_cmp(f->tag_to, tag_to) && l_tag_cmp(f->tag_from, tag_from) )
                  /* filter */
           return f->filter(ptr, tag_to, tag_from);
      f = f->next;
  };
  /* can't filter from the type ( tag_from ) to type ( tag_to ) */
  return NULL;
};

/* insert new filter function to (filter array). This filter can convert from types
   tag_from to tag_to.
*/
void   filter_insert ( l_tag tag_to, l_tag tag_from, l_ptr (*filter_func)(l_ptr ptr, l_tag tag_to, l_tag tag_from) )
{
  /* allocate memory for new filter item */
  p_filter f = (p_filter)_malloc(sizeof(t_filter));
  /* memory was allocated succesfull ? */
  if ( f ) {
      /* all memory to ZERO */
      clear_type(f, sizeof(t_filter));
      l_tag_cpy(f->tag_to, tag_to);
      l_tag_cpy(f->tag_from, tag_from);
      f->filter = filter_func;
      f->next = filter;
      filter = f;
  };

};


/* remove inserted filter function from filter' array.
*/
void   filter_remove ( l_tag tag_to, l_tag tag_from )
{

  /* find first item */
  p_filter f = filter;
  p_filter last = NULL;
  if ( f ) /* exists item */
   while ( f ) {
      /* saves the next filter item */
      p_filter s = f->next;
      /* we want to remove this filter ? */
      if ( (f->tag_to == tag_to) && (f->tag_from == tag_from) ) {

           /* safe the array */
           if ( !last ) /* it's first item */
                 /* move the first item to next (s = f->next) */
                 filter = s;
           else
                 /* else next of previous item point to next of this */
                 last->next = f->next;
           /* free memory of item */
          _free(f);
          /* break */
          return;
      };
      last = f;
      f = s;
   };
};


/* remove all inserted filters from filter' array.
*/
void   filter_remove_all ( void )
{
  p_filter f = filter;
  while ( f ) {
     p_filter s = f->next;
     _free(f);
     f = s;
  };
  filter = NULL;
};

#ifndef BUFFER_FORMAT_TEXT
#define BUFFER_FORMAT_TEXT   1024
#endif

#ifndef TEXTOUT_BUFFER_SIZE
#define TEXTOUT_BUFFER_SIZE 1024
#endif

#ifndef FIFO_BUFFER_SIZE
#define FIFO_BUFFER_SIZE   512
#endif
static char global_buf[FIFO_BUFFER_SIZE];
FILE  *seal_test_file = NULL;
FILE  *seal_debug_file = NULL;

char  *ini_mainfile = NULL;

l_big     event_timer = 0;
p_object  event_stop  = NULL;
t_event   event_main;
l_bool    go_process = true;

t_rect rect_empty = {{-0xFFFF, -0xFFFF},{ -0xFFFF, -0xFFFF}};

t_data  clipboard;

l_int  (*seal_error) ( l_int errtype, l_text str, ... ) = &_seal_error;
t_object *(*obj_init) ( t_object *o ) = &_obj_init;

#ifndef DEBUG_printf_defined
/* print to debuger */
l_int  DEBUG_printf ( l_text text, ...)
{
   l_int nc = 0;
   va_list arg;
   if ( !seal_debug_file ) return 0;
   va_start(arg, text);
   nc = vfprintf(seal_debug_file, text, arg);
   va_end(arg);
   fflush(seal_debug_file);
   return nc;
};
#endif
/* origin function */

inline t_point point_assign ( l_rect x, l_rect y )
{
  t_point p;
  p.x = x;
  p.y = y;
  return p;
};

inline t_rect  rect_assign ( l_rect ax, l_rect ay, l_rect bx, l_rect by )
{
  t_rect r;
  r.a.x = ax;
  r.a.y = ay;
  r.b.x = bx;
  r.b.y = by;
  return r;
};

inline t_rect  rect_move ( t_rect r, l_rect mx, l_rect my )
{
  r.a.x += mx;
  r.a.y += my;
  r.b.x += mx;
  r.b.y += my;
  return r;
};

inline t_point rect_size ( t_rect r )
{
  t_point p;
  p.x = r.b.x - r.a.x;
  p.y = r.b.y - r.a.y;
  return p;
};


inline l_bool  rect_overlay ( t_rect r, t_rect d )
{
  if ( r.a.x > d.b.x || r.a.y > d.b.y ||
       r.b.x < d.a.x || r.b.y < d.a.y ) return false;
  return true;
};

inline t_rect  rect_cliped ( t_rect r, t_rect d )
{
  if  ( !rect_overlay(r, d) ) return rect_empty;
  r.b.x = max(r.a.x, min(r.b.x, d.b.x));
  r.b.y = max(r.a.y, min(r.b.y, d.b.y));
  r.a.x = min(r.b.x, max(r.a.x, d.a.x));
  r.a.y = min(r.b.y, max(r.a.y, d.a.y));
  return r;
};



inline l_rect rect_sizex ( t_rect r )
{
  t_point p = rect_size(r);
  return p.x;
};


inline l_rect rect_sizey ( t_rect r )
{
  t_point p = rect_size(r);
  return p.y;
};


inline l_bool rect_check_empty ( t_rect r )
{
  if ( (r.a.x == rect_empty.a.x) && (r.b.x == rect_empty.b.x) &&
       (r.a.y == rect_empty.a.y) && (r.b.y == rect_empty.b.y) ) return true;
  return false;
};


inline l_bool rect_contains ( t_rect r, t_point p )
{
  if ( (r.a.x <= p.x) && (r.a.y <= p.y) && (r.b.x >= p.x) && (r.b.y >= p.y) )
    return true;
  return false;
};


inline l_bool  rect_equals ( t_rect r, t_rect nr )
{
  if ( (r.a.x != nr.a.x) || (r.a.y != nr.a.y) || (r.b.x != nr.b.x) ||
       (r.b.y != nr.b.y) ) return false;
  return true;
};


void  rect_double_overlay ( t_rect* fr, t_rect *lr )
{
  t_rect r   = *fr;
  t_rect xfr = *fr;
  t_rect xlr = *lr;
  if ( !rect_overlay(*fr, *lr) ) {
    *(fr) = rect_empty;
    *(lr) = rect_empty;
    return;
  };
  r = rect_cliped(r, *lr);
  if ( r.a.x > (*lr).a.x ) (*lr).b.x = r.a.x - 1;
  if ( r.b.x < (*lr).b.x ) (*lr).a.x = r.b.x + 1;
  if ( r.b.y < (*lr).b.y ) *fr = rect_assign(r.a.x, r.b.y + 1, r.b.x, (*lr).b.y);
  if ( r.a.y > (*lr).a.y ) *fr = rect_assign(r.a.x, (*lr).a.y, r.b.x, r.a.y - 1);
  if ( rect_equals(xfr, *fr) ) *fr = rect_empty;
  if ( rect_equals(xlr, *lr) ) *lr = rect_empty;
};


/* others functions */
void _afree ( void **p )
{
   if ( p ) {
     if(*p)_free(*p);
     *p = NULL;
   };
};


static l_big atimer = 0;

void  aclock ( void )
{
   atimer += 20;
};

/* time functions */
l_big time_get_mili ( void )
{
  return _time_get_mili();
};

l_big time_diff_mili ( l_big mili )
{
  return _time_diff_mili(mili);
};

/* string functions */
l_text  set_format_text ( l_text *dest, l_text format, ... )
{
  va_list arg;
  static  l_char buffer[BUFFER_FORMAT_TEXT];
  l_text         dst = NULL;
  //if ( dest ) {_free(dest); dest=NULL;} //afree((void**)dest);
  va_start(arg, format);
  vsprintf(buffer, format, arg);
  dst = _strdup(buffer);
  va_end(arg);

if(strlen(buffer)>BUFFER_FORMAT_TEXT){
 /* Sadly we cant sanely check how big the result is going to be so
   the overflow will have destroyed who knows what. Lets hope things are still alive enough to do... */
 fputs("set_format_text buffer has overflowed\n",stderr);
 exit(1);
}
  if ( dest ) *dest = dst;
  return dst;
};

/*
  it puts "..." before or after formated text if strlen of text is greater than
  size. If size is -size, then "..." are puts before text, else after.
*/
l_text  set_format_text_nice ( l_text *dest, l_int size, l_text format, ... )
{
  va_list arg;
  static  l_char buffer[BUFFER_FORMAT_TEXT];
  l_text         dst = NULL;
  l_int          strsz;
  //if ( dest )  {_free(dest); dest=NULL;}//afree((void**)dest);
  if ( !format ) return NULL;
  va_start(arg, format);
  strsz = vsprintf(buffer, format, arg);
  va_end(arg);

if(strlen(buffer)>(BUFFER_FORMAT_TEXT-4)){
 /* the overflow will have destroyed who knows what. Lets hope things are alive enough to do...*/
 fputs("set_format_text_nice buffer has overflowed\n",stderr);
 exit(1);
}

  if ( (strsz > abs(size)) && (size > 0) ) {
      buffer[0] = '\0';
      if ( size < 0 ) { /* before text */
         strcat(buffer, "...");
         buffer[abs(size)+3] = '\0';
      } else { /* after text */
         strcat(buffer, (&buffer)[(strsz-abs(size))-4]);
         strcat(buffer, "...");
      };
  };
  dst = _strdup(buffer);
  if ( dest ) *dest = dst;
  return dst;
};


l_text  insstr ( l_text dest, l_text str, l_long pos, l_long size )
{
  l_text n = NULL;
  l_long ttsize = 0;
  l_long sz = 0;
  if ( !str || pos < 0 || size <= 0 ) return dest;
  sz = strlen(str);
  if ( sz < 0 ) return dest;
  if ( !dest ) {
    n = (l_text)_malloc(lmin(sz, size)+1);
    n[0] = '\0';
    strcat(n, str);
    return n;
  } else {
    l_text p;
    l_text n;
    ttsize = strlen(dest)+1;
    size = lmax(0, lmin(sz, size));
    pos  = lmax(0, lmin(ttsize-1, pos));
    p = _strdup(&dest[pos]);

    n = (l_text)_realloc(dest, ttsize+size);
    if ( n && p ) {
         strcpy(&n[pos], str);
         strcpy(&n[pos+size], p);
    };
    _free(p);
    return n;
  };
};


l_text  delstr ( l_text dest, l_long pos, l_long size )
{
  if ( dest && size > 0 ) {
    l_long ttsize = strlen(dest);
    if ( pos >= ttsize ) return dest;
    size = lmax(0, lmin((ttsize-pos), size));
    pos  = lmax(0, lmin(ttsize, pos));
    if ( size > 0 ) {
      l_text p = _strdup(&dest[pos+size]);
      l_text n = (l_text)_realloc(dest, (ttsize-size)+1);
      if ( n && p ) {
        strcpy(&n[pos], p);
      };
      _free(p);
      return n;
    };
  };
  return dest;
};


l_text  stridup ( l_text str, l_long num )
{
  if ( str && num > 0 ) {
    l_text b = (l_text )_malloc(num+1);
    if ( b ) {
      strncpy(b, str, num);
      b[num] = '\0';
    };
    return b;
  };
  return NULL;
};


l_text stristr ( l_text s, l_text d )
{
  if ( s && d && *d ) {
    while ( *s ) {
      l_text olds = s;
      l_text oldd = d;
      while ( *s && *d && tolower(*s) == tolower(*d) ) {
        s++;
        d++;
      };
      if ( !(*d) ) return olds;
      if ( olds == s ) s++;
      d = oldd;
      s++;
    };
  };
  return NULL;
};

/* first in first out */
l_ptr fifo_add ( l_ptr list, l_ptr src, l_int size, l_int where )
{
   where = max(where, 0);
   if ( size <= 0 ) return NULL;
   if ( !list ) {
         list = _malloc(size);
         where = 0;
   } else
         list = _realloc(list, size*(where+1));
   if ( list )
       memcpy((l_ptr)((l_long)list+(where*size)), src, size);
   return list;
};


l_ptr fifo_get ( l_ptr *list, l_int size, l_int where )
{
   if ( !list || (size <= 0) ) return NULL;
   where = max(where, 0);
   if ( *list ) {

if (size>FIFO_BUFFER_SIZE ){
 fputs("fifo buffer overflowed\n",stderr);
 exit(1);
}
         memcpy(global_buf, (l_ptr)((l_long)(*list)+(where*size)), size);
         if ( where > 0 )
            *list = _realloc(*list, size*where);
         else {
            _free(*list);
            *list = NULL;
        };
        return (l_ptr)(global_buf);
   };
   return NULL;
};

/* error's functions */
l_int  _seal_error ( l_int errtype, l_text str, ... )
{
  switch ( errtype ) {
    case ERR_NONREGULAR : {
      va_list argptr;
      va_start(argptr, str);
      vprintf(str, argptr);
      va_end(argptr);
      return 0;
    }; break;
    case ERR_INFO : {
      va_list argptr;
      va_start(argptr, str);
      vprintf(str, argptr);
      va_end(argptr);
      return 1;
    }; break;
  };
  return 0;
};


/* object's function */
l_bool     is_my_object ( t_object *o, t_object *e )
{
  if ( e ) {
    if ( e == o ) return true;
    else {
      p_object l;
      o = o->last;
      l = o;
      if ( o )
        do {
          if ( is_my_object(o, e) ) return true;
          o = o->next;
        } while ( o != l );
    };
  };
  return false;
};

l_bool     is_active ( t_object *e )
{
   if ( e )
     while ( e ) {
        if ( IS_ACTIVE_PROCESS(e) &&
            !e->is_options(e, OB_OF_NOTACTIVATE) )  return true;
        e = e->owner;
     };
  return false;
};

/* also know as done_stillprocess (a macro that calls it with -1 milis) */
void  init_stillprocess ( p_object o, l_int milis )
{
   if ( !o ) return;
   if ( milis > 0 ) {
      o->process_tick = milis;
      o->set_options(o, OB_OF_STILLPROCESS, true);
      o->put_into_stillprocess(o, o);
   } else if ( o->options & OB_OF_STILLPROCESS ) {
      o->process_tick = 0;
      o->clear_from_stillprocess(o, o);
      o->set_options(o, OB_OF_STILLPROCESS, false);
   };
};

/* object functions */
l_bool  obj_done ( p_object o )
{
  if ( obj_exist(o) > 0 ) {
      t_object *l = o->last;
      done_stillprocess(o); /* get out from list of still-processing objects */
                            /* it's also in obj_remove, but... */
      if ( l ) /* if there is sub object */
      do {
         t_object *v = l->next;
         dispose(o->last);
         l = v;
      } while ( o->last );
      if ( o->owner ) {
         p_object owner = o->owner;
         owner->remove(owner, o);
         owner->reset_prefer(owner);
      };
      o->tag = TAG_DISPOSE;
      return true;
  };
  return false;
};

l_long    obj_index_of ( p_object o, p_object p )
{
  p_object  t = o->first(o);
  p_object  f = t;
  l_long i = 0;
  if ( !t || !p ) return 0;
  else
    do
     {
       if ( t == p ) return i;
       t = t->next;
       i++;
     } while ( t != f );
  return 0;
};

p_object  obj_at ( p_object o, l_long index )
{
  p_object  t = o->first(o);
  p_object  f = t;
  l_long i = 0;
  if ( !t || index < 0 ) return NULL;
  else
    do
     {
       if ( i == index ) return t;
       i++;
       t = t->next;
     } while (( t != f ) && ( i <= index ));
  return NULL;
};


p_object obj_find_match ( p_object o, l_dword sta, l_dword opt, l_bool forward )
{
  p_object s = o;
  if ( forward ) { /* forward */
    do {
      o = o->next;
      if ( o->is_options(o, OB_OF_ENABLE) && (!sta || o->is_state(o, sta)) &&
           (!opt || o->is_options(o, opt)) )
        return o;
    } while ( o != s );
  } else /* backward */
    do {
      o = o->prev;
      if ( o->is_options(o, OB_OF_ENABLE) && (!sta || o->is_state(o, sta)) &&
           (!opt || o->is_options(o, opt)) )
        return o;
    } while ( o != s );
  return NULL;
};


p_object obj_find_match_view ( p_object o, l_dword sta, l_dword opt, l_bool forward )
{
  p_object s = o;
  if ( forward ) { /* forward */
    do {
      o = o->next_view(o);
      if ( o->is_options(o, OB_OF_ENABLE) && (!sta || o->is_state(o, sta)) &&
           (!opt || o->is_options(o, opt)) )
        return o;
    } while ( o && o != s );
  } else /* backward */
    do {
      o = o->prev_view(o);
      if ( o->is_options(o, OB_OF_ENABLE) && (!sta || o->is_state(o, sta)) &&
           (!opt || o->is_options(o, opt)) )
        return o;
    } while ( o && o != s );
   return NULL;
};


p_object  obj_call_trans_events ( p_object o, l_dword st, l_dword op )
{
 return NULL;
};


p_object  obj_owner_view ( p_object o )
{
  if ( o->owner ) {
    if ( o->owner->tag & TAG_VIEW ) return o->owner;
    else return o->owner->owner_view(o->owner);
  } else return NULL;
};


p_object  obj_next_view ( p_object o )
{
  t_object *t = o;
  do {
    t = t->next;
    if ( t->tag & TAG_VIEW ) return t;
  } while ( t != o );
  return NULL;
};


p_object  obj_prev_view ( p_object o )
{
  t_object *t = o;
  do {
    t = t->prev;
    if ( t->tag & TAG_VIEW ) return t;
  } while ( t != o );
  return NULL;
};


p_object  obj_last_view ( p_object o )
{
  if ( !o->last ) return NULL;
  if ( o->last->tag & TAG_VIEW ) return o->last;
  else {
    t_object *p = o->last->next_view(o->last);
    if ( p ) /* last view exist */
      return p;
    else /* sub-last view doesn't exist */
      return o->last->last_view(o->last);
  };
};


p_object  obj_first_view ( p_object o )
{
  if ( !o->first(o) ) return NULL;
  if ( o->first(o)->tag & TAG_VIEW ) return o->first(o);
  else {
    t_object *p = o->first(o)->prev_view(o->first(o));
    if ( p ) /* first view exist */
      return p;
    else
      return o->first(o)->first_view(o->first(o));
  };
};


p_object  obj_prev_view_to_first ( p_object o )
{
  if ( o->owner )
    if ( o != o->owner->first_view(o->owner) ) return o->prev_view(o);
  return NULL;
};


p_object  obj_next_view_to_last ( p_object o )
{
  if ( o->owner )
    if ( o != o->owner->last_view(o->owner) ) return o->next_view(o);
  return NULL;
};


p_object  obj_prev_to_first ( p_object o )
{
  if ( o->owner )
    if ( o != o->owner->first(o->owner) ) return o->prev;
  return NULL;
};


p_object  obj_next_to_last ( p_object o )
{
  if ( o->owner )
    if ( o != o->owner->last ) return o->next;
  return NULL;
};


void       obj_setup ( p_object o )
{
  if ( o->owner ) o->owner->reset_prefer(o->owner);
};


void       obj_after_init ( p_object o )
{

};


l_bool     obj_select ( p_object o )
{
  if ( !o->is_options(o, OB_OF_ENABLE) || !o->is_state(o, OB_SF_VISIBLE) )
    return false;
  if ( !o->is_options(o, OB_OF_SELECTABLE) )
    return false;
  if ( o->is_options(o, OB_OF_TOPSELECT) && o->owner )
    o->put_in_front_of(o, o->owner->first_view(o->owner));
  else if ( o->owner ) o->owner->set_prefer(o->owner, o);
  return true;
};


int        obj_put_into_stillprocess ( p_object o, p_object s )
{
  if ( o->owner ) o = o->owner;
  return o->put_into_stillprocess(o, s);
};


int        obj_clear_from_stillprocess ( p_object o, p_object s )
{
  if ( o->owner ) o = o->owner;

  return o->clear_from_stillprocess(o, s);

};


t_object*  obj_insert ( p_object o, p_object sub )
{
  if ( sub ) sub->after_init(sub);
  if ( o->last && sub && sub->is_options(sub, OB_OF_TOPSELECT) )
    o->insert_before(o, sub, o->first(o));
  else o->insert_before(o, sub, NULL);
  if ( sub ) {
    if ( sub->is_options(sub, OB_OF_STILLPROCESS) ) {
      init_stillprocess(sub, sub->process_tick);
    };
    sub->setup(sub);
  };
  return sub;
};


t_object*  obj_insert_before ( p_object o, p_object sub, p_object before )
{
  p_object t = before;
  if ( !sub ) return NULL;
  sub->owner = o;
  if ( before )
  {
    before  = before->prev;
    sub->next = t;
    before->next = sub;
    t->prev = sub;
    sub->prev = before;
  }
  else
  {
    if ( !o->last )
    {
      sub->next = sub;
      sub->prev = sub;
    } else
    {
      t = o->last->next;
      sub->prev = o->last;
      sub->next = t;
      t->prev = sub;
      o->last->next = sub;
    };
    o->last = sub;
  };
  return sub;
};


void obj_put_in_front_of ( p_object o, p_object before )
{
  if ( o->owner ) {
    p_object owner = o->owner;
    owner->set_prefer(owner, o);
    owner->remove(owner, o);
    owner->insert_before(owner, o, before);
  };
};


t_object*  obj_first ( p_object o )
{
  if ( o->last ) return o->last->next; else return NULL;
};


void       obj_set_state ( p_object o, l_dword st, l_bool set )
{
  if ( set ) o->state |= st;
  else o->state &= ~st;
  if ( st & OB_SF_FOCUSED ) { /* focus flag for sub selected object */
    p_object p = o->first(o);
    if ( p ) {
      p = p->find_match(p, set?OB_SF_SELECTED:OB_SF_FOCUSED, 0, true);
      if ( p )
        p->set_state(p, OB_SF_FOCUSED, set);
    };
  };
};


l_bool     obj_is_state ( p_object o, l_dword st )
{
  return (l_bool)(o->state & st);
};


void       obj_set_options ( p_object o, l_dword op, l_bool set )
{
  if ( set ) o->options |= op;
  else o->options &= ~op;
};


l_bool     obj_is_options ( p_object o, l_dword op )
{
  return (l_bool)(o->options & op);
};


void       obj_remove ( p_object o, p_object sub )
{
  t_object* t = NULL;
  if ( !o->last || !sub ) return;
  if ( sub->next != sub )
  {
    done_stillprocess(sub);
    t = sub->prev;
    t->next = sub->next;
    t->next->prev = t;
    if ( sub == o->last ) o->last = t;
    if ( sub == o->prefer ) o->prefer = NULL;
  }
  else {
    done_stillprocess(sub);
    o->last = NULL;
    o->prefer = NULL;
  };
  sub->owner = NULL;
};


l_dword    obj_valid ( p_object o, l_dword msg )
{
  p_object f = o->first(o);
  p_object p = f;
  if ( f )
  do {
     l_dword msg = f->valid(f, msg);
     if ( msg ) return msg;
  } while ( p != f );
  return 0;
};


void       obj_get_event ( p_object o, t_event *event )
{
  if ( o->owner ) o = o->owner;
  o->get_event(o, event);
};


void       obj_put_event ( p_object o, t_event *event )
{
  if ( o->owner ) o = o->owner;
  o->put_event(o, event);
};


l_dword    obj_execute ( p_object o )
{
/*  t_event event;*/
  clear_event(&event_main);
  do {
    o->end_state = 0;
    do {
      o->get_event(o, &event_main);
      INTMAIN(&event_main);
      PLAY_PROCESS(o, &event_main);
    } while ( !o->end_state );
  } while ( o->valid(o, o->end_state) != 0 );
  return o->end_state;
};


void  obj_reset_prefer ( p_object o )
{
  o->set_prefer(o, o->first(o));
};


void  obj_set_prefer ( p_object o, p_object prefer )
{

/* Yo! Those macros were too tricky for words. Lets expand em and throw the !_enable bit out */
  if ( o->prefer != prefer )
  {
      if ( prefer && 
                   (prefer)->is_options(prefer, OB_OF_SELECTABLE) &&
                   (prefer)->is_options(prefer, OB_OF_ENABLE))
        {
           (prefer)->set_state(prefer, OB_SF_SELECTED, TRUE); // if prefer selectable and enabled say you are selected to prefer
        }
      if ( o->prefer &&
                   (o->prefer)->is_options(o->prefer, OB_OF_ENABLE))
        {
           (o->prefer)->set_state(o->prefer, OB_SF_SELECTED, FALSE); // if the objects prefer enabled say you arent selected  to objects prefer                                                                 
        }
      if ( prefer &&
                   o->is_state(o, OB_SF_FOCUSED) &&
                   (prefer)->is_options(prefer, OB_OF_SELECTABLE) &&
                   (prefer)->is_options(prefer, OB_OF_ENABLE) )
        {
           (prefer)->set_state(prefer, OB_SF_FOCUSED, TRUE); // if object is focussed and prefer selectable and enabled say you are focussed to prefer
        }
      /* Now, if setting prefer selected & focussed results in o becoming not focussed, this next bit wont happen
       o->prefer will not be told its not focussed 
       Is this right? 
       But, then again, prefer (or indeed o->prefer) setting o unfocussed probably results in o->prefer being told about it
       at that time.
       Well then, maybe it right.
      */
      if ( o->prefer &&
                   o->is_state(o, OB_SF_FOCUSED) &&
                   (o->prefer)->is_options(o->prefer, OB_OF_ENABLE) )
        {
           (o->prefer)->set_state(o->prefer, OB_SF_FOCUSED, FALSE); // if object focussed and objects prefer enabled say you arent focussed to objects prefer 
        }
      o->prefer = prefer;
  };

};


void       obj_for_each_event ( p_object o, t_event *event )
{
  t_object *f = o->first(o);
  t_object *x = f;
  if ( f )
    do {
      if ( IS_OKTOSUBPROCESS(x) && (x != o->prefer) )
            PLAY_PROCESS(x, event);
      x = x->next;
    } while ( x != f );
};

void * obj_find_extension(p_object o, l_text id, l_long version);

void       obj_play_process ( p_object o, t_event *event )
{
  if ( !o ) return;
  if ( !IS_ACTIVE_PROCESS(o) || o->is_options(o, OB_OF_NOTACTIVATE) ||
        EV_IGNORE_ACTIVE_PROCESS(event->type)  ) {
    ACTIVE_PROCESS(o);
    event_stop = o;
//if(o->find_extension != obj_find_extension)
// fprintf(stderr,"invalid object\n");
//fprintf(stderr,"obj_play_process %lx\n",(unsigned long)o);
    o->translate_event(o, event);
    PASSIVE_PROCESS(o);
  };
};

void       obj_translate_event ( p_object o, t_event *event )
{
  if ( (o->prefer) && IS_OKTOSUBPROCESS(o->prefer) )
    PLAY_PROCESS(o->prefer, event);
  o->for_each_event(o, event);
  /* object messages */
  if ( event->type & EV_MESSAGE )
     switch ( event->message ) {
         case MSG_PASTE : if ( o == event->obj ) { /* paste data */
                              o->set_data(o, &clipboard); /* from clipboard */
                              clear_event(event);
         }; break;
         case MSG_COPY  : if ( o == event->obj ) { /* copy data */
                              clipboard.style = DS_WHATEVER; /* get what object want to sent */
                              o->get_data(o, &clipboard); /* to clipboard */
                              clear_event(event);
         }; break;
         case MSG_CUT  : if ( o == event->obj ) { /* delete data */
                              t_data delclip;
                              clear_type(&delclip, sizeof(t_data)); /* from object */
                              delclip.style = DS_DELETE|DS_WHATEVER; /* delete what object want to */
                              l_tag_cpy(delclip.id, o->data_type); /* id of clipboard = o->data_type */
                              o->set_data(o, &delclip);
                              clear_event(event);
         }; break;
     };
};


/*
  l_bool  t_object . get_data(t_object *o, t_data *rec)
  - function that is called even when some data are dragged from the object,
    or data are copied from this object into clipboard, etc.
  - see set data for t_data structure.
  - return true, if data was copied from the object, else return false
  - usually used structure of function :
  if ( rec ) {
     rec->info_obj = o;
     rec->id = _your_id;
     switch ( rec->style ) {
       case DS_SELECTED : {
            rec->data = _your_selected_new_pointer;
       }; break;
       case DS_ALL : {
          rec->data = _your_alldata_new_pointer;
          return true;
       }; break;
       case DS_WHATEVER : {
          rec->style = DS_SELECTED;
          return OBJECT(o)->get_data(OBJECT(o), rec);
       }; break;
     };
     rec->id = DAT_NONE;
  };
  return false;
*/
l_bool     obj_get_data ( p_object o, t_data *rec )
{
  return false;
};

/*
  t_object . set_data(t_object *o, t_data *rec)
  - function that is called even when some data are dropped into the object,
    or data are pasted into object from clipboard, etc. set_data function
    may set data only if o->id is same to accepted (id's) that you select in
    t_object . data_type variable.
    - rec is pointer to t_data structure, that contains following information:
         l_tag     id;       - identification of data, may contains more
                               than one bit settings
            DAT_NONE    - none data type, it not contains any data
            DAT_TEXT    - t_data->data is text end by zero ( C text )
            DAT_IMAGE   - t_data->data contains pointer to Allegro BITMAP
            DAT_FILE    - t_data->data contains special info about file,
                          see t_fat structure that you will find in
                          _iodir.h file.
            DAT_LIST    - t_data->data is pointer to p_list structure. See id
                          of this ( t_list ) structure.
            DAT_ALLKNOW - all data (id's) are accepted, it's used in trash
                          BIN f.e.
         l_dword   style; - styles of t_data paste/copy
            DS_ALL       - get all data from object in current (id).
            DS_SELECTED  - get only selected data from object
            DS_WHATEVER  - object select, what style it want to copy to
                           clipboard
            DS_DELETE    - delete data from object. ( This may be combined
                           with previous styles. DS_ALL|DS_DELETE => delete
                           all data from object, DS_SELECTED|DS_DELETE => delete
                           only what's selected in object...)
         l_ptr    *data; - pointer to data ( l_ptr = void ), you get what
                           type of pointer it's from (id).
         p_object  info_obj; - pointer to object, from where were data copied
                               to clipboard.
  - return true, if data was pasted into this object, else return false
  - default structure of function :
  l_bool ok  = false;
  if ( rec && l_tag_cmp(o->data_type, rec->id) ) {
     if ( rec->style & DS_DELETE ) {
       if ( o->is_state(o, OB_SF_FOCUSED) )  { delete only if focused
         if ( rec->style & DS_ALL ) {
               delete all
               ok = true;
         } else {
               delete only_selected
               if ( was_some_selected ) ok = true;
               else return false;
         };
       };
     } else {  data are pasted into object
        paste_data;
        ok = true;
     };
     if ( ok )
         some redraw function;
  };
  return ok;
*/
l_bool     obj_set_data ( p_object o, t_data *rec )
{
  return false;
};

/*
  - function select data in object,
  - data_syle   can be one of following :
            DS_ALL       - select all data in object ( default, also 0 )
            ...
  - set  - true if select else unselect
    return true if selected/unselected, else false
*/
l_bool     obj_select_data ( p_object o, l_int data_style, l_bool set )
{
   return false;
};


#ifdef DEBUG
l_bool  screen_reload ( void );
void    screen_halt ( void );
void safe_timer_halt ( void );
void safe_timer_reload ( void );
void sstart(){
 screen_reload();
 safe_timer_reload();
}
void sstop(){
 screen_halt();
 safe_timer_halt();
}
#define STOP sstop();
#define START sstart();
#else
#define STOP
#define START
#endif


t_object* dispose ( t_object *o )
{
/*
  if ( o && (o->done(o)) ) _free(o);
  return NULL;
*/
/* jdh addition also free any items in the extension_list and their extension_data as well */
  if ( o && (o->done(o)) ) {
   p_extension nextlist,list=o->extension_list;
   while (list){nextlist=list->next;_free(list->id);_free(list->extension_data);_free(list);list=nextlist;}
   _free(o);
  }
  return NULL;
};

void  dispose_all ( t_object *o )
{
   if ( obj_exist(o) > 0 ) {
      t_object *l = OBJECT(o)->last;
      if ( l ) /* if there is sub object */
      do {
         t_object *v = l->next;
         l->done(l);
         l = v;
      } while ( o->last );
      o->last = NULL;
      o->tag = TAG_DISPOSE;
   };
};

void      message_info ( p_object o, l_dword type, l_dword message, p_object obj, void *info )
{
  if ( o ) {
    t_event ev;
    set_event_info(&ev, type, message, obj, info);
    PLAY_PROCESS(o, &ev);
  };
};

void      message_all_info ( l_dword type, l_dword message, p_object obj, void *info )
{
   message_info(&program, type, message, obj, info);
};

void      set_event ( t_event *event, l_dword type, l_dword message, p_object obj )
{
  t_event e;
  clear_event(&e);
  e.type = type;
  e.message = message;
  e.obj = obj;
  (*event) = e;
};

void      set_event_info ( t_event *event, l_dword type, l_dword message, p_object obj, void *rec )
{
  t_event e;
  clear_event(&e);
  e.type = type;
  e.message = message;
  e.obj = obj;
  e.info = rec;
  (*event) = e;
};

l_ptr    copy_type ( l_ptr what, l_long size )
{
   void *ptr = NULL;
   if ( what && (size > 0) )
       ptr = _malloc(size);
   if ( ptr )
      memcpy(ptr, what, size);
   return ptr;
};

static void  obj_func_nothing ( p_object o )
{
};


/* Logic.. 
 There is a need to add functionality - new function pointers, additional data etc -
 to various structures within Seal. These three functions allow this to be done in an
 extensible and memory-efficent way. If you need to extend a particular struct you simply call
 o->add_extension(o,"SomeTextId", version, data, &prev_version, &prev_data);
 If that returns your pointer (data) the request is granted and your extension data is available for
 use. The prev_data pointer might, in that case point to an earlier version (see below).
 If the request was refused then there is a higher version already around and the pointer it returned 
 should be used. In the apps wanting to use the extension, you simply request the data pointer of the
 highest version of that extension.
 o->find_extension(o,"SomeTextId",lowest_usable_version);
 If it exists you can use it knowing that at least all the fields you know of exist. You should fall 
 back to some baseline version with reduced functionality if find_extension() returns NULL meaning there
 is no support for what the extension wants to do, because an earlier version of Seal or that particular
 library is being used.
 Using these functions insulates your apps and your libraries against future changes.
 Clearly you must only ADD to the end of the struct to achieve later versions. -:
 struct this_thing {
  // version 1 
  p_thingy thing;
  l_int that;
  /////////////////////
  // stuff added in version 2 
  p_thingy souped_up_thing;
  l_int new_that;
  // stuff added in version 3.. 
 }
 Once the extension is released you can modify functions from version 1, but only in ways that are 100%
 compatible, new algorithms or whatnot. Such changes must NOT refer to anything but the version 1 
 bits of the struct - because this is still a version 1 function.
 Modifications to function that do require access to version 2 bits are added at the end of the struct (as
 new function pointers) - because they are no longer version 1. Of course you would actually implement
 the old version 1 pointers by replacing the version 1 stuff with a bit of glue that called your new code
 in such a way as to reproduce version 1's effects and not use any version 2 data - retaining compatibility
 without wasting code space.


   Hint, a neat trick would be to use sizeof(t_my_extension) in the version eg
      data=o->add_extension(o,"BadSealApp", sizeof(t_my_extension)*100+VERSION_NUMBER ,my_extension,&prior_version,&prior_data)
      That way even if you forget (or dont want) to change VERSION_NUMBER you will always get something
      that is big enough to fit all your data fields. Initialisation from a previous version is also easier -
      clear_type(data,sizeof(t_my_extension));
      if(prior_data)memcpy(data,prior_version/100); // secure in the knowledge that amount of data exists 
      // then make copies of any malloced data - cos whoever put it there will surely want free them themself 
      ... other inits.
 

   Example useage-:

   In your library dlx code if you need to add fields to the struct for object o ...

   l_int prior_version;
   void *prior_data;
   t_my_extension *data,*my_extension=malloc(sizeof(t_my_extension)); // create your extension structure

   if(! (data=(t_my_extension*)o->add_extension(o,"BadSealApp",1,my_extension,&prior_version,&prior_data) ) {
    // No memory, oooh...
    buy_more_memory();
   } else {
    if(data!=my_extension) { // This version or higher already existed before I was around
     // So you want to use it ->
     free(my_extension);
     my_extension=data;
     // you might want to (careful - you're probably fiddling with stuff thats not even been written yet!) 
     // override some of the functions defined in it with ones of your own. What an interesting concept - preheritance. Must ask the kids about my inheriting from them ...
     my_extension->overridden_function=&new_function;
    } else {
     // my extension will be used from now on 
     clear_type(my_extension,sizeof(t_my_extension));       
     if(prior_data) {
      // But there was an earlier version already around before you came along. You may wish to 
      // (carefully) pinch bits from that lower_or_equal version to init your struct with,
      // being careful to make a copy of malloced strings and such cos you dont own them.
      my_extension->this_function = (t_my_extension *)prior_data->this_function;
      // then add stuff specific to your version and all versions above prior_version (can be tricky)
     } else {
      // all clear - do the normal initialisations to your struct
      my_extension->this_function= &this_function;
      ... etc
     }
    }
    // thereafter go ahead and do your stuff ...
    my_extension->icon=the_icon;
    ... etc
   }
   ...

   In your apps, you get at the (latest version of the) struct extensions thus -

   if(! (my_extension=(t_my_extension *)o->find_extension(o,"BadSealApp",1)) ) { 
    // Whoops, not there. The library being used doesnt seem support this function then.
    fprintf(stderr,"Waaaah!\nGimme a library with BadSealApp version 1 upward (pretty please).\n");
    // or, better, just skip any extension specific stuff silently
   } else {
    // go ahead using your nice, shiny new code
    the_icon=my_extension->icon;
   }

   // You can also request the exact version rather than the latest version by specifying it with
   // the negative of the version number. This is specifically for removing extensions. 
   // See below for example.
   // You might like to think about this one ...
   if ( ap_process == AP_FREE ) {
    t_my_extension *my_extension;
    if( (my_extension=(t_my_extension *)o->find_extension(o,"BadSealApp",-1)) ) { // finds this specific version.
     o->remove_extension(o,my_extension); // and remove it.
     _free(my_extension);
     // Remember, the moment you leave, any pointers to your functions you may have put in an object
     // will suddenly become seriously invalid. Using them in an extension though, providing
     // there is an earlier version around to take over, means the objects you create might 
     // still be useable after you leave and not give problems (eg crashes). Interesting, eh?
    }
   }
  Warning - this IS version 1.. caveat emptor.
  Still, as proof of concept - Hey it works!
*/

void * obj_find_extension(p_object o, l_text id, l_long version)
{
/* Finds data for an extension whos text id == id with a version number greater 
   than or equal to version. Returns NULL if no such extension exists else the
   address of its data. If version is -ve returns the data for the match whos 
   version is exactly -version (if it exists).
*/
 p_extension list=o->extension_list;
 if(!list) return NULL;                  /* no extensions present of any kind */

//fprintf(stderr,"full list for %lx=\n",(unsigned long)o);
//while(list) {fprintf(stderr,"%s %ld %lx\n",list->id,list->version,(unsigned long)(list->extension_data));list=list->next;}
//fprintf(stderr,"\n");
//list=o->extension_list;

 if(version<0) /* find exact version number */
  while (list && stricmp(list->id,id) && (-version != list->version) ) list=list->next;
 else          /* find version greater than or equal to version */
  while (list && stricmp(list->id,id) && (version < list->version) ) list=list->next;

 /* case independent - I dont expect you to remember ThEeXaCTCaPiTAliSaTION the guy used */
//if(list) fprintf(stderr,"find %s %ld found %s %ld %lx\n",id,version,list->id,list->version,(unsigned long)(list->extension_data));
//else fprintf(stderr,"find %s %ld not found\n",id,version);
 if(list) return list->extension_data;
 else return NULL;
}

void * obj_add_extension (p_object o, l_text id, l_long version, void *data,
                                                l_long *prev_version, void **prev_data)
{
/* Adds this extension to the list of extensions. The list is maintained in version order so 
   we will find the best (highest version) match first. Returns NULL if no memory. if
   add_extension(o,id,v,data,&pv,&pd) !=data you need to and use the pointer it returns instead,
   freeing your proposed data pointer.
   If not your extension has been added, it will be found by anything requesting your version or lower.
   You might want to initialise your struct from fields in prev_data if it is non-zero. You should be able
   to figure out what is valid in that struct from its version number. 
   The id you specify could be a (text) string from ROM for all I care, the only function is to 
   identify the extension, printable ones are highly recommended however.

   Note that there is normally no need to _free the memory you alloc'ed for your extension struct -
   when dispose(o) gets called it will handle all the necessary deallocations for the entire list.
   You do have to deallocate any copies of things you may have made and any other allocs you put
   into the struct of course, as usual in a xxx_done() proc.

   The list lookup should be quite efficient and there should not be many additions in any case, so you
   dont need to worry too much about calling it all over the place. On the other hand, as always, if you
   can do something just the once it usually gives cleaner, faster code.
*/

 p_extension list=o->extension_list;
 p_extension new=_malloc(sizeof(t_extension)); 
 p_extension prior;
 *prev_version=0;
 *prev_data=NULL;
 if(new) {
  new->next=NULL;
  new->prev=NULL;
  new->id=_strdup(id);
  new->version=version;
  new->extension_data=data;
  if( list ) { /* find first matching this id */
   while (list && stricmp(list->id,id) ) list=list->next;
   if(!list) {        /* no matching id so we can put this one in at head of the list */
//fprintf(stderr,"add %lx not found adding %s %ld %lx\n",(unsigned long)o,id,version,(unsigned long)data);
    prior=o->extension_list;
    new->next=prior;  /* our next pointer is to what was head of list */
    prior->prev=new;  /* his predecessor (previously NULL) is now us, our predecessor is still NULL */
    o->extension_list=new; /* and we are the new start of the list */
    return data;
   } else {          /* id matches, if its version is same or greater than the one he wants to add return data address of the one we found */
    if(list->version >= version) return list->extension_data;
//fprintf(stderr,"add %lx adding %s %ld %lx (having found %s %ld %lx)\n",(unsigned long)o,id,version,(unsigned long)data,list->id,list->version,(unsigned long)(list->extension_data));
    /* The one we found must therefore have a lower version so we can insert this one before the one we found */
    if((prior=list->prev)) { /* it is not at the top of the list */
     new->prev=prior;        /* so our predecessor is what was his predecessor */
     new->next=list;         /* our successor is him */
     prior->next=new;        /* we are his predecessors successor */
     list->prev=new;         /* and we are his predecessor */
    } else {                 /* it is at top of the list */
     new->next=list;         /* he is our successor */
     list->prev=new;         /* his predecessor is us, our predecessor remains NULL */
     o->extension_list=new;  /* and we are the new top of the list */
    } /* Inserted. Now tell him the data pointer of the earlier version so he can use initialised fields from it. */
    *prev_version=list->version;
    *prev_data=list->extension_data;
    return data;
   }
  } else { /* This is first in list - prev and next both remain null since this is the sole resident */
//fprintf(stderr,"add %lx (empty list) adding %s %ld %lx\n",(unsigned long)o,id,version,(unsigned long)data);
   o->extension_list = new;
   return data; /* success */
  }
 } else return NULL; /* fail - no memory. */ 
}

void * obj_remove_extension (p_object o, void *data)
{
/* Converse function. Removes EXACTLY this extension, the one whos extension_data is data.
   Returns the data address if successful else NULL which is REAL BAD NEWS - probably memory corruption
    or maybe multiple instances playing silly beggars
*/
 p_extension prev,next,list=o->extension_list;
 if(!list)return NULL;
 while(list && (list->extension_data!=data)) list=list->next;

//if(list) fprintf(stderr,"remove obj=%lx %lx found %s %ld %lx\n",(unsigned long)o,(unsigned long)data,list->id,list->version,(unsigned long)list->extension_data);
//else fprintf(stderr,"remove obj=%lx %lx not found\n",(unsigned long)o,(unsigned long)data);

 if(list){                   /* found, unlink this from the list */
  if( (prev=list->prev) ) {  /* it has a predecessor */
   if( (next=list->next) ) { /* and it has a successor */
    next->prev=prev;         /* its predecessors successor is now the next one down */
    prev->next=next;         /* and the next one downs predecessor is what was this ones predecessor */
   } else {                  /* if no successor, its at end of the list */
    prev->next=NULL;         /* so its predecessor now becomes end of list */
   }
  } else {                   /* its the top of the list */
   if( (next=list->next) ) { /* and it has a successor */
    next->prev=NULL;         /* so next one down now has no predecessor */
    o->extension_list=next;  /* and next one down is now top of list */
   } else                    /* else its the only one left in the list */
    o->extension_list=NULL;  /* and list is now totally empty */
  }  /* unlinking complete, now remove our record */
  _free(list->id);
  _free(list);
  return data;   /* return his data, its up to him to _free it */
 }
 return NULL;
}

/* t_object */
t_object *_obj_init ( t_object *o )
{
  if ( !o ) return NULL;
//fprintf(stderr,"obj_init %lx\n",(unsigned long)o);
  memset(o, 0, sizeof(t_object));
  o->options = OB_OF_ENABLE;
  o->tag = TAG_OBJECT;
  o->process_tick = 20;
  o->owner = NULL;
  o->find_match_view = &obj_find_match_view;
  o->find_match = &obj_find_match;
  o->owner_view = &obj_owner_view;
  o->put_in_front_of = &obj_put_in_front_of;
  o->func_callback = &obj_func_nothing;
  o->next_view = &obj_next_view;
  o->prev_view = &obj_prev_view;
  o->last_view = &obj_last_view;
  o->first_view = &obj_first_view;
  o->prev_to_first = &obj_prev_to_first;
  o->next_to_last = &obj_next_to_last;
  o->prev_view_to_first = &obj_prev_view_to_first;
  o->next_view_to_last = &obj_next_view_to_last;
  o->set_prefer = &obj_set_prefer;
  o->reset_prefer = &obj_reset_prefer;
  o->select = &obj_select;
  o->set_state = &obj_set_state;
  o->is_state = &obj_is_state;
  o->set_options = &obj_set_options;
  o->is_options = &obj_is_options;
  o->get_data = &obj_get_data;
  o->set_data = &obj_set_data;
  o->select_data = &obj_select_data;
  o->at = &obj_at;
  o->index_of = &obj_index_of;
  o->play_process = &obj_play_process;
  o->translate_event = &obj_translate_event;
  o->for_each_event = &obj_for_each_event;
  o->first = &obj_first;
  o->after_init = &obj_after_init;
  o->setup = &obj_setup;
  o->insert_before = &obj_insert_before;
  o->insert = &obj_insert;
  o->remove = &obj_remove;
  o->done = &obj_done;
  o->valid = &obj_valid;
  o->put_into_stillprocess = &obj_put_into_stillprocess;
  o->clear_from_stillprocess = &obj_clear_from_stillprocess;
  o->get_event = &obj_get_event;
  o->put_event = &obj_put_event;
  o->execute = &obj_execute;
/* jdh additions - object extensions */
  o->find_extension = &obj_find_extension;
  o->add_extension = &obj_add_extension;
  o->remove_extension = &obj_remove_extension;
/* end jdh additions */

  return o;
};

/*#include"alltogrx.c"*/
/****************************************************************/
/*                                                              */
/*                         alltogrx.c                           */
/*                                                              */
/*                Seal - free desktop environment               */
/*                                                              */
/*                    Copyright (c) 1999,2000                   */
/*                         Michal Stencl                        */
/*                     All Rights Reserved                      */
/*                                                              */
/* mail : stenclpmd@ba.telecom.sk                               */
/* web  : http://www.home.sk/public/seal/                       */
/*                                                              */
/* This file is part of Seal.                                   */
/*                                                              */
/* Seal is free software; you can redistribute it and/or        */
/* modify it under the terms of the GNU General Public License  */
/* as published by the Free Software Foundation; either version */
/* 2, or (at your option) any later version.                    */
/*                                                              */
/* Seal is distributed in the hope that it will be useful, but  */
/* WITHOUT ANY WARRANTY; without even the implied warranty of   */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See    */
/* the GNU General Public License for more details.             */
/*                                                              */
/* You should have received a copy of the GNU General Public    */
/* License along with Seal; see the file COPYING.  If not,      */
/* write to the Free Software Foundation, 675 Mass Ave,         */
/* Cambridge, MA 02139, USA.                                    */
/*                                                              */
/****************************************************************/


#include  "allegro.h"
#include  "alltogrx.h"
#include <stdarg.h>
#include <string.h>

#ifndef strsize
#define strsize(p,v)    ((v)?((v)-(p)):strlen(p))
#endif
#ifndef min
#define min(x,y)    (((x) < (y)) ?  (x) : (y))
#endif
#ifndef max
#define max(x,y)    (((x) > (y)) ?  (x) : (y))
#endif
#ifndef imin
#define imin(x,y)   min((int)(x),(int)(y))
#endif
#ifndef imax
#define imax(x,y)   max((int)(x),(int)(y))
#endif
#ifndef umin
#define umin(x,y)   min((unsigned int)(x),(unsigned int)(y))
#endif
#ifndef umax
#define umax(x,y)   max((unsigned int)(x),(unsigned int)(y))
#endif
#ifndef lmin
#define lmin(x,y)   min((long)(x),(long)(y))
#endif
#ifndef lmax
#define lmax(x,y)   max((long)(x),(long)(y))
#endif
#ifndef dmin
#define dmin(x,y)   min((unsigned long)(x),(unsigned long)(y))
#endif
#ifndef dmax
#define dmax(x,y)   max((unsigned long)(x),(unsigned long)(y))
#endif

static int  get_text_length ( void *f, unsigned char *str, int len );
static int  get_text_height ( void *f );
static void draw_text_out ( BITMAP *b, void *f, unsigned char *text, int x1, int y1, GrCOLOR color );
static void _change_font_size ( void *f, int w, int h );

int   (*textlen)(void *f, unsigned char *text, int len ) = &get_text_length;
int   (*textheight)(void *f ) = &get_text_height;
void  (*drawtext)(BITMAP *b, void *f, unsigned char *text, int x1, int y1, GrCOLOR color ) = &draw_text_out;
void  (*change_font_size)(void *f, int w, int h) = &_change_font_size;


/*#include  "newscr.c"*/
/****************************************************************/
/*                                                              */
/*                           newscr.c                           */
/*                                                              */
/*                Seal - free desktop environment               */
/*                                                              */
/*                    Copyright (c) 1999,2000                   */
/*                         Michal Stencl                        */
/*                     All Rights Reserved                      */
/*                                                              */
/* mail : stenclpmd@ba.telecom.sk                               */
/* web  : http://www.home.sk/public/seal/                       */
/*                                                              */
/* This file is part of Seal.                                   */
/*                                                              */
/* Seal is free software; you can redistribute it and/or        */
/* modify it under the terms of the GNU General Public License  */
/* as published by the Free Software Foundation; either version */
/* 2, or (at your option) any later version.                    */
/*                                                              */
/* Seal is distributed in the hope that it will be useful, but  */
/* WITHOUT ANY WARRANTY; without even the implied warranty of   */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See    */
/* the GNU General Public License for more details.             */
/*                                                              */
/* You should have received a copy of the GNU General Public    */
/* License along with Seal; see the file COPYING.  If not,      */
/* write to the Free Software Foundation, 675 Mass Ave,         */
/* Cambridge, MA 02139, USA.                                    */
/*                                                              */
/****************************************************************/


static void _change_font_size ( void *f, int w, int h )
{
};

unsigned long get_context_mem_size ( BITMAP *b ) {
  if ( !b ) b = ScreenContext;
  return b?((unsigned long)b->w * (unsigned long)b->h * (unsigned long)(((b->vtable->color_depth+7)>>3)+1)):0;
};

int get_depth ( BITMAP *b ) {
  if ( !b ) b = ScreenContext;
  return (b?bitmap_color_depth(b):0);
};

int get_mask_color ( BITMAP *b ) {
  if ( !b ) b = ScreenContext;
  return (b? bitmap_mask_color(b) :0);
};

unsigned char* get_addr_line ( BITMAP *b, int line ) {
  if ( !b ) b = ScreenContext;
  return (b&&(line<b->h))?b->line[line]:0;
};

int get_width ( BITMAP *b ) {
  if ( !b ) b = ScreenContext;
  if ( b == ScreenContext ) return SCREEN_W;
  return b?b->w:0;
};

int get_height ( BITMAP *b ) {
  if ( !b ) b = ScreenContext;
  if ( b == ScreenContext ) return SCREEN_H;
  return b?b->h:0;
};

int get_clip ( BITMAP *b, int *x1, int *y1, int *x2, int *y2 ) {
  if ( !b ) b = screen;
  if ( b && (b->clip) ) {
    *x1 = b->cl;
    *y1 = b->ct;
    *x2 = b->cr;
    *y2 = b->cb;
    return 1;
  } else {
    *x1 = 0;
    *y1 = 0;
    *x2 = 0;
    *y2 = 0;
    return 0;
  };
};

void get_real_box ( BITMAP *b, int *x1, int *y1, int *x2, int *y2 ) {
  if ( !b ) b = ScreenContext;
  if ( b ) {
    *x1 = 0;
    *y1 = 0;
    *x2 = get_width(b)-1;
    *y2 = get_height(b)-1;
  } else {
    *x1 = 0;
    *y1 = 0;
    *x2 = 0;
    *y2 = 0;
  };
};


void get_size_of_ftext ( char *text, GrFONT *font, int *x, int *y )
{
  int xx = 0;
  int maxx = 0;
  int yy = 0;
  if ( text && font && (x || y) ) {
    yy = textheight(font);
    while ( *text ) {
      if ( *text == '\n' ) {
        yy += textheight(font);
        if ( maxx < xx ) maxx = xx;
        xx  = 0;
      } else
        xx += get_char_length ( font, *text);
      text++;
    };
  };
  if ( maxx < xx ) maxx = xx;
  if ( x ) *x = maxx;
  if ( y ) *y = yy;
};

/* how about using hardware acceleration for the lines? Happilly they are never used. */
void point_vline ( BITMAP *out, int x1, int y1, int y2, GrCOLOR color )
{
  while ( y2 > y1 ) {
    putpixel(out, x1, y2, color);
    putpixel(out, x1-1, y2-1, color);
    y2-=2;
  };
};


void point_hline ( BITMAP *out, int x1, int y1, int x2, GrCOLOR color )
{
  while ( x2 > x1 ) {
    putpixel(out, x2, y1, color);
    putpixel(out, x2-1, y1-1, color);
    x2-=2;
  };
};


void draw_char ( BITMAP *out, GrFONT *f, unsigned char chr, int x1, int y1,
                 GrCOLOR f1, GrCOLOR b1 )
{
  unsigned char s[2] = {0, '\0'};
  s[0] = chr;
  text_mode(-1);
  if ( !f ) return;
  if ( b1 >= -1 )
    rectfill(out, x1, y1, x1+textlen(f, s, 1), y1+textheight(f), b1);
  (*drawtext)(out,f,s,x1,y1,f1);
};


void draw_selected_text ( BITMAP *out, GrFONT *f,
                          unsigned char *txt, int size, int sfrom, int sto,
                          int x1, int y1, int x2, int y2, int align,
                          GrCOLOR f1, GrCOLOR b1, GrCOLOR f2, GrCOLOR b2, int clip )
{
  if ( (sfrom != 0) || (sto == sfrom) ) {
    if ( (sto == sfrom) || (sfrom < 0) || (sto < -1) ) {
      textout_draw_rect(out, f, txt, size, x1, y1, x2, y2, align, f1, b1, clip);
      return;
    };
    textout_draw_rect(out, f, txt, sfrom, x1, y1, x2, y2, align, f1, b1, clip);
    x1 += textlen(f, txt, sfrom);
  };
  if ( sfrom >= 0 ) {
    if ( sto == -1 ) {
      textout_draw_rect(out, f, &txt[sfrom], size-sfrom, x1, y1, x2, y2, align, f2, b2, clip);
      return;
    };
    textout_draw_rect(out, f, &txt[sfrom], sto-sfrom, x1, y1, x2, y2, align, f2, b2, clip);
    if ( sto > sfrom ) {
      x1 += textlen(f, &txt[sfrom], sto-sfrom);
      if ( sto < strlen(txt) )
        textout_draw_rect(out, f, &txt[sto], size-sto, x1, y1, x2, y2, align, f1, b1, clip);
    };
  };
};


void draw_double_text ( BITMAP *out, GrFONT *f, unsigned char *txt, int size,
                        int x1, int y1, int x2, int y2, int align,
                        GrCOLOR f1, GrCOLOR f2, int clip )
{
  x1++;
  y1++;
  x2++;
  y2++;
  textout_draw_rect(out, f, txt, size, x1, y1, x2, y2, align, f1, TX_NOCOLOR, clip);
  x1--;
  y1--;
  x2--;
  y2--;
  textout_draw_rect(out, f, txt, size, x1, y1, x2, y2, align, f2, TX_NOCOLOR, clip);
};


void lined_rect ( BITMAP *b, int x1, int y1, int x2, int y2, GrCOLOR one, GrCOLOR two )
{
  while ( y1 < y2 ) {
    hline(b, x1, y1, x2-1, one);
    hline(b, x1+1, y1+1, x2, two);
    y1+=3;
  };
};


void fade_rect ( BITMAP *b, int x1, int y1, int x2, int y2, GrCOLOR one, GrCOLOR two, int direct )
{
  int r1 = getr(one);
  int g1 = getg(one);
  int b1 = getb(one);
  double rd = (double)(getr(two)-r1)/(double)256;
  double gd = (double)(getg(two)-g1)/(double)256;
  double bd = (double)(getb(two)-b1)/(double)256;
  if ( get_depth(NULL) > 8 ) {
    if ( direct == 0x01 ) { /* horizontaly */
      double frame = (double)(x2-x1)/(double)256;
      int    x = 0;
      while ( x < 256 ) {
        int sx = x1 + (int)((double)x*frame);
        int ex = x1 + (int)((double)(x+1)*frame);
        int c = makecol(r1+(int)((double)x*rd),
                        g1+(int)((double)x*gd),
                        b1+(int)((double)x*bd));
        rectfill(b, sx, y1, ex, y2, c);
        x++;
      };
    } else { /* verticaly */
      double frame = (double)(y2-y1)/(double)256;
      int    y = 0;
      while ( y < 256 ) {
        int sy = y1 + (int)((double)y*frame);
        int ey = y1 + (int)((double)(y+1)*frame);
        int c = makecol(r1+(int)((double)y*rd),
                        g1+(int)((double)y*gd),
                        b1+(int)((double)y*bd));
        rectfill(b, x1, sy, x2, ey, c);
        y++;
      };
    };
  }
  else /* gradients are going to look really vile in an 8 bit 332 palette */
     rectfill(b, x1, y1, x2, y2, makecol8(r1,g1,b1) /* one */);
};


void light_image ( BITMAP *b, int x1, int y1, int x2, int y2, int dr, int dg, int db )
{
/* NB Should probably limit the colors max(min(rgb.r+dr,255),0) to prevent wrapping effects
yep - I was right, it can crash. 
*/
  int x = 0;
  int y = 0;
  while ( y < y2-y1 ) {
        while ( x < x2-x1 ) {
          int col = getpixel(b, x1+x, y1+y);
          RGB     rgb;
          rgb.r = getr(col);
          rgb.g = getg(col);
          rgb.b = getb(col);
          putpixel(b, x1+x, y1+y, makecol(MAX(0,MIN(255,rgb.r+dr)), MAX(0,MIN(255,rgb.g+dg)), MAX(0,MIN(255,rgb.b+db))));
          x++;
        };
        x = 0;
        y++;
      };
};


void button ( BITMAP *b, int x1, int y1, int x2, int y2, GrCOLOR c1, GrCOLOR c2 ) {
/* changed for slightly cuter 3d effect may benefit from hardware accelerated rect drawing if any */
  rect(b,x1,y1,x2-1,y2-1,c1);
  rect(b,x1,y1,x2,y2,c2);
};

static int get_text_length ( void *f, unsigned char *str, int len ) {
  int l = 0;
  if (font) {
   if ( str && (len > 0) ) {
    /* its not a good idea to change someones data even temporarily -  it might even be in read-only memory for all you know.*/
    unsigned char *t=strdup(str);
    if(t){
     if(len<strlen(t))t[len]=0;
     l = text_length((FONT*)f, t);
     _free(t);
    }
   } else if ( str && (len < 0) ) {
     return text_length((FONT*)f, str);
   };
  }
  return l;
};


int get_char_length ( void *f, unsigned char ch ) {
 if(ch){
  unsigned char s[2] = {0, '\0'};
  s[0] = ch;
  return textlen((FONT*)f, (unsigned char*)s, 1);
 } else return 0;
};


static int get_text_height ( void *f ) {
  return text_height((FONT*)f);
};

static void draw_text_out ( BITMAP *b, void *f, unsigned char *text, int x1, int y1, GrCOLOR color )
{
  textout(b, (FONT*)f, text, x1, y1, color);
};

void textout_draw(BITMAP *bmp, void *f, unsigned char *s, int len,
                  int x1, int y1, int align, GrColor color, GrColor bcolor ) {
  if ( s && len && f ) {
    unsigned char t;
    len = (len<0)?strlen((char*)s):len;
    t = s[len];
    s[len] = '\0';
    if ( align & TX_ALIGN_CENTERX ) x1 -= textlen(f, s, -1)/2;
      else if ( align & TX_ALIGN_RIGHT ) x1 -= textlen(f, s, -1);
    if ( align & TX_ALIGN_CENTERY ) y1 -= textheight(f)/2;
      else if ( align & TX_ALIGN_BOTTOM ) y1 -= textheight(f);
    text_mode(-1);
    if ( bcolor >= -1 )
      rectfill(bmp, x1, y1, x1+textlen(f, s, len), y1+textheight(f), bcolor);
    (*drawtext)(bmp,f,s,x1,y1,color);
    if ( align & TX_UNDERLINE )
       hline(bmp, x1, y1+textheight(f)-1, x1+textlen(f, s, len), color);
    if ( align & TX_STRIKELINE )
      hline(bmp, x1, y1+(textheight(f)/2), x1+textlen(f, s, len), color);
    s[len] = t;
  };
};

void textout_draw_rect(BITMAP *bmp, void *f, unsigned char *s, int len,
                  int x1, int y1, int x2, int y2, int align, GrColor color, GrColor bcolor,
                  int clip ) {
  if ( s && len && f ) {
    unsigned char t;
    int ox1, oy1, ox2, oy2;
    int sx1 = x1, sy1 = y1;
    len = (len<0)?strlen((char*)s):len;
    t = s[len];
    s[len] = '\0';
    if ( clip ) {
      if ( get_clip(bmp, &ox1, &oy1, &ox2, &oy2) ) {
        int tx1, ty1, tx2, ty2;
        tx1 = max(ox1, x1);
        ty1 = max(oy1, y1);
        tx2 = min(ox2, x2);
        ty2 = min(oy2, y2);
        set_clip(bmp, tx1, ty1, tx2, ty2);
      } else set_clip(bmp, x1, y1, x2, y2);
    };
    if ( align & TX_ALIGN_CENTERX ) x1 += ((x2-sx1)-textlen(f, s, -1))>>1;
      else if ( align & TX_ALIGN_RIGHT ) x1 = x2-textlen(f, s, -1);
    if ( align & TX_ALIGN_CENTERY ) y1 += ((y2-sy1)-textheight(f))>>1;
      else if ( align & TX_ALIGN_BOTTOM ) y1 = y2-textheight(f);
    text_mode(-1);
    if ( bcolor >= 0 )
      rectfill(bmp, x1, y1, x1+textlen(f, s, len), y1+textheight(f), bcolor);
    (*drawtext)(bmp,f,s,x1,y1,color);
    if ( align & TX_UNDERLINE )
      hline(bmp, x1, y1+textheight(f)-1, x1+textlen(f, s, len), color);
    if ( align & TX_STRIKELINE )
      hline(bmp, x1, y1+(textheight(f)/2), x1+textlen(f, s, len), color);
    if ( clip ) set_clip(bmp, ox1, oy1, ox2, oy2);
    s[len] = t;
  };
};

void textout_printf(BITMAP *bmp, void *f, int len,
                  int x1, int y1, int x2, int y2, int align, GrColor color, GrColor bcolor,
                  int clip, char *s, ... ) {
  static char buf[TEXTOUT_BUFFER_SIZE];
  /* I'm willing to let you to put 100Mb up in one go. Just not this way. Try using textout directly. */
  if ( s && len && f ) {
    char *t = NULL;
    int oldy2 = y2;
    va_list ap;
    va_start(ap, s);
    vsprintf(buf, s, ap);
    va_end(ap);

if(strlen(buf)>TEXTOUT_BUFFER_SIZE) {
/*
The overflow will have destroyed who knows what.
At least its no longer the stack since I made the buffer static 
it will overflow into the next chunk of data in the uninitialised data segment actually.
Whatever it destroyed the result has got to be really bad. So.. hope we can still say something.
*/
 fputs("textout_printf has overflowed\n",stderr);
 exit(1);
}
    t = buf;
    while ( t && *t ) {
      char *oldt = t;
      y2  = y1+textheight(f);
      t = strchr(t, '\n'); /* find end-char */
      textout_draw_rect(bmp, f, oldt, strsize(oldt, t), x1, y1, x2, y2, align, color, bcolor, clip);
      if ( t ) { /* enter was found */
        t++; /* move to next char after enter */
        y1 += (*textheight)(f);
        if ( y1 > oldy2 ) return;
      };
    };
  };
};

void  draw_flip_sprite ( BITMAP *b, BITMAP *s, int x, int y, int flag ) {
  if ( flag & SF_FLIPX ) {
    if ( flag & SF_FLIPY ) draw_sprite_vh_flip(b, s, x, y);
      else draw_sprite_h_flip(b, s, x, y);
  } else if ( flag & SF_FLIPY ) draw_sprite_v_flip(b, s, x, y);
           else draw_sprite(b, s, x, y);
};

void __blit_hline ( BITMAP *source, BITMAP *dest, int sx, int sy, int dx, int dy, int sw, int dw ) {
  sx %= source->w;
  sy %= source->h;
  sw = min(source->w-sx, min(sw, dw));
  while ( dw ) {
    blit(source, dest, sx, sy, dx, dy, sw, 1);
    sx = 0;
    dx += sw;
    dw -= sw;
    sw = min(source->w, dw);
  };
};

void blit_ex ( BITMAP *source, BITMAP *dest, int sx, int sy, int dx, int dy, int sw, int sh, int dw, int dh )
{
  int ssw, ssx, ddw, ddx;
  if ( !source ) source = ScreenContext;
  if ( !dest ) dest = ScreenContext;
  sx %= source->w;
  sy %= source->h;
  sw = min(source->w-sx, min(sw, dw));
  sh = min(source->h-sy, min(sh, dh));
  ssw = sw; ssx = sx; ddw = dw; ddx = dx;
  while ( dh ) {
    sw = ssw;
    sx = ssx;
    dw = ddw;
    dx = ddx;
    while ( dw ) {
      blit(source, dest, sx, sy, dx, dy, sw, sh);
      sx = 0;
      dx += sw;
      dw -= sw;
      sw = min(source->w, dw);
    };
    sy = 0;
    dy += sh;
    dh -= sh;
    sh = min(source->h, dh);
  };
};

void blit_hline ( BITMAP *source, BITMAP *dest, int sx, int sy, int dx, int dy, int sw, int dw ) {
  if ( !source ) source = ScreenContext;
  if ( !dest ) dest = ScreenContext;
  if ( !source->w || !source->h ) return;
  sx %= source->w;
  sy %= source->h;
  sw = min(source->w-sx, min(sw, dw));
  while ( dw ) {
    blit(source, dest, sx, sy, dx, dy, sw, 1);
    sx = 0;
    dx += sw;
    dw -= sw;
    sw = min(source->w, dw);
  };
};

/* blit_ellipsefill:
 *  Draws a blit filled circle. Without trying to use any hardware feature on the card - pity.
 */
void blit_circlefill(BITMAP *source, BITMAP *dest, int sx, int sy, int dx, int dy, int sw, int radius)
{
   int cx = 0;
   int cy = radius;
   int df = 1 - radius; 
   int d_e = 3;
   int d_se = -2 * radius + 5;
   int vx = (dx-radius)-sx;
   int vy = (dy-radius)-sy;
   if ( !source ) source = ScreenContext;
   if ( !dest ) dest = ScreenContext;
   do {
      __blit_hline(source, dest, (dx-cy)-vx, (dy-cx)-vy, dx-cy, dy-cx, sw, dx+cy-(dx-cy));
      if (cx)
        __blit_hline(source, dest, (dx-cy)-vx, (dy+cx)-vy, dx-cy, dy+cx, sw, dx+cy-(dx-cy));
      if (df < 0)  {
	 df += d_e;
	 d_e += 2;
	 d_se += 2;
      }
      else { 
	 if (cx != cy) {
           __blit_hline(source, dest, (dx-cx)-vx, (dy-cy)-vy, dx-cx, dy-cy, sw, dx+cx-(dx-cx));
	    if (cy)
              __blit_hline(source, dest, (dx-cx)-vx, (dy+cy)-vy, dx-cx, dy+cy, sw, dx+cx-(dx-cx));
	 }
	 df += d_se;
	 d_e += 2;
	 d_se += 4;
	 cy--;
      } 
      cx++; 
   } while (cx <= cy);
}

/* blit_ellipsefill:
 *  Draws a blit filled ellipse. without using hardware - shame.
 */
void blit_ellipsefill(BITMAP *source, BITMAP *dest, int sx, int sy, int dx, int dy, int sw, int rx, int ry)
{
   int x, y;
   int a, b, c, d;
   int da, db, dc, dd;
   int na, nb, nc, nd;
   int vx = (dx-rx)-sx, vy = (dy-ry)-sy;
   if ( !source ) source = ScreenContext;
   if ( !dest ) dest = ScreenContext;
   if (rx < 1)
      rx = 1;
   if (ry < 1) 
      ry = 1;
   if (rx > ry) {
      dc = -1;
      dd = 0xFFFF;
      x = 0; 
      y = rx * 64;
      na = 0; 
      nb = (y + 32) >> 6;
      nc = 0; 
      nd = (nb * ry) / rx;
      do {
       a = na; 
       b = nb; 
       c = nc; 
       d = nd;
       x = x + (y / rx);
       y = y - (x / rx);
       na = (x + 32) >> 6; 
       nb = (y + 32) >> 6;
       nc = (na * ry) / rx; 
       nd = (nb * ry) / rx;
       if ((c > dc) && (c < dd)) {
         __blit_hline(source, dest, (dx-b)-vx, (dy+c)-vy, dx-b, dy+c, sw, dx+b-(dx-b));
         if (c)
         __blit_hline(source, dest, (dx-b)-vx, (dy-c)-vy, dx-b, dy-c, sw, dx+b-(dx-b));
         dc = c;
       }
       if ((d < dd) && (d > dc)) { 
         __blit_hline(source, dest, (dx-a)-vx, (dy+d)-vy, dx-a, dy+d, sw, dx+a-(dx-a));
         __blit_hline(source, dest, (dx-a)-vx, (dy-d)-vy, dx-a, dy-d, sw, dx+a-(dx-a));
         dd = d;
       }
      } while(b > a);
   } 
   else {
      da = -1;
      db = 0xFFFF;
      x = 0; 
      y = ry * 64; 
      na = 0; 
      nb = (y + 32) >> 6;
      nc = 0; 
      nd = (nb * rx) / ry;
      do {
       a = na; 
       b = nb; 
       c = nc; 
       d = nd; 
       x = x + (y / ry); 
       y = y - (x / ry);
       na = (x + 32) >> 6; 
       nb = (y + 32) >> 6;
       nc = (na * rx) / ry; 
       nd = (nb * rx) / ry;
       if ((a > da) && (a < db)) {
         __blit_hline(source, dest, (dx-d)-vx, (dy+a)-vy, dx-d, dy+a, sw, dx+d-(dx-d));
         if (a)
          __blit_hline(source, dest, (dx-d)-vx, (dy-a)-vy, dx-d, dy-a, sw, dx+d-(dx-d));
          da = a;
       }
       if ((b < db) && (b > da)) { 
         __blit_hline(source, dest, (dx-c)-vx, (dy+b)-vy, dx-c, dy+b, sw, dx+c-(dx-c));
         __blit_hline(source, dest, (dx-c)-vx, (dy-b)-vy, dx-c, dy-b, sw, dx+c-(dx-c));
         db = b;
       }
      } while(b > a);
   }
}

#ifndef uchar
#define uchar        unsigned char
#endif
#ifndef ulong
#define ulong        unsigned long
#endif
#define lrgb         long

#define sizeuchar    1
#define sizeint      2
#define sizelrgb     3
#define sizelong     4

BITMAP *gen_neg ( int depth, BITMAP *src ) { // if depth == 0 create standard bitmap
  #define loop_neg(d,s,rgb1,rgb2)                           \
    while ( w-- ) {                                         \
      ssdat = *((s*)sdat);                                  \
        rcol  = getr##rgb2(ssdat);                          \
        gcol  = getg##rgb2(ssdat);                          \
        bcol  = getb##rgb2(ssdat);                          \
                                                            \
        rcol = 255-rcol;                                    \
        gcol = 255-gcol;                                    \
        bcol = 255-bcol;                                    \
                                                            \
        *((d*)bdat) = (d)makecol##rgb1(rcol, gcol, bcol);   \
      sdat += size##s;                                      \
      bdat += size##d;                                      \
    }
  int y = 0;
  int w = src->w, h = src->h;
  int ssdat = 0;
  unsigned char *sdat, *bdat;
  unsigned char rcol, gcol, bcol;
  BITMAP *bmp = NULL;
  if ( depth ) bmp = create_bitmap_ex(depth, src->w, src->h);
    else bmp = create_bitmap(src->w, src->h);
  if ( !bmp ) return NULL;
  h = min(h, bmp->h);
  while ( h-- ) {
    sdat = src->line[y];
    bdat = bmp->line[y];
    switch ( bmp->vtable->color_depth ) {
      case 8: switch ( src->vtable->color_depth ) {
             case 8 : loop_neg(uchar, uchar, 8, 8); break;
             case 15: loop_neg(uchar, int, 8, 15); break;
             case 16: loop_neg(uchar, int, 8, 16); break;
             case 24: loop_neg(uchar, lrgb, 8, 24); break;
             case 32: loop_neg(uchar, long, 8, 32); break;
           }; break;
      case 15: switch ( src->vtable->color_depth ) {
             case 8 : loop_neg(int, uchar, 15, 8); break;
             case 15: loop_neg(int, int, 15, 15); break;
             case 16: loop_neg(int, int, 15, 16); break;
             case 24: loop_neg(int, lrgb, 15, 24); break;
             case 32: loop_neg(int, long, 15, 32); break;
           }; break;
      case 16: switch ( src->vtable->color_depth ) {
             case 8 : loop_neg(int, uchar, 16, 8); break;
             case 15: loop_neg(int, int, 16, 15); break;
             case 16: loop_neg(int, int, 16, 16); break;
             case 24: loop_neg(int, lrgb, 16, 24); break;
             case 32: loop_neg(int, long, 16, 32); break;
           }; break;
      case 24: switch ( src->vtable->color_depth ) {
             case 8 : loop_neg(lrgb, uchar, 24, 8); break;
             case 15: loop_neg(lrgb, int, 24, 15); break;
             case 16: loop_neg(lrgb, int, 24, 16); break;
             case 24: loop_neg(lrgb, lrgb, 24, 24); break;
             case 32: loop_neg(lrgb, long, 24, 32); break;
           }; break;
      case 32: switch ( src->vtable->color_depth ) {
             case 8 : loop_neg(long, uchar, 32, 8); break;
             case 15: loop_neg(long, int, 32, 15); break;
             case 16: loop_neg(long, int, 32, 16); break;
             case 24: loop_neg(long, lrgb, 32, 24); break;
             case 32: loop_neg(long, long, 32, 32); break;
           }; break;
    };
    y++;
    w = min(src->w, bmp->w);
  };
  return bmp;
};

BITMAP *gen_emboss ( int depth, BITMAP *src ) { // if depth == 0 create standard bitmap
  #define loop_print(d,s,rgb1,rgb2)                         \
    while ( w-- ) {                                         \
      ssdat = *((s*)sdat);                                  \
      dddat = *((s*)ddat);                                  \
        rcol  = getr##rgb2(ssdat);                          \
        gcol  = getg##rgb2(ssdat);                          \
        bcol  = getb##rgb2(ssdat);                          \
                                                            \
        drcol  = getr##rgb2(dddat);                         \
        dgcol  = getg##rgb2(dddat);                         \
        dbcol  = getb##rgb2(dddat);                         \
                                                            \
        rcol = (rcol+(255-drcol))>>1;                       \
        gcol = (gcol+(255-dgcol))>>1;                       \
        bcol = (bcol+(255-dbcol))>>1;                       \
                                                            \
        *((d*)bdat) = (d)makecol##rgb1(rcol, gcol, bcol);   \
      sdat += size##s;                                      \
      ddat += size##s;                                      \
      bdat += size##d;                                      \
    }
  int y = 0;
  int w = src->w-1, h = src->h-1;
  int ssdat = 0;
  int dddat = 0;
  unsigned char *sdat, *ddat, *bdat;
  unsigned char rcol, gcol, bcol, drcol, dgcol, dbcol;
  BITMAP *bmp = NULL;
  if ( depth ) bmp = create_bitmap_ex(depth, src->w, src->h);
    else bmp = create_bitmap(src->w, src->h);
  if ( !bmp ) return NULL;
  clear(bmp);
  h = min(h, bmp->h-1);
  if ( (w > 1) && (h > 1) )
  while ( h-- ) {
    sdat = src->line[y];
    ddat = src->line[y+1]+2;
    bdat = bmp->line[y];
    switch ( bmp->vtable->color_depth ) {
      case 8: switch ( src->vtable->color_depth ) {
             case 8 : loop_print(uchar, uchar, 8, 8); break;
             case 15: loop_print(uchar, int, 8, 15); break;
             case 16: loop_print(uchar, int, 8, 16); break;
             case 24: loop_print(uchar, lrgb, 8, 24); break;
             case 32: loop_print(uchar, long, 8, 32); break;
           }; break;
      case 15: switch ( src->vtable->color_depth ) {
             case 8 : loop_print(int, uchar, 15, 8); break;
             case 15: loop_print(int, int, 15, 15); break;
             case 16: loop_print(int, int, 15, 16); break;
             case 24: loop_print(int, lrgb, 15, 24); break;
             case 32: loop_print(int, long, 15, 32); break;
           }; break;
      case 16: switch ( src->vtable->color_depth ) {
             case 8 : loop_print(int, uchar, 16, 8); break;
             case 15: loop_print(int, int, 16, 15); break;
             case 16: loop_print(int, int, 16, 16); break;
             case 24: loop_print(int, lrgb, 16, 24); break;
             case 32: loop_print(int, long, 16, 32); break;
           }; break;
      case 24: switch ( src->vtable->color_depth ) {
             case 8 : loop_print(lrgb, uchar, 24, 8); break;
             case 15: loop_print(lrgb, int, 24, 15); break;
             case 16: loop_print(lrgb, int, 24, 16); break;
             case 24: loop_print(lrgb, lrgb, 24, 24); break;
             case 32: loop_print(lrgb, long, 24, 32); break;
           }; break;
      case 32: switch ( src->vtable->color_depth ) {
             case 8 : loop_print(long, uchar, 32, 8); break;
             case 15: loop_print(long, int, 32, 15); break;
             case 16: loop_print(long, int, 32, 16); break;
             case 24: loop_print(long, lrgb, 32, 24); break;
             case 32: loop_print(long, long, 32, 32); break;
           }; break;
    };
    y++;
    w = min(src->w-1, bmp->w-1);
  };
  return bmp;
};
static l_int    screen_depth;

BITMAP *conv_to_skipcolor_bitmap ( BITMAP *ctx, int r, int g, int b ) {
  int x, y = 0;
  if ( !ctx || (ctx == screen) ) return NULL;
  for ( y = 0; y < ctx->h; y++ )
    for ( x = 0; x < ctx->w; x++ ) {
      GrCOLOR color = getpixel(ctx, x, y);
      if ( (getr(color) == r) && (getg(color) == g) && (getb(color) == b) ) {
         putpixel(ctx, x, y, ctx->vtable->mask_color);
      }
    };
  return ctx;
};

DATAFILE *conv_to_skipcolor_data ( DATAFILE *f, int r, int g, int b ) {
  int c = 0;
  if ( f )
   for (c=0; f[c].type != DAT_END; c++) {
      if ( f[c].type == DAT_BITMAP )
         f[c].dat = (BITMAP*)conv_to_skipcolor_bitmap((BITMAP*)(f[c].dat), r, g, b);
      else
      if ( f[c].type == DAT_FILE )
         conv_to_skipcolor_data((DATAFILE*)(f[c].dat), r, g, b);
   };
  return f;
};


ICONLIBRARY  *load_icon_library ( char *filename, int r, int g, int b )
{
    DATAFILE *dat = load_skip_datafile ( filename, r, g, b );
    return convert_to_icon_library ( dat );
};


void  unload_icon_library ( ICONLIBRARY *icl )
{
   while ( icl ) {
     ICONLIBRARY *i = icl->next;
     if ( icl->datfile ) unload_datafile(icl->datfile);
     _free(icl);
     icl = i;
   };

};


BITMAP  *get_icon_from_library ( ICONLIBRARY *icl, int id, int size )
{
   while ( icl ) {
      if ( icl->id == id ) {
          if ( size == ICON_32 ) {
             if ( icl->icon32 ) return icl->icon32;
             else return icl->icon16;
          } else
             if ( icl->icon16 ) return icl->icon16;
             else return icl->icon32;
          return icl->icon32;
      };
      icl = icl->next;
   };
   return NULL;
};


static ICONLIBRARY  *_convert_to_icon_library ( DATAFILE *f, ICONLIBRARY *icl, int id )
{
  int c = 0;
  int h = 0;
  if ( f ) {
     ICONLIBRARY *i = NULL;
     if ( id ) {
       i = (ICONLIBRARY*)_malloc(sizeof(ICONLIBRARY));
       memset(i, 0, sizeof(ICONLIBRARY));
       if ( !icl ) i->datfile = f;
       i->id = id-1;
     };
     for (c=0; f[c].type != DAT_END; c++) {
          if ( i && (f[c].type == DAT_BITMAP) && (h < 2) ) {
            if ( h == 1 ) i->icon16 = (BITMAP*)(f[c].dat);
            else
            if ( h == 0 ) i->icon32 = (BITMAP*)(f[c].dat);
            h++;
          } else
          if ( f[c].type == DAT_FILE ) {
             ICONLIBRARY *t;
             id++;
             t = _convert_to_icon_library ( (DATAFILE*)(f[c].dat), i, id );
             t->next = i;
             i = t;
          };
     };
     return i;
  };
  return NULL;
};


ICONLIBRARY  *convert_to_icon_library ( DATAFILE *f )
{
  return _convert_to_icon_library(f, NULL, 0);
};



void *get_datafile_object ( DATAFILE *dat, int obj_num )
{
  int c = 0;
  if ( dat )
    for (c=0; dat[c].type != DAT_END; c++) {
      if ( c == obj_num ) return dat[c].dat;
    };
  return NULL;
};


DATAFILE *load_skip_datafile ( char *filename, int r, int g, int b ) {
  DATAFILE *f = load_datafile(filename);
  f = conv_to_skipcolor_data(f, r, g, b);
  return f;
};

int  _gr_setmode ( int _s, int _w, int _h, int _v_w, int _v_h, char _depth )
{
/* new allegro insists on 8 15 16 24 or 32 (at least in debug mode - presumably does it wrongly in non-debug mode) */
  set_color_depth((_depth<8)?8:_depth);
#ifndef NO_SCREEN
  if ( set_gfx_mode(_s, _w, _h, _v_w, _v_h) ) {
    return 0;
  };
#endif
  if ( (_depth < 15) && (_s >= 0) ) { // dunno about >= 0
     PALLETE pal;
     generate_332_palette(pal);
//     pal[0].r = 0;
//     pal[0].g = 0;
//     pal[0].b = 0;
#ifndef NO_SCREEN
     set_pallete(pal);
#endif
  };
  return 1;
};

extern FONT* load_ttf_font_ex (const char* filename, const int points_w, const int points_h, const int smooth);
extern l_int font_smooth;
extern FONT *load_font(char *f);
static void *_seal_load_font ( char *_filename, char *xxname, int w, int h, int from, int to ) {
  /* you shouldnt make an 8 point font anything but 8 point anyhow */
  void *f=NULL;
  if(_filename){
   f=load_font(_filename);
   if ( !f ) f = (void*)load_ttf_font_ex(_filename, w, h, font_smooth);
  }
  return f;
};

static void  _destroy_font ( void *f ) {
  destroy_font((FONT*)f);
};

void   *(*seal_load_font)(char* _filename, char *xxname, int w, int h, int from, int to) = &_seal_load_font;

void    (*unload_font)(void *f) = &_destroy_font;

/*#include"dataini.c"*/
/****************************************************************/
/*                                                              */
/*                          dataini.c                           */
/*                                                              */
/*                Seal - free desktop environment               */
/*                                                              */
/*                    Copyright (c) 1999,2000                   */
/*                         Michal Stencl                        */
/*                     All Rights Reserved                      */
/*                                                              */
/* mail : stenclpmd@ba.telecom.sk                               */
/* web  : http://www.home.sk/public/seal/                       */
/*                                                              */
/* This file is part of Seal.                                   */
/*                                                              */
/* Seal is free software; you can redistribute it and/or        */
/* modify it under the terms of the GNU General Public License  */
/* as published by the Free Software Foundation; either version */
/* 2, or (at your option) any later version.                    */
/*                                                              */
/* Seal is distributed in the hope that it will be useful, but  */
/* WITHOUT ANY WARRANTY; without even the implied warranty of   */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See    */
/* the GNU General Public License for more details.             */
/*                                                              */
/* You should have received a copy of the GNU General Public    */
/* License along with Seal; see the file COPYING.  If not,      */
/* write to the Free Software Foundation, 675 Mass Ave,         */
/* Cambridge, MA 02139, USA.                                    */
/*                                                              */
/****************************************************************/


#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#include<pc.h>
#include<io.h>
#include"dataini.h"
#define INI_ISENTER(d)  ((*(d) == INI_ENTERLINE) && !isspace(*((d)+1)))
#define INI_ENTERLINE   INI_ENDLINE
#define INI_FIRSTCHAR   '['
#define INI_LASTCHAR    ']'
#define INI_FUNCFIRST   '('
#define INI_FUNCLAST    ')'
#define INI_CEQUALS     '='
#define INI_ENDLINE     '\n'
#define INI_EQUALS      "="
#define INI_NUMBER      long
#define INI_NONE        "(none)"
#define INI_TEXTCHAR    '"'
#define INI_CFIRSTCHAR  "["
#define INI_CLASTCHAR   "]"

#ifndef strsize
#define strsize(p,v)    ((v)?((v)-(p)):strlen(p))
#endif

#ifndef lmin
#define lmin(x,y)   (((x)<(y))?(x):(y))
#endif
#ifndef lmax
#define lmax(x,y)   (((x)>(y))?(x):(y))
#endif

static INI_NUMBER  ini_number;

/* UNUSED
static char*  delfrommem ( char *_dest, long _pos, long _size )
{
  if ( _dest ) {
    long ttsize = strlen(_dest);
    _size = lmax(0, lmin((ttsize-_pos), _size));
    _pos  = lmax(0, lmin(ttsize-1, _pos));
    if ( _size > 0 ) {
      char *p = _strdup(&_dest[_pos+_size]);
      char *n = (char*)_realloc(_dest, ttsize-_size);
      strcpy(&n[_pos], p);
      _free(p);
      return n;
    };
  };
  return _dest;
};
*/

#ifdef __RSXNT__
extern void bzero(char *mem,long size);
#endif
static char *addtomem ( char *mem, long where, char *what1, char *what2, char *what3, char *what4 ) {
  long s1 = strlen(what1);
  long s2 = strlen(what2);
  long s3 = strlen(what3);
  long s4 = strlen(what4);
  long size = s1+s2+s3+s4;
  if ( size ) {
    if ( !mem ) {
      mem = (char *)_malloc(size+1);
      if ( mem ) {
        bzero(mem, size+1);
        strcat(mem, what1);
        strcat(mem, what2);
        strcat(mem, what3);
        strcat(mem, what4);
      };
    } else {
      long max = strlen(mem);
      if ( where < 0 ) where = max; else where = lmax(0, lmin(max, where));
      mem = (char *)_realloc(mem, max+size+1);
      if ( mem ) {
        char *s;
        bzero(&mem[max], size+1);
        s = stridup(&mem[where], lmax(0, max-where));
        strcat(&mem[where], what1);
        strcat(&mem[where+s1], what2);
        strcat(&mem[where+s1+s2], what3);
        strcat(&mem[where+s1+s2+s3], what4);
        if ( s ) strcat(&mem[where+size], s);
      };
    };
  }
  return mem;
};

char *strtoend ( char *str, char endchar ) {
  if ( str ) {
    char *v = strchr(str, endchar);
    long size = 0;
    if ( v ) size = v-str; else size = strlen(str);
    if ( size ) {
      char *t = (char *)_malloc(size+1);
      if ( t ) {
        bzero(t, size+1);
        strncat(t, str, size);
      };
      return t;
    };
  };
  return NULL;
};


static char *transini_line ( char *line ) {
   if ( !line || strstr(line, INI_NONE) ) return NULL;
   else {
     char *endp;
     endp = strchr(line, INI_TEXTCHAR);
     if ( endp ) return strtoend(endp+1, INI_TEXTCHAR); /* string */
     ini_number = strtol(line, &endp, 0);
     if ( endp ) return ((char*)(&ini_number)); /* number */
   };
   return NULL; /* else it's string */
};

static char *copy_not_space ( char *dst, char *src, int sz )
{
  char *o = dst;
  if ( dst && src ) {
    while ( sz > 0 ) {
      if ( (*src != ' ') && (*src != 10) && (*src != 13) && (*src != 9) ) {
        *dst = *src;
        dst++;
      };
      src++;
      sz--;
    };
  };
  return o;
};

static char *strli ( char **p, int *type )
{
      #define __v  (*p)
      if ( __v ) {
        char *oldp = __v;
        int   size;
        char *fname;
        __v = strchr(__v, ',');
        size = strsize(oldp, __v);
        if ( __v ) __v++; else __v = NULL;
        fname = (char *)_malloc(size+1);
        if ( fname ) {
          bzero(fname, size+1);
          copy_not_space(fname, oldp, size);
          if ( *fname == '$' ) {/* first char is $, so it's string */
             char *t = stridup(&fname[1], strlen(fname)-1);
             (*type) = INI_STRING;
             _free(fname);
             fname = t;
          } else {
            char *t = transini_line(fname);
            _free(fname);
            fname = t;
            (*type) = INI_DECANUM;
          };
       };
       return fname;
     };
     return NULL;
};

/*
  get arguments or values form the definition strvalue and divide it to sections.
  example of "seal.ini" :
  ...
  [fonts]
  symbol="$hello, 1, $mr., $stencl"
  ...
  char *def;
  ini_data *d = getinidata_fromfile("seal.ini", "fonts");
  char *value = getini_line ( NULL, d, 0);
  char **f = getini_values(value);
  (*f) point to memory of array. Each item contains these information :
  0    byte  = type
  1... bytes = data
  In this case the f[0][0] = INI_STRING and &(f[0][1]) = pointer to memory,
  where string "hello" is stored.
                   f[1][0] = INI_DECANUM and *((long*)(f[1][1])) = 1.
                   f[2][0] = INI_STRING and &(f[2][1]) = pointer to memory, where
  string "mr" is stored.
                   f[3][0] = INI_STRING and &(f[3][1]) = pointer to memory, where
  string "stencl" is stored.
  This array is ended by ZERO. - if f[x] == NULL
*/
char **getini_values ( char *strvalue )
{
  #define ret7(x) do { \
     _free(val);       \
     freeini_values(all); \
     return x;        \
  } while (0)
  char **all = NULL;
  int n = 0;
  while ( strvalue ) {
    int type = 0;
    char *val = strli(&strvalue, &type);
    if ( type ) {
      if ( !all ) {
        all = (char**)_malloc((n+2)*sizeof(char*));
        if ( !all ) ret7(NULL);
        memset(all, 0, (n+2)*sizeof(char*));
        n++;
      } else {
        all = (char**)_realloc(all, (n+2)*sizeof(char*));
        if ( !all ) ret7(NULL);
        all[n+1] = NULL;
        n++;
      };
      if ( all ) {
        char *t = NULL;
        if ( type == INI_STRING ) {
          int s = strlen(val);
          t = (char*)_malloc(s+2);
          if ( !t ) ret7(NULL);
          t[0] = (char)type;
          memcpy(&t[1], val, s+1);
        } else {
          t = (char*)_malloc(sizeof(long)+sizeof(char));
          if ( !t ) ret7(NULL);
          t[0] = (char)type;
          *((long*)(&t[1])) = *((long*)val);
        };
        all[n-1] = t;
        all[n] = NULL;
      };
    };
    if ( type == INI_STRING ) _free(val);
  };
  #undef ret7
  return all;
};

/*
  free values you got by function "char **getini_values ( char *strvalue );"
*/
void  freeini_values ( char **values )
{
  if ( values ) {
    char **v = values;
    while ( *v ) {
      char *t = (*v);
      v++;
      _free(t);
    };
    _free(values);
  };
};

/*
 get number of lines in structure (dat), you got by function
 "ini_data *getinidata_fromfile ( char *filename, char *ininame )" ...see above
 example:
 "seal.ini" file :
 ...
 [SEAL]
 info = "desktop environment"
 version = 1
 ...

 ini_data *d = getinidata_fromfile("seal.ini", "SEAL");
 int lines = getini_linenum(d);
 (lines) now equals to 2 = number of lines in [SEAL] structure.
*/
int   getini_linenum ( ini_data *dat )
{
  int lines = 0;
  if ( dat ) {
    while ( *dat ) {
      if ( INI_ISENTER(dat) ) /* enter */
        lines++;
      dat++;
    };
  };
  return lines;
};

static char *while_text ( char *text ) {
  char *oldt = NULL;
  if ( text )
    while ( *text ) {
      if ( *text == INI_CEQUALS ) return oldt;
      if ( !isspace(*text) ) oldt = text+1;
      text++;
    };
  return text;
};

/*
 return information about line (line) from structure (dat), you got by function
 "ini_data *getinidata_fromfile ( char *filename, char *ininame )" ...see above
 example:
 ...
 [SEAL]
 info = "desktop environment"
 version = 1
 ...
 ini_data *d = getinidata_fromfile("seal.ini", "SEAL");
 char *head = NULL;
 char *def = getini_line ( &head, d, 1);
 (def) now contains text "desktop environment" and (head) contains text "info".
*/
char *getini_line ( char **def, ini_data *dat, int line )
{
  int i = 0;
  if ( def ) *def = NULL;
  if ( dat ) {
    while ( *dat && (i < line) ) {
      if ( INI_ISENTER(dat) ) /* enter */
        i++;
      dat++;
    };
    if ( *dat ) {
      char *cline = NULL;
      char *out = NULL;
      char *e = strchr(dat, INI_ENTERLINE);
      if ( e && (*e == INI_ENTERLINE) ) e = strchr(e+1, INI_ENTERLINE);
      cline = stridup(dat, strsize(dat, e));
      if ( cline ) {
        char *to = while_text(cline);
        char *eq = strchr(cline, INI_CEQUALS);
        if ( def ) *def = stridup(cline, strsize(cline, to));
        if ( eq ) out = transini_line(eq+1);
      };
      _free(cline);
      return out;
    };
  };
  return NULL;
};

/*
  get arguments form the "function" (one). in (def) is returned pointer to memory, where
  the header is stored and function return pointer to array of values, that line
  contains.
  example of "seal.ini" :
  ...
  [fonts]
  symbol("Hello",-1, "Hi")= "Hello everybody !"
  ...
  char *def;
  ini_data *d = getinidata_fromfile("seal.ini", "fonts");
  char *value = getini_line ( &def, d, 0);
  char **f = getini_function ( &func, def );
  (*def) point to memory, where the text "Hello everybody !" is stored.
  (*f) point to memory of array. Each item contains these information :
  0    byte  = type
  1... bytes = data
  In this case the f[0][0] = INI_STRING and &(f[0][1]) = pointer to memory,
  where string "Hello" is stored.
                   f[1][0] = INI_DECANUM and *((long*)(f[1][1])) = -1.
                   f[2][0] = INI_STRING and &(f[2][1]) = pointer to memory, where
  string "Hi" is stored.
  This array is ended by ZERO. - if f[x] == NULL
*/
char **getini_function ( char **def, char *one )
{
  char *n;
  char *v;
  char *v1;
  if ( !def ) *def = NULL;
  if ( !one ) return NULL;
  n = strchr(one, INI_FUNCFIRST);
  v = NULL;
  v1 = NULL;
  if ( def ) *def = stridup(one, strsize(one, n));
  if ( n ) {
    v = strchr(n+1, INI_FUNCLAST);
    if ( v ) {
      char **x = NULL;
      v1 = stridup(n+1, strsize(n, v)-1);
      x = getini_values(v1);
      _free(v1);
      return x;
    };
  };
  return NULL;
};

/*
 read line (ixname) from inidata (dat) you have got by previous funtion.
*/
char  *getini_value ( ini_data *dat, char *ixname ) {
  if ( dat && ixname ) {
    char *p = dat;
    int ok = 0;
    while ( p && !ok ) {
      p = strstr(p, ixname);
      if ( p && (*(p-1) == '\n') ) ok = 1; /* if found it and it's placed at first char in line */
      else if ( p ) p++;
    };
    if ( ok ) {
      char *v = strstr(p+1, INI_EQUALS);
      if ( v ) v = strtoend(v+strlen(INI_EQUALS), INI_ENDLINE);
      p = transini_line(v);
      _free(v);
      return p;
    };
  };
  return NULL;
};


static ini_data *searchini ( char *mem, char *ininame, int _alloc ) {
  if ( mem && ininame ) {
    int   s = strlen(ininame);
    char *p = mem;
    int   ok = 0;
    while ( p && !ok ) {
      p = strstr(p, ininame);
      if ( p && (p != mem) && (*(p-1) == INI_FIRSTCHAR) && (*(p+s) == INI_LASTCHAR) )
        ok = 1; /* I found ininame and first and last ini char */
      else if ( p ) p++;
    };
    if ( ok ) { /* if I found ininame */
      p = p+s+1; /* move to char after last ini char */
      return _alloc?(ini_data *)strtoend(p, INI_FIRSTCHAR):p;
    };
  };
  return NULL;
};

static char *getini_ixname ( ini_data **mem, char *ininame, char *ixname ) {
  if ( mem && *mem && ininame && ixname ) {
    long siz = 0;
    ini_data *v = NULL;
    *mem = searchini(*mem, ininame, 0);
    v = (ini_data *)strtoend(*mem, INI_FIRSTCHAR);
    siz = strlen(v);
    if ( v ) { /* if I found ininame */
      char *p = v;
      int   ok = 0;
      while ( p && !ok ) {
        p = strstr(p, ixname);
        if ( (p != v) && p && (*(p-1) == '\n') )
          ok = 1; /* I found ixname */
        else if ( p ) p++;
      };
      if ( ok ) { /* if I found ixname */
        *mem += p-v;
        p = strtoend(p, '\n');
        _free(v);
        return p;
      };
      p = v+siz-1;
      while ( (p != v) && (*p == '\n') ) {
        p--;
        siz--;
      };
      if ( p != v ) siz++;
      _free(v);
      *mem += siz;
    };
  };
  return NULL;
};


/*
  get number from ini file (filename), from section [ininame] and in the colum (ixname)
  example of seal.ini :
  ...
  [mouse]
  speed = 1
  ....
  long x = getininum_fromfile ("seal.ini", "mouse", "speed");
  x contains 1
*/
long getininum_fromfile ( char *filename, char *ininame, char *ixname )
{
  long *x = ((long*)getini_fromfile(filename, ininame, ixname));
  if ( x ) return (*x);
  else return 0;
};

/*
 read data from file (filename) and structure (ininame).
 This read all lines from structure [ininame] to next
 structure or to end of the file.
*/
char *inisavefile=NULL; /* cache reading the ini file (should help unless multiple ini file calls are interleaved) */
char *inimem=NULL;
ini_data *getinidata_fromfile ( char *filename, char *ininame ) {
  ini_data *d = NULL;
  FILE *f;
  if ((inimem==NULL) || (inisavefile==NULL) || (strlen(filename)!=strlen(inisavefile)) || strcmp(inisavefile,filename) ) {
   if (inimem) {_free(inimem);inimem=NULL;}
   if (inisavefile) {_free(inisavefile);inisavefile=NULL;}
   f = fopen(filename, "rt");
   if ( f ) {
     long size = filelength(fileno(f));
     inimem = (char *)_malloc(size+1);
     if ( inimem ) {
       bzero(inimem, size+1);
       fread(inimem, size, 1, f);
       d = searchini(inimem, ininame, 1);
     };
     /* remember the filename for the data we have cached */
     inisavefile=(char *)_malloc(strlen(filename)+1);
     if (inisavefile) strcpy(inisavefile,filename);
     fclose(f); 
   };
  } else {
   d = searchini(inimem, ininame, 1);
  }
  return d;
};

/*
 return color from INI file (filename), from structure (ininame),
 from line (ixname).
 typedef struct ini_rgb {
   int r; - index of red color
   int g; - index of green color
   int b; - index of blue color
   int x; - reserved
 } ini_rgb;
 example:
 "seal.ini" file :
 ...
 [colors]
 desktop = "255, 0, 128"
 ...
 ini_rgb rgb = geini_color("seal.ini", "colors", "desktop");
 now rgb contains in r (255), in g (0) and in b (128).
*/
ini_rgb  *getini_color ( char *filename, char *ininame, char *ixname )
{
  static ini_rgb xcolor; /* static cos we are passing its address back */
  char *scolor  = getini_fromfile (filename, ininame, ixname);
  xcolor.r = 0;
  xcolor.g = 0;
  xcolor.b = 0;
  if ( scolor ) {
    char **v = getini_values ( scolor );
    int     r = 0;
    int     g = 0;
    int     b = 0;
    if ( v && v[0] && (*(v[0]) == INI_DECANUM) )
      r = *((long*)&(v[0][1]));
    if ( v && v[1] && (*(v[1]) == INI_DECANUM) )
      g = *((long*)&(v[1][1]));
    if ( v && v[2] && (*(v[2]) == INI_DECANUM) )
      b = *((long*)&(v[2][1]));
    xcolor.r = r;
    xcolor.g = g;
    xcolor.b = b;
    freeini_values(v);
    _free(scolor);
    return &xcolor;
  };
  return NULL;
};

/*
 read value that is set in file (filename) in structure (ininame)
 and in line (ixname)
 example :
 seal.ini file :
 ...
 [SEAL]
 info = "desktop environment"
 version = 1
 ...
...to get, what is set in [SEAL], [info] line you simple call
char *i = getini_fromfile("seal.ini", "SEAL", "info");

...to get, what is set in [SEAL], [version] line you simple call
long  v = *((long *)getini_fromfile("seal.ini", "SEAL", "version"));
...(i) now contains text : "desktop environment" and (v) number 1.
*/
char *getini_fromfile ( char *filename, char *ininame, char *ixname ) {
  ini_data *vx = getinidata_fromfile(filename, ininame);
  char *p = NULL;
  if ( vx ) {
   p = getini_value(vx, ixname);
   _free(vx);
  }
  return p;
};

static char *setini_toline ( char *ixname, char *value ) {
/* jdh adding support for set_key emulating BadSector's registry function.
 a null string passed just sets ixname without any = or quotes 
*/
if( strcmp(value,INI_NONE) )
   return addtomem(NULL, 0, ixname, "=", value, "\n"); // value is not "(none)" so carry on as normal
else { // value would end up as ixname="(none)" but we ignore the ="(none)" totally
   char *s=malloc(strlen(ixname)+2);
   strcpy(s,ixname);
   strcat(s,"\n");
   return s;
  }
};
#ifdef __RSXNT__
char *itoa(long value,char *buf,int radix) {sprintf(buf,(radix==16)?"%lx":((radix==10)?"%ld":"%lo"),value);return buf;}
#endif
static char *setini_todata ( char *ixname, char *value, int type ) {
  if ( ixname ) {
    switch ( type ) {
      case INI_STRING: { /* string */
        int size = strlen(value);
        if ( size ) { /* string exist */
          char *v = _malloc(size+3);
          if ( v ) {
            char *ok = NULL;
            bzero(v, size+3);
            v[0] = INI_TEXTCHAR;
            strcat(v, value);
            v[size+1] = INI_TEXTCHAR;
            ok = setini_toline(ixname, v);
            _free(v);
            return ok;
          };
        } else { /* string not exist */
/* jdh modification, this now returns just the string in ixname with a newline appended */
          return setini_toline(ixname, INI_NONE);
        };
      }; break;
      case INI_HEXANUM: { /* hexa number */
        INI_NUMBER x = *((INI_NUMBER*)(value));
        char cx1[33];
        char cx2[33+2] = {'\0'};
        strcat(cx2, "0x");
        return setini_toline(ixname, strcat(cx2, itoa(x, cx1, 16)));
      }; break;
      case INI_DECANUM: { /* deca number */
        INI_NUMBER x = *((INI_NUMBER*)(value));
        char cx1[33];
        return setini_toline(ixname, itoa(x, cx1, 10));
      }; break;
    };
  };
  return NULL;
};

/*
 set or write "value" into file (filename) to structure
 (ininame) and to line (ixname). Value is pointer to value ( text or number ),
 type is type of pointer. ( INI_DECANUM, INI_HEXANUM, INI_STRING ).
 example :
 l_int i = 1;
 setini_tofile("seal.ini", "SEAL", "info", "FREE desktop environment", INI_STRING);
 setini_tofile("seal.ini", "SEAL", "version", &i, INI_DECANUM);
 setini_tofile("seal.ini", "SEAL", "hexa version", &i, INI_HEXANUM);
 Output of "seal.ini" file :
 ...
 [SEAL]
 info = "FREE desktop environment"
 version = 1
 hexa version = 0x1
 ...

Modification jdh - if value is "" or NULL we write BadSector style registry entries.
The "line" parameter (parameter 3) in this case is the string to be written. If value is "" the string is
appended to the section provided it does not already exist in that section 
(BUG - should allow repeated identical values within the section - probably), 
if value is NULL the string replaces the (entire) sections content to the next section start.
eg:
setini_tofile("seal.ini","system/graphics/depth","16","",INI_STRING)
replaces the contents of the entire section so the section
[system/graphics/depth]
some
values
...
last
value

[next section]

becomes

[system/graphics/depth]
16

[next section]

if it had been NULL not "" then the section would read
[system/graphics/depth]
some
values
...
last
value
16

[next section]

instead.

In either case if the section does not already exist it will be created at the end of the file. 
It is up to you to maintain any parts of the tree your app needs eg You may need to expicitly 
create or modify [system] and [system/graphics] entries if you intend looking things up through them.
Eg. If you add a new section say [system/graphics/hypertext], you MAY need to append the
hypertext item to [system/graphics] if it doesnt exist and IF you intend iterating through 
[system/graphics] to visit each part of the sub-tree. If you need to change an existing
entry, (using "") you may need to collect other items before modifying it (if the section is
a list) so you can recreate its structure. eg to change
[system/graphics/hypertext]
color
size
font

[next section]

into

[system/graphics/hypertext]
color
point size
font

[next section]

you would need to iterate through and save the "color" and "font" items. Then you would write "color" (using "") 
and append "point size" and the saved "font" value (using NULL)
There is however no requirement whatesover that the tree be complete nor even connected. Simply maintain 
those parts you need and leave the rest alone.
Note that these are DOS text files, /r is verboten(ish) /n/r and /r/n are liable to be reinterpreted.
Binary data could be stored but you would need to protect /0 /r /n  [ and ] using some kind of escape scheme.
(example 0xff,0xff means 0xff, 0xff,0xfe means /0, 0xff,0xfd means /r and 0xff,0xfc means /n, 
0xff,0xfb means [ 0xff,9xfa means ], 0xff,anythingelse means anythingelse)
*/

void  setini_tofile ( char *filename, char *ininame, char *ixname, char *value, int type )
{
  if ( ininame && ixname ) {
    FILE *f = fopen(filename, "rt");
    if ( !f ) { /* if file not exist then create new one */
      f = fopen(filename, "wt");
      fclose(f);
      f = fopen(filename, "rt");
    };
    if ( f ) {
      long size = filelength(fileno(f));
/* size ends up incorrect because you are using silly-putty MSDOS text files which add/remove /r all over the place.
 You may rely on it to be at least large enough for the data.
*/
      long six  = 0;
      char *mem = (char *)_malloc(size+1);
      if ( mem ) {
        char *p = mem;
        char *ixptr=NULL;
        bzero(mem, size+1);
        fread(mem, size, 1, f);
size=strlen(mem);
/* should fix the above size problem */
if(value && *value){
        ixptr = getini_ixname(&p, ininame, ixname);
} else {
    type=INI_STRING;    /* forces jdh special interpretation in setini_todata(). */
    if(value)
        ixptr = getini_ixname(&p, ininame, ixname);     
    else
        p = searchini(p, ininame, 0);
}
        fclose(f);
        f = fopen(filename, "wt"); /* reopen file in write mode */
        if ( f ) {
          if ( p ) { /* if ininame was found */
            fwrite(mem, (p-mem), 1, f); /* write to ixname */
if( value ) {
            if ( ixptr ) { /* if ixname was found */
              six = strlen(ixptr);
              _free(ixptr);
              ixptr = setini_todata(ixname, value, type);
              fwrite(ixptr, strlen(ixptr)-1, 1, f);
              fwrite(p+six, size-(((long)p+six)-(long)mem), 1, f);
            } else { /* if ixname wasn't found */
              ixptr = setini_todata(ixname, value, type);
              fwrite(ixptr, strlen(ixptr), 1, f);
              fwrite(p, size-((long)p-(long)mem), 1, f);
            };
} else {
 /* replace the entire section with the ixname parameter */
ixptr = setini_todata(ixname, value, type);
fprintf(f, "\n"); /* enter line */
fwrite(ixptr, strlen(ixptr)-1, 1, f);
fprintf(f, "\n\n"); /* add two enter lines */
while(*p && ((mem-p)<size) && (*p != INI_CFIRSTCHAR[0] )/* '[' */ ) p++;
fwrite(p, strlen(p), 1, f);
}
          } else { /* if ininame wasn't found */
            long sz = strlen(mem);
            fwrite(mem, sz, 1, f); /* write to end of file */
            if ( mem[sz-1] != 10 )
              fprintf(f, "\n"); /* enter line */
            fprintf(f, "\n"); /* enter line */
            ixptr = addtomem(NULL, 0, INI_CFIRSTCHAR, ininame, INI_CLASTCHAR, "\n");
            fwrite(ixptr, strlen(ixptr), 1, f);
            _free(ixptr);
            if(value)
             ixptr = setini_todata(ixname, value, type);
            else
             ixptr = setini_todata(ixname, "", type);
            fwrite(ixptr, strlen(ixptr), 1, f);
          };
        };
        fclose(f);
        if(ixptr)_free(ixptr);
        if(mem)_free(mem);
      };
    };
    fclose(f);
  };
};


#include"dlx.h"
/*#include"_iodir.c"*/
/****************************************************************/
/*                                                              */
/*                        _iodir.c                              */
/*                                                              */
/*             source for IO operations under Seal              */
/*                                                              */
/*                Seal - free desktop environment               */
/*                                                              */
/*                  Copyright (c) 1999,2000                     */
/*                       Michal Stencl                          */
/*                    All Rights Reserved                       */
/*                                                              */
/* mail : stenclpmd@ba.telecom.sk                               */
/* web  : http://www.home.sk/public/seal/                       */
/*                                                              */
/* This file is part of Seal.                                   */
/*                                                              */
/* Seal is free software; you can redistribute it and/or        */
/* modify it under the terms of the GNU General Public License  */
/* as published by the Free Software Foundation; either version */
/* 2, or (at your option) any later version.                    */
/*                                                              */
/* Seal is distributed in the hope that it will be useful, but  */
/* WITHOUT ANY WARRANTY; without even the implied warranty of   */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See    */
/* the GNU General Public License for more details.             */
/*                                                              */
/* You should have received a copy of the GNU General Public    */
/* License along with Seal; see the file COPYING.  If not,      */
/* write to the Free Software Foundation, 675 Mass Ave,         */
/* Cambridge, MA 02139, USA.                                    */
/*                                                              */
/****************************************************************/


#include"_iodir.h"
#include<io.h>
#include<fcntl.h>
#include<dir.h>
#include<unistd.h>
#include<limits.h>
#include<string.h>
#include<dirent.h>

#ifdef __RSXNT__
/* these will need to be written for rsx */
int fnsplit (const char *_path, char *_drive, char *_dir, char *_name, char *_ext){return 0;} //todo
int findfirst(const char *name, struct ffblk *ff, int attrib)   {return 0;} // todo
int findnext(struct ffblk *ff) {return 0;} // todo
#endif

/* list of drives */
p_drives drives = NULL;

#ifndef BUFFER_FILE_COPY
#define BUFFER_FILE_COPY   2048  /* greater size is higher speed */
#endif
static char drv_buf[2] = {0, '\0'};
static int io_testlinks ( struct t_ffblk *ffblk )
{
    l_text ext = get_extension(ffblk->info.ff_name);
    strncpy(ffblk->ff_filename, ffblk->info.ff_name, 259);
    ffblk->ff_filename[259] = '\0';
      if ( ext && !stricmp(ext, "lnk") ) {
         ffblk->info.ff_attrib |= FA_LINK;
         fnsplit(ffblk->ff_filename, NULL, NULL, ffblk->info.ff_name, NULL);
         return ffblk->ff_attrib&FA_LINK?1:0;
      } else
      if ( ext && !stricmp(ext, "ldv") ) {
         ffblk->info.ff_attrib |= FA_LINKDIR;
         fnsplit(ffblk->ff_filename, NULL, NULL, ffblk->info.ff_name, NULL);
         return ffblk->ff_attrib&FA_LINKDIR?1:0;
      };
      return 1;
};

/*
 return new string for filename if the extension is not ".ldv" or ".lnk".
 If it is, it truncate extension and return only new string of poor filename.
 example:
 l_text link_to_directory = io_nicelink("hello.ldv");
 l_text link_to_file = io_nicelink("ciao.lnk");
 l_text file = io_nicelink("image.bmp");
 link_to_directory = "hello"
 link_to_file = "ciao"
 file = "image.bmp"
*/
l_text io_nicelink ( l_text filename )
{
   if ( filename ) {
      l_text ext = get_extension(filename);
      if ( ext && (
           !stricmp(ext, "lnk") ||
           !stricmp(ext, "ldv")) ) {
         return stridup(filename, strlen(filename)-4); /* ignore .lnk or .ldv */
      } else _strdup(filename);
   };
   return NULL;
};

/*
 allocate new memory for t_file structure and set arguments to this
 structure from function arguments.
*/
t_file *new_tfile ( l_text path, l_text filename, l_int attrib, l_word time, l_word date, l_dword size )
{
   t_file *p = (t_file *)_malloc(sizeof(t_file));
   if ( p ) {
      clear_type(p, sizeof(t_file));
      p->path = _strdup(path);
      p->filename = _strdup(filename);
      p->attrib = attrib;
      p->time = time;
      p->date = date;
      p->size = size;
   };
   return p;
};


/*
 free context of (p) and (p) too.
*/
void    free_tfile ( void *p )
{
   if ( p ) {
       _free(((t_file*)p)->filename);
       _free(((t_file*)p)->path);
   };
   _free(p);
};


/*
 find first occurence of file pathname that is defined by
 atributions "attrib". Result put into t_ffblk structure.
 Return 0, if file was found, otherwise it return non-zero.
 This function is simple to findfirst function, but it support links
 files and it keep compatility with next versions.
 Please use this function for all file searches.
*/
int io_findfirst(const char *pathname, struct t_ffblk *ffblk, int attrib)
{
   l_int done = findfirst(pathname, &ffblk->info, attrib);
   ffblk->ff_attrib = attrib;
   while ( !done && !io_testlinks(ffblk) )
     done = findnext(&ffblk->info);
   return done;
};


/*
 find next occurence of file, that you defined in previous io_findfirst function.
 Return non-zero if no next file found, or ZERO  in succesfull.
 Use this function together with io_findfirst for all file finding.
 You will keep the compatibility.
*/
int io_findnext(struct t_ffblk *ffblk)
{
   l_int done = findnext(&ffblk->info);
   while ( !done && !io_testlinks(ffblk) )
     done = findnext(&ffblk->info);
   return done;
};


/*
 run callback function for each file in directory ( path ) and its
 subdirectories etc,... Callback function contains 1 argument l_text realpath,
 what's in path+'/'+file type. (o) is pointer to t_object structure
 what control process, couldn't be used, but support halt of process.
 If something destroy this object, then process will be halted. (*ind) is
 pointer to l_dword variable that contains number of files for which were
 callback function called. Flags may contains combination of these constants :
 'DIF_SIZE'        - get size of directory
 'DIF_HALT'        - process was halted not use for function, it's only
                     returned argument
 'DIF_DIRCALLBACK' - callback function is also called for directories,
                     not only for files.
  callback  - in each file run this callback, by argument : filename
  o         - object that control this process, if object is closed ( done )
              also process is canceled.
*/
t_dirinfo io_foreach_file_ex ( l_text path, l_int flags, l_int (*callback)(), p_object o, l_dword *ind )
{
  t_dirinfo all;
  clear_type(&all, sizeof(t_dirinfo));
  if ( path ) {
    struct t_ffblk f;
    l_text files = io_realpath(path, "*.*");
    l_int done = io_findfirst(files, &f, FA_ALL);
    _while ( !done && (obj_exist(o) != TAG_DISPOSE) ) { /* multitasking while */
      if ( f.ff_filename && f.info.ff_attrib & FA_DIREC && stricmp(f.ff_filename, "..") &&
           stricmp(f.ff_filename, ".") ) { /* found directory */
        l_text f1 = io_realpath(path, f.ff_filename); /* get real filename */
        t_dirinfo dir = io_foreach_file_ex(f1, flags, callback, o, ind);
        if ( ind ) (*ind)++;
        all.files += dir.files;
        all.dirs += dir.dirs+1;
        all.size += dir.size;
        all.flags = dir.flags;
        _free(f1);
      } else if ( !(f.info.ff_attrib & FA_DIREC) ) { /* found file */
          l_text curfile = io_realpath(path, f.ff_filename);
          all.files++;
          if ( ind ) (*ind)++;
          if ( flags & DIF_SIZE ) {  /* get also sizes of files */
             l_int fl = _open(curfile, O_RDONLY);
             if ( fl != -1 ) {
                all.size += filelength(fl);
                close(fl);
             };
          };
          if ( callback ) callback(curfile);
          _free(curfile);
      };
      done = io_findnext(&f);
    };
    if ( !done ) all.flags |= DIF_HALT;
    _free(files);
  };
  if ( path && flags & DIF_DIRCALLBACK && callback ) { /* call calback for directory */
      if ( ind ) (*ind)++;
      callback(path);
  };
  return all;
};

/*
 copy directories from directory (path) to destination directory (dst).
 (o) and (ind) have same efect as in io_foreach_file_ex function ( see above ).
 return zero, if some error occured or zero if files was copyied.
*/
l_int io_foreach_file_copy ( l_text dst, l_text path, p_object o, l_dword *ind )
{
  l_int ok = 0;
  if ( path ) {
    struct t_ffblk f;
    l_text files = io_realpath(path, "*.*");
    l_int done = io_findfirst(files, &f, FA_ALL);
    ok = _io_copyfile(dst, path);
    if ( ind ) (*ind)++;
    _while ( !done && (obj_exist(o) != TAG_DISPOSE) ) { /* multitasking while */
      if ( f.ff_filename && f.info.ff_attrib & FA_DIREC && stricmp(f.ff_filename, "..") &&
           stricmp(f.ff_filename, ".") ) { /* found directory */
        l_text f1 = io_realpath(path, f.ff_filename); /* get real filename */
        l_text dst1 = io_realpath(dst, f.ff_filename); /* get real filename */
        ok = io_foreach_file_copy(dst1, f1, o, ind);
        _free(f1);
        _free(dst1);
      } else if ( !(f.info.ff_attrib & FA_DIREC) ) { /* found file */
          l_text curfile = io_realpath(path, f.ff_filename);
          l_text dstfile = io_realpath(dst, f.ff_filename);
          ok = _io_copyfile(dstfile, curfile);
          if ( ind ) (*ind)++;
          _free(curfile);
          _free(dstfile);
      };
      done = io_findnext(&f);
    };
    _free(files);
  };
  return ok;
};


/*
 rename file or directory (nameold) to (newnew). It finds if  nameold is
 directory or file and then rename it to new name (namenew).
 It return true if operation was succesfull, otherwise it return false. (false being -ve here)
*/
l_int io_rename ( l_text nameold, l_text namenew )
{
   if ( nameold && namenew ) {
      /* not same */
      if ( stricmp(nameold, namenew) ) {
          l_int t = rename(nameold, namenew);
          return abs(t);
      }
      else return -1;
   }
   return -2;
};


/*
 remove ( delete ) file/directory ( f ).
 (ob), (ind) have same effect as in io_foreach_file_ex function ( see above ).
 return zero if some error occured, else return true.
 PLEASE, CALL THIS FUNCTION FOR COPYING (files/dirs), IT CONTROLS ALL !
*/
l_int  io_removefile ( p_object ob, t_file *f, l_dword *ind )
{
    if ( f ) {
        t_dirinfo ok;
        l_text path = io_realpath(f->path, f->filename);
        if ( f->attrib & FA_DIREC )
             ok = io_foreach_file_ex(path, DIF_DIRCALLBACK, &_io_removefile, ob, ind);
        else {
            l_bool ok2 = _io_removefile(path);
            if ( ind ) (*ind)++;
            _free(path);
            return ok2;
        };
        _free(path);
        return (!(ok.flags & DIF_HALT));
    };
    return false;
};

/*
  copy file/directory (src) into destination file/directory (dst)
  - src   file for copying
  - dst   destination of file, that will be created and filled by context of src
  - ob    object ( t_view ), that's controle this process, if t_view (ob) is
          closed, than process is stoped and return false.
  return true if success, otherwise false
*/
l_int  io_copyfile ( p_object o, t_file *dst, t_file *src, l_dword *ind )
{
  if ( src && dst ) {
      l_text srcpath = io_realpath(src->path, src->filename);
      l_text dpath = io_realpath(dst->path, dst->filename);
      l_text dstpath = io_realpath(dpath, src->filename);
      l_int ret = io_foreach_file_copy(dstpath, srcpath, o, ind);
      _free(dpath);
      _free(dstpath);
      _free(srcpath);
      return ret;
  };
  return 0;
};

/*
  get number of files in directory f, or return 1 if it's only one file
  number of files is returned in *ind pointer to l_dword
  - f   file for removing
  - ob  object ( t_view ), that's controle this process, if t_view (ob) is
        closed, than process is stoped and return false.
  return true if success, otherwise false
*/
l_int  io_numberfile ( p_object ob, t_file *f, l_dword *ind )
{
    if ( f ) {
        if ( f->attrib & FA_DIREC ) {
             l_text path = io_realpath(f->path, f->filename);
             io_foreach_file_ex(path, 0, NULL, NULL, ind);
             _free(path);
        } else {
            if ( ind ) (*ind)++;
        };
        return true;
    };
    return false;
};


/*
 remove file or directory (src). It find if (src) is file or directory and then
 remove it. Return true if succesfull, else return false.
 It remove only empty directory. It's low-level deleting.
 Use io_removefile(...) you will see later for deleting.
*/
l_int   _io_removefile ( l_text file )
{
   if ( remove(file) ) /* error - it's maybe directory */
       return !rmdir(file);
   return true; /* succes removing of file */
};

/*
  copy file from (src) to (dst)
  make new file dst and copy data from src into the new ( dst ) file.
  if (src) is directory it creat directory (dst)
  return true if success, else return false
*/
l_int   _io_copyfile ( l_text dst, l_text src )
{
   if ( !dst || !src || !stricmp(dst, src) ) return false;
   if ( io_isdir(src) ) { /* (src) is directory */
      return mkdir(dst, S_IWUSR/* ignored under DOS */);
   } else { /* (src) is file */
      static l_char buffer[BUFFER_FILE_COPY];
      FILE *s = fopen(src, "rb");
      FILE *d = fopen(dst, "wb");
      l_bool ok = false;
      if ( s && d ) {
         ok = true;
         _while ( !feof(s) ) { /* multitasking while */
               l_dword size = fread(buffer, sizeof(l_char), BUFFER_FILE_COPY, s);
               fwrite(buffer, sizeof(l_char), size, d);
         };
      };
      fclose(s);
      fclose(d);
      return ok;
   };
   return false;
};


/*
 same as mkdir(dir, ...), but it keeps compatibility.
*/
l_bool  io_mkdir ( l_text dir )
{
   return (!mkdir(dir, S_IWUSR));
};


/*
 get unique name of directory that not exists in path (path).
 Return the new name of this directory.
 example:
 l_text d1 = io_uniquedir("c:/seal");
 io_mkdir(io_realpath("c:/seal", d1);
 l_text d2 = io_uniquedir("c:/seal");
  d1 = "DIR0"
  d2 = "DIR1"
  ...
*/
l_text  io_uniquedir ( l_text path )
{
   l_char dir[9] = "DIR0";
   l_int  i = 0;
   l_text rp = io_realpath(path, dir);
   l_text ret = NULL;
   while ( (i < 99999) && io_isdir(rp) ) { /* if directory exist */
       itoa(i, &dir[3], 10);
       i++;
       _free(rp);
       rp = io_realpath(path, dir);
   };
   if (i < 99999) ret = _strdup(dir);
   _free(rp);
   return ret;
};

/*
  check if file1 is same as file2
  if it's, it return true, otherwise false
*/
l_bool  io_issame ( l_text file1, l_text file2 )
{
  l_bool ret = false;
  l_text f1;
  l_text f2;
  l_int  x1;
  l_int  x2;
  if ( !file1 || !file2 ) return false;
  f1 = _strdup(file1);
  f2 = _strdup(file2);
  x1 = strlen(f1)-1;
  x2 = strlen(f2)-1;
  if ( *(f1+x1) == '\\' || *(f1+x1) == '/' ) *(f1+x1) = '\0';
  if ( *(f2+x2) == '\\' || *(f2+x2) == '/' ) *(f2+x2) = '\0';
  if ( f1 && f2 && !stricmp(f1, f2) )
       ret = true;
  _free(f1);
  _free(f2);
  return ret;
};


/*
  check if file is directory or file.
  return true if it's file, else if directory
*/
l_bool  io_isfile ( l_text file )
{
   if ( access(file, D_OK) && !access(file, F_OK) ) return true;
   return false;
};

/*
 return real parent directory of the path.
 example :
 l_text  x = io_parentdir("c:/seal/");
 l_text  y = io_parentdir("c:/seal");
 x = c:/
 y = c:/
*/
l_text  io_parentdir ( l_text path )
{
   l_text endpath = NULL;
   l_text path2 = _strdup(path);
   if ( path2 ) {
      l_text last_slash = strrchr(path2, '/');
      if ( last_slash && !(*(last_slash+1)) ) { /* end of path is slash */
          (*last_slash) = '\0';
          last_slash = strrchr(path2, '/');
      };
      endpath = stridup(path2, strsize(path2, last_slash));
   };
   _free(path2);
   return endpath;
};

/*
 return true (file) is directory and it exist, else it return false.
*/
l_bool  io_isdir ( l_text file )
{
   if ( !access(file, D_OK) ) return true;
   return false;
};

/*
 return true if file or directory exist or false if not.
*/
l_bool  io_exist ( l_text file )
{
   l_bool ok = false;
   l_text odir = getcwd(NULL, PATH_MAX);
   if ( !access(file, R_OK) ) ok = true;
   if ( !chdir(file) ) ok = true;
   chdir(odir);
   return ok;
};

/*
  get real name of file (file) that we find in path (path)and return new string.
  This function control if '/' was after (path) or not. If not, it will add it.
  example :
  l_text all1path = io_realpath("c:/seal/", "seal.exe");
  l_text all2path = io_realpath("c:/seal", "seal.exe");
  all1path contains "c:/seal/seal.exe"
  all2path contains "c:/seal/seal.exe"
*/

l_text  io_realpath ( l_text path, l_text file )
{
  if ( path ) {
      l_int s = strlen(path);
      if ( s && ((*(path+s-1) == '/') || (*(path+s-1) == '\\')) ) {
            if ( file ) return set_format_text(NULL, "%s%s", path, file);
            else return _strdup(path);
      } else if ( s ) {
         if ( file ) return set_format_text(NULL, "%s/%s", path, file);
         else return set_format_text(NULL, "%s", path);
      }
  } else
  {
     if ( file ) return _strdup(file);
  };
  return NULL;
};

/*
 clear memory for pointer t_file, same as
 clear_type(f, sizeof(t_file)) or memset(f, 0, sizeof(t_file))
*/
void  io_cleartfile ( t_file *f )
{
   if ( f ) {
      _free(f->path);
      _free(f->filename);
   };
};

/*
 convert filename to t_file structure and return this structure.
 Filename is real path, it means it looks like this : path+'/'+file
*/
t_file  io_filetotfile ( l_text filename )
{
   l_text path  = io_path(filename);
   l_text file  = io_filename(filename);
   t_file f;
   clear_type(&f, sizeof(t_file));
   f.path = path;
   f.filename = file;
   return f;
};


/*
 return true if filename (file) has extension same to (ext)
*/
l_bool  io_isas ( l_text file, l_text ext )
{
    if ( file && ext ) {
         l_text ext1 = get_extension(file);
         if ( ext1 && !stricmp(ext, ext1) ) return true;
    };
    return false;
};


/*
 return link to directory in (*link) and icon filename (*icon) from ".ldv" file (file).
*/
void  io_linkedpath_ex ( l_text file, l_text *link, l_text *icon )
{
   if ( link ) (*link) = getini_fromfile(file, "definition", "link");
   if ( icon ) (*icon) = getini_fromfile(file, "definition", "icon32");
};


/*
 return link to file in (*link) and icon filename (*icon) from ".lnk" file (file).
*/
void  io_linkedfile_ex (  l_text file, l_text *link, l_text *icon )
{
   if ( link ) (*link) = getini_fromfile(file, "definition", "link");
   if ( icon ) (*icon) = getini_fromfile(file, "definition", "icon32");
};


/*
 return link from ".lnk" file (file). It return new filename where the
 physical file is.
 example:
 l_text shfile = io_linkedfile("c:/seal/desktop/hello.lnk");
 shfile contains "c:/seal/image.exe" f.e.
*/
l_text  io_linkedfile ( l_text file )
{
   l_text link = NULL;
   io_linkedfile_ex(file, &link, NULL);
   return link?link:_strdup(file);
};

/*
 return link from ".ldv" file (file).
 It returns new path from this link.
 Seal has also shortcuts to directories not only to files.
 example:
 l_text shpath = io_linkedpath("c:/seal/desktop/pc/hd-c.ldv");
 shpath contains "c:/"
*/
l_text  io_linkedpath ( l_text file )
{
   l_text path = NULL;
   io_linkedpath_ex(file, &path, NULL);
   return path?path:_strdup(file);
};


/*
  set link ( reference ) to directory ( link ) when you use file (file).
*/
void  io_set_linkedpath ( l_text file, l_text link )
{
   io_set_linkedpath_ex(file, link, NULL);
};


/*
  set link ( reference ) to file ( link ) when you use file (file).
  icon for this directory will be the name of the file (icon), where icon is placed
*/
void  io_set_linkedfile ( l_text file, l_text link )
{
   io_set_linkedfile_ex(file, link, NULL);
};

/*
  set link ( reference ) to directory ( link ) when you use file (file).
  icon for this directory will be the name of the file (icon), where icon is placed
*/
void  io_set_linkedfile_ex ( l_text file, l_text link, l_text icon )
{
   if ( link )
      setini_tofile(file, "definition", "link", link, INI_STRING);
   if ( icon )
      setini_tofile(file, "definition", "icon32", icon, INI_STRING);
};

/*
  set link ( reference ) to file ( link ) when you use file (file).
  icon for this directory will be the name of the file (icon), where icon is placed
*/
void  io_set_linkedpath_ex ( l_text file, l_text link, l_text icon )
{
   if ( link )
      setini_tofile(file, "definition", "link", link, INI_STRING);
   if ( icon )
      setini_tofile(file, "definition", "icon32", icon, INI_STRING);
};

/*
 return true if name of file is extension or false if not.
 example:
 l_bool ext = io_isext("file.*");
 l_bool file = io_isext("file.bmp");
 ext is true.
 file is false.
*/
l_bool io_isext ( l_text file )
{
   if ( file ) {
       if ( strchr(file, '*') || strchr(file, '?') )
           return true;
   };
   return false;
};


/*
 return true if file is file or directory name, not ".", ".." etc., else return false.
*/
l_bool io_isfilename ( l_text file )
{
   if ( file ) {
       if ( !stricmp(file, "..") ) return false;
       if ( !stricmp(file, ".") ) return false;
       return true;
   };
   return false;
};

static p_drives drv_info ( l_text drive, l_text rdr, l_int id )
{
   p_drives d = drives;
   while ( d ) {
      if ( (drive && d->name && !stricmp(drive, d->name)) ||
           (rdr && d->path && !stricmp(rdr, d->path)) || (id == d->id) )
          return d;
      d = d->next;
   };
   return NULL;
};

l_text  drv_findfirst ( p_drives *drv )
{
   if ( drv ) *drv = drives;
   if ( drives ) return drives->name;
   return NULL;
};

l_text  drv_findnext ( p_drives *drv )
{
   if ( drv ) {
     (*drv) = (*drv)->next;
     return (*drv)?(*drv)->name:NULL;
   };
   return NULL;
};

l_text  drv_fixname ( l_text drive )
{
   p_drives d = drv_info(drive, NULL, -1);
   if ( d ) return d->path;
   return drive;
};

l_text  drv_fixreal ( l_text path )
{
   p_drives d = drv_info(NULL, path, -1);
   if ( d ) return d->name;
   return path;
};


l_text  drv_isdriver ( l_text path )
{
   p_drives d = drv_info(NULL, path, -1);
   if ( d ) return d->name;
   return NULL;
};


l_text  drv_fixid ( l_int id )
{
   p_drives d = drv_info(NULL, NULL, id);
   if ( d ) return d->name;
   drv_buf[0] = id+'a';
   return (l_text)drv_buf;
};


void   drv_set ( l_text namedrive, l_text path, l_int id )
{
   if ( namedrive && path ) {
      p_drives d = drv_info(namedrive, NULL, -1);
      l_bool   ok = false;
      if ( d ) {
         ok = true;
         _free(d->name);
         _free(d->path);
      } else d = (p_drives)_malloc(sizeof(t_drives));
      if ( d ) {
         clear_type(d, sizeof(t_drives));
         d->name = _strdup(namedrive);
         d->path = _strdup(path);
         d->id = id;
         if ( !ok ) {
            d->next = drives;
            drives = d;
         };
      };
   };
};


/*
 return all text from file (filename)
*/
l_text  file_gettext ( l_text filename )
{
  FILE *f = fopen(filename, "rt");
  if ( f ) { /* file exist */
      l_long size = filelength(fileno(f));
      l_text text = _malloc(size+1); /* allocate size for file */
      if ( text ) {
         clear_type(text, size+1); /* clear all to zero */
         fread(text, size, 1, f);
      };
      fclose(f);
      return text;
  };
  return NULL; /* file not exist */
};


/*
 returns true if text (text) was writen into the file (filename),
 otherwise returns false
*/
l_bool  file_puttext ( l_text filename, l_text text )
{
  FILE *f = fopen(filename, "wt");
  if ( f ) { /* file exist */
      if ( text ) {
         fwrite(text, strlen(text), 1, f);
      };
      fclose(f);
      return true;
  };
  return false;
};


void   drv_init ( void )
{
  ini_data *drvs = getinidata_fromfile ( INI_MAINFILE, INI_DRIVES );
  DEBUG_printf("\n...initializing drivers from file '%s' and structure '%s'\n", INI_MAINFILE, INI_DRIVES); /* error message, if some occures */
  if ( drvs ) {
    l_int lines = getini_linenum(drvs);
    while ( lines > 0 ) {
      l_char *drvname = NULL;
      l_char *path = NULL;
      path = getini_line(&drvname, drvs, lines);
      if ( path || drvname )
          DEBUG_printf(" - found drive '%s' for path '%s'\n", drvname, path); /* error message, if some occures */
      drv_set(drvname, path, 0);
      _free(path);
      _free(drvname);
      lines--;
    };
   _free(drvs);
  };
};


void   drv_done ( void )
{
  while ( drives ) {
     p_drives o = drives->next;
     _free(drives->name);
     _free(drives->path);
     _free(drives);
     drives = o;
  };
  drives = NULL;
};


/*#include"list.c"*/
/****************************************************************/
/*                                                              */
/*                           list.c                             */
/*                                                              */
/*                Seal - free desktop environment               */
/*                                                              */
/*                    Copyright (c) 1999,2000                   */
/*                         Michal Stencl                        */
/*                     All Rights Reserved                      */
/*                                                              */
/* mail : stenclpmd@ba.telecom.sk                               */
/* web  : http://www.home.sk/public/seal/                       */
/*                                                              */
/* This file is part of Seal.                                   */
/*                                                              */
/* Seal is free software; you can redistribute it and/or        */
/* modify it under the terms of the GNU General Public License  */
/* as published by the Free Software Foundation; either version */
/* 2, or (at your option) any later version.                    */
/*                                                              */
/* Seal is distributed in the hope that it will be useful, but  */
/* WITHOUT ANY WARRANTY; without even the implied warranty of   */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See    */
/* the GNU General Public License for more details.             */
/*                                                              */
/* You should have received a copy of the GNU General Public    */
/* License along with Seal; see the file COPYING.  If not,      */
/* write to the Free Software Foundation, 675 Mass Ave,         */
/* Cambridge, MA 02139, USA.                                    */
/*                                                              */
/****************************************************************/


#include"object.h"
#include"list.h"
#include<string.h>

p_list  (*list_init) ( p_list o, void (*func_free)(void *), l_tag tag ) = &_list_init;
l_bool  list_done ( p_list o )
{
  while ( o->last ) {
    o->remove_item(o, o->last);
  };
  o->last = NULL;
  l_tag_cpy(o->tag, TAG_NONE);
  o->func_free = NULL;
  return true;
};


void    list_copy_ctx ( p_list dst, p_list src )
{
   p_item x = src->first(src);
   p_item f = x;
   if  ( f )
   do {
      dst->insert_ex(dst, x->rec, x->func_free, x->tag);
      x = x->next;
   } while ( x != f );
};

/*
  call function callback for each item in the list,
  callback contains argument ob = (object f.e.), o->item = item of list,
  ind what's indicator of process. This argument contains value of number of
  items ( f.e. copied size )
  if callback return false, for_each cycle is stoped
  else go while all items are done
  -  return false, if some callback return false, else return true
*/
l_bool    list_for_each_item ( p_list o, void *ob, l_int (*callback)(), l_dword *ind )
{
   p_item x = o->first(o);
   p_item f = x;
   l_bool ok = true;
   if  ( f && callback )
   do {
      ok = callback(ob, x->rec, ind);
      x = x->next;
   } while ( ok && (x != f) );
   return ok;
};

/*
  call function callback for each item in the list,
  callback contains argument ob, item and o->item = item of list,
  ind what's indicator of process. This argument contains value of number of
  items ( f.e. copied size )
  if callback return false, for_each cycle is stopped
  else go while all items are done
  -  return false, if some callback return false, else return true
*/
l_bool    list_for_each_item_to_item ( p_list o, void *ob, void *item, l_int (*callback)(), l_dword *ind )
{
   p_item x = o->first(o);
   p_item f = x;
   l_bool ok = true;
   if  ( f && callback )
   do {
      ok = callback(ob, item, x->rec, ind);
      x = x->next;
   } while ( ok && (x != f) );
   return ok;
};


void   *list_first_rec ( p_list o )
{
  if ( o->last ) return o->last->next->rec;
  return NULL;
};


p_item  list_first ( p_list o )
{
  if ( o->last ) return o->last->next;
  return NULL;
};


p_item  list_at_item ( p_list o, l_long index )
{
  p_item v = o->first(o);
  p_item f = v;
  if ( v && (index >= 0) )
    do {
      if ( !index ) return v;
      v = v->next;
      index--;
    } while ( v != f );
  return NULL;
};


void   *list_at ( p_list o, l_long index )
{
  p_item v = o->at_item(o, index);
  if ( v ) return v->rec;
  return NULL;
};



l_long  list_index_of ( p_list o, void *rec )
{
  p_item v = o->first(o);
  p_item f = v;
  l_long index = 0;
  if ( v && rec )
    do {
      if ( v->rec == rec ) return index;
      v = v->next;
      index++;
    } while ( v != f );
  return -1;
};


l_long  list_index_of_item ( p_list o, p_item item )
{
  p_item v = o->first(o);
  p_item f = v;
  l_long index = 0;
  if ( v && item )
    do {
      if ( v == item ) return index;
      v = v->next;
      index++;
    } while ( v != f );
  return -1;
};


p_item  list_find_rec ( p_list o, void *rec )
{
  p_item v = o->first(o);
  p_item f = v;
  if ( v && rec )
    do {
      if ( v->rec == rec ) return v;
      v = v->next;
    } while ( v != f );
  return NULL;
};


l_long  list_insert_ex ( p_list o, void *rec, void (*f_free)(void *), l_tag tag )
{
  p_item xp = o->find_rec(o, rec);
  if ( !xp ) {
      p_item v = (p_item)_malloc(sizeof(t_item));
      if ( !v ) return -1;
      clear_type(v, sizeof(t_item));
      v->rec = rec;
      v->func_free = f_free;
      l_tag_cpy(v->tag, tag);
      v->next = v;
      v->prev = v;
      if ( !o->last ) o->last = v;
      else {
         v->prev = o->last;
         v->next = o->first(o);
         o->first(o)->prev = v;
         o->last->next = v;
         o->last = v;
      };
      return o->index_of_item(o, v);
  };
  return o->index_of_item(o, xp);
};


l_long  list_insert ( p_list o, void *rec )
{
  return o->insert_ex(o, rec, o->func_free, o->tag);
};

l_long  list_get_max_item ( p_list o )
{
  return o->index_of_item(o, o->last);
};

void    list_remove_index ( p_list o, l_long index )
{
  p_item v = o->at_item(o, index);
  o->remove_item(o, v);
};

void    list_remove_item ( p_list o, p_item item )
{
  if ( item ) {
    p_item x = item->prev;
    if ( o->last == item ) o->last = x;
    if ( o->last == item ) o->last = NULL;
    x->next = item->next;
    item->next->prev = x;
    item->next = NULL;
    item->prev = NULL;
    _free(item);
  };
};


void    list_free_index ( p_list o, l_long index )
{
  p_item v = o->at_item(o, index);
  o->free_item(o, v);
};


void    list_free_item ( p_list o, p_item item )
{
  if ( item ) {
    if ( item->func_free ) item->func_free(item->rec);
    else
    if ( o->func_free ) o->func_free(item->rec);
    o->remove_item(o, item);
  };
};

void    list_free_all ( p_list o )
{
  while ( o->last ) {
    o->free_item(o, o->last);
  };
  o->last = NULL;
  l_tag_cpy(o->tag, TAG_NONE);
  o->func_free = NULL;
};


l_bool  list_collect_by_name_from ( p_list o, p_item from, l_int rec_delta )
{
   p_item f = o->first(o);
   p_item m = from;
   if ( !from ) from = m = f;
   else from = m = from->next;
   if ( from && m )
   do {
      from = m;
      do {
         if ( !l_tag_cmp(o->tag, DAT_TEXT) && from->rec && m->rec ) { /* it's structure */
            struct s_t {
               l_text s;
            };
            l_text str1 = ((struct s_t*)from->rec)->s;
            l_text str2 = ((struct s_t*)m->rec)->s;
            if ( str1 && str2 && (stricmp(str1, str2) < 0) ) {
                  void *old = m->rec;
                  m->rec = from->rec;
                  from->rec = old;
            };
         } else { /* it's text */
            l_text str1 = from->rec;
            l_text str2 = m->rec;
            if ( str1 && str2 && (stricmp(str1, str2) < 0) ) {
                  void *old = m->rec;
                  m->rec = from;
                  from->rec = old;
            };
         };
         from = from->next;
      } while ( from != f );
      m = m->next;
   } while ( m != f );
   return true;
};



void  dispose_list ( p_list *o, l_bool freeitem )
{
   if ( o && (*o) ) {
     if ( freeitem )
         (*o)->free_all(*o);
     (*o)->done(*o);
     _free(*o);
     (*o) = NULL;
   };
};


p_list  _list_init ( p_list o, void (*func_free)(void *), l_tag tag )
{
  if ( !o ) return NULL;
  clear_type(o, sizeof(t_list));
  o->func_free = func_free;
  l_tag_cpy(o->tag, tag);
  /* list's functions */
  o->done = &list_done;
  o->copy_ctx = &list_copy_ctx;
  o->first_rec = &list_first_rec;
  o->first = &list_first;
  o->at_item = &list_at_item;
  o->at = &list_at;
  o->index_of = &list_index_of;
  o->index_of_item = &list_index_of_item;
  o->find_rec = &list_find_rec;
  o->insert_ex = &list_insert_ex;
  o->insert = &list_insert;
  o->get_max_item = &list_get_max_item;
  o->remove_index = &list_remove_index;
  o->remove_item = &list_remove_item;
  o->free_index = &list_free_index;
  o->free_item = &list_free_item;
  o->free_all = &list_free_all;
  o->for_each_item = &list_for_each_item;
  o->for_each_item_to_item = &list_for_each_item_to_item;
  o->collect_by_name_from = &list_collect_by_name_from;
  return o;
};


/*#include"exedlx.c"*/
/****************************************************************/
/*                                                              */
/*                          exedlx.c                            */
/*                                                              */
/*                Seal - free desktop environment               */
/*                                                              */
/*                    Copyright (c) 1999,2000                   */
/*                         Michal Stencl                        */
/*                     All Rights Reserved                      */
/*                                                              */
/* mail : stenclpmd@ba.telecom.sk                               */
/* web  : http://www.home.sk/public/seal/                       */
/*                                                              */
/* This file is part of Seal.                                   */
/*                                                              */
/* Seal is free software; you can redistribute it and/or        */
/* modify it under the terms of the GNU General Public License  */
/* as published by the Free Software Foundation; either version */
/* 2, or (at your option) any later version.                    */
/*                                                              */
/* Seal is distributed in the hope that it will be useful, but  */
/* WITHOUT ANY WARRANTY; without even the implied warranty of   */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See    */
/* the GNU General Public License for more details.             */
/*                                                              */
/* You should have received a copy of the GNU General Public    */
/* License along with Seal; see the file COPYING.  If not,      */
/* write to the Free Software Foundation, 675 Mass Ave,         */
/* Cambridge, MA 02139, USA.                                    */
/*                                                              */
/****************************************************************/

#include<stdio.h>
#include<io.h>
#include<string.h>
#include<stdlib.h>
#include<dir.h>
#include<unistd.h>
#include<process.h>
#include"allegro.h"
#include"object.h"
#include"view.h"
#include"dataini.h"
#include"dlx.h"
#include"exedlx.h"
#include"drivers.h"
#include"list.h"
#include"_iodir.h"
#include"program.h"
#include "registry.h"

extern char* _LIBEXPORTTABLE[]; /* declaration must be in main exe file */

p_ico_loaders   ico_loaders = NULL;

l_int (*read_set_file)( l_char *_filename) = &_read_set_file;
l_int (*dos_command) ( l_text newpath, l_text command ) = &_dos_command;
l_int (*dos_run) ( l_text newpath, l_text command ) = &_dos_run;

l_int  _read_set_file ( l_char *_filename )
{
  l_text  buffer = file_gettext(_filename);
  l_text  ptr = buffer;
  if ( !buffer ) return 0;
  DLXImport(_LIBEXPORTTABLE);
  while ( ptr ) {
      if ( (ptr = strchr(ptr, '<')) ) {
        l_text sptr = strchr(ptr, '>');
        if ( sptr ) {
          l_text filedlx = (l_text)_malloc(sptr-ptr);
          if ( filedlx ) {
            memcpy(filedlx, ptr+1, (sptr-ptr)-1);
            filedlx[(sptr-ptr)-1] = '\0';
            run_file(filedlx); /* run file */
            _free(filedlx);
          };
          ptr = sptr;
        };
      };
  };
  _free(buffer);
  return 1;
};

/* icon loaders */
BITMAP*  _load_exe_icon ( l_char *args, l_int size, l_int *ownmem )
{
   return NULL;
};


BITMAP*  _load_ldv_icon ( l_char *args, l_int size, l_int *ownmem )
{
   BITMAP *ic = NULL;
   l_text def32 = getini_fromfile(args, "definition", "icon32");
   l_text def16 = getini_fromfile(args, "definition", "icon16");
   if ( ownmem ) *(ownmem) = 0;
   if ( !def32 ) def32 = def16;
   if ( !def16 ) def16 = def32;
   if ( (size > 16) && def32 ) { /* it's 32 icon */
   /* 32 icon */
     if ( *def32 == '%' ) /* system icon */
         ic = icon_sysget(icon_getid(def32+1), size);
   } else
   if ( (size <= 16) && def16 ) { /* it's 16 icon */
   /* 16 icon */
     if ( *def16 == '%' ) { /* system icon */
         ic = icon_sysget(icon_getid(def16+1), size);
     };
   };
   if(def16)_free(def16);
   if(def32 && (def32!=def16))_free(def32);
   return ic;
};


BITMAP*  _load_lnk_icon ( l_char *args, l_int size, l_int *ownmem )
{
   BITMAP *ic = NULL;
   l_text def32 = getini_fromfile(args, "definition", "icon32");
   l_text def16 = getini_fromfile(args, "definition", "icon16");
   if ( ownmem ) *(ownmem) = 0;
   if ( !def32 ) def32 = def16;
   if ( !def16 ) def16 = def32;
   if ( (size > 16) && def32 ) { /* it's 32 icon *//* 32 icon */
     if ( *def32 == '%' ) /* system icon */
         ic = icon_sysget(icon_getid(def32+1), size);
   } else
   if ( (size <= 16) && def16 ) {/* it's 16 icon *//* 16 icon */
     if ( *def16 == '%' ) { /* system icon */
         ic = icon_sysget(icon_getid(def16+1), size);
     };
   };
   if(def16)_free(def16);
   if(def32 && (def16!=def32))_free(def32);
   return ic;
};

BITMAP*  load_file_icon ( l_char *args, l_int size, l_int *ownmem )
{
  p_ico_loaders p = ico_loaders;
  if ( ownmem ) *(ownmem) = 0;
  if ( args )
     while ( p ) {
        l_text ext = get_extension(args);
        if ( ext && p->ext && !stricmp(ext, p->ext) )
           return p->load_icon(args, size, ownmem);
        p = p->next;
     };
  return NULL;
};


void     add_to_loadfileicon ( l_char *ext, BITMAP *(*load_icon)(l_text filename, l_int size, l_int *ownmem) )
{
  p_ico_loaders p = (p_ico_loaders)_malloc(sizeof(t_ico_loaders));
  if ( p ) {
    clear_type(p, sizeof(t_ico_loaders));
    p->ext = _strdup(ext);
    p->load_icon = load_icon;
    p->next = ico_loaders;
    ico_loaders = p;
  };
};


void     init_loadfileicon ( void )
{
  add_to_loadfileicon("exe", &_load_exe_icon);
  add_to_loadfileicon("dlx", &_load_exe_icon);
  add_to_loadfileicon("ldv", &_load_ldv_icon);
  add_to_loadfileicon("lnk", &_load_lnk_icon);
};


void     free_loadfileicon ( void )
{
   while ( ico_loaders ) {
     p_ico_loaders p = ico_loaders->next;
     _free(ico_loaders->ext);
     _free(ico_loaders);
     ico_loaders = p;
   };
   ico_loaders = NULL;
};



BITMAP*  get_icon_for_file_ex ( l_text filename, l_int attrib, l_int *ownmem, l_int size )
{
   l_text  file = io_getfilename(filename);
   BITMAP *ex   = NULL;
   if ( ownmem ) *(ownmem) = 0;
   if ( file && !stricmp(file, DRIVE_DESKTOP) ) /* desktop */
      ex = icon_desktop(size);
   else
   if ( file && !stricmp(file, DRIVE_PC) ) /* pc */
      ex = icon_mycomputer(size);
   else
   if ( attrib & FA_DIREC ) ex = icon_folder(size);
   else
      ex = load_file_icon(filename, size, ownmem);
   return ex;
};


BITMAP*  get_icon_for_file ( l_text filename, l_int attrib, l_int *ownmem )
{
   return get_icon_for_file_ex(filename, attrib, ownmem, system_item_size);
};


p_list  get_args ( l_char *args )
{
   p_list list = list_init(_malloc(sizeof(t_list)), NULL, DAT_TEXT);
   if ( list )
   while ( args ) {
      l_text arg = args;
      args = strchr(args, ' ');
      if ( args ) {
         if ( args != arg )
               list->insert(list, stridup(arg, strsize(arg, args)));
         while ( *args && (*(args++) != ' ') );
      } else {
         list->insert(list, _strdup(arg));
      };
   };
   return list;
};

l_char *lwrcase(l_char *s){
 l_char *t=s;
 if (s) {
  while(*s) {
   (*s)=tolower(*s);
   s++;
  }
 }
 return t;
}

l_int  run_file_args ( l_text file, l_char *args )
{
  l_char *ext = get_extension(file);
  l_char *kip_ = key_in_path("system/filetypes", lwrcase(ext)); /* so jpg,Jpg,jPg,jpG,JpG,JPG - all match the .../jpg/loader entry */
  l_char *kip = key_in_path(kip_, "loader");
  l_char *app = get_key(kip);
  _free(kip);_free(kip_);

  if(app) {
   if ( app[0] ) {
    hdlx_t i = 0;
    if ( !stricmp(app, "%lib-seal") ) /* if args is file known by os-x library */
      i = DLXLoad(file, args);
    else {
      l_text arg1 = set_format_text(NULL, "%s %s", file, args);
      i = DLXLoad(app, arg1);
      if ( !i ) /* try to run as DOS file */
          i = dos_run(arg1, arg1);
      _free(arg1);
    };
    _free(app);
    return i?1:0;
   }; /* else we know about it but there is no runner defined for it */
  } else { /* no clues from the ini file about what to do. fallback position just tries these ...*/
   if( !strcmp(ext,"dlx") ) return DLXLoad(file,args)?1:0;
   else if ( !strcmp(ext,"exe") ) {
    if( is_dos_executable(file) ) return dos_run(file,args); /* try as a regular dos executable */
    else return DLXLoad(file,args)?1:0; /* try as a dlx format exe file */
   } 
  }
  return 0;
};


l_int  run_file ( l_char *args )
{
  l_char *ext = get_extension(args);
  l_char *kip_;
  l_char *kip;
  l_char *app;
  kip_ = key_in_path("system/filetypes", lwrcase(ext));
  kip = key_in_path(kip_, "loader");
  app = get_key(kip);
  _free(kip_);
//fprintf(stderr,"Trying to run '%s' using '%s'='%s'....", args, kip,app);
  _free(kip);
  if(app) {
   if ( app[0] ) {
    hdlx_t i = 0;
    if ( is_dos_executable(args) ) { /* can execute in MS-DOS mode ? */
       i = dos_run(NULL, args);
       _free(app);
//fprintf(stderr,"result=%ld\n", (unsigned long)i); /* dos (may) return something sane as its return value */
       return i;
    };
    if ( !stricmp(app, "%lib-seal") ) {/* if args is file known by os-x library */
      i = DLXLoad(args, NULL);
    } else {
      i = DLXLoad(app, args);
    }
    _free(app);
//fprintf(stderr,"result=%d\n", i?1:0); /* DLXLoad returns 0 if failed else some arbitrary number */
    return i?1:0;
   };
   _free(app);
//fprintf(stderr,"no runner specified\n");
  } else { /* no help in the ini file - use fallback position */
//fprintf(stderr,"\n");
   if( !strcmp(ext,"dlx") ) return DLXLoad(args,NULL)?1:0;
   else if ( !strcmp(ext,"exe") ) {
    if( is_dos_executable(args) ) return dos_run(NULL,args); /* try as a regular dos executable */
    else return DLXLoad(args,NULL)?1:0; /* try as a dlx format exe file */
   } else {
    l_int   oldsys = __system_flags; /* something strange to do with the djgpp c library system() call */
    int err;
    screen_halt();
    safe_timer_halt();
    __system_flags |= __system_call_cmdproc;
    err = system(args);
    __system_flags = oldsys;
    screen_reload();
    safe_timer_reload();
   }
  }
  return 0;
};


void  init_ext_runners ( void )
{
};


void  done_ext_runners ( void )
{
};


l_int  is_dos_executable ( l_text file )
{
  FILE *f = fopen(file, "rt");
  if ( f ) {
      l_bool ok   = 0;
      l_char h[2] = {'\0', '\0'};
      fread(h, 2, 1, f); /* read MZ header */
      if ( (h[0] == 'M') && (h[1] == 'Z') ) /* it's MZ */
           ok = 1;
      fclose(f);
      return ok;
  };
  return 0;
};


l_int  _dos_command ( l_text newpath, l_text command )
{
    l_int   err = 0;
    l_text  path   = getcwd(NULL, IO_DIR_LIMIT);
#ifndef __RSXNT__
    l_int   oldsys = __system_flags; /* something strange to do with the djgpp c library system() call */
#endif
    l_text  p = io_path(newpath);
    l_text  f = io_filename(command);
    if ( p ) chdir(p);
    if ( is_dos_executable(f) ) { /* can execute in MS-DOS mode ? */
         screen_halt();
         safe_timer_halt();
#ifndef __RSXNT__
         __system_flags |= __system_call_cmdproc;
#endif
         err = system(f);
#ifndef __RSXNT__
         __system_flags = oldsys;
#endif
         screen_reload();
         safe_timer_reload();
    } else {
       err = (DLXLoad(command, NULL)==0?-1:0);
    };
    if ( path && newpath ) chdir(path);
    _free(p);
    _free(f);
    return (err==-1?0:1);
};


l_int  _dos_run ( l_text newpath, l_text command )
{
    l_int   err = 0;
    l_text  path   = getcwd(NULL, IO_DIR_LIMIT);
    l_text  p = io_path(newpath);
    l_text  f = io_filename(command);
    if ( p ) chdir(p);
         screen_halt();
         safe_timer_halt();
         err = spawnlp(P_WAIT, f, f, 0);
         if ( path && newpath ) chdir(path);
         screen_reload();
         safe_timer_reload();
    if ( path && newpath ) chdir(path);
    _free(p);
    _free(f);
    return (err==-1?0:1);
};


/*#include"keyboard.c"*/
/****************************************************************/
/*                                                              */
/*                           keyboard.c                         */
/*                                                              */
/*                Seal - free desktop environment               */
/*                                                              */
/*                    Copyright (c) 1999,2000                   */
/*                         Michal Stencl                        */
/*                     All Rights Reserved                      */
/*                                                              */
/* mail : stenclpmd@ba.telecom.sk                               */
/* web  : http://www.home.sk/public/seal/                       */
/*                                                              */
/* This file is part of Seal.                                   */
/*                                                              */
/* Seal is free software; you can redistribute it and/or        */
/* modify it under the terms of the GNU General Public License  */
/* as published by the Free Software Foundation; either version */
/* 2, or (at your option) any later version.                    */
/*                                                              */
/* Seal is distributed in the hope that it will be useful, but  */
/* WITHOUT ANY WARRANTY; without even the implied warranty of   */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See    */
/* the GNU General Public License for more details.             */
/*                                                              */
/* You should have received a copy of the GNU General Public    */
/* License along with Seal; see the file COPYING.  If not,      */
/* write to the Free Software Foundation, 675 Mass Ave,         */
/* Cambridge, MA 02139, USA.                                    */
/*                                                              */
/****************************************************************/


#include"allegro.h"
#include"keyboard.h"

t_keyboard   *keyb = NULL;

t_keyboard*   (*key_init) ( p_keyboard o ) = &_key_init;

void   key_simulate_keypress ( p_keyboard o, l_int keycode )
{
  simulate_keypress(keycode);
};


void   key_set_ctrl_alt_del_flag ( p_keyboard o, l_bool enable )
{
  three_finger_flag = (int)enable;
};


l_bool key_get_ctrl_alt_del_flag ( p_keyboard o )
{
  return three_finger_flag;
};


void   key_clear_buffer ( p_keyboard o )
{
  clear_keybuf();
};


l_bool key_keypressed ( p_keyboard o )
{
  return keypressed();
};


l_int  key_readkey ( p_keyboard o )
{
  return readkey();
};


void  key_translate_event ( t_object *o, t_event *event )
{
  o->state &= ~KB_SF_KEYDOWN;
  ((t_keyboard*)o)->shifts = key_shifts;
  if ( ((t_keyboard*)o)->keypressed((p_keyboard)o) )  {

    ((t_keyboard*)o)->code = ((t_keyboard*)o)->readkey((p_keyboard)o);
    ((t_keyboard*)o)->keychar = (char)TO_CHAR(((t_keyboard*)o)->code);
    if ( ((t_keyboard*)o)->code ) o->state |= KB_SF_KEYDOWN;
    ((t_keyboard*)o)->clear_buffer( ((t_keyboard*)o) );
  } else ((t_keyboard*)o)->code = ((t_keyboard*)o)->keychar;
  if ( o->state & KB_SF_KEYDOWN ) {
    set_event(event, EV_KEYBOARD, MSG_NOTHING, o);
  } else {
    (*event).type &= ~EV_KEYBOARD;
  };
};


static void   *keyboard_callback_old = NULL;
/* stop timer of the keyboard */
static void    key_stop_timer ( void )
{
  keyboard_callback_old = keyboard_callback;
  remove_keyboard();
  keyboard_callback = NULL;
};

/* reload timer of the keyboard */
static void    key_reload_timer ( void )
{
  install_keyboard();
  keyboard_callback = keyboard_callback_old;
};


l_bool  key_done ( t_object *o )
{
  if ( !obj_done(o) ) return false;
  remove_keyboard();
  keyboard_callback = NULL;
  erase_safe_timer(&key_stop_timer);
  return true;
};

t_keyboard*   _key_init ( p_keyboard o )
{
  if ( !o ) return NULL;
  memset(o, 0, sizeof(t_keyboard));
  keyb = o;
  obj_init(&(o->obclass));
  install_keyboard();
  /* safe timer */
  safe_timer(&key_stop_timer, &key_reload_timer);
  o->simulate_keypress = &key_simulate_keypress;
  o->set_ctrl_alt_del_flag = &key_set_ctrl_alt_del_flag;
  o->get_ctrl_alt_del_flag = &key_get_ctrl_alt_del_flag;
  o->readkey = &key_readkey;
  o->clear_buffer = &key_clear_buffer;
  o->keypressed = &key_keypressed;
  o->obclass.translate_event = &key_translate_event;
  o->obclass.done = &key_done;
  OBJECT(o)->set_options(OBJECT(o), OB_OF_NOTACTIVATE+OB_OF_TOPSELECT, true);
  return o;
};


/*#include"mouse.c"*/
/****************************************************************/
/*                                                              */
/*                           mouse.c                            */
/*                                                              */
/*                Seal - free desktop environment               */
/*                                                              */
/*                    Copyright (c) 1999,2000                   */
/*                         Michal Stencl                        */
/*                     All Rights Reserved                      */
/*                                                              */
/* mail : stenclpmd@ba.telecom.sk                               */
/* web  : http://www.home.sk/public/seal/                       */
/*                                                              */
/* This file is part of Seal.                                   */
/*                                                              */
/* Seal is free software; you can redistribute it and/or        */
/* modify it under the terms of the GNU General Public License  */
/* as published by the Free Software Foundation; either version */
/* 2, or (at your option) any later version.                    */
/*                                                              */
/* Seal is distributed in the hope that it will be useful, but  */
/* WITHOUT ANY WARRANTY; without even the implied warranty of   */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See    */
/* the GNU General Public License for more details.             */
/*                                                              */
/* You should have received a copy of the GNU General Public    */
/* License along with Seal; see the file COPYING.  If not,      */
/* write to the Free Software Foundation, 675 Mass Ave,         */
/* Cambridge, MA 02139, USA.                                    */
/*                                                              */
/****************************************************************/


#include"dataini.h"
#include"mouse.h"

t_mouse   *mouse = NULL;
t_mouse*  (*mouse_init) ( p_mouse o ) = &_mouse_init;
DATAFILE  *cursor_system = NULL;
BITMAP    *cursor_standard = NULL;
l_int   msblock0 = 0;
l_int   msblock1 = 0;
l_int   msblock2 = 0;
l_int   msblock3 = 0;
l_int   msblock4 = 0;
l_byte cursor_focus[CUR_DATAMAX*2] = {
     0,0, 2,2, 8,8, 8,8, 8,8, 2,15, 7,6, 7,7, 8,8, 6,6, 6,1, 6,15, 1,6, 15,6, 8,8,
     0,0, 3,3, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0
};

l_int   mouse_flags = 0;

extern int mouse_x_focus;
extern int mouse_y_focus;
int mouse_on=1;
l_int mouse_minx=0,mouse_miny=0,mouse_maxx=0x7fffffff,mouse_maxy=0x7fffffff;
l_bool mouse_is_visible(){
 return mouse_on;
}
void get_mouse_focus(int *x,int *y){*x=mouse_x_focus;*y=mouse_y_focus;}
#define mouse_block(b,ax,ay,bx,by) ({scare_mouse_area(ax,ay,bx-ax+1,by-ay+1);mouse_on=0;1;})
#define mouse_unblock(x) ({unscare_mouse();mouse_on=1;})
#define mouse_is_block() (mouse_on==0)
void get_mouse_range(int *ax,int *ay, int *bx, int *by){
   *ax=mouse_minx;*ay=mouse_miny;*bx=mouse_maxx;*by=mouse_maxy;
}
void      mouse_set_range(t_mouse *o, t_rect r )
{
  mouse_minx=r.a.x;mouse_miny=r.a.y;mouse_maxx=r.b.x;mouse_maxy=r.b.y;
  set_mouse_range(r.a.x, r.a.y, r.b.x, r.b.y);
};
BITMAP*   mouse_get_cursor(t_mouse *o)
{
  return mouse_sprite;
};
l_bool    mouse_set_cursor(t_mouse *o, BITMAP *cursor)
{
  if ( cursor && (cursor != o->get_cursor(o)) ) {
    set_mouse_sprite(cursor);
    mouse_sprite=cursor;
    return true;
  };
  return false;
};



/* these two are called during interrupt to think about the mouses buttons in some way or other */
void write_mouse_queue ( int f )
{
    mouse_flags = f<<5;
};
static END_OF_FUNCTION(write_mouse_queue)

int read_mouse_queue ( void )
{
  int x = mouse_flags;
  mouse_flags = 0;
  return x;
};
#ifndef __RSXNT__
static END_OF_FUNCTION(read_mouse_queue)
#endif

t_rect    mouse_get_range(t_mouse *o)
{
  t_rect r;
  get_mouse_range((int*)(&r.a.x), (int*)(&r.a.y), (int*)(&r.b.x), (int*)(&r.b.y));
  return r;
};

/* mouse object functions */
t_point    mouse_get_focus(t_mouse *o)
{
  t_point p;
  get_mouse_focus((int*)&p.x, (int*)&p.y);
  return p;
};

l_bool    xx_mouse_is_visible(t_mouse *o)
{
  return (l_bool)mouse_is_visible();
};

l_bool    mouse_show ( t_mouse *o )
{
  show_mouse(screen);
 mouse_on=1;
  if ( o->is_visible(o) ) return true;
  return false;
};

l_bool    mouse_hide(t_mouse *o)
{
  show_mouse(NULL);
 mouse_on=0;
  if ( !o->is_visible(o) ) return true;
  return false;
};


l_bool    mouse_set_cursor_focus(t_mouse *o, BITMAP *cursor, t_point p )
{
  if ( o->set_cursor(o, cursor) ) {
    o->set_focus(o, p);
    return true;
  };
  return false;
};



void      mouse_set_dclick_diff(p_mouse o, l_int mili)
{
  o->dclick_milisec = mili;
};


void      mouse_set_speed(t_mouse *o, t_point speed )
{
  o->speed = speed;
  set_mouse_speed(speed.x, speed.y);
};

void      mouse_set_pos(t_mouse *o, t_point where )
{
  position_mouse(where.x, where.y);
};

l_int     xx_mouse_block(p_mouse o, t_rect r )
{
  return mouse_block(screen, r.a.x, r.a.y, r.b.x, r.b.y);
};


void      xx_mouse_unblock(p_mouse o, l_int i )
{
  mouse_unblock(i);
};


l_bool    xx_mouse_is_block(p_mouse o )
{
  return (l_bool)mouse_is_block();
};


void      mouse_set_focus(t_mouse *o, t_point focus )
{
  t_point f = o->get_focus(o);
  if ( (f.x != focus.x) || (f.y != focus.y) ) {
    l_bool ib = o->is_block(o);
    if ( !ib ) show_mouse(NULL); /* hide mouse */
    set_mouse_sprite_focus(focus.x, focus.y);
    if ( !ib ) show_mouse(screen); /* show mouse */
  };
};


/* from object */
void      mouse_translate_event ( t_object *o, t_event *event )
{
  l_big diftime = time_diff_mili(((p_mouse)o)->dclick_old_time);
  o->state = read_mouse_queue();
  ((p_mouse)o)->where = point_assign(mouse_x, mouse_y);
  if ( !(o->state & MO_SF_MOUSEUP) ) {
     if ( ((p_mouse)o)->state_last & MO_SF_MOUSELPRESS ) o->state |= MO_SF_MOUSELAUTO;
     if ( ((p_mouse)o)->state_last & MO_SF_MOUSEMPRESS ) o->state |= MO_SF_MOUSEMAUTO;
     if ( ((p_mouse)o)->state_last & MO_SF_MOUSERPRESS ) o->state |= MO_SF_MOUSERAUTO;
  };
  if ( diftime > ((p_mouse)o)->dclick_milisec )
    ((p_mouse)o)->dclick_old_time = 0;
  if ( o->state >= MO_SF_MOUSEMOVE ) {
    if ( o->state & MO_SF_MOUSEDOWN ) {
      if ( diftime <= (((p_mouse)o)->dclick_milisec) &&
           (((p_mouse)o)->state_last & MO_SF_MOUSEUP) ) {
        if ( o->state & MO_SF_MOUSELDOWN ) o->state |= MO_SF_MOUSELDOUBLE;
        if ( o->state & MO_SF_MOUSERDOWN ) o->state |= MO_SF_MOUSERDOUBLE;
        if ( o->state & MO_SF_MOUSEMDOWN ) o->state |= MO_SF_MOUSEMDOUBLE;
        o->state &= ~MO_SF_MOUSEAUTO;
        o->state &= ~MO_SF_MOUSEDOWN;
        ((p_mouse)o)->dclick_old_time = 0;
      } else
        ((p_mouse)o)->dclick_old_time = time_get_mili();
    }
    ((p_mouse)o)->state_last = o->state;
    set_event(event, EV_MOUSE, MSG_NOTHING, o);
  } else {
    (*event).type &= ~EV_MOUSE;
  };
};

void    clear_event_mouse ( void )
{
};


l_bool  mouse_set_cursor_system_file ( l_char *cursorfile, l_int r, l_int g, l_int b )
{
  DATAFILE *p = cursor_load_file(cursorfile, r, g, b);
  if ( p ) {
    cursor_system = p;
    cursor_standard = CURSOR_GETCURSOR(p, CUR_ARROW);
    if ( MOUSE ) mouse_set_cursor_id(CUR_ARROW);
    return true;
  };
  return false;
};


l_bool  mouse_load_cursors ( void )
{
  l_char *name  = getini_fromfile(INI_MAINFILE, INI_MOUSE, "cursors_file");
  l_bool ok=0;
  if(name) {
   ok = mouse_set_cursor_system_file(name, CUR_SKIP_RED, CUR_SKIP_GREEN, CUR_SKIP_BLUE);
   _free(name);
  }
  return ok;
};


static void   *mouse_callback_old = NULL;
/* stop timer of the mouse */
static void    mouse_stop_timer ( void )
{
  mouse_callback_old = mouse_callback;
  remove_mouse();
  mouse_callback = NULL;
};


/* reload timer of the mouse */
static void    mouse_reload_timer ( void )
{
  install_mouse();
  LOCK_FUNCTION(write_mouse_queue);
  LOCK_FUNCTION(read_mouse_queue);

  mouse_callback = mouse_callback_old;
  mouse->set_speed(mouse, mouse->speed);
};


l_bool      mouse_done ( t_object *o )
{
  if ( !obj_done(o) ) return false;
  erase_safe_timer(&mouse_stop_timer);
  ((p_mouse)o)->hide((p_mouse)o);
  remove_mouse();
  mouse_callback = NULL;
  return true;
};


/* mouse init function */
t_mouse *_mouse_init ( t_mouse *o )
{
  l_int  x = install_mouse();
  l_long speed = getininum_fromfile (INI_MAINFILE, INI_MOUSE, "speed");
  DEBUG_printf("\n...initializing mouse object\n"); /* error message, if some occures */
  if ( !o ) return NULL;
  mouse = o;
  clear_type(o, sizeof(t_mouse));
  obj_init(&(o->obclass));
  o->buttons = (l_char)x;
  if ( (x <= 0) || !_mouse_installed ) { /* mouse is not installed */
    o->buttons = 0;
    DEBUG_printf(" - ERROR :: mouse driver not found\n"); /* error message, if some occures */
  } else { /* mouse is installed */

     safe_timer(&mouse_stop_timer, &mouse_reload_timer);
     mouse_callback = &write_mouse_queue;

     LOCK_FUNCTION(write_mouse_queue);
     LOCK_FUNCTION(read_mouse_queue);
     DEBUG_printf(" - found %i button's mouse\n", o->buttons); /* error message, if some occures */
  };
  o->speed = point_assign(speed, speed);
  o->where = point_assign(0, 0);
  /* function settings */
  o->get_cursor = &mouse_get_cursor;
  o->get_focus = &mouse_get_focus;
  o->get_range = &mouse_get_range;
  o->is_visible = &xx_mouse_is_visible;
  o->show = &mouse_show;
  o->hide = &mouse_hide;
  o->set_dclick_diff = &mouse_set_dclick_diff;
  o->set_cursor = &mouse_set_cursor;
  o->set_cursor_focus = &mouse_set_cursor_focus;
  o->set_range = &mouse_set_range;
  o->set_speed = &mouse_set_speed;
  o->set_focus = &mouse_set_focus;
  o->set_pos   = &mouse_set_pos;
  o->block = &xx_mouse_block;
  o->unblock = &xx_mouse_unblock;
  o->is_block = &xx_mouse_is_block;
  o->obclass.done = &mouse_done;
  o->obclass.translate_event = &mouse_translate_event;
  o->set_dclick_diff(o, MOUSE_TIME_DOUBLECLICK);
  OBJECT(o)->set_options(OBJECT(o), OB_OF_NOTACTIVATE+OB_OF_TOPSELECT, true);
  mouse_load_cursors();
  set_mouse_speed(o->speed.x, o->speed.y);
  mouse->show(mouse); /* show mouse */
/* init the mouse region to screen size */
  mouse_maxx=screen_width-1;
  mouse_maxy=screen_height-1;
  DEBUG_printf("   mouse was succesfull installed at speed %i\n", o->speed.x);
  /* error message, if some occures */
  return o;
};


/*#include"screen.c"*/
/****************************************************************/
/*                                                              */
/*                           screen.c                           */
/*                                                              */
/*                Seal - free desktop environment               */
/*                                                              */
/*                    Copyright (c) 1999,2000                   */
/*                         Michal Stencl                        */
/*                     All Rights Reserved                      */
/*                                                              */
/* mail : stenclpmd@ba.telecom.sk                               */
/* web  : http://www.home.sk/public/seal/                       */
/*                                                              */
/* This file is part of Seal.                                   */
/*                                                              */
/* Seal is free software; you can redistribute it and/or        */
/* modify it under the terms of the GNU General Public License  */
/* as published by the Free Software Foundation; either version */
/* 2, or (at your option) any later version.                    */
/*                                                              */
/* Seal is distributed in the hope that it will be useful, but  */
/* WITHOUT ANY WARRANTY; without even the implied warranty of   */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See    */
/* the GNU General Public License for more details.             */
/*                                                              */
/* You should have received a copy of the GNU General Public    */
/* License along with Seal; see the file COPYING.  If not,      */
/* write to the Free Software Foundation, 675 Mass Ave,         */
/* Cambridge, MA 02139, USA.                                    */
/*                                                              */
/****************************************************************/



#include"allegro.h"
#include"object.h"
#include"screen.h"

#define  INI_SCREEN     "screen"
BITMAP  *screen_virtual = NULL;
l_int    screen_width     = 0;
l_int    screen_height    = 0;
static l_int    screen_viswidth  = 0;
static l_int    screen_visheight = 0;
static l_int    screen_card      = 0;
static l_int    screen_depth     = 0;

/* jdh moved - needs to know about screen_depth - 
NOTE you need to use load_bitmap directly if you ever intend saving the image (in some other format?)
because this (now, rightly or wrongly) converts it to whatever depth we have on screen
which can be destructive, particularly in 8bits, but still not good in 15 or 16 bits.
This added to ensure gifs (in particular) get converted to screen depth.
*/

static PALETTE Gr_pal;
static PALETTE the_palette;

/*
We need to use our own version of _fixup_loaded_bitmap (to use the current palette and rgb_map
in 8 bit mode rather than generating its own) so we have to duplicate all of readbmp.o - rats!
so we make this slight modification of ...
*/
/*         ______   ___    ___
 *        /\  _  \ /\_ \  /\_ \
 *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___
 *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
 *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
 *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
 *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
 *                                           /\____/
 *                                           \_/__/
 *
 *      Top level bitmap reading routines.
 *
 *      By Shawn Hargreaves.
 *
 *      See readme.txt for copyright information.
 */
#include <string.h>
#include "allegro.h"
#include "allegro/aintern.h"

typedef struct BITMAP_TYPE_INFO
{
   char *ext;
   BITMAP *(*load)(AL_CONST char *filename, RGB *pal);
   int (*save)(AL_CONST char *filename, BITMAP *bmp, AL_CONST RGB *pal);
} BITMAP_TYPE_INFO;

struct bitmap_loader_type
{
   BITMAP_TYPE_INFO type;
   struct bitmap_loader_type *next;
};

static struct bitmap_loader_type *first_loader_type = NULL;

/* register_bitmap_file_type:
 *  Informs Allegro of a new image file type, telling it how to load and
 *  save files of this format (either function may be NULL).
 */
void register_bitmap_file_type(AL_CONST char *ext, BITMAP *(*load)(AL_CONST char *filename, RGB *pal), int (*save)(AL_CONST char *filename, BITMAP *bmp, AL_CONST RGB *pal))
{
   char tmp[32], *aext;
   struct bitmap_loader_type *iter = first_loader_type;
   aext = uconvert_toascii(ext, tmp);
   if (strlen(aext) == 0) return;
   if (!iter) {
      first_loader_type = malloc(sizeof(struct bitmap_loader_type));
      if (!first_loader_type) return;
      iter = first_loader_type;
   } 
   else {
      while (iter) {
	 if (stricmp(iter->type.ext, aext) == 0) {
	    iter->type.load = load;
	    iter->type.save = save;
	    return;
	 }
	 iter = iter->next;
      }
      iter = first_loader_type;
      while (iter->next) iter = iter->next;
      iter->next = malloc(sizeof(struct bitmap_loader_type));
      if (!iter->next) return;
      iter = iter->next;
   }
   iter->type.load = load;
   iter->type.save = save;
   iter->type.ext = strdup(aext);
   iter->next = NULL;
}

/* load_bitmap:
 *  Loads a bitmap from disk.
 */
BITMAP *load_bitmap(AL_CONST char *filename, RGB *pal)
{
   char tmp[16], *aext;
   struct bitmap_loader_type* iter = first_loader_type;
   aext = uconvert_toascii(get_extension(filename), tmp);
   while (iter) {
      if (stricmp(iter->type.ext, aext) == 0) {
	 if (iter->type.load) 
	    return iter->type.load(filename, pal);
	 return NULL;
      }
      iter = iter->next;
   }
   return NULL;
}

/* save_bitmap:
 *  Writes a bitmap to disk.
 */
int save_bitmap(AL_CONST char *filename, BITMAP *bmp, AL_CONST RGB *pal)
{
   char tmp[16], *aext;
   struct bitmap_loader_type *iter = first_loader_type;
   aext = uconvert_toascii(get_extension(filename), tmp);
   while (iter) {
      if (stricmp(iter->type.ext, aext) == 0) {
	 if (iter->type.save) 
	    return iter->type.save(filename, bmp, pal);
	 return 1;
      }
      iter = iter->next;
   }
   return 1;
}
/* _fixup_loaded_bitmap:
 *  Helper function for adjusting the color depth of a loaded image.
 */
BITMAP *_fixup_loaded_bitmap(BITMAP *bmp, PALETTE pal, int bpp)
{
   BITMAP *b2;
   b2 = create_bitmap_ex(bpp, bmp->w, bmp->h);
   if (!b2) {
      destroy_bitmap(bmp);
      return NULL;
   }
   if (bpp == 8) {
/* 
jdh modification - uses the current (332) rgb_map (which has our desktop colours in it) because we need
to display multiple 24bit images at the same time. So they must use the same palette (so window colours 
stay correct) rather than the optimised palette (sadly)
(BadSector's 24bit buttons and skins look quite disgusting if you don't do this..)
*/
//      RGB_MAP *old_map = rgb_map;
//      generate_optimized_palette(bmp, pal, NULL);
//      rgb_map = malloc(sizeof(RGB_MAP));
//      if (rgb_map != NULL)
//	 create_rgb_table(rgb_map, pal, NULL);
      blit(bmp, b2, 0, 0, 0, 0, bmp->w, bmp->h);
//      if (rgb_map != NULL)
//	 free(rgb_map);
//      rgb_map = old_map;
   }
   else if (bitmap_color_depth(bmp) == 8) {
      select_palette(pal);
      blit(bmp, b2, 0, 0, 0, 0, bmp->w, bmp->h);
      unselect_palette();
   }
   else {
      blit(bmp, b2, 0, 0, 0, 0, bmp->w, bmp->h);
   }
   destroy_bitmap(bmp);
   return b2;
}
/* register_bitmap_file_type_exit:
 *  Free list of registered bitmap file types.
 */
static void register_bitmap_file_type_exit(void)
{
   struct bitmap_loader_type *iter = first_loader_type, *iter2 = NULL;
   while (iter) {
      iter2 = iter->next;
      free(iter->type.ext);
      free(iter);
      iter = iter2;
   }
   first_loader_type = NULL;
   _remove_exit_func(register_bitmap_file_type_exit);
}

/* register_bitmap_file_type_init:
 *  Register built-in bitmap file types.
 */
void register_bitmap_file_type_init(void)
{
   char buf[16];
   _add_exit_func(register_bitmap_file_type_exit);
   register_bitmap_file_type(uconvert_ascii("bmp", buf), load_bmp, save_bmp);
   register_bitmap_file_type(uconvert_ascii("lbm", buf), load_lbm, NULL);
   register_bitmap_file_type(uconvert_ascii("pcx", buf), load_pcx, save_pcx);
   register_bitmap_file_type(uconvert_ascii("tga", buf), load_tga, save_tga);
}


/* 
 _fixup_loaded_bitmap() can be called here (mostly I suspect because gbm.a and libjpeg.a aren't updated to WIP allegro)
 because gifs return 8 bit images. Ensures that everything we get ends up at screen depth,
 the new allegro functions call it in any case so we will not.
 conv_to_skipcolor_bitmap is needed in 8 bit mode to turn full magenta (which allegro docs erratically refer to as
 bright pink) into (transparent) palette entry 0. If dithering happens to use this colour the result is - interesting - 
 (try rgbcmy ramps in 8 bit -): serves em right for reusing ancient ideas from the TV industry I guess)
*/
GrIMAGE *gr_load_bitmap ( char *filename )
{
  if(filename) {
   GrIMAGE * bmp;
   memcpy(Gr_pal,the_palette,sizeof(PALETTE)); /* the loaders change the palette (though I'm trying hard to make sure the dont.) */
   if(screen_depth<15) set_color_conversion(COLORCONV_TOTAL|COLORCONV_DITHER);
   bmp=load_bitmap(filename, Gr_pal);
   if(screen_depth<15)set_color_conversion(COLORCONV_TOTAL);

   if(bmp) {
    if (screen_depth < 15) {
     if(bmp->vtable->color_depth == screen_depth) return conv_to_skipcolor_bitmap(bmp,NOCOLOR_RED,NOCOLOR_GREEN,NOCOLOR_BLUE);
     memcpy(Gr_pal,the_palette,sizeof(PALETTE)); /* fixup_loaded_bitmap can alter the palette too */
     set_color_conversion(COLORCONV_TOTAL|COLORCONV_DITHER);
     bmp=_fixup_loaded_bitmap((BITMAP *)bmp,Gr_pal,screen_depth);
     set_color_conversion(COLORCONV_TOTAL);
     if(bmp)conv_to_skipcolor_bitmap(bmp,NOCOLOR_RED,NOCOLOR_GREEN,NOCOLOR_BLUE);
     return bmp;
    } else {
     if(bmp->vtable->color_depth == screen_depth) return bmp;
     memcpy(Gr_pal,the_palette,sizeof(PALETTE)); /* fixup_loaded_bitmap can alter the palette too */
     return(_fixup_loaded_bitmap((BITMAP *)bmp,Gr_pal,screen_depth));
    }
   }
  }
  return NULL;
};

void colors_init ( void );
l_bool  screen_reload ( void )
{
  GrSetMode(screen_card, screen_viswidth, screen_visheight, screen_width, screen_height, screen_depth);
  GrClearClip(screen);
  if ( screen_virtual ) {
     draw_sprite(screen, screen_virtual, 0, 0);
     set_clip(screen, -1, -1, -1, -1);
     set_clip(screen_virtual, -1, -1, -1, -1);
  };
/* I think we probably need to reload the palette in 8 bit mode, seems good */
  if ( screen_depth < 15 ) colors_init();
  return true;
};

void    screen_halt ( void )
{
  if ( screen_virtual ) {
     set_clip(screen, 0, 0, screen_width, screen_height);
     set_clip(screen_virtual, 0, 0, screen_width, screen_height);
     show_mouse(NULL);
     blit(screen, screen_virtual, 0, 0, 0, 0, screen_width, screen_height);
     show_mouse(screen);
  };
  GrSetTextMode();
};


l_bool  screen_init ( void )
{
  long card  = 0;//get_key_integer(INI_MAINFILE, INI_SCREEN, "card");
  long w     = get_key_integer("system/graphics/width");
  long h     = get_key_integer("system/graphics/height");
  long v_w   = 0;//get_key_integer(INI_MAINFILE, INI_SCREEN, "virtual_width");
  long v_h   = 0;//get_key_integer(INI_MAINFILE, INI_SCREEN, "virtual_height");
  long depth = get_key_integer("system/graphics/depth");
  DEBUG_printf("\n...initializing screen from file '%s' in structure '%s' by these parameters :\n", INI_MAINFILE, INI_SCREEN); /* error message, if some occures */
  DEBUG_printf(" - depth: %i bits per pixel (%d colors)\n - width: %i\n - height: %i\n", depth, 1<<depth, w, h); /* error message, if some occures */
  if (w==0 || h == 0 || depth == 0) safe_mode=true; /* elementary precaution jdh */
  /* safe mode is set */
  if ( safe_mode ) {
      DEBUG_printf("\nSEAL running in safe mode\n\n");
      w = 640;
      h = 480;
      v_w = 0;
      v_h = 0;
      depth = 8;
      /* actually there is a GFX_SAFE card defined in new_allegro we should try to use I guess, because some
         modern cards can refuse to do VGA (cheapskates!), so the docs say. Still, we do try 15 bits before giving up..
      */
  };

  if ( !GrSetMode(card, w, h, v_w, v_h, depth) ) { /* not exist this resolution */
     DEBUG_printf("\n...try to initializing in new resolution"); /* error message, if some occures */
     DEBUG_printf(" - depth: 15 bits per pixel (%d colors)\n - width: 640\n - height: 480\n", 1<<15); /* error message, if some occures */
     w = v_w = 640;
     h = v_h = 480;
     depth = 15;
     if ( !GrSetMode(card, w, h, v_w, v_h, depth) ) { /* not found this driver */
         DEBUG_printf("\nERROR: Your card does not support 640x480 resolution in %d colors", 1<<15); /* error message, if some occures */
         seal_error(ERR_NONREGULAR, "\nERROR: Your card not support 640x480 resolution in %d colors\n", 1<<15);
     };
  };
#ifndef NO_SCREEN
  GrClearClip(screen);
#endif
  DEBUG_printf("\nScreen is ok");
  screen_viswidth  =  SCREEN_W;
  screen_visheight =  SCREEN_H;
  screen_depth     =  get_depth(screen);
#ifdef NO_SCREEN
if(screen_depth==0)screen_depth=8;
#endif
  screen_card      =  card;
  screen_width    = max(SCREEN_W, min(VIRTUAL_W, v_w));
  screen_height   = max(SCREEN_H, min(VIRTUAL_H, v_h));
  DEBUG_printf("\n..initializing the virtual screen");
  set_color_conversion(COLORCONV_TOTAL);
  screen_virtual = create_bitmap(screen_width, screen_height);
  DEBUG_printf("   ...initializing virtual screen\n"); /* error message, if some occures */
  if ( !screen_virtual ) {
      DEBUG_printf("     - ERROR :: virtual screen is not installed\n"); /* error message, if some occures */
      return false;
  };
  DEBUG_printf("   screen and virtual screen were succesfully installed\n"); /* error message, if some occures */
  return true;
};


void screen_done ( void )
{
  if ( screen_virtual )
     destroy_bitmap(screen_virtual);
  screen_virtual = NULL;
  screen_width    = 0;
  screen_height   = 0;
  GrSetTextMode();
};


/*#include"drivers.c"*/
/****************************************************************/
/*                                                              */
/*                          drivers.c                           */
/*                                                              */
/*                Seal - free desktop environment               */
/*                                                              */
/*                    Copyright (c) 1999,2000                   */
/*                         Michal Stencl                        */
/*                     All Rights Reserved                      */
/*                                                              */
/* mail : stenclpmd@ba.telecom.sk                               */
/* web  : http://www.home.sk/public/seal/                       */
/*                                                              */
/* This file is part of Seal.                                   */
/*                                                              */
/* Seal is free software; you can redistribute it and/or        */
/* modify it under the terms of the GNU General Public License  */
/* as published by the Free Software Foundation; either version */
/* 2, or (at your option) any later version.                    */
/*                                                              */
/* Seal is distributed in the hope that it will be useful, but  */
/* WITHOUT ANY WARRANTY; without even the implied warranty of   */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See    */
/* the GNU General Public License for more details.             */
/*                                                              */
/* You should have received a copy of the GNU General Public    */
/* License along with Seal; see the file COPYING.  If not,      */
/* write to the Free Software Foundation, 675 Mass Ave,         */
/* Cambridge, MA 02139, USA.                                    */
/*                                                              */
/****************************************************************/

/*#include"brffunc.c"*/
/****************************************************************/
/*                                                              */
/*                          brffunc.c                           */
/*                                                              */
/*                Seal - free desktop environment               */
/*                                                              */
/*                    Copyright (c) 1999,2000                   */
/*                         Michal Stencl                        */
/*                     All Rights Reserved                      */
/*                                                              */
/* mail : stenclpmd@ba.telecom.sk                               */
/* web  : http://www.home.sk/public/seal/                       */
/*                                                              */
/* This file is part of Seal.                                   */
/*                                                              */
/* Seal is free software; you can redistribute it and/or        */
/* modify it under the terms of the GNU General Public License  */
/* as published by the Free Software Foundation; either version */
/* 2, or (at your option) any later version.                    */
/*                                                              */
/* Seal is distributed in the hope that it will be useful, but  */
/* WITHOUT ANY WARRANTY; without even the implied warranty of   */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See    */
/* the GNU General Public License for more details.             */
/*                                                              */
/* You should have received a copy of the GNU General Public    */
/* License along with Seal; see the file COPYING.  If not,      */
/* write to the Free Software Foundation, 675 Mass Ave,         */
/* Cambridge, MA 02139, USA.                                    */
/*                                                              */
/****************************************************************/


#include"alltogrx.h"

/* from allegttf library */

#include"drivers.h"
#include"colors.h"
/*#include"fonts.c"*/
/****************************************************************/
/*                                                              */
/*                           fonts.c                            */
/*                                                              */
/*                Seal - free desktop environment               */
/*                                                              */
/*                    Copyright (c) 1999,2000                   */
/*                         Michal Stencl                        */
/*                     All Rights Reserved                      */
/*                                                              */
/* mail : stenclpmd@ba.telecom.sk                               */
/* web  : http://www.home.sk/public/seal/                       */
/*                                                              */
/* This file is part of Seal.                                   */
/*                                                              */
/* Seal is free software; you can redistribute it and/or        */
/* modify it under the terms of the GNU General Public License  */
/* as published by the Free Software Foundation; either version */
/* 2, or (at your option) any later version.                    */
/*                                                              */
/* Seal is distributed in the hope that it will be useful, but  */
/* WITHOUT ANY WARRANTY; without even the implied warranty of   */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See    */
/* the GNU General Public License for more details.             */
/*                                                              */
/* You should have received a copy of the GNU General Public    */
/* License along with Seal; see the file COPYING.  If not,      */
/* write to the Free Software Foundation, 675 Mass Ave,         */
/* Cambridge, MA 02139, USA.                                    */
/*                                                              */
/****************************************************************/

#include"fonts.h"
#include<unistd.h>

t_fonts_loaded *system_fonts = NULL;

int    add_font_to_system ( void *font, char *name, int weight )
{
  t_fonts_loaded *s = system_fonts;
  t_fonts_loaded *n;
  t_fonts_loaded *last = NULL;
  int             fh = 0;
  if ( !font || !name ) return 0;
  while ( s ) {
    last = s;
    if ( (fh == s->weight) && !stricmp(s->name, name) ) return 2;
    s = s->next;
  };
  n = (t_fonts_loaded *)_malloc(sizeof(t_fonts_loaded));
  if ( !n ) return 0;
  memset(n, 0, sizeof(t_fonts_loaded));
  if ( last ) last->next = n;
  else system_fonts = n;
  n->font = font;
  n->weight = weight;
  n->name = _strdup(name);
  return 1;
};


void    load_supported_fonts ( char *ini_array_name, l_bool in_mem )
{
  ini_data *d = getinidata_fromfile(INI_MAINFILE, ini_array_name);
  DEBUG_printf("\n...initializing supported fonts from '%s' file, from structure '%s'\n", INI_MAINFILE, ini_array_name); /* error message, if some occures */
  if ( d ) {
    int   lines = getini_linenum ( d );
    while ( lines > 0 ) {
      char *def = NULL;
      char *func = NULL;
      char *value = getini_line ( &def, d, lines );
      char **f = getini_function ( &func, def );
      char **h = getini_values ( value );
      int    fr = -1;
      int    to = -1;
      /* load font, value is name of font to be loaded, def is file of font */

/* Yuk! What a bunch of nasty non-descriptive things. */

      if ( f && f[0] && (*(f[0]) == INI_DECANUM) )
        fr = *((long*)(&(f[0][1])));
      if ( f && f[1] && (*(f[1]) == INI_DECANUM) )
        to = *((long*)(&(f[1][1])));
      if ( h && h[0] && (*(h[0]) == INI_STRING) ) {
        char **t = h;
        while ( (*t) ) {
          if ( t[0] && (*(t[0]) == INI_DECANUM) ) {
            void *f = NULL;
               /* mem is irrelevant, we load to memory always - prefer a delay at start than during operation.*/
               f = seal_load_font(&(h[0][1]), NULL, *((long*)(&(t[0][1]))), *((long*)(&(t[0][1]))), fr, to);
            DEBUG_printf(" - try to support font '%s' from file '%s' in width %i & height %i \n", func, &(h[0][1]), *((long*)(&(t[0][1]))), *((long*)(&(t[0][1])))); /* error message, if some occures */
            if ( !add_font_to_system(f, func, *((long*)(&(t[0][1])))) ) /* error */
                 DEBUG_printf("   - ERROR :: font wasn't installed\n"); /* error message, if some occures */
            else /* all is ok */
                 DEBUG_printf("   - font was succesfull installed\n"); /* error message, if some occures */
          };
          t++;
        };
      };
      freeini_values(f);
      freeini_values(h);
      if(func)_free(func);
      if(value)_free(value);
      if(def)_free(def);
      lines--;
    };
    _free(d);
  } /* else nothing in ini file */
};


void    unload_system_fonts ( void )
{
  /* did nothing - what a pity */
  t_fonts_loaded *s = system_fonts;
  t_fonts_loaded *last = NULL;
  while ( s ) {
    last = s;
    _free(s->name);
    s = s->next;
    _free(last);
  };
};

l_font *get_font_in_size ( char *fontname, int w, int h )
{
  t_fonts_loaded *s = system_fonts;
  while ( s ) {
/* jdh request for -1 matches first available size in (first) matching font */
    if ( ((w==-1)?1:(s->weight == w)) && fontname && s->name &&
         !stricmp(fontname, s->name) ) {
      return s->font;
    };
    s = s->next;
  };
  return NULL;
};



/*#include"sig_err.c"*/
/****************************************************************/
/*                                                              */
/*                          sig_err.c                           */
/*                                                              */
/*                Seal - free desktop environment               */
/*                                                              */
/*                    Copyright (c) 1999,2000                   */
/*                         Michal Stencl                        */
/*                     All Rights Reserved                      */
/*                                                              */
/* mail : stenclpmd@ba.telecom.sk                               */
/* web  : http://www.home.sk/public/seal/                       */
/*                                                              */
/* This file is part of Seal.                                   */
/*                                                              */
/* Seal is free software; you can redistribute it and/or        */
/* modify it under the terms of the GNU General Public License  */
/* as published by the Free Software Foundation; either version */
/* 2, or (at your option) any later version.                    */
/*                                                              */
/* Seal is distributed in the hope that it will be useful, but  */
/* WITHOUT ANY WARRANTY; without even the implied warranty of   */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See    */
/* the GNU General Public License for more details.             */
/*                                                              */
/* You should have received a copy of the GNU General Public    */
/* License along with Seal; see the file COPYING.  If not,      */
/* write to the Free Software Foundation, 675 Mass Ave,         */
/* Cambridge, MA 02139, USA.                                    */
/*                                                              */
/****************************************************************/

#include<signal.h>
#include "window.h"


l_int opened = 0;


void signal_init ( void )
{
     /* lock memory of error signal function */
//   LOCK_FUNCTION(signal_error);
     /* set signals function */
//   old_sig_abrt = signal(SIGABRT, signal_error);
//   old_sig_fpe  = signal(SIGFPE,  signal_error);
//   old_sig_ill  = signal(SIGILL,  signal_error);
//   old_sig_segv = signal(SIGSEGV, signal_error);
//   old_sig_term = signal(SIGTERM, signal_error);
//   old_sig_int  = signal(SIGINT,  signal_error);
//   old_sig_kill = signal(SIGKILL, signal_error);
//   old_sig_quit = signal(SIGQUIT, signal_error);
   opened = 0;
};


void signal_done ( void )
{

};


#include"registry.h"

/* names of system icons */
l_text  icon_id[256] = {"application", "browser", "controlpanel", "run", "network",
                        "shutdown", "editor", "folder", "openfolder", "fonts",
                         "mycomputer", "internetdialer", "internet", "internetsettings",
                         "help", "mailer", "printers", "settings", "systemfolder",
                         "pcsystem", "template", "trashempty", "trashfull", "utility",
                         "drive3_5", "drive5_25", "drivecd", "drivehd", "favorites",
                         "documents", "seallogo", "audio", "desktop", NULL};
/* icon library, get icon from this library by icon_sysget(id, size), ...see above */
l_iconlibrary *icon_system = NULL;
/* if non-zero the safe mode is set */
l_int   safe_mode = 0;
/* array of saved timers */
p_timer_def safe_timers = NULL;
/* font declarations */
/* defines if the font will be smooth */
l_int   font_smooth = 0;
/* defines if the font will 1,25* greater than normal font */
l_long  font_times_1_25 = 0;
/* pointer to regular system font */
l_font *font_system = NULL;
/* pointer to bold system font */
l_font *font_system_bd = NULL;
/* pointer to italic system font */
l_font *font_system_i = NULL;
/* pointer to bold+italic system font */
l_font *font_system_bi = NULL;

/* this function is called before Seal starting. You can initialize it to your own
   shot function.
*/
void (*screen_shot) ( void ) = NULL;

/* safe timer */
void  safe_timer ( void (*stop)(), void (*reload)() )
{
   p_timer_def x = safe_timers;
   /* search for timer */
   while ( x && (x->stop != stop) ) x = x->next;
   if ( !x ) { /* not found, so insert */
       p_timer_def x = (p_timer_def)_malloc(sizeof(t_timer_def));
       if ( x ) /* enough memory */
          clear_type(x, sizeof(t_timer_def)); /* all to ZERO */
          x->stop = stop;
          x->reload = reload;
          x->active = 1;
          x->next = safe_timers;
          safe_timers = x;
   };
};


/* erase previous saved timer */
void  erase_safe_timer ( void (*stop)() )
{
   p_timer_def x = safe_timers;
   p_timer_def last = NULL;
   /* search for timer */
   while ( x ) {
      p_timer_def s = x->next;
      if ( !last ) { /* first item */
         safe_timers = x->next;
         _free(x);
         return;
      } else { /*  */
          last->next = x->next;
          _free(x);
          return;
      };
      last = x;
      x = s;
   };
};


void safe_timer_halt ( void )
{
   p_timer_def x = safe_timers;
   /* for all timers */
   while ( x ) {
      /* it's able to stop it */
      if ( x->active ) {
            x->stop(); /* stop timer */
            x->active = 0; /* set to passive timer */
      };
      /* go to the next one */
      x = x->next;
   };
};


void safe_timer_reload ( void )
{
   p_timer_def x = safe_timers;
   /* for all timers */
   while ( x ) {
      /* it's able to reload it */
      if ( !x->active ) {
            x->reload(); /* stop timer */
            x->active = 1; /* set to passive timer */
      };
      /* go to the next one */
      x = x->next;
   };
};


void safe_timer_free_all ( void )
{
   p_timer_def x = safe_timers;
   /* for all timers */
   while ( x ) {
      p_timer_def n = x->next;
      /* free memory */
      _free(x);
      /* go to the next one */
      x = n;
   };
   safe_timers = NULL;
};


/* initialize icons. function is called in function drivers_init */

void icons_init ( void )
{
    char *icon_sys = getini_fromfile (INI_MAINFILE, INI_ICONS, "system_icons");
    if(!icon_sys) icon_sys=_strdup("./system/icons/system.dat"); /* use a sane default */
    DEBUG_printf("\n...initializing icons from '%s'\n", icon_sys); /* error message, if some occures */
    /* icon loading... */
    icon_system = load_icon_library(icon_sys, NOCOLOR_RED, NOCOLOR_GREEN, NOCOLOR_BLUE);
    if ( icon_system ) /* debug info */
        DEBUG_printf(" - icons was succesfully loaded\n"); /* error message, if some occures */
    else
        DEBUG_printf(" - ERROR :: icons wasn't succesfully loaded\n"); /* error message, if some occures */
    _free(icon_sys); /* free text of path */
};


/* destroy icons. function is called in function drivers_done */

void icons_done ( void )
{
  unload_icon_library(icon_system);
  icon_system = NULL;
};


/* return (id) of the system font from the name (n) */
l_int  get_icon_id ( l_text n )
{
  l_int i;
  for ( i=0; (i < 256) && icon_id[i]; i++ ) {
     if ( !stricmp(icon_id[i], n) ) return i;
  };
  return -1;
};


/* initialize fonts. function is called in function drivers_init */
void fonts_init ( void )
{
  font_times_1_25  = getininum_fromfile(INI_MAINFILE, INI_FONTS, "size 125%");
  font_smooth      = getininum_fromfile(INI_MAINFILE, INI_FONTS, "antialiasing");
  DEBUG_printf("\n...initializing fonts in "); /* error message, if some occures */
  DEBUG_test(font_times_1_25, DEBUG_printf("125%% size \n"),
                              DEBUG_printf("100%% size \n"));
  if ( font_times_1_25 ) {
    char *f_reg = getini_fromfile (INI_MAINFILE, INI_FONTS, "regular 125%");
    char *f_bd  = getini_fromfile (INI_MAINFILE, INI_FONTS, "bold 125%");
    char *f_i   = getini_fromfile (INI_MAINFILE, INI_FONTS, "italic 125%");
    char *f_bi  = getini_fromfile (INI_MAINFILE, INI_FONTS, "bold+italic 125%");
    if (!f_reg) f_reg=_strdup("./system/fonts/helv15.fnt"); /* try a sane default */
    if (!f_bd) f_bd=_strdup(f_reg); /* things are going bad - hope we still have a regular font at least */
    if (!f_i) f_i=_strdup(f_reg); /* ditto */
    if (!f_bi) f_bi=_strdup(f_reg); /* ditto */

    font_system    = GrLoadFont(f_reg, NULL, FONT_DEFAULT_WIDTH, FONT_DEFAULT_HEIGHT, -1, -1);
    font_system_bd = GrLoadFont(f_bd, NULL, FONT_DEFAULT_WIDTH, FONT_DEFAULT_HEIGHT, -1, -1);
    font_system_i  = GrLoadFont(f_i, NULL, FONT_DEFAULT_WIDTH, FONT_DEFAULT_HEIGHT, -1, -1);
    font_system_bi = GrLoadFont(f_bi, NULL, FONT_DEFAULT_WIDTH, FONT_DEFAULT_HEIGHT, -1, -1);
    if ( !font_system ) { /* regular system font */
      DEBUG_printf(
" - ERROR :: system regular font not loaded in 125%% size from '%s' and try to load in 100% size from ", f_reg);
      /* error message, if some occures */
      _free(f_reg);
      f_reg   = getini_fromfile (INI_MAINFILE, INI_FONTS, "regular");
      font_system   = GrLoadFont(f_reg, NULL, FONT_DEFAULT_WIDTH, FONT_DEFAULT_HEIGHT, -1, -1);
      DEBUG_printf("%s \n", f_reg);
    };
    if ( !font_system_bd ) { /* bold system font */
      DEBUG_printf(
" - ERROR :: system bold font not loaded in 125%% size from '%s' and try to load in 100% size from ", f_bd);
      /* error message, if some occures */
      _free(f_bd);
      f_bd   = getini_fromfile (INI_MAINFILE, INI_FONTS, "bold");
      font_system_bd   = GrLoadFont(f_bd, NULL, FONT_DEFAULT_WIDTH, FONT_DEFAULT_HEIGHT, -1, -1);
      DEBUG_printf("%s \n", f_bd);
    };
    if ( !font_system_i ) { /* italic system font */
      DEBUG_printf(
" - ERROR :: system italic font not loaded in 125%% size from '%s' and try to load in 100% size from ", f_i);
      /* error message, if some occures */
      _free(f_i);
      f_i   = getini_fromfile (INI_MAINFILE, INI_FONTS, "italic");
      font_system_i   =GrLoadFont(f_i, NULL, FONT_DEFAULT_WIDTH, FONT_DEFAULT_HEIGHT, -1, -1);
      DEBUG_printf("%s \n", f_i);
    };
    if ( !font_system_bi ) {
      DEBUG_printf(
" - ERROR :: system bold italic font not loaded in 125%% size from '%s' and try to load in 100% size from ", f_bi);
       /* error message, if some occures */
      _free(f_bi);
      f_bi   = getini_fromfile (INI_MAINFILE, INI_FONTS, "bold+italic");
      font_system_bi   = GrLoadFont(f_bi, NULL, FONT_DEFAULT_WIDTH, FONT_DEFAULT_HEIGHT, -1, -1);
      DEBUG_printf("%s \n", f_bi);
    };
    _free(f_reg);
    _free(f_bd);
    _free(f_i);
    _free(f_bi);
  } else {
    char *f_reg   = getini_fromfile (INI_MAINFILE, INI_FONTS, "regular");
    char *f_bd    = getini_fromfile (INI_MAINFILE, INI_FONTS, "bold");
    char *f_i     = getini_fromfile (INI_MAINFILE, INI_FONTS, "italic");
    char *f_bi    = getini_fromfile (INI_MAINFILE, INI_FONTS, "bold+italic");
    if (!f_reg) f_reg=_strdup("./system/fonts/helv15.fnt"); /* try some sane default */
    if (!f_bd) f_bd=_strdup(f_reg); /* things are going bad - hope we still have the regular font at least */
    if (!f_i) f_i=_strdup(f_reg); /* ditto */
    if (!f_bi) f_bi=_strdup(f_reg); /* ditto */
    font_system    = GrLoadFont(f_reg, NULL, FONT_DEFAULT_WIDTH, FONT_DEFAULT_HEIGHT, -1, -1);
    font_system_bd = GrLoadFont(f_bd, NULL, FONT_DEFAULT_WIDTH, FONT_DEFAULT_HEIGHT, -1, -1);
    font_system_i  = GrLoadFont(f_i, NULL, FONT_DEFAULT_WIDTH, FONT_DEFAULT_HEIGHT, -1, -1);
    font_system_bi = GrLoadFont(f_bi, NULL, FONT_DEFAULT_WIDTH, FONT_DEFAULT_HEIGHT, -1, -1);
    _free(f_reg);
    _free(f_bd);
    _free(f_i);
    _free(f_bi);
  };
  if(!font_system) font_system=(l_font *)font; /* use the (nasty allegro default) 8*8 font for these two cos we should be able to read something */
  if(!font_system_bd) font_system=(l_font *)font;

  /* debugger infos */
  DEBUG_test(font_system, DEBUG_printf(" - system regular font was succesfull loaded\n"),
                          DEBUG_printf(" - ERROR :: system regular font wasn't succesfull loaded\n"));
  DEBUG_test(font_system_bd, DEBUG_printf(" - system bold font was succesfull loaded\n"),
                          DEBUG_printf(" - ERROR :: system bold font wasn't succesfull loaded\n"));
  DEBUG_test(font_system_i, DEBUG_printf(" - system italic font was succesfull loaded\n"),
                          DEBUG_printf(" - ERROR :: system italic font wasn't succesfull loaded\n"));
  DEBUG_test(font_system_bi, DEBUG_printf(" - system bold italic font was succesfull loaded\n"),
                          DEBUG_printf(" - ERROR :: system bold italic font wasn't succesfull loaded\n"));
  load_supported_fonts(INI_TTFFONTS, false); /* load system true type fonts, quick */
  load_supported_fonts(INI_TTFFONTSMEM, true); /* load system true type fonts to memory */
};


/* destroy icons. function is called in function drivers_done  */
void fonts_done ( void )
{
  if(font_system && (font_system!=(l_font *)font))GrUnloadFont(font_system);
  if(font_system_bd && (font_system_bd!=(l_font *)font))GrUnloadFont(font_system_bd);
  if(font_system_i)GrUnloadFont(font_system_i);
  if(font_system_bi)GrUnloadFont(font_system_bi);
  font_system = NULL;
  font_system_bd = NULL;
  font_system_i = NULL;
  font_system_bi = NULL;
  unload_system_fonts();
  safe_timer_free_all();
};


/* done all drivers. this function is called in function program_done in "program.c" */
l_bool  drivers_done ( p_object o )
{
  if ( !obj_done((p_object)o) ) return false;
  fonts_done();
  icons_done();
  screen_done();
  colors_done();
  signal_done();
  allegro_exit();
  return true;
};


/* init all drivers. this function is called in function program_init in "program.c".
   return pointer to drivers, where objects such as mouse, keyboard, ... are stored
*/
t_drivers *drivers_init ( t_drivers *o )
{
  p_registry_search inf = (p_registry_search) _malloc(sizeof(t_registry_search));
  if ( !o ) return NULL;
  memset(o, 0, sizeof(t_drivers));
  obj_init(&o->obclass);
  OBJECT(o)->done = &drivers_done;

  //set_config_file("startup.cfg"); /* allegro standard startup configuration file */
  // set_config_file has problems - any error in it and the allegro_err global is used without
  // ever having been initialised leading to an unsightly crash
  // No matter, we just can use the standard allegro.cnf in any case

  DEBUG_printf(" - drivers_init : allegro_init() function \n");
  /* init allegro */
  allegro_init();
  DEBUG_printf(" - drivers_init : install_timer() function \n");
  /* install allegro timer */
  install_timer();
  DEBUG_printf(" - drivers_init : install timer for aclock function to 20 milsc \n");
  /* put aclock to timer queue. each 20 milisecs call this function. */
  install_int(&aclock, 20);
  DEBUG_printf(" - program_init : loading drivers from registry\n");
  DLXImport(_LIBEXPORTTABLE);
  if (reg_find_first("system/startup/drivers", inf)){
   do {
    l_text filedlx = get_key(inf->name);
    if(filedlx) {
     run_file(filedlx);
     _free(filedlx);
    }
   } while (reg_find_next(inf));
  }
  _free(inf);
  DEBUG_printf(" - drivers_init : screen_init() \n");
  /* init screen */
  screen_init();
  DEBUG_printf(" - drivers_init : colors_init() \n");
  /* init colors */
  colors_init();
  DEBUG_printf(" - drivers_init : keyboard inserting into drivers \n");
  /* make new keyboard and insert it to drivers object. ...see keyboard.h */
  o->obclass.insert((p_object)o, (p_object)key_init(_malloc(sizeof(t_keyboard))));
  DEBUG_printf(" - drivers_init : mouse inserting into drivers \n");
  /* make new mouse and insert it to drivers object. ...see mouse.h */
  o->obclass.insert((p_object)o, (p_object)mouse_init(_malloc(sizeof(t_mouse))));
  /* not call this object from program.translate_event ! */
  OBJECT(o)->set_options(OBJECT(o), OB_OF_NOTACTIVATE, true);
  DEBUG_printf(" - drivers_init : screen_shot() \n");
  /* draw something to screen before, we initialize all drivers and libraries */
  if ( screen_shot )
     screen_shot();
  DEBUG_printf(" - drivers_init : icons_init() \n");
  /* icons initialization */
  icons_init();
  DEBUG_printf(" - drivers_init : fonts_init() \n");
  /* init fonts */
  fonts_init();
  DEBUG_printf(" - drivers_init : signal_init() \n");
  /* signal errors init */
  signal_init();
  DEBUG_printf(" - drivers_init : return(o) \n\n");
  return o;
};


/*#include"colors.c"*/
/****************************************************************/
/*                                                              */
/*                          colors.c                            */
/*                                                              */
/*                Seal - free desktop environment               */
/*                                                              */
/*                    Copyright (c) 1999,2000                   */
/*                         Michal Stencl                        */
/*                     All Rights Reserved                      */
/*                                                              */
/* mail : stenclpmd@ba.telecom.sk                               */
/* web  : http://www.home.sk/public/seal/                       */
/*                                                              */
/* This file is part of Seal.                                   */
/*                                                              */
/* Seal is free software; you can redistribute it and/or        */
/* modify it under the terms of the GNU General Public License  */
/* as published by the Free Software Foundation; either version */
/* 2, or (at your option) any later version.                    */
/*                                                              */
/* Seal is distributed in the hope that it will be useful, but  */
/* WITHOUT ANY WARRANTY; without even the implied warranty of   */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See    */
/* the GNU General Public License for more details.             */
/*                                                              */
/* You should have received a copy of the GNU General Public    */
/* License along with Seal; see the file COPYING.  If not,      */
/* write to the Free Software Foundation, 675 Mass Ave,         */
/* Cambridge, MA 02139, USA.                                    */
/*                                                              */
/****************************************************************/
#include"colors.h"
#include"dataini.h"
#include"registry.h"
l_color *colors_standard = NULL;
l_byte palette_standard[]
 = {
       28,  1,   1, /* This line only set how many triples are there
                       => (25*1*1)                                    */
       0,   0,   0, /* -+                                             */
       0,   0, 128, /*  |                                             */
       0, 128,   0, /*  |                                             */
       0, 128, 128, /*  |                                             */
     128,   0,   0, /*  |                                             */
     128,   0, 128, /*  |                                             */
     128,  64,   0, /*  |                                             */
     192, 192, 192, /*  |=> Basic 16 Colors                           */
     128, 128, 128, /*  |                                             */
       0,   0, 255, /*  |                                             */
       0, 255,   0, /*  |                                             */
       0, 255, 255, /*  |                                             */
     255,   0,   0, /*  |                                             */
     255,   0, 255, /*  |                                             */
     255, 255,   0, /*  |                                             */
     255, 255, 255, /* -+                                             */

      90,  90,  100,
     105, 105, 115,
     120, 120, 130,
     135, 135, 145,
     150, 150, 150,
     156, 160, 178,
     170, 170, 188,
     195, 195, 195,
     210, 210, 220,
     225, 225, 235,
     240, 240, 250,
     255, 255, 255,
};

/*
  get color name_of_color for the file "seal.ini" from section [colors]
  example:
  l_color x = color_get_from_ini("3D_background");
Buglet = should cache the values rather than keep on reading them from the (cached) file
*/
l_color color_get_from_ini ( char *name_of_color )
{
  ini_rgb *scolor  = getini_color (INI_MAINFILE, INI_COLORS, name_of_color);
  if(scolor) {
   return makecol(scolor->r, scolor->g, scolor->b);
  } else return -1;
};



/* internal seal function, default called from drivers_init function */

l_color *color_trans_palette ( l_byte *rgb )
{
  l_long    size;
  static l_color  *pal; /* hopefully forcing optimiser not to put this on stack which seems to be a problem for some reason */
  l_long    i;
  l_int     dpt;
  if ( rgb ) {
    size = rgb[0]*rgb[1]*rgb[2];
    pal = _malloc(sizeof(l_color)*(size+1));
    if (pal) {
     dpt = get_depth(screen);
     pal[0] = size;
     for (i=1; i < size ; i++ ) {
        if ( dpt < 15 ) {
          RGB pr;
          pr.r = rgb[i*3];
          pr.g = rgb[i*3+1];
          pr.b = rgb[i*3+2];
#ifdef NOTDEF
          set_color(i, &pr);
#endif
          pal[i] = i;
        } else
          pal[i] = makecol(rgb[i*3], rgb[i*3+1], rgb[i*3+2]);
     }
    }
    return pal;
  }
  return NULL;
}

void colors_init ( void )
{
  colors_standard = color_trans_palette(palette_standard);
if(!rgb_map && (screen_depth<15)){
/* make an rgb lookup table to speed bitmap conversions */
     int i;
     generate_332_palette(the_palette);
     rgb_map=_malloc(sizeof(RGB_MAP));
     /* I think this should be right... */	
     for(i=1 ; i<colors_standard[0] ; i++) {
      the_palette[i].r=getr(colors_standard[i]);
      the_palette[i].g=getg(colors_standard[i]);
      the_palette[i].b=getb(colors_standard[i]);
     }
#ifndef NO_SCREEN
set_palette(the_palette); /* better than set_color, set_palette knows how to do it during retrace so as not to produce snow */
#endif
     create_rgb_table(rgb_map, the_palette , NULL);
}

};


void colors_done ( void )
{
  _free(colors_standard);
  colors_standard = NULL;
};




/*#include"view.c"*/
/****************************************************************/
/*                                                              */
/*                             view.c                           */
/*                                                              */
/*                Seal - free desktop environment               */
/*                                                              */
/*                    Copyright (c) 1999,2000                   */
/*                         Michal Stencl                        */
/*                     All Rights Reserved                      */
/*                                                              */
/* mail : stenclpmd@ba.telecom.sk                               */
/* web  : http://www.home.sk/public/seal/                       */
/*                                                              */
/* This file is part of Seal.                                   */
/*                                                              */
/* Seal is free software; you can redistribute it and/or        */
/* modify it under the terms of the GNU General Public License  */
/* as published by the Free Software Foundation; either version */
/* 2, or (at your option) any later version.                    */
/*                                                              */
/* Seal is distributed in the hope that it will be useful, but  */
/* WITHOUT ANY WARRANTY; without even the implied warranty of   */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See    */
/* the GNU General Public License for more details.             */
/*                                                              */
/* You should have received a copy of the GNU General Public    */
/* License along with Seal; see the file COPYING.  If not,      */
/* write to the Free Software Foundation, 675 Mass Ave,         */
/* Cambridge, MA 02139, USA.                                    */
/*                                                              */
/****************************************************************/

#include"alltogrx.h"
#include"drivers.h"
#include"view.h"
p_view  (*view_init) ( p_view o, t_rect r ) = &_view_init;
/* info board */
l_int   info_board_key = 0;
/* info board foreground color = black */
l_color info_board_fcolor = 0;
/* info board background color */
l_color info_board_b1color = 0;
/* info board background color */
l_color info_board_b2color = 0;
/* info board font = helv15 */
l_font *info_board_font = NULL;
/* size of standard item (menu, listbox, ... )*/
l_int   system_item_size = 16;

/* init info board from main ini file */
void  info_board_init ( void )
{
  info_board_fcolor=COLOR(CO_BLACK);
  info_board_b1color=COLOR(CO_LIGHTGRAY);
  info_board_b2color=COLOR(CO_DARKGRAY);
  iffound(info_board_fcolor , registry_color("info_board_fcolor"));
  iffound(info_board_b1color , registry_color("info_board_bcolor"));
  iffound(info_board_b2color , registry_color("info_board_bcolor_gradient"));
  info_board_key = 68; //(l_int)getininum_fromfile( INI_MAINFILE, "speed_keys", "info_board");
  info_board_font = font_system;
};


/* init views from main ini file */
void  view_ini ( void )
{
   info_board_init();
};


void  view_reset_prefer ( p_object o )
{
  p_object p = o->last_view(o);
  p_object x = NULL;
  if ( p ) {
    x = p->find_match_view(p, OB_SF_VISIBLE, OB_OF_SELECTABLE, true);
    if ( x ) o->set_prefer(o, x);
  };
  if ( !x ) obj_reset_prefer(o);
};

l_bool    view_done ( p_object o )
{
/* tricky - obj_exist(o) < 0 means o is a null pointer, 
   0 means its tag is TAG_DISPOSE (because dispose has had its way with this object)
*/
  if ( obj_exist(o) <= 0 ) return false;

  VIEW(o)->hide(VIEW(o));
  /* background releasing */
  if ( VIEW(o)->brush.background && (VIEW(o)->brush.state & BRUSH_SELFIMG) ) {
      destroy_bitmap(VIEW(o)->brush.background);
      VIEW(o)->brush.background = NULL;
      VIEW(o)->brush.state &= ~BRUSH_SELFIMG;
  };
  /* each object has own palette */
  #ifdef __EACH_OWN_PALETTE__
  if ( o->palette )  {_free( o->palette);  o->palette=NULL;} //afree((void**)(&o->palette));
  #endif
  if(VIEW(o)->info_text) {_free(VIEW(o)->info_text);VIEW(o)->info_text=NULL;}
  return obj_done(o);
};

/*
  show info board with text (text)
*/
void  _show_info_board ( p_view from, l_text text )
{
  p_view  o;
  t_point where;
  BITMAP *mb;
  t_point size;
  t_point s;
  t_rect  r;
  t_rect  safe;
  t_point p;
  BITMAP *back;
  BITMAP *out;
  if ( !text || !is_top_view_under_mouse_in_me(from) )  return;
  o = from->top_view(from);
  where = mouse->where;
  mb = mouse->get_cursor(mouse);
  size  = mb?point_assign(mb->w, mb->h):point_assign(16, 16);
  r = rect_assign(where.x, where.y, where.x, where.y);
  p = o->get_global_point(o, r.a);
  get_size_of_ftext(text, info_board_font, (int*)(&r.b.x), (int*)(&r.b.y));
  r.b.x += r.a.x+4;
  r.b.y += r.a.y+4;
  s = point_assign(rect_sizex(r), rect_sizey(r));
  back = create_bitmap(s.x+1, s.y+1);
  if ( !back ) return;
  if ( r.b.x > SCREEN_W ) {
    r.b.x = SCREEN_W;
    r.a.x = SCREEN_W-s.x;
  };
  if ( r.a.x < 0 ) {
    r.a.x = 0;
    r.b.x = s.x;
  };
  if ( r.b.y > SCREEN_H ) {
    r.b.y = SCREEN_H;
    r.a.y = SCREEN_H-s.y;
  };
  if ( r.a.y < 0 ) {
    r.a.y = 0;
    r.b.y = s.y;
  };
  mouse->hide(mouse);
  blit(screen, back, r.a.x, r.a.y, 0, 0, s.x+1, s.y+1);
  mouse->show(mouse);
  out = o->begin_paint(o, &p, r);
  safe = r;
  if ( out ) {
    l_text t = text;
    fade_rect(out, r.a.x+p.x, r.a.y+p.y, r.b.x+p.x, r.b.y+p.y, info_board_b1color, info_board_b2color, 2);
    button(out, r.a.x+p.x, r.a.y+p.y, r.b.x+p.x, r.b.y+p.y, COLOR(CO_WHITE), COLOR(CO_DARKGRAY));
    r = rect_assign(r.a.x+2, r.a.y+2, r.b.x-2, r.b.y-2);
    while ( t && *t && (r.a.y <= safe.b.y) ) {
      l_text oldt = t;
      r.b.y  = r.a.y + FONT_GETHEIGHT(info_board_font);
      t = strchr(t, '\n'); /* find end-char */
      textout_draw_rect(out, info_board_font, oldt, strsize(oldt, t),
                        r.a.x+p.x, r.a.y+p.y, r.b.x+p.x, r.b.y+p.y,
                        TX_ALIGN_LEFT, info_board_fcolor, TX_NOCOLOR, 0);
      if ( t ) { /* enter was found */
        t++; /* move to next char after enter */
        r.a.y += FONT_GETHEIGHT(info_board_font);
      };
    };
  };
  o->end_of_paint(o, safe);
  while ( KEYCTRL(KB_F1) || !event_main.type ) {
      OBJECT(from)->get_event(OBJECT(from), &event_main);
  };
  if ( back ) {
    BLIT(back, screen, 0, 0, safe.a.x, safe.a.y, s.x, s.y);
    destroy_bitmap(back);
  };
};

void  view_show_info_board ( p_view from )
{
   _show_info_board(from, from->info_text);
};

p_view  view_top_view ( p_view o )
{
  while ( OBJECT(o)->owner_view(OBJECT(o)) )
    o = VIEW(OBJECT(o)->owner_view(OBJECT(o)));
  return o;
};


void    view_set_mouse_cursor ( p_view o )
{
  mouse_set_cursor_focus_id(o->cursor);
};


l_bool  view_is_mouse_in_view ( p_view o )
{
  t_point p = o->get_local_point(o, mouse->where);
  if ( (p.x < 0) || (p.x > rect_sizex(o->bounds)) ||
       (p.y < 0) || (p.y > rect_sizey(o->bounds)) ) return false;
  return true;
};


p_view  view_get_view_under_mouse ( p_view o )
{
  p_view x = VIEW(OBJECT(o)->first_view(OBJECT(o)));
  p_view f = x;
  t_point where = o->get_local_point(o, mouse->where);
  if ( x )
    do {
      if ( OBJECT(x)->is_state(OBJECT(x), OB_SF_VISIBLE) ) /* is visible ? */
        if ( rect_contains(x->bounds, where) )
          return x;
      x = VIEW(OBJECT(x)->next_view(OBJECT(x)));
    } while ( x != f );
  return NULL;
};


p_view  view_get_top_view_under_mouse ( p_view o )
{
  p_view x = VIEW(OBJECT(o)->first_view(OBJECT(o)));
  p_view f = x;
  t_point where = o->get_local_point(o, mouse->where);
  if ( x )
    do {
      if ( OBJECT(x)->is_state(OBJECT(x), OB_SF_VISIBLE) ) /* is visible ? */
        if ( rect_contains(x->bounds, where) ) {
            return x->get_top_view_under_mouse(x);
        };
      x = VIEW(OBJECT(x)->next_view(OBJECT(x)));
    } while ( x != f );
  if ( rect_contains(o->get_local_extent(o), where) ) return o;
  return NULL;
};

l_bool  is_top_view_under_mouse_in_me ( p_view o )
{
    p_view tv = VIEW(o)->top_view(VIEW(o));
    p_view t  = tv->get_top_view_under_mouse(tv);
    return is_my_object(OBJECT(o), OBJECT(t));
};

void       view_play_process ( p_object o, t_event *event )
{
  if ( !o ) return;
  if ( !IS_ACTIVE_PROCESS(o) || o->is_options(o, OB_OF_NOTACTIVATE) ||
       EV_IGNORE_ACTIVE_PROCESS(event->type)  ) {
    if ( !o->is_options(o, OB_OF_STILLPROCESS) &&
          event->type & EV_MOUSE &&
         !is_top_view_under_mouse_in_me(VIEW(o)) ) return;
    ACTIVE_PROCESS(o);
    event_stop = o;
    o->translate_event(o, event);
    PASSIVE_PROCESS(o);
  };
};

void    view_translate_event ( p_object o, t_event *event )
{
//int junk=fprintf(stderr,"view_translate_event %lx\n",(unsigned long)o);
  p_view v  = VIEW(o)->get_view_under_mouse(VIEW(o));
  p_view t  = VIEW(o)->get_top_view_under_mouse(VIEW(o));
  if ( event->type & EV_MOUSE ) {
     if ( t )
        t->set_mouse_cursor(t);
     if ( v && IS_OKTOSUBPROCESS(v) ) {
       if ( !t ) t = v;
       /* drag & drop data controls... jdh lets use middle(=left+right) button to drag, using keyboard+mouse is distracting */
       if (  OBJECT(mouse)->state & MO_SF_MOUSEMDOWN ) { 
          /* drag data - allegro nicely says left+right together is middle mouse button if you have a 2 button mouse */
          t->drag_data(t);
          clear_event(event);
       };
       /* ...end of drag & drop data controls */
       PLAY_PROCESS(OBJECT(v), event);
     };
  } else
    obj_translate_event(o, event);
  if ( (OBJECT(keyb)->state & KB_SF_KEYDOWN) && KEYCTRL(KB_F1) )  /* show info text */
      VIEW(o)->show_info_board(VIEW(o));
//fprintf(stderr,"end view_translate_event %lx\n",(unsigned long)o);
};

void    view_setup ( p_object o )
{
  VIEW(o)->reset_align(VIEW(o));
  VIEW(o)->show(VIEW(o));
};

void    view_after_init ( p_object o )
{

};

void    view_put_in_front_of ( p_object o, p_object before )
{
  if ( o->owner ) {
    p_object owner = o->owner;
    owner->set_prefer(owner, o);
    if ( o != before ) {
      VIEW(o)->draw_overlays(VIEW(o));
      owner->remove(owner, o);
      owner->insert_before(owner, o, before);
      owner->reset_prefer(owner);
    };
  };
};

void    view_set_state ( p_object o, l_dword st, l_bool set )
{
  p_view own = VIEW(o->owner_view(o));
  if ( !(st & OB_SF_VISIBLE) || !set || o->is_options(o, VW_OF_VISIBILITY) )
    obj_set_state(o, st, set); /* object function */
  /* OB_SF_VISIBLE */
  if ( st & OB_SF_VISIBLE &&
      (!own || OBJECT(own)->is_state(OBJECT(own), OB_SF_VISIBLE)) ) {
    if ( o->owner && o->is_options(o, OB_OF_SELECTABLE) ) {
      if ( set ) {
         if ( !o->owner->prefer || o->is_options(o, OB_OF_TOPSELECT) )
            o->owner->reset_prefer(o->owner);
      } else if ( o->is_state(o, OB_SF_SELECTED) ) o->owner->reset_prefer(o->owner);
    }
    if ( set )
      VIEW(o)->draw_view(VIEW(o));
    else
      VIEW(o)->draw_under_view(VIEW(o));
  };
  if ( st & OB_SF_VISIBLE )
    VIEW(o)->for_each_sub_view_set_state(VIEW(o), OB_SF_VISIBLE, set);
};

void  view_reset_align ( p_view o )
{
  t_rect r = o->bounds;
  p_view ov = VIEW(OBJECT(o)->owner_view(OBJECT(o)));
  if ( ov ) {
    t_rect s = ov->get_limits(ov);
    if ( o->align & TX_ALIGN_CENTERX )
      r.a.x = s.a.x+(rect_sizex(s)-rect_sizex(r))/2;
    if ( o->align & TX_ALIGN_CENTERY )
      r.a.y = s.a.y+(rect_sizey(s)-rect_sizey(r))/2;
    if ( o->align & TX_ALIGN_LEFT )
      r.a.x = s.a.x;
    if ( o->align & TX_ALIGN_TOP )
      r.a.y = s.a.y;
    r.b.x = r.a.x+rect_sizex(o->bounds);
    r.b.y = r.a.y+rect_sizey(o->bounds);
    o->set_bounds(o, r);
  };
};

l_dword   view_execute_view ( p_view o, p_view sub )
{
  l_dword    message = MSG_NOTHING;
  if ( sub ) {
    l_dword     poptions = OBJECT(sub)->options;
    l_dword     pstate   = OBJECT(sub)->state;
    p_object    owner    = OBJECT(OBJECT(sub)->owner_view(OBJECT(sub)));
    p_object    prefer   = OBJECT(o)->prefer;
    OBJECT(sub)->set_options(OBJECT(sub), OB_OF_TOPSELECT, true);
    OBJECT(sub)->set_state(OBJECT(sub), OB_SF_MODAL, true);
    if ( !owner ) OBJECT(o)->insert(OBJECT(o), OBJECT(sub));
    OBJECT(o)->set_prefer(OBJECT(o), OBJECT(sub));
    message = OBJECT(sub)->execute(OBJECT(sub));
    if ( !owner ) {
      dispose(OBJECT(sub));
      sub = NULL;
    };
    if ( sub ) {
      OBJECT(sub)->options = poptions;
      OBJECT(sub)->state = pstate;
    };
    OBJECT(o)->set_prefer(OBJECT(o), prefer);
  };
  return message;
};

p_data  view_drag_data ( p_view o )
{
  clear_type(&clipboard, sizeof(t_data)); /* should you really clear the clipboard if there is no data (next statement)? */
  if ( l_tag_cmp(OBJECT(o)->data_type, TAG_NONE) ) /* it haven't any datatype */
     return NULL;
  clipboard.style = DS_WHATEVER;
  if ( OBJECT(o)->get_data(OBJECT(o), &clipboard) ) {
    p_view t  = NULL;
    mouse_set_cursor_focus_id(CUR_DRAG); /* set drag cursor */
    while ( OBJECT(mouse)->state & MO_SF_MOUSEMPRESS ) {  /* repeat until mouse is unpressed */
      p_view oldt = NULL; /* get top view */
      p_view tv = VIEW(o)->top_view(VIEW(o)); /* get top view */
      t  = tv->get_top_view_under_mouse(tv); /* get top mouse view */
      OBJECT(o)->get_event(OBJECT(o), &event_main);
      if ( t && OBJECT(mouse)->state & MO_SF_MOUSEMOVE ) { /* cal mouse top view object */
         /* info drag function */
          t->drag_where(t, &clipboard, t->get_local_point(t, mouse->where));
          if ( oldt ) { /* info drag function, that was call before */
              oldt->drag_where(oldt, &clipboard, oldt->get_local_point(oldt, mouse->where));
          };
      };
      oldt = t;
    };
    if ( t ) { /* redraw last */
         t->drag_where(t, NULL, t->get_local_point(t, mouse->where));
    };
    if ( o ) { /* redraw me */
         o->drag_where(o, NULL, o->get_local_point(o, mouse->where));
    };
    if ( t ) {
         set_event(&event_main, EV_MESSAGE, MSG_PASTE, OBJECT(t));
         OBJECT(o)->put_event(OBJECT(o), &event_main);
    };
    clear_event(&event_main);
    mouse_set_cursor_focus_id(CUR_ARROW); /* set arrow cursor */
    return (&clipboard);
  };
  return NULL;
};


/*
   - this function is called even, when data is dropped under the object
   - where is local point to object in rectangle
          (0,0, rect_sizex(o->bounds), rect_sizey(o->bounds))
   - rec is pointer to t_data structure and contains information about
     data that comes from draging object.
   - when rec = NULL, it means it's end of draging so take things to right
     place. if mouse(where) is not under me, it means that mouse was under me
     before.
   - standard structure of function is :
   if ( view_drag_where(o, rec, where) ) {  is mouse under view
       return true;
   } else  mouse is not under view or end
   if ( OBJECT(o)->is_state(OBJECT(o), OB_SF_FOCUSED) ) {
       if ( rec )  mouse is not under view
       else  end of dragging - give things to right place
   } else {  mouse is not under view or end but in non-focused view
   };
   return false;
*/
l_bool  view_drag_where ( p_view o, p_data rec, t_point where )
{
  if ( rec && rect_contains(o->get_local_extent(o), where) ) {
     if ( l_tag_cmp(OBJECT(o)->data_type, rec->id) &&
          !is_active(OBJECT(o)) ) { /* not active process me + under me */
         mouse_set_cursor_focus_id(CUR_DRAG); /* set drag cursor */
         return true;
     } else { /* my data_type not include rec->id */
        mouse_set_cursor_focus_id(CUR_STOP); /* set stop cursor */
        return false;
     };
  } else
     return false;
};


/*
  - where is local point
  - not used, it's for compatibility
*/

l_bool  view_drop_data ( p_view o, p_data rec, t_point where )
{
   return false;
};

void   view_drag_view ( p_view o, l_word mode, t_event *event )
{
  t_rect r;
  if ( o->drag_mode & mode ) {
    STOP_PROCESS();
    if ( mode & DM_DRAGMOVE ) mouse_set_cursor_focus_id(CUR_MOVE);
    if ( mode & DM_DRAGGROW ) mouse_set_cursor_focus_id(CUR_GROW);
    r = o->drag(o, mode, event);
    START_PROCESS();
    o->change_bounds(o, r);
  };
};

/* already set by allegro.h. No harm in reminding the compiler not to optimise these two out though */
extern volatile int mouse_x;
extern volatile int mouse_y;
extern int mouse_x_focus;
extern int mouse_y_focus;
t_rect  view_drag ( p_view o, l_word mode, t_event *event )
{
 static t_rect box; // we pass this struct back, make sure it still there after we leave
 box.a.x= o->bounds.a.x;
 box.a.y= o->bounds.a.y;
 box.b.x= o->bounds.b.x;
 box.b.y= o->bounds.b.y;
 if ( event->type & EV_MOUSE )  {
  if (!(mode & DM_DRAGGROW)){
/* Bug(ish)s - Cursors are wrong if the window changes (eg things in stillprocess, clocks for example).
 Wont let you shift stuff above or to left of screen - fixable
*/
    l_int fcx,fcy,msx,msy,cx=0,cy=0;
    l_int sx = box.b.x-box.a.x+1; // boxes size+1?
    l_int sy = box.b.y-box.a.y+1; // boxes +1??
    p_object o2=(p_object)o;
    BITMAP *old_cursor=mouse_get_cursor(mouse);
    BITMAP *cursor_bitmap=cursor_bitmap=create_bitmap(sx, sy);
    while ( o2 ) { // finds top left eliminating some indirection and overheads
     cx += ((p_view)o2)->bounds.a.x;cy += ((p_view)o2)->bounds.a.y;
     o2 =( (o2->owner) ? ( (o2->owner->tag & TAG_VIEW) ? o2->owner : obj_owner_view(o2->owner) ) : NULL );
    }
    msx=mouse_x-cx;  msy=mouse_y-cy; // current pointers distance from top left to restore it later
    position_mouse(cx, cy);
    blit(screen, cursor_bitmap, cx, cy, 0, 0, sx, sy); // make a mouse pointer from the screen & use it as cursor
    set_mouse_sprite(cursor_bitmap); // put it on screen

    fcx=mouse_x;fcy=mouse_y; // record starting mouse position
    while ( (event->type & EV_MOUSE) && !(OBJECT(mouse)->state & MO_SF_MOUSEUP) ) {
      OBJECT(o)->get_event(OBJECT(o), event);
    }
    sx=mouse_x;sy=mouse_y; // record ending mouse position

    fcx = sx-fcx;fcy = sy-fcy; // change= current mouse pos - original mouse pos
    set_mouse_sprite(old_cursor); // restore original mouse pointer
    position_mouse(msx+sx, msy+sy); // reposition so pointer is in same rel position as when we captured the screen
    destroy_bitmap(cursor_bitmap);
    box.a.x+=fcx;box.a.y+=fcy;box.b.x+=fcx;box.b.y+=fcy; // alter position
  } else {
    l_int sx,sy,fcx=mouse_x,fcy=mouse_y; // record starting mouse position
    while ( !(OBJECT(mouse)->state & MO_SF_MOUSEUP) ) {
      OBJECT(o)->get_event(OBJECT(o), event);
      sx = mouse_x;sy = mouse_y; // change= current mouse pos - prior mouse pos
      if( (sx-fcx) || (sy-fcy) ){
       box.b.x+=sx-fcx;box.b.y+=sy-fcy; // alter size
       fcx=sx;fcy=sy;
       o->change_bounds(o, box);
      }
    }
  }
 }
 return box;
};

t_rect  view_get_limits ( p_view o )
{
  p_view owner = VIEW(OBJECT(o)->owner_view(OBJECT(o)));
  if ( !owner ) return o->bounds;
  if ( OBJECT(o)->is_options(OBJECT(o), VW_OF_IGNORELIM) )
    return owner->get_local_extent(owner);
  return owner->size_limits(owner);
};


t_point  view_size_minimum ( p_view o )
{
  return point_assign(1, 1);
};


void    view_for_each_sub_view_set_state ( p_view o, l_dword st, l_bool set )
{
  o = VIEW(OBJECT(o)->last_view(OBJECT(o)));
  while ( o ) {
    OBJECT(o)->set_state(OBJECT(o), st, set);
    o = VIEW(OBJECT(o)->prev_view_to_first(OBJECT(o)));
  };
};


void    view_lock_drawing ( p_view o )
{
  o->draw_lock++;
};


l_bool  view_unlock_drawing ( p_view o )
{
  o->draw_lock--;
  if ( o->draw_lock <= 0 ) {
    o->draw_lock = 0;
  };
  return o->is_draw_lock(o);
};

l_bool  view_is_draw_lock ( p_view o )
{
  while ( o ) {
    if ( o->draw_lock ) return true;
    o = VIEW(OBJECT(o)->owner_view(OBJECT(o)));
  };
  return false;
};

void   view_draw_overlays ( p_view o )
{
  p_view owner = VIEW(OBJECT(o)->owner_view(OBJECT(o)));
  p_view p;
  l_long nobj;
  l_long safe;
  l_dword *states;
  if ( !owner ) return;
  p = VIEW(OBJECT(owner)->first_view(OBJECT(owner)));
  nobj = OBJECT(owner)->index_of(OBJECT(owner), OBJECT(o));
  safe = nobj;
  states = (l_dword*)_calloc(nobj, sizeof(l_dword));
  if ( !states ) return;
  while ( (p != o) && nobj ) {
    nobj--;
    states[nobj] = OBJECT(p)->state;
    OBJECT(p)->state &= ~OB_SF_VISIBLE;
    p = VIEW(OBJECT(p)->next_view(OBJECT(p)));
  };
  p = VIEW(OBJECT(owner)->first_view(OBJECT(owner)));
  while ( p != o ) {
    t_rect r = rect_cliped(o->bounds, p->bounds);
    if ( !rect_check_empty(r) ) {
      t_rect safeclip = o->clip;
      o->clip = rect_move(r, -o->bounds.a.x, -o->bounds.a.y);
      o->draw_me(o);
      o->clip = safeclip;
    };
    p = VIEW(OBJECT(p)->next_view(OBJECT(p)));
  };
  p = VIEW(OBJECT(owner)->first_view(OBJECT(owner)));
  nobj = safe;
  while ( (p != o) && nobj ) {
    t_rect r = rect_cliped(o->bounds, p->bounds);
    nobj--;
    if ( !rect_check_empty(r) ) {
      t_rect safeclip = o->clip;
      o->clip = rect_move(r, -o->bounds.a.x, -o->bounds.a.y);
      o->draw_sub_views(o, VIEW(OBJECT(o)->first_view(OBJECT(o))), VIEW(OBJECT(o)->first_view(OBJECT(o))));
      o->clip = safeclip;
    };
    OBJECT(p)->state = states[nobj];
    p = VIEW(OBJECT(p)->next_view(OBJECT(p)));
  };
  _cfree(states);
};

void  view_draw ( p_view o )
{
  t_rect  r = o->get_local_extent(o);
  t_point p;
  BITMAP *out = o->begin_paint(o, &p, r);
  if ( out )
    o->background(o, out, rect_move(r, p.x, p.y));
  o->end_of_paint(o, r);
};

void  view_draw_me ( p_view o )
{
  l_bool dwm = o->is_draw_mode(o, DWM_TESTSUBVIEWS);
  o->set_draw_mode(o, DWM_TESTSUBVIEWS, true);
  o->draw(o);
  o->set_draw_mode(o, DWM_TESTSUBVIEWS, dwm);
};

void  view_draw_view ( p_view o )
{
  if ( OBJECT(o)->is_state(OBJECT(o), OB_SF_VISIBLE) ) {
    o->draw(o);
    o->draw_sub_views(o, VIEW(OBJECT(o)->first_view(OBJECT(o))), VIEW(OBJECT(o)->first_view(OBJECT(o))));
  };
};

void  view_draw_sub_views ( p_view o, p_view from, p_view to )
{
   p_view s = from;
   p_view f = VIEW(OBJECT(o)->first_view(OBJECT(o)));
   if ( !f ) return;
   if ( !to ) to = from;
   if ( from && OBJECT(o)->is_state(OBJECT(o), OB_SF_VISIBLE) ) {
     do {   /* draw only owner views */
       if ( rect_overlay(o->clip, from->bounds) )
          from->draw(from);
       from = VIEW(OBJECT(from)->next_view(OBJECT(from)));
     } while ( (from != to) && (from != f) );
     from = s;
     do {   /* draw only sub-views */
       from->draw_sub_views(from, VIEW(OBJECT(from)->first_view(OBJECT(from))),
                                  VIEW(OBJECT(from)->first_view(OBJECT(from))));
       from = VIEW(OBJECT(from)->next_view(OBJECT(from)));
     } while ( (from != to) && (from != f) );
   };
};

void    view_draw_under_rect ( p_view o, t_rect r )
{
  p_view own = VIEW(OBJECT(o)->owner_view(OBJECT(o)));
  if ( own && OBJECT(own)->is_state(OBJECT(own), OB_SF_VISIBLE) ) {
    p_view f = VIEW(OBJECT(o)->next_view(OBJECT(o)));
    l_dword dwm = own->is_draw_mode(own, DWM_TESTSUBVIEWS);
    t_rect  olc = own->clip;
    /* if not "o" is last view */
    if ( f != VIEW(OBJECT(own)->first_view(OBJECT(own))) ) {
      own->clip = r;
      own->draw_sub_views(own, f, VIEW(OBJECT(own)->first_view(OBJECT(own))));
      own->clip = olc;
    };
    own->set_draw_mode(own, DWM_TESTSUBVIEWS, true);
    own->begin_paint(own, NULL, r);
    own->draw(own);
    own->end_of_paint(own, r);
    own->set_draw_mode(own, DWM_TESTSUBVIEWS, dwm);
  };
};


void    view_draw_under_view ( p_view o )
{
  o->draw_under_rect(o, o->bounds);
};


void    view_draw_in_rect ( p_view o, t_rect r )
{
  o->clip = r;
  o->draw_view(o);
  o->clip = o->get_local_extent(o);
};


l_bool  view_draw_mini_box ( p_view o, BITMAP *out, BITMAP *buffer, t_rect rwhere )
{
  t_view *p = o;
  l_long  rnum = 1;
  t_rect* rout = NULL;
  t_rect  clip = o->get_cliped_extent(o);
  if ( !rect_check_empty(clip) && OBJECT(o)->is_state(OBJECT(o), OB_SF_VISIBLE) && out ) { 
    /* object is visible and output bitmap is set */
    t_rect* r = o->get_not_overlays ( o, rwhere, rect_move(rwhere, o->bounds.a.x, o->bounds.a.y),
                                      o, p, &rnum, &rout, o->is_draw_mode(o, DWM_TESTSUBVIEWS), false);
    if ( r ) { /* rect exist */
      t_point globe = o->get_global_point(o, point_assign(0, 0));
      while ( !rect_check_empty(*r) ) {
        t_rect d = rect_move(*r, globe.x, globe.y);
        o->set_clips(o, *r);
 scare_mouse_area(d.a.x, d.a.y, rect_sizex(d)+1, rect_sizey(d)+1);
        blit(buffer, out, d.a.x, d.a.y, d.a.x, d.a.y, rect_sizex(d)+1, rect_sizey(d)+1);
 unscare_mouse();
        o->reset_clips(o);
        r++;
      };
      _free(rout);
      return true;
    };
  };
  return false;
};

l_color view_get_color ( p_view o, l_int cpos )
{
  l_color  *color = o->palette;
  if ( color ) return color[cpos];
  return 0;
};

l_bool view_is_top_view ( p_view o )
{
  p_view p = o;
  p_view t = NULL;
  while ( p ) {
    t = VIEW(OBJECT(p)->owner_view(OBJECT(p)));
    if ( t && (OBJECT(t)->first_view(OBJECT(t)) != OBJECT(p)) ) return false;
    p = VIEW(OBJECT(p)->owner_view(OBJECT(p)));
  };
  return true;
};

void  view_move_accel ( p_view o, t_rect from, t_rect to )
{
  p_view p = VIEW(OBJECT(o)->owner_view(OBJECT(o)));
  t_rect clip;
  t_rect xfrom;
  t_rect xto;
  l_int  x;
  l_int  y;
  l_int  nx;
  l_int  ny;
  l_int  i = 0;
  t_rect  d;
  t_point pt;
  if ( !p ) return;
  clip = rect_cliped(p->get_cliped_extent(p), o->get_limits(o));
  xfrom = rect_cliped(from, clip);
  xto = rect_cliped(to, clip);
  x  = (from.a.x - to.a.x)+1;
  y  = (from.a.y - to.a.y)+1;
  nx = (from.a.x > to.a.x)?1:0;
  ny = (from.a.y > to.a.y)?1:0;
  xfrom = rect_move(xfrom, ((xfrom.a.x > xto.a.x) ? -x : (to.b.x - from.b.x)-nx), 0);
  xfrom = rect_move(xfrom, 0, ((xfrom.a.y > xto.a.y) ? -y : (to.b.y - from.b.y)-ny));
  p->set_clips(p, clip);
  pt = p->get_global_point(p, point_assign(0, 0));
  /* blit function */
  d = rect_assign(pt.x+to.a.x, pt.y+to.a.y, pt.x+to.b.x, pt.y+to.b.y);
  i = mouse->block(mouse, d);
  blit(OUTBUFFER(o->draw_out), OUTBUFFER(o->draw_out), pt.x+from.a.x, pt.y+from.a.y, pt.x+to.a.x, pt.y+to.a.y, rect_sizex(from)+1, rect_sizey(from)+1);
  mouse->unblock(mouse, i);
  p->reset_clips(p);
  rect_double_overlay(&xfrom, &xto);
  if ( !rect_check_empty(xfrom) ) {
    xfrom = rect_move(xfrom, -o->bounds.a.x, -o->bounds.a.y);
    o->draw_in_rect(o, xfrom);
  };
  if ( !rect_check_empty(xto) ) {
    xto = rect_move(xto, -o->bounds.a.x, -o->bounds.a.y);
    o->draw_in_rect(o, xto);
  };
};

void  view_move_view ( p_view o, t_rect r )
{
  t_rect safebounds = o->bounds;
  if ( !rect_equals(r, safebounds) &&
        (rect_sizex(r) == rect_sizex(safebounds)) &&
        (rect_sizey(r) == rect_sizey(safebounds)) ) {
      o->set_bounds(o, r);
      o->draw_view(o);
      o->draw_under_rect(o, safebounds);
    };
};

void  view_grow_view ( p_view o, t_rect r )
{
  p_object p = OBJECT(o)->first_view(OBJECT(o));
  t_rect safebounds = o->bounds;
  o->set_bounds(o, r);
  o->draw(o);
  if ( p )
    do {
         p_view v = VIEW(p);
         t_rect r = v->bounds;
         l_rect dx = rect_sizex(o->bounds) - rect_sizex(safebounds);
         l_rect dy = rect_sizey(o->bounds) - rect_sizey(safebounds);
         if ( v->align & TX_ALIGN_RIGHT ) {
               if ( v->align & TX_ALIGN_FIXEDX )  r.a.x += dx;
               r.b.x += dx;
         };
         if ( v->align & TX_ALIGN_BOTTOM )  {
               if ( v->align & TX_ALIGN_FIXEDY )  r.a.y += dy;
               r.b.y += dy;
         };
         if ( !rect_equals(r, v->bounds) )
               v->change_bounds(v, r);
         else
               v->draw_view(v);
         p = p->next_view_to_last(p);
    } while ( p );
  o->draw_under_rect(o, safebounds);
};

void    view_set_palette ( p_view o, l_int *palette )
{
  /* each object has own palette */
  #ifdef __EACH_OWN_PALETTE__
  l_int i = 1;
  if ( o->palette ) _free(o->palette);
  if ( palette ) {
      /* get num of colors */
      while ( CO_ISCOLOR(*pallette) ) i++;
      /* allocate mem and copy palette to destination */
      o->palette = copy_type(palette, i*sizeof(l_int));
  } else o->palette = NULL;
  #else
  /* each object has global palette */
      o->palette = palette;
  #endif
};

BITMAP* view_begin_paint ( p_view o, t_point *p, t_rect r )
{
  t_point t = point_assign(0, 0);
  l_bool  is_screen_out = o->is_draw_mode(o, DWM_ONLYTOSCREEN);
  if ( p ) *p = o->get_global_point(o, t);

  /* fifo safe_clip => ptr to each clip */
  o->safe_clip = fifo_add_rect(o->safe_clip, &(o->clip), o->draw_lock);

  o->clip = rect_cliped(o->clip, rect_cliped(o->get_local_extent(o), r));
  o->set_clips(o, r);
  o->lock_drawing(o);
  if ( is_screen_out )
     return rect_check_empty(o->get_cliped_extent(o))?NULL:OUTBUFFER(o->draw_out);
  else /* when we don't want to write into buffer => only for speed */
    return rect_check_empty(o->get_cliped_extent(o))?NULL:o->draw_buffer;
};

void    view_end_of_paint ( p_view o, t_rect rwhere )
{
  t_rect *t = NULL;
  o->unlock_drawing(o);
  if ( !o->draw_lock ) { /* not locking, so i'm going to draw */
    if ( !o->is_draw_mode(o, DWM_ONLYTOBUFFER) &&
         !o->is_draw_mode(o, DWM_ONLYTOSCREEN) ) {
      o->draw_mini_box(o, OUTBUFFER(o->draw_out), o->draw_buffer, rwhere);
    };
  };
  /* get old clip */
  t = fifo_get_rect(&(o->safe_clip), o->draw_lock);
  if ( t ) {
      o->clip = *t;
      o->set_clips(o, o->clip);
  };
  if ( !o->draw_lock ) /* */
      o->reset_clips(o); /* it's not important to use, but may-be...*/
};

void    view_set_clips ( p_view o, t_rect r )
{
  r = o->get_global_bounds(o, rect_cliped(r, o->get_cliped_extent(o)));
  if ( o->draw_buffer )
    set_clip(o->draw_buffer, r.a.x, r.a.y, r.b.x, r.b.y);
  if ( OUTBUFFER(o->draw_out) )
    set_clip(OUTBUFFER(o->draw_out), r.a.x, r.a.y, r.b.x, r.b.y);
};


void    view_reset_clips ( p_view o )
{
  if ( o->draw_buffer )
    set_clip(o->draw_buffer, rect_empty.a.x, rect_empty.a.y, rect_empty.b.x, rect_empty.b.y);
  if ( OUTBUFFER(o->draw_out) )
    set_clip(OUTBUFFER(o->draw_out), rect_empty.a.x, rect_empty.a.y, rect_empty.b.x, rect_empty.b.y);
};


t_rect  view_get_local_extent ( p_view o )
{
  t_rect r = rect_assign(0, 0, rect_sizex(o->bounds), rect_sizey(o->bounds));
  return r;
};

t_rect  view_get_global_bounds ( p_view o, t_rect r )
{
  while ( o ) {
    r = rect_move(r, o->bounds.a.x, o->bounds.a.y);
    o = (p_view)(((p_object)o)->owner_view((p_object)o));
  };
  return r;
};

t_rect  view_get_local_bounds ( p_view o, t_rect r )
{
  t_rect dest = o->get_local_extent(o);
  dest = o->get_global_bounds(o, dest);
  dest.a.x = r.a.x - dest.a.x;
  dest.a.y = r.a.y - dest.a.y;
  dest.b.x = dest.a.x + rect_sizex(r);
  dest.b.y = dest.a.y + rect_sizey(r);
  return dest;
};

t_point view_get_global_point ( p_view o, t_point p )
{
  t_rect  r = o->get_global_bounds(o, rect_assign(p.x, p.y, p.x, p.y));
  return r.a;
};

t_point view_get_local_point ( p_view o, t_point p )
{
  t_rect r = o->get_local_bounds(o, rect_assign(p.x, p.y, p.x, p.y));
  return r.a;
};

t_rect* view_get_not_overlays ( p_view o, t_rect rlocal, t_rect rgroup,
                                p_view p, p_view target, l_long *rnum,
                                t_rect **rout, l_bool sub, l_bool ws )
{
  t_rect box = rect_empty;
  t_rect start_box;
  t_rect safe;
  __1:
  if ( p ) p = VIEW(OBJECT(p)->prev_view_to_first(OBJECT(p)));
  __2:
  start_box = rgroup;
  safe = rgroup;
  while ( p ) {
    t_rect  r = p->bounds;
    l_dword state = OBJECT(p)->state;
    /* if p is subview & not ignore owner's limits */
    if ( ws && !OBJECT(p)->is_options(OBJECT(p), VW_OF_IGNORELIM) )
         r = rect_cliped(o->size_limits(o), r);
    if ( !(state & OB_SF_VISIBLE) ||/* !(state & OB_SF_EXPOSED) ||*/
         rgroup.b.x < r.a.x || rgroup.b.y < r.a.y ||
         rgroup.a.x > r.b.x || rgroup.a.y > r.b.y )
         p = VIEW(OBJECT(p)->prev_view_to_first(OBJECT(p)));
    else {
      box.a.x = max(r.a.x, rgroup.a.x);
      box.a.y = max(r.a.y, rgroup.a.y);
      box.b.x = min(r.b.x, rgroup.b.x);
      box.b.y = min(r.b.y, rgroup.b.y);
      break;
    };
  };
  if ( !rect_check_empty(box) ) {
    if ((box.a.y > rgroup.a.y) && (box.a.y <= rgroup.b.y) ) {
      rgroup.b.y = box.a.y-1; safe.a.y = box.a.y;
      (*rout) = o->get_not_overlays(o, rgroup, rgroup, p, target, rnum, rout, sub, ws);
      rgroup = start_box;
    };
    if ( (box.b.y < rgroup.b.y) && (box.b.y >= rgroup.a.y) ) {
      rgroup.a.y = box.b.y+1; safe.b.y = box.b.y;
      (*rout) = o->get_not_overlays(o, rgroup, rgroup, p, target, rnum, rout, sub, ws);
      rgroup = start_box;
    };
    if ( (box.a.x > rgroup.a.x) && (box.a.x <= rgroup.b.x) ) {
      rgroup.a.y = safe.a.y; rgroup.b.y = safe.b.y;
      rgroup.b.x = box.a.x-1;
      (*rout) = o->get_not_overlays(o, rgroup, rgroup, p, target, rnum, rout, sub, ws);
      rgroup = start_box;
    };
    if ( (box.b.x < rgroup.b.x) && (box.b.x >= rgroup.a.x) ) {
      rgroup.a.x = box.b.x+1;
      rgroup.a.y = safe.a.y; rgroup.b.y = safe.b.y;
      (*rout) = o->get_not_overlays(o, rgroup, rgroup, p, target, rnum, rout, sub, ws);
      rgroup = start_box;
    };
  } else {
    if ( target ) {
      target = VIEW(OBJECT(target)->owner_view(OBJECT(target)));
      p = target;
      if ( p )
        rgroup = rect_move(rgroup, p->bounds.a.x, p->bounds.a.y);
      goto __1;
    } else if ( !sub ) {
        (*rnum)++;
        if ( rout ) {
          (*rout) = (t_rect*)_realloc((*rout), (*rnum)*sizeof(t_rect));
          if (*rout) {
            (*rout)[(*rnum)-2] = ws?rgroup:o->get_local_bounds(o, rgroup);
            (*rout)[(*rnum)-1] = rect_empty;
          };
          return (*rout);
        } else return NULL;
      } else {
        p = VIEW(OBJECT(o)->last_view(OBJECT(o)));
        rgroup = o->get_local_bounds(o, rgroup);
        ws = true; /* to be able to know that p(s) are subviews */
        sub = false;
        goto __2; /* can't move p to prev position */
      };
  };
  return (*rout);
};


void    view_set_draw_mode ( p_view o, l_int dm, l_bool set )
{
  if ( set )
    o->draw_mode |= dm;
  else
    o->draw_mode &= ~dm;
};


l_bool  view_is_draw_mode ( p_view o, l_int dm )
{
  return (l_bool)(o->draw_mode & dm);
};


void    view_show ( p_view o )
{
  if ( !OBJECT(o)->is_state(OBJECT(o), OB_SF_VISIBLE) )
    OBJECT(o)->set_state(OBJECT(o), OB_SF_VISIBLE, true);
};

void    view_hide ( p_view o )
{
  if ( OBJECT(o)->is_state(OBJECT(o), OB_SF_VISIBLE) )
    OBJECT(o)->set_state(OBJECT(o), OB_SF_VISIBLE, false);
};

t_rect  view_get_cliped_extent ( p_view o )
{
  t_rect r = o->clip;
  p_view s = o;
  while ( o ) {
    r = rect_cliped(r, o->get_local_extent(o));
    if ( !rect_check_empty(r) ) r = rect_cliped(r, o->clip);
    else return rect_empty;
    if ( !rect_check_empty(r) ) {
      r = rect_move(r, o->bounds.a.x, o->bounds.a.y);
      r = rect_cliped(r, o->get_limits(o));
    } else return rect_empty;
    if ( rect_check_empty(r) ) return rect_empty;
    o = VIEW(OBJECT(o)->owner_view(OBJECT(o)));
  };
  return s->get_local_bounds(s, r);
};


t_rect  view_size_limits ( p_view o )
{
  return o->get_local_extent(o);
};


void    view_set_bounds ( p_view o, t_rect r )
{
  t_point s = o->size_minimum(o);
  o->bounds = rect_assign(r.a.x, r.a.y, lmax(r.b.x, r.a.x+s.x), lmax(r.b.y, r.a.y+s.y));
  o->clip = o->get_local_extent(o);
};


void    view_change_bounds ( p_view o, t_rect nr )
{
  t_rect oldb = o->bounds;
  /* maybe mimimum size is yet and we make also mimimum, so try it */
  o->set_bounds(o, nr);
  nr = o->bounds;
  o->set_bounds(o, oldb);
  if ( !rect_equals(o->bounds, nr) ) {  /* old rect and new one are not same */
    if ( (rect_sizex(nr) == rect_sizex(o->bounds)) &&
         (rect_sizey(nr) == rect_sizey(o->bounds)) ) {
      o->move_view(o, nr);
    } else {
      o->grow_view(o, nr);
    };
  };
};


void    view_set_clip ( p_view o, BITMAP *out, t_rect r )
{
  r = o->get_global_bounds(o, rect_cliped(r, o->get_cliped_extent(o)));
  if ( out )
    set_clip(out, r.a.x, r.a.y, r.b.x, r.b.y);
};


void    view_background ( p_view o, BITMAP *out, t_rect r )
{
  if ( o->brush.background ) {
    if ( o->brush.state & BRUSH_STRETCH )
      stretch_sprite(out, o->brush.background, 0, 0, rect_sizex(r)+1, rect_sizey(r)+1);
    else
    if ( o->brush.state & BRUSH_CENTER ) {
      l_rect dx = (rect_sizex(r)-IMAGE_WIDTH(o->brush.background))/2;
      l_rect dy = (rect_sizey(r)-IMAGE_HEIGHT(o->brush.background))/2;
      rectfill(out, r.a.x, r.a.y, r.b.x, r.b.y, o->brush.color);
      draw_sprite(out, o->brush.background, r.a.x+dx, r.a.y+dy);
    }
    else
      blit_ex(o->brush.background, out, 0, 0, r.a.x, r.a.y,
              IMAGE_WIDTH(o->brush.background), IMAGE_HEIGHT(o->brush.background),
              rect_sizex(r)+1, rect_sizey(r)+1);
  } else {
    if (  (o->brush.color == o->brush.color2) ||
        !(o->brush.state & BRUSH_GRADIENT) )
       rectfill(out, r.a.x, r.a.y, r.b.x, r.b.y, o->brush.color);
    else
       if ( o->brush.state & BRUSH_GRADIENT_HOR )
          fade_rect(out, r.a.x, r.a.y, r.b.x, r.b.y, o->brush.color, o->brush.color2, FR_HOR);
       else
          fade_rect(out, r.a.x, r.a.y, r.b.x, r.b.y, o->brush.color, o->brush.color2, FR_VER);
  };
};


/* using jdh extensions addition instead to support BadSectors additions */
typedef struct vers2 {
  BITMAP           *icon16;
  BITMAP           *icon32;
  t_rect           orig_bounds;
  char * name;
} vers2;


p_view  _view_init ( p_view o, t_rect r )
{
  if ( !o ) return NULL;
  clear_type(o, sizeof(t_view));
  obj_init(&o->obclass);
  /* object's functions */
  OBJECT(o)->reset_prefer = &view_reset_prefer;
  OBJECT(o)->setup = &view_setup;
  OBJECT(o)->after_init = &view_after_init;
  OBJECT(o)->set_state = &view_set_state;
  OBJECT(o)->put_in_front_of = &view_put_in_front_of;
  OBJECT(o)->done = &view_done;
  OBJECT(o)->translate_event = &view_translate_event;
  OBJECT(o)->play_process = &view_play_process;
  /* view's functions */
  o->draw_out = &screen;
  o->draw_buffer = screen_virtual;
  o->font = font_system;
  o->cursor = CUR_ARROW;
  o->palette = NULL;
  o->reset_align = &view_reset_align;
  o->execute_view = &view_execute_view;
  o->get_top_view_under_mouse = &view_get_top_view_under_mouse;
  o->top_view = &view_top_view;
  o->show_info_board = &view_show_info_board;
  o->set_mouse_cursor = &view_set_mouse_cursor;
  o->is_mouse_in_view = &view_is_mouse_in_view;
  o->drag_data = &view_drag_data;
  o->drag_where = &view_drag_where;
  o->drop_data = &view_drop_data;
  o->drag_view = &view_drag_view;
  o->drag = &view_drag;
  o->size_minimum = &view_size_minimum;
  o->get_limits = &view_get_limits;
  o->draw = &view_draw;
  o->draw_me = &view_draw_me;
  o->draw_view = &view_draw_view;
  o->draw_sub_views = &view_draw_sub_views;
  o->get_color = &view_get_color;
  o->set_palette = &view_set_palette;
  o->is_top_view = &view_is_top_view;
  o->move_accel = &view_move_accel;
  o->move_view = &view_move_view;
  o->grow_view = &view_grow_view;
  o->get_view_under_mouse = &view_get_view_under_mouse;
  o->lock_drawing = &view_lock_drawing;
  o->unlock_drawing = &view_unlock_drawing;
  o->is_draw_lock = &view_is_draw_lock;
  o->begin_paint = &view_begin_paint;
  o->end_of_paint = &view_end_of_paint;
  o->draw_overlays = &view_draw_overlays;
  o->draw_mini_box = &view_draw_mini_box;
  o->draw_under_view = &view_draw_under_view;
  o->draw_under_rect = &view_draw_under_rect;
  o->draw_in_rect = &view_draw_in_rect;
  o->get_local_extent = &view_get_local_extent;
  o->get_global_bounds = &view_get_global_bounds;
  o->get_local_bounds = &view_get_local_bounds;
  o->get_global_point = &view_get_global_point;
  o->get_local_point = &view_get_local_point;
  o->get_not_overlays = &view_get_not_overlays;
  o->show = &view_show;
  o->hide = &view_hide;
  o->for_each_sub_view_set_state = &view_for_each_sub_view_set_state;
  o->set_draw_mode = &view_set_draw_mode;
  o->is_draw_mode = &view_is_draw_mode;
  o->size_limits = &view_size_limits;
  o->get_cliped_extent = &view_get_cliped_extent;
  o->set_bounds = &view_set_bounds;
  o->change_bounds = &view_change_bounds;
  o->set_clip = &view_set_clip;
  o->background = &view_background;
  o->set_clips = &view_set_clips;
  o->reset_clips = &view_reset_clips;
  o->icon_size = system_item_size;
  /* functions running & variable's declarations */
  OBJECT(o)->set_options(OBJECT(o), VW_OF_VISIBILITY+OB_OF_SELECTABLE, true);
  o->set_bounds(o, r);
  o->clip = o->get_local_extent(o);
  o->brush.color = COLOR(CO_BLACK);
  o->brush.skin = NULL;
  OBJECT(o)->tag |= TAG_VIEW;
  return o;
};


/*#include"window.c"*/
/****************************************************************/
/*                                                              */
/*              window.c - Basic window functions               */
/*                                                              */
/*      Bad Seal 0.54 (c) 2000,2001 Kostas Michalopoulos        */
/*      e-mail: michalopoylos@hotmail.com                       */
/*      www: http://www.badseal.org/                            */
/*                                                              */
/****************************************************************/
/*                                                              */
/*                           window.c                           */
/*                                                              */
/*                Seal - free desktop environment               */
/*                                                              */
/*                    Copyright (c) 1999,2000                   */
/*                         Michal Stencl                        */
/*                     All Rights Reserved                      */
/*                                                              */
/* mail : stenclpmd@ba.telecom.sk                               */
/* web  : http://www.home.sk/public/seal/                       */
/*                                                              */
/* This file is part of Seal.                                   */
/*                                                              */
/* Seal is free software; you can redistribute it and/or        */
/* modify it under the terms of the GNU General Public License  */
/* as published by the Free Software Foundation; either version */
/* 2, or (at your option) any later version.                    */
/*                                                              */
/* Seal is distributed in the hope that it will be useful, but  */
/* WITHOUT ANY WARRANTY; without even the implied warranty of   */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See    */
/* the GNU General Public License for more details.             */
/*                                                              */
/* You should have received a copy of the GNU General Public    */
/* License along with Seal; see the file COPYING.  If not,      */
/* write to the Free Software Foundation, 675 Mass Ave,         */
/* Cambridge, MA 02139, USA.                                    */
/*                                                              */
/****************************************************************/

#include<string.h>
#include"window.h"
#include"safmem.h"

/*
   palette of window object :
   --------------------------
   standard colors from Seal.ini file, from section [colors] :
   index of palette        name in [colors]
   0                       3d face
   1                       3d light
   2                       3d shadow
   3                       3d dark
   4                       active title
   5                       active title gradient
   6                       active title text
   7                       pasive title
   8                       pasive title gradient
   9                       passive title text
*/
l_color   pal_window[] = 
// jdh reset this back to use something (nasty) that at least works in 8 bits if no config file
  {CO_SYSx20,    CO_WHITE, CO_SYSx2,    CO_SYSx5, CO_RED,   CO_SYSx8, CO_SYSx19, CO_BLACK,     CO_WHITE, CO_SYSx21, CO_NOCOLOR};

p_window  (*win_init) ( p_window o, t_rect r, l_text caption, l_int flags ) = &_win_init;
/*
  this function is used as an equivalent for "l_bool t_object.done(t_object *)" see object.h.
  win_init set the t_object.done to this form : OBJECT(win)->done = &win_done;
  Calls view_done function and free memory of the t_window.caption.
*/
l_bool  win_done ( p_object o )
{
  if ( !view_done(o) ) return false;
  /* free memory of caption string and set the pointer to ZERO */
  if(WINDOW(o)->caption) {_free(WINDOW(o)->caption);WINDOW(o)->caption=NULL;}
  return true;
};


/*
  an equivalent for "void t_object.set_state(t_object *, l_dword, l_dword )" see object.h.
  Calls view_set_state function and redraw caption when window is selected or deselected.
*/
void  win_set_state ( p_object o, l_dword st, l_bool set )
{
  view_set_state(o, st, set);
  if ( st & OB_SF_SELECTED ) {
    /* draw title if windows is selected/deselected.
       also test sub-views in the title. ! not redrawn them !
    */
    TEST_SUB_VIEWS(o, WINDOW(o)->draw_title(WINDOW(o)));
  };
};


/*
  an equivalent for "t_rect t_view.size_limits(t_view * )" see view.h.
  Only return area for subviews. NOTE: if variable t_view.options is set to flag
  VW_OF_INGORELIM, this area is not accepted. For X button, for example.
*/
t_rect  win_size_limits ( p_view o )
{
  /* r = { 0, 0, width of object, height of object }*/
  t_rect r = o->get_local_extent(o);
  r.a.x += 2;
  r.a.y += FONT_GETHEIGHT(VIEW(o)->font)+4; /* place for title */
  r.b.x -= 2;
  r.b.y -= 2;
  return r;
};


/*
  an equivalent for "t_rect t_view.size_minimum(t_view * )" see view.h.
  return minimal sizes for the t_window. Default values are 220, 50. Of-course
  you can change it by your own function for the object :
  static t_point my_size_minumum ( p_view o ) { return point_assign(400, 300); };
  VIEW(win)->size_minumum = &my_size_minimum;
*/
t_point   win_size_minimum ( p_view o )
{
  return point_assign(220, 50);
};

/*
  an equivalent for "void t_view.draw(t_view * )" see view.h.
  main drawing function of the window. The standard form of this function will find
  in file view.h.
*/
void  win_draw ( p_view o )
{
  /* r = { 0, 0, width of object, height of object }*/
  t_rect  r = o->get_local_extent(o);
  t_point p;
  /* start to draw in rect (r) and return delta (p.x,p.y) from 0,0 of the screen
     return output for the drawing ( default : t_view.draw_buffer - virtual_screen ).
     When last end_of_paint is called, it's drawn to t_view.draw_out ( screen )
  */
  BITMAP *out = o->begin_paint(o, &p, r);
  if ( out ) {
    /* draw background by the brush.color, or brush.background image, if is set ...
       see "view.h" t_view.background(t_view *, BITMAP*, t_rect)
    */
//    o->background(o, out, rect_move(r, p.x, p.y));
    rectfill(out, r.a.x+p.x, r.a.y+p.y, r.b.x+p.x, r.b.y+p.y, o->get_color(o, 0));
    /* draw BLACK rectangle */
//    rect(out, r.a.x+p.x, r.a.y+p.y, r.b.x+p.x, r.b.y+p.y, COLOR(CO_BLACK));
    /* draw 3D object - efect as button */
    button(out, r.a.x+p.x, r.a.y+p.y, r.b.x+p.x, r.b.y+p.y, o->get_color(o, 0),
                                                            o->get_color(o, 3));
    button(out, r.a.x+p.x+1, r.a.y+p.y+1, r.b.x+p.x-1, r.b.y+p.y-1, o->get_color(o, 1),
                                                            o->get_color(o, 2));
    /* window is growable ? */
    if ( VIEW(o)->drag_mode & DM_DRAGGROW ) {
         /* draw two WHITE lines on the left,bottom corner */
//         hline(out, p.x+r.b.x - 13, p.y+r.b.y - 2, p.x+r.b.x - 2, COLOR(CO_WHITE));
//         vline(out, p.x+r.b.x - 2, p.y+r.b.y - 13, p.y+r.b.y - 2, COLOR(CO_WHITE));
         line(out, p.x+r.b.x-2, p.y+r.b.y-2, p.x+r.b.x-2, p.y+r.b.y-2, o->get_color(o, 1));
         line(out, p.x+r.b.x-3, p.y+r.b.y-2, p.x+r.b.x-2, p.y+r.b.y-3, o->get_color(o, 2));
         line(out, p.x+r.b.x-6, p.y+r.b.y-2, p.x+r.b.x-2, p.y+r.b.y-6, o->get_color(o, 1));
         line(out, p.x+r.b.x-7, p.y+r.b.y-2, p.x+r.b.x-2, p.y+r.b.y-7, o->get_color(o, 2));
         line(out, p.x+r.b.x-10, p.y+r.b.y-2, p.x+r.b.x-2, p.y+r.b.y-10, o->get_color(o, 1));
         line(out, p.x+r.b.x-11, p.y+r.b.y-2, p.x+r.b.x-2, p.y+r.b.y-11, o->get_color(o, 2));
    };
    /* draw title of the window */
    WINDOW(o)->draw_title(WINDOW(o));
  };
  /* end of drawing. if this is the last call of end_of_paint, it draws context to
     t_view.draw_out, from (out)... by default t_view.draw_buffer.
  */
  o->end_of_paint(o, r);
};


/*
  an equivalent for "void t_view.set_mouse_cursor ( t_view * )" see view.h.
  If mouse cursor is placed in the left,bottom corner and the t_view.drag_mode is
  set to flag DM_DRAGGROW, this set mouse cursor to CUR_GROW.
*/
void      win_set_mouse_cursor ( p_view o )
{
   /* make mouse->where to local bounds */
   t_point m = VIEW(o)->get_local_point ( VIEW(o), mouse->where );
   /* r = { 0, 0, width of the view, height of the view } */
   t_rect  r = VIEW(o)->get_local_extent ( VIEW(o) );
   /* possible to grow, and mouse is on the left,bottom corner */
   if (  o->drag_mode & DM_DRAGGROW && (m.x > (r.b.x - 10)) && (m.x < r.b.x) &&
         (m.y > (r.b.y - 10)) && (m.y < r.b.y) )
      /* set mouse cursor to CUR_GROW */
      mouse_set_cursor_focus_id(CUR_GROW);
   else
      /* set mouse cursor to t_view.cursor - CUR_ARROW */
      view_set_mouse_cursor(o);
};


/*
  an equivalent for "void t_object.translate_event ( t_object *, t_event *)" see object.h.
  this function is called, whenever some event occurs. It calls function
  view_translate_event, and then are controled events for moving, closing, or
  switching between views.
*/
void      win_translate_event ( p_object o, t_event *event )
{
  /* return back, if window is not visible or mouse is not in the object and
     event comes from the mouse
  */
  RETVIEW(o, event);
  /* event comes from mouse - see mouse.h */
  if ( event->type & EV_MOUSE ) { /* mouse event */
    /* left mouse button was pressed */
    if ( OBJECT(mouse)->state & MO_SF_MOUSELDOWN ) { /* select */
      /* select window */
      o->select(o); /* if it was selected before, make nothing */
    };
    /* left mouse button was pressed */
    if ( OBJECT(mouse)->state & MO_SF_MOUSELDOWN ) { /* growing */
        t_point m = VIEW(o)->get_local_point ( VIEW(o), mouse->where );
        t_rect  r = VIEW(o)->get_local_extent ( VIEW(o) );
        /* check if mouse is in left, bottom corner */
        if (  (m.x > (r.b.x - 10)) && (m.x < r.b.x) && (m.y > (r.b.y - 10)) && (m.y < r.b.y) )
        /* and if isn't maximized (if FLAG WF_MAXSIZE (0x08) is set then window is maximized) */
        if (!(WINDOW(o)->flags & WF_MAXSIZE /*0x08*/)) {
             /* ok, grow object */
             VIEW(o)->drag_view(VIEW(o), DM_DRAGGROW, event);
             /* clear event, now event->type is EV_NOTHING, etc... */
             clear_event(event);
        };
    };
  };
  /* calls other objects placed in the window */
  view_translate_event(o, event); /* old translate_event function */
  if (( event->type & EV_MOUSE) && (!(WINDOW(o)->flags & WF_MAXSIZE /*0x08*/))) { /* 0x08 is WF_MAXSIZE */
    /* left mouse button was pressed */
    if ( OBJECT(mouse)->state & MO_SF_MOUSELDOWN ) { /* dragging */
      t_point m = VIEW(o)->get_local_point ( VIEW(o), mouse->where );
      t_rect  r = VIEW(o)->get_local_extent ( VIEW(o) );
      t_rect  s = VIEW(o)->size_limits(VIEW(o));
      /* check if mouse is in the caption */
      if (  (m.x > (r.a.x + 2)) && (m.x < (r.b.x -2)) && (m.y > (r.a.y + 2)) && (m.y < (r.a.y + s.a.y -2)) )
      {
           /* move window */
           VIEW(o)->drag_view(VIEW(o), DM_DRAGMOVE, event);
           /* clear event, now event->type is EV_NOTHING, etc... */
           clear_event(event);
      };
    };
  };
  /* event comes from keyboard */
  if ( event->type & EV_KEYBOARD ) { /* keyboard event */
    /* ALT+TAB was pressed */
    if ( keyb->code == TO_KEY(KB_TAB) ) { /* select next view */
        p_object v = NULL;
        /* exist preferable object */
        if ( o->prefer )
           /* find next object placed in window, that's selectable and visible */
           v = o->prefer->find_match_view(o->prefer, OB_SF_VISIBLE, OB_OF_SELECTABLE, true);
        /* clear event -> we want only one to make this */
  	   clear_event(event);
        /* select previous found object */
        if ( v ) v->select(v);
    };
    /* ALT+F4 was pressed */
    if ( keyb->code == TO_ALT(KB_F4) ) {
      /* ok.... we are going to close the window */
      set_event(event, EV_MESSAGE, MSG_CLOSE, o);
      /* set program queue to event -> start in the next process */
      o->put_event(o, event);
      /* current process clear */
      clear_event(event);
    };
  };
  if ( event->type & EV_MESSAGE ) { /* message event */
    switch ( event->message ) {
      /* messages MSG_OK, or MSG_CLOSE occured */
      case MSG_OK :
      case MSG_CLOSE : {
        if ( !o->is_state(o, OB_SF_MODAL) )
           /* done (hide) the window and free memory of the window and of all
              subobjects, see "object.h"
           */
           dispose(o);
        else
        /* if object is modal = others wait for ending this one.
           see "object.h" l_dword t_object.execute(t_object *) +
               "view.h" l_dword execute_view(t_view *, t_view *)
        */
           o->end_state = event->message;
        /* clear current process */
        clear_event(event);
      }; break;
    };
  };
};


/*
  draws window title. The default form of drawing will find in file "view.h"
*/
void  win_draw_title ( p_window o )
{
  t_rect  r = rect_assign(2, 2, rect_sizex(VIEW(o)->bounds)-2,
                          FONT_GETHEIGHT(VIEW(o)->font)+2);
  t_point p;
  /* start to draw in rect (r) and return delta (p.x,p.y) from 0,0 of the screen
     return output for the drawing ( default : t_view.draw_buffer - virtual_screen ).
     When last end_of_paint is called, it's drawn to t_view.draw_out ( screen )
  */
  BITMAP *out = VIEW(o)->begin_paint(VIEW(o), &p, r);
    l_color fcolor = VIEW(o)->get_color(VIEW(o), 9);
    l_color bcolor = VIEW(o)->get_color(VIEW(o), 7);
    l_text  caption = set_format_text(NULL, "  %s  ", o->caption);
    l_int dy = (rect_sizey(r) - FONT_GETHEIGHT(VIEW(o)->font)) / 2;
    /* Note there was some code here that did nothing, supposedly setting bcolor if window was selected */
    if(out) {
    rectfill(out, r.a.x+p.x, r.a.y+p.y, r.b.x+p.x, r.b.y+p.y, bcolor);
     /* object is selected ? */
     if ( OBJECT(o)->state & OB_SF_SELECTED )
        /* draw lines CO_WHITE, CO_BLACK, trought the title : MacOS efect */
        lined_rect(out, r.a.x+p.x+5, r.a.y+p.y+dy+3,
                        r.b.x+p.x-5, r.a.y+p.y+dy+FONT_GETHEIGHT(VIEW(o)->font),
                        VIEW(o)->get_color(VIEW(o), 3), VIEW(o)->get_color(VIEW(o), 2));
     /* draw text by the font t_view.font, text (caption), length -1 = strlen, ... you know */
     textout_draw_rect(out, VIEW(o)->font, caption, -1, r.a.x+p.x, r.a.y+p.y+1,
                           r.b.x+p.x, r.b.y+p.y, TX_ALIGN_CENTER, fcolor, bcolor, 0);
    }
    /* free memory last allocated by function set_format_text ...see above */
    _free(caption);
  /* end of drawing. if this is the last call of end_of_paint, it draws context to
     t_view.draw_out, from (out)... by default t_view.draw_buffer.
  */
  VIEW(o)->end_of_paint(VIEW(o), r);
};


/*
  init t_window class and return (o). (o) is previous allocated memory in size
  sizeof(t_window). (r) are bounds of the window ( where is placed ). (caption) is
  the title of the window. Please not use new memory for this title such by the
  function _strdup, because win_init function make it by the self. (flags) are flags of
  the window. These flags are used later ...in objects (t_appwin), what's inherited
  object from object t_window ( see app.h ).
  Example :
  t_rect   r   = rect_assign(100, 100, 300, 300);
  p_window win = win_init(_malloc(sizeof(t_window)), r, "Hello Mr. Stencl", 0);
  OBJECT(desktop)->insert(OBJECT(desktop), OBJECT(win));
  Insert new (win) to the (desktop) ...(for object (desktop) see program.h)
  ...you must use OBJECT(), becouse (insert) function is the function of the class
  t_object, and arguments are : (p_object own, p_object sub)
*/
p_window  _win_init ( p_window o, t_rect r, l_text caption, l_int flags )
{
  if ( !o ) return NULL;
  /* set memory of pointer to ZERO */
  clear_type(o, sizeof(t_window));
  /* call old initialization function */
  view_init(&o->obclass, r);
  /* object's declarations */
  OBJECT(o)->tag |= TAG_WINDOW;
  /* view's declarations */

  VIEW(o)->drag_mode |=  DM_DRAGMOVE;
  /* window's declarations */
  /* allocate memory for (caption) and copy string from (caption) to new place */
  o->caption = (l_text)_strdup((char*)caption);
  o->flags = flags;
  /* object's functions */
  /* set old functions to new */
  OBJECT(o)->set_state = &win_set_state;
  OBJECT(o)->translate_event = &win_translate_event;
  /* view's functions */
  VIEW(o)->draw = &win_draw;
  VIEW(o)->size_limits = &win_size_limits;
  VIEW(o)->size_minimum = &win_size_minimum;
  VIEW(o)->set_mouse_cursor = &win_set_mouse_cursor;
  /* window functions */
  o->draw_title = &win_draw_title;
  /* function calling */
  /* object can be selected on the top */
  OBJECT(o)->set_options(OBJECT(o), OB_OF_TOPSELECT, true);
  /* set palette of the window from standard window palette */
  VIEW(o)->set_palette(VIEW(o), pal_window);
  /* set font of "caption" to bold system font. declaration is in "driver.c" file */
  VIEW(o)->font = font_system_bd;
  /* set background color of window to first palette (pal_window) color.
     this is used when function "t_view.background(t_view*, r)" is used in draw function.
  */
  VIEW(o)->brush.color = VIEW(o)->get_color(VIEW(o), 0);
  return o;
};

/*
   this is called only once, when Seal start. It's called from the function program_init
   and read colors for 
 from the seal.ini file.
*/
void  window_ini_palette ( void )
{
  int i;
  for(i=0; i<10 ; i++)pal_window[i]=COLOR(pal_window[i]);

  iffound(pal_window[0] , registry_color("3d_face"));
  iffound(pal_window[1] , registry_color("3d_light"));
  iffound(pal_window[2] , registry_color("3d_shadow"));
  iffound(pal_window[3] , registry_color("3d_dark"));
  iffound(pal_window[4] , registry_color("window_active_title"));
  iffound(pal_window[5] , registry_color("window_active_title_gradient"));
  iffound(pal_window[6] , registry_color("window_active_caption"));
  iffound(pal_window[7] , registry_color("window_pasive_title"));
  iffound(pal_window[8] , registry_color("window_pasive_title_gradient"));
  iffound(pal_window[9] , registry_color("window_pasive_caption"));
};

#include"program.h"
/*#include"seal.exp"*/ /* export table */
/****************************************************************/
/*                                                              */
/*                           seal.exp                           */
/*                                                              */
/*                Seal - free desktop environment               */
/*                                                              */
/*                  Copyright (c) 1999,2000                     */
/*                       Michal Stencl                          */
/*                    All Rights Reserved                       */
/*                                                              */
/* mail : stenclpmd@ba.telecom.sk                               */
/* web  : http://www.home.sk/public/seal/                       */
/*                                                              */
/* This file is part of Seal.                                   */
/*                                                              */
/* Seal is free software; you can redistribute it and/or        */
/* modify it under the terms of the GNU General Public License  */
/* as published by the Free Software Foundation; either version */
/* 2, or (at your option) any later version.                    */
/*                                                              */
/* Seal is distributed in the hope that it will be useful, but  */
/* WITHOUT ANY WARRANTY; without even the implied warranty of   */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See    */
/* the GNU General Public License for more details.             */
/*                                                              */
/* You should have received a copy of the GNU General Public    */
/* License along with Seal; see the file COPYING.  If not,      */
/* write to the Free Software Foundation, 675 Mass Ave,         */
/* Cambridge, MA 02139, USA.                                    */
/*                                                              */
/****************************************************************/
#ifndef __RSXNT__
#include"djc.h"
#endif
#include"allegro.h"
#include"dzcomm.h"
void *_dxe_load(char *dxe_filename) { return NULL; };
void* __builtin_new(size_t mysize); /* c++ */
void __builtin_delete(void* myptr); /* c++ */
#ifdef __cplusplus
extern "C" {
#endif
#ifndef __RSXNT__
  extern jmp_buf *__djgpp_exception_state_ptr;	/* Must include setjmp.h first */
  extern void *_stubinfo;
  extern void __umoddi3(void);
  extern void __udivdi3(void);
#endif
  extern int _textmode;
  extern BLENDER_MAP _trans_blender15;
  extern BLENDER_MAP _trans_blender16;
  extern BLENDER_MAP _trans_blender24;
  extern int _blender_alpha;
  extern int _color_conv;
 int _color_load_depth(int _depth, int hasalpha);
#ifndef __RSXNT__
  void _unlock_dpmi_data(void *addr, int size);
#endif
#ifdef __cplusplus
};
#endif

DLXUSE_BEGIN
  LIBEXPORT_BEGIN
    /* osx */
    /* c++ */
    LIBEXPORT(DLXImport)
    LIBEXPORT(DLXError)
    LIBEXPORT(DLXGetID)

    LIBEXPORT(DLXGetLMaxInstance)
    LIBEXPORT(DLXGetLInstance)
    LIBEXPORT(DLXGetLOverload)
    LIBEXPORT(DLXSetLMaxInstance)
    LIBEXPORT(DLXSetLOverload)
    LIBEXPORT(DLXGetFileData)
    LIBEXPORT(DLXLoad)
    LIBEXPORT(DLXUnload)
    LIBEXPORT(DLXUnloadEx)
    LIBEXPORT(DLXImport)
    LIBEXPORT(DLXGetEntry)
    LIBEXPORT(DLXGetMemoryBlock)
    LIBEXPORT(DLXGetMemoryBlockLength)
    LIBEXPORT(DLXGetResource)
    LIBEXPORT(DLXGetResourceLength)
#ifndef __RSXNT__
    /* c */

#ifdef WANT_REGISTRY 
    LIBEXPORT(registry_color) 
#endif

/* Humm, probably loads of these should not be exported. The structs have the relevant pointers in any case. */

    LIBEXPORT(tag_strcpy) 
    LIBEXPORT(tag_stricmp) 
    LIBEXPORT(sf_alert) 
    LIBEXPORT(sf_malloc) 
    LIBEXPORT(sf_free) 
    LIBEXPORT(sf_cfree) 
    LIBEXPORT(sf_calloc) 
    LIBEXPORT(sf_strdup) 
    LIBEXPORT(sf_realloc) 
    LIBEXPORT(dat_filter) 
    LIBEXPORT(filter_insert) 
    LIBEXPORT(filter_remove) 
    LIBEXPORT(filter_remove_all) 
#ifndef DEBUG_printf_defined
    LIBEXPORT(DEBUG_printf)
#endif
    LIBEXPORT(point_assign) 
    LIBEXPORT(rect_assign) 
    LIBEXPORT(rect_move) 
    LIBEXPORT(rect_size) 
    LIBEXPORT(rect_cliped) 
    LIBEXPORT(rect_overlay) 
    LIBEXPORT(rect_sizex) 
    LIBEXPORT(rect_sizey) 
    LIBEXPORT(rect_check_empty) 
    LIBEXPORT(rect_contains) 
    LIBEXPORT(rect_equals) 
    LIBEXPORT(rect_double_overlay) 
    LIBEXPORT(_afree)
    LIBEXPORT(aclock) 
    LIBEXPORT(time_get_mili) 
    LIBEXPORT(time_diff_mili) 
    LIBEXPORT(set_format_text) 
    LIBEXPORT(set_format_text_nice) 
    LIBEXPORT(insstr) 
    LIBEXPORT(delstr) 
    LIBEXPORT(stridup) 
    LIBEXPORT(stristr) 
    LIBEXPORT(fifo_add) 
    LIBEXPORT(fifo_get) 
    LIBEXPORT(_seal_error) 
    LIBEXPORT(is_my_object) 
    LIBEXPORT(is_active) 
    LIBEXPORT(init_stillprocess) 
    LIBEXPORT(obj_done) 
    LIBEXPORT(obj_index_of) 
    LIBEXPORT(obj_at) 
    LIBEXPORT(obj_find_match) 
    LIBEXPORT(obj_find_match_view) 
    LIBEXPORT(obj_call_trans_events) 
    LIBEXPORT(obj_owner_view) 
    LIBEXPORT(obj_next_view) 
    LIBEXPORT(obj_prev_view) 
    LIBEXPORT(obj_last_view) 
    LIBEXPORT(obj_first_view) 
    LIBEXPORT(obj_prev_view_to_first) 
    LIBEXPORT(obj_next_view_to_last) 
    LIBEXPORT(obj_prev_to_first) 
    LIBEXPORT(obj_next_to_last) 
    LIBEXPORT(obj_setup) 
    LIBEXPORT(obj_after_init) 
    LIBEXPORT(obj_select) 
    LIBEXPORT(obj_put_into_stillprocess) 
    LIBEXPORT(obj_clear_from_stillprocess) 
    LIBEXPORT(obj_insert) 
    LIBEXPORT(obj_insert_before) 
    LIBEXPORT(obj_put_in_front_of) 
    LIBEXPORT(obj_first) 
    LIBEXPORT(obj_set_state) 
    LIBEXPORT(obj_is_state) 
    LIBEXPORT(obj_set_options) 
    LIBEXPORT(obj_is_options) 
    LIBEXPORT(obj_remove) 
    LIBEXPORT(obj_valid) 
    LIBEXPORT(obj_get_event) 
    LIBEXPORT(obj_put_event) 
    LIBEXPORT(obj_execute) 
    LIBEXPORT(obj_reset_prefer) 
    LIBEXPORT(obj_set_prefer) 
    LIBEXPORT(obj_for_each_event) 
    LIBEXPORT(obj_play_process) 
    LIBEXPORT(obj_translate_event) 
    LIBEXPORT(obj_get_data) 
    LIBEXPORT(obj_set_data) 
    LIBEXPORT(obj_select_data)
    LIBEXPORT(dispose)
    LIBEXPORT(dispose_all) 
    LIBEXPORT(message_info) 
    LIBEXPORT(message_all_info) 
    LIBEXPORT(set_event) 
    LIBEXPORT(set_event_info) 
    LIBEXPORT(copy_type) 
    LIBEXPORT(_obj_init) 
    LIBEXPORT(get_context_mem_size) 
    LIBEXPORT(get_depth) 
    LIBEXPORT(get_mask_color) 
    LIBEXPORT(get_addr_line) 
    LIBEXPORT(get_width) 
    LIBEXPORT(get_height) 
    LIBEXPORT(get_clip) 
    LIBEXPORT(get_real_box) 
    LIBEXPORT(get_size_of_ftext) 
    LIBEXPORT(point_vline) 
    LIBEXPORT(point_hline) 
    LIBEXPORT(draw_selected_text) 
    LIBEXPORT(draw_double_text) 
    LIBEXPORT(lined_rect) 
    LIBEXPORT(fade_rect) 
    LIBEXPORT(light_image) 
    LIBEXPORT(button) 
    LIBEXPORT(get_char_length) 
    LIBEXPORT(textout_draw) 
    LIBEXPORT(textout_draw_rect) 
    LIBEXPORT(textout_printf) 
    LIBEXPORT(draw_flip_sprite) 
    LIBEXPORT(__blit_hline) 
    LIBEXPORT(blit_ex) 
    LIBEXPORT(blit_hline) 
    LIBEXPORT(blit_circlefill) 
    LIBEXPORT(blit_ellipsefill) 
    LIBEXPORT(gen_neg) 
    LIBEXPORT(gen_emboss) 
    LIBEXPORT(conv_to_skipcolor_bitmap) 
    LIBEXPORT(conv_to_skipcolor_data) 
    LIBEXPORT(load_icon_library) 
    LIBEXPORT(unload_icon_library) 
    LIBEXPORT(get_icon_from_library) 
    LIBEXPORT(convert_to_icon_library) 
    LIBEXPORT(get_datafile_object) 
    LIBEXPORT(gr_load_bitmap) 
    LIBEXPORT(load_skip_datafile) 
    LIBEXPORT(_gr_setmode) 
    LIBEXPORT(getini_values) 
    LIBEXPORT(freeini_values) 
    LIBEXPORT(getini_linenum) 
    LIBEXPORT(getini_line) 
    LIBEXPORT(getini_function) 
    LIBEXPORT(getini_value) 
    LIBEXPORT(getininum_fromfile) 
    LIBEXPORT(getinidata_fromfile) 
    LIBEXPORT(getini_color) 
    LIBEXPORT(getini_fromfile) 
    LIBEXPORT(setini_tofile) 
    LIBEXPORT(io_nicelink) 
    LIBEXPORT(new_tfile) 
    LIBEXPORT(free_tfile) 
    LIBEXPORT(io_findfirst) 
    LIBEXPORT(io_findnext) 
    LIBEXPORT(io_foreach_file_ex) 
    LIBEXPORT(io_foreach_file_copy) 
    LIBEXPORT(io_rename) 
    LIBEXPORT(io_removefile) 
    LIBEXPORT(io_copyfile) 
    LIBEXPORT(io_numberfile) 
    LIBEXPORT(_io_removefile) 
    LIBEXPORT(_io_copyfile) 
    LIBEXPORT(io_mkdir) 
    LIBEXPORT(io_uniquedir) 
    LIBEXPORT(io_issame) 
    LIBEXPORT(io_isfile) 
    LIBEXPORT(io_parentdir) 
    LIBEXPORT(io_isdir) 
    LIBEXPORT(io_exist) 
    LIBEXPORT(io_realpath) 
    LIBEXPORT(io_cleartfile) 
    LIBEXPORT(io_filetotfile) 
    LIBEXPORT(io_isas) 
    LIBEXPORT(io_linkedpath_ex) 
    LIBEXPORT(io_linkedfile_ex) 
    LIBEXPORT(io_linkedfile) 
    LIBEXPORT(io_linkedpath) 
    LIBEXPORT(io_set_linkedpath) 
    LIBEXPORT(io_set_linkedfile) 
    LIBEXPORT(io_set_linkedfile_ex) 
    LIBEXPORT(io_set_linkedpath_ex) 
    LIBEXPORT(io_isext) 
    LIBEXPORT(io_isfilename) 
    LIBEXPORT(drv_findfirst) 
    LIBEXPORT(drv_findnext) 
    LIBEXPORT(drv_fixname) 
    LIBEXPORT(drv_fixreal) 
    LIBEXPORT(drv_isdriver) 
    LIBEXPORT(drv_fixid) 
    LIBEXPORT(drv_set) 
    LIBEXPORT(file_gettext) 
    LIBEXPORT(file_puttext) 
    LIBEXPORT(drv_init) 
    LIBEXPORT(drv_done) 
    LIBEXPORT(list_done) 
    LIBEXPORT(list_copy_ctx) 
    LIBEXPORT(list_for_each_item) 
    LIBEXPORT(list_for_each_item_to_item) 
    LIBEXPORT(list_first_rec) 
    LIBEXPORT(list_first) 
    LIBEXPORT(list_at_item) 
    LIBEXPORT(list_at) 
    LIBEXPORT(list_index_of) 
    LIBEXPORT(list_index_of_item) 
    LIBEXPORT(list_find_rec) 
    LIBEXPORT(list_insert_ex) 
    LIBEXPORT(list_insert) 
    LIBEXPORT(list_get_max_item) 
    LIBEXPORT(list_remove_index) 
    LIBEXPORT(list_remove_item) 
    LIBEXPORT(list_free_index) 
    LIBEXPORT(list_free_item) 
    LIBEXPORT(list_free_all) 
    LIBEXPORT(list_collect_by_name_from) 
    LIBEXPORT(dispose_list) 
    LIBEXPORT(_list_init) 
    LIBEXPORT(_read_set_file) 
    LIBEXPORT(_load_exe_icon) 
    LIBEXPORT(_load_ldv_icon) 
    LIBEXPORT(_load_lnk_icon) 
    LIBEXPORT(load_file_icon) 
    LIBEXPORT(add_to_loadfileicon) 
    LIBEXPORT(init_loadfileicon) 
    LIBEXPORT(free_loadfileicon) 
    LIBEXPORT(get_icon_for_file_ex) 
    LIBEXPORT(get_icon_for_file) 
    LIBEXPORT(get_args) 
    LIBEXPORT(run_file_args) 
    LIBEXPORT(run_file) 
    LIBEXPORT(init_ext_runners) 
    LIBEXPORT(done_ext_runners) 
    LIBEXPORT(_dos_command) 
    LIBEXPORT(_dos_run) 
    LIBEXPORT(key_simulate_keypress) 
    LIBEXPORT(key_set_ctrl_alt_del_flag) 
    LIBEXPORT(key_get_ctrl_alt_del_flag) 
    LIBEXPORT(key_clear_buffer) 
    LIBEXPORT(key_keypressed) 
    LIBEXPORT(key_readkey) 
    LIBEXPORT(key_translate_event) 
    LIBEXPORT(key_done) 
    LIBEXPORT(_key_init) 
    LIBEXPORT(write_mouse_queue) 
    LIBEXPORT(read_mouse_queue) 
    LIBEXPORT(mouse_get_cursor) 
    LIBEXPORT(mouse_get_range) 
    LIBEXPORT(mouse_get_focus) 
    LIBEXPORT(xx_mouse_is_visible) 
    LIBEXPORT(mouse_show) 
    LIBEXPORT(mouse_hide) 
    LIBEXPORT(mouse_set_cursor) 
    LIBEXPORT(mouse_set_cursor_focus) 
    LIBEXPORT(mouse_set_range) 
    LIBEXPORT(mouse_set_dclick_diff) 
    LIBEXPORT(mouse_set_speed) 
    LIBEXPORT(mouse_set_pos) 
    LIBEXPORT(xx_mouse_block) 
    LIBEXPORT(xx_mouse_unblock) 
    LIBEXPORT(xx_mouse_is_block) 
    LIBEXPORT(mouse_set_focus) 
    LIBEXPORT(mouse_translate_event) 
    LIBEXPORT(clear_event_mouse) 
    LIBEXPORT(mouse_set_cursor_system_file) 
    LIBEXPORT(mouse_load_cursors) 
    LIBEXPORT(mouse_done) 
    LIBEXPORT(_mouse_init) 
    LIBEXPORT(screen_reload) 
    LIBEXPORT(screen_halt) 
    LIBEXPORT(screen_init) 
    LIBEXPORT(screen_done) 
    LIBEXPORT(add_font_to_system) 
    LIBEXPORT(load_supported_fonts) 
    LIBEXPORT(unload_system_fonts) 
    LIBEXPORT(get_font_in_size) 
    LIBEXPORT(safe_timer) 
    LIBEXPORT(erase_safe_timer) 
    LIBEXPORT(safe_timer_halt) 
    LIBEXPORT(safe_timer_reload) 
    LIBEXPORT(safe_timer_free_all) 
    LIBEXPORT(icons_init) 
    LIBEXPORT(icons_done) 
    LIBEXPORT(get_icon_id) 
    LIBEXPORT(fonts_init) 
    LIBEXPORT(fonts_done) 
    LIBEXPORT(drivers_done) 
    LIBEXPORT(drivers_init) 
    LIBEXPORT(color_get_from_ini)
    LIBEXPORT(color_trans_palette) 
    LIBEXPORT(colors_init) 
    LIBEXPORT(colors_done) 
    LIBEXPORT(info_board_init) 
    LIBEXPORT(view_ini) 
    LIBEXPORT(view_reset_prefer) 
    LIBEXPORT(view_done) 
    LIBEXPORT(_show_info_board) 
    LIBEXPORT(view_show_info_board) 
    LIBEXPORT(view_top_view) 
    LIBEXPORT(view_set_mouse_cursor) 
    LIBEXPORT(view_is_mouse_in_view) 
    LIBEXPORT(view_get_view_under_mouse) 
    LIBEXPORT(view_get_top_view_under_mouse) 
    LIBEXPORT(is_top_view_under_mouse_in_me) 
    LIBEXPORT(view_play_process) 
    LIBEXPORT(view_translate_event) 
    LIBEXPORT(view_setup) 
    LIBEXPORT(view_after_init) 
    LIBEXPORT(view_put_in_front_of) 
    LIBEXPORT(view_set_state) 
    LIBEXPORT(view_reset_align) 
    LIBEXPORT(view_execute_view) 
    LIBEXPORT(view_drag_data) 
    LIBEXPORT(view_drag_where) 
    LIBEXPORT(view_drop_data) 
    LIBEXPORT(view_drag_view) 
    LIBEXPORT(view_drag) 
    LIBEXPORT(view_get_limits) 
    LIBEXPORT(view_size_minimum) 
    LIBEXPORT(view_for_each_sub_view_set_state) 
    LIBEXPORT(view_lock_drawing) 
    LIBEXPORT(view_unlock_drawing) 
    LIBEXPORT(view_is_draw_lock) 
    LIBEXPORT(view_draw_overlays) 
    LIBEXPORT(view_draw) 
    LIBEXPORT(view_draw_me) 
    LIBEXPORT(view_draw_view) 
    LIBEXPORT(view_draw_sub_views) 
    LIBEXPORT(view_draw_under_rect) 
    LIBEXPORT(view_draw_under_view) 
    LIBEXPORT(view_draw_in_rect) 
    LIBEXPORT(view_draw_mini_box) 
    LIBEXPORT(view_get_color) 
    LIBEXPORT(view_is_top_view) 
    LIBEXPORT(view_move_accel) 
    LIBEXPORT(view_move_view) 
    LIBEXPORT(view_grow_view) 
    LIBEXPORT(view_set_palette) 
    LIBEXPORT(view_begin_paint) 
    LIBEXPORT(view_end_of_paint) 
    LIBEXPORT(view_set_clips) 
    LIBEXPORT(view_reset_clips) 
    LIBEXPORT(view_get_local_extent) 
    LIBEXPORT(view_get_global_bounds) 
    LIBEXPORT(view_get_local_bounds) 
    LIBEXPORT(view_get_global_point) 
    LIBEXPORT(view_get_local_point) 
    LIBEXPORT(view_get_not_overlays) 
    LIBEXPORT(view_set_draw_mode) 
    LIBEXPORT(view_is_draw_mode) 
    LIBEXPORT(view_show) 
    LIBEXPORT(view_hide) 
    LIBEXPORT(view_get_cliped_extent) 
    LIBEXPORT(view_size_limits) 
    LIBEXPORT(view_set_bounds) 
    LIBEXPORT(view_change_bounds) 
    LIBEXPORT(view_set_clip) 
    LIBEXPORT(view_background) 
    LIBEXPORT(_view_init) 
    LIBEXPORT(win_done) 
    LIBEXPORT(win_set_state) 
    LIBEXPORT(win_size_limits) 
    LIBEXPORT(win_size_minimum) 
    LIBEXPORT(win_draw) 
    LIBEXPORT(win_set_mouse_cursor) 
    LIBEXPORT(win_translate_event) 
    LIBEXPORT(win_draw_title) 
    LIBEXPORT(_win_init) 
    LIBEXPORT(window_ini_palette) 
    LIBEXPORT(_dxe_load) 
    LIBEXPORT(program_put_event) 
    LIBEXPORT(program_put_into_stillprocess) 
    LIBEXPORT(program_clear_from_stillprocess) 
    LIBEXPORT(program_get_event) 
    LIBEXPORT(program_translate_event) 
    LIBEXPORT(program_done) 
    LIBEXPORT(program_init) 
    LIBEXPORT(program_call_each_stillprocess) 
    LIBEXPORT(program_int) 
    LIBEXPORT(clipboard) 
    LIBEXPORT(system_fonts) 
    LIBEXPORT(screen_shot) 
    LIBEXPORT(msblock1) 
    LIBEXPORT(screen_virtual) 
    LIBEXPORT(list_init) 
    LIBEXPORT(stillprocess_list) 
    LIBEXPORT(font_times_1_25) 
    LIBEXPORT(palette_standard) 
    LIBEXPORT(cursor_focus) 
    LIBEXPORT(screen_height) 
    LIBEXPORT(msblock0) 
    LIBEXPORT(system_item_size) 
    LIBEXPORT(msblock4) 
    LIBEXPORT(task_tick) 
    LIBEXPORT(filter) 
    LIBEXPORT(key_init) 
    LIBEXPORT(ini_mainfile) 
    LIBEXPORT(cursor_system) 
    LIBEXPORT(info_board_font) 
    LIBEXPORT(mouse_init)
    LIBEXPORT(obj_init) 
    LIBEXPORT(font_system_bd) 
    LIBEXPORT(colors_standard) 
    LIBEXPORT(read_set_file) 
    LIBEXPORT(rect_empty) 
    LIBEXPORT(drivers) 
    LIBEXPORT(safe_timers) 
    LIBEXPORT(desktop) 
    LIBEXPORT(mouse_flags) 
    LIBEXPORT(cursor_standard) 
    LIBEXPORT(unload_font) 
    LIBEXPORT(drawtext) 
    LIBEXPORT(screen_width) 
    LIBEXPORT(font_smooth) 
    LIBEXPORT(drives) 
    LIBEXPORT(go_process) 
    LIBEXPORT(icon_system) 
    LIBEXPORT(program) 
    LIBEXPORT(font_system_i) 
    LIBEXPORT(win_init) 
    LIBEXPORT(font_system_bi) 
    LIBEXPORT(keyb) 
    LIBEXPORT(info_board_b1color) 
    LIBEXPORT(dos_command) 
    LIBEXPORT(seal_test_file) 
    LIBEXPORT(textheight) 
    LIBEXPORT(dos_run)
    LIBEXPORT(info_board_key) 
    LIBEXPORT(seal_error) 
    LIBEXPORT(msblock2) 
    LIBEXPORT(icon_id) 
    LIBEXPORT(info_board_b2color) 
    LIBEXPORT(seal_debug_file)
    LIBEXPORT(view_init) 
    LIBEXPORT(event_stop) 
    LIBEXPORT(pal_window) 
    LIBEXPORT(msblock3) 
    LIBEXPORT(draw_char)
    LIBEXPORT(ico_loaders) 
    LIBEXPORT(mouse) 
    LIBEXPORT(event_timer) 
    LIBEXPORT(event_main) 
    LIBEXPORT(textlen) 
    LIBEXPORT(change_font_size) 
    LIBEXPORT(clock_including) 
    LIBEXPORT(info_board_fcolor) 
    LIBEXPORT(load_font) 
    LIBEXPORT(font_system) 

  /* djc.inp */

    LIBEXPORT(__dpmi_allocate_memory) 
    LIBEXPORT(rewinddir) 
    LIBEXPORT(__dpmi_clear_debug_watchpoint) 
    LIBEXPORT(__exit) 
    LIBEXPORT(__dpmi_unlock_linear_region) 
    LIBEXPORT(__dj_assert) 
    LIBEXPORT(insque) 
    LIBEXPORT(mblen) 
    LIBEXPORT(__dpmi_get_and_disable_virtual_interrupt_state) 
    LIBEXPORT(usleep) 
    LIBEXPORT(intensevideo) 
    LIBEXPORT(__dpmi_set_multiple_descriptors) 
    LIBEXPORT(getpid) 
    LIBEXPORT(putchar) 
    LIBEXPORT(getgid) 
    LIBEXPORT(execl) 
    LIBEXPORT(textmode) 
    LIBEXPORT(__dpmi_lock_linear_region) 
    LIBEXPORT(statfs) 
    LIBEXPORT(bioskey) 
    LIBEXPORT(symlink) 
    LIBEXPORT(outpw) 
    LIBEXPORT(strcspn) 
    LIBEXPORT(sprintf) 
    LIBEXPORT(_dos_gettime) 
    LIBEXPORT(textattr) 
    LIBEXPORT(__dpmi_set_real_mode_interrupt_vector) 
    LIBEXPORT(feof) 
    LIBEXPORT(blinkvideo) 
    LIBEXPORT(getche) 
    LIBEXPORT(_exit) 
    LIBEXPORT(__crt0_setup_arguments) 
    LIBEXPORT(setcbrk) 
    LIBEXPORT(utime) 
    LIBEXPORT(_truename) 
    LIBEXPORT(getgrent) 
    LIBEXPORT(__djgpp_memory_handle) 
    LIBEXPORT(tcgetattr) 
    LIBEXPORT(free) 
    LIBEXPORT(strcat) 
    LIBEXPORT(dup) 
    LIBEXPORT(gettext) 
    LIBEXPORT(strcmp) 
    LIBEXPORT(cgets) 
    LIBEXPORT(getpass) 
    LIBEXPORT(memccpy) 
    LIBEXPORT(__dpmi_install_resident_service_provider_callback) 
    LIBEXPORT(__dpmi_simulate_real_mode_procedure_retf_stack) 
    LIBEXPORT(ScreenUpdate) 
    LIBEXPORT(regfree) 
    LIBEXPORT(fabs) 
    LIBEXPORT(pause) 
    LIBEXPORT(bzero) 
    LIBEXPORT(getpgrp) 
    LIBEXPORT(spawnlp) 
    LIBEXPORT(globfree) 
    LIBEXPORT(execlp) 
    LIBEXPORT(__dpmi_allocate_real_mode_callback) 
    LIBEXPORT(lock) 
    LIBEXPORT(nice) 
    LIBEXPORT(acosh) 
    LIBEXPORT(mkdir) 
    LIBEXPORT(sleep) 
    LIBEXPORT(__dpmi_get_memory_information) 
    LIBEXPORT(__djgpp_exception_toggle) 
    LIBEXPORT(searchpath) 
    LIBEXPORT(mkstemp) 
    LIBEXPORT(_go32_dpmi_get_protected_mode_interrupt_vector) 
    LIBEXPORT(__djgpp_nearptr_enable) 
    LIBEXPORT(vsprintf) 
    LIBEXPORT(__dpmi_get_virtual_interrupt_state) 
    LIBEXPORT(_go32_info_block) 
    LIBEXPORT(log10) 
    LIBEXPORT(settime) 
    LIBEXPORT(ScreenClear) 
    LIBEXPORT(pow10) 
    LIBEXPORT(_bios_disk) 
    LIBEXPORT(telldir) 
    LIBEXPORT(longjmp) 
    LIBEXPORT(__dpmi_get_vendor_specific_api_entry_point) 
    LIBEXPORT(fgetpos) 
    LIBEXPORT(glob) 
    LIBEXPORT(vprintf) 
    LIBEXPORT(__dpmi_get_page_size) 
    LIBEXPORT(setlinebuf) 
    LIBEXPORT(_dos_lock) 
    LIBEXPORT(_write) 
    LIBEXPORT(rand) 
    LIBEXPORT(__dpmi_serialize_on_shared_memory) 
    LIBEXPORT(modf) 
    LIBEXPORT(ceil) 
    LIBEXPORT(putc) 
    LIBEXPORT(clock) 
    LIBEXPORT(_bios_timeofday) 
    LIBEXPORT(strupr) 
    LIBEXPORT(ScreenGetCursor) 
    LIBEXPORT(wctomb) 
    LIBEXPORT(__dpmi_set_debug_watchpoint) 
    LIBEXPORT(__libc_termios_init) 
    LIBEXPORT(getc) 
    LIBEXPORT(bsearch) 
    LIBEXPORT(_djstat_describe_lossage) 
    LIBEXPORT(insline) 
    LIBEXPORT(ungetc) 
    LIBEXPORT(getw) 
    LIBEXPORT(putch) 
    LIBEXPORT(closedir) 
    LIBEXPORT(ScreenRetrieve) 
    LIBEXPORT(rewind) 
    LIBEXPORT(_dosmemputl) 
    LIBEXPORT(_go32_dpmi_allocate_real_mode_callback_retf) 
    LIBEXPORT(getlogin) 
    LIBEXPORT(biosdisk) 
    LIBEXPORT(sysconf) 
    LIBEXPORT(_read) 
    LIBEXPORT(spawnle) 
    LIBEXPORT(_movedataw) 
    LIBEXPORT(__dj_stdin) 
    LIBEXPORT(setvbuf) 
    LIBEXPORT(biosequip) 
    LIBEXPORT(ScreenPutString) 
    LIBEXPORT(setbuf) 
    LIBEXPORT(abs) 
    LIBEXPORT(puts) 
    LIBEXPORT(_go32_dpmi_remaining_virtual_memory) 
    LIBEXPORT(getftime) 
    LIBEXPORT(_status87) 
    LIBEXPORT(__dpmi_terminate_and_stay_resident) 
    LIBEXPORT(strtoull) 
    LIBEXPORT(_dos_findnext) 
    LIBEXPORT(_dos_setfileattr) 
    LIBEXPORT(findfirst) 
    LIBEXPORT(setdisk) 
    LIBEXPORT(execv) 
    LIBEXPORT(select) 
    LIBEXPORT(ftime) 
    LIBEXPORT(int86) 
    LIBEXPORT(__dpmi_relock_real_mode_region) 
    LIBEXPORT(rmdir) 
    LIBEXPORT(_dos_settime) 
    LIBEXPORT(strncat) 
    LIBEXPORT(getpagesize) 
    LIBEXPORT(_open) 
    LIBEXPORT(putenv)
    LIBEXPORT(__dpmi_set_segment_limit) 
    LIBEXPORT(strlwr) 
    LIBEXPORT(tcsetattr) 
    LIBEXPORT(fstat) 
    LIBEXPORT(spawnl) 
    LIBEXPORT(atanh) 
    LIBEXPORT(clreol) 
    LIBEXPORT(readdir) 
    LIBEXPORT(spawnve) 
    LIBEXPORT(fputc) 
    LIBEXPORT(regcomp) 
    LIBEXPORT(execvp) 
    LIBEXPORT(setrlimit) 
    LIBEXPORT(chsize) 
    LIBEXPORT(__dpmi_yield) 
    LIBEXPORT(__dpmi_free_shared_memory) 
    LIBEXPORT(cfgetospeed) 
    LIBEXPORT(__dpmi_set_extended_exception_handler_vector_rm) 
    LIBEXPORT(_fpreset) 
    LIBEXPORT(scanf) 
    LIBEXPORT(__dpmi_simulate_real_mode_interrupt) 
    LIBEXPORT(addmntent) 
    LIBEXPORT(getwd) 
    LIBEXPORT(__dpmi_allocate_linear_memory) 
    LIBEXPORT(gethostname) 
    LIBEXPORT(_set_screen_lines) 
    LIBEXPORT(fnsplit) 
    LIBEXPORT(mktime) 
    LIBEXPORT(cputs) 
    LIBEXPORT(__dpmi_set_descriptor_access_rights) 
    LIBEXPORT(getegid) 
    LIBEXPORT(getrusage) 
    LIBEXPORT(random) 
    LIBEXPORT(fputs) 
    LIBEXPORT(setdate) 
    LIBEXPORT(ftw) 
    LIBEXPORT(rindex) 
    LIBEXPORT(_dos_close) 
    LIBEXPORT(atof) 
    LIBEXPORT(ferror) 
    LIBEXPORT(_go32_dpmi_free_real_mode_callback) 
    LIBEXPORT(_osmajor) 
    LIBEXPORT(isatty) 
    LIBEXPORT(__builtin_delete) 
    LIBEXPORT(sinh) 
    LIBEXPORT(movetext) 
    LIBEXPORT(strcoll) 
    LIBEXPORT(fdopen) 
    LIBEXPORT(movedata)
    LIBEXPORT(__dpmi_allocate_dos_memory) 
    LIBEXPORT(inportsb) 
    LIBEXPORT(open) 
    LIBEXPORT(__dpmi_map_conventional_memory_in_memory_block) 
    LIBEXPORT(_doprnt) 
    LIBEXPORT(sbrk) 
    LIBEXPORT(ftruncate) 
    LIBEXPORT(_atold) 
    LIBEXPORT(_dos_read) 
    LIBEXPORT(__dpmi_free_memory) 
    LIBEXPORT(raise) 
    LIBEXPORT(__dpmi_set_extended_exception_handler_vector_pm) 
    LIBEXPORT(_go32_dpmi_allocate_real_mode_callback_iret) 
    LIBEXPORT(mknod) 
    LIBEXPORT(fwrite) 
    LIBEXPORT(gets) 
    LIBEXPORT(fnmatch) 
    LIBEXPORT(strncpy) 
    LIBEXPORT(outp) 
    LIBEXPORT(window) 
    LIBEXPORT(__file_tree_walk) 
    LIBEXPORT(hasmntopt) 
    LIBEXPORT(sys_nerr) 
    LIBEXPORT(times) 
    LIBEXPORT(endgrent) 
    LIBEXPORT(_lfn_get_ftime) 
    LIBEXPORT(_dosmemgetb) 
    LIBEXPORT(atoi) 
    LIBEXPORT(bdos) 
    LIBEXPORT(wcstombs) 
    LIBEXPORT(getkey) 
    LIBEXPORT(rawclock) 
    LIBEXPORT(__dpmi_physical_address_mapping) 
    LIBEXPORT(biosprint) 
    LIBEXPORT(_chmod) 
    LIBEXPORT(ttyname) 
    LIBEXPORT(regexec) 
    LIBEXPORT(_mono_putc) 
    LIBEXPORT(_dos_creatnew) 
    LIBEXPORT(__dpmi_get_real_mode_interrupt_vector) 
    LIBEXPORT(memcpy) 
    LIBEXPORT(__dpmi_get_free_memory_information) 
    LIBEXPORT(opendir) 
    LIBEXPORT(_dos_open) 
    LIBEXPORT(execvpe) 
    LIBEXPORT(_go32_dpmi_set_real_mode_interrupt_vector) 
    LIBEXPORT(getgrgid) 
    LIBEXPORT(_go32_interrupt_stack_size) 
    LIBEXPORT(popen) 
    LIBEXPORT(memset) 
    LIBEXPORT(setlocale) 
    LIBEXPORT(_go32_dpmi_lock_code) 
    LIBEXPORT(settimeofday) 
    LIBEXPORT(dosmemget)
    LIBEXPORT(stat) 
    LIBEXPORT(_dosmemputw) 
    LIBEXPORT(fileno) 
    LIBEXPORT(ScreenSetCursor) 
    LIBEXPORT(frexp) 
    LIBEXPORT(__dpmi_int) 
    LIBEXPORT(puttext) 
    LIBEXPORT(__dj_ctype_toupper) 
    LIBEXPORT(__dpmi_get_raw_mode_switch_addr) 
    LIBEXPORT(mprotect) 
    LIBEXPORT(_go32_was_ctrl_break_hit) 
    LIBEXPORT(bcmp) 
    LIBEXPORT(waitpid) 
    LIBEXPORT(strerror) 
    LIBEXPORT(mkfifo) 
    LIBEXPORT(_go32_dpmi_resize_dos_memory) 
    LIBEXPORT(outportsw) 
    LIBEXPORT(__dpmi_get_descriptor_access_rights) 
    LIBEXPORT(remove) 
    LIBEXPORT(setpgid) 
    LIBEXPORT(_dosexterr) 
    LIBEXPORT(chdir) 
    LIBEXPORT(getlongpass) 
    LIBEXPORT(_control87) 
    LIBEXPORT(cfgetispeed) 
    LIBEXPORT(mbstowcs) 
    LIBEXPORT(ScreenUpdateLine) 
    LIBEXPORT(getuid) 
    LIBEXPORT(fflush) 
    LIBEXPORT(clearerr) 
    LIBEXPORT(dosmemput)
    LIBEXPORT(_djstat_flags) 
    LIBEXPORT(itoa) 
    LIBEXPORT(fpurge) 
    LIBEXPORT(__dpmi_get_extended_exception_handler_vector_pm) 
    LIBEXPORT(ldiv) 
    LIBEXPORT(__dpmi_set_protected_mode_interrupt_vector) 
    LIBEXPORT(_strtold) 
    LIBEXPORT(asinh) 
    LIBEXPORT(strxfrm) 
    LIBEXPORT(_dos_setdrive) 
    LIBEXPORT(fprintf) 
    LIBEXPORT(setitimer) 
    LIBEXPORT(llabs) 
    LIBEXPORT(wait) 
    LIBEXPORT(gettextinfo) 
    LIBEXPORT(__dpmi_set_processor_exception_handler_vector) 
    LIBEXPORT(_mono_printf) 
    LIBEXPORT(lseek) 
    LIBEXPORT(delline) 
    LIBEXPORT(inportsw) 
    LIBEXPORT(strtol) 
    LIBEXPORT(getdate) 
    LIBEXPORT(getitimer) 
    LIBEXPORT(_get_volume_info) 
    LIBEXPORT(_clear87) 
    LIBEXPORT(log2) 
    LIBEXPORT(strdup) 
    LIBEXPORT(normvideo) 
    LIBEXPORT(setpwent) 
    LIBEXPORT(tell) 
    LIBEXPORT(__FSEXT_alloc_fd) 
    LIBEXPORT(_go32_dpmi_free_iret_wrapper) 
    LIBEXPORT(setbuffer) 
    LIBEXPORT(strcasecmp) 
    LIBEXPORT(outportsb) 
    LIBEXPORT(execle) 
    LIBEXPORT(fread) 
    LIBEXPORT(__dpmi_get_state_of_debug_watchpoint) 
    LIBEXPORT(_creat) 
    LIBEXPORT(gmtime) 
    LIBEXPORT(_dos_getdiskfree) 
    LIBEXPORT(getgrnam) 
    LIBEXPORT(gppconio_init) 
    LIBEXPORT(pow2) 
    LIBEXPORT(swab) 
    LIBEXPORT(fopen) 
    LIBEXPORT(setmode) 
    LIBEXPORT(_go32_dpmi_chain_protected_mode_interrupt_vector) 
    LIBEXPORT(__dpmi_free_real_mode_callback) 
    LIBEXPORT(_dosmemgetl) 
    LIBEXPORT(calloc) 
    LIBEXPORT(time) 
    LIBEXPORT(_fmode) 
    LIBEXPORT(__dpmi_get_memory_block_size_and_base) 
    LIBEXPORT(getrlimit) 
    LIBEXPORT(__dpmi_resize_memory) 
    LIBEXPORT(cfsetspeed) 
    LIBEXPORT(__dpmi_get_multiple_descriptors) 
    LIBEXPORT(ScreenAttrib) 
    LIBEXPORT(_go32_rmcb_stack_size) 
    LIBEXPORT(strpbrk) 
    LIBEXPORT(textbackground) 
    LIBEXPORT(getchar) 
    LIBEXPORT(__dpmi_set_descriptor) 
    LIBEXPORT(ctime) 
    LIBEXPORT(stpcpy) 
    LIBEXPORT(ScreenPutChar) 
    LIBEXPORT(dup2) 
    LIBEXPORT(strftime) 
    LIBEXPORT(brk) 
    LIBEXPORT(hypot) 
    LIBEXPORT(outportsl) 
    LIBEXPORT(__dpmi_free_serialization_on_shared_memory) 
    LIBEXPORT(setmntent) 
    LIBEXPORT(_go32_dpmi_get_real_mode_interrupt_vector) 
    LIBEXPORT(__dpmi_get_selector_increment_value) 
    LIBEXPORT(bcopy) 
    LIBEXPORT(getpwnam) 
    LIBEXPORT(_dos_setftime) 
    LIBEXPORT(cfree) 
    LIBEXPORT(tmpnam) 
    LIBEXPORT(__dj_stdout) 
    LIBEXPORT(realloc) 
    LIBEXPORT(getch) 
    LIBEXPORT(_go32_dpmi_remaining_physical_memory) 
    LIBEXPORT(memchr) 
    LIBEXPORT(link) 
    LIBEXPORT(sys_errlist) 
    LIBEXPORT(tcsendbreak) 
    LIBEXPORT(__builtin_new) 
    LIBEXPORT(__dpmi_get_segment_base_address) 
    LIBEXPORT(__dpmi_get_coprocessor_status) 
    LIBEXPORT(exp) 
    LIBEXPORT(fnmerge) 
    LIBEXPORT(setgrent) 
    LIBEXPORT(getenv) 
    LIBEXPORT(_movedatal) 
    LIBEXPORT(strncasecmp) 
    LIBEXPORT(labs) 
    LIBEXPORT(chown) 
    LIBEXPORT(access) 
    LIBEXPORT(_use_lfn) 
    LIBEXPORT(filelength) 
    LIBEXPORT(textcolor) 
    LIBEXPORT(_dos_findfirst) 
    LIBEXPORT(uname) 
    LIBEXPORT(creat) 
    LIBEXPORT(bioscom) 
    LIBEXPORT(__dpmi_simulate_real_mode_procedure_iret) 
    LIBEXPORT(__dpmi_get_descriptor) 
    LIBEXPORT(spawnv) 
    LIBEXPORT(__dpmi_get_capabilities) 
    LIBEXPORT(_setcursortype) 
    LIBEXPORT(strtod) 
    LIBEXPORT(cfmakeraw) 
    LIBEXPORT(inpw) 
    LIBEXPORT(alarm) 
    LIBEXPORT(__djgpp_set_ctrl_c) 
    LIBEXPORT(rename) 
    LIBEXPORT(__FSEXT_call_open_handlers) 
    LIBEXPORT(strchr) 
    LIBEXPORT(fsync) 
    LIBEXPORT(log) 
    LIBEXPORT(uclock) 
    LIBEXPORT(__FSEXT_get_function) 
    LIBEXPORT(__dpmi_create_alias_descriptor) 
    LIBEXPORT(__djgpp_set_page_attributes) 
    LIBEXPORT(fgetc) 
    LIBEXPORT(__dpmi_segment_to_descriptor) 
    LIBEXPORT(cfsetispeed) 
    LIBEXPORT(_preserve_fncase) 
    LIBEXPORT(signal) 
    LIBEXPORT(__dpmi_get_version) 
    LIBEXPORT(tcdrain) 
    LIBEXPORT(malloc) 
    LIBEXPORT(_os_flavor) 
    LIBEXPORT(fclose) 
    LIBEXPORT(pipe) 
    LIBEXPORT(strcpy) 
    LIBEXPORT(_go32_want_ctrl_break) 
    LIBEXPORT(sscanf) 
    LIBEXPORT(cprintf) 
    LIBEXPORT(fseek) 
    LIBEXPORT(__djgpp_map_physical_memory) 
    LIBEXPORT(biosmemory) 
    LIBEXPORT(execve) 
    LIBEXPORT(memcmp) 
    LIBEXPORT(_dos_getdrive) 
    LIBEXPORT(_dos_getftime) 
    LIBEXPORT(__dpmi_mark_real_mode_region_as_pageable) 
    LIBEXPORT(getmntent) 
    LIBEXPORT(__dpmi_allocate_ldt_descriptors) 
    LIBEXPORT(strspn) 
    LIBEXPORT(atexit) 
    LIBEXPORT(__dpmi_get_and_set_virtual_interrupt_state) 
    LIBEXPORT(chmod) 
    LIBEXPORT(_dos_commit) 
    LIBEXPORT(perror) 
    LIBEXPORT(fpathconf) 
    LIBEXPORT(__dpmi_mark_page_as_demand_paging_candidate) 
    LIBEXPORT(strtok) 
    LIBEXPORT(_get_dev_info) 
    LIBEXPORT(_dos_unlock) 
    LIBEXPORT(localtime) 
    LIBEXPORT(__dpmi_set_segment_base_address) 
    LIBEXPORT(execlpe) 
    LIBEXPORT(_go32_dpmi_set_protected_mode_interrupt_vector) 
    LIBEXPORT(abort) 
    LIBEXPORT(_doscan) 
    LIBEXPORT(tmpfile) 
    LIBEXPORT(__djgpp_memory_handle_list) 
    LIBEXPORT(_dos_setdate) 
    LIBEXPORT(fgetgrent) 
    LIBEXPORT(ScreenCols) 
    LIBEXPORT(endmntent) 
    LIBEXPORT(modfl) 
    LIBEXPORT(ldexp) 
    LIBEXPORT(asctime) 
    LIBEXPORT(wherey) 
    LIBEXPORT(_osminor) 
    LIBEXPORT(_is_executable) 
    LIBEXPORT(__djgpp_nearptr_disable) 
    LIBEXPORT(__dj_ctype_flags) 
    LIBEXPORT(unlink) 
    LIBEXPORT(cscanf) 
    LIBEXPORT(lldiv) 
    LIBEXPORT(_dos_write) 
    LIBEXPORT(_dosmemgetw) 
    LIBEXPORT(strtoll) 
    LIBEXPORT(fork) 
    LIBEXPORT(ScreenGetChar) 
    LIBEXPORT(__dpmi_resize_dos_memory) 
    LIBEXPORT(tanh) 
    LIBEXPORT(pow) 
    LIBEXPORT(__dpmi_reset_debug_watchpoint) 
    LIBEXPORT(truncate) 
    LIBEXPORT(__dpmi_get_protected_mode_interrupt_vector) 
    LIBEXPORT(localeconv) 
    LIBEXPORT(_dos_getdate) 
    LIBEXPORT(ScreenMode) 
    LIBEXPORT(__FSEXT_add_open_handler) 
    LIBEXPORT(_movedatab) 
    LIBEXPORT(sound) 
    LIBEXPORT(endpwent) 
    LIBEXPORT(getcwd) 
    LIBEXPORT(wherex) 
    LIBEXPORT(mbtowc) 
    LIBEXPORT(strncmp) 
    LIBEXPORT(ftell) 
    LIBEXPORT(_flush_disk_cache) 
    LIBEXPORT(_close) 
    LIBEXPORT(gettime) 
    LIBEXPORT(write) 
    LIBEXPORT(strnicmp) 
    LIBEXPORT(utimes) 
    LIBEXPORT(__dpmi_free_dos_memory) 
    LIBEXPORT(__dpmi_set_page_attributes) 
    LIBEXPORT(__dj_ctype_tolower) 
    LIBEXPORT(__dpmi_free_physical_address_mapping) 
    LIBEXPORT(sync) 
    LIBEXPORT(__dpmi_allocate_shared_memory) 
    LIBEXPORT(__dpmi_get_and_enable_virtual_interrupt_state) 
    LIBEXPORT(ScreenRows) 
    LIBEXPORT(highvideo) 
    LIBEXPORT(strrchr) 
    LIBEXPORT(inportsl) 
    LIBEXPORT(_dos_creat) 
    LIBEXPORT(getdfree) 
    LIBEXPORT(_fixpath) 
    LIBEXPORT(strtoul) 
    LIBEXPORT(pclose) 
    LIBEXPORT(stricmp) 
    LIBEXPORT(_mono_clear) 
    LIBEXPORT(_go32_dpmi_allocate_dos_memory) 
    LIBEXPORT(__dpmi_set_coprocessor_emulation) 
    LIBEXPORT(siglongjmp) 
    LIBEXPORT(__dpmi_resize_linear_memory) 
    LIBEXPORT(srandom) 
    LIBEXPORT(remque) 
    LIBEXPORT(spawnvp) 
    LIBEXPORT(difftime) 
    LIBEXPORT(atol) 
    LIBEXPORT(getxkey) 
    LIBEXPORT(int86x) 
    LIBEXPORT(__dpmi_get_state_save_restore_addr) 
    LIBEXPORT(fgets) 
    LIBEXPORT(div) 
    LIBEXPORT(fmod) 
    LIBEXPORT(kill) 
    LIBEXPORT(lowvideo) 
    LIBEXPORT(strsep) 
    LIBEXPORT(__dpmi_free_ldt_descriptor) 
    LIBEXPORT(strlen) 
    LIBEXPORT(spawnvpe) 
    LIBEXPORT(errno) 
    LIBEXPORT(gettimeofday) 
    LIBEXPORT(disable) 
    LIBEXPORT(__dpmi_map_device_in_memory_block) 
    LIBEXPORT(read) 
    LIBEXPORT(getcbrk) 
    LIBEXPORT(memmove) 
    LIBEXPORT(getdisk) 
    LIBEXPORT(_crt0_startup_flags) 
    LIBEXPORT(setjmp) 
    LIBEXPORT(ffs) 
    LIBEXPORT(__file_exists) 
    LIBEXPORT(tcflow) 
    LIBEXPORT(setftime) 
    LIBEXPORT(exit) 
    LIBEXPORT(tcflush) 
    LIBEXPORT(findnext) 
    LIBEXPORT(gotoxy) 
    LIBEXPORT(inp) 
    LIBEXPORT(_dosmemputb) 
    LIBEXPORT(__dpmi_get_page_attributes) 
    LIBEXPORT(freopen) 
    LIBEXPORT(mktemp) 
    LIBEXPORT(setenv) 
    LIBEXPORT(fsetpos) 
    LIBEXPORT(geteuid) 
    LIBEXPORT(__FSEXT_set_function) 
    LIBEXPORT(printf) 
    LIBEXPORT(biostime) 
    LIBEXPORT(crlf2nl) 
    LIBEXPORT(__dj_stderr) 
    LIBEXPORT(putw) 
    LIBEXPORT(__dpmi_simulate_real_mode_procedure_retf) 
    LIBEXPORT(_go32_dpmi_allocate_iret_wrapper) 
    LIBEXPORT(_get_dos_version) 
    LIBEXPORT(getopt) 
    LIBEXPORT(delay) 
    LIBEXPORT(umask) 
    LIBEXPORT(sigsetjmp) 
    LIBEXPORT(__dpmi_get_processor_exception_handler_vector) 
    LIBEXPORT(qsort) 
    LIBEXPORT(_go32_dpmi_free_dos_memory) 
    LIBEXPORT(ScreenVisualBell) 
    LIBEXPORT(unlock) 
    LIBEXPORT(getdtablesize) 
    LIBEXPORT(fcntl) 
    LIBEXPORT(getpwuid) 
    LIBEXPORT(getpwent) 
    LIBEXPORT(__dpmi_error) 
    LIBEXPORT(clrscr) 
    LIBEXPORT(spawnlpe) 
    LIBEXPORT(_rename) 
    LIBEXPORT(cosh) 
    LIBEXPORT(ungetch) 
    LIBEXPORT(floor) 
    LIBEXPORT(pathconf) 
    LIBEXPORT(regerror) 
    LIBEXPORT(strstr) 
    LIBEXPORT(__crt0_load_environment_file) 
    LIBEXPORT(_go32_dpmi_lock_data) 
    LIBEXPORT(_lfn_gen_short_fname)
    LIBEXPORT(_djstat_fail_bits) 
    LIBEXPORT(vfprintf)
    LIBEXPORT(_dos_getfileattr)
    LIBEXPORT(_conio_kbhit) 
    LIBEXPORT(system) 
    LIBEXPORT(seekdir) 
    LIBEXPORT(create_sample)
    LIBEXPORT(__dpmi_allocate_specific_ldt_descriptor) 
    LIBEXPORT(__dpmi_get_extended_exception_handler_vector_rm) 
    LIBEXPORT(__dpmi_discard_page_contents) 
    LIBEXPORT(fscanf) 
    LIBEXPORT(close)
    LIBEXPORT(enable) 
    LIBEXPORT(cfsetospeed) 
    LIBEXPORT(__crt0_glob_function) 
    LIBEXPORT(__dpmi_get_segment_limit)

  /* funcallg.inp */

    LIBEXPORT(fix_filename_path)
    LIBEXPORT(_unlock_dpmi_data)
    LIBEXPORT(getr) 
    LIBEXPORT(remove_joystick) 
    LIBEXPORT(_persp_yscale_f) 
    LIBEXPORT(vector_length_f) 
    LIBEXPORT(timer_simulate_retrace) 
    LIBEXPORT(set_pallete) 
    LIBEXPORT(quad3d) 
    LIBEXPORT(calibrate_joystick) 
    LIBEXPORT(voice_set_position) 
    LIBEXPORT(makecol32)
    LIBEXPORT(gfx_modex) 
    LIBEXPORT(fcos) 
    LIBEXPORT(destroy_font) 
    LIBEXPORT(getr16) 
    LIBEXPORT(__linear_vtable15) 
    LIBEXPORT(voice_set_pan) 
    LIBEXPORT(request_scroll) 
    LIBEXPORT(_rgb_r_shift_15) 
    LIBEXPORT(get_vector_rotation_matrix_f) 
    LIBEXPORT(remove_mouse) 
    LIBEXPORT(draw_character) 
    LIBEXPORT(getr24) 
    LIBEXPORT(getb16) 
    LIBEXPORT(remove_timer) 
    LIBEXPORT(reallocate_voice) 
    LIBEXPORT(install_joystick) 
    LIBEXPORT(_textmode) 
    LIBEXPORT(polygon3d) 
    LIBEXPORT(digi_card) 
    LIBEXPORT(remove_sound) 
    LIBEXPORT(mouse_y) 
    LIBEXPORT(deallocate_voice) 
    LIBEXPORT(key) 
    LIBEXPORT(pack_mputl) 
    LIBEXPORT(cross_product) 
    LIBEXPORT(packfile_password) 
    LIBEXPORT(fli_pal_dirty_from) 
    LIBEXPORT(gfx_mode_select) 
    LIBEXPORT(d_icon_proc) 
    LIBEXPORT(itofix) 
    LIBEXPORT(screen) 
    LIBEXPORT(set_projection_viewport) 
    LIBEXPORT(retrace_proc) 
    LIBEXPORT(_blender_alpha) 
    LIBEXPORT(rgb_to_hsv) 
    LIBEXPORT(_rgb_b_shift_16) 
    LIBEXPORT(dialog_message) 
    LIBEXPORT(rectfill) 
    LIBEXPORT(allegro_exit) 
    LIBEXPORT(fixup_datafile) 
    LIBEXPORT(gfx_vga) 
    LIBEXPORT(fix_filename_case) 
    LIBEXPORT(poll_joystick) 
    LIBEXPORT(play_memory_fli) 
    LIBEXPORT(rotate_sprite) 
    LIBEXPORT(cpu_3dnow) 
    LIBEXPORT(_persp_xscale) 
    LIBEXPORT(_fixup_loaded_bitmap) 
    LIBEXPORT(_color_depth) 
    LIBEXPORT(_sort_out_putc) 
    LIBEXPORT(_set_color)
    LIBEXPORT(midi_mpu401) 
    LIBEXPORT(split_modex_screen) 
    LIBEXPORT(_tan_tbl) 
    LIBEXPORT(polygon) 
    LIBEXPORT(apply_matrix)
    LIBEXPORT(poll_modex_scroll) 
    LIBEXPORT(normalize_vector_f) 
    LIBEXPORT(get_mouse_mickeys) 
    LIBEXPORT(get_z_rotate_matrix_f) 
    LIBEXPORT(set_mouse_sprite) 
    LIBEXPORT(install_mouse)
    LIBEXPORT(key_capslock_table) 
    LIBEXPORT(d_check_proc) 
    LIBEXPORT(gui_font_baseline) 
    LIBEXPORT(keyboard_callback) 
    LIBEXPORT(fdiv)
    LIBEXPORT(__modex_vtable) 
    LIBEXPORT(do_line) 
    LIBEXPORT(poll_scroll) 
    LIBEXPORT(pack_getc) 
    LIBEXPORT(gui_mouse_focus) 
    LIBEXPORT(stop_audio_stream) 
    LIBEXPORT(ftan) 
    LIBEXPORT(gfx_driver) 
    LIBEXPORT(d_radio_proc) 
    LIBEXPORT(_color_conv) 
    LIBEXPORT(_joystick_driver_list) 
    LIBEXPORT(_joystick_installed)
    LIBEXPORT(midi_sb_out)
    LIBEXPORT(install_keyboard) 
    LIBEXPORT(get_align_matrix_f) 
    LIBEXPORT(reserve_voices) 
    LIBEXPORT(install_int_ex) 
    LIBEXPORT(get_config_int) 
    LIBEXPORT(gui_bg_color) 
    LIBEXPORT(__linear_vtable32) 
    LIBEXPORT(get_config_hex) 
    LIBEXPORT(voice_set_vibrato) 
    LIBEXPORT(voice_stop_pan_sweep) 
    LIBEXPORT(draw_lit_rle_sprite) 
    LIBEXPORT(dot_product) 
    LIBEXPORT(_gfx_driver_list) 
    LIBEXPORT(set_leds) 
    LIBEXPORT(readkey) 
    LIBEXPORT(create_rgb_table) 
    LIBEXPORT(exists) 
    LIBEXPORT(replace_extension) 
    LIBEXPORT(getg16) 
    LIBEXPORT(cpu_vendor) 
    LIBEXPORT(makecol15) 
    LIBEXPORT(voice_set_priority) 
    LIBEXPORT(detect_midi_driver) 
    LIBEXPORT(getb15) 
    LIBEXPORT(unload_datafile) 
    LIBEXPORT(line) 
    LIBEXPORT(voice_sweep_pan) 
    LIBEXPORT(solid_mode)
    LIBEXPORT(_rgb_b_shift_24) 
    LIBEXPORT(qscale_matrix) 
    LIBEXPORT(makecol16) 
    LIBEXPORT(textout_justify) 
    LIBEXPORT(load_midi) 
    LIBEXPORT(voice_get_volume) 
    LIBEXPORT(get_scaling_matrix_f) 
    LIBEXPORT(rest_callback) 
    LIBEXPORT(gfx_mode_select_ex) 
    LIBEXPORT(calibrate_joystick_throttle_max) 
    LIBEXPORT(get_align_matrix) 
    LIBEXPORT(voice_ramp_volume) 
    LIBEXPORT(_rgb_g_shift_16) 
    LIBEXPORT(normalize_vector) 
    LIBEXPORT(install_int) 
    LIBEXPORT(d_keyboard_proc) 
    LIBEXPORT(joy) 
    LIBEXPORT(key_shifts) 
    LIBEXPORT(midi_none) 
    LIBEXPORT(get_filename) 
    LIBEXPORT(pallete_color) 
    LIBEXPORT(joystick_none) 
    LIBEXPORT(pack_fputs) 
    LIBEXPORT(voice_get_frequency) 
    LIBEXPORT(release_voice) 
    LIBEXPORT(get_rotation_matrix_f) 
    LIBEXPORT(install_sound) 
    LIBEXPORT(getb24) 
    LIBEXPORT(calibrate_joystick_tl) 
    LIBEXPORT(midi_pos) 
    LIBEXPORT(fli_bitmap) 
    LIBEXPORT(identity_matrix_f) 
    LIBEXPORT(save_bitmap) 
    LIBEXPORT(mouse_pos) 
    LIBEXPORT(load_wav) 
    LIBEXPORT(set_pallete_range) 
    LIBEXPORT(fade_out_range) 
    LIBEXPORT(fli_pal_dirty_to) 
    LIBEXPORT(install_timer)
    LIBEXPORT(i_love_bill) 
    LIBEXPORT(stop_midi) 
    LIBEXPORT(cross_product_f) 
    LIBEXPORT(textprintf) 
    LIBEXPORT(free_audio_stream_buffer) 
    LIBEXPORT(simulate_keypress) 
    LIBEXPORT(getg24) 
    LIBEXPORT(_rgb_b_shift_32) 
    LIBEXPORT(load_sample)
    LIBEXPORT(set_mouse_sprite_focus) 
    LIBEXPORT(qscale_matrix_f) 
    LIBEXPORT(voice_sweep_frequency) 
    LIBEXPORT(identity_matrix) 
    LIBEXPORT(calibrate_joystick_hat) 
    LIBEXPORT(fli_bmp_dirty_from)
    LIBEXPORT(joystick_sw) 
    LIBEXPORT(is_same_bitmap) 
    LIBEXPORT(pack_mgetw) 
    LIBEXPORT(getg15) 
    LIBEXPORT(midi_sysex_callback) 
    LIBEXPORT(destroy_rle_sprite) 
    LIBEXPORT(ellipsefill) 
    LIBEXPORT(fade_in_range) 
    LIBEXPORT(midi_resume) 
    LIBEXPORT(_getpixel) 
    LIBEXPORT(play_midi) 
    LIBEXPORT(digi_driver) 
    LIBEXPORT(_rgb_g_shift_32) 
    LIBEXPORT(rect) 
    LIBEXPORT(allocate_voice) 
    LIBEXPORT(polygon3d_f) 
    LIBEXPORT(_persp_yoffset_f) 
    LIBEXPORT(fade_interpolate) 
    LIBEXPORT(set_config_int) 
    LIBEXPORT(fsqrt) 
    LIBEXPORT(cpu_fpu) 
    LIBEXPORT(play_audio_stream) 
    LIBEXPORT(xor_mode) 
    LIBEXPORT(is_memory_bitmap) 
    LIBEXPORT(font) 
    LIBEXPORT(get_audio_stream_buffer) 
    LIBEXPORT(voice_set_volume) 
    LIBEXPORT(unselect_pallete) 
    LIBEXPORT(getg) 
    LIBEXPORT(pack_mputw) 
    LIBEXPORT(bitmap_color_depth) 
    LIBEXPORT(draw_sprite) 
    LIBEXPORT(__linear_vtable16) 
    LIBEXPORT(get_datafile_property) 
    LIBEXPORT(_persp_xoffset_f) 
    LIBEXPORT(voice_set_echo) 
    LIBEXPORT(_trans_blender24) 
    LIBEXPORT(load_pcx) 
    LIBEXPORT(get_z_rotate_matrix) 
    LIBEXPORT(create_bitmap) 
    LIBEXPORT(d_edit_proc) 
    LIBEXPORT(file_select) 
    LIBEXPORT(file_exists) 
    LIBEXPORT(get_scaling_matrix) 
    LIBEXPORT(apply_matrix_f) 
    LIBEXPORT(load_lbm) 
    LIBEXPORT(_persp_yoffset) 
    LIBEXPORT(ftofix) 
    LIBEXPORT(close_fli) 
    LIBEXPORT(remove_keyboard) 
    LIBEXPORT(unload_datafile_object) 
    LIBEXPORT(rgb_map) 
    LIBEXPORT(load_datafile) 
    LIBEXPORT(rotate_scaled_sprite) 
    LIBEXPORT(pack_fgets) 
    LIBEXPORT(do_ellipse)
    LIBEXPORT(load_ibk) 
    LIBEXPORT(adjust_sample) 
    LIBEXPORT(_rgb_scale_5) 
    LIBEXPORT(midi_awe32)
    LIBEXPORT(joystick_standard) 
    LIBEXPORT(qtranslate_matrix_f) 
    LIBEXPORT(fade_from_range) 
    LIBEXPORT(mouse_x) 
    LIBEXPORT(fadd) 
    LIBEXPORT(cpu_family) 
    LIBEXPORT(getg32) 
    LIBEXPORT(pack_fwrite)
    LIBEXPORT(key_shift_table)
    LIBEXPORT(lock_midi) 
    LIBEXPORT(_rgb_r_shift_32) 
    LIBEXPORT(set_config_data) 
    LIBEXPORT(dot_product_f) 
    LIBEXPORT(load_midi_patches) 
    LIBEXPORT(digi_none)
    LIBEXPORT(key_ascii_table) 
    LIBEXPORT(fmul) 
    LIBEXPORT(draw_sprite_h_flip) 
    LIBEXPORT(set_config_file) 
    LIBEXPORT(fade_in) 
    LIBEXPORT(detect_digi_driver) 
    LIBEXPORT(allegro_id) 
    LIBEXPORT(fli_pallete)
    LIBEXPORT(key_control_table)
    LIBEXPORT(joystick_sp2)
    LIBEXPORT(get_config_string) 
    LIBEXPORT(centre_dialog)
    LIBEXPORT(windows_version) 
    LIBEXPORT(pack_fclose_chunk) 
    LIBEXPORT(for_each_file) 
    LIBEXPORT(fsin) 
    LIBEXPORT(voice_set_frequency) 
    LIBEXPORT(_midi_driver_list) 
    LIBEXPORT(getr8) 
    LIBEXPORT(cpu_model) 
    LIBEXPORT(load_joystick_data) 
    LIBEXPORT(is_linear_bitmap) 
    LIBEXPORT(circlefill) 
    LIBEXPORT(set_mouse_range) 
    LIBEXPORT(_rgb_g_shift_15) 
    LIBEXPORT(facos) 
    LIBEXPORT(triangle) 
    LIBEXPORT(select_pallete) 
    LIBEXPORT(midi_out) 
    LIBEXPORT(draw_rle_sprite) 
    LIBEXPORT(stop_sample) 
    LIBEXPORT(pack_mgetl) 
    LIBEXPORT(play_sample) 
    LIBEXPORT(create_bitmap_ex) 
    LIBEXPORT(textprintf_centre) 
    LIBEXPORT(gui_fg_color) 
    LIBEXPORT(show_mouse) 
    LIBEXPORT(_rgb_scale_6) 
    LIBEXPORT(draw_compiled_sprite) 
    LIBEXPORT(getr15) 
    LIBEXPORT(d_list_proc) 
    LIBEXPORT(hline) 
    LIBEXPORT(set_trans_blender) 
    LIBEXPORT(save_pcx) 
    LIBEXPORT(get_x_rotate_matrix_f) 
    LIBEXPORT(create_sub_bitmap) 
    LIBEXPORT(override_config_file) 
    LIBEXPORT(gui_mg_color) 
    LIBEXPORT(d_ctext_proc) 
    LIBEXPORT(fixtoi) 
    LIBEXPORT(set_config_string) 
    LIBEXPORT(reset_fli_variables) 
    LIBEXPORT(os_type) 
    LIBEXPORT(scroll_screen) 
    LIBEXPORT(calibrate_joystick_br) 
    LIBEXPORT(position_mouse) 
    LIBEXPORT(fix_filename_slashes) 
    LIBEXPORT(getb32) 
    LIBEXPORT(pack_igetw) 
    LIBEXPORT(gui_strlen) 
    LIBEXPORT(d_slider_proc) 
    LIBEXPORT(request_modex_scroll)
    LIBEXPORT(joystick_sp1) 
    LIBEXPORT(vline) 
    LIBEXPORT(lock_sample) 
    LIBEXPORT(_color_load_depth) 
    LIBEXPORT(set_config_float) 
    LIBEXPORT(get_color) 
    LIBEXPORT(is_planar_bitmap) 
    LIBEXPORT(_cos_tbl) 
    LIBEXPORT(active_dialog) 
    LIBEXPORT(quad3d_f) 
    LIBEXPORT(masked_blit) 
    LIBEXPORT(is_sub_bitmap) 
    LIBEXPORT(textout_centre) 
    LIBEXPORT(getr_depth) 
    LIBEXPORT(get_y_rotate_matrix_f) 
    LIBEXPORT(freeze_mouse_flag) 
    LIBEXPORT(midi_card) 
    LIBEXPORT(get_transformation_matrix_f) 
    LIBEXPORT(blit) 
    LIBEXPORT(create_light_table) 
    LIBEXPORT(putpixel) 
    LIBEXPORT(voice_stop_frequency_sweep) 
    LIBEXPORT(generate_optimized_palette) 
    LIBEXPORT(save_bmp) 
    LIBEXPORT(floodfill) 
    LIBEXPORT(save_joystick_data) 
    LIBEXPORT(clear_keybuf) 
    LIBEXPORT(makecol24) 
    LIBEXPORT(text_height) 
    LIBEXPORT(desktop_pallete) 
    LIBEXPORT(cpu_cpuid) 
    LIBEXPORT(midi_meta_callback) 
    LIBEXPORT(getg_depth) 
    LIBEXPORT(bmp_read_line) 
    LIBEXPORT(voice_set_playmode) 
    LIBEXPORT(bmp_write_line) 
    LIBEXPORT(load_tga) 
    LIBEXPORT(fli_frame) 
    LIBEXPORT(register_datafile_object) 
    LIBEXPORT(circle) 
    LIBEXPORT(_persp_yscale) 
    LIBEXPORT(_acos_tbl) 
    LIBEXPORT(get_vector_rotation_matrix) 
    LIBEXPORT(load_bmp) 
    LIBEXPORT(set_clip)
    LIBEXPORT(drawing_mode) 
    LIBEXPORT(midi_driver) 
    LIBEXPORT(popup_dialog) 
    LIBEXPORT(gfx_capabilities) 
    LIBEXPORT(get_transformation_matrix) 
    LIBEXPORT(calc_spline) 
    LIBEXPORT(retrace_count)
    LIBEXPORT(gfx_vesa_2l) 
    LIBEXPORT(draw_sprite_vh_flip) 
    LIBEXPORT(destroy_compiled_sprite) 
    LIBEXPORT(d_shadow_box_proc) 
    LIBEXPORT(fli_timer) 
    LIBEXPORT(fasin) 
    LIBEXPORT(get_translation_matrix_f) 
    LIBEXPORT(voice_get_position) 
    LIBEXPORT(alert) 
    LIBEXPORT(text_mode) 
    LIBEXPORT(mouse_b) 
    LIBEXPORT(get_config_float) 
    LIBEXPORT(pack_fclose) 
    LIBEXPORT(_rgb_r_shift_16) 
    LIBEXPORT(initialise_joystick) 
    LIBEXPORT(voice_stop_volumeramp) 
    LIBEXPORT(d_box_proc) 
    LIBEXPORT(request_video_bitmap) 
    LIBEXPORT(pack_fopen_chunk) 
    LIBEXPORT(destroy_bitmap) 
    LIBEXPORT(draw_lit_sprite) 
    LIBEXPORT(_trans_blender16) 
    LIBEXPORT(black_rgb) 
    LIBEXPORT(register_bitmap_file_type) 
    LIBEXPORT(stretch_sprite) 
    LIBEXPORT(_vtable_list) 
    LIBEXPORT(shutdown_dialog) 
    LIBEXPORT(midi_msg_callback) 
    LIBEXPORT(delete_file) 
    LIBEXPORT(cpu_mmx) 
    LIBEXPORT(textout) 
    LIBEXPORT(bitmap_mask_color) 
    LIBEXPORT(fixtof) 
    LIBEXPORT(num_joysticks) 
    LIBEXPORT(_persp_xscale_f) 
    LIBEXPORT(set_dialog_color) 
    LIBEXPORT(text_length) 
    LIBEXPORT(triangle3d_f) 
    LIBEXPORT(set_color_depth) 
    LIBEXPORT(black_pallete) 
    LIBEXPORT(override_config_data) 
    LIBEXPORT(getpixel) 
    LIBEXPORT(triangle3d) 
    LIBEXPORT(_persp_xoffset) 
    LIBEXPORT(keypressed) 
    LIBEXPORT(get_extension) 
    LIBEXPORT(polygon_z_normal_f) 
    LIBEXPORT(destroy_sample) 
    LIBEXPORT(midi_loop_end) 
    LIBEXPORT(active_menu) 
    LIBEXPORT(do_circle) 
    LIBEXPORT(_digi_driver_list) 
    LIBEXPORT(fli_bmp_dirty_to) 
    LIBEXPORT(check_cpu) 
    LIBEXPORT(find_dialog_focus)
    LIBEXPORT(gfx_vesa_2b) 
    LIBEXPORT(bestfit_color) 
    LIBEXPORT(joy_type) 
    LIBEXPORT(show_video_bitmap) 
    LIBEXPORT(joystick_driver) 
    LIBEXPORT(__linear_vtable8) 
    LIBEXPORT(ellipse) 
    LIBEXPORT(voice_set_tremolo) 
    LIBEXPORT(set_mouse_speed) 
    LIBEXPORT(draw_trans_rle_sprite) 
    LIBEXPORT(calibrate_joystick_name) 
    LIBEXPORT(color_map) 
    LIBEXPORT(append_filename) 
    LIBEXPORT(alert3) 
    LIBEXPORT(set_gfx_mode) 
    LIBEXPORT(get_camera_matrix) 
    LIBEXPORT(save_tga) 
    LIBEXPORT(_rgb_b_shift_15) 
    LIBEXPORT(open_fli) 
    LIBEXPORT(getb8) 
    LIBEXPORT(getb) 
    LIBEXPORT(get_rotation_matrix) 
    LIBEXPORT(pack_fread) 
    LIBEXPORT(push_config_state) 
    LIBEXPORT(get_x_rotate_matrix) 
    LIBEXPORT(d_clear_proc) 
    LIBEXPORT(get_translation_matrix) 
    LIBEXPORT(persp_project) 
    LIBEXPORT(play_looped_midi) 
    LIBEXPORT(remove_int) 
    LIBEXPORT(set_blender_mode) 
    LIBEXPORT(set_volume) 
    LIBEXPORT(get_config_argv) 
    LIBEXPORT(voice_get_pan) 
    LIBEXPORT(pack_fseek) 
    LIBEXPORT(file_time) 
    LIBEXPORT(fatan) 
    LIBEXPORT(do_dialog) 
    LIBEXPORT(lock_bitmap) 
    LIBEXPORT(create_color_table) 
    LIBEXPORT(fade_out) 
    LIBEXPORT(load_voc) 
    LIBEXPORT(load_datafile_object) 
    LIBEXPORT(rest) 
    LIBEXPORT(pack_iputw) 
    LIBEXPORT(three_finger_flag) 
    LIBEXPORT(set_config_hex) 
    LIBEXPORT(getg8) 
    LIBEXPORT(update_dialog) 
    LIBEXPORT(midi_loop_start) 
    LIBEXPORT(get_camera_matrix_f) 
    LIBEXPORT(fatan2) 
    LIBEXPORT(_rgb_r_shift_24) 
    LIBEXPORT(makecol) 
    LIBEXPORT(create_trans_table)
    LIBEXPORT(open_memory_fli) 
    LIBEXPORT(_putpixel) 
    LIBEXPORT(midi_digmid) 
    LIBEXPORT(draw_trans_sprite) 
    LIBEXPORT(load_bitmap) 
    LIBEXPORT(spline) 
    LIBEXPORT(is_screen_bitmap) 
    LIBEXPORT(d_button_proc) 
    LIBEXPORT(pop_config_state) 
    LIBEXPORT(gfx_vesa_1) 
    LIBEXPORT(d_textbox_proc) 
    LIBEXPORT(voice_stop) 
    LIBEXPORT(voice_start) 
    LIBEXPORT(getr32) 
    LIBEXPORT(stretch_blit) 
    LIBEXPORT(windows_sub_version) 
    LIBEXPORT(d_text_proc) 
    LIBEXPORT(_sort_out_getc)
    LIBEXPORT(get_pallete_range) 
    LIBEXPORT(key_led_flag) 
    LIBEXPORT(_rgb_g_shift_24) 
    LIBEXPORT(midi_seek) 
    LIBEXPORT(d_menu_proc)
    LIBEXPORT(joystick_sp3) 
    LIBEXPORT(qtranslate_matrix) 
    LIBEXPORT(gfx_xtended) 
    LIBEXPORT(next_fli_frame) 
    LIBEXPORT(clear_to_color) 
    LIBEXPORT(makecol8) 
    LIBEXPORT(clear) 
    LIBEXPORT(calibrate_joystick_throttle_min) 
    LIBEXPORT(get_rle_sprite) 
    LIBEXPORT(destroy_midi) 
    LIBEXPORT(persp_project_f) 
    LIBEXPORT(set_color) 
    LIBEXPORT(set_color_conversion) 
    LIBEXPORT(install_keyboard_hooks) 
    LIBEXPORT(allegro_error) 
    LIBEXPORT(pack_igetl) 
    LIBEXPORT(init_dialog) 
    LIBEXPORT(hsv_to_rgb) 
    LIBEXPORT(pack_putc) 
    LIBEXPORT(d_bitmap_proc) 
    LIBEXPORT(vsync) 
    LIBEXPORT(play_fli) 
    LIBEXPORT(midi_pause) 
    LIBEXPORT(pack_fopen) 
    LIBEXPORT(gui_textout) 
    LIBEXPORT(pack_iputl) 
    LIBEXPORT(get_pallete) 
    LIBEXPORT(generate_332_palette) 
    LIBEXPORT(voice_check) 
    LIBEXPORT(_trans_blender15) 
    LIBEXPORT(getb_depth) 
    LIBEXPORT(makecol_depth)
    LIBEXPORT(gfx_vbeaf) 
    LIBEXPORT(mouse_callback) 
    LIBEXPORT(replace_filename) 
    LIBEXPORT(get_y_rotate_matrix) 
    LIBEXPORT(fade_from) 
    LIBEXPORT(__linear_vtable24) 
    LIBEXPORT(do_menu) 
    LIBEXPORT(draw_sprite_v_flip) 
    LIBEXPORT(broadcast_dialog_message) 
    LIBEXPORT(file_size)
    LIBEXPORT(key_altgr_table)
    LIBEXPORT(put_backslash) 
    LIBEXPORT(get_compiled_sprite) 
    LIBEXPORT(matrix_mul_f) 
    LIBEXPORT(fsub)
    LIBEXPORT(joystick_gpro) 
    LIBEXPORT(strtoend)
    LIBEXPORT(scancode_to_ascii)
    LIBEXPORT(unscare_mouse)
    LIBEXPORT(scare_mouse)
#endif /* not __RSXNT__ */
  LIBEXPORT_END
DLXUSE_END


/* the main program object */
l_int  task_tick        = 20;

/* the main program object */
t_object   program;
/* list of objects that use OB_OF_STILLPROCESS option flag */
t_list     stillprocess_list;

/* drivers, the main object for drivers, such as keyboard, mouse, ... */
p_drivers  drivers = NULL;

/* destkop, the main object of visible objects */
p_view     desktop = NULL;

/* define if clock-cursor must be included */
l_int      clock_including = 0;

/* contains queue of events ( if you use put_event function ) */
static     t_event event_panding;

/* saves the last time of calling program_translate_event function */
static     l_big   event_oldtimer;

/* insert event to event queue */
void program_put_event ( t_object *o, t_event *event )
{
  event_panding = (*event);
};


/*
   insert object (s) to stillprocess objects
   return 1, if all is OK
   return 0, on error
*/

int  program_put_into_stillprocess ( p_object o, p_object s )
{
  if ( stillprocess_list.insert(&stillprocess_list, s) != -1 )
    return 1;
  return 0;
};

/*
  clear object (s) from stillprocess objects
  return 1, if everything is OK
  return 0, on error

*/
int  program_clear_from_stillprocess ( p_object o, p_object s )
{
  p_item p = stillprocess_list.find_rec(&stillprocess_list, s);
  if ( p ) {
      stillprocess_list.remove_item(&stillprocess_list, p);
      return 1;
  };
  return 0;
};

/*
  main get_event function.
  - get last event from event's queue
*/
void program_get_event ( t_object *o, t_event *event )
{
  STOP_PROCESS();
  clear_event(event);
  /* save the last time of calling */
  event_oldtimer = time_get_mili();
  if ( event_panding.type != EV_NOTHING ) { /* something in queue */
    (*event) = event_panding; /* new event will contain last from queue */
    clear_event(&event_panding); /* clear queue */
  } else { /* nothing in queue */
    if ( drivers ) { /* drivers exists */
        /* call each object in drivers object */
        ((p_object)drivers)->translate_event((p_object)drivers, event);
    };
  };
  START_PROCESS();
  /* call each stillprocess object */
  program_call_each_stillprocess(&stillprocess_list);
};


/* main translate event function */
void program_translate_event ( t_object *o, t_event *event )
{
  /* save the last time of calling */
  event_timer = time_get_mili();
  /* event type is message */
  if ( event->type == EV_MESSAGE ) {
    /* event message is MSG_QUIT */
    if ( event->message == MSG_QUIT ) {
      clear_event(event);
      /* set end_state of program to MSG_QUIT
         this is used in function obj_execute
      */
      o->end_state = MSG_QUIT;
    };
  };
  /* call each object in program hierarchy */
  obj_translate_event(o, event);
  /* if keyboard was pressed and the keycode of the key is ALT+X */
  if ( (event->type == EV_KEYBOARD) && (OBJECT(keyb)->state & KB_SF_KEYDOWN)
       && KEYPRESSED(TO_ALT(KB_X)) ) {
    clear_event(event);
    /* set event by these arguments */
    set_event(event, EV_MESSAGE, MSG_QUIT, o);
    /* put event to a queue */
    o->put_event(o, event);
    clear_event(event);
  };
  /* if keyboard was pressed and the keycode of the key is ALT+D */
  if ( (event->type == EV_KEYBOARD) && (OBJECT(keyb)->state & KB_SF_KEYDOWN)
       && KEYPRESSED(TO_ALT(KB_D)) ) {
        p_object v = NULL;
        /* find the selected object in the desktop */
        if ( OBJECT(desktop)->prefer )
           /* search for the next visible, selectable, enable object in the desktop */
           v = OBJECT(desktop)->prefer->find_match_view(OBJECT(desktop)->prefer,
                     OB_SF_VISIBLE, OB_OF_SELECTABLE+OB_OF_ENABLE, false);
  	     clear_event(event);
        /* if found some, select it */
        if ( v ) v->select(v);
  };
};


/*
  done program
  return true, if succesfull, otherwise false
*/
l_bool  program_done ( t_object *o )
{
  /* done each object in the program hierarchy */
  if ( !obj_done(o) ) return false;
  /* free list of stillprocess objects */
  stillprocess_list.done(&stillprocess_list);
  if ( drivers )
        /* done drivers = mouse, keyboard, screen, fonts, etc... */
        OBJECT(drivers)->done(OBJECT(drivers));
  /* free list of extension runners */
  done_ext_runners();
  drv_done(); /* done drives */
  filter_remove_all(); /* remove all filters from the array */
  return true;
};

void minimal_app();

t_object *program_init ( t_object *o )
{
#ifdef WANT_FLASHY_STUFF
  t_rect r;
  p_window win;
#endif

  if ( !o ) return NULL;
  obj_init(o);
  DEBUG_printf(" - program_init : init_registry()\n");
  init_registry();
  DEBUG_printf(" - program_init : Registry initialized correctly to file %s\n", registry_file);
  init_ext_runners(); /* define extension runners */
  drv_init(); /* init drives */
  drivers = drivers_init(_malloc(sizeof(t_drivers)));
  DEBUG_printf(" - program_init : window_ini_palette()\n");
  window_ini_palette(); /* set window palette */
  clear_event(&event_panding);
  DEBUG_printf(" - program_init : view_ini()\n");
  view_ini(); /* all view initalizations */
  DEBUG_printf(" - program_init : list_init(&stillprocess_list, NULL, 0)\n");
  list_init(&stillprocess_list, NULL, 0);
  o->get_event = &program_get_event;
  o->put_event = &program_put_event;
  o->done = &program_done;
  o->translate_event = &program_translate_event;
  o->put_into_stillprocess = &program_put_into_stillprocess;
  o->clear_from_stillprocess = &program_clear_from_stillprocess;
  o->set_state(o, OB_SF_FOCUSED, true);
  o->set_options(o, OB_OF_TOPSELECT+OB_OF_NOTACTIVATE, true);
  DEBUG_printf(" - program_init : clock_including = getininum_fromfile(...)\n");
  /* get info from INI file, if clock-cursor must be included */
  clock_including = get_key_integer("current/seal/optical/clock_including");
  DEBUG_printf(" - program_init : desktop = view_init(...)\n");
  if ( !desktop ) /* install desktop */
     desktop = view_init(_malloc(sizeof(t_view)), rect_assign(0, 0, screen_width-1, screen_height-1));
  if ( desktop ) { /* set desktop */
    /* set desktop background */
    l_text fname = get_key("global/seal/desktop/wallpaper");
    l_int style = get_key_integer("global/seal/desktop/placement");
    desktop->brush.color=makecol(65,83,137); /* insert a default */
    iffound(desktop->brush.color , registry_color("desktop_background"));
    /* exist name of wall_paper */
    if ( fname ) {
      /* load image for wall_paper */
      desktop->brush.background = load_image(fname);
      /* if wall_paper exist */
      if ( desktop->brush.background ) {
        desktop->brush.state     |= BRUSH_SELFIMG;
        if ( style == 1 ) /* style is stretch */
          desktop->brush.state     |= BRUSH_STRETCH;
        else
        if ( style == 2 ) /* style is center */
          desktop->brush.state     |= BRUSH_CENTER;
      };
    _free(fname);
    };
    OBJECT(desktop)->set_options(OBJECT(desktop), OB_OF_TOPSELECT+OB_OF_NOTACTIVATE, true);
    /* insert desktop to program */
    o->insert(o, OBJECT(desktop));
  } else {
   DEBUG_printf("Can't get memory or view_init desktop (width=%d height=%d) - No desktop, ouch! Stopping right now.\n",screen_width,screen_height);
   exit(1);
  }
  DEBUG_printf(" - program_init : load registry defined applications\n");

#ifdef WANT_FLASHY_STUFF
  r = rect_assign(0, 0, 404, 130);
  win = win_init(_malloc(sizeof(t_window)), r, "Bad Seal initialization", 0);
#endif
  if(1) {
    l_text filedlx;
    p_registry_search inf = (p_registry_search) _malloc(sizeof(t_registry_search));
#ifdef WANT_FLASHY_STUFF
    l_color c=NULL;
    BITMAP *startimg = load_image("startwin.bmp");
    if(win) {
     VIEW(win)->align |= TX_ALIGN_CENTER;
     VIEW(win)->drag_mode = 0;
     OBJECT(desktop)->insert(OBJECT(desktop), OBJECT(win));
     c = VIEW(win)->get_color(VIEW(win), 0);
    }
#endif
    DLXImport(_LIBEXPORTTABLE);
    if (reg_find_first("system/startup/load", inf)) {
     do {
#ifdef WANT_FLASHY_STUFF
       t_rect  r;
       t_point p;
       BITMAP *out=NULL;
       if(win){
        r = VIEW(win)->get_local_extent(VIEW(win));
        out = VIEW(win)->begin_paint(VIEW(win), &p, r);
       }
#endif
      filedlx = get_key(inf->name);
      if(filedlx) {
#ifdef WANT_FLASHY_STUFF
       if (win && out && startimg ) {
        l_text s=NULL;
        l_text s2 = key_in_path(inf->name, "title");
        if(s2) {
         s=get_key(s2); // no need for if_exists, returns null if not found in any case
         if(!s) s=get_key(inf->name);
         if(s &&  (out->w > (r.a.x+p.x+404)) && (out->h > (r.a.y+p.y+130)) ){
          blit(startimg, out, 0, 0, r.a.x+p.x+2, r.a.y+p.y+20, 400, 50);
          rectfill(out, r.a.x+p.x+10, r.a.y+p.y+80, r.b.x+p.x-3, r.b.y+p.y-5, c);
          textout_draw_rect(out, VIEW(win)->font, "Loading", -1, r.a.x+p.x+10, r.a.y+p.y+80,
                           r.b.x+p.x, r.b.y+p.y, TX_ALIGN_LEFT|TX_ALIGN_TOP, COLOR(0), c, 0);
          textout_draw_rect(out, VIEW(win)->font, s, -1, r.a.x+p.x+10, r.a.y+p.y+100,
                           r.b.x+p.x, r.b.y+p.y, TX_ALIGN_LEFT|TX_ALIGN_TOP, COLOR(0), c, 0);
          VIEW(win)->end_of_paint(VIEW(win), r);
         }
         if(s)_free(s);
         _free(s2);
        } 
       }
#endif
       run_file(filedlx);
       _free(filedlx);
      }
     } while (reg_find_next(inf));
#ifdef WANT_FLASHY_STUFF
 if(startimg)destroy_bitmap(startimg);
 if(win)OBJECT(win)->done(OBJECT(win));
#endif
    }
    else 
    {
     DEBUG_printf(" - program_init : no applications - loading mini_application instead.\n");
     minimal_app();    
     /* no system/startup/load section (or no ini file)? At least let him do something! */
    }
    _free(inf);
  };
  DEBUG_printf(" - program_init : mouse_set_cursor_id(CUR_ARROW)\n");
  /* set mouse cursor to standard arrow */
  mouse_set_cursor_id(CUR_ARROW);
  DEBUG_printf(" - program_init : return (o) \n");
  return o;
};


/* jdh flagrantly stolen from dialogs.c & run.c */
typedef struct t_miniapp *p_miniapp;
typedef struct t_miniapp {
  struct t_view  obclass;
  void (*ins_char)( p_miniapp o, l_int pos, l_char chr );
  void (*del_char)( p_miniapp o, l_int pos );
  l_char         text[256];
} t_miniapp;
#define MINIAPP(o) ((p_miniapp)(o))
void  miniapp_draw ( p_view o )
{
  t_rect  r = o->get_local_extent(o);
  t_point p = o->get_global_point(o, r.a);
  BITMAP *out = o->begin_paint(o, &p, r);
  if ( out ) {
    rectfill(out, p.x+r.a.x, p.y+r.a.y, p.x+r.b.x, p.y+r.b.y, o->get_color(o, 0));
    button(out, p.x+r.a.x, p.y+r.a.y, p.x+r.b.x, p.y+r.b.y, o->get_color(o, 3), o->get_color(o, 4));
    button(out, p.x+r.a.x+1, p.y+r.a.y+1, p.x+r.b.x-1, p.y+r.b.y-1, o->get_color(o, 8), o->get_color(o, 5));
    textout_draw_rect(out, o->font, MINIAPP(o)->text, strlen(MINIAPP(o)->text),
                        r.a.x+p.x+3, r.a.y+p.y, r.b.x+p.x+3, r.b.y+p.y,
                        TX_ALIGN_LEFT,o->get_color(o, 2), TX_NOCOLOR, 0);
  };
  o->end_of_paint(o, r);
};
void  miniapp_ins_char ( p_miniapp o, l_int pos, l_char chr )
{
 if( (pos >= 0) && (pos < 256) ){
  o->text[pos]=chr;
  o->text[pos+1]=0;
 }
};
void  miniapp_del_char ( p_miniapp o, l_int pos )
{
 if( (pos >= 0) && (pos < 256) ){
  o->text[pos]=0;
 }
};

/* miniapp functions */
void  miniapp_translate_event ( p_object o, t_event *event )
{
/* <IRONY> Yeesh, what a cheapskate, no cursors, no selections, no mouse, barely even a backspace! </IRONY>
   C'mon gimme a break - there's nowt on screen for him to do anything else with.
*/
 RETVIEW(o, event);
 view_translate_event(o, event); /* old translate_event function */
 if ( o->is_state(o, OB_SF_FOCUSED) ) /* keyboard events */
  if ( event->type & EV_KEYBOARD ) 
   if ( OBJECT(keyb)->state & KB_SF_KEYDOWN )
    switch ( KEY_TO(keyb->code) ) {
      case KB_DEL :   /* key DEL key BACKSPACE pressed */
      case KB_BACKSPACE : {
        MINIAPP(o)->del_char(MINIAPP(o),strlen(MINIAPP(o)->text)-1);
        VIEW(o)->draw(VIEW(o));
        clear_event(event);
      }; break;
      case KB_ENTER: {
       run_file(MINIAPP(o)->text);
       strcpy(MINIAPP(o)->text, "");
       VIEW(o)->draw(VIEW(o));
       clear_event(event);
       }; break;
      default :
        if ( ((l_byte)TO_CHAR(keyb->code) >= 32) &&
             ((l_byte)TO_CHAR(keyb->code) <= 255) ) {
             MINIAPP(o)->ins_char(MINIAPP(o), strlen(MINIAPP(o)->text), keyb->code);
             VIEW(o)->draw(VIEW(o));
             clear_event(event);
        }; break;
    };
};

l_color  pal_miniapp[] = {CO_WHITE, CO_LIGHTGRAY, CO_BLACK, CO_BLUE, CO_WHITE, CO_LIGHTGRAY, CO_WHITE, 
 CO_DARKGRAY, CO_BLACK, CO_NOCOLOR};

p_miniapp miniapp_init(p_miniapp o,t_rect r,char *text){
  /* a minimal application uses no libraries, allows you to at least type in a filename and run it */
  clear_type(o, sizeof(t_miniapp));
  view_init(VIEW(o), r);
  /* view's functions */
  VIEW(o)->draw = &miniapp_draw;
  /* miniapp's declarations */
  OBJECT(o)->translate_event = &miniapp_translate_event;
  o->ins_char = &miniapp_ins_char;
  o->del_char = &miniapp_del_char;
  strcpy(o->text, text);
  /* function calling */
  OBJECT(o)->set_options(OBJECT(o), OB_OF_SELECTABLE, true);
  OBJECT(o)->set_options(OBJECT(o), OB_OF_ENABLE, true);
  VIEW(o)->set_palette(VIEW(o), pal_miniapp);
  VIEW(o)->brush.color = VIEW(o)->get_color(VIEW(o), 0);
  return o;
}


void minimal_app(){
  /* Yes. The dreaded c:> prompt rises from the grave to haunt seal! */
  p_miniapp o;
  t_rect   r;
  p_window win;
  int i;
  for(i=0; i<10 ; i++)pal_miniapp[i]=COLOR(pal_miniapp[i]);
  r   = rect_assign(100, 100, 300, 300);
  win = win_init(_malloc(sizeof(t_window)), r, "Run", 0);
  r = rect_assign(10, 20, 240, 240);
  o=miniapp_init(malloc(sizeof(t_miniapp)),r,"");
  if (o)   OBJECT(win)->insert(OBJECT(win),OBJECT(o));
  if (win) OBJECT(desktop)->insert(OBJECT(desktop), OBJECT(win));
}

/*
  call each object's func_callback, that is placed in stillprocess list.
*/
void program_call_each_stillprocess ( p_list o )
{
  if ( o ) {
    p_item v = o->first(o); /* find first object */
    p_item f = v;
    if ( v )
      do {
        if ( v->rec &&
             /* difference between last calling and current is GE to process_tick of object */
             (time_diff_mili(OBJECT(v->rec)->process_time) >= OBJECT(v->rec)->process_tick )) {
          /* set current time */
          OBJECT(v->rec)->process_time = time_get_mili();
          /* call function of the object */
          OBJECT(v->rec)->func_callback(OBJECT(v->rec));
        };
        v = v->next;
      } while ( v != f );
  };
};

#ifdef __CLOCK_INCLUDED__
/*
static void  aclock2 ( void )
{
  if ( !mouse->is_block(mouse) ) {
   if ( _time_diff_mili(event_oldtimer) >= 500 )
      mouse_set_cursor_id(CUR_CLOCK);
   else if ( mouse->get_cursor(mouse) == CURSOR_GETSYSTEM(CUR_CLOCK) )
      mouse_set_cursor_id(CUR_ARROW);
 }
};
*/
#endif

/* main process interrupt */
int program_int ( void ) {
  /* if not pause in multitasking */
  if ( go_process ) {
    /* difference is greater then task_tick */
    if ( _time_diff_mili(event_timer) >= task_tick ) {
      /* get current time */
      event_timer = time_get_mili();
      /* get events */
      program.get_event(&program, &event_main);
      /* one loop program_translate_event */
      program.translate_event(&program, &event_main);
    };
  };
  return 1;
};
/* end of interrupt */

/* first screen you ever see */
static void  xx_screen_shot ( void )
{
    /* load image */
    BITMAP *b = load_image("startup.bmp");
    if ( b ) { /* image exist */
        /* calculate center of screen  for image */
        l_rect x = (screen_width  - IMAGE_WIDTH(b))/2;
        l_rect y = (screen_height - IMAGE_HEIGHT(b))/2;
        mouse->hide(mouse);
        /* draw image */
        clear_to_color(screen, makecol(51, 51, 31));
        draw_sprite(screen, b, x, y);
        mouse->show(mouse);
        mouse_set_cursor_id(CUR_CLOCK);
        /* destroy image */
        destroy_bitmap(b);
   };
};


#include <stdio.h>
#include <allegro.h>
#include <allegro/aintern.h>
extern int _packfile_type;

#define N            4096           /* 4k buffers for LZ compression */
#define F            18             /* upper limit for LZ match length */
typedef struct UNPACK_DATA          /* for reading LZ files */
{
   int state;                       /* where have we got to? */
   int i, j, k, r, c;
   int flags;
   unsigned char text_buf[N+F-1];   /* ring buffer, with F-1 extra bytes for string comparison */
} UNPACK_DATA;

/* my_load_datafile:
 * Loads an entire data file into memory, and returns a pointer to it. 
 * On error, sets errno and returns NULL.
 * The following was divined by perusing the runes in the allegro library datafile.c and file.c 
 * It allows loading from an open FILE * rather than a filename.  FILE *f assumed to be positioned at the
 * appended data upon entry, it is still open on exit.
 * If future versions of allegro change the load_datafile routine, this will have to be modified to
 * account for it - good luck. 
 */
void *my_load_datafile(FILE *fp,long len)
{
   DATAFILE *dat=NULL;
   int type;
   PACKFILE *f;
   _packfile_type = DAT_MAGIC;
   errno = *allegro_errno = 0;
   if ((f = malloc(sizeof(PACKFILE))) == NULL) {
      *allegro_errno = ENOMEM;
      return NULL;
   }
   f->buf_pos = f->buf;
   f->flags = 0;
   f->flags &= ~PACKFILE_FLAG_WRITE;
   f->buf_size = 0;
   f->filename = NULL;
   f->passdata = NULL;
   f->passpos = NULL;
   f->parent = NULL;
   f->pack_data = NULL;
   f->passpos = f->passdata;
   f->todo=len;
   f->hndl=fileno(fp); // _al_open (somehow ends up) same as open() so we can use the file number
   /* that is essentially what pack_fopen does for a readonly file */

   lseek(f->hndl,ftell(fp),SEEK_SET); 
   /* ensures the unbuffered file position is set correctly so allegro internal buffering works */

   /* dlxinsd simply appends standard allegro data files onto the file after its marker,
      we have to figure out the first bit ourselves because this is what read_datafile does prior to 
      calling load_file_object()
   */
   type = pack_mgetl(f);

   if (type == F_PACK_MAGIC) {  /* read a packed file */
      PACKFILE *f1;
      UNPACK_DATA *dat1 = malloc(sizeof(UNPACK_DATA));
      int c;
      if (!dat1) {
            *allegro_errno = ENOMEM;
            free(f);
            return NULL;
      }
      if ((f1 = malloc(sizeof(PACKFILE))) == NULL) {
         *allegro_errno = ENOMEM;
         free(f);
         return NULL;
      }
      f1->buf_pos = f1->buf;
      f1->flags = 0;
      f1->flags &= ~PACKFILE_FLAG_WRITE;
      f1->buf_size = 0;
      f1->filename = NULL;
      f1->passdata = NULL;
      f1->passpos = NULL;
      f1->parent = f;
      f1->pack_data = NULL;
      f1->passpos = f->passdata;
      f1->hndl=fileno(fp); // _al_open (somehow ends up) same as open() so we can use the file number
      for (c=0; c < N - F; c++)
               dat1->text_buf[c] = 0; 
      dat1->state = 0;
      f1->todo = LONG_MAX;
      f1->pack_data = (char *)dat1;
      f1->flags |= PACKFILE_FLAG_PACK;
      type = pack_mgetl(f1);
      if ( type != DAT_MAGIC ) {
            if (*allegro_errno == 0)
               *allegro_errno = EDOM;
            free(f1);free(f);
            return NULL;
      }
      dat = load_file_object(f1, LONG_MAX); 
      if (f1->passdata)
	  free(f1->passdata);
      if (f1->pack_data)
	  free(f1->pack_data);
      free(f1);
   } else {
      type = pack_mgetl(f);
      if ( type != DAT_MAGIC ) {
            if (*allegro_errno == 0)
               *allegro_errno = EDOM;
            return NULL;
      } /* we expect this next */
      /* load_file_object might use a callback (its address is internal to file.c sadly).
       It should be null when we call it though because it gets reset after use. 
      */
      dat = load_file_object(f, len); 
   }
   // Now clean up the way pack_fclose does  - without closing the file though.
   if (f->passdata)
	 free(f->passdata);
   if (f->pack_data)
	 free(f->pack_data);
   free(f);
   return dat; 
}



/* main of seal */
int main ( int argc, char **argv )
{
  ini_mainfile = _strdup("seal.ini");
  screen_shot = &xx_screen_shot;
  /* test if SEAL run in safe mode */
  if ( (argc > 1) && argv[1] && !stricmp(argv[1], "-safe") )
      /* SEAL now run in safe mode */
      safe_mode = 1;
  #ifdef __TEST_FILE__
    seal_test_file = fopen("seal.tst", "wt");
  #endif
  #ifdef __DEBUG_FILE__
    seal_debug_file = fopen("seal.dbg", "wt");
  #endif
  program_init(&program);
  #ifdef __TEST_FILE__
    fclose(seal_test_file);
  #endif
  #ifdef __CLOCK_INCLUDED__
  if ( clock_including )
       install_int(&aclock2, 500);
  #endif
  #ifdef __DEBUG_FILE__
    fclose(seal_debug_file);
  #endif
  program.execute(&program);
  program.done(&program);
  return 0;
};
/* new allegro would like to know (though not needed for djgpp) where main ends lets be polite */
END_OF_MAIN();

