1 /* $Id: glibwww-callbacks.cc,v 1.1.1.1 2003/07/04 22:30:07 atterer Exp $ -*- C++ -*-
2 
3   This code was taken from glibwww2
4   <http://cvs.gnome.org/lxr/source/glibwww2/>, main author: James
5   Henstdridge <james@daa.com.au>, distributable under GPL, v2 or
6   later.
7 
8   Added support for compilation under Windows (mingw32).
9 
10 */
11 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
12 #include <config.h>
13 #include <glibwww.hh>
14 #undef PACKAGE
15 #undef _
16 extern "C" {
17 #include <HTEvent.h>
18 #include <HTTimer.h>
19 }
20 
21 #define WWW_HIGH_PRIORITY (G_PRIORITY_HIGH_IDLE + 50)
22 #define WWW_LOW_PRIORITY G_PRIORITY_LOW
23 #define WWW_SCALE_PRIORITY(p) ((WWW_HIGH_PRIORITY - WWW_LOW_PRIORITY) * p \
24                                     / HT_PRIORITY_MAX + WWW_LOW_PRIORITY)
25 
26 #define READ_CONDITION (G_IO_IN | G_IO_HUP | G_IO_ERR)
27 #define WRITE_CONDITION (G_IO_OUT | G_IO_ERR)
28 #define EXCEPTION_CONDITION (G_IO_PRI)
29 /* Windows: Need G_IO_IN for WRITE_CONDITION, otherwise connect() hangs */
30 #ifdef G_OS_WIN32
31 #       undef WRITE_CONDITION
32 #       define WRITE_CONDITION (G_IO_IN | G_IO_OUT | G_IO_ERR)
33 #endif
34 
35 #if HTEVENT_TYPES != 3
36 #       warning "HTEVENT_TYPES != 3 not supported"
37 #       warning "Compile libwww without WWW_WIN_ASYNC for Windows!"
38 #endif
39 
40 typedef struct _SockEventInfo SockEventInfo;
41 struct _SockEventInfo {
42         SOCKET s;
43         HTEventType type;
44         HTEvent *event;
45         guint io_tag;
46         guint timer_tag;
47 };
48 
49 typedef struct _SockInfo SockInfo;
50 struct _SockInfo {
51         SOCKET s;
52         GIOChannel *io;
53         SockEventInfo ev[HTEvent_TYPES];
54 };
55 
56 static GHashTable *sockhash = NULL;
57 
58 
59 static SockInfo *
get_sock_info(SOCKET s,gboolean create)60 get_sock_info(SOCKET s, gboolean create)
61 {
62         SockInfo *info;
63 
64         if (!sockhash)
65                 sockhash = g_hash_table_new(g_direct_hash, g_direct_equal);
66 
67         info = static_cast<SockInfo*>(
68             g_hash_table_lookup(sockhash, GINT_TO_POINTER(s)));
69         if (!info && create) {
70                 info = g_new0(SockInfo, 1);
71                 info->s = s;
72 #               ifdef G_OS_WIN32
73                         info->io = g_io_channel_win32_new_socket(s);
74 #               else
75                         info->io = g_io_channel_unix_new(s);
76 #               endif
77                 info->ev[0].s = info->ev[1].s = info->ev[2].s = s;
78                 info->ev[0].type = HTEvent_READ;
79                 info->ev[1].type = HTEvent_WRITE;
80                 info->ev[2].type = HTEvent_OOB;
81                 info->ev[0].io_tag = info->ev[1].io_tag = info->ev[2].io_tag = 0;
82                 info->ev[0].timer_tag = info->ev[1].timer_tag = info->ev[2].timer_tag = 0;
83                 g_hash_table_insert(sockhash, GINT_TO_POINTER(s), info);
84         }
85         return info;
86 }
87 
88 static gboolean glibwww_timeout_func (gpointer data);
89 static gboolean glibwww_io_func(GIOChannel *source, GIOCondition condition,
90                                 gpointer data);
91 
92 static int
glibwww_event_register(SOCKET s,HTEventType type,HTEvent * event)93 glibwww_event_register (SOCKET s, HTEventType type, HTEvent *event)
94 {
95   //fprintf(stderr, "glibwww_event_register socket=%d type=%d event=%p\n", s, int(type), event);
96         SockInfo *info;
97         gint priority = G_PRIORITY_DEFAULT;
98         GIOCondition condition;
99 
100         if (s == INVSOC || HTEvent_INDEX(type) >= HTEvent_TYPES)
101                 return 0;
102 
103         info = get_sock_info(s, TRUE);
104         info->ev[HTEvent_INDEX(type)].event = event;
105 
106         switch (HTEvent_INDEX(type)) {
107         case HTEvent_INDEX(HTEvent_READ):
108                 condition = static_cast<GIOCondition>(READ_CONDITION); break;
109         case HTEvent_INDEX(HTEvent_WRITE):
110                 condition = static_cast<GIOCondition>(WRITE_CONDITION); break;
111         case HTEvent_INDEX(HTEvent_OOB):
112                 condition = static_cast<GIOCondition>(EXCEPTION_CONDITION); break;
113         default:
114                 g_assert_not_reached ();
115                 condition = static_cast<GIOCondition>(0); /* this should never occur */
116         }
117         if (event->priority != HT_PRIORITY_OFF)
118                 priority = WWW_SCALE_PRIORITY(event->priority);
119 
120         if (!info->ev[HTEvent_INDEX(type)].io_tag) {
121                 info->ev[HTEvent_INDEX(type)].io_tag =
122                         g_io_add_watch_full(info->io, priority, condition, glibwww_io_func,
123                                             &info->ev[HTEvent_INDEX(type)], NULL);
124         }
125 
126         if (event->millis >= 0 && !info->ev[HTEvent_INDEX(type)].timer_tag) {
127                 info->ev[HTEvent_INDEX(type)].timer_tag =
128                         g_timeout_add_full(priority, event->millis, glibwww_timeout_func,
129                                            &info->ev[HTEvent_INDEX(type)], NULL);
130         }
131 
132         return HT_OK;
133 }
134 
135 static int
glibwww_event_unregister(SOCKET s,HTEventType type)136 glibwww_event_unregister (SOCKET s, HTEventType type)
137 {
138         SockInfo *info = get_sock_info(s, FALSE);
139         //fprintf(stderr, "glibwww_event_unregister socket=%d type=%d info=%p\n", s, int(type), info);
140 
141         if (info) {
142                 if (info->ev[HTEvent_INDEX(type)].io_tag)
143                         g_source_remove(info->ev[HTEvent_INDEX(type)].io_tag);
144                 if (info->ev[HTEvent_INDEX(type)].timer_tag)
145                         g_source_remove(info->ev[HTEvent_INDEX(type)].timer_tag);
146 
147                 info->ev[HTEvent_INDEX(type)].event = NULL;
148                 info->ev[HTEvent_INDEX(type)].io_tag = 0;
149                 info->ev[HTEvent_INDEX(type)].timer_tag = 0;
150 
151 #               ifdef G_OS_WIN32
152                   /* clean up sock hash if needed */
153                   if (info->ev[0].io_tag == 0 && info->ev[1].io_tag == 0
154                       && info->ev[2].io_tag == 0) {
155                     /*g_message("Freeing sock:%d", s);*/
156                     g_hash_table_remove(sockhash, GINT_TO_POINTER(s));
157                     g_io_channel_unref(info->io);
158                     g_free(info);
159                   }
160 #               endif
161 
162                 return HT_OK;
163         }
164         return HT_ERROR;
165 }
166 
167 static gboolean
glibwww_timeout_func(gpointer data)168 glibwww_timeout_func (gpointer data)
169 {
170         SockEventInfo *info = (SockEventInfo *)data;
171         HTEvent *event = info->event;
172 
173         if (event)
174                 (* event->cbf) (info->s, event->param, HTEvent_TIMEOUT);
175         return info->timer_tag != 0; /* XXXX a hack */
176 }
177 
178 static gboolean
glibwww_io_func(GIOChannel *,GIOCondition,gpointer data)179 glibwww_io_func(GIOChannel */*source*/, GIOCondition /*condition*/,
180                 gpointer data)
181 {
182         SockEventInfo *info = (SockEventInfo *)data;
183         HTEvent *event = info->event;
184         //fprintf(stderr, "glibwww_io_func: event %p, event->cbf %p\n", event, event->cbf);
185 
186         if (info->timer_tag) {
187                 g_source_remove(info->timer_tag);
188                 info->timer_tag = 0;
189         }
190         if (event && event->millis >= 0) {
191                 gint priority = G_PRIORITY_DEFAULT;
192 
193                 if (event->priority != HT_PRIORITY_OFF)
194                         priority = WWW_SCALE_PRIORITY(event->priority);
195                 info->timer_tag =
196                         g_timeout_add_full(priority, info->event->millis, glibwww_timeout_func,
197                                            info, NULL);
198         }
199 
200         if (event)
201                 (* event->cbf) (info->s, event->param, info->type);
202         return info->io_tag != 0; /* XXXX a hack */
203 }
204 
205 static GHashTable *timers = NULL;
206 
207 static gboolean
glibwww_dispatch_timer(gpointer data)208 glibwww_dispatch_timer(gpointer data)
209 {
210         HTTimer *timer = (HTTimer *)data;
211 
212         HTTimer_dispatch(timer);
213         return FALSE;
214 }
215 
216 static BOOL
glibwww_timer_register(HTTimer * timer)217 glibwww_timer_register(HTTimer *timer)
218 {
219         guint tag;
220 
221         if (!timers)
222                 timers = g_hash_table_new(g_direct_hash, g_direct_equal);
223 
224         tag = g_timeout_add(HTTimer_expiresRelative(timer),
225                             glibwww_dispatch_timer, timer);
226         g_hash_table_insert(timers, timer, GUINT_TO_POINTER(tag));
227         return YES;
228 }
229 
230 static BOOL
glibwww_timer_unregister(HTTimer * timer)231 glibwww_timer_unregister(HTTimer *timer) {
232         guint tag;
233 
234         if (!timers)
235                 return NO;
236         tag = GPOINTER_TO_UINT(g_hash_table_lookup(timers, timer));
237         if (tag) {
238                 g_source_remove(tag);
239                 g_hash_table_remove(timers, timer);
240                 return YES;
241         }
242         return NO;
243 }
244 
245 void
glibwww_register_callbacks(void)246 glibwww_register_callbacks(void)
247 {
248         HTEvent_setRegisterCallback(glibwww_event_register);
249         HTEvent_setUnregisterCallback(glibwww_event_unregister);
250 
251         HTTimer_registerSetTimerCallback(glibwww_timer_register);
252         HTTimer_registerDeleteTimerCallback(glibwww_timer_unregister);
253 }
254 
255