The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#include <stddef.h>

#include <glib.h>
#include "EVAPI.h"

static GMainContext *
get_gcontext (SV *context)
{
  if (!SvOK (context))
    return g_main_context_default ();

  croak ("only the default context is currently supported.");
}

struct econtext
{
  GPollFD *pfd;
  ev_io *iow;
  int nfd, afd;
  gint maxpri;

  ev_prepare pw;
  ev_check cw;
  ev_timer tw;

  GMainContext *gc;
};

static void
timer_cb (EV_P_ ev_timer *w, int revents)
{
  /* nop */
}

static void
io_cb (EV_P_ ev_io *w, int revents)
{
  /* nop */
}

static void
prepare_cb (EV_P_ ev_prepare *w, int revents)
{
  struct econtext *ctx = (struct econtext *)(((char *)w) - offsetof (struct econtext, pw));
  gint timeout;
  int i;

  g_main_context_dispatch (ctx->gc);

  g_main_context_prepare (ctx->gc, &ctx->maxpri);

  while (ctx->afd < (ctx->nfd = g_main_context_query  (
    ctx->gc,
    ctx->maxpri,
    &timeout,
    ctx->pfd,
    ctx->afd))
  )
    {
      free (ctx->pfd);
      free (ctx->iow);

      ctx->afd = 1;
      while (ctx->afd < ctx->nfd)
        ctx->afd <<= 1;

      ctx->pfd = malloc (ctx->afd * sizeof (GPollFD));
      ctx->iow = malloc (ctx->afd * sizeof (ev_io));
    }

  for (i = 0; i < ctx->nfd; ++i)
    {
      GPollFD *pfd = ctx->pfd + i;
      ev_io *iow = ctx->iow + i;

      pfd->revents = 0;

      ev_io_init (
        iow,
        io_cb,
        pfd->fd,
        (pfd->events & G_IO_IN ? EV_READ : 0)
         | (pfd->events & G_IO_OUT ? EV_WRITE : 0)
      );
      iow->data = (void *)pfd;
      ev_set_priority (iow, EV_MINPRI);
      ev_io_start (EV_A, iow);
    }

  if (timeout >= 0)
    {
      ev_timer_set (&ctx->tw, timeout * 1e-3, 0.);
      ev_timer_start (EV_A, &ctx->tw);
    }
}

static void
check_cb (EV_P_ ev_check *w, int revents)
{
  struct econtext *ctx = (struct econtext *)(((char *)w) - offsetof (struct econtext, cw));
  int i;

  for (i = 0; i < ctx->nfd; ++i)
    {
      ev_io *iow = ctx->iow + i;

      if (ev_is_pending (iow))
        {
          GPollFD *pfd = ctx->pfd + i;
          int revents = ev_clear_pending (EV_A, iow);

          pfd->revents |= pfd->events &
            ((revents & EV_READ ? G_IO_IN : 0)
             | (revents & EV_WRITE ? G_IO_OUT : 0));
        }

      ev_io_stop (EV_A, iow);
    }

  if (ev_is_active (&ctx->tw))
    ev_timer_stop (EV_A, &ctx->tw);

  g_main_context_check (ctx->gc, ctx->maxpri, ctx->pfd, ctx->nfd);
}

static struct econtext default_context;

MODULE = EV::Glib                PACKAGE = EV::Glib

PROTOTYPES: ENABLE

BOOT:
{
	I_EV_API ("EV::Glib");
}

long
install (SV *context)
	CODE:
{
	GMainContext *gc = get_gcontext (context);
        struct econtext *ctx = &default_context;

        ctx->gc  = g_main_context_ref (gc);
        ctx->nfd = 0;
        ctx->afd = 0;
        ctx->iow = 0;
        ctx->pfd = 0;

        ev_prepare_init (&ctx->pw, prepare_cb);
        ev_set_priority (&ctx->pw, EV_MINPRI);
        ev_prepare_start (EV_DEFAULT, &ctx->pw);

        ev_check_init (&ctx->cw, check_cb);
        ev_set_priority (&ctx->cw, EV_MAXPRI);
        ev_check_start (EV_DEFAULT, &ctx->cw);

        ev_init (&ctx->tw, timer_cb);
        ev_set_priority (&ctx->tw, EV_MINPRI);
}
	OUTPUT:
        RETVAL