/* vio.c (emx+gcc) */

#define __TILED__
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define INCL_DOSPROCESS
#define INCL_DOSERRORS
#define INCL_VIO
#define INCL_KBD
#include <os2.h>

/* These variables must not cross a 64KB boundary */
/* Making them auto variables is dangerous */

static VIOMODEINFO vmi;
static VIOPHYSBUF vpb;
static VIOMODEINFO graph_mode;
static VIOMODEINFO org_mode;
static VIOPHYSBUF phys;
static KBDKEYINFO key;
static char msg[] = "Hello world!\r\n";


static void usage (void)
{
  puts ("Usage: vio [-gmp]");
  exit (1);
}


static void test_physbuf (void)
{
  USHORT rc;
  unsigned char *p;
  int i;

  vpb.pBuf = (PBYTE)0xb8000;
  vpb.cb = 80*25*2;
  rc = VioGetPhysBuf (&vpb, 0);
  if (rc == ERROR_VIO_EXTENDED_SG)
    {
      fputs ("Must run in a full-screen session.\n", stderr);
      exit (2);
    }
  if (rc != 0)
    {
      fprintf (stderr, "VioGetPhysBuf failed, rc=%u\n", (unsigned)rc);
      exit (2);
    }
  p = MAKEP (vpb.asel[0], 0);
  for (i = 0; i < 80*25; ++i)
    {
      *p ^= 1;
      p += 2;
    }
}


static void test_hello (void)
{
  VioWrtTTY (msg, strlen (msg), 0);
}


static void test_getmode (void)
{
  USHORT rc;

  vmi.cb = sizeof (vmi);
  rc = VioGetMode (&vmi, 0);
  if (rc != 0)
    {
      fprintf (stderr, "VioGetMode failed, rc=%u\n", (unsigned)rc);
      exit (2);
    }
  printf ("Display: %s\n", (vmi.fbType == 0 ? "monochrome" : "color"));
  printf ("Colors:  %d\n", 1 << vmi.color);
  printf ("Columns: %d\n", vmi.col);
  printf ("Rows:    %d\n", vmi.row);
  printf ("Hres:    %d\n", vmi.hres);
  printf ("Vres:    %d\n", vmi.vres);
}


static char *graph_phys;
static char graph_save_buf[0x10000];
static int graph_saved = 0;
static int graph_on = 0;


static void graph_restore (void)
{
  if (graph_saved)
    {
      VioSetMode (&graph_mode, 0);
      memcpy (graph_phys, graph_save_buf, 0x10000);
    }
}


static void redraw_thread (void *arg)
{
  USHORT notify;

  for (;;)
    {
      if (VioSavRedrawWait (VSRWI_SAVEANDREDRAW, &notify, 0) != 0)
        _endthread ();
      DosBeep ((notify == VSRWN_SAVE ? 200 : 400), 200);
      if (graph_on)
        {
          if (notify == VSRWN_SAVE)
            {
              memcpy (graph_save_buf, graph_phys, 0x10000);
              graph_saved = 1;
            }
          else 
            graph_restore ();
        }
    }
}


static void mode_thread (void *arg)
{
  USHORT rc, notify;
  
  for (;;)
    {
      rc = VioModeWait (VMWR_POPUP, &notify, 0);
      if (rc != 0)
        _endthread ();
      DosBeep (1000, 200);
      if (graph_on)
        graph_restore ();
    }
}


static void test_graphics (void)
{
  USHORT rc, len;
  BYTE not_locked;
  PBYTE p;
  PUSHORT pus;
  PVIOCONFIGINFO pconfig;
  int i, j;

  /* Get the maximum size of the VIOCONFIGINFO structure */
  len = 2;      
  rc = VioGetConfig (VIO_CONFIG_CURRENT, (PVIOCONFIGINFO)&len, 0);
  if (rc != 0)
    {
      fprintf (stderr, "VioGetConfig failed, rc=%d\n", (unsigned)rc);
      exit (2);
    }

  /* Allocate a VIOCONFIGINFO structure */

  pconfig = _tmalloc (len);
  if (pconfig == NULL)
    {
      fputs ("_tmalloc() failed\n", stderr);
      exit (2);
    }

  /* Now get the configuration information */

  pconfig->cb = len;
  rc = VioGetConfig (VIO_CONFIG_CURRENT, pconfig, 0);
  if (rc != 0)
    {
      fprintf (stderr, "VioGetConfig failed, rc=%d\n", (unsigned)rc);
      exit (2);
    }

  /* Check if VGA is one of the emulated adapters. */

  pus = (PUSHORT)((char *)pconfig + pconfig->EMAdaptersOFF);
  if (pus[0] < 1)
    {
      fputs ("No list of emulated adapters\n", stderr);
      exit (2);
    }
  if (!(pus[1] & 8))
    {
      fputs ("Need VGA adapter\n", stderr);
      exit (2);
    }

  /* Check if the VGA color display is one of the emulated display. */

  pus = (PUSHORT)((char *)pconfig + pconfig->EMDisplaysOFF);
  if (pus[0] < 1)
    {
      fputs ("No list of emulated displays\n", stderr);
      exit (2);
    }
  if (!(pus[1] & 0x10))
    {
      fputs ("Need color VGA display\n", stderr);
      exit (2);
    }

  org_mode.cb = sizeof (org_mode);
  VioGetMode (&org_mode, 0);
  memset (&graph_mode, 0, sizeof (graph_mode));
  graph_mode.cb = sizeof (graph_mode);
  graph_mode.fbType = VGMT_OTHER | VGMT_GRAPHICS;
  graph_mode.color = 8;
  graph_mode.col = 80;
  graph_mode.row = 25;
  graph_mode.hres = 320;
  graph_mode.vres = 200;
  rc = VioSetMode (&graph_mode, 0);
  if (rc == ERROR_VIO_MODE)
    {
      fputs ("Must run in a full-screen session.\n", stderr);
      exit (2);
    }
  if (rc != 0)
    {
      fprintf (stderr, "VioSetMode failed, rc=%d\n", (unsigned)rc);
      exit (2);
    }
  phys.pBuf = (PBYTE)0xa0000;
  phys.cb = 0x10000;
  rc = VioGetPhysBuf (&phys, 0);
  if (rc != 0)
    {
      VioSetMode (&org_mode, 0);
      fprintf (stderr, "VioGetPhysBuf failed, rc=%d\n", (unsigned)rc);
      exit (2);
    }
  graph_phys = MAKEP (phys.asel[0], 0);
  graph_on = 1;
  _beginthread (redraw_thread, NULL, 0x4000, NULL);
  _beginthread (mode_thread, NULL, 0x4000, NULL);
  p = graph_phys;
  for (j = 0;; ++j)
    {
      VioScrLock (LOCKIO_WAIT, &not_locked, 0);
      for (i = 0; i < 320*200; ++i)
        p[i] = ((i+j) >> 8) & 0xff;
      VioScrUnLock (0);
      if (KbdCharIn (&key, IO_NOWAIT, 0) == 0 && key.fbStatus != 0)
        break;
    }
  VioSetMode (&org_mode, 0);
}


int main (int argc, char *argv[])
{
  if (argc == 2 && strcmp (argv[1], "-p") == 0)
    test_physbuf ();
  else if (argc == 2 && strcmp (argv[1], "-g") == 0)
    test_graphics ();
  else if (argc == 2 && strcmp (argv[1], "-m") == 0)
    test_getmode ();
  else if (argc == 1)
    test_hello ();
  else
    usage ();
  return (0);
}
