/*
 * DANG_BEGIN_MODULE
 *
 * This file handles the signal queue.  It does not initialize the symbols
 * themselves as that is done by the files in the ./arch directory.  It
 * includes a default action for most signals.
 *
 * DANG_END_MODULE
 *
 * DANG_BEGIN_CHANGELOG
 *
 * $Date: $
 * $Source: $
 * $Revision: $
 * $State: $
 *
 * $Log: $
 *
 * DANG_END_CHANGELOG
 */

#include "dosemu.h"
#include "dosext/dpmi.h"
#include "base/mouse.h"
#include "base/shared.h"
#include "base/bios.h"
#include "base/pic.h"
#include "base/int.h"
#include "base/ioctlq.h"
#include "env/video.h"
#include "arch/signals.h"     /* get rid of this... */

#define MAX_SIG_QUEUE_SIZE 50

struct signal_struct_t {
  void (* handler)(void);
};

static struct signal_struct_t signal_queue_[MAX_SIG_QUEUE_SIZE];
static Bit16u signal_head = 0;
static Bit16u signal_tail = 0;

/*
 * DANG_BEGIN_FUNCTION signal_run
 *
 * This function loops through the signal queue and executes the functions
 * for the signals that have been queued since the last signal_run call.
 *
 * Signals need to be queued because they generally can not be executed in
 * the middle of a vm86() call or in the middle of an instruction execution
 * (in the case of an 80x86 emulator.)
 *
 * DANG_END_FUNCTION
 */
void signal_run(void)
{
  if (signal_head != signal_tail) {
    signal_queue_[signal_head].handler();
    signal_head = (signal_head + 1) % MAX_SIG_QUEUE_SIZE;

    /*
     * Note:  For reasons that I do not pretend to understand, we do not
     *        loop over the signals, we merely do one and set a flag if
     *        there are others present.  Would someone care to explain?
     *                                               - Scott
     */

    if (signal_head != signal_tail) { 
      if (in_dpmi)
	dpmi_eflags |= VIP;
      REG(eflags) |= VIP;
    }
  }
}

/*
 * DANG_BEGIN_FUNCTION signal_timer
 *
 * Check a variety of events on a timer signal.
 *
 * DANG_END_FUNCTION
 */
void signal_timer(void)
{
  static volatile int running = 0;
#if CONFIG_VIDEO_CHECK_DIRTY
  static int update_pending = 0;
#endif
  int retval;

#ifdef CONFIG_DEBUG_VERBOSE
  g_printf("SIGNAL: SIGALRM_call\n");
#endif
  
#ifdef CONFIG_ENV_X
  if (config.X) 
     X_handle_events();
#endif

#ifdef CONFIG_IPX 
  /* check for available packets on the packet driver interface */
  /* (timeout=0, so it immediately returns when none are available) */
  pkt_check_receive(0);
#endif

  /* If it is running in termcap mode, then update the screen.
   * First it sets a running flag, so as to avoid re-entrancy of 
   * update_screen while it is in use.  After update_screen is done,
   * it returns a nonzero value if there was any updates to the screen.
   * If there were any updates to the screen, then set a countdown value
   * in order to give DOSEMU more CPU time, between screen updates.
   * This increases the DOSEMU-to-termcap update efficiency greatly.
   * The countdown counter is currently at a value of 2.
   */
   
   /* This now (again) tests screen_bitmap, i.e. checks if the screen 
    * was written to at all. This doesn't seem to achieve much for now,
    * but it will be helpful when implementing X graphics.
    * It's a bit tricky, however, because previous calls of update_screen
    * might not have updated the entire screen. Therefore update_pending
    * is set to 1 if only part of the screen was updated (update_screen
    * returns 2), meaning that update_screen will in any case be called
    * next time.
    * (*** this only applies if VIDEO_CHECK_DIRTY is set, which is 
    *      currently not the default! ***)
    *
    * return vales for update_screen are now:    
    *       0 nothing changed
    *       1 changed, entire screen updated
    *       2 changed, only partially updated
    *
    * note that update_screen also updates the cursor.
    */
#ifdef CONFIG_ENV_X
  if (config.X && config.X_blinkrate) {
     X_blink_cursor();
  }
#endif

  if (!running && !video_update_lock) {
    if (Video->update_screen 
#if CONFIG_VIDEO_CHECK_DIRTY
       && (update_pending || vm86s.screen_bitmap&screen_mask)
#endif
       ) 
    {
       running = -1;
       retval = Video->update_screen();
#ifdef CONFIG_ENV_X
       running = retval ? (config.X?config.X_updatefreq:config.term_updatefreq) 
                        : 0;
#else
       running = retval ? config.term_updatefreq : 0;
#endif
#if CONFIG_VIDEO_CHECK_DIRTY
       update_pending=(retval==2);
       vm86s.screen_bitmap=0;
#endif
    }
    else if (Video->update_cursor) {
       Video->update_cursor();
    }
  }
  else if (running > 0) {
    running--;
  }
  
  if (mice->intdrv)
    mouse_curtick();

  /* TRB - perform processing for the IPX Asynchronous Event Service */
#ifdef CONFIG_IPX
  if (config.ipxsup)
    AESTimerTick();
#endif

/*
 * DANG_BEGIN_REMARK
 * Check for keyboard coming from client
 * For now, first byte is interrupt requests from Client 
 * DANG_END_REMARK
 */
  if (*(u_char *)(shared_qf_memory + CLIENT_REQUEST_FLAG_AREA) & 0x40) {
    k_printf("KBD: Client sent key\n");
    pic_request (PIC_IRQ1);
    *(u_char *)(shared_qf_memory + CLIENT_REQUEST_FLAG_AREA) &=  ~0x40;
  }

  if (not_use_sigio)
    io_select(fds_no_sigio);
}

/*
 * DANG_BEGIN_FUNCTION signal_sigio
 *
 * Whenever I/O occurs on devices allowing SIGIO to occur, DOSEMU
 * will be flagged to run this call which inturn checks which 
 * fd(s) was set and execute the proper routine to get the I/O
 * from that device.
 *
 * DANG_END_FUNCTION
 */
void signal_sigio(void)
{
  /* Call select to see if any I/O is ready on devices */
  io_select(fds_sigio);
}

/*
 * DANG_BEGIN_FUNCTION signal_quit
 *
 * Handles a SIGQUIT message.  Please queue this!!!
 *
 * DANG_END_FUNCTION
 */
void signal_quit(void)
{
  WRITE_BYTE(bios_ctrl_break_flag, 0x80);

  int_run(0x1b);
}

/*
 * DANG_BEGIN_FUNCTION signal_queue
 *
 * arguments:
 * signal_call - signal handling routine to be called.
 *
 * description:
 * Save into an array structure queue the signal context of the current
 * signal as well as the function to call for dealing with this signal.
 * This is a queue because any signal may occur multiple times before
 * DOSEMU deals with it down the road.
 *
 * DANG_END_FUNCTION
 */
void signal_queue(void (*signal_call)(void))
{
  signal_queue_[signal_tail].handler = signal_call;
  signal_tail = (signal_tail + 1) % MAX_SIG_QUEUE_SIZE;

  /*
   * Would someone like to explain to me how this is going to help in
   * the MIDDLE of a vm86() call? - Scott
   */
  if (in_dpmi)
    dpmi_eflags |= VIP;
  REG(eflags) |= VIP;
}

void signal_init(void)
{
}

void signal_reset(void)
{
  signal_head = 0;
  signal_tail = 0;
}
