WvStreams
wvwinstreamclone.cc
00001 
00002 #define WINVER 0x0500
00003 #include "wvwinstreamclone.h"
00004 
00005 ATOM WvWinStreamClone::s_aClass = 0;
00006 WvWinStreamClone::WndVector WvWinStreamClone::s_wndpool;
00007 WvWinStreamClone::WndStreamMap WvWinStreamClone::s_wndmap;
00008 
00009 HWND WvWinStreamClone::alloc_wnd()
00010 {
00011     if (s_wndpool.empty())
00012     {
00013         HWND hWnd = CreateWindow(
00014             "WvWinStreamClone",
00015             "WvWinStreamWindowName",
00016             WS_POPUP | WS_DISABLED,
00017             CW_USEDEFAULT, // initial x position
00018             CW_USEDEFAULT, // initial y position
00019             CW_USEDEFAULT, // initial x extent
00020             CW_USEDEFAULT, // initial y extent
00021             HWND_MESSAGE,
00022             NULL,
00023             NULL,
00024             NULL
00025         );
00026         assert(hWnd);
00027         s_wndpool.push_back(hWnd);
00028     }
00029 
00030     HWND hWnd = s_wndpool.back();
00031     s_wndpool.pop_back();
00032 
00033     // associate window with this instance
00034     s_wndmap[hWnd] = this;
00035 
00036     return hWnd;
00037 }
00038 
00039 void WvWinStreamClone::free_wnd(HWND w)
00040 {
00041     s_wndpool.push_back(w);
00042 }
00043 
00044 DWORD WvWinStreamClone::Initialize()
00045 {
00046     WNDCLASS wc;
00047     wc.style = CS_HREDRAW | CS_VREDRAW;
00048     wc.lpfnWndProc = WvWinStreamClone::WndProc;
00049     wc.cbClsExtra = 0;
00050     wc.cbWndExtra = 0;
00051     wc.hInstance = GetModuleHandle(NULL);
00052     wc.hIcon = NULL; 
00053     wc.hCursor = NULL;
00054     wc.hbrBackground = NULL; 
00055     wc.lpszMenuName = NULL;
00056     wc.lpszClassName = "WvWinStreamClone";
00057 
00058     s_aClass = RegisterClass(&wc);
00059     if (!s_aClass)
00060     {
00061         DWORD error = GetLastError();
00062         return error;
00063     }
00064     return 0;
00065 }
00066 
00067 WvWinStreamClone::WvWinStreamClone(WvStream * _cloned) :
00068     WvStreamClone(_cloned), m_pending_callback(false), m_select_in_progress(false),
00069     m_msec_timeout(500)
00070 {
00071     memset(&m_si, 0, sizeof(m_si));
00072     m_hWnd = alloc_wnd();
00073     pre_poll();
00074 }
00075 
00076 WvWinStreamClone::~WvWinStreamClone()
00077 {
00078     free_wnd(m_hWnd);
00079 }
00080 
00081 // for each socket in "set", add the "event" to the its associated mask in sockmap 
00082 void WvWinStreamClone::select_set(SocketEventsMap &sockmap, fd_set *set, long event )
00083 {
00084     for (unsigned i=0; i<set->fd_count; i++)
00085     {
00086         SOCKET &socket = set->fd_array[i];
00087         sockmap[socket] |= event;
00088     }
00089 
00090     FD_ZERO(set);
00091 }
00092 
00093 void WvWinStreamClone::pre_poll()
00094 {
00095     this->_build_selectinfo(m_si, m_msec_timeout, 
00096                             false, false, false, true);
00097     
00098     // We must only call WSAAsyncSelect once per socket, so we need
00099     // to collect all the events from each set first, grouping them by
00100     // socket rather than by event
00101     SocketEventsMap sockmap;
00102     this->select_set(sockmap, &m_si.read, FD_READ);
00103     this->select_set(sockmap, &m_si.write, FD_WRITE);
00104     this->select_set(sockmap, &m_si.except, FD_OOB);
00105 
00106     // Call WSAAsyncSelect, asking the OS to send us a message when the socket
00107     // becomes readable | writable | exceptional
00108     for (SocketEventsMap::iterator i = sockmap.begin(); i!=sockmap.end(); ++i)
00109     {
00110         SOCKET socket = (*i).first;
00111         long events = (*i).second;
00112         
00113         int result = ::WSAAsyncSelect(socket, m_hWnd, WM_SELECT, 
00114                                       events | FD_CONNECT | FD_CLOSE | FD_ACCEPT);
00115         assert(result == 0);
00116     }
00117 
00118     // alarm
00119     ::KillTimer(m_hWnd, TIMER_ID);
00120     if (m_si.msec_timeout >= 0)
00121     {
00122         ::SetTimer(m_hWnd, TIMER_ID, m_si.msec_timeout, NULL);
00123     }
00124 
00125     m_select_in_progress = true;
00126 }
00127 
00128 void WvWinStreamClone::post_poll()
00129 {
00130     bool sure = this->_process_selectinfo(m_si, true);
00131     
00132     if (sure || m_pending_callback)
00133     {
00134         m_pending_callback = false;
00135         callback();
00136         if (globalstream) globalstream->callback();
00137     }
00138 }
00139 
00140 void WvWinStreamClone::select_callback(SOCKET socket, int events, int error)
00141 {
00142     if (events | FD_READ) FD_SET(socket, &m_si.read);
00143     if (events | FD_WRITE) FD_SET(socket, &m_si.write);
00144     if (events | FD_OOB) FD_SET(socket, &m_si.except);
00145     m_pending_callback = true;
00146 
00147     if (m_select_in_progress)
00148     {
00149         ::PostMessage(m_hWnd, WM_DONESELECT, 0, 0);
00150         m_select_in_progress = false;
00151     }
00152 }
00153 
00154 LRESULT CALLBACK WvWinStreamClone::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
00155 {
00156     switch (uMsg)
00157     {
00158     case WM_DONESELECT:
00159     {   
00160         WvWinStreamClone *_this = s_wndmap[hwnd];
00161         assert(_this);
00162         _this->post_poll();
00163         _this->pre_poll();
00164         
00165         break;
00166     }
00167 
00168     case WM_SELECT:
00169     {
00170         WvWinStreamClone *_this = s_wndmap[hwnd];       
00171         assert(_this);
00172         SOCKET socket = wParam;
00173         int events = WSAGETSELECTEVENT(lParam);
00174         int error = WSAGETSELECTERROR(lParam);
00175         _this->select_callback( socket, events, error );
00176         
00177         break;
00178     }
00179 
00180     case WM_TIMER:
00181     {
00182         ::PostMessage(hwnd, WM_DONESELECT, 0, 0);
00183 
00184         break;
00185     }
00186 
00187     default:
00188         return DefWindowProc(hwnd, uMsg, wParam, lParam);
00189     }
00190     return 0;
00191 }
00192 
00193 
00194 
00195 void WvWinStreamClone::setclone(IWvStream *newclone)
00196 {
00197     WvStreamClone::setclone(newclone);
00198     
00199     if (newclone != NULL)
00200         my_type = WvString("WvWinStreamClone:%s", newclone->wstype());
00201     else
00202         my_type = "WvWinStreamClone:(none)";
00203 }