#pragma once #include "../Mil.h" #include "../Module.h" #include "../ModuleExecuteProxy.h" #include "WindowProcedureRedirector.h" #ifdef _MSC_VER // XXX #pragma warning (disable: 4100) #endif namespace mil { const UINT WM_MIL_GUIMODULE = WM_APP+100; /** * public should be thread safe */ template class GuiModule : public Thread > { class EventBase : boost::noncopyable { public: //void (*execute)(void* event, void* target); }; template class Event : public EventBase { public: int owner_id; T data; }; atomic hSharedWnd; //LONG_PTR breakLoopRequest; PVOID breakLoopRequest; //DWORD_PTR destroyed; atomic destroyed; bool repost; bool handled; LRESULT handled_result; protected: HWND hLocalWnd; public: Tls tls; GuiModule() : hLocalWnd(NULL), hSharedWnd(NULL), breakLoopRequest(false), repost(false), handled(true), handled_result(0), destroyed(0), wndproc(getChild()), tls(this->thread_id) { } ~GuiModule() { ExitEvent e; post(e, *this); destroyed.store((PVOID)1); HWND w = getWindow(); if (IsWindow(w)) { if (DestroyWindow(w) == 0) { // error //DebugBreak(); } } this->join(); } template static void dispatch(void* event_, void* target_) { typedef Child Target; Target& target = *static_cast(target_); target.repost = false; if (sizeof(Data) <= sizeof(LPARAM)) { Data data; memcpy(&data, &event_, sizeof(data)); mil::ModuleExecuteProxy::execute(data, target); if (!target.repost) { return; } LPARAM l = 0; memcpy(&l, &data, sizeof(l)); void (*func)(void*, void*) = dispatch; PostMessage(target.hLocalWnd /* write operation should be done */ , WM_MIL_GUIMODULE, reinterpret_cast(func), l); return; } Event& event = *static_cast*>(event_); mil::ModuleExecuteProxy::execute(event.data, target); if (target.repost) { void (*func)(void*, void*) = dispatch; PostMessage(target.hLocalWnd /* write operation should be done */ , WM_MIL_GUIMODULE, reinterpret_cast(func) , reinterpret_cast(&event)); return; } if (event.owner_id == target.thread_id) { mil::pool::ReturnMemoryProxy::free(&event, target.tls.producer, mil::pool::Blocks >::value); return; } void* mem = NULL; pool::MemoryList* m = NULL; boost::tie(mem, m) = target.tls.consumer.work(&event); if (!m) { return; } pool::ReturnMemoryEvent >::value> e; e.memory = m; void* t = threads[event.owner_id].load(); if (!t) { // error return; } (reinterpret_cast(t))->post(e, target, mem, event.owner_id); } protected: void setWindow(HWND hWnd_) { hLocalWnd = hWnd_; hSharedWnd.store(hWnd_); } public: HWND getWindow() { return hSharedWnd.load(); } Child& getChild() { return *static_cast(this); } void destroy() //XXX thread safe { //PostQuitMessage(0); breakLoopRequest = (PVOID)1; destroyed.store((PVOID)1); if (IsWindow(hLocalWnd)) { if (DestroyWindow(hLocalWnd) == 0) { // error //DebugBreak(); } } wndproc.inactivate(); } void loop() { if (breakLoopRequest || destroyed.load() || !IsWindow(this->hLocalWnd)) { return; } MSG msg = {}; while (__builtin_expect(!breakLoopRequest, true)) { BOOL result = GetMessage(&msg, NULL, 0, 0); if (!result) { break; } else if (result == -1) { // error halt << msg.message; break; } TranslateMessage(&msg); DispatchMessage(&msg); } } template void post(const T& event, Sender& sender, void* memory = NULL, intptr_t owner_id = 0) { HWND hWnd = sender.tls.thread[this->thread_id].hWnd; if (__builtin_expect(!hWnd, false)) { hWnd = getWindow(); if (!IsWindow(hWnd)) { return; } sender.tls.thread[this->thread_id].hWnd = hWnd; } if (sizeof(T) <= sizeof(LPARAM)) { LPARAM l = 0; memcpy(&l, &event, sizeof(l)); void (*func)(void*, void*) = dispatch; PostMessage(hWnd, WM_MIL_GUIMODULE, reinterpret_cast(func),l); return; } void* p = NULL; if (memory) { p = memory; } else { mil::pool::Producer& producer = sender.tls.producer; p = producer.malloc)>(); } if (p == NULL) { // XXX return; } Event* e = new(p) Event; e->data = event; if (memory) { e->owner_id = owner_id; } else { e->owner_id = sender.tls.thread_id; } void (*func)(void*, void*) = dispatch; if (PostMessage(hWnd, WM_MIL_GUIMODULE, reinterpret_cast(func), reinterpret_cast(e)) == 0) { // error std::cout << "e" << std::flush; } } // Message Cracker Wizard for Win32 SDK Developers // http://www.codeproject.com/KB/winsdk/msgcrackwizard.aspx #include "DummyMessageHandler-inl.h" protected: void repostEvent() { repost = true; } void SetMsgHandled(bool handled_, LRESULT handled_result_ = 0) { handled = handled_; handled_result = handled_result_; } LRESULT MessageMap(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { if (msg == mil::WM_MIL_GUIMODULE) { BOOST_STATIC_ASSERT(sizeof(EventBase*) == sizeof(lParam)); EventBase* e = reinterpret_cast(lParam); void (*func)(void*, void*) = reinterpret_cast(wParam); func(e, &getChild()); return 0; } else if (msg == WM_DESTROY || msg == WM_QUIT) { destroy(); } // XXX will be return switch (msg) { #include "MessageCracker-inl.h" default: SetMsgHandled(false); break; } return handled_result; } public: LRESULT WindowProcedure(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { SetMsgHandled(true); LRESULT result = MessageMap(hWnd, msg, wParam, lParam); if (handled) { return result; } return DefWindowProc(hWnd, msg, wParam, lParam); } void execute_front() { MSG msg = {}; unsigned int loop = 10000; while (PeekMessage (&msg,NULL,0,0,PM_REMOVE) != 0) { TranslateMessage(&msg); DispatchMessage(&msg); if (msg.message == WM_MIL_GUIMODULE) { return; } if (loop-- == 0) { return; } } } mil::WindowProcedureRedirector< GuiModule, &GuiModule::WindowProcedure> wndproc; }; }