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
;
49 /* Private. For locking only */
52 sipe_miranda_input_condition cond
;
56 input_cb_async(void *data
)
58 struct sipe_miranda_sel_entry
*entry
= (struct sipe_miranda_sel_entry
*)data
;
59 if (entry
->fd
== NULL
)
61 SIPE_DEBUG_INFO("Entry <%08x> already removed. Not calling read/write function", entry
);
63 SIPE_DEBUG_INFO("Calling real read/write function for entry <%08x>", entry
);
64 entry
->func(entry
->user_data
, entry
->source
, entry
->cond
);
66 SetEvent(entry
->hDoneEvent
);
69 static unsigned __stdcall
73 struct sipe_miranda_sel_entry
*entry
;
76 m_select
.cbSize
= sizeof(m_select
);
77 m_select
.dwTimeout
= 6000;
79 while( m_select
.hReadConns
[0] || m_select
.hWriteConns
[0])
82 SIPE_DEBUG_INFO_NOFORMAT("About to run select");
83 lstRes
= CallService(MS_NETLIB_SELECTEX
, 0, (LPARAM
)&m_select
);
86 SIPE_DEBUG_INFO_NOFORMAT("Connection failed while waiting.");
91 SIPE_DEBUG_INFO_NOFORMAT("Select Timeout.");
95 SIPE_DEBUG_INFO_NOFORMAT("Back from select");
97 for ( cnt
=0 ; m_select
.hReadConns
[cnt
] ; cnt
++ )
99 if (!m_select
.hReadStatus
[cnt
]) continue;
100 SIPE_DEBUG_INFO("FD at position <%d> ready to read.", cnt
);
101 entry
= (struct sipe_miranda_sel_entry
*)g_hash_table_lookup(m_readhash
, (gconstpointer
)m_select
.hReadConns
[cnt
]);
104 SIPE_DEBUG_INFO_NOFORMAT("ERROR: no read handler found.");
107 SIPE_DEBUG_INFO_NOFORMAT("About to call read function.");
108 entry
->source
= (gint
)m_select
.hReadConns
[cnt
];
109 entry
->cond
= SIPE_MIRANDA_INPUT_READ
;
110 entry
->hDoneEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
111 CallFunctionAsync(input_cb_async
, entry
);
112 WaitForSingleObject(entry
->hDoneEvent
, INFINITE
);
113 CloseHandle(entry
->hDoneEvent
);
114 SIPE_DEBUG_INFO_NOFORMAT("read function returned.");
117 for ( cnt
=0 ; m_select
.hWriteConns
[cnt
] ; cnt
++ )
119 if (!m_select
.hWriteStatus
[cnt
]) continue;
120 SIPE_DEBUG_INFO("FD at position <%d> ready to write.", cnt
);
121 entry
= (struct sipe_miranda_sel_entry
*)g_hash_table_lookup(m_writehash
, (gconstpointer
)m_select
.hWriteConns
[cnt
]);
124 SIPE_DEBUG_INFO_NOFORMAT("ERROR: no write handler found.");
127 SIPE_DEBUG_INFO_NOFORMAT("About to call write function.");
128 entry
->source
= (gint
)m_select
.hWriteConns
[cnt
];
129 entry
->cond
= SIPE_MIRANDA_INPUT_WRITE
;
130 entry
->hDoneEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
131 CallFunctionAsync(input_cb_async
, entry
);
132 WaitForSingleObject(entry
->hDoneEvent
, INFINITE
);
133 CloseHandle(entry
->hDoneEvent
);
134 SIPE_DEBUG_INFO_NOFORMAT("write function returned.");
138 /* Free all removed entries */
139 while (m_entries
) g_list_delete_link(m_entries
, g_list_last(m_entries
));
145 struct sipe_miranda_sel_entry
*
146 sipe_miranda_input_add(HANDLE fd
, sipe_miranda_input_condition cond
, sipe_miranda_input_function func
, gpointer user_data
)
150 struct sipe_miranda_sel_entry
*entry
;
153 m_readhash
= g_hash_table_new(NULL
, NULL
);
156 m_writehash
= g_hash_table_new(NULL
, NULL
);
158 if ((cond
!= SIPE_MIRANDA_INPUT_READ
) && (cond
!= SIPE_MIRANDA_INPUT_WRITE
))
160 SIPE_DEBUG_INFO("Invalid input condition <%d> cond.", cond
);
164 entry
= g_new0(struct sipe_miranda_sel_entry
,1);
165 entry
->sig
= ENTRY_SIG
;
167 entry
->user_data
= user_data
;
170 if (cond
== SIPE_MIRANDA_INPUT_READ
)
172 for ( rcnt
=0 ; m_select
.hReadConns
[rcnt
] && m_select
.hReadConns
[rcnt
]!=(HANDLE
)fd
; rcnt
++ );
173 m_select
.hReadConns
[rcnt
] = (HANDLE
)fd
;
174 g_hash_table_replace( m_readhash
, (gpointer
)fd
, entry
);
176 else if (cond
== SIPE_MIRANDA_INPUT_WRITE
)
178 for ( wcnt
=0 ; m_select
.hWriteConns
[wcnt
] && m_select
.hWriteConns
[wcnt
]!=(HANDLE
)fd
; wcnt
++ );
179 m_select
.hWriteConns
[rcnt
] = (HANDLE
)fd
;
180 g_hash_table_replace( m_writehash
, (gpointer
)fd
, entry
);
184 CloseHandle((HANDLE
) mir_forkthreadex( inputloop
, NULL
, 8192, NULL
));
186 SIPE_DEBUG_INFO_NOFORMAT("Added input handler.");
191 sipe_miranda_input_remove(struct sipe_miranda_sel_entry
*entry
)
197 SIPE_DEBUG_INFO_NOFORMAT("Not a valid entry. NULL.");
201 if (entry
->sig
!= ENTRY_SIG
)
203 SIPE_DEBUG_INFO("Not a valid entry. Sig is <%08x>.", entry
->sig
);
207 if (g_hash_table_lookup(m_readhash
, (gconstpointer
)entry
->fd
) == entry
)
209 for ( cnt
=0 ; m_select
.hReadConns
[cnt
] && m_select
.hReadConns
[cnt
]!=(HANDLE
)entry
->fd
; cnt
++ );
210 for ( ; m_select
.hReadConns
[cnt
] ; cnt
++ ) m_select
.hReadConns
[cnt
] = m_select
.hReadConns
[cnt
+1];
211 g_hash_table_remove(m_readhash
, (gconstpointer
)entry
->fd
);
214 if (g_hash_table_lookup(m_writehash
, (gconstpointer
)entry
->fd
) == entry
)
216 for ( cnt
=0 ; m_select
.hWriteConns
[cnt
] && m_select
.hWriteConns
[cnt
]!=(HANDLE
)entry
->fd
; cnt
++ );
217 for ( ; m_select
.hWriteConns
[cnt
] ; cnt
++ ) m_select
.hWriteConns
[cnt
] = m_select
.hWriteConns
[cnt
+1];
218 g_hash_table_remove(m_writehash
, (gconstpointer
)entry
->fd
);
221 /* Set fd to NULL so we won't try to call the callback if we're
222 currently waiting to get back to the main thread */
225 /* Add it to the list of entries that can be freed after the next select
226 * loop in the thread that's handling the actual select
228 g_list_append( m_entries
, entry
);