2 * @file miranda-input.c
6 * Copyright (C) 2010-11 SIPE Project <http://sipe.sourceforge.net/>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include "sipe-backend.h"
29 #include "newpluginapi.h"
30 #include "m_protosvc.h"
31 #include "m_protoint.h"
33 #include "miranda-private.h"
35 #define ENTRY_SIG 0x88442211
37 static NETLIBSELECTEX m_select
= {0};
38 static GHashTable
*m_readhash
= NULL
;
39 static GHashTable
*m_writehash
= NULL
;
40 static GList
*m_entries
= NULL
;
42 typedef struct sipe_miranda_sel_entry
46 sipe_miranda_input_function func
;
50 /* Private. For locking only */
53 sipe_miranda_input_condition cond
;
57 input_cb_async(void *data
)
59 struct sipe_miranda_sel_entry
*entry
= (struct sipe_miranda_sel_entry
*)data
;
60 if (entry
->fd
== NULL
)
62 SIPE_DEBUG_INFO("[IE:%08x] Entry already removed. Not calling read/write function", entry
);
64 SIPE_DEBUG_INFO("[IE:%08x] Calling real read/write function", entry
);
65 entry
->func(entry
->user_data
, entry
->source
, entry
->cond
);
67 SetEvent(entry
->hDoneEvent
);
70 static unsigned __stdcall
74 struct sipe_miranda_sel_entry
*entry
;
77 m_select
.cbSize
= sizeof(m_select
);
78 m_select
.dwTimeout
= 6000;
80 while( m_select
.hReadConns
[0] || m_select
.hWriteConns
[0])
84 for ( rc
=0 ; m_select
.hReadConns
[rc
] ; rc
++ );
85 for ( wc
=0 ; m_select
.hWriteConns
[wc
] ; wc
++ );
87 SIPE_DEBUG_INFO("About to run select on <%d> read and <%d> write", rc
, wc
);
88 lstRes
= CallService(MS_NETLIB_SELECTEX
, 0, (LPARAM
)&m_select
);
92 SIPE_DEBUG_INFO_NOFORMAT("Connection failed while waiting.");
97 SIPE_DEBUG_INFO_NOFORMAT("Select Timeout.");
101 SIPE_DEBUG_INFO_NOFORMAT("Back from select");
103 for ( cnt
=0 ; m_select
.hReadConns
[cnt
] ; cnt
++ )
106 if (!m_select
.hReadStatus
[cnt
]) continue;
107 SIPE_DEBUG_INFO("FD at position <%d> ready to read.", cnt
);
108 entry
= (struct sipe_miranda_sel_entry
*)g_hash_table_lookup(m_readhash
, (gconstpointer
)m_select
.hReadConns
[cnt
]);
111 SIPE_DEBUG_INFO_NOFORMAT("ERROR: no read handler found.");
114 SIPE_DEBUG_INFO("[IE:%08x] About to call read function.", entry
);
115 entry
->source
= (gint
)m_select
.hReadConns
[cnt
];
116 entry
->cond
= SIPE_MIRANDA_INPUT_READ
;
117 entry
->hDoneEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
120 CallFunctionAsync(input_cb_async
, entry
);
121 wr
= WaitForSingleObject(entry
->hDoneEvent
, INFINITE
);
123 input_cb_async(entry
);
125 CloseHandle(entry
->hDoneEvent
);
126 SIPE_DEBUG_INFO("[IE:%08x] read function returned.", entry
);
129 for ( cnt
=0 ; m_select
.hWriteConns
[cnt
] ; cnt
++ )
131 if (!m_select
.hWriteStatus
[cnt
]) continue;
132 SIPE_DEBUG_INFO("FD at position <%d> ready to write.", cnt
);
133 entry
= (struct sipe_miranda_sel_entry
*)g_hash_table_lookup(m_writehash
, (gconstpointer
)m_select
.hWriteConns
[cnt
]);
136 SIPE_DEBUG_INFO_NOFORMAT("ERROR: no write handler found.");
139 SIPE_DEBUG_INFO("[IE:%08x] About to call write function.", entry
);
140 entry
->source
= (gint
)m_select
.hWriteConns
[cnt
];
141 entry
->cond
= SIPE_MIRANDA_INPUT_WRITE
;
142 entry
->hDoneEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
145 CallFunctionAsync(input_cb_async
, entry
);
146 WaitForSingleObject(entry
->hDoneEvent
, INFINITE
);
148 input_cb_async(entry
);
150 CloseHandle(entry
->hDoneEvent
);
151 SIPE_DEBUG_INFO("[IE:%08x] write function returned.", entry
);
155 /* Free all removed entries */
156 while (m_entries
) g_list_delete_link(m_entries
, g_list_last(m_entries
));
162 struct sipe_miranda_sel_entry
*
163 sipe_miranda_input_add(HANDLE fd
, sipe_miranda_input_condition cond
, sipe_miranda_input_function func
, gpointer user_data
)
167 struct sipe_miranda_sel_entry
*entry
;
170 m_readhash
= g_hash_table_new(NULL
, NULL
);
173 m_writehash
= g_hash_table_new(NULL
, NULL
);
175 if ((cond
!= SIPE_MIRANDA_INPUT_READ
) && (cond
!= SIPE_MIRANDA_INPUT_WRITE
))
177 SIPE_DEBUG_INFO("Invalid input condition <%d> cond.", cond
);
181 entry
= g_new0(struct sipe_miranda_sel_entry
,1);
182 entry
->sig
= ENTRY_SIG
;
184 entry
->user_data
= user_data
;
186 entry
->async
= FALSE
;
188 if (cond
== SIPE_MIRANDA_INPUT_READ
)
190 for ( wcnt
=0 ; m_select
.hWriteConns
[wcnt
] ; wcnt
++ );
191 for ( rcnt
=0 ; m_select
.hReadConns
[rcnt
] && m_select
.hReadConns
[rcnt
]!=(HANDLE
)fd
; rcnt
++ );
192 g_hash_table_replace( m_readhash
, (gpointer
)fd
, entry
);
193 m_select
.hReadStatus
[rcnt
] = FALSE
;
194 m_select
.hReadConns
[rcnt
] = (HANDLE
)fd
;
196 else if (cond
== SIPE_MIRANDA_INPUT_WRITE
)
198 for ( rcnt
=0 ; m_select
.hReadConns
[rcnt
] ; rcnt
++ );
199 for ( wcnt
=0 ; m_select
.hWriteConns
[wcnt
] && m_select
.hWriteConns
[wcnt
]!=(HANDLE
)fd
; wcnt
++ );
200 g_hash_table_replace( m_writehash
, (gpointer
)fd
, entry
);
201 m_select
.hWriteStatus
[rcnt
] = FALSE
;
202 m_select
.hWriteConns
[rcnt
] = (HANDLE
)fd
;
206 CloseHandle((HANDLE
) mir_forkthreadex( inputloop
, NULL
, 8192, NULL
));
208 SIPE_DEBUG_INFO_NOFORMAT("Added input handler.");
213 sipe_miranda_input_remove(struct sipe_miranda_sel_entry
*entry
)
219 SIPE_DEBUG_INFO_NOFORMAT("Not a valid entry. NULL.");
223 if (entry
->sig
!= ENTRY_SIG
)
225 SIPE_DEBUG_INFO("Not a valid entry. Sig is <%08x>.", entry
->sig
);
229 if (g_hash_table_lookup(m_readhash
, (gconstpointer
)entry
->fd
) == entry
)
231 for ( cnt
=0 ; m_select
.hReadConns
[cnt
] && m_select
.hReadConns
[cnt
]!=(HANDLE
)entry
->fd
; cnt
++ );
232 for ( ; m_select
.hReadConns
[cnt
] ; cnt
++ ) m_select
.hReadConns
[cnt
] = m_select
.hReadConns
[cnt
+1];
233 g_hash_table_remove(m_readhash
, (gconstpointer
)entry
->fd
);
236 if (g_hash_table_lookup(m_writehash
, (gconstpointer
)entry
->fd
) == entry
)
238 for ( cnt
=0 ; m_select
.hWriteConns
[cnt
] && m_select
.hWriteConns
[cnt
]!=(HANDLE
)entry
->fd
; cnt
++ );
239 for ( ; m_select
.hWriteConns
[cnt
] ; cnt
++ ) m_select
.hWriteConns
[cnt
] = m_select
.hWriteConns
[cnt
+1];
240 g_hash_table_remove(m_writehash
, (gconstpointer
)entry
->fd
);
243 /* Set fd to NULL so we won't try to call the callback if we're
244 currently waiting to get back to the main thread */
247 /* Add it to the list of entries that can be freed after the next select
248 * loop in the thread that's handling the actual select
250 g_list_append( m_entries
, entry
);