aboutsummaryrefslogtreecommitdiff
path: root/Src/omBrowser/browserThread.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Src/omBrowser/browserThread.cpp')
-rw-r--r--Src/omBrowser/browserThread.cpp413
1 files changed, 413 insertions, 0 deletions
diff --git a/Src/omBrowser/browserThread.cpp b/Src/omBrowser/browserThread.cpp
new file mode 100644
index 00000000..36de9e80
--- /dev/null
+++ b/Src/omBrowser/browserThread.cpp
@@ -0,0 +1,413 @@
+#include "main.h"
+#include "./browserThread.h"
+#include "../nu/threadname.h"
+
+#include <exdisp.h>
+
+typedef struct __BROWSERTHREADCRAEATEPARAM
+{
+ BTCREATEWNDPROC fnCreateWnd;
+ BTKEYFILTERPROC fnKeyFilter;
+ ULONG_PTR user;
+ HANDLE readyEvent;
+ HWND hHost;
+ HWND hWinamp;
+} BROWSERTHREADCREATEPARAM;
+
+typedef struct __BROWSERTHREAD
+{
+ HHOOK messageHook;
+ HANDLE wakeupEvent;
+ UINT flags;
+} BROWSERTHREAD;
+
+#define NAVIGATE_WAITTIMEOUT 30
+
+static size_t tlsIndex = TLS_OUT_OF_INDEXES;
+static UINT BHTM_DESTROY = 0xFEFE;
+
+static DWORD CALLBACK BrowserThread_MainLoop(LPVOID param);
+
+#define GetThreadInstance() ((TLS_OUT_OF_INDEXES != tlsIndex) ? (BROWSERTHREAD*)Plugin_TlsGetValue(tlsIndex) : NULL)
+
+BOOL BrowserThread_IsQuiting()
+{
+ BROWSERTHREAD *thread = GetThreadInstance();
+ return (NULL == thread || 0 != ((BHTF_BEGINDESTROY | BHTF_QUITLOOP) & thread->flags));
+}
+
+BOOL BrowserThread_SetFlags(UINT flags, UINT flagsMask, BOOL fAlarm)
+{
+ BROWSERTHREAD *thread = GetThreadInstance();
+ if (NULL == thread) return FALSE;
+
+ thread->flags = ((thread->flags & flagsMask) | flags);
+ if (FALSE == fAlarm)
+ return TRUE;
+
+ return (NULL != thread->wakeupEvent && SetEvent(thread->wakeupEvent));
+}
+
+
+HANDLE BrowserThread_Create(HWND hWinamp, BTCREATEWNDPROC fnCreateWnd, ULONG_PTR user, BTKEYFILTERPROC fnKeyFilter, HWND *pWnd, DWORD *pThreadId)
+{
+ if (NULL == fnCreateWnd)
+ return NULL;
+
+ if (TLS_OUT_OF_INDEXES == tlsIndex)
+ {
+ tlsIndex = Plugin_TlsAlloc();
+ if (TLS_OUT_OF_INDEXES == tlsIndex)
+ return NULL;
+ }
+
+ DWORD threadId;
+
+ BROWSERTHREADCREATEPARAM param;
+ ZeroMemory(&param, sizeof(BROWSERTHREADCREATEPARAM));
+
+ param.fnCreateWnd = fnCreateWnd;
+ param.fnKeyFilter = fnKeyFilter;
+ param.user = user;
+ param.readyEvent = CreateEvent(0, TRUE, FALSE, 0);
+ param.hWinamp = hWinamp;
+
+ HANDLE hThread = CreateThread(NULL, 0, BrowserThread_MainLoop, (LPVOID)&param, 0, &threadId);
+
+ if (NULL != hThread)
+ {
+ if (NULL != param.readyEvent)
+ WaitForSingleObject(param.readyEvent, INFINITE);
+ }
+ else
+ {
+ if (NULL != param.hHost)
+ {
+ DestroyWindow(param.hHost);
+ param.hHost = NULL;
+ }
+ threadId = 0;
+ }
+
+ if (NULL != param.readyEvent)
+ CloseHandle(param.readyEvent);
+
+
+ if (NULL != pThreadId)
+ *pThreadId = threadId;
+
+ if (NULL != pWnd)
+ *pWnd = param.hHost;
+
+ return hThread;
+}
+
+
+
+BOOL BrowserThread_PostDestroyEx(DWORD threadId, HWND hHost)
+{
+ if (0 == BHTM_DESTROY)
+ BHTM_DESTROY = RegisterWindowMessage(L"omBrowserDestroyMsg");
+
+ if (0 == BHTM_DESTROY ||
+ FALSE == PostThreadMessage(threadId, BHTM_DESTROY, 0, (LPARAM)hHost))
+ {
+ return FALSE;
+ }
+
+ BrowserThread_SetFlags(BHTF_BEGINDESTROY, BHTF_BEGINDESTROY, FALSE);
+ return TRUE;
+}
+
+BOOL BrowserThread_PostDestroy(HWND hHost)
+{
+ return BrowserThread_PostDestroyEx(GetCurrentThreadId(), hHost);
+}
+
+BOOL BrowserThread_WaitNavigateComplete(IWebBrowser2 *pWeb2, UINT waitMax)
+{
+ MSG msg;
+ READYSTATE state;
+ if (NULL == pWeb2)
+ return FALSE;
+
+ BOOL resultOk = FALSE;
+ DWORD tickStart = GetTickCount();
+
+ for(;;)
+ {
+ if (FAILED(pWeb2->get_ReadyState(&state)))
+ break;
+
+ if (READYSTATE_INTERACTIVE <= state)
+ {
+ resultOk = TRUE;
+ break;
+ }
+ else
+ {
+ DWORD tickNow = GetTickCount();
+ if (tickNow < tickStart || (tickNow - tickStart) >= waitMax)
+ {
+ break; // time out
+ }
+ }
+
+ DWORD status = MsgWaitForMultipleObjectsEx(0, NULL, NAVIGATE_WAITTIMEOUT, QS_POSTMESSAGE | QS_TIMER | QS_SENDMESSAGE, MWMO_ALERTABLE);
+ switch(status)
+ {
+ case (WAIT_OBJECT_0 + 0):
+ while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ if (!CallMsgFilter(&msg, MSGF_BROWSERLOOP))
+ {
+ DispatchMessageW(&msg);
+ }
+ }
+ break;
+ }
+ }
+ return resultOk;
+}
+
+static BOOL BrowserThread_HandleMessage(MSG *pMsg)
+{
+ switch(pMsg->message)
+ {
+ case WM_QUIT:
+ BrowserThread_SetFlags(BHTF_QUITLOOP, BHTF_QUITLOOP, TRUE);
+ return TRUE;
+ }
+
+ if (0 != BHTM_DESTROY && BHTM_DESTROY == pMsg->message)
+ {
+ HWND hHost = (HWND)pMsg->lParam;
+ if (NULL != hHost)
+ {
+ BrowserThread_SetFlags(BHTF_BEGINDESTROY, BHTF_BEGINDESTROY, FALSE);
+ SendMessage(hHost, BTM_RELEASECONTAINER, 0, 0L);
+ DestroyWindow(hHost);
+ }
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static LRESULT CALLBACK BrowserThread_MessageFilterProc(INT code, WPARAM wParam, LPARAM lParam)
+{
+ BROWSERTHREAD *thread = GetThreadInstance();
+ if (code >= 0)
+ {
+ if (BrowserThread_HandleMessage((MSG*)lParam))
+ {
+ return TRUE;
+ }
+ }
+
+ return (NULL != thread && NULL != thread->messageHook) ?
+ CallNextHookEx(thread->messageHook, code, wParam, lParam) :
+ FALSE;
+}
+
+static BOOL CALLBACK BrowserThread_DefaultKeyFilter(HWND hwnd, MSG *pMsg)
+{
+ return FALSE;
+}
+
+
+inline static BOOL BrowserThread_ProcessMessage(HWND hHost, HWND hWinamp, MSG *pMsg, BTKEYFILTERPROC IsHostMessage)
+{
+ if (hHost != pMsg->hwnd && FALSE == IsChild(hHost, pMsg->hwnd))
+ return FALSE;
+
+ if (pMsg->message >= WM_KEYFIRST && pMsg->message <= WM_KEYLAST)
+ {
+ if (FALSE != IsHostMessage(hHost, pMsg))
+ return TRUE;
+
+ switch(pMsg->wParam)
+ {
+ case VK_TAB:
+ {
+ HWND hOwner = (HWND)(LONG_PTR)GetWindowLongPtr(hHost, GWLP_HWNDPARENT);
+ if (NULL == hOwner || hWinamp == hOwner)
+ hOwner = hHost;
+ return IsDialogMessageW(hOwner, pMsg);
+ }
+ break;
+ }
+ }
+
+ if (pMsg->message == WM_MOUSEWHEEL)
+ {
+ POINT cursor;
+ HWND targetWindow;
+
+ POINTSTOPOINT(cursor, pMsg->lParam);
+ targetWindow = WindowFromPoint(cursor);
+
+ if (NULL != targetWindow &&
+ FALSE == IsChild(hHost, targetWindow ) &&
+ GetWindowThreadProcessId(targetWindow, NULL) != GetWindowThreadProcessId(hHost, NULL))
+ {
+ PostMessage(hWinamp, pMsg->message, pMsg->wParam, pMsg->lParam);
+ return TRUE;
+ }
+
+ }
+
+ return FALSE;
+}
+
+static void BrowserThread_FinishThread(BROWSERTHREAD *thread)
+{
+ if (NULL != thread)
+ {
+ if (NULL != thread->messageHook)
+ {
+ UnhookWindowsHookEx(thread->messageHook);
+ thread->messageHook = NULL;
+ }
+
+ if (NULL != thread->wakeupEvent)
+ {
+ CloseHandle(thread->wakeupEvent);
+ thread->wakeupEvent = NULL;
+ }
+ }
+
+ if (TLS_OUT_OF_INDEXES != tlsIndex)
+ Plugin_TlsSetValue(tlsIndex, NULL);
+
+ OleUninitialize();
+
+ #ifdef _DEBUG
+ aTRACE_FMT("[%d] %S: thread exit\r\n", GetCurrentThreadId(), OMBROWSER_NAME);
+ #endif // _DEBUG
+}
+
+static DWORD CALLBACK BrowserThread_MainLoop(LPVOID param)
+{
+
+#ifdef _DEBUG
+ SetThreadName(GetCurrentThreadId(), "omBrowserThread");
+ aTRACE_FMT("[%d] %S: thread created\r\n", GetCurrentThreadId(), OMBROWSER_NAME);
+#endif //_DEBUG
+
+ BROWSERTHREADCREATEPARAM *createParam = (BROWSERTHREADCREATEPARAM*)param;
+
+ HWND hWinamp = createParam->hWinamp;
+
+ BROWSERTHREAD thread;
+ ZeroMemory(&thread, sizeof(BROWSERTHREAD));
+
+ if (TLS_OUT_OF_INDEXES != tlsIndex)
+ Plugin_TlsSetValue(tlsIndex, &thread);
+
+ MSG msg;
+ PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
+
+ BTKEYFILTERPROC IsHostMessage = (NULL != createParam->fnKeyFilter) ? createParam->fnKeyFilter : BrowserThread_DefaultKeyFilter;
+
+ thread.messageHook = SetWindowsHookEx(WH_MSGFILTER, BrowserThread_MessageFilterProc, NULL, GetCurrentThreadId());
+ thread.wakeupEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ HWND hHost = createParam->fnCreateWnd(createParam->user);
+
+ createParam->hHost = hHost;
+
+#ifdef _DEBUG
+ if (NULL != hHost)
+ aTRACE_FMT("[%d] %S: host created\r\n", GetCurrentThreadId(), OMBROWSER_NAME);
+ else
+ aTRACE_FMT("[%d] %S: host creation fialed\r\n", GetCurrentThreadId(), OMBROWSER_NAME);
+#endif //_DEBUG
+
+ if (NULL != createParam->readyEvent)
+ SetEvent(createParam->readyEvent);
+
+
+ if (NULL != hHost && FAILED(OleInitialize(0)))
+ {
+ DestroyWindow(hHost);
+ hHost = NULL;
+ }
+
+ if (NULL == hHost)
+ {
+ BrowserThread_FinishThread(&thread);
+ return -1;
+ }
+
+ SendMessage(hHost, BTM_INITCONTAINER, (WPARAM)hWinamp, 0L);
+ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
+
+ while (0 == (BHTF_QUITLOOP & thread.flags))
+ {
+ DWORD status = MsgWaitForMultipleObjectsEx(1, &thread.wakeupEvent, INFINITE,
+ QS_ALLPOSTMESSAGE | QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);
+
+ switch(status)
+ {
+ case (WAIT_OBJECT_0 + 0):
+ // wake up!!!
+ break;
+
+ case (WAIT_OBJECT_0 + 1):
+ while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ if (!CallMsgFilter(&msg, MSGF_BROWSERLOOP) && NULL != msg.hwnd)
+ {
+ if (0 == (BHTF_BEGINDESTROY & thread.flags))
+ {
+ if (FALSE == BrowserThread_ProcessMessage(hHost, hWinamp, &msg, IsHostMessage))
+ {
+ TranslateMessage(&msg);
+ DispatchMessageW(&msg);
+ }
+ }
+ else
+ {
+ DispatchMessageW(&msg);
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ BrowserThread_FinishThread(&thread);
+ return 0;
+}
+
+INT BrowserThread_ModalLoop(HWND hwnd, HANDLE hCancel, DWORD timeout)
+{
+ MSG msg;
+ for (;;)
+ {
+ DWORD status = MsgWaitForMultipleObjectsEx(1, &hCancel, timeout, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);
+ if (WAIT_OBJECT_0 == status)
+ {
+ return 0;
+ }
+ else if ((WAIT_OBJECT_0 + 1) == status)
+ {
+ while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ if (msg.message == WM_QUIT)
+ {
+ PostQuitMessage((INT)msg.wParam);
+ return (INT)msg.wParam;
+ }
+
+ if (!IsDialogMessageW(hwnd, &msg))
+ {
+ TranslateMessage(&msg);
+ DispatchMessageW(&msg);
+ }
+ }
+ }
+ }
+
+ return 0;
+} \ No newline at end of file