/* $Id: glibwww-callbacks.cc,v 1.1.1.1 2003/07/04 22:30:07 atterer Exp $ -*- C++ -*-
This code was taken from glibwww2
, main author: James
Henstdridge , distributable under GPL, v2 or
later.
Added support for compilation under Windows (mingw32).
*/
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
#include
#include
#undef PACKAGE
#undef _
extern "C" {
#include
#include
}
#define WWW_HIGH_PRIORITY (G_PRIORITY_HIGH_IDLE + 50)
#define WWW_LOW_PRIORITY G_PRIORITY_LOW
#define WWW_SCALE_PRIORITY(p) ((WWW_HIGH_PRIORITY - WWW_LOW_PRIORITY) * p \
/ HT_PRIORITY_MAX + WWW_LOW_PRIORITY)
#define READ_CONDITION (G_IO_IN | G_IO_HUP | G_IO_ERR)
#define WRITE_CONDITION (G_IO_OUT | G_IO_ERR)
#define EXCEPTION_CONDITION (G_IO_PRI)
/* Windows: Need G_IO_IN for WRITE_CONDITION, otherwise connect() hangs */
#ifdef G_OS_WIN32
# undef WRITE_CONDITION
# define WRITE_CONDITION (G_IO_IN | G_IO_OUT | G_IO_ERR)
#endif
#if HTEVENT_TYPES != 3
# warning "HTEVENT_TYPES != 3 not supported"
# warning "Compile libwww without WWW_WIN_ASYNC for Windows!"
#endif
typedef struct _SockEventInfo SockEventInfo;
struct _SockEventInfo {
SOCKET s;
HTEventType type;
HTEvent *event;
guint io_tag;
guint timer_tag;
};
typedef struct _SockInfo SockInfo;
struct _SockInfo {
SOCKET s;
GIOChannel *io;
SockEventInfo ev[HTEvent_TYPES];
};
static GHashTable *sockhash = NULL;
static SockInfo *
get_sock_info(SOCKET s, gboolean create)
{
SockInfo *info;
if (!sockhash)
sockhash = g_hash_table_new(g_direct_hash, g_direct_equal);
info = static_cast(
g_hash_table_lookup(sockhash, GINT_TO_POINTER(s)));
if (!info && create) {
info = g_new0(SockInfo, 1);
info->s = s;
# ifdef G_OS_WIN32
info->io = g_io_channel_win32_new_socket(s);
# else
info->io = g_io_channel_unix_new(s);
# endif
info->ev[0].s = info->ev[1].s = info->ev[2].s = s;
info->ev[0].type = HTEvent_READ;
info->ev[1].type = HTEvent_WRITE;
info->ev[2].type = HTEvent_OOB;
info->ev[0].io_tag = info->ev[1].io_tag = info->ev[2].io_tag = 0;
info->ev[0].timer_tag = info->ev[1].timer_tag = info->ev[2].timer_tag = 0;
g_hash_table_insert(sockhash, GINT_TO_POINTER(s), info);
}
return info;
}
static gboolean glibwww_timeout_func (gpointer data);
static gboolean glibwww_io_func(GIOChannel *source, GIOCondition condition,
gpointer data);
static int
glibwww_event_register (SOCKET s, HTEventType type, HTEvent *event)
{
//fprintf(stderr, "glibwww_event_register socket=%d type=%d event=%p\n", s, int(type), event);
SockInfo *info;
gint priority = G_PRIORITY_DEFAULT;
GIOCondition condition;
if (s == INVSOC || HTEvent_INDEX(type) >= HTEvent_TYPES)
return 0;
info = get_sock_info(s, TRUE);
info->ev[HTEvent_INDEX(type)].event = event;
switch (HTEvent_INDEX(type)) {
case HTEvent_INDEX(HTEvent_READ):
condition = static_cast(READ_CONDITION); break;
case HTEvent_INDEX(HTEvent_WRITE):
condition = static_cast(WRITE_CONDITION); break;
case HTEvent_INDEX(HTEvent_OOB):
condition = static_cast(EXCEPTION_CONDITION); break;
default:
g_assert_not_reached ();
condition = static_cast(0); /* this should never occur */
}
if (event->priority != HT_PRIORITY_OFF)
priority = WWW_SCALE_PRIORITY(event->priority);
if (!info->ev[HTEvent_INDEX(type)].io_tag) {
info->ev[HTEvent_INDEX(type)].io_tag =
g_io_add_watch_full(info->io, priority, condition, glibwww_io_func,
&info->ev[HTEvent_INDEX(type)], NULL);
}
if (event->millis >= 0 && !info->ev[HTEvent_INDEX(type)].timer_tag) {
info->ev[HTEvent_INDEX(type)].timer_tag =
g_timeout_add_full(priority, event->millis, glibwww_timeout_func,
&info->ev[HTEvent_INDEX(type)], NULL);
}
return HT_OK;
}
static int
glibwww_event_unregister (SOCKET s, HTEventType type)
{
SockInfo *info = get_sock_info(s, FALSE);
//fprintf(stderr, "glibwww_event_unregister socket=%d type=%d info=%p\n", s, int(type), info);
if (info) {
if (info->ev[HTEvent_INDEX(type)].io_tag)
g_source_remove(info->ev[HTEvent_INDEX(type)].io_tag);
if (info->ev[HTEvent_INDEX(type)].timer_tag)
g_source_remove(info->ev[HTEvent_INDEX(type)].timer_tag);
info->ev[HTEvent_INDEX(type)].event = NULL;
info->ev[HTEvent_INDEX(type)].io_tag = 0;
info->ev[HTEvent_INDEX(type)].timer_tag = 0;
# ifdef G_OS_WIN32
/* clean up sock hash if needed */
if (info->ev[0].io_tag == 0 && info->ev[1].io_tag == 0
&& info->ev[2].io_tag == 0) {
/*g_message("Freeing sock:%d", s);*/
g_hash_table_remove(sockhash, GINT_TO_POINTER(s));
g_io_channel_unref(info->io);
g_free(info);
}
# endif
return HT_OK;
}
return HT_ERROR;
}
static gboolean
glibwww_timeout_func (gpointer data)
{
SockEventInfo *info = (SockEventInfo *)data;
HTEvent *event = info->event;
if (event)
(* event->cbf) (info->s, event->param, HTEvent_TIMEOUT);
return info->timer_tag != 0; /* XXXX a hack */
}
static gboolean
glibwww_io_func(GIOChannel */*source*/, GIOCondition /*condition*/,
gpointer data)
{
SockEventInfo *info = (SockEventInfo *)data;
HTEvent *event = info->event;
//fprintf(stderr, "glibwww_io_func: event %p, event->cbf %p\n", event, event->cbf);
if (info->timer_tag) {
g_source_remove(info->timer_tag);
info->timer_tag = 0;
}
if (event && event->millis >= 0) {
gint priority = G_PRIORITY_DEFAULT;
if (event->priority != HT_PRIORITY_OFF)
priority = WWW_SCALE_PRIORITY(event->priority);
info->timer_tag =
g_timeout_add_full(priority, info->event->millis, glibwww_timeout_func,
info, NULL);
}
if (event)
(* event->cbf) (info->s, event->param, info->type);
return info->io_tag != 0; /* XXXX a hack */
}
static GHashTable *timers = NULL;
static gboolean
glibwww_dispatch_timer(gpointer data)
{
HTTimer *timer = (HTTimer *)data;
HTTimer_dispatch(timer);
return FALSE;
}
static BOOL
glibwww_timer_register(HTTimer *timer)
{
guint tag;
if (!timers)
timers = g_hash_table_new(g_direct_hash, g_direct_equal);
tag = g_timeout_add(HTTimer_expiresRelative(timer),
glibwww_dispatch_timer, timer);
g_hash_table_insert(timers, timer, GUINT_TO_POINTER(tag));
return YES;
}
static BOOL
glibwww_timer_unregister(HTTimer *timer) {
guint tag;
if (!timers)
return NO;
tag = GPOINTER_TO_UINT(g_hash_table_lookup(timers, timer));
if (tag) {
g_source_remove(tag);
g_hash_table_remove(timers, timer);
return YES;
}
return NO;
}
void
glibwww_register_callbacks(void)
{
HTEvent_setRegisterCallback(glibwww_event_register);
HTEvent_setUnregisterCallback(glibwww_event_unregister);
HTTimer_registerSetTimerCallback(glibwww_timer_register);
HTTimer_registerDeleteTimerCallback(glibwww_timer_unregister);
}