/* Timer handler used to do the VT switch at a time when not drawing */
static void
vt_do_switch(void *arg)
{
    static unsigned short r[16], g[16], b[16];

    /*
     * If a drawing function is in progress then we cannot mode
     * switch right now because the drawing function would continue to
     * scribble on the screen after the switch.  So disable further
     * drawing and schedule an alarm to try again in .1 second.
     */
    if(mwdrawing) {
    	draw_disable ();
	GdAddTimer(100, vt_do_switch, NULL);
    	return;
    }
      
    if(visible) {
    	draw_disable ();
	ioctl_getpalette(0, 16, r, g, b);

	if(ioctl (ttyfd, VT_RELDISP, 1) == -1)
	    EPRINTF("Error can't switch away from VT: %m\n");
    } else {
	ioctl_setpalette(0, 16, r, g, b);
    	draw_enable ();
      
	if(ioctl (ttyfd, VT_RELDISP, VT_ACKACQ) == -1)
		EPRINTF("Error can't acknowledge VT switch: %m\n");
    }
}
/* Signal handler called whenever the kernel wants to switch to or
   from our tty. */
static void
vt_switch (int sig unused)
{
  signal (SIGUSR2, vt_switch);

  /* If a BOGL drawing function is in progress then we cannot mode
     switch right now because the drawing function would continue to
     scribble on the screen after the switch.  So disable further
     drawing and schedule an alarm to try again in .1 second. */
  if (bogl_drawing)
    {
      draw_disable ();

      signal (SIGALRM, vt_switch);
      
      {
	struct itimerval duration;
	
	duration.it_interval.tv_sec = 0;
	duration.it_interval.tv_usec = 0;
	duration.it_value.tv_sec = 0;
	duration.it_value.tv_usec = 100000;
	if (-1 == setitimer (ITIMER_REAL, &duration, NULL))
	  bogl_fail ("can't set timer: %s", strerror (errno));
      }
      
      return;
    }
      
  if (visible)
    {
      visible = 0;
      draw_disable ();

      if (-1 == ioctl (tty, VT_RELDISP, 1))
	bogl_fail ("can't switch away from VT: %s", strerror (errno));
    }
  else
    {
      visible = 1;
      draw_enable ();
      
      if (-1 == ioctl (tty, VT_RELDISP, VT_ACKACQ))
	bogl_fail ("can't acknowledge VT switch: %s", strerror (errno));

      if (bogl_reinit != NULL)
        bogl_reinit ();

      bogl_refresh = 2;
    }
}
/* Initialize BOGL. */
int
bogl_init (void)
{
  unsigned long bogl_frame_offset, bogl_frame_len;
  struct fb_var_screeninfo fb_var;
  struct vt_stat vts;

  assert (status < 2);
  visible = 1;
  
  fb = open ("/dev/fb0", O_RDWR);
  if (fb < 0)
    fb = open ("/dev/fb/0", O_RDWR);
  if (fb < 0) {
	  return bogl_fail ("opening /dev/fb0: %s", strerror (errno));
  }

  tty = open ("/dev/tty0", O_RDWR);
  if (tty < 0)
    tty = open ("/dev/vc/0", O_RDWR);
  if (tty < 0) {
	  return bogl_fail ("opening /dev/tty0: %s", strerror (errno));
  }

  if (-1 == ioctl (tty, VT_GETSTATE, &vts)) {
	  return bogl_fail ("can't get VT state: %s", strerror (errno));
  }
  tty_no = vts.v_active;

  if (-1 == ioctl (fb, FBIOGET_FSCREENINFO, &fb_fix)
      || -1 == ioctl (fb, FBIOGET_VSCREENINFO, &fb_var)) {
	  return bogl_fail ("reading screen info: %s", strerror (errno));
  }
  
  bogl_xres = fb_var.xres;
  bogl_yres = fb_var.yres;
  bogl_bpp = fb_var.bits_per_pixel;
  bogl_line_len = fb_fix.line_length;
  type = fb_fix.type;
  visual = fb_fix.visual;

  /* Some broken framebuffers (2.4.x virgefb, 2.5 is OK) don't provide
     line_length.  */
  if (bogl_line_len == 0)
    bogl_line_len = fb_var.xres * fb_var.bits_per_pixel / 8;

  if (!draw_enable ()) {
	  return bogl_fail ("don't know screen type %d", type);
  }

  if (ioctl (tty, VT_GETMODE, &mode) == -1) {
	  return bogl_fail ("can't get VT mode: %s", strerror (errno));
  }

  mode.mode = VT_PROCESS;
  mode.relsig = SIGUSR2;
  mode.acqsig = SIGUSR2;

  signal (SIGUSR2, vt_switch);

  if (-1 == ioctl (tty, VT_SETMODE, &mode)) {
	  return bogl_fail ("can't set VT mode: %s", strerror (errno));
  }

  if (-1 == ioctl (tty, KDSETMODE, KD_GRAPHICS)) {
	  return bogl_fail ("setting graphics mode: %s", strerror (errno));
  }

  if (!init_fb()) {
	  bogl_fail ("EXPLODE\n");

	  return 0;
  }

  bogl_frame_offset = fb_fix.smem_start & ~PAGE_MASK;
  bogl_frame_len = ((bogl_frame_offset + fb_fix.smem_len + ~PAGE_MASK)
		    & PAGE_MASK);

  bogl_frame_mapped
    = mmap (NULL, bogl_frame_len, PROT_READ | PROT_WRITE, MAP_SHARED, fb, 0);
  if (bogl_frame_mapped == NULL || bogl_frame_mapped == (char *) -1)
     return bogl_fail ("mmaping /dev/fb0: %s", strerror (errno));

  bogl_frame = bogl_frame_mapped + bogl_frame_offset;

  kbd_init ();

  if (visual == FB_VISUAL_PSEUDOCOLOR
      || visual == FB_VISUAL_STATIC_PSEUDOCOLOR)
  {
    static struct fb_cmap cmap;

    cmap.start = 0;
    cmap.len = 16;
    cmap.red = saved_red;
    cmap.green = saved_green;
    cmap.blue = saved_blue;
    cmap.transp = NULL;

    ioctl (fb, FBIOGETCMAP, &cmap);
  }
  
  if (!status)
    atexit (bogl_done);
  status = 2;

  return 1;
}