diff --git a/main.pro b/main.pro index 29619bbd0..eefa861c1 100644 --- a/main.pro +++ b/main.pro @@ -1,33 +1,35 @@ -TEMPLATE = subdirs -CONFIG *= ordered debug_and_release - -!CONFIG(no-client) { - unix:!CONFIG(bundled-speex):system(pkg-config --atleast-version=1.2 speexdsp) { - CONFIG *= no-bundled-speex - } - !CONFIG(no-bundled-speex) { - SUBDIRS *= speexbuild - } - SUBDIRS *= src/mumble plugins - - win32 { - SUBDIRS *= overlay - !CONFIG(no-g15) { - SUBDIRS *= g15helper - } - } - - unix:!macx { - SUBDIRS *= overlay_gl - } - - macx { - SUBDIRS *= overlay_macx - } -} - -!CONFIG(no-server) { - SUBDIRS *= src/murmur -} - -DIST=LICENSE INSTALL README README.Linux CHANGES compiler.pri +TEMPLATE = subdirs +CONFIG *= ordered debug_and_release + +!CONFIG(no-client) { + unix:!CONFIG(bundled-speex):system(pkg-config --atleast-version=1.2 speexdsp) { + CONFIG *= no-bundled-speex + } + !CONFIG(no-bundled-speex) { + SUBDIRS *= speexbuild + } + SUBDIRS *= src/mumble plugins + + win32 { + SUBDIRS *= overlay + !CONFIG(no-g15) { + SUBDIRS *= g15helper + } + } + + unix:!macx { + SUBDIRS *= overlay_gl + } + + macx { + SUBDIRS *= overlay_macx + } +} + +!CONFIG(no-server) { + SUBDIRS *= src/murmur +} + +DIST=LICENSE INSTALL README README.Linux CHANGES + +include(scripts/scripts.pro) diff --git a/overlay/d3d9.cpp b/overlay/d3d9.cpp index 41a1e3bb0..15341665d 100644 --- a/overlay/d3d9.cpp +++ b/overlay/d3d9.cpp @@ -1,680 +1,680 @@ -/* Copyright (C) 2005-2009, Thorvald Natvig - - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - - Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - Neither the name of the Mumble Developers nor the names of its - contributors may be used to endorse or promote products derived from this - software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include "lib.h" -#include - -Direct3D9Data *d3dd; - -typedef IDirect3D9*(WINAPI *pDirect3DCreate9)(UINT SDKVersion) ; -typedef HRESULT(WINAPI *pDirect3DCreate9Ex)(UINT SDKVersion, IDirect3D9Ex **ppD3D) ; - -struct D3DTLVERTEX { - float x, y, z, rhw; // Position - D3DCOLOR color; // Vertex colour - float tu, tv; // Texture coordinates -}; -const DWORD D3DFVF_TLVERTEX = D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1; - -class DevState { - public: - IDirect3DDevice9 *dev; - IDirect3DStateBlock9 *pSB; - - LONG initRefCount; - LONG refCount; - LONG myRefCount; - DWORD dwMyThread; - - LPDIRECT3DTEXTURE9 tex[NUM_TEXTS]; - unsigned int uiCounter[NUM_TEXTS]; - - DevState(); - - void createCleanState(); - void releaseData(); - void releaseAll(); - void draw(); - void postDraw(); -}; - -static map devMap; -static bool bHooked = false; -static bool bChaining = false; -static bool bPresenting = false; - -DevState::DevState() { - dev = NULL; - pSB = NULL; - dwMyThread = 0; - refCount = 0; - myRefCount = 0; - for (int i = 0;i < NUM_TEXTS;i++) - tex[i] = NULL; -} - -void DevState::releaseData() { - ods("D3D9: Release Data"); - - for (int i=0;iRelease(); - tex[i] = NULL; - } -} - -void DevState::releaseAll() { - ods("D3D9: Release All"); - releaseData(); - if (pSB) - pSB->Release(); - pSB = NULL; -} - -void DevState::draw() { - D3DVIEWPORT9 vp; - dev->GetViewport(&vp); - - int idx = 0; - - vector texs; - vector widths; - vector yofs; - vector colors; - - unsigned int y = 0; - - if (sm->fFontSize < 0.01f) - sm->fFontSize = 0.01f; - else if (sm->fFontSize > 1.0f) - sm->fFontSize = 1.0f; - - int iHeight = lround(vp.Height * sm->fFontSize); - - if (iHeight > TEXT_HEIGHT) - iHeight = TEXT_HEIGHT; - - float s = iHeight / 60.0f; - - ods("D3D9: Init: Scale %f. iH %d. Final scale %f", sm->fFontSize, iHeight, s); - - DWORD dwWaitResult = WaitForSingleObject(hSharedMutex, 50L); - if (dwWaitResult == WAIT_OBJECT_0) { - for (int i=0;itexts[i].width == 0) { - y += iHeight / 4; - } else if (sm->texts[i].width > 0) { - if (!tex[i] || (sm->texts[i].uiCounter != uiCounter[i])) { - if (tex[i]) - tex[i]->Release(); - - dev->CreateTexture(sm->texts[i].width, TEXT_HEIGHT, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &tex[i], NULL); - - D3DLOCKED_RECT lr; - tex[i]->LockRect(0, &lr, NULL, D3DLOCK_DISCARD); - - for (int r=0;r(lr.pBits) + r * lr.Pitch; - memcpy(dptr, sm->texts[i].texture + r * TEXT_WIDTH * 4, sm->texts[i].width * 4); - } - - tex[i]->UnlockRect(0); - uiCounter[i] = sm->texts[i].uiCounter; - } - unsigned int w = lround(sm->texts[i].width * s); - texs.push_back(tex[i]); - colors.push_back(sm->texts[i].color); - widths.push_back(w); - yofs.push_back(y); - idx++; - y += iHeight; - } - } - ReleaseMutex(hSharedMutex); - } - - if (idx == 0) - return; - - int height = y; - y = lround(vp.Height * sm->fY); - - if (sm->bTop) { - y -= height; - } else if (sm->bBottom) { - } else { - y -= height / 2; - } - - - if (y < 1) - y = 1; - if ((y + height + 1) > vp.Height) - y = vp.Height - height - 1; - - for (int i=0;ifX); - - if (sm->bLeft) { - x -= width; - } else if (sm->bRight) { - } else { - x -= width / 2; - } - - if (x < 1) - x = 1; - if ((x + width + 1) > vp.Width) - x = vp.Width - width - 1; - - D3DCOLOR color = colors[i]; - - float left = static_cast(x); - float top = static_cast(y + yofs[i]); - float right = left + width; - float bottom = top + iHeight; - - const float z = 1.0f; - D3DTLVERTEX vertices[4] = { - // x, y, z, color, tu, tv - { left, top, z, 1, color, 0, 0 }, - { right, top, z, 1, color, 1, 0 }, - { right, bottom, z, 1, color, 1, 1 }, - { left, bottom, z, 1, color, 0, 1 } - }; - - dev->SetTexture(0, texs[i]); - dev->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, vertices, sizeof(D3DTLVERTEX)); - } -} - -void DevState::createCleanState() { - DWORD dwOldThread = dwMyThread; - if (dwOldThread) { - ods("CreateCleanState from other thread."); - } - dwMyThread = GetCurrentThreadId(); - - if (pSB) - pSB->Release(); - pSB = NULL; - - IDirect3DStateBlock9* pStateBlock = NULL; - dev->CreateStateBlock(D3DSBT_ALL, &pStateBlock); - pStateBlock->Capture(); - - dev->CreateStateBlock(D3DSBT_ALL, &pSB); - - D3DVIEWPORT9 vp; - dev->GetViewport(&vp); - - dev->SetVertexShader(NULL); - dev->SetPixelShader(NULL); - dev->SetFVF(D3DFVF_TLVERTEX); - - dev->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID); - dev->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD); - dev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); // 0x16 - dev->SetRenderState(D3DRS_WRAP0, FALSE); // 0x80 - - dev->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); - dev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); - dev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); - - dev->SetRenderState(D3DRS_ALPHATESTENABLE, TRUE); - dev->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATER); - - dev->SetRenderState(D3DRS_ZENABLE, FALSE); - dev->SetRenderState(D3DRS_ZWRITEENABLE, FALSE); - dev->SetRenderState(D3DRS_ZFUNC, D3DCMP_ALWAYS); - dev->SetRenderState(D3DRS_COLORVERTEX, FALSE); - - dev->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE); - dev->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); - dev->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); - - dev->SetRenderState(D3DRS_LIGHTING, FALSE); - - pSB->Capture(); - - pStateBlock->Apply(); - pStateBlock->Release(); - - dwMyThread = dwOldThread; -} - -static HardHook hhCreateDevice; -static HardHook hhCreateDeviceEx; -static HardHook hhReset; -static HardHook hhAddRef; -static HardHook hhRelease; -static HardHook hhPresent; -static HardHook hhPresentEx; -static HardHook hhSwapPresent; - -static void doPresent(IDirect3DDevice9 *idd) { - DevState *ds = devMap[idd]; - - if (ds && sm->bShow) { - DWORD dwOldThread = ds->dwMyThread; - if (dwOldThread) - ods("doPresent from other thread"); - ds->dwMyThread = GetCurrentThreadId(); - - IDirect3DSurface9 *pTarget = NULL; - IDirect3DSurface9 *pRenderTarget = NULL; - idd->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &pTarget); - idd->GetRenderTarget(0, &pRenderTarget); - - ods("D3D9: doPresent Back %p RenderT %p",pTarget,pRenderTarget); - - - IDirect3DStateBlock9* pStateBlock = NULL; - idd->CreateStateBlock(D3DSBT_ALL, &pStateBlock); - pStateBlock->Capture(); - - ds->pSB->Apply(); - - if (pTarget != pRenderTarget) - idd->SetRenderTarget(0, pTarget); - - if (sm->bReset) { - sm->bReset = false; - ds->releaseData(); - } - - idd->BeginScene(); - ds->draw(); - idd->EndScene(); - - pStateBlock->Apply(); - pStateBlock->Release(); - - pRenderTarget->Release(); - pTarget->Release(); - - ods("Finished ref is %d %d", ds->myRefCount, ds->refCount); - ds->dwMyThread = dwOldThread; - } -} - - -typedef HRESULT(__stdcall *SwapPresentType)(IDirect3DSwapChain9 *, CONST RECT *, CONST RECT *, HWND, CONST RGNDATA *, DWORD); -static HRESULT __stdcall mySwapPresent(IDirect3DSwapChain9 * ids, CONST RECT *pSourceRect, CONST RECT *pDestRect, HWND hDestWindowOverride, CONST RGNDATA *pDirtyRegion, DWORD dwFlags) { - ods("D3D9: SwapChain Present"); - - if (bPresenting) { - ods("D3D9: Not doublepresenting in chain!"); - } else { - IDirect3DDevice9 *idd = NULL; - ids->GetDevice(&idd); - if (idd) { - doPresent(idd); - idd->Release(); - } - } - - SwapPresentType oSwapPresent; - oSwapPresent = (SwapPresentType) hhSwapPresent.call; - - hhSwapPresent.restore(); - HRESULT hr = oSwapPresent(ids, pSourceRect,pDestRect,hDestWindowOverride,pDirtyRegion,dwFlags); - hhSwapPresent.inject(); - return hr; -} - -typedef HRESULT(__stdcall *PresentType)(IDirect3DDevice9 *, CONST RECT *, CONST RECT *, HWND, CONST RGNDATA *); -static HRESULT __stdcall myPresent(IDirect3DDevice9 * idd, CONST RECT* pSourceRect,CONST RECT* pDestRect,HWND hDestWindowOverride,CONST RGNDATA* pDirtyRegion) { - ods("D3D9: Device Present"); - - doPresent(idd); - - PresentType oPresent = (PresentType) hhPresent.call; - - hhPresent.restore(); - HRESULT hr = oPresent(idd,pSourceRect,pDestRect,hDestWindowOverride,pDirtyRegion); - hhPresent.inject(); - return hr; -} - -typedef HRESULT(__stdcall *ResetType)(IDirect3DDevice9 *, D3DPRESENT_PARAMETERS *); -static HRESULT __stdcall myReset(IDirect3DDevice9 * idd, D3DPRESENT_PARAMETERS *param) { - ods("D3D9: Chaining Reset"); - - DevState *ds = devMap[idd]; - if (ds) { - DWORD dwOldThread = ds->dwMyThread; - if (dwOldThread) - ods("myReset from other thread"); - ds->dwMyThread = GetCurrentThreadId(); - - ds->releaseAll(); - ds->dwMyThread = dwOldThread; - } - - - ResetType oReset = (ResetType) hhReset.call; - - hhReset.restore(); - HRESULT hr=oReset(idd, param); - hhReset.inject(); - - ds->createCleanState(); - return hr; -} - -typedef ULONG(__stdcall *AddRefType)(IDirect3DDevice9 *); - -static ULONG __stdcall myAddRef(IDirect3DDevice9 *idd) { - Mutex m; - DevState *ds = devMap[idd]; - if (ds) { - if (ds->dwMyThread == GetCurrentThreadId()) { - ds->myRefCount++; - } else - ds->refCount++; - return ds->refCount + ds->initRefCount; - } - AddRefType oAddRef = (AddRefType) hhAddRef.call; - - hhAddRef.restore(); - LONG res = oAddRef(idd); - hhAddRef.inject(); - ods("D3D9: Chaining AddRef: %d", res); - return res; -} - -typedef ULONG(__stdcall *ReleaseType)(IDirect3DDevice9 *); -static ULONG __stdcall myRelease(IDirect3DDevice9 *idd) { - Mutex m; - DevState *ds = devMap[idd]; - - if (ds) { - if (ds->dwMyThread == GetCurrentThreadId()) { - ds->myRefCount--; - return ds->refCount + ds->initRefCount; - } else { - ds->refCount--; - } - - if (ds->refCount >= 0) - return ds->refCount + ds->initRefCount; - - ods("D3D9: Final release. MyRefs = %d, Tot = %d", ds->myRefCount, ds->refCount); - - DWORD dwOldThread = ds->dwMyThread; - if (dwOldThread) - ods("finalRelease from other thread"); - ds->dwMyThread = GetCurrentThreadId(); - - ds->releaseAll(); - - ds->dwMyThread = dwOldThread; - - ods("D3D9: Final release, MyRefs = %d Tot = %d", ds->myRefCount, ds->refCount); - - devMap.erase(idd); - delete ds; - } - ReleaseType oRelease = (ReleaseType) hhRelease.call; - hhRelease.restore(); - LONG res = oRelease(idd); - hhRelease.inject(); - ods("D3D9: Chaining Release: %d", res); - return res; -} - -typedef HRESULT(__stdcall *CreateDeviceType)(IDirect3D9 *, UINT, D3DDEVTYPE, HWND, DWORD, D3DPRESENT_PARAMETERS *, IDirect3DDevice9 **); -static HRESULT __stdcall myCreateDevice(IDirect3D9 * id3d, UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS *pPresentationParameters, IDirect3DDevice9 **ppReturnedDeviceInterface) { - ods("D3D9: Chaining CreateDevice"); - Mutex m; - -// BehaviorFlags &= ~D3DCREATE_PUREDEVICE; - - CreateDeviceType oCreateDevice = (CreateDeviceType) hhCreateDevice.call; - - hhCreateDevice.restore(); - HRESULT hr=oCreateDevice(id3d, Adapter,DeviceType,hFocusWindow,BehaviorFlags,pPresentationParameters,ppReturnedDeviceInterface); - hhCreateDevice.inject(); - - if (FAILED(hr)) - return hr; - - IDirect3DDevice9 *idd = *ppReturnedDeviceInterface; - IDirect3DSwapChain9 *pSwap = NULL; - - // Get real interface, please. - bool bfound; - do { - bfound = false; - idd->GetSwapChain(0, &pSwap); - if (pSwap) { - IDirect3DDevice9 *idorig = NULL; - if (SUCCEEDED(pSwap->GetDevice(&idorig))) { - if (idorig != idd) { - ods("Prepatched device, using original. %p => %p", idorig, idd); - if (idd != *ppReturnedDeviceInterface) - idd->Release(); - idd = idorig; - bfound = true; - } else { - idorig->Release(); - } - } - pSwap->Release(); - } - } while (bfound); - - DevState *ds = new DevState; - ds->dev = idd; - - idd->AddRef(); - ds->initRefCount = idd->Release(); - - devMap[idd] = ds; - - hhAddRef.setupInterface(idd, 1, reinterpret_cast(myAddRef)); - hhRelease.setupInterface(idd, 2, reinterpret_cast(myRelease)); - hhReset.setupInterface(idd, 16, reinterpret_cast(myReset)); - hhPresent.setupInterface(idd, 17, reinterpret_cast(myPresent)); - - pSwap = NULL; - idd->GetSwapChain(0, &pSwap); - if (pSwap) { - hhSwapPresent.setupInterface(pSwap, 3, reinterpret_cast(mySwapPresent)); - pSwap->Release(); - } else { - ods("D3D9: Failed to get swapchain"); - } - - ds->createCleanState(); - return hr; -} - -typedef HRESULT(__stdcall *CreateDeviceExType)(IDirect3D9Ex *, UINT, D3DDEVTYPE, HWND, DWORD, D3DPRESENT_PARAMETERS *, D3DDISPLAYMODEEX *, IDirect3DDevice9Ex **); -static HRESULT __stdcall myCreateDeviceEx(IDirect3D9Ex * id3d, UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS *pPresentationParameters, D3DDISPLAYMODEEX* pFullscreenDisplayMode, IDirect3DDevice9Ex** ppReturnedDeviceInterface) { - Mutex m; - ods("D3D9: Chaining CreateDeviceEx"); - -// BehaviorFlags &= ~D3DCREATE_PUREDEVICE; - - CreateDeviceExType oCreateDeviceEx = (CreateDeviceExType) hhCreateDeviceEx.call; - - hhCreateDeviceEx.restore(); - HRESULT hr=oCreateDeviceEx(id3d, Adapter,DeviceType,hFocusWindow,BehaviorFlags,pPresentationParameters,pFullscreenDisplayMode,ppReturnedDeviceInterface); - hhCreateDeviceEx.inject(); - - if (FAILED(hr)) - return hr; - - IDirect3DDevice9Ex *idd = *ppReturnedDeviceInterface; - - DevState *ds = new DevState; - ds->dev = idd; - - idd->AddRef(); - ds->initRefCount = idd->Release(); - - devMap[idd] = ds; - - hhAddRef.setupInterface(idd, 1, reinterpret_cast(myAddRef)); - hhRelease.setupInterface(idd, 2, reinterpret_cast(myRelease)); - hhReset.setupInterface(idd, 16, reinterpret_cast(myReset)); - hhPresent.setupInterface(idd, 17, reinterpret_cast(myPresent)); - - IDirect3DSwapChain9 *pSwap = NULL; - idd->GetSwapChain(0, &pSwap); - if (pSwap) { - hhSwapPresent.setupInterface(pSwap, 3, reinterpret_cast(mySwapPresent)); - pSwap->Release(); - } else { - ods("D3D9: Failed to get swapchain for DevEx"); - } - - ds->createCleanState(); - return hr; -} - -static void HookCreateRaw(voidFunc vfCreate) { - ods("D3D9: Injecting CreateDevice Raw"); - hhCreateDevice.setup(vfCreate, reinterpret_cast(myCreateDevice)); -} - -static void HookCreate(IDirect3D9 *pD3D) { - ods("D3D9: Injecting CreateDevice"); - - hhCreateDevice.setupInterface(pD3D, 16, reinterpret_cast(myCreateDevice)); -} - -static void HookCreateEx(IDirect3D9Ex *pD3D) { - ods("D3D9Ex: Injecting CreateDevice / CreateDeviceEx"); - - hhCreateDevice.setupInterface(pD3D, 16, reinterpret_cast(myCreateDevice)); - hhCreateDeviceEx.setupInterface(pD3D, 20, reinterpret_cast(myCreateDeviceEx)); -} - -void checkD3D9Hook(bool preonly) { - if (bChaining) { - return; - ods("D3D9: Causing a chain"); - } - - bChaining = true; - - HMODULE hD3D = GetModuleHandle("D3D9.DLL"); - - if (hD3D != NULL) { - if (! bHooked) { - char procname[2048]; - GetModuleFileName(NULL, procname, 2048); - fods("D3D9: CreateWnd in unhooked D3D App %s", procname); - bHooked = true; - - // Add a ref to ourselves; we do NOT want to get unloaded directly from this process. - GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast(&HookCreate), &hSelf); - - // Can we use the prepatch data? - GetModuleFileName(hD3D, procname, 2048); - if (_stricmp(d3dd->cFileName, procname) == 0) { - unsigned char *raw = (unsigned char *) hD3D; - HookCreateRaw((voidFunc)(raw + d3dd->iOffsetCreate)); - } else if (! preonly) { - fods("D3D9 Interface changed, can't rawpatch"); - pDirect3DCreate9 d3dc9 = reinterpret_cast(GetProcAddress(hD3D, "Direct3DCreate9")); - ods("Got %p", d3dc9); - if (d3dc9) { - IDirect3D9 *id3d9 = d3dc9(D3D_SDK_VERSION); - if (id3d9) { - HookCreate(id3d9); - id3d9->Release(); - } else { - ods("Failed Direct3DCreate9"); - } - } else { - ods("D3D Library without Direct3DCreate9?"); - } - } else { - bHooked = false; - } - } - } - - bChaining = false; -} - -extern "C" __declspec(dllexport) void __cdecl PrepareD3D9() { - ods("Preparing static data for D3D9 Injection"); - - char buffb[2048]; - - HMODULE hD3D = LoadLibrary("D3D9.DLL"); - HMODULE hRef; - - if (hD3D != NULL) { - GetModuleFileName(hD3D, d3dd->cFileName, 2048); - pDirect3DCreate9 d3dc9 = reinterpret_cast(GetProcAddress(hD3D, "Direct3DCreate9")); - if (! d3dc9) { - ods("D3D9 Library without Direct3DCreate9"); - } else { - if (! GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (const char *) d3dc9, &hRef)) { - ods("Failed to get module for D3D9"); - } else { - GetModuleFileName(hRef, buffb, 2048); - if (_stricmp(d3dd->cFileName, buffb) != 0) { - ods("Direct3DCreate9 is not in D3D9 library"); - } else { - IDirect3D9 *id3d9 = d3dc9(D3D_SDK_VERSION); - if (id3d9) { - void ***vtbl = (void ***) id3d9; - void *pCreate = (*vtbl)[16]; - - if (! GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (char *) pCreate, &hRef)) { - ods("Failed to get module for CreateDevice"); - } else { - GetModuleFileName(hRef, buffb, 2048); - if (_stricmp(d3dd->cFileName, buffb) != 0) { - ods("CreateDevice is not in D3D9 library"); - } else { - unsigned char *b = (unsigned char *) pCreate; - unsigned char *a = (unsigned char *) hD3D; - d3dd->iOffsetCreate = b-a; - ods("Successfully found prepatch offset: %p %p %p: %d", hD3D, d3dc9, pCreate, d3dd->iOffsetCreate); - } - } - id3d9->Release(); - } - } - } - } - FreeLibrary(hD3D); - } -} +/* Copyright (C) 2005-2009, Thorvald Natvig + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + - Neither the name of the Mumble Developers nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "lib.h" +#include + +Direct3D9Data *d3dd; + +typedef IDirect3D9*(WINAPI *pDirect3DCreate9)(UINT SDKVersion) ; +typedef HRESULT(WINAPI *pDirect3DCreate9Ex)(UINT SDKVersion, IDirect3D9Ex **ppD3D) ; + +struct D3DTLVERTEX { + float x, y, z, rhw; // Position + D3DCOLOR color; // Vertex colour + float tu, tv; // Texture coordinates +}; +const DWORD D3DFVF_TLVERTEX = D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1; + +class DevState { + public: + IDirect3DDevice9 *dev; + IDirect3DStateBlock9 *pSB; + + LONG initRefCount; + LONG refCount; + LONG myRefCount; + DWORD dwMyThread; + + LPDIRECT3DTEXTURE9 tex[NUM_TEXTS]; + unsigned int uiCounter[NUM_TEXTS]; + + DevState(); + + void createCleanState(); + void releaseData(); + void releaseAll(); + void draw(); + void postDraw(); +}; + +static map devMap; +static bool bHooked = false; +static bool bChaining = false; +static bool bPresenting = false; + +DevState::DevState() { + dev = NULL; + pSB = NULL; + dwMyThread = 0; + refCount = 0; + myRefCount = 0; + for (int i = 0;i < NUM_TEXTS;i++) + tex[i] = NULL; +} + +void DevState::releaseData() { + ods("D3D9: Release Data"); + + for (int i=0;iRelease(); + tex[i] = NULL; + } +} + +void DevState::releaseAll() { + ods("D3D9: Release All"); + releaseData(); + if (pSB) + pSB->Release(); + pSB = NULL; +} + +void DevState::draw() { + D3DVIEWPORT9 vp; + dev->GetViewport(&vp); + + int idx = 0; + + vector texs; + vector widths; + vector yofs; + vector colors; + + unsigned int y = 0; + + if (sm->fFontSize < 0.01f) + sm->fFontSize = 0.01f; + else if (sm->fFontSize > 1.0f) + sm->fFontSize = 1.0f; + + int iHeight = lround(vp.Height * sm->fFontSize); + + if (iHeight > TEXT_HEIGHT) + iHeight = TEXT_HEIGHT; + + float s = iHeight / 60.0f; + + ods("D3D9: Init: Scale %f. iH %d. Final scale %f", sm->fFontSize, iHeight, s); + + DWORD dwWaitResult = WaitForSingleObject(hSharedMutex, 50L); + if (dwWaitResult == WAIT_OBJECT_0) { + for (int i=0;itexts[i].width == 0) { + y += iHeight / 4; + } else if (sm->texts[i].width > 0) { + if (!tex[i] || (sm->texts[i].uiCounter != uiCounter[i])) { + if (tex[i]) + tex[i]->Release(); + + dev->CreateTexture(sm->texts[i].width, TEXT_HEIGHT, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &tex[i], NULL); + + D3DLOCKED_RECT lr; + tex[i]->LockRect(0, &lr, NULL, D3DLOCK_DISCARD); + + for (int r=0;r(lr.pBits) + r * lr.Pitch; + memcpy(dptr, sm->texts[i].texture + r * TEXT_WIDTH * 4, sm->texts[i].width * 4); + } + + tex[i]->UnlockRect(0); + uiCounter[i] = sm->texts[i].uiCounter; + } + unsigned int w = lround(sm->texts[i].width * s); + texs.push_back(tex[i]); + colors.push_back(sm->texts[i].color); + widths.push_back(w); + yofs.push_back(y); + idx++; + y += iHeight; + } + } + ReleaseMutex(hSharedMutex); + } + + if (idx == 0) + return; + + int height = y; + y = lround(vp.Height * sm->fY); + + if (sm->bTop) { + y -= height; + } else if (sm->bBottom) { + } else { + y -= height / 2; + } + + + if (y < 1) + y = 1; + if ((y + height + 1) > vp.Height) + y = vp.Height - height - 1; + + for (int i=0;ifX); + + if (sm->bLeft) { + x -= width; + } else if (sm->bRight) { + } else { + x -= width / 2; + } + + if (x < 1) + x = 1; + if ((x + width + 1) > vp.Width) + x = vp.Width - width - 1; + + D3DCOLOR color = colors[i]; + + float left = static_cast(x); + float top = static_cast(y + yofs[i]); + float right = left + width; + float bottom = top + iHeight; + + const float z = 1.0f; + D3DTLVERTEX vertices[4] = { + // x, y, z, color, tu, tv + { left, top, z, 1, color, 0, 0 }, + { right, top, z, 1, color, 1, 0 }, + { right, bottom, z, 1, color, 1, 1 }, + { left, bottom, z, 1, color, 0, 1 } + }; + + dev->SetTexture(0, texs[i]); + dev->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, vertices, sizeof(D3DTLVERTEX)); + } +} + +void DevState::createCleanState() { + DWORD dwOldThread = dwMyThread; + if (dwOldThread) { + ods("CreateCleanState from other thread."); + } + dwMyThread = GetCurrentThreadId(); + + if (pSB) + pSB->Release(); + pSB = NULL; + + IDirect3DStateBlock9* pStateBlock = NULL; + dev->CreateStateBlock(D3DSBT_ALL, &pStateBlock); + pStateBlock->Capture(); + + dev->CreateStateBlock(D3DSBT_ALL, &pSB); + + D3DVIEWPORT9 vp; + dev->GetViewport(&vp); + + dev->SetVertexShader(NULL); + dev->SetPixelShader(NULL); + dev->SetFVF(D3DFVF_TLVERTEX); + + dev->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID); + dev->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD); + dev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); // 0x16 + dev->SetRenderState(D3DRS_WRAP0, FALSE); // 0x80 + + dev->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + dev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + dev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + + dev->SetRenderState(D3DRS_ALPHATESTENABLE, TRUE); + dev->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATER); + + dev->SetRenderState(D3DRS_ZENABLE, FALSE); + dev->SetRenderState(D3DRS_ZWRITEENABLE, FALSE); + dev->SetRenderState(D3DRS_ZFUNC, D3DCMP_ALWAYS); + dev->SetRenderState(D3DRS_COLORVERTEX, FALSE); + + dev->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE); + dev->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + dev->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); + + dev->SetRenderState(D3DRS_LIGHTING, FALSE); + + pSB->Capture(); + + pStateBlock->Apply(); + pStateBlock->Release(); + + dwMyThread = dwOldThread; +} + +static HardHook hhCreateDevice; +static HardHook hhCreateDeviceEx; +static HardHook hhReset; +static HardHook hhAddRef; +static HardHook hhRelease; +static HardHook hhPresent; +static HardHook hhPresentEx; +static HardHook hhSwapPresent; + +static void doPresent(IDirect3DDevice9 *idd) { + DevState *ds = devMap[idd]; + + if (ds && sm->bShow) { + DWORD dwOldThread = ds->dwMyThread; + if (dwOldThread) + ods("doPresent from other thread"); + ds->dwMyThread = GetCurrentThreadId(); + + IDirect3DSurface9 *pTarget = NULL; + IDirect3DSurface9 *pRenderTarget = NULL; + idd->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &pTarget); + idd->GetRenderTarget(0, &pRenderTarget); + + ods("D3D9: doPresent Back %p RenderT %p",pTarget,pRenderTarget); + + + IDirect3DStateBlock9* pStateBlock = NULL; + idd->CreateStateBlock(D3DSBT_ALL, &pStateBlock); + pStateBlock->Capture(); + + ds->pSB->Apply(); + + if (pTarget != pRenderTarget) + idd->SetRenderTarget(0, pTarget); + + if (sm->bReset) { + sm->bReset = false; + ds->releaseData(); + } + + idd->BeginScene(); + ds->draw(); + idd->EndScene(); + + pStateBlock->Apply(); + pStateBlock->Release(); + + pRenderTarget->Release(); + pTarget->Release(); + + ods("Finished ref is %d %d", ds->myRefCount, ds->refCount); + ds->dwMyThread = dwOldThread; + } +} + + +typedef HRESULT(__stdcall *SwapPresentType)(IDirect3DSwapChain9 *, CONST RECT *, CONST RECT *, HWND, CONST RGNDATA *, DWORD); +static HRESULT __stdcall mySwapPresent(IDirect3DSwapChain9 * ids, CONST RECT *pSourceRect, CONST RECT *pDestRect, HWND hDestWindowOverride, CONST RGNDATA *pDirtyRegion, DWORD dwFlags) { + ods("D3D9: SwapChain Present"); + + if (bPresenting) { + ods("D3D9: Not doublepresenting in chain!"); + } else { + IDirect3DDevice9 *idd = NULL; + ids->GetDevice(&idd); + if (idd) { + doPresent(idd); + idd->Release(); + } + } + + SwapPresentType oSwapPresent; + oSwapPresent = (SwapPresentType) hhSwapPresent.call; + + hhSwapPresent.restore(); + HRESULT hr = oSwapPresent(ids, pSourceRect,pDestRect,hDestWindowOverride,pDirtyRegion,dwFlags); + hhSwapPresent.inject(); + return hr; +} + +typedef HRESULT(__stdcall *PresentType)(IDirect3DDevice9 *, CONST RECT *, CONST RECT *, HWND, CONST RGNDATA *); +static HRESULT __stdcall myPresent(IDirect3DDevice9 * idd, CONST RECT* pSourceRect,CONST RECT* pDestRect,HWND hDestWindowOverride,CONST RGNDATA* pDirtyRegion) { + ods("D3D9: Device Present"); + + doPresent(idd); + + PresentType oPresent = (PresentType) hhPresent.call; + + hhPresent.restore(); + HRESULT hr = oPresent(idd,pSourceRect,pDestRect,hDestWindowOverride,pDirtyRegion); + hhPresent.inject(); + return hr; +} + +typedef HRESULT(__stdcall *ResetType)(IDirect3DDevice9 *, D3DPRESENT_PARAMETERS *); +static HRESULT __stdcall myReset(IDirect3DDevice9 * idd, D3DPRESENT_PARAMETERS *param) { + ods("D3D9: Chaining Reset"); + + DevState *ds = devMap[idd]; + if (ds) { + DWORD dwOldThread = ds->dwMyThread; + if (dwOldThread) + ods("myReset from other thread"); + ds->dwMyThread = GetCurrentThreadId(); + + ds->releaseAll(); + ds->dwMyThread = dwOldThread; + } + + + ResetType oReset = (ResetType) hhReset.call; + + hhReset.restore(); + HRESULT hr=oReset(idd, param); + hhReset.inject(); + + ds->createCleanState(); + return hr; +} + +typedef ULONG(__stdcall *AddRefType)(IDirect3DDevice9 *); + +static ULONG __stdcall myAddRef(IDirect3DDevice9 *idd) { + Mutex m; + DevState *ds = devMap[idd]; + if (ds) { + if (ds->dwMyThread == GetCurrentThreadId()) { + ds->myRefCount++; + } else + ds->refCount++; + return ds->refCount + ds->initRefCount; + } + AddRefType oAddRef = (AddRefType) hhAddRef.call; + + hhAddRef.restore(); + LONG res = oAddRef(idd); + hhAddRef.inject(); + ods("D3D9: Chaining AddRef: %d", res); + return res; +} + +typedef ULONG(__stdcall *ReleaseType)(IDirect3DDevice9 *); +static ULONG __stdcall myRelease(IDirect3DDevice9 *idd) { + Mutex m; + DevState *ds = devMap[idd]; + + if (ds) { + if (ds->dwMyThread == GetCurrentThreadId()) { + ds->myRefCount--; + return ds->refCount + ds->initRefCount; + } else { + ds->refCount--; + } + + if (ds->refCount >= 0) + return ds->refCount + ds->initRefCount; + + ods("D3D9: Final release. MyRefs = %d, Tot = %d", ds->myRefCount, ds->refCount); + + DWORD dwOldThread = ds->dwMyThread; + if (dwOldThread) + ods("finalRelease from other thread"); + ds->dwMyThread = GetCurrentThreadId(); + + ds->releaseAll(); + + ds->dwMyThread = dwOldThread; + + ods("D3D9: Final release, MyRefs = %d Tot = %d", ds->myRefCount, ds->refCount); + + devMap.erase(idd); + delete ds; + } + ReleaseType oRelease = (ReleaseType) hhRelease.call; + hhRelease.restore(); + LONG res = oRelease(idd); + hhRelease.inject(); + ods("D3D9: Chaining Release: %d", res); + return res; +} + +typedef HRESULT(__stdcall *CreateDeviceType)(IDirect3D9 *, UINT, D3DDEVTYPE, HWND, DWORD, D3DPRESENT_PARAMETERS *, IDirect3DDevice9 **); +static HRESULT __stdcall myCreateDevice(IDirect3D9 * id3d, UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS *pPresentationParameters, IDirect3DDevice9 **ppReturnedDeviceInterface) { + ods("D3D9: Chaining CreateDevice"); + Mutex m; + +// BehaviorFlags &= ~D3DCREATE_PUREDEVICE; + + CreateDeviceType oCreateDevice = (CreateDeviceType) hhCreateDevice.call; + + hhCreateDevice.restore(); + HRESULT hr=oCreateDevice(id3d, Adapter,DeviceType,hFocusWindow,BehaviorFlags,pPresentationParameters,ppReturnedDeviceInterface); + hhCreateDevice.inject(); + + if (FAILED(hr)) + return hr; + + IDirect3DDevice9 *idd = *ppReturnedDeviceInterface; + IDirect3DSwapChain9 *pSwap = NULL; + + // Get real interface, please. + bool bfound; + do { + bfound = false; + idd->GetSwapChain(0, &pSwap); + if (pSwap) { + IDirect3DDevice9 *idorig = NULL; + if (SUCCEEDED(pSwap->GetDevice(&idorig))) { + if (idorig != idd) { + ods("Prepatched device, using original. %p => %p", idorig, idd); + if (idd != *ppReturnedDeviceInterface) + idd->Release(); + idd = idorig; + bfound = true; + } else { + idorig->Release(); + } + } + pSwap->Release(); + } + } while (bfound); + + DevState *ds = new DevState; + ds->dev = idd; + + idd->AddRef(); + ds->initRefCount = idd->Release(); + + devMap[idd] = ds; + + hhAddRef.setupInterface(idd, 1, reinterpret_cast(myAddRef)); + hhRelease.setupInterface(idd, 2, reinterpret_cast(myRelease)); + hhReset.setupInterface(idd, 16, reinterpret_cast(myReset)); + hhPresent.setupInterface(idd, 17, reinterpret_cast(myPresent)); + + pSwap = NULL; + idd->GetSwapChain(0, &pSwap); + if (pSwap) { + hhSwapPresent.setupInterface(pSwap, 3, reinterpret_cast(mySwapPresent)); + pSwap->Release(); + } else { + ods("D3D9: Failed to get swapchain"); + } + + ds->createCleanState(); + return hr; +} + +typedef HRESULT(__stdcall *CreateDeviceExType)(IDirect3D9Ex *, UINT, D3DDEVTYPE, HWND, DWORD, D3DPRESENT_PARAMETERS *, D3DDISPLAYMODEEX *, IDirect3DDevice9Ex **); +static HRESULT __stdcall myCreateDeviceEx(IDirect3D9Ex * id3d, UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS *pPresentationParameters, D3DDISPLAYMODEEX* pFullscreenDisplayMode, IDirect3DDevice9Ex** ppReturnedDeviceInterface) { + Mutex m; + ods("D3D9: Chaining CreateDeviceEx"); + +// BehaviorFlags &= ~D3DCREATE_PUREDEVICE; + + CreateDeviceExType oCreateDeviceEx = (CreateDeviceExType) hhCreateDeviceEx.call; + + hhCreateDeviceEx.restore(); + HRESULT hr=oCreateDeviceEx(id3d, Adapter,DeviceType,hFocusWindow,BehaviorFlags,pPresentationParameters,pFullscreenDisplayMode,ppReturnedDeviceInterface); + hhCreateDeviceEx.inject(); + + if (FAILED(hr)) + return hr; + + IDirect3DDevice9Ex *idd = *ppReturnedDeviceInterface; + + DevState *ds = new DevState; + ds->dev = idd; + + idd->AddRef(); + ds->initRefCount = idd->Release(); + + devMap[idd] = ds; + + hhAddRef.setupInterface(idd, 1, reinterpret_cast(myAddRef)); + hhRelease.setupInterface(idd, 2, reinterpret_cast(myRelease)); + hhReset.setupInterface(idd, 16, reinterpret_cast(myReset)); + hhPresent.setupInterface(idd, 17, reinterpret_cast(myPresent)); + + IDirect3DSwapChain9 *pSwap = NULL; + idd->GetSwapChain(0, &pSwap); + if (pSwap) { + hhSwapPresent.setupInterface(pSwap, 3, reinterpret_cast(mySwapPresent)); + pSwap->Release(); + } else { + ods("D3D9: Failed to get swapchain for DevEx"); + } + + ds->createCleanState(); + return hr; +} + +static void HookCreateRaw(voidFunc vfCreate) { + ods("D3D9: Injecting CreateDevice Raw"); + hhCreateDevice.setup(vfCreate, reinterpret_cast(myCreateDevice)); +} + +static void HookCreate(IDirect3D9 *pD3D) { + ods("D3D9: Injecting CreateDevice"); + + hhCreateDevice.setupInterface(pD3D, 16, reinterpret_cast(myCreateDevice)); +} + +static void HookCreateEx(IDirect3D9Ex *pD3D) { + ods("D3D9Ex: Injecting CreateDevice / CreateDeviceEx"); + + hhCreateDevice.setupInterface(pD3D, 16, reinterpret_cast(myCreateDevice)); + hhCreateDeviceEx.setupInterface(pD3D, 20, reinterpret_cast(myCreateDeviceEx)); +} + +void checkD3D9Hook(bool preonly) { + if (bChaining) { + return; + ods("D3D9: Causing a chain"); + } + + bChaining = true; + + HMODULE hD3D = GetModuleHandle("D3D9.DLL"); + + if (hD3D != NULL) { + if (! bHooked) { + char procname[2048]; + GetModuleFileName(NULL, procname, 2048); + fods("D3D9: CreateWnd in unhooked D3D App %s", procname); + bHooked = true; + + // Add a ref to ourselves; we do NOT want to get unloaded directly from this process. + GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast(&HookCreate), &hSelf); + + // Can we use the prepatch data? + GetModuleFileName(hD3D, procname, 2048); + if (_stricmp(d3dd->cFileName, procname) == 0) { + unsigned char *raw = (unsigned char *) hD3D; + HookCreateRaw((voidFunc)(raw + d3dd->iOffsetCreate)); + } else if (! preonly) { + fods("D3D9 Interface changed, can't rawpatch"); + pDirect3DCreate9 d3dc9 = reinterpret_cast(GetProcAddress(hD3D, "Direct3DCreate9")); + ods("Got %p", d3dc9); + if (d3dc9) { + IDirect3D9 *id3d9 = d3dc9(D3D_SDK_VERSION); + if (id3d9) { + HookCreate(id3d9); + id3d9->Release(); + } else { + ods("Failed Direct3DCreate9"); + } + } else { + ods("D3D Library without Direct3DCreate9?"); + } + } else { + bHooked = false; + } + } + } + + bChaining = false; +} + +extern "C" __declspec(dllexport) void __cdecl PrepareD3D9() { + ods("Preparing static data for D3D9 Injection"); + + char buffb[2048]; + + HMODULE hD3D = LoadLibrary("D3D9.DLL"); + HMODULE hRef; + + if (hD3D != NULL) { + GetModuleFileName(hD3D, d3dd->cFileName, 2048); + pDirect3DCreate9 d3dc9 = reinterpret_cast(GetProcAddress(hD3D, "Direct3DCreate9")); + if (! d3dc9) { + ods("D3D9 Library without Direct3DCreate9"); + } else { + if (! GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (const char *) d3dc9, &hRef)) { + ods("Failed to get module for D3D9"); + } else { + GetModuleFileName(hRef, buffb, 2048); + if (_stricmp(d3dd->cFileName, buffb) != 0) { + ods("Direct3DCreate9 is not in D3D9 library"); + } else { + IDirect3D9 *id3d9 = d3dc9(D3D_SDK_VERSION); + if (id3d9) { + void ***vtbl = (void ***) id3d9; + void *pCreate = (*vtbl)[16]; + + if (! GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (char *) pCreate, &hRef)) { + ods("Failed to get module for CreateDevice"); + } else { + GetModuleFileName(hRef, buffb, 2048); + if (_stricmp(d3dd->cFileName, buffb) != 0) { + ods("CreateDevice is not in D3D9 library"); + } else { + unsigned char *b = (unsigned char *) pCreate; + unsigned char *a = (unsigned char *) hD3D; + d3dd->iOffsetCreate = b-a; + ods("Successfully found prepatch offset: %p %p %p: %d", hD3D, d3dc9, pCreate, d3dd->iOffsetCreate); + } + } + id3d9->Release(); + } + } + } + } + FreeLibrary(hD3D); + } +} diff --git a/overlay/lib.h b/overlay/lib.h index 72eb0a18b..d699bb207 100644 --- a/overlay/lib.h +++ b/overlay/lib.h @@ -1,109 +1,109 @@ -/* Copyright (C) 2005-2009, Thorvald Natvig - - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - - Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - Neither the name of the Mumble Developers nor the names of its - contributors may be used to endorse or promote products derived from this - software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef _LIB_H -#define _LIB_H - -#define _UNICODE -#define _WIN32_WINNT 0x0501 -#include -#include -#include -#include -#include -#include -#include -#include -#include "overlay.h" - -#define lround(x) static_cast((x) + (((x) >= 0.0) ? 0.5 : -0.5)) - -using namespace std; - -void __cdecl fods(const char *format, ...); -void __cdecl ods(const char *format, ...); - -typedef void *(*voidFunc)(); - -struct HardHook { - unsigned char *baseptr; - unsigned char orig[6]; - unsigned char replace[6]; - bool bTrampoline; - voidFunc call; - - static void *pCode; - static unsigned int uiCode; - - HardHook(); - void *cloneCode(void **orig); - void setup(voidFunc func, voidFunc replacement); - void setupInterface(IUnknown *intf, LONG funcoffset, voidFunc replacement); - void inject(bool force = false); - void restore(bool force = false); - void print(); -}; - -struct Direct3D9Data { - char cFileName[2048]; - int iOffsetCreate; -}; - -struct FakeInterface { - typedef ULONG(FakeInterface::* voidMemberFunc)(); - void **vtbl; - IUnknown *pOriginal; - IUnknown *pNew; - void *pAssembly; - FakeInterface(IUnknown *orig, int entries); - ~FakeInterface(); - void replace(LONG funcoffset, voidMemberFunc replacement); -}; - -class Mutex { - protected: - static CRITICAL_SECTION cs; - public: - static void init(); - Mutex(); - ~Mutex(); -}; - -extern void checkD3D9Hook(bool preonly = false); -extern void checkOpenGLHook(); - -extern SharedMem *sm; -extern Direct3D9Data *d3dd; -extern HANDLE hSharedMutex; -extern HMODULE hSelf; -extern unsigned int uiAudioCount; -extern bool bVideoHooked; - -#endif +/* Copyright (C) 2005-2009, Thorvald Natvig + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + - Neither the name of the Mumble Developers nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _LIB_H +#define _LIB_H + +#define _UNICODE +#define _WIN32_WINNT 0x0501 +#include +#include +#include +#include +#include +#include +#include +#include +#include "overlay.h" + +#define lround(x) static_cast((x) + (((x) >= 0.0) ? 0.5 : -0.5)) + +using namespace std; + +void __cdecl fods(const char *format, ...); +void __cdecl ods(const char *format, ...); + +typedef void *(*voidFunc)(); + +struct HardHook { + unsigned char *baseptr; + unsigned char orig[6]; + unsigned char replace[6]; + bool bTrampoline; + voidFunc call; + + static void *pCode; + static unsigned int uiCode; + + HardHook(); + void *cloneCode(void **orig); + void setup(voidFunc func, voidFunc replacement); + void setupInterface(IUnknown *intf, LONG funcoffset, voidFunc replacement); + void inject(bool force = false); + void restore(bool force = false); + void print(); +}; + +struct Direct3D9Data { + char cFileName[2048]; + int iOffsetCreate; +}; + +struct FakeInterface { + typedef ULONG(FakeInterface::* voidMemberFunc)(); + void **vtbl; + IUnknown *pOriginal; + IUnknown *pNew; + void *pAssembly; + FakeInterface(IUnknown *orig, int entries); + ~FakeInterface(); + void replace(LONG funcoffset, voidMemberFunc replacement); +}; + +class Mutex { + protected: + static CRITICAL_SECTION cs; + public: + static void init(); + Mutex(); + ~Mutex(); +}; + +extern void checkD3D9Hook(bool preonly = false); +extern void checkOpenGLHook(); + +extern SharedMem *sm; +extern Direct3D9Data *d3dd; +extern HANDLE hSharedMutex; +extern HMODULE hSelf; +extern unsigned int uiAudioCount; +extern bool bVideoHooked; + +#endif diff --git a/overlay/opengl.cpp b/overlay/opengl.cpp index 8f5e0cc1f..03042ca85 100644 --- a/overlay/opengl.cpp +++ b/overlay/opengl.cpp @@ -1,415 +1,415 @@ -/* Copyright (C) 2005-2009, Thorvald Natvig - - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - - Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - Neither the name of the Mumble Developers nor the names of its - contributors may be used to endorse or promote products derived from this - software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include "lib.h" - -typedef unsigned int GLenum; -typedef unsigned char GLboolean; -typedef void GLvoid; -typedef int GLint; -typedef unsigned char GLubyte; -typedef unsigned int GLuint; -typedef int GLsizei; -typedef float GLfloat; -typedef double GLdouble; - -#define GL_TEXTURE_2D 0x0DE1 -#define GL_UNSIGNED_BYTE 0x1401 -#define GL_QUADS 0x0007 -#define GL_RGBA 0x1908 -#define GL_BGRA 0x80E1 -#define GL_TEXTURE_WRAP_S 0x2802 -#define GL_TEXTURE_WRAP_T 0x2803 -#define GL_CLAMP_TO_EDGE 0x812F -#define GL_TEXTURE_MAG_FILTER 0x2800 -#define GL_TEXTURE_MIN_FILTER 0x2801 -#define GL_LINEAR 0x2601 -#define GL_FRONT_AND_BACK 0x0408 -#define GL_AMBIENT_AND_DIFFUSE 0x1602 -#define GL_SRC_ALPHA 0x0302 -#define GL_ONE_MINUS_SRC_ALPHA 0x0303 -#define GL_TEXTURE_GEN_S 0x0C60 -#define GL_TEXTURE_GEN_T 0x0C61 -#define GL_TEXTURE_GEN_R 0x0C62 -#define GL_TEXTURE_GEN_Q 0x0C63 -#define GL_STENCIL_TEST 0x0B90 -#define GL_SCISSOR_TEST 0x0C11 -#define GL_SEPARABLE_2D 0x8012 -#define GL_MINMAX 0x802E -#define GL_LIGHTING 0x0B50 -#define GL_INDEX_LOGIC_OP 0x0BF1 -#define GL_HISTOGRAM 0x8024 -#define GL_FOG 0x0B60 -#define GL_DITHER 0x0BD0 -#define GL_DEPTH_TEST 0x0B71 -#define GL_CULL_FACE 0x0B44 -#define GL_CONVOLUTION_1D 0x8010 -#define GL_CONVOLUTION_2D 0x8011 -#define GL_COLOR_TABLE 0x80D0 -#define GL_COLOR_MATERIAL 0x0B57 -#define GL_AUTO_NORMAL 0x0D80 -#define GL_ALPHA_TEST 0x0BC0 -#define GL_COLOR_LOGIC_OP 0x0BF2 -#define GL_MATRIX_MODE 0x0BA0 -#define GL_MODELVIEW 0x1700 -#define GL_PROJECTION 0x1701 -#define GL_BLEND 0x0BE2 - -#define TDEF(ret, name, arg) typedef ret (__stdcall * t##name) arg -#define GLDEF(ret, name, arg) TDEF(ret, name, arg); t##name o##name = NULL - -GLDEF(HGLRC, wglCreateContext, (HDC)); -GLDEF(void, glGenTextures, (GLsizei, GLuint *)); -GLDEF(void, glEnable, (GLenum)); -GLDEF(void, glDisable, (GLenum)); -GLDEF(void, glBlendFunc, (GLenum, GLenum)); -GLDEF(void, glColorMaterial, (GLenum, GLenum)); -GLDEF(void, glViewport, (GLint, GLint, GLsizei, GLsizei)); -GLDEF(void, glMatrixMode, (GLenum)); -GLDEF(void, glLoadIdentity, (void)); -GLDEF(void, glOrtho, (GLdouble, GLdouble, GLdouble, GLdouble, GLdouble, GLdouble)); -GLDEF(void, glBindTexture, (GLenum, GLuint)); -GLDEF(void, glPushMatrix, (void)); -GLDEF(void, glColor4ub, (GLubyte, GLubyte, GLubyte, GLubyte)); -GLDEF(void, glTranslatef, (GLfloat, GLfloat, GLfloat)); -GLDEF(void, glBegin, (GLenum)); -GLDEF(void, glEnd, (void)); -GLDEF(void, glTexCoord2f, (GLfloat, GLfloat)); -GLDEF(void, glVertex2f, (GLfloat, GLfloat)); -GLDEF(void, glPopMatrix, (void)); -GLDEF(void, glTexParameteri, (GLenum, GLenum, GLint)); -GLDEF(void, glTexImage2D, (GLenum, GLint, GLint, GLsizei, GLsizei, GLint, GLenum, GLenum, const GLvoid *)); -GLDEF(void, wglMakeCurrent, (HDC, HGLRC)); -GLDEF(HGLRC, wglGetCurrentContext, (void)); -GLDEF(HDC, wglGetCurrentDC, (void)); -GLDEF(int, GetDeviceCaps, (HDC, int)); - -#define INJDEF(ret, name, arg) GLDEF(ret, name, arg); static HardHook hh##name -#define INJECT(name) { o##name = reinterpret_cast(GetProcAddress(hGL, #name)); if (o##name) { hh##name.setup(reinterpret_cast(o##name), reinterpret_cast(my##name)); o##name = (t##name) hh##name.call; } else { ods("OpenGL: No GetProc for %s", #name);} } - -INJDEF(BOOL, wglSwapLayerBuffers, (HDC, UINT)); -INJDEF(BOOL, wglSwapBuffers, (HDC)); -INJDEF(BOOL, SwapBuffers, (HDC)); - -static bool bHooked = false; -static bool bChaining = false; - -struct Context { - HGLRC ctx; - GLuint textures[NUM_TEXTS]; - unsigned int uiCounter[NUM_TEXTS]; - Context(HDC hdc); - void draw(HDC hdc); -}; - -Context::Context(HDC hdc) { - ctx = owglCreateContext(hdc); - owglMakeCurrent(hdc, ctx); - - oglGenTextures(NUM_TEXTS, textures); - - // Here we go. From the top. Where is glResetState? - oglDisable(GL_ALPHA_TEST); - oglDisable(GL_AUTO_NORMAL); - oglEnable(GL_BLEND); - // Skip clip planes, there are thousands of them. - oglDisable(GL_COLOR_LOGIC_OP); - oglEnable(GL_COLOR_MATERIAL); - oglDisable(GL_COLOR_TABLE); - oglDisable(GL_CONVOLUTION_1D); - oglDisable(GL_CONVOLUTION_2D); - oglDisable(GL_CULL_FACE); - oglDisable(GL_DEPTH_TEST); - oglDisable(GL_DITHER); - oglDisable(GL_FOG); - oglDisable(GL_HISTOGRAM); - oglDisable(GL_INDEX_LOGIC_OP); - oglDisable(GL_LIGHTING); - // Skip line smmooth - // Skip map - oglDisable(GL_MINMAX); - // Skip polygon offset - oglDisable(GL_SEPARABLE_2D); - oglDisable(GL_SCISSOR_TEST); - oglDisable(GL_STENCIL_TEST); - oglEnable(GL_TEXTURE_2D); - oglDisable(GL_TEXTURE_GEN_Q); - oglDisable(GL_TEXTURE_GEN_R); - oglDisable(GL_TEXTURE_GEN_S); - oglDisable(GL_TEXTURE_GEN_T); - - oglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - oglColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); - - DWORD dwWaitResult = WaitForSingleObject(hSharedMutex, 50L); - if (dwWaitResult != WAIT_OBJECT_0) - return; - - for (int i=0;ibHooked = true; - - // DEBUG - // sm->bDebug = true; - - unsigned int width, height; - int i; - - width = oGetDeviceCaps(hdc, HORZRES); - height = oGetDeviceCaps(hdc, VERTRES); - - ods("DrawStart: Screen is %d x %d", width, height); - - if (sm->fFontSize < 0.01f) - sm->fFontSize = 0.01f; - else if (sm->fFontSize > 1.0f) - sm->fFontSize = 1.0f; - - int iHeight = (int)((height * 1.0) * sm->fFontSize); - if (iHeight > TEXT_HEIGHT) - iHeight = TEXT_HEIGHT; - - float s = iHeight / 60.0f; - int y = 0; - int idx = 0; - - int texs[NUM_TEXTS]; - int widths[NUM_TEXTS]; - int yofs[NUM_TEXTS]; - unsigned int color[NUM_TEXTS]; - - - DWORD dwWaitResult = WaitForSingleObject(hSharedMutex, 50L); - if (dwWaitResult != WAIT_OBJECT_0) - return; - - oglViewport(0, 0, width, height); - - oglMatrixMode(GL_PROJECTION); - oglLoadIdentity(); - oglOrtho(0, width, height, 0, -100.0, 100.0); - - oglMatrixMode(GL_MODELVIEW); - - for (i = 0; i < NUM_TEXTS; i++) { - if (sm->texts[i].width == 0) { - y += iHeight / 4; - } else if (sm->texts[i].width > 0) { - if (sm->texts[i].uiCounter != uiCounter[i]) { - ods("OpenGL: Updating %d %d texture", sm->texts[i].width, TEXT_HEIGHT); - oglBindTexture(GL_TEXTURE_2D, textures[i]); - oglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - oglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - oglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - oglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - oglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, TEXT_WIDTH, TEXT_HEIGHT, 0, GL_BGRA, GL_UNSIGNED_BYTE, sm->texts[i].texture); - uiCounter[i] = sm->texts[i].uiCounter; - } - texs[idx] = textures[i]; - widths[idx] = sm->texts[i].width; - color[idx] = sm->texts[i].color; - yofs[idx] = y; - y += iHeight; - idx++; - } - } - ReleaseMutex(hSharedMutex); - - int h = y; - y = (int)(height * sm->fY); - - if (sm->bTop) { - y -= h; - } else if (sm->bBottom) { - } else { - y -= h / 2; - } - - if (y < 1) - y = 1; - if ((y + h + 1) > (int)height) - y = height - h - 1; - - - for (i = 0; i < idx; i++) { - int w = (int)(widths[i] * s); - int x = (int)(width * sm->fX); - if (sm->bLeft) { - x -= w; - } else if (sm->bRight) { - } else { - x -= w / 2; - } - - if (x < 1) - x = 1; - if ((x + w + 1) > (int)width) - x = width - w - 1; - - ods("OpenGL: Drawing text at %d %d %d %d", x, y + yofs[i], w, iHeight); - oglBindTexture(GL_TEXTURE_2D, texs[i]); - oglPushMatrix(); - oglLoadIdentity(); - - float xm = 0.0; - float ym = 0.0; - float xmx = (1.0f * widths[i]) / TEXT_WIDTH; - float ymx = 1.0f; - - unsigned int c = color[i]; - - oglColor4ub((c >> 16) & 0xFF, (c >> 8) & 0xFF, c & 0xFF, (c >> 24) & 0xFF); - - - oglTranslatef(static_cast(x), static_cast(y + yofs[i]), 0.0f); - oglBegin(GL_QUADS); - oglTexCoord2f(xm, ymx); - oglVertex2f(0.0f, static_cast(iHeight)); - oglTexCoord2f(xm, ym); - oglVertex2f(0.0f, 0.0f); - oglTexCoord2f(xmx, ym); - oglVertex2f(static_cast(w), 0.0f); - oglTexCoord2f(xmx, ymx); - oglVertex2f(static_cast(w), static_cast(iHeight)); - oglEnd(); - oglPopMatrix(); - } -} - -static map contexts; - -static void doSwap(HDC hdc) { - HGLRC oldctx = owglGetCurrentContext(); - HDC oldhdc = owglGetCurrentDC(); - Context *c = contexts[hdc]; - - if (!c) { - ods("OpenGL: New context for device %p", hdc); - c = new Context(hdc); - contexts[hdc] = c; - } else { - ods("OpenGL: Reusing old context"); - owglMakeCurrent(hdc, c->ctx); - } - c->draw(hdc); - owglMakeCurrent(oldhdc, oldctx); -} - -static BOOL __stdcall mywglSwapBuffers(HDC hdc) { - ods("OpenGL: wglSwapBuffers"); - doSwap(hdc); - hhwglSwapBuffers.restore(); - BOOL ret=owglSwapBuffers(hdc); - hhwglSwapBuffers.inject(); - return ret; -} - -static BOOL __stdcall mySwapBuffers(HDC hdc) { - ods("OpenGL: SwapBuffers"); - hhSwapBuffers.restore(); - BOOL ret=oSwapBuffers(hdc); - hhSwapBuffers.inject(); - return ret; -} - -static BOOL __stdcall mywglSwapLayerBuffers(HDC hdc, UINT fuPlanes) { - ods("OpenGL: SwapLayerBuffers %x",fuPlanes); - hhwglSwapLayerBuffers.restore(); - BOOL ret=owglSwapLayerBuffers(hdc, fuPlanes); - hhwglSwapLayerBuffers.inject(); - return ret; -} - - -#undef GLDEF -#define GLDEF(name) o##name = reinterpret_cast(GetProcAddress(hGL, #name)) - -void checkOpenGLHook() { - if (bChaining) { - return; - ods("Causing a chain"); - } - - bChaining = true; - - HMODULE hGL = GetModuleHandle("OpenGL32.DLL"); - - if (hGL != NULL) { - if (! bHooked) { - char procname[1024]; - GetModuleFileName(NULL, procname, 1024); - fods("OpenGL: Unhooked OpenGL App %s", procname); - bHooked = true; - - INJECT(wglSwapBuffers); - // INJECT(wglSwapLayerBuffers); - - GLDEF(wglCreateContext); - GLDEF(glGenTextures); - GLDEF(glEnable); - GLDEF(glDisable); - GLDEF(glBlendFunc); - GLDEF(glColorMaterial); - GLDEF(glViewport); - GLDEF(glMatrixMode); - GLDEF(glLoadIdentity); - GLDEF(glOrtho); - GLDEF(glBindTexture); - GLDEF(glPushMatrix); - GLDEF(glColor4ub); - GLDEF(glTranslatef); - GLDEF(glBegin); - GLDEF(glEnd); - GLDEF(glTexCoord2f); - GLDEF(glVertex2f); - GLDEF(glPopMatrix); - GLDEF(glTexParameteri); - GLDEF(glTexImage2D); - GLDEF(wglMakeCurrent); - GLDEF(wglGetCurrentContext); - GLDEF(wglGetCurrentDC); - - hGL = GetModuleHandle("GDI32.DLL"); - if (hGL) { - // INJECT(SwapBuffers); - GLDEF(GetDeviceCaps); - } else { - ods("OpenGL: Failed to find GDI32"); - } - } - } - - bChaining = false; -} +/* Copyright (C) 2005-2009, Thorvald Natvig + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + - Neither the name of the Mumble Developers nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "lib.h" + +typedef unsigned int GLenum; +typedef unsigned char GLboolean; +typedef void GLvoid; +typedef int GLint; +typedef unsigned char GLubyte; +typedef unsigned int GLuint; +typedef int GLsizei; +typedef float GLfloat; +typedef double GLdouble; + +#define GL_TEXTURE_2D 0x0DE1 +#define GL_UNSIGNED_BYTE 0x1401 +#define GL_QUADS 0x0007 +#define GL_RGBA 0x1908 +#define GL_BGRA 0x80E1 +#define GL_TEXTURE_WRAP_S 0x2802 +#define GL_TEXTURE_WRAP_T 0x2803 +#define GL_CLAMP_TO_EDGE 0x812F +#define GL_TEXTURE_MAG_FILTER 0x2800 +#define GL_TEXTURE_MIN_FILTER 0x2801 +#define GL_LINEAR 0x2601 +#define GL_FRONT_AND_BACK 0x0408 +#define GL_AMBIENT_AND_DIFFUSE 0x1602 +#define GL_SRC_ALPHA 0x0302 +#define GL_ONE_MINUS_SRC_ALPHA 0x0303 +#define GL_TEXTURE_GEN_S 0x0C60 +#define GL_TEXTURE_GEN_T 0x0C61 +#define GL_TEXTURE_GEN_R 0x0C62 +#define GL_TEXTURE_GEN_Q 0x0C63 +#define GL_STENCIL_TEST 0x0B90 +#define GL_SCISSOR_TEST 0x0C11 +#define GL_SEPARABLE_2D 0x8012 +#define GL_MINMAX 0x802E +#define GL_LIGHTING 0x0B50 +#define GL_INDEX_LOGIC_OP 0x0BF1 +#define GL_HISTOGRAM 0x8024 +#define GL_FOG 0x0B60 +#define GL_DITHER 0x0BD0 +#define GL_DEPTH_TEST 0x0B71 +#define GL_CULL_FACE 0x0B44 +#define GL_CONVOLUTION_1D 0x8010 +#define GL_CONVOLUTION_2D 0x8011 +#define GL_COLOR_TABLE 0x80D0 +#define GL_COLOR_MATERIAL 0x0B57 +#define GL_AUTO_NORMAL 0x0D80 +#define GL_ALPHA_TEST 0x0BC0 +#define GL_COLOR_LOGIC_OP 0x0BF2 +#define GL_MATRIX_MODE 0x0BA0 +#define GL_MODELVIEW 0x1700 +#define GL_PROJECTION 0x1701 +#define GL_BLEND 0x0BE2 + +#define TDEF(ret, name, arg) typedef ret (__stdcall * t##name) arg +#define GLDEF(ret, name, arg) TDEF(ret, name, arg); t##name o##name = NULL + +GLDEF(HGLRC, wglCreateContext, (HDC)); +GLDEF(void, glGenTextures, (GLsizei, GLuint *)); +GLDEF(void, glEnable, (GLenum)); +GLDEF(void, glDisable, (GLenum)); +GLDEF(void, glBlendFunc, (GLenum, GLenum)); +GLDEF(void, glColorMaterial, (GLenum, GLenum)); +GLDEF(void, glViewport, (GLint, GLint, GLsizei, GLsizei)); +GLDEF(void, glMatrixMode, (GLenum)); +GLDEF(void, glLoadIdentity, (void)); +GLDEF(void, glOrtho, (GLdouble, GLdouble, GLdouble, GLdouble, GLdouble, GLdouble)); +GLDEF(void, glBindTexture, (GLenum, GLuint)); +GLDEF(void, glPushMatrix, (void)); +GLDEF(void, glColor4ub, (GLubyte, GLubyte, GLubyte, GLubyte)); +GLDEF(void, glTranslatef, (GLfloat, GLfloat, GLfloat)); +GLDEF(void, glBegin, (GLenum)); +GLDEF(void, glEnd, (void)); +GLDEF(void, glTexCoord2f, (GLfloat, GLfloat)); +GLDEF(void, glVertex2f, (GLfloat, GLfloat)); +GLDEF(void, glPopMatrix, (void)); +GLDEF(void, glTexParameteri, (GLenum, GLenum, GLint)); +GLDEF(void, glTexImage2D, (GLenum, GLint, GLint, GLsizei, GLsizei, GLint, GLenum, GLenum, const GLvoid *)); +GLDEF(void, wglMakeCurrent, (HDC, HGLRC)); +GLDEF(HGLRC, wglGetCurrentContext, (void)); +GLDEF(HDC, wglGetCurrentDC, (void)); +GLDEF(int, GetDeviceCaps, (HDC, int)); + +#define INJDEF(ret, name, arg) GLDEF(ret, name, arg); static HardHook hh##name +#define INJECT(name) { o##name = reinterpret_cast(GetProcAddress(hGL, #name)); if (o##name) { hh##name.setup(reinterpret_cast(o##name), reinterpret_cast(my##name)); o##name = (t##name) hh##name.call; } else { ods("OpenGL: No GetProc for %s", #name);} } + +INJDEF(BOOL, wglSwapLayerBuffers, (HDC, UINT)); +INJDEF(BOOL, wglSwapBuffers, (HDC)); +INJDEF(BOOL, SwapBuffers, (HDC)); + +static bool bHooked = false; +static bool bChaining = false; + +struct Context { + HGLRC ctx; + GLuint textures[NUM_TEXTS]; + unsigned int uiCounter[NUM_TEXTS]; + Context(HDC hdc); + void draw(HDC hdc); +}; + +Context::Context(HDC hdc) { + ctx = owglCreateContext(hdc); + owglMakeCurrent(hdc, ctx); + + oglGenTextures(NUM_TEXTS, textures); + + // Here we go. From the top. Where is glResetState? + oglDisable(GL_ALPHA_TEST); + oglDisable(GL_AUTO_NORMAL); + oglEnable(GL_BLEND); + // Skip clip planes, there are thousands of them. + oglDisable(GL_COLOR_LOGIC_OP); + oglEnable(GL_COLOR_MATERIAL); + oglDisable(GL_COLOR_TABLE); + oglDisable(GL_CONVOLUTION_1D); + oglDisable(GL_CONVOLUTION_2D); + oglDisable(GL_CULL_FACE); + oglDisable(GL_DEPTH_TEST); + oglDisable(GL_DITHER); + oglDisable(GL_FOG); + oglDisable(GL_HISTOGRAM); + oglDisable(GL_INDEX_LOGIC_OP); + oglDisable(GL_LIGHTING); + // Skip line smmooth + // Skip map + oglDisable(GL_MINMAX); + // Skip polygon offset + oglDisable(GL_SEPARABLE_2D); + oglDisable(GL_SCISSOR_TEST); + oglDisable(GL_STENCIL_TEST); + oglEnable(GL_TEXTURE_2D); + oglDisable(GL_TEXTURE_GEN_Q); + oglDisable(GL_TEXTURE_GEN_R); + oglDisable(GL_TEXTURE_GEN_S); + oglDisable(GL_TEXTURE_GEN_T); + + oglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + oglColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); + + DWORD dwWaitResult = WaitForSingleObject(hSharedMutex, 50L); + if (dwWaitResult != WAIT_OBJECT_0) + return; + + for (int i=0;ibHooked = true; + + // DEBUG + // sm->bDebug = true; + + unsigned int width, height; + int i; + + width = oGetDeviceCaps(hdc, HORZRES); + height = oGetDeviceCaps(hdc, VERTRES); + + ods("DrawStart: Screen is %d x %d", width, height); + + if (sm->fFontSize < 0.01f) + sm->fFontSize = 0.01f; + else if (sm->fFontSize > 1.0f) + sm->fFontSize = 1.0f; + + int iHeight = (int)((height * 1.0) * sm->fFontSize); + if (iHeight > TEXT_HEIGHT) + iHeight = TEXT_HEIGHT; + + float s = iHeight / 60.0f; + int y = 0; + int idx = 0; + + int texs[NUM_TEXTS]; + int widths[NUM_TEXTS]; + int yofs[NUM_TEXTS]; + unsigned int color[NUM_TEXTS]; + + + DWORD dwWaitResult = WaitForSingleObject(hSharedMutex, 50L); + if (dwWaitResult != WAIT_OBJECT_0) + return; + + oglViewport(0, 0, width, height); + + oglMatrixMode(GL_PROJECTION); + oglLoadIdentity(); + oglOrtho(0, width, height, 0, -100.0, 100.0); + + oglMatrixMode(GL_MODELVIEW); + + for (i = 0; i < NUM_TEXTS; i++) { + if (sm->texts[i].width == 0) { + y += iHeight / 4; + } else if (sm->texts[i].width > 0) { + if (sm->texts[i].uiCounter != uiCounter[i]) { + ods("OpenGL: Updating %d %d texture", sm->texts[i].width, TEXT_HEIGHT); + oglBindTexture(GL_TEXTURE_2D, textures[i]); + oglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + oglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + oglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + oglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + oglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, TEXT_WIDTH, TEXT_HEIGHT, 0, GL_BGRA, GL_UNSIGNED_BYTE, sm->texts[i].texture); + uiCounter[i] = sm->texts[i].uiCounter; + } + texs[idx] = textures[i]; + widths[idx] = sm->texts[i].width; + color[idx] = sm->texts[i].color; + yofs[idx] = y; + y += iHeight; + idx++; + } + } + ReleaseMutex(hSharedMutex); + + int h = y; + y = (int)(height * sm->fY); + + if (sm->bTop) { + y -= h; + } else if (sm->bBottom) { + } else { + y -= h / 2; + } + + if (y < 1) + y = 1; + if ((y + h + 1) > (int)height) + y = height - h - 1; + + + for (i = 0; i < idx; i++) { + int w = (int)(widths[i] * s); + int x = (int)(width * sm->fX); + if (sm->bLeft) { + x -= w; + } else if (sm->bRight) { + } else { + x -= w / 2; + } + + if (x < 1) + x = 1; + if ((x + w + 1) > (int)width) + x = width - w - 1; + + ods("OpenGL: Drawing text at %d %d %d %d", x, y + yofs[i], w, iHeight); + oglBindTexture(GL_TEXTURE_2D, texs[i]); + oglPushMatrix(); + oglLoadIdentity(); + + float xm = 0.0; + float ym = 0.0; + float xmx = (1.0f * widths[i]) / TEXT_WIDTH; + float ymx = 1.0f; + + unsigned int c = color[i]; + + oglColor4ub((c >> 16) & 0xFF, (c >> 8) & 0xFF, c & 0xFF, (c >> 24) & 0xFF); + + + oglTranslatef(static_cast(x), static_cast(y + yofs[i]), 0.0f); + oglBegin(GL_QUADS); + oglTexCoord2f(xm, ymx); + oglVertex2f(0.0f, static_cast(iHeight)); + oglTexCoord2f(xm, ym); + oglVertex2f(0.0f, 0.0f); + oglTexCoord2f(xmx, ym); + oglVertex2f(static_cast(w), 0.0f); + oglTexCoord2f(xmx, ymx); + oglVertex2f(static_cast(w), static_cast(iHeight)); + oglEnd(); + oglPopMatrix(); + } +} + +static map contexts; + +static void doSwap(HDC hdc) { + HGLRC oldctx = owglGetCurrentContext(); + HDC oldhdc = owglGetCurrentDC(); + Context *c = contexts[hdc]; + + if (!c) { + ods("OpenGL: New context for device %p", hdc); + c = new Context(hdc); + contexts[hdc] = c; + } else { + ods("OpenGL: Reusing old context"); + owglMakeCurrent(hdc, c->ctx); + } + c->draw(hdc); + owglMakeCurrent(oldhdc, oldctx); +} + +static BOOL __stdcall mywglSwapBuffers(HDC hdc) { + ods("OpenGL: wglSwapBuffers"); + doSwap(hdc); + hhwglSwapBuffers.restore(); + BOOL ret=owglSwapBuffers(hdc); + hhwglSwapBuffers.inject(); + return ret; +} + +static BOOL __stdcall mySwapBuffers(HDC hdc) { + ods("OpenGL: SwapBuffers"); + hhSwapBuffers.restore(); + BOOL ret=oSwapBuffers(hdc); + hhSwapBuffers.inject(); + return ret; +} + +static BOOL __stdcall mywglSwapLayerBuffers(HDC hdc, UINT fuPlanes) { + ods("OpenGL: SwapLayerBuffers %x",fuPlanes); + hhwglSwapLayerBuffers.restore(); + BOOL ret=owglSwapLayerBuffers(hdc, fuPlanes); + hhwglSwapLayerBuffers.inject(); + return ret; +} + + +#undef GLDEF +#define GLDEF(name) o##name = reinterpret_cast(GetProcAddress(hGL, #name)) + +void checkOpenGLHook() { + if (bChaining) { + return; + ods("Causing a chain"); + } + + bChaining = true; + + HMODULE hGL = GetModuleHandle("OpenGL32.DLL"); + + if (hGL != NULL) { + if (! bHooked) { + char procname[1024]; + GetModuleFileName(NULL, procname, 1024); + fods("OpenGL: Unhooked OpenGL App %s", procname); + bHooked = true; + + INJECT(wglSwapBuffers); + // INJECT(wglSwapLayerBuffers); + + GLDEF(wglCreateContext); + GLDEF(glGenTextures); + GLDEF(glEnable); + GLDEF(glDisable); + GLDEF(glBlendFunc); + GLDEF(glColorMaterial); + GLDEF(glViewport); + GLDEF(glMatrixMode); + GLDEF(glLoadIdentity); + GLDEF(glOrtho); + GLDEF(glBindTexture); + GLDEF(glPushMatrix); + GLDEF(glColor4ub); + GLDEF(glTranslatef); + GLDEF(glBegin); + GLDEF(glEnd); + GLDEF(glTexCoord2f); + GLDEF(glVertex2f); + GLDEF(glPopMatrix); + GLDEF(glTexParameteri); + GLDEF(glTexImage2D); + GLDEF(wglMakeCurrent); + GLDEF(wglGetCurrentContext); + GLDEF(wglGetCurrentDC); + + hGL = GetModuleHandle("GDI32.DLL"); + if (hGL) { + // INJECT(SwapBuffers); + GLDEF(GetDeviceCaps); + } else { + ods("OpenGL: Failed to find GDI32"); + } + } + } + + bChaining = false; +} diff --git a/overlay_macx/overlay_macx.pro b/overlay_macx/overlay_macx.pro index df3703175..d73c74b9c 100644 --- a/overlay_macx/overlay_macx.pro +++ b/overlay_macx/overlay_macx.pro @@ -2,4 +2,3 @@ TEMPLATE = subdirs CONFIG += debug_and_release SUBDIRS = injector overlay stub -DIST = bundle.pri diff --git a/plugins/aoc/aoc.cpp b/plugins/aoc/aoc.cpp index 40cada44a..cb3a66e7d 100644 --- a/plugins/aoc/aoc.cpp +++ b/plugins/aoc/aoc.cpp @@ -1,184 +1,184 @@ -#include -#include -#include -#include - -#define _USE_MATH_DEFINES -#include - -#include "../mumble_plugin.h" - -HANDLE h; -BYTE *posptr; -BYTE *rotptr; -BYTE *stateptr; - -static DWORD getProcess(const wchar_t *exename) { - PROCESSENTRY32 pe; - DWORD pid = 0; - - pe.dwSize = sizeof(pe); - HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - if (hSnap != INVALID_HANDLE_VALUE) { - BOOL ok = Process32First(hSnap, &pe); - - while (ok) { - if (wcscmp(pe.szExeFile, exename)==0) { - pid = pe.th32ProcessID; - break; - } - ok = Process32Next(hSnap, &pe); - } - CloseHandle(hSnap); - } - return pid; -} - -static BYTE *getModuleAddr(DWORD pid, const wchar_t *modname) { - MODULEENTRY32 me; - BYTE *addr = NULL; - me.dwSize = sizeof(me); - HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid); - if (hSnap != INVALID_HANDLE_VALUE) { - BOOL ok = Module32First(hSnap, &me); - - while (ok) { - if (wcscmp(me.szModule, modname)==0) { - addr = me.modBaseAddr; - break; - } - ok = Module32Next(hSnap, &me); - } - CloseHandle(hSnap); - } - return addr; -} - - -static bool peekProc(VOID *base, VOID *dest, SIZE_T len) { - SIZE_T r; - BOOL ok=ReadProcessMemory(h, base, dest, len, &r); - return (ok && (r == len)); -} - -static void about(HWND h) { - ::MessageBox(h, L"Reads audio position information from Age of Chivalry (Build 3740)", L"Mumble AOC Plugin", MB_OK); -} - -static bool calcout(float *pos, float *rot, float *opos, float *front, float *top) { - float h = rot[0]; - float v = rot[1]; - - if ((v < -360.0f) || (v > 360.0f) || (h < -360.0f) || (h > 360.0f)) - return false; - - h *= static_cast(M_PI / 180.0f); - v *= static_cast(M_PI / 180.0f); - - // Seems AOC is in inches. INCHES?!? - opos[0] = pos[0] / 39.37f; - opos[1] = pos[2] / 39.37f; - opos[2] = pos[1] / 39.37f; - - front[0] = cos(v) * cos(h); - front[1] = -sin(h); - front[2] = sin(v) * cos(h); - - h -= static_cast(M_PI / 2.0f); - - top[0] = cos(v) * cos(h); - top[1] = -sin(h); - top[2] = sin(v) * cos(h); - - return true; -} - -static int trylock() { - h = NULL; - posptr = rotptr = NULL; - - DWORD pid=getProcess(L"hl2.exe"); - if (!pid) - return false; - BYTE *mod=getModuleAddr(pid, L"client.dll"); - if (!mod) - return false; - h=OpenProcess(PROCESS_VM_READ, false, pid); - if (!h) - return false; - - // Check if we really have AOC running - /* - position tuple: client.dll+0xb72ad0 (x,y,z, float) - orientation tuple: client.dll+0xb73b90 (v,h float) - ID string: client.dll+0xb2e6e0 = "ageofchivalry" (13 characters, text) - spawn state: client.dll+0xb33ae8 (0 when at main menu, 1 when at team selection, 2 when not spawned, - 6 when spawned on red team, 7 when spawned on blue team, byte) - */ - char sMagic[13]; - if(!peekProc(mod + 0xb2e6e0, sMagic, 13) || strncmp("ageofchivalry", sMagic, 13)!=0) - return false; - - // Remember addresses for later - posptr = mod + 0xb72ad0; - rotptr = mod + 0xb73b90; - stateptr = mod + 0xb33ae8; - - float pos[3]; - float rot[3]; - float opos[3], top[3], front[3]; - - bool ok = peekProc(posptr, pos, 12) && - peekProc(rotptr, rot, 12); - - if (ok) - return calcout(pos, rot, opos, top, front); - // If it failed clean up - CloseHandle(h); - h = NULL; - return false; -} - -static void unlock() { - if (h) { - CloseHandle(h); - h = NULL; - } - return; -} - -static int fetch(float *pos, float *front, float *top) { - for (int i=0;i<3;i++) - pos[i] = front[i] = top[i] = 0; - - float ipos[3], rot[3]; - bool ok; - char state; - - ok = peekProc(posptr, ipos, 12) && - peekProc(rotptr, rot, 12) && - peekProc(stateptr, &state, 1); - if (!ok) - return false; - - // Check to see if you are spawned - if (state == 0 || state == 1 || state == 2) - return true; // Deactivate plugin - - return calcout(ipos, rot, pos, front, top); -} - -static MumblePlugin aocplug = { - MUMBLE_PLUGIN_MAGIC, - L"Age of Chivalry (Build 3740)", - L"Age of Chivalry", - about, - NULL, - trylock, - unlock, - fetch -}; - -extern "C" __declspec(dllexport) MumblePlugin *getMumblePlugin() { - return &aocplug; -} +#include +#include +#include +#include + +#define _USE_MATH_DEFINES +#include + +#include "../mumble_plugin.h" + +HANDLE h; +BYTE *posptr; +BYTE *rotptr; +BYTE *stateptr; + +static DWORD getProcess(const wchar_t *exename) { + PROCESSENTRY32 pe; + DWORD pid = 0; + + pe.dwSize = sizeof(pe); + HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (hSnap != INVALID_HANDLE_VALUE) { + BOOL ok = Process32First(hSnap, &pe); + + while (ok) { + if (wcscmp(pe.szExeFile, exename)==0) { + pid = pe.th32ProcessID; + break; + } + ok = Process32Next(hSnap, &pe); + } + CloseHandle(hSnap); + } + return pid; +} + +static BYTE *getModuleAddr(DWORD pid, const wchar_t *modname) { + MODULEENTRY32 me; + BYTE *addr = NULL; + me.dwSize = sizeof(me); + HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid); + if (hSnap != INVALID_HANDLE_VALUE) { + BOOL ok = Module32First(hSnap, &me); + + while (ok) { + if (wcscmp(me.szModule, modname)==0) { + addr = me.modBaseAddr; + break; + } + ok = Module32Next(hSnap, &me); + } + CloseHandle(hSnap); + } + return addr; +} + + +static bool peekProc(VOID *base, VOID *dest, SIZE_T len) { + SIZE_T r; + BOOL ok=ReadProcessMemory(h, base, dest, len, &r); + return (ok && (r == len)); +} + +static void about(HWND h) { + ::MessageBox(h, L"Reads audio position information from Age of Chivalry (Build 3740)", L"Mumble AOC Plugin", MB_OK); +} + +static bool calcout(float *pos, float *rot, float *opos, float *front, float *top) { + float h = rot[0]; + float v = rot[1]; + + if ((v < -360.0f) || (v > 360.0f) || (h < -360.0f) || (h > 360.0f)) + return false; + + h *= static_cast(M_PI / 180.0f); + v *= static_cast(M_PI / 180.0f); + + // Seems AOC is in inches. INCHES?!? + opos[0] = pos[0] / 39.37f; + opos[1] = pos[2] / 39.37f; + opos[2] = pos[1] / 39.37f; + + front[0] = cos(v) * cos(h); + front[1] = -sin(h); + front[2] = sin(v) * cos(h); + + h -= static_cast(M_PI / 2.0f); + + top[0] = cos(v) * cos(h); + top[1] = -sin(h); + top[2] = sin(v) * cos(h); + + return true; +} + +static int trylock() { + h = NULL; + posptr = rotptr = NULL; + + DWORD pid=getProcess(L"hl2.exe"); + if (!pid) + return false; + BYTE *mod=getModuleAddr(pid, L"client.dll"); + if (!mod) + return false; + h=OpenProcess(PROCESS_VM_READ, false, pid); + if (!h) + return false; + + // Check if we really have AOC running + /* + position tuple: client.dll+0xb72ad0 (x,y,z, float) + orientation tuple: client.dll+0xb73b90 (v,h float) + ID string: client.dll+0xb2e6e0 = "ageofchivalry" (13 characters, text) + spawn state: client.dll+0xb33ae8 (0 when at main menu, 1 when at team selection, 2 when not spawned, + 6 when spawned on red team, 7 when spawned on blue team, byte) + */ + char sMagic[13]; + if (!peekProc(mod + 0xb2e6e0, sMagic, 13) || strncmp("ageofchivalry", sMagic, 13)!=0) + return false; + + // Remember addresses for later + posptr = mod + 0xb72ad0; + rotptr = mod + 0xb73b90; + stateptr = mod + 0xb33ae8; + + float pos[3]; + float rot[3]; + float opos[3], top[3], front[3]; + + bool ok = peekProc(posptr, pos, 12) && + peekProc(rotptr, rot, 12); + + if (ok) + return calcout(pos, rot, opos, top, front); + // If it failed clean up + CloseHandle(h); + h = NULL; + return false; +} + +static void unlock() { + if (h) { + CloseHandle(h); + h = NULL; + } + return; +} + +static int fetch(float *pos, float *front, float *top) { + for (int i=0;i<3;i++) + pos[i] = front[i] = top[i] = 0; + + float ipos[3], rot[3]; + bool ok; + char state; + + ok = peekProc(posptr, ipos, 12) && + peekProc(rotptr, rot, 12) && + peekProc(stateptr, &state, 1); + if (!ok) + return false; + + // Check to see if you are spawned + if (state == 0 || state == 1 || state == 2) + return true; // Deactivate plugin + + return calcout(ipos, rot, pos, front, top); +} + +static MumblePlugin aocplug = { + MUMBLE_PLUGIN_MAGIC, + L"Age of Chivalry (Build 3740)", + L"Age of Chivalry", + about, + NULL, + trylock, + unlock, + fetch +}; + +extern "C" __declspec(dllexport) MumblePlugin *getMumblePlugin() { + return &aocplug; +} diff --git a/plugins/aoc/aoc.pro b/plugins/aoc/aoc.pro index 413126e4e..782776c74 100644 --- a/plugins/aoc/aoc.pro +++ b/plugins/aoc/aoc.pro @@ -1,7 +1,7 @@ -include(../plugins.pri) - -TARGET = aoc -SOURCES = aoc.cpp -LIBS += -luser32 - - +include(../plugins.pri) + +TARGET = aoc +SOURCES = aoc.cpp +LIBS += -luser32 + + diff --git a/plugins/css/css.cpp b/plugins/css/css.cpp index 3f265ce35..379c493fe 100644 --- a/plugins/css/css.cpp +++ b/plugins/css/css.cpp @@ -1,184 +1,184 @@ -#include -#include -#include -#include - -#define _USE_MATH_DEFINES -#include - -#include "../mumble_plugin.h" - -HANDLE h; -BYTE *posptr; -BYTE *rotptr; -BYTE *stateptr; - -static DWORD getProcess(const wchar_t *exename) { - PROCESSENTRY32 pe; - DWORD pid = 0; - - pe.dwSize = sizeof(pe); - HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - if (hSnap != INVALID_HANDLE_VALUE) { - BOOL ok = Process32First(hSnap, &pe); - - while (ok) { - if (wcscmp(pe.szExeFile, exename)==0) { - pid = pe.th32ProcessID; - break; - } - ok = Process32Next(hSnap, &pe); - } - CloseHandle(hSnap); - } - return pid; -} - -static BYTE *getModuleAddr(DWORD pid, const wchar_t *modname) { - MODULEENTRY32 me; - BYTE *addr = NULL; - me.dwSize = sizeof(me); - HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid); - if (hSnap != INVALID_HANDLE_VALUE) { - BOOL ok = Module32First(hSnap, &me); - - while (ok) { - if (wcscmp(me.szModule, modname)==0) { - addr = me.modBaseAddr; - break; - } - ok = Module32Next(hSnap, &me); - } - CloseHandle(hSnap); - } - return addr; -} - - -static bool peekProc(VOID *base, VOID *dest, SIZE_T len) { - SIZE_T r; - BOOL ok=ReadProcessMemory(h, base, dest, len, &r); - return (ok && (r == len)); -} - -static void about(HWND h) { - ::MessageBox(h, L"Reads audio position information from Counter-Strike: Source (Build 3698)", L"Mumble CSS Plugin", MB_OK); -} - -static bool calcout(float *pos, float *rot, float *opos, float *front, float *top) { - float h = rot[0]; - float v = rot[1]; - - if ((v < -180.0f) || (v > 180.0f) || (h < -180.0f) || (h > 180.0f)) - return false; - - h *= static_cast(M_PI / 180.0f); - v *= static_cast(M_PI / 180.0f); - - // Seems CSS is in inches. INCHES?!? - opos[0] = pos[0] / 39.37f; - opos[1] = pos[2] / 39.37f; - opos[2] = pos[1] / 39.37f; - - front[0] = cos(v) * cos(h); - front[1] = -sin(h); - front[2] = sin(v) * cos(h); - - h -= static_cast(M_PI / 2.0f); - - top[0] = cos(v) * cos(h); - top[1] = -sin(h); - top[2] = sin(v) * cos(h); - - return true; -} - -static int trylock() { - h = NULL; - posptr = rotptr = NULL; - - DWORD pid=getProcess(L"hl2.exe"); - if (!pid) - return false; - BYTE *mod=getModuleAddr(pid, L"client.dll"); - if (!mod) - return false; - h=OpenProcess(PROCESS_VM_READ, false, pid); - if (!h) - return false; - - // Check if we really have CSS running - /* - position tuple: client.dll+0x39b504 (x,y,z, float) - orientation tuple: client.dll+0x3ec684 (v,h float) - ID string: client.dll+0x39cde9 = "CSSpectatorGUI@@" (16 characters, text) - spawn state: client.dll+0x38e050 (0 in main menu, 3 when at team selection menu, 5 when spawned as CT, 6 when spawns as T) - */ - char sMagic[16]; - if(!peekProc(mod + 0x39cde9, sMagic, 16) || strncmp("CSSpectatorGUI@@", sMagic, 16)!=0) - return false; - - // Remember addresses for later - posptr = mod + 0x39b504; - rotptr = mod + 0x3ec684; - stateptr = mod + 0x38e050; - - float pos[3]; - float rot[3]; - float opos[3], top[3], front[3]; - - bool ok = peekProc(posptr, pos, 12) && - peekProc(rotptr, rot, 12); - - if (ok) - return calcout(pos, rot, opos, top, front); - // If it failed clean up - CloseHandle(h); - h = NULL; - return false; -} - -static void unlock() { - if (h) { - CloseHandle(h); - h = NULL; - } - return; -} - -static int fetch(float *pos, float *front, float *top) { - float ipos[3], rot[3]; - bool ok; - char state; - - for (int i=0;i<3;i++) - pos[i] = front[i] = top[i] = 0; - - ok = peekProc(posptr, ipos, 12) && - peekProc(rotptr, rot, 12) && - peekProc(stateptr , &state, 1); - if (!ok) - return false; - - //Check to see if you are in a server - //spawn state: client.dll+0x38e050 (0 in main menu, 3 when at team selection menu, 5 when spawned as CT, 6 when spawns as T) - if (state == 0 || state == 3) - return true; // Deactivate plugin - - return calcout(ipos, rot, pos, front, top); -} - -static MumblePlugin cssplug = { - MUMBLE_PLUGIN_MAGIC, - L"Counter-Strike: Source (Build 3698)", - L"Counter-Strike: Source", - about, - NULL, - trylock, - unlock, - fetch -}; - -extern "C" __declspec(dllexport) MumblePlugin *getMumblePlugin() { - return &cssplug; -} +#include +#include +#include +#include + +#define _USE_MATH_DEFINES +#include + +#include "../mumble_plugin.h" + +HANDLE h; +BYTE *posptr; +BYTE *rotptr; +BYTE *stateptr; + +static DWORD getProcess(const wchar_t *exename) { + PROCESSENTRY32 pe; + DWORD pid = 0; + + pe.dwSize = sizeof(pe); + HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (hSnap != INVALID_HANDLE_VALUE) { + BOOL ok = Process32First(hSnap, &pe); + + while (ok) { + if (wcscmp(pe.szExeFile, exename)==0) { + pid = pe.th32ProcessID; + break; + } + ok = Process32Next(hSnap, &pe); + } + CloseHandle(hSnap); + } + return pid; +} + +static BYTE *getModuleAddr(DWORD pid, const wchar_t *modname) { + MODULEENTRY32 me; + BYTE *addr = NULL; + me.dwSize = sizeof(me); + HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid); + if (hSnap != INVALID_HANDLE_VALUE) { + BOOL ok = Module32First(hSnap, &me); + + while (ok) { + if (wcscmp(me.szModule, modname)==0) { + addr = me.modBaseAddr; + break; + } + ok = Module32Next(hSnap, &me); + } + CloseHandle(hSnap); + } + return addr; +} + + +static bool peekProc(VOID *base, VOID *dest, SIZE_T len) { + SIZE_T r; + BOOL ok=ReadProcessMemory(h, base, dest, len, &r); + return (ok && (r == len)); +} + +static void about(HWND h) { + ::MessageBox(h, L"Reads audio position information from Counter-Strike: Source (Build 3698)", L"Mumble CSS Plugin", MB_OK); +} + +static bool calcout(float *pos, float *rot, float *opos, float *front, float *top) { + float h = rot[0]; + float v = rot[1]; + + if ((v < -180.0f) || (v > 180.0f) || (h < -180.0f) || (h > 180.0f)) + return false; + + h *= static_cast(M_PI / 180.0f); + v *= static_cast(M_PI / 180.0f); + + // Seems CSS is in inches. INCHES?!? + opos[0] = pos[0] / 39.37f; + opos[1] = pos[2] / 39.37f; + opos[2] = pos[1] / 39.37f; + + front[0] = cos(v) * cos(h); + front[1] = -sin(h); + front[2] = sin(v) * cos(h); + + h -= static_cast(M_PI / 2.0f); + + top[0] = cos(v) * cos(h); + top[1] = -sin(h); + top[2] = sin(v) * cos(h); + + return true; +} + +static int trylock() { + h = NULL; + posptr = rotptr = NULL; + + DWORD pid=getProcess(L"hl2.exe"); + if (!pid) + return false; + BYTE *mod=getModuleAddr(pid, L"client.dll"); + if (!mod) + return false; + h=OpenProcess(PROCESS_VM_READ, false, pid); + if (!h) + return false; + + // Check if we really have CSS running + /* + position tuple: client.dll+0x39b504 (x,y,z, float) + orientation tuple: client.dll+0x3ec684 (v,h float) + ID string: client.dll+0x39cde9 = "CSSpectatorGUI@@" (16 characters, text) + spawn state: client.dll+0x38e050 (0 in main menu, 3 when at team selection menu, 5 when spawned as CT, 6 when spawns as T) + */ + char sMagic[16]; + if (!peekProc(mod + 0x39cde9, sMagic, 16) || strncmp("CSSpectatorGUI@@", sMagic, 16)!=0) + return false; + + // Remember addresses for later + posptr = mod + 0x39b504; + rotptr = mod + 0x3ec684; + stateptr = mod + 0x38e050; + + float pos[3]; + float rot[3]; + float opos[3], top[3], front[3]; + + bool ok = peekProc(posptr, pos, 12) && + peekProc(rotptr, rot, 12); + + if (ok) + return calcout(pos, rot, opos, top, front); + // If it failed clean up + CloseHandle(h); + h = NULL; + return false; +} + +static void unlock() { + if (h) { + CloseHandle(h); + h = NULL; + } + return; +} + +static int fetch(float *pos, float *front, float *top) { + float ipos[3], rot[3]; + bool ok; + char state; + + for (int i=0;i<3;i++) + pos[i] = front[i] = top[i] = 0; + + ok = peekProc(posptr, ipos, 12) && + peekProc(rotptr, rot, 12) && + peekProc(stateptr , &state, 1); + if (!ok) + return false; + + //Check to see if you are in a server + //spawn state: client.dll+0x38e050 (0 in main menu, 3 when at team selection menu, 5 when spawned as CT, 6 when spawns as T) + if (state == 0 || state == 3) + return true; // Deactivate plugin + + return calcout(ipos, rot, pos, front, top); +} + +static MumblePlugin cssplug = { + MUMBLE_PLUGIN_MAGIC, + L"Counter-Strike: Source (Build 3698)", + L"Counter-Strike: Source", + about, + NULL, + trylock, + unlock, + fetch +}; + +extern "C" __declspec(dllexport) MumblePlugin *getMumblePlugin() { + return &cssplug; +} diff --git a/plugins/css/css.pro b/plugins/css/css.pro index 8e5fa1fa6..7623526b3 100644 --- a/plugins/css/css.pro +++ b/plugins/css/css.pro @@ -1,7 +1,7 @@ -include(../plugins.pri) - -TARGET = css -SOURCES = css.cpp -LIBS += -luser32 - - +include(../plugins.pri) + +TARGET = css +SOURCES = css.cpp +LIBS += -luser32 + + diff --git a/plugins/gmod/gmod.cpp b/plugins/gmod/gmod.cpp index 123f5cd03..7be625de4 100644 --- a/plugins/gmod/gmod.cpp +++ b/plugins/gmod/gmod.cpp @@ -1,183 +1,183 @@ -#include -#include -#include -#include - -#define _USE_MATH_DEFINES -#include - -#include "../mumble_plugin.h" - -HANDLE h; -BYTE *posptr; -BYTE *rotptr; -BYTE *stateptr; - -static DWORD getProcess(const wchar_t *exename) { - PROCESSENTRY32 pe; - DWORD pid = 0; - - pe.dwSize = sizeof(pe); - HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - if (hSnap != INVALID_HANDLE_VALUE) { - BOOL ok = Process32First(hSnap, &pe); - - while (ok) { - if (wcscmp(pe.szExeFile, exename)==0) { - pid = pe.th32ProcessID; - break; - } - ok = Process32Next(hSnap, &pe); - } - CloseHandle(hSnap); - } - return pid; -} - -static BYTE *getModuleAddr(DWORD pid, const wchar_t *modname) { - MODULEENTRY32 me; - BYTE *addr = NULL; - me.dwSize = sizeof(me); - HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid); - if (hSnap != INVALID_HANDLE_VALUE) { - BOOL ok = Module32First(hSnap, &me); - - while (ok) { - if (wcscmp(me.szModule, modname)==0) { - addr = me.modBaseAddr; - break; - } - ok = Module32Next(hSnap, &me); - } - CloseHandle(hSnap); - } - return addr; -} - - -static bool peekProc(VOID *base, VOID *dest, SIZE_T len) { - SIZE_T r; - BOOL ok=ReadProcessMemory(h, base, dest, len, &r); - return (ok && (r == len)); -} - -static void about(HWND h) { - ::MessageBox(h, L"Reads audio position information from Garry's Mod 11 (Build 3740)", L"Mumble Gmod Plugin", MB_OK); -} - -static bool calcout(float *pos, float *rot, float *opos, float *front, float *top) { - float h = rot[0]; - float v = rot[1]; - - if ((v < -360.0f) || (v > 360.0f) || (h < -360.0f) || (h > 360.0f)) - return false; - - h *= static_cast(M_PI / 180.0f); - v *= static_cast(M_PI / 180.0f); - - // Seems Gmod is in inches. INCHES?!? - opos[0] = pos[0] / 39.37f; - opos[1] = pos[2] / 39.37f; - opos[2] = pos[1] / 39.37f; - - front[0] = cos(v) * cos(h); - front[1] = -sin(h); - front[2] = sin(v) * cos(h); - - h -= static_cast(M_PI / 2.0f); - - top[0] = cos(v) * cos(h); - top[1] = -sin(h); - top[2] = sin(v) * cos(h); - - return true; -} - -static int trylock() { - h = NULL; - posptr = rotptr = NULL; - - DWORD pid=getProcess(L"hl2.exe"); - if (!pid) - return false; - BYTE *mod=getModuleAddr(pid, L"client.dll"); - if (!mod) - return false; - h=OpenProcess(PROCESS_VM_READ, false, pid); - if (!h) - return false; - - // Check if we really have Gmod running - /* - position tuple: client.dll+0x5ee120 (x,y,z, float) - orientation tuple: client.dll+0x43e2dc (v,h float) - ID string: client.dll+0x44e740 = "GMod@@" (6 characters, text) - spawn state: client.dll+0x5b6a9d (0 when at main menu, 8 when spawned, byte) - */ - char sMagic[6]; - if(!peekProc(mod + 0x44e740, sMagic, 6) || strncmp("GMod@@", sMagic, 6)!=0) - return false; - - // Remember addresses for later - posptr = mod + 0x5ee120; - rotptr = mod + 0x43e2dc; - stateptr = mod + 0x5b6a9d; - - float pos[3]; - float rot[3]; - float opos[3], top[3], front[3]; - - bool ok = peekProc(posptr, pos, 12) && - peekProc(rotptr, rot, 12); - - if (ok) - return calcout(pos, rot, opos, top, front); - // If it failed clean up - CloseHandle(h); - h = NULL; - return false; -} - -static void unlock() { - if (h) { - CloseHandle(h); - h = NULL; - } - return; -} - -static int fetch(float *pos, float *front, float *top) { - for (int i=0;i<3;i++) - pos[i] = front[i] = top[i] = 0; - - float ipos[3], rot[3]; - bool ok; - char state; - - ok = peekProc(posptr, ipos, 12) && - peekProc(rotptr, rot, 12) && - peekProc(stateptr, &state, 1); - if (!ok) - return false; - - // Check to see if you are in a server - if (state == 0) - return true; // Deactivate plugin - - return calcout(ipos, rot, pos, front, top); -} - -static MumblePlugin gmodplug = { - MUMBLE_PLUGIN_MAGIC, - L"Garry's Mod 11 (Build 3740)", - L"Garry's Mod 11", - about, - NULL, - trylock, - unlock, - fetch -}; - -extern "C" __declspec(dllexport) MumblePlugin *getMumblePlugin() { - return &gmodplug; -} +#include +#include +#include +#include + +#define _USE_MATH_DEFINES +#include + +#include "../mumble_plugin.h" + +HANDLE h; +BYTE *posptr; +BYTE *rotptr; +BYTE *stateptr; + +static DWORD getProcess(const wchar_t *exename) { + PROCESSENTRY32 pe; + DWORD pid = 0; + + pe.dwSize = sizeof(pe); + HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (hSnap != INVALID_HANDLE_VALUE) { + BOOL ok = Process32First(hSnap, &pe); + + while (ok) { + if (wcscmp(pe.szExeFile, exename)==0) { + pid = pe.th32ProcessID; + break; + } + ok = Process32Next(hSnap, &pe); + } + CloseHandle(hSnap); + } + return pid; +} + +static BYTE *getModuleAddr(DWORD pid, const wchar_t *modname) { + MODULEENTRY32 me; + BYTE *addr = NULL; + me.dwSize = sizeof(me); + HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid); + if (hSnap != INVALID_HANDLE_VALUE) { + BOOL ok = Module32First(hSnap, &me); + + while (ok) { + if (wcscmp(me.szModule, modname)==0) { + addr = me.modBaseAddr; + break; + } + ok = Module32Next(hSnap, &me); + } + CloseHandle(hSnap); + } + return addr; +} + + +static bool peekProc(VOID *base, VOID *dest, SIZE_T len) { + SIZE_T r; + BOOL ok=ReadProcessMemory(h, base, dest, len, &r); + return (ok && (r == len)); +} + +static void about(HWND h) { + ::MessageBox(h, L"Reads audio position information from Garry's Mod 11 (Build 3740)", L"Mumble Gmod Plugin", MB_OK); +} + +static bool calcout(float *pos, float *rot, float *opos, float *front, float *top) { + float h = rot[0]; + float v = rot[1]; + + if ((v < -360.0f) || (v > 360.0f) || (h < -360.0f) || (h > 360.0f)) + return false; + + h *= static_cast(M_PI / 180.0f); + v *= static_cast(M_PI / 180.0f); + + // Seems Gmod is in inches. INCHES?!? + opos[0] = pos[0] / 39.37f; + opos[1] = pos[2] / 39.37f; + opos[2] = pos[1] / 39.37f; + + front[0] = cos(v) * cos(h); + front[1] = -sin(h); + front[2] = sin(v) * cos(h); + + h -= static_cast(M_PI / 2.0f); + + top[0] = cos(v) * cos(h); + top[1] = -sin(h); + top[2] = sin(v) * cos(h); + + return true; +} + +static int trylock() { + h = NULL; + posptr = rotptr = NULL; + + DWORD pid=getProcess(L"hl2.exe"); + if (!pid) + return false; + BYTE *mod=getModuleAddr(pid, L"client.dll"); + if (!mod) + return false; + h=OpenProcess(PROCESS_VM_READ, false, pid); + if (!h) + return false; + + // Check if we really have Gmod running + /* + position tuple: client.dll+0x5ee120 (x,y,z, float) + orientation tuple: client.dll+0x43e2dc (v,h float) + ID string: client.dll+0x44e740 = "GMod@@" (6 characters, text) + spawn state: client.dll+0x5b6a9d (0 when at main menu, 8 when spawned, byte) + */ + char sMagic[6]; + if (!peekProc(mod + 0x44e740, sMagic, 6) || strncmp("GMod@@", sMagic, 6)!=0) + return false; + + // Remember addresses for later + posptr = mod + 0x5ee120; + rotptr = mod + 0x43e2dc; + stateptr = mod + 0x5b6a9d; + + float pos[3]; + float rot[3]; + float opos[3], top[3], front[3]; + + bool ok = peekProc(posptr, pos, 12) && + peekProc(rotptr, rot, 12); + + if (ok) + return calcout(pos, rot, opos, top, front); + // If it failed clean up + CloseHandle(h); + h = NULL; + return false; +} + +static void unlock() { + if (h) { + CloseHandle(h); + h = NULL; + } + return; +} + +static int fetch(float *pos, float *front, float *top) { + for (int i=0;i<3;i++) + pos[i] = front[i] = top[i] = 0; + + float ipos[3], rot[3]; + bool ok; + char state; + + ok = peekProc(posptr, ipos, 12) && + peekProc(rotptr, rot, 12) && + peekProc(stateptr, &state, 1); + if (!ok) + return false; + + // Check to see if you are in a server + if (state == 0) + return true; // Deactivate plugin + + return calcout(ipos, rot, pos, front, top); +} + +static MumblePlugin gmodplug = { + MUMBLE_PLUGIN_MAGIC, + L"Garry's Mod 11 (Build 3740)", + L"Garry's Mod 11", + about, + NULL, + trylock, + unlock, + fetch +}; + +extern "C" __declspec(dllexport) MumblePlugin *getMumblePlugin() { + return &gmodplug; +} diff --git a/plugins/gmod/gmod.pro b/plugins/gmod/gmod.pro index 0130a5c1f..627fa546f 100644 --- a/plugins/gmod/gmod.pro +++ b/plugins/gmod/gmod.pro @@ -1,7 +1,7 @@ -include(../plugins.pri) - -TARGET = gmod -SOURCES = gmod.cpp -LIBS += -luser32 - - +include(../plugins.pri) + +TARGET = gmod +SOURCES = gmod.cpp +LIBS += -luser32 + + diff --git a/plugins/hl2dm/hl2dm.cpp b/plugins/hl2dm/hl2dm.cpp index 0155a5c5c..d2dfb9af0 100644 --- a/plugins/hl2dm/hl2dm.cpp +++ b/plugins/hl2dm/hl2dm.cpp @@ -1,184 +1,184 @@ -#include -#include -#include -#include - -#define _USE_MATH_DEFINES -#include - -#include "../mumble_plugin.h" - -HANDLE h; -BYTE *posptr; -BYTE *rotptr; -BYTE *stateptr; - -static DWORD getProcess(const wchar_t *exename) { - PROCESSENTRY32 pe; - DWORD pid = 0; - - pe.dwSize = sizeof(pe); - HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - if (hSnap != INVALID_HANDLE_VALUE) { - BOOL ok = Process32First(hSnap, &pe); - - while (ok) { - if (wcscmp(pe.szExeFile, exename)==0) { - pid = pe.th32ProcessID; - break; - } - ok = Process32Next(hSnap, &pe); - } - CloseHandle(hSnap); - } - return pid; -} - -static BYTE *getModuleAddr(DWORD pid, const wchar_t *modname) { - MODULEENTRY32 me; - BYTE *addr = NULL; - me.dwSize = sizeof(me); - HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid); - if (hSnap != INVALID_HANDLE_VALUE) { - BOOL ok = Module32First(hSnap, &me); - - while (ok) { - if (wcscmp(me.szModule, modname)==0) { - addr = me.modBaseAddr; - break; - } - ok = Module32Next(hSnap, &me); - } - CloseHandle(hSnap); - } - return addr; -} - - -static bool peekProc(VOID *base, VOID *dest, SIZE_T len) { - SIZE_T r; - BOOL ok=ReadProcessMemory(h, base, dest, len, &r); - return (ok && (r == len)); -} - -static void about(HWND h) { - ::MessageBox(h, L"Reads audio position information from Half-Life 2: Deathmatch (Build 3698)", L"Mumble HL2DM Plugin", MB_OK); -} - -static bool calcout(float *pos, float *rot, float *opos, float *front, float *top) { - float h = rot[0]; - float v = rot[1]; - - if ((v < -360.0f) || (v > 360.0f) || (h < -360.0f) || (h > 360.0f)) - return false; - - h *= static_cast(M_PI / 180.0f); - v *= static_cast(M_PI / 180.0f); - - // Seems HL2DM is in inches. INCHES?!? - opos[0] = pos[0] / 39.37f; - opos[1] = pos[2] / 39.37f; - opos[2] = pos[1] / 39.37f; - - front[0] = cos(v) * cos(h); - front[1] = -sin(h); - front[2] = sin(v) * cos(h); - - h -= static_cast(M_PI / 2.0f); - - top[0] = cos(v) * cos(h); - top[1] = -sin(h); - top[2] = sin(v) * cos(h); - - return true; -} - -static int trylock() { - - h = NULL; - posptr = rotptr = NULL; - - DWORD pid=getProcess(L"hl2.exe"); - if (!pid) - return false; - BYTE *mod=getModuleAddr(pid, L"client.dll"); - if (!mod) - return false; - h=OpenProcess(PROCESS_VM_READ, false, pid); - if (!h) - return false; - - // Check if we really have HL2DM running - /* - position tuple: client.dll+0x3dcad4 (x,y,z, float) - orientation tuple: client.dll+0x3dcae0 (v,h float) - ID string: client.dll+0x3a5674 = "Dm/$" (4 characters, text) - spawn state: client.dll+0x37e180 (0 when at main menu, 2 when not spawned, 7 when spawned, byte) - */ - char sMagic[4]; - if(!peekProc(mod + 0x3a5674, sMagic, 4) || strncmp("Dm/$", sMagic, 4)!=0) - return false; - - // Remember addresses for later - posptr = mod + 0x3dcad4; - rotptr = mod + 0x3dcae0; - stateptr = mod + 0x37e180; - - float pos[3]; - float rot[3]; - float opos[3], top[3], front[3]; - - bool ok = peekProc(posptr, pos, 12) && - peekProc(rotptr, rot, 12); - - if (ok) - return calcout(pos, rot, opos, top, front); - // If it failed clean up - CloseHandle(h); - h = NULL; - return false; -} - -static void unlock() { - if (h) { - CloseHandle(h); - h = NULL; - } - return; -} - -static int fetch(float *pos, float *front, float *top) { - for (int i=0;i<3;i++) - pos[i] = front[i] = top[i] = 0; - - float ipos[3], rot[3]; - bool ok; - char state; - - ok = peekProc(posptr, ipos, 12) && - peekProc(rotptr, rot, 12) && - peekProc(stateptr, &state, 1); - if (!ok) - return false; - - // Check to see if you are spawned - if (state == 0 || state == 2) - return true; // Deactivate plugin - - return calcout(ipos, rot, pos, front, top); -} - -static MumblePlugin hl2dmplug = { - MUMBLE_PLUGIN_MAGIC, - L"Half-Life 2: Deathmatch (Build 3698)", - L"Half-Life 2: Deathmatch", - about, - NULL, - trylock, - unlock, - fetch -}; - -extern "C" __declspec(dllexport) MumblePlugin *getMumblePlugin() { - return &hl2dmplug; -} +#include +#include +#include +#include + +#define _USE_MATH_DEFINES +#include + +#include "../mumble_plugin.h" + +HANDLE h; +BYTE *posptr; +BYTE *rotptr; +BYTE *stateptr; + +static DWORD getProcess(const wchar_t *exename) { + PROCESSENTRY32 pe; + DWORD pid = 0; + + pe.dwSize = sizeof(pe); + HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (hSnap != INVALID_HANDLE_VALUE) { + BOOL ok = Process32First(hSnap, &pe); + + while (ok) { + if (wcscmp(pe.szExeFile, exename)==0) { + pid = pe.th32ProcessID; + break; + } + ok = Process32Next(hSnap, &pe); + } + CloseHandle(hSnap); + } + return pid; +} + +static BYTE *getModuleAddr(DWORD pid, const wchar_t *modname) { + MODULEENTRY32 me; + BYTE *addr = NULL; + me.dwSize = sizeof(me); + HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid); + if (hSnap != INVALID_HANDLE_VALUE) { + BOOL ok = Module32First(hSnap, &me); + + while (ok) { + if (wcscmp(me.szModule, modname)==0) { + addr = me.modBaseAddr; + break; + } + ok = Module32Next(hSnap, &me); + } + CloseHandle(hSnap); + } + return addr; +} + + +static bool peekProc(VOID *base, VOID *dest, SIZE_T len) { + SIZE_T r; + BOOL ok=ReadProcessMemory(h, base, dest, len, &r); + return (ok && (r == len)); +} + +static void about(HWND h) { + ::MessageBox(h, L"Reads audio position information from Half-Life 2: Deathmatch (Build 3698)", L"Mumble HL2DM Plugin", MB_OK); +} + +static bool calcout(float *pos, float *rot, float *opos, float *front, float *top) { + float h = rot[0]; + float v = rot[1]; + + if ((v < -360.0f) || (v > 360.0f) || (h < -360.0f) || (h > 360.0f)) + return false; + + h *= static_cast(M_PI / 180.0f); + v *= static_cast(M_PI / 180.0f); + + // Seems HL2DM is in inches. INCHES?!? + opos[0] = pos[0] / 39.37f; + opos[1] = pos[2] / 39.37f; + opos[2] = pos[1] / 39.37f; + + front[0] = cos(v) * cos(h); + front[1] = -sin(h); + front[2] = sin(v) * cos(h); + + h -= static_cast(M_PI / 2.0f); + + top[0] = cos(v) * cos(h); + top[1] = -sin(h); + top[2] = sin(v) * cos(h); + + return true; +} + +static int trylock() { + + h = NULL; + posptr = rotptr = NULL; + + DWORD pid=getProcess(L"hl2.exe"); + if (!pid) + return false; + BYTE *mod=getModuleAddr(pid, L"client.dll"); + if (!mod) + return false; + h=OpenProcess(PROCESS_VM_READ, false, pid); + if (!h) + return false; + + // Check if we really have HL2DM running + /* + position tuple: client.dll+0x3dcad4 (x,y,z, float) + orientation tuple: client.dll+0x3dcae0 (v,h float) + ID string: client.dll+0x3a5674 = "Dm/$" (4 characters, text) + spawn state: client.dll+0x37e180 (0 when at main menu, 2 when not spawned, 7 when spawned, byte) + */ + char sMagic[4]; + if (!peekProc(mod + 0x3a5674, sMagic, 4) || strncmp("Dm/$", sMagic, 4)!=0) + return false; + + // Remember addresses for later + posptr = mod + 0x3dcad4; + rotptr = mod + 0x3dcae0; + stateptr = mod + 0x37e180; + + float pos[3]; + float rot[3]; + float opos[3], top[3], front[3]; + + bool ok = peekProc(posptr, pos, 12) && + peekProc(rotptr, rot, 12); + + if (ok) + return calcout(pos, rot, opos, top, front); + // If it failed clean up + CloseHandle(h); + h = NULL; + return false; +} + +static void unlock() { + if (h) { + CloseHandle(h); + h = NULL; + } + return; +} + +static int fetch(float *pos, float *front, float *top) { + for (int i=0;i<3;i++) + pos[i] = front[i] = top[i] = 0; + + float ipos[3], rot[3]; + bool ok; + char state; + + ok = peekProc(posptr, ipos, 12) && + peekProc(rotptr, rot, 12) && + peekProc(stateptr, &state, 1); + if (!ok) + return false; + + // Check to see if you are spawned + if (state == 0 || state == 2) + return true; // Deactivate plugin + + return calcout(ipos, rot, pos, front, top); +} + +static MumblePlugin hl2dmplug = { + MUMBLE_PLUGIN_MAGIC, + L"Half-Life 2: Deathmatch (Build 3698)", + L"Half-Life 2: Deathmatch", + about, + NULL, + trylock, + unlock, + fetch +}; + +extern "C" __declspec(dllexport) MumblePlugin *getMumblePlugin() { + return &hl2dmplug; +} diff --git a/plugins/hl2dm/hl2dm.pro b/plugins/hl2dm/hl2dm.pro index f26da7d26..a25e8d69c 100644 --- a/plugins/hl2dm/hl2dm.pro +++ b/plugins/hl2dm/hl2dm.pro @@ -1,7 +1,7 @@ -include(../plugins.pri) - -TARGET = hl2dm -SOURCES = hl2dm.cpp -LIBS += -luser32 - - +include(../plugins.pri) + +TARGET = hl2dm +SOURCES = hl2dm.cpp +LIBS += -luser32 + + diff --git a/plugins/insurgency/insurgency.cpp b/plugins/insurgency/insurgency.cpp index 2f77ce158..dfd5d5dd8 100644 --- a/plugins/insurgency/insurgency.cpp +++ b/plugins/insurgency/insurgency.cpp @@ -1,183 +1,183 @@ -#include -#include -#include -#include - -#define _USE_MATH_DEFINES -#include - -#include "../mumble_plugin.h" - -HANDLE h; -BYTE *posptr; -BYTE *rotptr; -BYTE *stateptr; - -static DWORD getProcess(const wchar_t *exename) { - PROCESSENTRY32 pe; - DWORD pid = 0; - - pe.dwSize = sizeof(pe); - HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - if (hSnap != INVALID_HANDLE_VALUE) { - BOOL ok = Process32First(hSnap, &pe); - - while (ok) { - if (wcscmp(pe.szExeFile, exename)==0) { - pid = pe.th32ProcessID; - break; - } - ok = Process32Next(hSnap, &pe); - } - CloseHandle(hSnap); - } - return pid; -} - -static BYTE *getModuleAddr(DWORD pid, const wchar_t *modname) { - MODULEENTRY32 me; - BYTE *addr = NULL; - me.dwSize = sizeof(me); - HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid); - if (hSnap != INVALID_HANDLE_VALUE) { - BOOL ok = Module32First(hSnap, &me); - - while (ok) { - if (wcscmp(me.szModule, modname)==0) { - addr = me.modBaseAddr; - break; - } - ok = Module32Next(hSnap, &me); - } - CloseHandle(hSnap); - } - return addr; -} - - -static bool peekProc(VOID *base, VOID *dest, SIZE_T len) { - SIZE_T r; - BOOL ok=ReadProcessMemory(h, base, dest, len, &r); - return (ok && (r == len)); -} - -static void about(HWND h) { - ::MessageBox(h, L"Reads audio position information from Insurgency: Modern Infantry Combat (Build 3698)", L"Mumble Insurgency Plugin", MB_OK); -} - -static bool calcout(float *pos, float *rot, float *opos, float *front, float *top) { - float h = rot[0]; - float v = rot[1]; - - if ((v < -360.0f) || (v > 360.0f) || (h < -360.0f) || (h > 360.0f)) - return false; - - h *= static_cast(M_PI / 180.0f); - v *= static_cast(M_PI / 180.0f); - - // Seems Insurgency is in inches. INCHES?!? - opos[0] = pos[0] / 39.37f; - opos[1] = pos[2] / 39.37f; - opos[2] = pos[1] / 39.37f; - - front[0] = cos(v) * cos(h); - front[1] = -sin(h); - front[2] = sin(v) * cos(h); - - h -= static_cast(M_PI / 2.0f); - - top[0] = cos(v) * cos(h); - top[1] = -sin(h); - top[2] = sin(v) * cos(h); - - return true; -} - -static int trylock() { - h = NULL; - posptr = rotptr = NULL; - - DWORD pid=getProcess(L"hl2.exe"); - if (!pid) - return false; - BYTE *mod=getModuleAddr(pid, L"client.dll"); - if (!mod) - return false; - h=OpenProcess(PROCESS_VM_READ, false, pid); - if (!h) - return false; - - // Check if we really have Insurgency running - /* - position tuple: client.dll+0x38a950 (x,y,z, float) - orientation tuple: client.dll+0x38abc0 (v,h float) - ID string: client.dll+0x31a80a = "CombatWeapon@@" (14 characters, text) - spawn state: client.dll+0x3536a8 (0 when at main menu, 1 when not spawned, 4 when spawned) - */ - char sMagic[14]; - if(!peekProc(mod + 0x31a80a, sMagic, 14) || strncmp("CombatWeapon@@", sMagic, 14)!=0) - return false; - - // Remember addresses for later - posptr = mod + 0x38a950; - rotptr = mod + 0x38abc0; - stateptr = mod + 0x3536a8; - - float pos[3]; - float rot[3]; - float opos[3], top[3], front[3]; - - bool ok = peekProc(posptr, pos, 12) && - peekProc(rotptr, rot, 12); - - if (ok) - return calcout(pos, rot, opos, top, front); - // If it failed clean up - CloseHandle(h); - h = NULL; - return false; -} - -static void unlock() { - if (h) { - CloseHandle(h); - h = NULL; - } - return; -} - -static int fetch(float *pos, float *front, float *top) { - for (int i=0;i<3;i++) - pos[i] = front[i] = top[i] = 0; - - float ipos[3], rot[3]; - bool ok; - char state; - - ok = peekProc(posptr, ipos, 12) && - peekProc(rotptr, rot, 12) && - peekProc(stateptr, &state, 1); - if (!ok) - return false; - - // Check to see if you are spawned - if (state == 0 || state == 1) - return true; // Deactivate plugin - - return calcout(ipos, rot, pos, front, top); -} - -static MumblePlugin insurgencyplug = { - MUMBLE_PLUGIN_MAGIC, - L"Insurgency: Modern Infantry Combat (Build 3698)", - L"Insurgency: Modern Infantry Combat", - about, - NULL, - trylock, - unlock, - fetch -}; - -extern "C" __declspec(dllexport) MumblePlugin *getMumblePlugin() { - return &insurgencyplug; -} +#include +#include +#include +#include + +#define _USE_MATH_DEFINES +#include + +#include "../mumble_plugin.h" + +HANDLE h; +BYTE *posptr; +BYTE *rotptr; +BYTE *stateptr; + +static DWORD getProcess(const wchar_t *exename) { + PROCESSENTRY32 pe; + DWORD pid = 0; + + pe.dwSize = sizeof(pe); + HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (hSnap != INVALID_HANDLE_VALUE) { + BOOL ok = Process32First(hSnap, &pe); + + while (ok) { + if (wcscmp(pe.szExeFile, exename)==0) { + pid = pe.th32ProcessID; + break; + } + ok = Process32Next(hSnap, &pe); + } + CloseHandle(hSnap); + } + return pid; +} + +static BYTE *getModuleAddr(DWORD pid, const wchar_t *modname) { + MODULEENTRY32 me; + BYTE *addr = NULL; + me.dwSize = sizeof(me); + HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid); + if (hSnap != INVALID_HANDLE_VALUE) { + BOOL ok = Module32First(hSnap, &me); + + while (ok) { + if (wcscmp(me.szModule, modname)==0) { + addr = me.modBaseAddr; + break; + } + ok = Module32Next(hSnap, &me); + } + CloseHandle(hSnap); + } + return addr; +} + + +static bool peekProc(VOID *base, VOID *dest, SIZE_T len) { + SIZE_T r; + BOOL ok=ReadProcessMemory(h, base, dest, len, &r); + return (ok && (r == len)); +} + +static void about(HWND h) { + ::MessageBox(h, L"Reads audio position information from Insurgency: Modern Infantry Combat (Build 3698)", L"Mumble Insurgency Plugin", MB_OK); +} + +static bool calcout(float *pos, float *rot, float *opos, float *front, float *top) { + float h = rot[0]; + float v = rot[1]; + + if ((v < -360.0f) || (v > 360.0f) || (h < -360.0f) || (h > 360.0f)) + return false; + + h *= static_cast(M_PI / 180.0f); + v *= static_cast(M_PI / 180.0f); + + // Seems Insurgency is in inches. INCHES?!? + opos[0] = pos[0] / 39.37f; + opos[1] = pos[2] / 39.37f; + opos[2] = pos[1] / 39.37f; + + front[0] = cos(v) * cos(h); + front[1] = -sin(h); + front[2] = sin(v) * cos(h); + + h -= static_cast(M_PI / 2.0f); + + top[0] = cos(v) * cos(h); + top[1] = -sin(h); + top[2] = sin(v) * cos(h); + + return true; +} + +static int trylock() { + h = NULL; + posptr = rotptr = NULL; + + DWORD pid=getProcess(L"hl2.exe"); + if (!pid) + return false; + BYTE *mod=getModuleAddr(pid, L"client.dll"); + if (!mod) + return false; + h=OpenProcess(PROCESS_VM_READ, false, pid); + if (!h) + return false; + + // Check if we really have Insurgency running + /* + position tuple: client.dll+0x38a950 (x,y,z, float) + orientation tuple: client.dll+0x38abc0 (v,h float) + ID string: client.dll+0x31a80a = "CombatWeapon@@" (14 characters, text) + spawn state: client.dll+0x3536a8 (0 when at main menu, 1 when not spawned, 4 when spawned) + */ + char sMagic[14]; + if (!peekProc(mod + 0x31a80a, sMagic, 14) || strncmp("CombatWeapon@@", sMagic, 14)!=0) + return false; + + // Remember addresses for later + posptr = mod + 0x38a950; + rotptr = mod + 0x38abc0; + stateptr = mod + 0x3536a8; + + float pos[3]; + float rot[3]; + float opos[3], top[3], front[3]; + + bool ok = peekProc(posptr, pos, 12) && + peekProc(rotptr, rot, 12); + + if (ok) + return calcout(pos, rot, opos, top, front); + // If it failed clean up + CloseHandle(h); + h = NULL; + return false; +} + +static void unlock() { + if (h) { + CloseHandle(h); + h = NULL; + } + return; +} + +static int fetch(float *pos, float *front, float *top) { + for (int i=0;i<3;i++) + pos[i] = front[i] = top[i] = 0; + + float ipos[3], rot[3]; + bool ok; + char state; + + ok = peekProc(posptr, ipos, 12) && + peekProc(rotptr, rot, 12) && + peekProc(stateptr, &state, 1); + if (!ok) + return false; + + // Check to see if you are spawned + if (state == 0 || state == 1) + return true; // Deactivate plugin + + return calcout(ipos, rot, pos, front, top); +} + +static MumblePlugin insurgencyplug = { + MUMBLE_PLUGIN_MAGIC, + L"Insurgency: Modern Infantry Combat (Build 3698)", + L"Insurgency: Modern Infantry Combat", + about, + NULL, + trylock, + unlock, + fetch +}; + +extern "C" __declspec(dllexport) MumblePlugin *getMumblePlugin() { + return &insurgencyplug; +} diff --git a/plugins/insurgency/insurgency.pro b/plugins/insurgency/insurgency.pro index 81c51c69d..a41e3aba1 100644 --- a/plugins/insurgency/insurgency.pro +++ b/plugins/insurgency/insurgency.pro @@ -1,7 +1,7 @@ -include(../plugins.pri) - -TARGET = insurgency -SOURCES = insurgency.cpp -LIBS += -luser32 - - +include(../plugins.pri) + +TARGET = insurgency +SOURCES = insurgency.cpp +LIBS += -luser32 + + diff --git a/plugins/l4d/l4d.cpp b/plugins/l4d/l4d.cpp index f25890953..83a5b6c47 100644 --- a/plugins/l4d/l4d.cpp +++ b/plugins/l4d/l4d.cpp @@ -1,166 +1,166 @@ -#include -#include -#include -#include - -#define _USE_MATH_DEFINES -#include - -#include "../mumble_plugin.h" - -HANDLE h; -BYTE *posptr; -BYTE *rotptr; - -static DWORD getProcess(const wchar_t *exename) { - PROCESSENTRY32 pe; - DWORD pid = 0; - - pe.dwSize = sizeof(pe); - HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - if (hSnap != INVALID_HANDLE_VALUE) { - BOOL ok = Process32First(hSnap, &pe); - - while (ok) { - if (wcscmp(pe.szExeFile, exename)==0) { - pid = pe.th32ProcessID; - break; - } - ok = Process32Next(hSnap, &pe); - } - CloseHandle(hSnap); - } - return pid; -} - -static BYTE *getModuleAddr(DWORD pid, const wchar_t *modname) { - MODULEENTRY32 me; - BYTE *addr = NULL; - me.dwSize = sizeof(me); - HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid); - if (hSnap != INVALID_HANDLE_VALUE) { - BOOL ok = Module32First(hSnap, &me); - - while (ok) { - if (wcscmp(me.szModule, modname)==0) { - addr = me.modBaseAddr; - break; - } - ok = Module32Next(hSnap, &me); - } - CloseHandle(hSnap); - } - return addr; -} - - -static bool peekProc(VOID *base, VOID *dest, SIZE_T len) { - SIZE_T r; - BOOL ok=ReadProcessMemory(h, base, dest, len, &r); - return (ok && (r == len)); -} - -static void about(HWND h) { - ::MessageBox(h, L"Reads audio position information from Left 4 Dead (Build 3730)", L"Mumble L4D Plugin", MB_OK); -} - -static bool calcout(float *pos, float *rot, float *opos, float *front, float *top) { - float h = rot[0]; - float v = rot[1]; - - if ((v < -180.0f) || (v > 180.0f) || (h < -180.0f) || (h > 180.0f)) - return false; - - h *= static_cast(M_PI / 180.0f); - v *= static_cast(M_PI / 180.0f); - - // Seems L4D is in inches. INCHES?!? - opos[0] = pos[0] / 39.37f; - opos[1] = pos[2] / 39.37f; - opos[2] = pos[1] / 39.37f; - - front[0] = cos(v) * cos(h); - front[1] = -sin(h); - front[2] = sin(v) * cos(h); - - h -= static_cast(M_PI / 2.0f); - - top[0] = cos(v) * cos(h); - top[1] = -sin(h); - top[2] = sin(v) * cos(h); - /* - printf("Poll\n"); - printf("%f %f %f : %f %f\n",pos[0],pos[1],pos[2], rot[0], rot[1]); - printf("%f %f %f :: %.2f %.2f %.2f :: %.2f %.2f %.2f\n", opos[0], opos[1], opos[2], front[0], front[1], front[2], top[0], top[1], top[2]); - */ - return true; -} - -static int trylock() { - h = NULL; - posptr = rotptr = NULL; - - DWORD pid=getProcess(L"left4dead.exe"); - if (!pid) - return false; - - BYTE *mod=getModuleAddr(pid, L"client.dll"); - if (!mod) - return false; - - h=OpenProcess(PROCESS_VM_READ, false, pid); - if (!h) - return false; - - posptr = mod + 0x576240; - rotptr = mod + 0x576240 + 24; - - float pos[3]; - float rot[3]; - float opos[3], top[3], front[3]; - - bool ok = peekProc(posptr, pos, 12) && - peekProc(rotptr, rot, 12); - - if (ok) - return calcout(pos, rot, opos, top, front); - - CloseHandle(h); - h = NULL; - return false; -} - -static void unlock() { - if (h) { - CloseHandle(h); - h = NULL; - } - return; -} - -static int fetch(float *pos, float *front, float *top) { - float ipos[3], rot[3]; - bool ok; - ok = peekProc(posptr, ipos, 12) && - peekProc(rotptr, rot, 12); - - if (ok) - return calcout(ipos, rot, pos, front, top); - - return false; -} - -static MumblePlugin l4dplug = { - MUMBLE_PLUGIN_MAGIC, - L"Left 4 Dead (Build 3730)", - L"Left 4 Dead", - about, - NULL, - trylock, - unlock, - fetch -}; - -extern "C" __declspec(dllexport) MumblePlugin *getMumblePlugin() { - return &l4dplug; -} +#include +#include +#include +#include + +#define _USE_MATH_DEFINES +#include + +#include "../mumble_plugin.h" + +HANDLE h; +BYTE *posptr; +BYTE *rotptr; + +static DWORD getProcess(const wchar_t *exename) { + PROCESSENTRY32 pe; + DWORD pid = 0; + + pe.dwSize = sizeof(pe); + HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (hSnap != INVALID_HANDLE_VALUE) { + BOOL ok = Process32First(hSnap, &pe); + + while (ok) { + if (wcscmp(pe.szExeFile, exename)==0) { + pid = pe.th32ProcessID; + break; + } + ok = Process32Next(hSnap, &pe); + } + CloseHandle(hSnap); + } + return pid; +} + +static BYTE *getModuleAddr(DWORD pid, const wchar_t *modname) { + MODULEENTRY32 me; + BYTE *addr = NULL; + me.dwSize = sizeof(me); + HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid); + if (hSnap != INVALID_HANDLE_VALUE) { + BOOL ok = Module32First(hSnap, &me); + + while (ok) { + if (wcscmp(me.szModule, modname)==0) { + addr = me.modBaseAddr; + break; + } + ok = Module32Next(hSnap, &me); + } + CloseHandle(hSnap); + } + return addr; +} + + +static bool peekProc(VOID *base, VOID *dest, SIZE_T len) { + SIZE_T r; + BOOL ok=ReadProcessMemory(h, base, dest, len, &r); + return (ok && (r == len)); +} + +static void about(HWND h) { + ::MessageBox(h, L"Reads audio position information from Left 4 Dead (Build 3730)", L"Mumble L4D Plugin", MB_OK); +} + +static bool calcout(float *pos, float *rot, float *opos, float *front, float *top) { + float h = rot[0]; + float v = rot[1]; + + if ((v < -180.0f) || (v > 180.0f) || (h < -180.0f) || (h > 180.0f)) + return false; + + h *= static_cast(M_PI / 180.0f); + v *= static_cast(M_PI / 180.0f); + + // Seems L4D is in inches. INCHES?!? + opos[0] = pos[0] / 39.37f; + opos[1] = pos[2] / 39.37f; + opos[2] = pos[1] / 39.37f; + + front[0] = cos(v) * cos(h); + front[1] = -sin(h); + front[2] = sin(v) * cos(h); + + h -= static_cast(M_PI / 2.0f); + + top[0] = cos(v) * cos(h); + top[1] = -sin(h); + top[2] = sin(v) * cos(h); + /* + printf("Poll\n"); + printf("%f %f %f : %f %f\n",pos[0],pos[1],pos[2], rot[0], rot[1]); + printf("%f %f %f :: %.2f %.2f %.2f :: %.2f %.2f %.2f\n", opos[0], opos[1], opos[2], front[0], front[1], front[2], top[0], top[1], top[2]); + */ + return true; +} + +static int trylock() { + h = NULL; + posptr = rotptr = NULL; + + DWORD pid=getProcess(L"left4dead.exe"); + if (!pid) + return false; + + BYTE *mod=getModuleAddr(pid, L"client.dll"); + if (!mod) + return false; + + h=OpenProcess(PROCESS_VM_READ, false, pid); + if (!h) + return false; + + posptr = mod + 0x576240; + rotptr = mod + 0x576240 + 24; + + float pos[3]; + float rot[3]; + float opos[3], top[3], front[3]; + + bool ok = peekProc(posptr, pos, 12) && + peekProc(rotptr, rot, 12); + + if (ok) + return calcout(pos, rot, opos, top, front); + + CloseHandle(h); + h = NULL; + return false; +} + +static void unlock() { + if (h) { + CloseHandle(h); + h = NULL; + } + return; +} + +static int fetch(float *pos, float *front, float *top) { + float ipos[3], rot[3]; + bool ok; + ok = peekProc(posptr, ipos, 12) && + peekProc(rotptr, rot, 12); + + if (ok) + return calcout(ipos, rot, pos, front, top); + + return false; +} + +static MumblePlugin l4dplug = { + MUMBLE_PLUGIN_MAGIC, + L"Left 4 Dead (Build 3730)", + L"Left 4 Dead", + about, + NULL, + trylock, + unlock, + fetch +}; + +extern "C" __declspec(dllexport) MumblePlugin *getMumblePlugin() { + return &l4dplug; +} diff --git a/plugins/link/link.cpp b/plugins/link/link.cpp index 4538f1be2..e8e39684d 100644 --- a/plugins/link/link.cpp +++ b/plugins/link/link.cpp @@ -1,126 +1,126 @@ -#ifndef QT_NO_DEBUG -#include -#include -#endif -#include -#include - -#include "../mumble_plugin.h" - -static wchar_t wcPluginName[256]; - -struct LinkedMem { - UINT32 uiVersion; - DWORD dwcount; - float fPosition[3]; - float fFront[3]; - float fTop[3]; - wchar_t name[256]; -}; - -static void about(HWND h) { - ::MessageBox(h, L"Reads audio position information from linked game", L"Mumble Link Plugin", MB_OK); -} - -static HANDLE hMapObject = NULL; -static LinkedMem *lm = NULL; -static DWORD last_count = 0; -static DWORD last_tick = 0; - -static void unlock() { - lm->dwcount = last_count = 0; - lm->uiVersion = 0; - lm->name[0] = 0; - wcscpy_s(wcPluginName, 256, L"Link"); - return; -} - -static int trylock() { - if (lm->uiVersion == 1) { - if (lm->dwcount != last_count) { - last_count = lm->dwcount; - last_tick = GetTickCount(); - - if (lm->name[0]) { - errno_t err = wcscpy_s(wcPluginName, 256, lm->name); - if (err != 0) { - wcscpy_s(wcPluginName, 256, L"Link"); - return false; - } - } - return true; - } - } - return false; -} - - -static int fetch(float *pos, float *front, float *top) { - if (lm->dwcount != last_count) { - last_count = lm->dwcount; - last_tick = GetTickCount(); - } else if ((GetTickCount() - last_tick) > 5000) - return false; - - if (lm->uiVersion != 1) - return false; - - for (int i=0;i<3;i++) - pos[i]=lm->fPosition[i]; - for (int i=0;i<3;i++) - front[i]=lm->fFront[i]; - for (int i=0;i<3;i++) - top[i]=lm->fTop[i]; - - return true; -} - -BOOL WINAPI DllMain(HINSTANCE, DWORD fdwReason, LPVOID) { - bool bCreated = false; - switch (fdwReason) { - case DLL_PROCESS_ATTACH: - wcscpy_s(wcPluginName, 256, L"Link"); - hMapObject = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, L"MumbleLink"); - if (hMapObject == NULL) { - hMapObject = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(LinkedMem), L"MumbleLink"); - bCreated = true; - if (hMapObject == NULL) - return false; - } - lm = (LinkedMem *) MapViewOfFile(hMapObject, FILE_MAP_ALL_ACCESS, 0, 0, 0); - if (lm == NULL) { - CloseHandle(hMapObject); - hMapObject = NULL; - return false; - } - if (bCreated) - memset(lm, 0, sizeof(LinkedMem)); - break; - case DLL_PROCESS_DETACH: - if (lm) { - UnmapViewOfFile(lm); - lm = NULL; - } - if (hMapObject) { - CloseHandle(hMapObject); - hMapObject = NULL; - } - break; - } - return true; -} - -static MumblePlugin linkplug = { - MUMBLE_PLUGIN_MAGIC, - L"Link v1.0.1", - wcPluginName, - about, - NULL, - trylock, - unlock, - fetch -}; - -extern "C" __declspec(dllexport) MumblePlugin *getMumblePlugin() { - return &linkplug; -} +#ifndef QT_NO_DEBUG +#include +#include +#endif +#include +#include + +#include "../mumble_plugin.h" + +static wchar_t wcPluginName[256]; + +struct LinkedMem { + UINT32 uiVersion; + DWORD dwcount; + float fPosition[3]; + float fFront[3]; + float fTop[3]; + wchar_t name[256]; +}; + +static void about(HWND h) { + ::MessageBox(h, L"Reads audio position information from linked game", L"Mumble Link Plugin", MB_OK); +} + +static HANDLE hMapObject = NULL; +static LinkedMem *lm = NULL; +static DWORD last_count = 0; +static DWORD last_tick = 0; + +static void unlock() { + lm->dwcount = last_count = 0; + lm->uiVersion = 0; + lm->name[0] = 0; + wcscpy_s(wcPluginName, 256, L"Link"); + return; +} + +static int trylock() { + if (lm->uiVersion == 1) { + if (lm->dwcount != last_count) { + last_count = lm->dwcount; + last_tick = GetTickCount(); + + if (lm->name[0]) { + errno_t err = wcscpy_s(wcPluginName, 256, lm->name); + if (err != 0) { + wcscpy_s(wcPluginName, 256, L"Link"); + return false; + } + } + return true; + } + } + return false; +} + + +static int fetch(float *pos, float *front, float *top) { + if (lm->dwcount != last_count) { + last_count = lm->dwcount; + last_tick = GetTickCount(); + } else if ((GetTickCount() - last_tick) > 5000) + return false; + + if (lm->uiVersion != 1) + return false; + + for (int i=0;i<3;i++) + pos[i]=lm->fPosition[i]; + for (int i=0;i<3;i++) + front[i]=lm->fFront[i]; + for (int i=0;i<3;i++) + top[i]=lm->fTop[i]; + + return true; +} + +BOOL WINAPI DllMain(HINSTANCE, DWORD fdwReason, LPVOID) { + bool bCreated = false; + switch (fdwReason) { + case DLL_PROCESS_ATTACH: + wcscpy_s(wcPluginName, 256, L"Link"); + hMapObject = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, L"MumbleLink"); + if (hMapObject == NULL) { + hMapObject = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(LinkedMem), L"MumbleLink"); + bCreated = true; + if (hMapObject == NULL) + return false; + } + lm = (LinkedMem *) MapViewOfFile(hMapObject, FILE_MAP_ALL_ACCESS, 0, 0, 0); + if (lm == NULL) { + CloseHandle(hMapObject); + hMapObject = NULL; + return false; + } + if (bCreated) + memset(lm, 0, sizeof(LinkedMem)); + break; + case DLL_PROCESS_DETACH: + if (lm) { + UnmapViewOfFile(lm); + lm = NULL; + } + if (hMapObject) { + CloseHandle(hMapObject); + hMapObject = NULL; + } + break; + } + return true; +} + +static MumblePlugin linkplug = { + MUMBLE_PLUGIN_MAGIC, + L"Link v1.0.1", + wcPluginName, + about, + NULL, + trylock, + unlock, + fetch +}; + +extern "C" __declspec(dllexport) MumblePlugin *getMumblePlugin() { + return &linkplug; +} diff --git a/plugins/plugins.pro b/plugins/plugins.pro index 5641163b0..1c9f03e546 100644 --- a/plugins/plugins.pro +++ b/plugins/plugins.pro @@ -1,9 +1,9 @@ -TEMPLATE = subdirs - -CONFIG += debug_and_release -SUBDIRS = link -DIST = plugins.pri - -win32 { - SUBDIRS += aoc bf2 cod2 cod4 cod5 css gmod hl2dm insurgency l4d tf2 wolfet wow -} +TEMPLATE = subdirs + +CONFIG += debug_and_release +SUBDIRS = link +DIST = plugins.pri + +win32 { + SUBDIRS += aoc bf2 cod2 cod4 cod5 css gmod hl2dm insurgency l4d tf2 wolfet wow +} diff --git a/plugins/tf2/tf2.cpp b/plugins/tf2/tf2.cpp index 0b2cb84c7..4f0b10c5e 100644 --- a/plugins/tf2/tf2.cpp +++ b/plugins/tf2/tf2.cpp @@ -1,184 +1,184 @@ -#include -#include -#include -#include - -#define _USE_MATH_DEFINES -#include - -#include "../mumble_plugin.h" - -HANDLE h; -BYTE *posptr; -BYTE *rotptr; -BYTE *stateptr; - -static DWORD getProcess(const wchar_t *exename) { - PROCESSENTRY32 pe; - DWORD pid = 0; - - pe.dwSize = sizeof(pe); - HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - if (hSnap != INVALID_HANDLE_VALUE) { - BOOL ok = Process32First(hSnap, &pe); - - while (ok) { - if (wcscmp(pe.szExeFile, exename)==0) { - pid = pe.th32ProcessID; - break; - } - ok = Process32Next(hSnap, &pe); - } - CloseHandle(hSnap); - } - return pid; -} - -static BYTE *getModuleAddr(DWORD pid, const wchar_t *modname) { - MODULEENTRY32 me; - BYTE *addr = NULL; - me.dwSize = sizeof(me); - HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid); - if (hSnap != INVALID_HANDLE_VALUE) { - BOOL ok = Module32First(hSnap, &me); - - while (ok) { - if (wcscmp(me.szModule, modname)==0) { - addr = me.modBaseAddr; - break; - } - ok = Module32Next(hSnap, &me); - } - CloseHandle(hSnap); - } - return addr; -} - - -static bool peekProc(VOID *base, VOID *dest, SIZE_T len) { - SIZE_T r; - BOOL ok=ReadProcessMemory(h, base, dest, len, &r); - return (ok && (r == len)); -} - -static void about(HWND h) { - ::MessageBox(h, L"Reads audio position information from Team Fortress 2 (Build 3755)", L"Mumble TF2 Plugin", MB_OK); -} - -static bool calcout(float *pos, float *rot, float *opos, float *front, float *top) { - float h = rot[0]; - float v = rot[1]; - - if ((v < -360.0f) || (v > 360.0f) || (h < -360.0f) || (h > 360.0f)) - return false; - - h *= static_cast(M_PI / 180.0f); - v *= static_cast(M_PI / 180.0f); - - // Seems TF2 is in inches. INCHES?!? - opos[0] = pos[0] / 39.37f; - opos[1] = pos[2] / 39.37f; - opos[2] = pos[1] / 39.37f; - - front[0] = cos(v) * cos(h); - front[1] = -sin(h); - front[2] = sin(v) * cos(h); - - h -= static_cast(M_PI / 2.0f); - - top[0] = cos(v) * cos(h); - top[1] = -sin(h); - top[2] = sin(v) * cos(h); - - return true; -} - -static int trylock() { - - h = NULL; - posptr = rotptr = NULL; - - DWORD pid=getProcess(L"hl2.exe"); - if (!pid) - return false; - BYTE *mod=getModuleAddr(pid, L"client.dll"); - if (!mod) - return false; - h=OpenProcess(PROCESS_VM_READ, false, pid); - if (!h) - return false; - - // Check if we really have TF2 running - /* - position tuple: client.dll+0x4cd594 (x,y,z, float) - orientation tuple: client.dll+0x52529c (v,h float) - ID string: client.dll+0x47726b = "teamJet@@" (9 characters, text) - spawn state: client.dll+0x466b84 (0 when at main menu, 1 when spectator, 3 when at team selection menu, and 6 or 9 when on a team (depending on the team side and gamemode), byte) - */ - char sMagic[9]; - if(!peekProc(mod + 0x47726b, sMagic, 9) || strncmp("teamJet@@", sMagic, 9)!=0) - return false; - - // Remember addresses for later - posptr = mod + 0x4cd594; - rotptr = mod + 0x52529c; - stateptr = mod + 0x466b84; - - float pos[3]; - float rot[3]; - float opos[3], top[3], front[3]; - - bool ok = peekProc(posptr, pos, 12) && - peekProc(rotptr, rot, 12); - - if (ok) - return calcout(pos, rot, opos, top, front); - // If it failed clean up - CloseHandle(h); - h = NULL; - return false; -} - -static void unlock() { - if (h) { - CloseHandle(h); - h = NULL; - } - return; -} - -static int fetch(float *pos, float *front, float *top) { - for (int i=0;i<3;i++) - pos[i] = front[i] = top[i] = 0; - - float ipos[3], rot[3]; - bool ok; - char state; - - ok = peekProc(posptr, ipos, 12) && - peekProc(rotptr, rot, 12) && - peekProc(stateptr, &state, 1); - if (!ok) - return false; - - // Check to see if you are in a server - if (state == 0 || state == 1 || state == 3) - return true; // Deactivate plugin - - return calcout(ipos, rot, pos, front, top); -} - -static MumblePlugin tf2plug = { - MUMBLE_PLUGIN_MAGIC, - L"Team Fortress 2 (Build 3755)", - L"Team Fortress 2", - about, - NULL, - trylock, - unlock, - fetch -}; - -extern "C" __declspec(dllexport) MumblePlugin *getMumblePlugin() { - return &tf2plug; -} +#include +#include +#include +#include + +#define _USE_MATH_DEFINES +#include + +#include "../mumble_plugin.h" + +HANDLE h; +BYTE *posptr; +BYTE *rotptr; +BYTE *stateptr; + +static DWORD getProcess(const wchar_t *exename) { + PROCESSENTRY32 pe; + DWORD pid = 0; + + pe.dwSize = sizeof(pe); + HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (hSnap != INVALID_HANDLE_VALUE) { + BOOL ok = Process32First(hSnap, &pe); + + while (ok) { + if (wcscmp(pe.szExeFile, exename)==0) { + pid = pe.th32ProcessID; + break; + } + ok = Process32Next(hSnap, &pe); + } + CloseHandle(hSnap); + } + return pid; +} + +static BYTE *getModuleAddr(DWORD pid, const wchar_t *modname) { + MODULEENTRY32 me; + BYTE *addr = NULL; + me.dwSize = sizeof(me); + HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid); + if (hSnap != INVALID_HANDLE_VALUE) { + BOOL ok = Module32First(hSnap, &me); + + while (ok) { + if (wcscmp(me.szModule, modname)==0) { + addr = me.modBaseAddr; + break; + } + ok = Module32Next(hSnap, &me); + } + CloseHandle(hSnap); + } + return addr; +} + + +static bool peekProc(VOID *base, VOID *dest, SIZE_T len) { + SIZE_T r; + BOOL ok=ReadProcessMemory(h, base, dest, len, &r); + return (ok && (r == len)); +} + +static void about(HWND h) { + ::MessageBox(h, L"Reads audio position information from Team Fortress 2 (Build 3755)", L"Mumble TF2 Plugin", MB_OK); +} + +static bool calcout(float *pos, float *rot, float *opos, float *front, float *top) { + float h = rot[0]; + float v = rot[1]; + + if ((v < -360.0f) || (v > 360.0f) || (h < -360.0f) || (h > 360.0f)) + return false; + + h *= static_cast(M_PI / 180.0f); + v *= static_cast(M_PI / 180.0f); + + // Seems TF2 is in inches. INCHES?!? + opos[0] = pos[0] / 39.37f; + opos[1] = pos[2] / 39.37f; + opos[2] = pos[1] / 39.37f; + + front[0] = cos(v) * cos(h); + front[1] = -sin(h); + front[2] = sin(v) * cos(h); + + h -= static_cast(M_PI / 2.0f); + + top[0] = cos(v) * cos(h); + top[1] = -sin(h); + top[2] = sin(v) * cos(h); + + return true; +} + +static int trylock() { + + h = NULL; + posptr = rotptr = NULL; + + DWORD pid=getProcess(L"hl2.exe"); + if (!pid) + return false; + BYTE *mod=getModuleAddr(pid, L"client.dll"); + if (!mod) + return false; + h=OpenProcess(PROCESS_VM_READ, false, pid); + if (!h) + return false; + + // Check if we really have TF2 running + /* + position tuple: client.dll+0x4cd594 (x,y,z, float) + orientation tuple: client.dll+0x52529c (v,h float) + ID string: client.dll+0x47726b = "teamJet@@" (9 characters, text) + spawn state: client.dll+0x466b84 (0 when at main menu, 1 when spectator, 3 when at team selection menu, and 6 or 9 when on a team (depending on the team side and gamemode), byte) + */ + char sMagic[9]; + if (!peekProc(mod + 0x47726b, sMagic, 9) || strncmp("teamJet@@", sMagic, 9)!=0) + return false; + + // Remember addresses for later + posptr = mod + 0x4cd594; + rotptr = mod + 0x52529c; + stateptr = mod + 0x466b84; + + float pos[3]; + float rot[3]; + float opos[3], top[3], front[3]; + + bool ok = peekProc(posptr, pos, 12) && + peekProc(rotptr, rot, 12); + + if (ok) + return calcout(pos, rot, opos, top, front); + // If it failed clean up + CloseHandle(h); + h = NULL; + return false; +} + +static void unlock() { + if (h) { + CloseHandle(h); + h = NULL; + } + return; +} + +static int fetch(float *pos, float *front, float *top) { + for (int i=0;i<3;i++) + pos[i] = front[i] = top[i] = 0; + + float ipos[3], rot[3]; + bool ok; + char state; + + ok = peekProc(posptr, ipos, 12) && + peekProc(rotptr, rot, 12) && + peekProc(stateptr, &state, 1); + if (!ok) + return false; + + // Check to see if you are in a server + if (state == 0 || state == 1 || state == 3) + return true; // Deactivate plugin + + return calcout(ipos, rot, pos, front, top); +} + +static MumblePlugin tf2plug = { + MUMBLE_PLUGIN_MAGIC, + L"Team Fortress 2 (Build 3755)", + L"Team Fortress 2", + about, + NULL, + trylock, + unlock, + fetch +}; + +extern "C" __declspec(dllexport) MumblePlugin *getMumblePlugin() { + return &tf2plug; +} diff --git a/scripts/murmur.init b/scripts/murmur.init index 3dd75a52a..5c15a3c3a 100755 --- a/scripts/murmur.init +++ b/scripts/murmur.init @@ -26,6 +26,8 @@ INIFILE=/etc/mumble-server.ini DAEMON_OPTS="-ini $INIFILE" MURMUR_DAEMON_START=0 MURMUR_USE_CAPABILITIES=0 +MURMUR_LIMIT_NOFILE=0 +MURMUR_LIMIT_RTPRIO=0 # Include murmur defaults if available if [ -f /etc/default/$NAME ] ; then @@ -35,6 +37,13 @@ fi . /lib/init/vars.sh . /lib/lsb/init-functions +if [ "$MURMUR_LIMIT_NOFILE" -gt 0 ] ; then + ulimit -n $MURMUR_LIMIT_NOFILE +fi +if [ "$MURMUR_LIMIT_RTPRIO" -gt 0 ]; then + ulimit -r 1 +fi + case "$1" in start) if [ "$MURMUR_DAEMON_START" != "1" ] ; then diff --git a/scripts/release.pl b/scripts/release.pl index 020c10591..6e8486aa9 100644 --- a/scripts/release.pl +++ b/scripts/release.pl @@ -31,41 +31,58 @@ my %filevars = ( 'sources' => 1, 'headers' => 1, 'rc_file' => 1, 'dist' => 1, 'f system("rm mumble-*"); -my @pro = ("main.pro", "src/mumble.pri"); -adddir(".", \@pro); +my @pro = ("main.pro"); +#, "src/mumble.pri"); +#adddir(".", \@pro); -foreach my $pro (@pro) { +while (my $pro = shift @pro) { open(F, $pro) or croak "Failed to open $pro"; print "Processing $pro\n"; $files{$pro}=1; my $basedir=$pro; $basedir =~ s/[^\/]+\Z//g; + my @vpath = ($basedir); while() { chomp(); - if (/^\s*(\w+)\s*?[\+\-\*]{0,1}=\s*(.+)$/) { + if (/^include\((.+)\)/) { + my $f = $basedir . $1; + $f =~ s/(\/|\A)[^\/]+\/\.\.\//$1/g; + $f =~ s/(\/|\A)[^\/]+\/\.\.\//$1/g; + $f =~ s/(\/|\A)[^\/]+\/\.\.\//$1/g; + push @pro, $f; + } elsif (/^\s*(\w+)\s*?[\+\-\*]{0,1}=\s*(.+)$/) { my ($var,$value)=(lc $1,$2); switch ($var) { case "version" { croak "Versions don't match" if (defined($ver) && ($ver ne $value)); $ver=$value; } + case "vpath" { + push @vpath,map { "$basedir$_/"} split(/\s/, $value); + } + case "subdirs" { + push @pro,map { my ($b,$p) = ($_,$_); $p =~ s/^.+\///g; "$basedir$b/$p.pro" } split(/\s/, $value); + } case %filevars { foreach my $f (split(/\s+/,$value)) { - $f =~ s/^.+\///g; - if (-f "${basedir}$f") { - $files{$basedir.$f}=1; - } else { my $ok = 0; - foreach my $d ("", "speexbuild/", "speexbuild/speex/", "src/", "src/mumble/", "src/murmur/", "icons/", "scripts/", "plugins/", "overlay/", "overlay_gl/", "speex/libspeex/", "g15helper/") { + foreach my $d (@vpath) { if (-f "$d$f") { - $files{$d.$f}=1; + $f = $d.$f; $ok = 1; + last; } } if (! $ok) { - croak "Failed to find $f"; + croak "Failed to find $f in ".join(" ",@vpath); + } else { + if ($f =~ /\.\./) { + $f =~ s/(\/|\A)[^\/]+\/\.\.\//$1/g; + $f =~ s/(\/|\A)[^\/]+\/\.\.\//$1/g; + $f =~ s/(\/|\A)[^\/]+\/\.\.\//$1/g; + } + $files{$f}=1; } - } } } } @@ -80,7 +97,11 @@ while() { if (/\(.+)<\/file\>/) { $files{$1}=1; } elsif (/\/) { - $files{"icons/$1"}=1; + if ( -f "icons/$1") { + $files{"icons/$1"}=1; + } else { + $files{"samples/$1"}=1; + } } } close(F); diff --git a/scripts/scripts.pro b/scripts/scripts.pro new file mode 100644 index 000000000..bfd1ede85 --- /dev/null +++ b/scripts/scripts.pro @@ -0,0 +1,2 @@ +DIST *= murmur.pl murmur.ini murmur.ini.system murmur.init murmur.conf murmur.logrotate dbusauth.pl weblist.pl weblist.php icedemo.php murmur-user-wrapper +DIST *= mumble-overlay mumble.desktop mumble.protocol 11-input-mumble-policy.fdi diff --git a/src/OSInfo.h b/src/OSInfo.h index f7c1aabf2..900b21a4d 100644 --- a/src/OSInfo.h +++ b/src/OSInfo.h @@ -1,44 +1,44 @@ -/* Copyright (C) 2005-2009, Thorvald Natvig - - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - - Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - Neither the name of the Mumble Developers nor the names of its - contributors may be used to endorse or promote products derived from this - software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef _OSINFO_H -#define _OSINFO_H - -#include "murmur_pch.h" - -class OSInfo { - public: - static QString getMacHash(const QHostAddress & = QHostAddress(QHostAddress::Any)); - static QString getOS(); - static QString getOSVersion(); - static void fillXml(QDomDocument &doc, QDomElement &root, const QString &os = OSInfo::getOS(), const QString &osver = OSInfo::getOSVersion(), const QHostAddress & = QHostAddress(QHostAddress::Any)); -}; - -#endif +/* Copyright (C) 2005-2009, Thorvald Natvig + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + - Neither the name of the Mumble Developers nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _OSINFO_H +#define _OSINFO_H + +#include "murmur_pch.h" + +class OSInfo { + public: + static QString getMacHash(const QHostAddress & = QHostAddress(QHostAddress::Any)); + static QString getOS(); + static QString getOSVersion(); + static void fillXml(QDomDocument &doc, QDomElement &root, const QString &os = OSInfo::getOS(), const QString &osver = OSInfo::getOSVersion(), const QHostAddress & = QHostAddress(QHostAddress::Any)); +}; + +#endif diff --git a/src/Timer.cpp b/src/Timer.cpp index 6aa3d0363..32265cdc8 100644 --- a/src/Timer.cpp +++ b/src/Timer.cpp @@ -1,88 +1,88 @@ -/* Copyright (C) 2005-2009, Thorvald Natvig - - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - - Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - Neither the name of the Mumble Developers nor the names of its - contributors may be used to endorse or promote products derived from this - software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include -#include "Timer.h" - -Timer::Timer() { - uiStart = now(); -} - -quint64 Timer::elapsed() const { - return now() - uiStart; -} - -bool Timer::isElapsed(quint64 us) { - if (elapsed() > us) { - uiStart += us; - return true; - } - return false; -} - -quint64 Timer::restart() { - quint64 n = now(); - quint64 e = n - uiStart; - uiStart = n; - return e; -} - -#if defined(Q_OS_WIN) -quint64 Timer::now() { - static quint64 f = 0LL; - - if (f == 0LL) { - LARGE_INTEGER freq; - QueryPerformanceFrequency(&freq); - f = freq.QuadPart; - } - - LARGE_INTEGER li; - QueryPerformanceCounter(&li); - quint64 e = li.QuadPart; - - return (e * 1000000LL) / f; -} -#elif defined(Q_OS_UNIX) -#include -quint64 Timer::now() { - struct timeval tv; - gettimeofday(&tv, NULL); - quint64 e= tv.tv_sec * 1000000LL; - e += tv.tv_usec; - return e; -} -#else -quint64 Timer::now() { - static QTime ticker; - quint64 elapsed = ticker.elapsed(); - return elapsed * 1000LL; -} -#endif +/* Copyright (C) 2005-2009, Thorvald Natvig + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + - Neither the name of the Mumble Developers nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include "Timer.h" + +Timer::Timer() { + uiStart = now(); +} + +quint64 Timer::elapsed() const { + return now() - uiStart; +} + +bool Timer::isElapsed(quint64 us) { + if (elapsed() > us) { + uiStart += us; + return true; + } + return false; +} + +quint64 Timer::restart() { + quint64 n = now(); + quint64 e = n - uiStart; + uiStart = n; + return e; +} + +#if defined(Q_OS_WIN) +quint64 Timer::now() { + static quint64 f = 0LL; + + if (f == 0LL) { + LARGE_INTEGER freq; + QueryPerformanceFrequency(&freq); + f = freq.QuadPart; + } + + LARGE_INTEGER li; + QueryPerformanceCounter(&li); + quint64 e = li.QuadPart; + + return (e * 1000000LL) / f; +} +#elif defined(Q_OS_UNIX) +#include +quint64 Timer::now() { + struct timeval tv; + gettimeofday(&tv, NULL); + quint64 e= tv.tv_sec * 1000000LL; + e += tv.tv_usec; + return e; +} +#else +quint64 Timer::now() { + static QTime ticker; + quint64 elapsed = ticker.elapsed(); + return elapsed * 1000LL; +} +#endif diff --git a/src/Timer.h b/src/Timer.h index bf81fae45..8ea12528a 100644 --- a/src/Timer.h +++ b/src/Timer.h @@ -1,47 +1,47 @@ -/* Copyright (C) 2005-2009, Thorvald Natvig - - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - - Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - Neither the name of the Mumble Developers nor the names of its - contributors may be used to endorse or promote products derived from this - software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef _TIMER_H -#define _TIMER_H - -// All timer resolutions are in microseconds. - -struct Timer { -protected: - quint64 uiStart; - static quint64 now(); -public: - Timer(); - bool isElapsed(quint64 us); - quint64 elapsed() const; - quint64 restart(); -}; - -#endif +/* Copyright (C) 2005-2009, Thorvald Natvig + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + - Neither the name of the Mumble Developers nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _TIMER_H +#define _TIMER_H + +// All timer resolutions are in microseconds. + +struct Timer { +protected: + quint64 uiStart; + static quint64 now(); +public: + Timer(); + bool isElapsed(quint64 us); + quint64 elapsed() const; + quint64 restart(); +}; + +#endif diff --git a/src/mumble.pri b/src/mumble.pri index 7115e1b53..02b36782b 100644 --- a/src/mumble.pri +++ b/src/mumble.pri @@ -1,7 +1,7 @@ include(../compiler.pri) VERSION = 1.1.8 -DIST = mumble.pro murmur.pro mumble.pri mumble.ico Message.h mumble_plugin.h PacketDataStream.h CryptState.h Timer.h Version.h OSInfo.h +DIST = mumble.pri Message.h PacketDataStream.h CryptState.h Timer.h Version.h OSInfo.h CONFIG += qt thread debug_and_release warn_on DEFINES *= MUMBLE_VERSION_STRING=$$VERSION diff --git a/src/mumble/AudioWizard.cpp b/src/mumble/AudioWizard.cpp index 3fd1023f8..2ed3dff74 100644 --- a/src/mumble/AudioWizard.cpp +++ b/src/mumble/AudioWizard.cpp @@ -1,897 +1,897 @@ -/* Copyright (C) 2005-2009, Thorvald Natvig - - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - - Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - Neither the name of the Mumble Developers nor the names of its - contributors may be used to endorse or promote products derived from this - software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include "AudioWizard.h" -#include "AudioInput.h" -#include "Global.h" -#include "Settings.h" -#include "MainWindow.h" - -CompletablePage::CompletablePage(QWizard *p) : QWizardPage(p) { - bComplete = true; -} - -void CompletablePage::setComplete(bool b) { - bComplete = b; - emit completeChanged(); -} - -bool CompletablePage::isComplete() const { - return bComplete; -} - -AudioWizard::AudioWizard(QWidget *p) : QWizard(p) { - bInit = true; - - setOption(QWizard::NoCancelButton, false); -#ifndef Q_OS_MAC - resize(700, 500); -#endif - - addPage(qwpIntro = introPage()); - addPage(qwpDevice = devicePage()); - addPage(qwpDeviceTuning = deviceTuningPage()); - addPage(qwpVolume = volumePage()); - addPage(qwpTrigger = triggerPage()); - addPage(qwpPositional = positionalPage()); - addPage(qwpDone = donePage()); - setWindowTitle(tr("Audio Tuning Wizard")); - - updateTriggerWidgets(qrPTT->isChecked()); - - sOldSettings = g.s; - - g.s.lmLoopMode = Settings::Local; - g.s.dPacketLoss = 0.0; - g.s.dMaxPacketDelay = 0.0; - g.s.bMute = true; - g.s.bDeaf = false; - - g.s.atTransmit = Settings::Continous; - bTransmitChanged = false; - - iMaxPeak = 0; - iTicks = 0; - - g.bEchoTest = true; - - ticker = new QTimer(this); - ticker->setObjectName(QLatin1String("Ticker")); - - setObjectName(QLatin1String("Wizard")); - connect(this, SIGNAL(currentIdChanged(int)), this, SLOT(showPage(int))); - - QMetaObject::connectSlotsByName(this); - bInit = false; - - ticker->setSingleShot(false); - ticker->start(20); -} - -bool AudioWizard::eventFilter(QObject *obj, QEvent *evt) { - if ((evt->type() == QEvent::MouseButtonPress) || - (evt->type() == QEvent::MouseMove)) { - QMouseEvent *qme = dynamic_cast(evt); - if (qme) { - if (qme->buttons() & Qt::LeftButton) { - QPointF qpf = qgvView->mapToScene(qme->pos()); - fX = static_cast(qpf.x()); - fY = static_cast(qpf.y()); - } - } - } - return QWizard::eventFilter(obj, evt); -} - -CompletablePage *AudioWizard::introPage() { - CompletablePage *qwpage = new CompletablePage(this); - qwpage->setTitle(tr("Introduction")); - qwpage->setSubTitle(tr("Welcome to the Mumble Audio Wizard")); - QVBoxLayout *v=new QVBoxLayout(qwpage); - QLabel *label=new QLabel(tr("This is the audio tuning wizard for Mumble. This will help you correctly set the input levels of your sound card, and also set the correct " - "parameters for sound processing in Mumble."), qwpage); - label->setWordWrap(true); - v->addWidget(label); - - label=new QLabel(tr("Please be aware that as long as this wizard is active, audio will be looped locally to allow you to listen to it, and no audio will be sent to the " - "server."), qwpage); - label->setWordWrap(true); - v->addWidget(label); - - return qwpage; -} - -CompletablePage *AudioWizard::donePage() { - CompletablePage *qwpage = new CompletablePage(this); - qwpage->setTitle(tr("Finished")); - qwpage->setSubTitle(tr("Enjoy using Mumble")); - QVBoxLayout *v=new QVBoxLayout(qwpage); - - QLabel *label=new QLabel(tr("Congratulations. You should now be ready to enjoy a richer sound experience with Mumble."), qwpage); - label->setWordWrap(true); - v->addWidget(label); - - label=new QLabel(tr("Mumble is under continuous development, and the development team wants to focus on the features " - "that benefit the most users. To this end, Mumble supports submitting anonymous statistics " - "about your configuration to the developers. These statistcs are essential for future development, and also " - "make sure the features you use aren't deprecated.")); - label->setWordWrap(true); - v->addWidget(label); - - qcbUsage = new QCheckBox(tr("Submit anonymous statistics to the Mumble project")); - qcbUsage->setChecked(g.s.bUsage); - v->addWidget(qcbUsage); - - return qwpage; -} - -CompletablePage *AudioWizard::devicePage() { - CompletablePage *qwpage = new CompletablePage(this); - QLabel *l; - QGridLayout *grid; - - qwpage->setTitle(tr("Device selection")); - qwpage->setSubTitle(tr("Selecting the input and output device to use with Mumble.")); - - QGroupBox *qgbInput = new QGroupBox(tr("Input device"), qwpage); - grid = new QGridLayout(qgbInput); - - l = new QLabel(tr("This is the device your microphone is connected to."), qgbInput); - l->setWordWrap(true); - grid->addWidget(l, 0, 0, 1, 2); - - qcbInput = new QComboBox(qgbInput); - if (AudioInputRegistrar::qmNew) { - foreach(AudioInputRegistrar *air, *AudioInputRegistrar::qmNew) { - qcbInput->addItem(air->name); - if (air->name == AudioInputRegistrar::current) - qcbInput->setCurrentIndex(qcbInput->count() - 1); - QList ql= air->getDeviceChoices(); - } - } - - l = new QLabel(tr("System"), qgbInput); - l->setBuddy(qcbInput); - - qcbInput->setToolTip(tr("Input method for audio")); - qcbInput->setWhatsThis(tr("This is the input method to use for audio.
Most likely you want to use DirectSound.")); - qcbInput->setObjectName(QLatin1String("Input")); - - if (qcbInput->count() < 2) { - qcbInput->setEnabled(false); - } - - grid->addWidget(l, 1, 0); - grid->addWidget(qcbInput, 1, 1); - - - qcbInputDevice = new QComboBox(qgbInput); - l = new QLabel(tr("Device"), qgbInput); - l->setBuddy(qcbInputDevice); - - qcbInputDevice->setToolTip(tr("Input device to use")); - qcbInputDevice->setWhatsThis(tr("Selects which sound card to use for audio input.")); - qcbInputDevice->setObjectName(QLatin1String("InputDevice")); - - grid->addWidget(l, 2, 0); - grid->addWidget(qcbInputDevice, 2, 1); - - qcbEcho = new QCheckBox(tr("Use echo cancellation"), qgbInput); - qcbEcho->setToolTip(tr("Cancel echo from headset or speakers.")); - qcbEcho->setWhatsThis(tr("This enables echo cancellation of outgoing audio, which helps both on speakers and on headsets.")); - qcbEcho->setObjectName(QLatin1String("Echo")); - qcbEcho->setChecked(g.s.bEcho); - grid->addWidget(qcbEcho, 3, 1); - - QGroupBox *qgbOutput = new QGroupBox(tr("Output device"), qwpage); - grid = new QGridLayout(qgbOutput); - - l = new QLabel(tr("This is the device your speakers or headphones are connected to."), qgbOutput); - l->setWordWrap(true); - grid->addWidget(l, 0, 0, 1, 2); - - qcbOutput = new QComboBox(qgbOutput); - if (AudioOutputRegistrar::qmNew) { - foreach(AudioOutputRegistrar *aor, *AudioOutputRegistrar::qmNew) { - qcbOutput->addItem(aor->name); - if (aor->name == AudioOutputRegistrar::current) { - qcbOutput->setCurrentIndex(qcbOutput->count() - 1); - bDelay = aor->usesOutputDelay(); - } - QList ql= aor->getDeviceChoices(); - } - } - - l = new QLabel(tr("System"), qgbOutput); - l->setBuddy(qcbOutput); - - qcbOutput->setToolTip(tr("Output method for audio")); - qcbOutput->setWhatsThis(tr("This is the Output method to use for audio.
Most likely you want to use DirectSound.")); - qcbOutput->setObjectName(QLatin1String("Output")); - - if (qcbOutput->count() < 2) { - qcbOutput->setEnabled(false); - } - - grid->addWidget(l, 1, 0); - grid->addWidget(qcbOutput, 1, 1); - - - qcbOutputDevice = new QComboBox(qgbOutput); - l = new QLabel(tr("Device"), qgbOutput); - l->setBuddy(qcbOutputDevice); - - qcbOutputDevice->setToolTip(tr("Output device to use")); - qcbOutputDevice->setWhatsThis(tr("Selects which sound card to use for audio Output.")); - qcbOutputDevice->setObjectName(QLatin1String("OutputDevice")); - - grid->addWidget(l, 2, 0); - grid->addWidget(qcbOutputDevice, 2, 1); - - qcbPositional = new QCheckBox(tr("Enable positional audio"), qgbOutput); - qcbPositional->setToolTip(tr("Allows positioning of sound.")); - qcbPositional->setWhatsThis(tr("This allows Mumble to use positional audio to place voices.")); - qcbPositional->setObjectName(QLatin1String("Positional")); - qcbPositional->setChecked(g.s.bPositionalAudio); - grid->addWidget(qcbPositional, 3, 1); - - QVBoxLayout *v=new QVBoxLayout(qwpage); - v->addWidget(qgbInput); - v->addWidget(qgbOutput); - - on_Input_activated(qcbInput->currentIndex()); - on_Output_activated(qcbOutput->currentIndex()); - - return qwpage; -} - -CompletablePage *AudioWizard::positionalPage() { - CompletablePage *qwpage = new CompletablePage(this); - qwpage->setTitle(tr("Positional Audio")); - qwpage->setSubTitle(tr("Adjusting attenuation of positional audio.")); - - QVBoxLayout *v = new QVBoxLayout(qwpage); - QLabel *l; - - l = new QLabel(tr("Mumble supports positional audio for some games, and will position the voice of other players relative to their position in game. Depending on their position, the " - "volume of the voice will be changed between the speakers to simulate the direction and distance the other player is at. Such positioning depends on your speaker " - "configuration being correct in your operating system, so a test is done here."), qwpage); - l->setWordWrap(true); - v->addWidget(l); - l = new QLabel(tr("The graph below shows the position of you, the speakers and a moving sound source as if seen from above. You should hear the audio move between the channels."), qwpage); - l->setWordWrap(true); - v->addWidget(l); - - qcbHeadphone = new QCheckBox(tr("Use headphones"), qwpage); - qcbHeadphone->setToolTip(tr("Use headphones instead of speakers.")); - qcbHeadphone->setWhatsThis(tr("This ignores the OS speaker configuration and configures the positioning for headphones instead.")); - qcbHeadphone->setObjectName(QLatin1String("Headphone")); - qcbHeadphone->setChecked(g.s.bPositionalHeadphone); - v->addWidget(qcbHeadphone); - - - fAngle = 0.0f; - fX = fY = 0.0f; - qgsScene = NULL; - qgiSource = NULL; - aosSource = NULL; - qgvView = new QGraphicsView(qwpage); - qgvView->scale(1.0f, -1.0f); - qgvView->viewport()->installEventFilter(this); - - v->addWidget(qgvView); - - return qwpage; -} - -CompletablePage *AudioWizard::volumePage() { - CompletablePage *qwpage = new CompletablePage(this); - qwpage->setTitle(tr("Volume tuning")); - qwpage->setSubTitle(tr("Tuning microphone hardware volume to optimal settings.")); - - QVBoxLayout *v = new QVBoxLayout(qwpage); - QLabel *l; - - l = new QLabel(tr("Open your sound control panel and go to the recording settings. " - "Make sure the microphone is selected as active input with maximum recording volume. If there's an option to enable a \"Microphone boost\" make sure it's checked."), qwpage); - l->setWordWrap(true); - v->addWidget(l); - - l = new QLabel(tr("Speak loudly, as when you are annoyed or excited. " - "Decrease the volume in the sound control panel until the bar below stays as high as possible in the blue and green " - "but not the red " - "zone while you speak."), qwpage); - l->setWordWrap(true); - v->addWidget(l); - - abAmplify = new AudioBar(qwpage); - abAmplify->qcBelow = Qt::blue; - v->addWidget(abAmplify); - - l = new QLabel(tr("Now talk softly, as you would when talking late at night and you don't want to disturb anyone. " - "Adjust the slider below so that the bar moves into green when you talk, but stays blue while you're silent."), qwpage); - l->setWordWrap(true); - v->addWidget(l); - - qsMaxAmp = new QSlider(Qt::Horizontal, qwpage); - qsMaxAmp->setRange(1, 32767); - qsMaxAmp->setSingleStep(100); - qsMaxAmp->setPageStep(1000); - qsMaxAmp->setValue(g.s.iMinLoudness); - qsMaxAmp->setObjectName(QLatin1String("MaxAmp")); - - v->addWidget(qsMaxAmp); - - return qwpage; -} - -CompletablePage *AudioWizard::triggerPage() { - CompletablePage *qwpage = new CompletablePage(this); - qwpage->setTitle(tr("Voice Activity Detection")); - qwpage->setSubTitle(tr("Letting Mumble figure out when you're talking and when you're silent.")); - - QHBoxLayout *h; - QVBoxLayout *v; - - v= new QVBoxLayout(qwpage); - QLabel *l; - - l = new QLabel(tr("This will help Mumble figure out when you are talking. The first step is selecting which data value to use."), qwpage); - l->setWordWrap(true); - v->addWidget(l); - - h = new QHBoxLayout; - - qrPTT = new QRadioButton(tr("Push To Talk:"), qwpage); - qrPTT->setObjectName(QLatin1String("PTT")); - h->addWidget(qrPTT); - - skwPTT = new ShortcutKeyWidget(qwpage); - skwPTT->setObjectName(QLatin1String("PTTKey")); - foreach(const Shortcut &s, g.s.qlShortcuts) { - if (s.iIndex == g.mw->gsPushTalk->idx) { - skwPTT->setShortcut(s.qlButtons); - break; - } - } - h->addWidget(skwPTT); - - v->addLayout(h); - - qrSNR = new QRadioButton(tr("Signal-To-Noise ratio"), qwpage); - qrSNR->setObjectName(QLatin1String("SNR")); - v->addWidget(qrSNR); - - qrAmplitude = new QRadioButton(tr("Raw amplitude from input"), qwpage); - qrAmplitude->setObjectName(QLatin1String("Amplitude")); - v->addWidget(qrAmplitude); - - if (g.s.atTransmit == Settings::PushToTalk) - qrPTT->setChecked(true); - else if (g.s.vsVAD == Settings::Amplitude) - qrAmplitude->setChecked(true); - else - qrSNR->setChecked(true); - - qwVAD = new QWidget(qwpage); - v->addWidget(qwVAD); - - v = new QVBoxLayout(qwVAD); - - abVAD = new AudioBar(qwpage); - abVAD->qcBelow = Qt::red; - abVAD->qcInside = Qt::yellow; - abVAD->qcAbove = Qt::green; - - v->addWidget(abVAD); - - l = new QLabel(tr("Next you need to adjust the following two sliders. The first few utterances you say should end up in the " - "green area (definitive speech). While talking, you should stay inside the yellow (might be speech) and when you're " - "not talking, everything should be in the red (definitively not speech)."), qwpage); - l->setWordWrap(true); - v->addWidget(l); - - qsMinVAD = new QSlider(Qt::Horizontal, qwpage); - qsMinVAD->setRange(1, 32767); - qsMinVAD->setSingleStep(100); - qsMinVAD->setPageStep(1000); - qsMinVAD->setValue(static_cast(g.s.fVADmin * 32767.0)); - qsMinVAD->setObjectName(QLatin1String("VADmin")); - - v->addWidget(qsMinVAD); - - qsMaxVAD = new QSlider(Qt::Horizontal, qwpage); - qsMaxVAD->setRange(1, 32767); - qsMaxVAD->setSingleStep(100); - qsMaxVAD->setPageStep(1000); - qsMaxVAD->setValue(static_cast(g.s.fVADmax * 32767)); - qsMaxVAD->setObjectName(QLatin1String("VADmax")); - - v->addWidget(qsMaxVAD); - - l = new QLabel(tr("Last, you need to adjust the grace time. Mumble may detect this much non-speech and still keep transmitting before " - "it breaks off. This allows you to catch your breath while speaking."), qwpage); - l->setWordWrap(true); - v->addWidget(l); - - h = new QHBoxLayout; - - qsHoldtime = new QSlider(Qt::Horizontal, qwpage); - qsHoldtime->setRange(20, 250); - qsHoldtime->setSingleStep(5); - qsHoldtime->setPageStep(25); - qsHoldtime->setValue(g.s.iVoiceHold); - qsHoldtime->setObjectName(QLatin1String("Holdtime")); - - qlHoldtime = new QLabel(qwpage); - qlHoldtime->setMinimumWidth(40); - on_Holdtime_valueChanged(qsHoldtime->value()); - - h->addWidget(qsHoldtime); - h->addWidget(qlHoldtime); - - v->addLayout(h); - - return qwpage; -} - -CompletablePage *AudioWizard::deviceTuningPage() { - CompletablePage *qwpage = new CompletablePage(this); - qwpage->setTitle(tr("Device tuning")); - qwpage->setSubTitle(tr("Changing hardware output delays to their minimum value.")); - - QGridLayout *grid = new QGridLayout(qwpage); - - QLabel *l = new QLabel(tr("To keep latency to an absolute minium, it's important to buffer as little audio as possible on the soundcard. " - "However, many soundcards report that they require a much smaller buffer than what they can actually work with, " - "so the only way to set this value is to try and fail."), qwpage); - l->setWordWrap(true); - grid->addWidget(l, 0, 0, 1, 2); - - l = new QLabel(tr("You should hear a single tone that's changing in frequency. Change the slider below to the lowest value which gives no interruptions or jitter " - "in the sound. Please note that local echo is disabled during this test to " - "improve audio path recognition."), qwpage); - l->setWordWrap(true); - grid->addWidget(l, 1, 0, 1, 2); - - qsOutputDelay = new QSlider(Qt::Horizontal, qwpage); - qsOutputDelay->setRange(1, 6); - - qsOutputDelay->setSingleStep(1); - qsOutputDelay->setPageStep(2); - qsOutputDelay->setValue(g.s.iOutputDelay); - qsOutputDelay->setObjectName(QLatin1String("OutputDelay")); - - qlOutputDelay=new QLabel(qwpage); - qlOutputDelay->setMinimumWidth(30); - on_OutputDelay_valueChanged(qsOutputDelay->value()); - qsOutputDelay->setToolTip(tr("Amount of data to buffer.")); - qsOutputDelay->setWhatsThis(tr("This sets the amount of data to prebuffer in the output buffer. " - "Experiment with different values and set it to the lowest which doesn't " - "cause rapid jitter in the sound.")); - grid->addWidget(qsOutputDelay, 2, 1); - grid->addWidget(qlOutputDelay, 2, 2); - - l = new QLabel(tr("If you position the microphone so it can pick up the speakers or headset, Mumble will measure the total audio path " - "delay in your system; this means the delay from a sample is placed in an outbound buffer until it's found in " - "a matching incoming buffer."), qwpage); - l->setWordWrap(true); - grid->addWidget(l, 3, 0, 1, 2); - - qlAudioPath = new QLabel(qwpage); - grid->addWidget(qlAudioPath, 4, 0, 1, 2); - - return qwpage; -} - -void AudioWizard::on_Input_activated(int) { - qcbInputDevice->clear(); - - if (! AudioInputRegistrar::qmNew) - return; - - AudioInputRegistrar *air = AudioInputRegistrar::qmNew->value(qcbInput->currentText()); - QList ql = air->getDeviceChoices(); - - foreach(audioDevice d, ql) { - qcbInputDevice->addItem(d.first, d.second); - } - - qcbInputDevice->setEnabled(ql.count() > 1); - - on_InputDevice_activated(0); -} - -void AudioWizard::on_InputDevice_activated(int) { - if (bInit) - return; - - if (! AudioInputRegistrar::qmNew) - return; - - boost::weak_ptr wai(g.ai); - g.ai.reset(); - - while (! wai.expired()) { - } - - AudioInputRegistrar *air = AudioInputRegistrar::qmNew->value(qcbInput->currentText()); - int idx = qcbInputDevice->currentIndex(); - if (idx > -1) { - air->setDeviceChoice(qcbInputDevice->itemData(idx), g.s); - } - - qcbEcho->setEnabled(air->canEcho(qcbOutput->currentText())); - - g.ai = AudioInputPtr(air->create()); - g.ai->start(QThread::HighestPriority); -} - -void AudioWizard::on_Output_activated(int) { - qcbOutputDevice->clear(); - - if (! AudioOutputRegistrar::qmNew) - return; - - AudioOutputRegistrar *aor = AudioOutputRegistrar::qmNew->value(qcbOutput->currentText()); - QList ql = aor->getDeviceChoices(); - - foreach(audioDevice d, ql) { - qcbOutputDevice->addItem(d.first, d.second); - } - - qcbOutputDevice->setEnabled(ql.count() > 1); - - on_OutputDevice_activated(0); -} - -void AudioWizard::on_OutputDevice_activated(int) { - if (bInit) - return; - - if (! AudioOutputRegistrar::qmNew) - return; - - boost::weak_ptr wai(g.ao); - g.ao.reset(); - - while (! wai.expired()) { - } - - AudioOutputRegistrar *aor = AudioOutputRegistrar::qmNew->value(qcbOutput->currentText()); - int idx = qcbOutputDevice->currentIndex(); - if (idx > -1) { - aor->setDeviceChoice(qcbOutputDevice->itemData(idx), g.s); - bDelay = aor->usesOutputDelay(); - } - - AudioInputRegistrar *air = AudioInputRegistrar::qmNew->value(qcbInput->currentText()); - qcbEcho->setEnabled(air->canEcho(qcbOutput->currentText())); - - g.ao = AudioOutputPtr(aor->create()); - g.ao->start(QThread::HighPriority); -} - -void AudioWizard::on_OutputDelay_valueChanged(int v) { - qlOutputDelay->setText(tr("%1ms").arg(v*20)); - g.s.iOutputDelay = v; - on_OutputDevice_activated(0); - if (! bInit) { - AudioOutputPtr ao = g.ao; - ao->playSine(0.0f, 0.0f, 0xfffffff, 0.5f); - } -} - -void AudioWizard::on_MaxAmp_valueChanged(int v) { - g.s.iMinLoudness = qMin(v, 30000); -} - -void AudioWizard::showPage(int) { - CompletablePage *cp = qobject_cast(currentPage()); - - AudioOutputPtr ao = g.ao; - if (ao) - ao->wipe(); - aosSource = NULL; - - g.bPosTest = false; - - if (cp == qwpIntro) { - g.s.bMute = true; - } else if (cp == qwpDone) { - g.s.bMute = true; - } else if (cp == qwpDeviceTuning) { - g.s.bMute = true; - playChord(); - } else if (cp == qwpPositional) { - fX = fY = 0.0f; - g.s.bMute = true; - g.bPosTest = true; - if (qgsScene) { - delete qgsScene; - qgiSource = NULL; - qgsScene = NULL; - } - playChord(); - } else { - g.s.bMute = false; - } -} - -int AudioWizard::nextId() const { - AudioOutputPtr ao = g.ao; - - int nextid = QWizard::nextId(); - if (currentPage() == qwpTrigger && ! g.s.bPositionalAudio) - nextid++; - else if ((currentPage() == qwpDevice) && ! bDelay) - nextid++; - return nextid; -} - -void AudioWizard::playChord() { - AudioOutputPtr ao = g.ao; - if (! ao || aosSource) - return; - aosSource = ao->playSample(QLatin1String("skin:wb_male.spx"), true); -} - -void AudioWizard::restartAudio() { - boost::weak_ptr wai(g.ai); - boost::weak_ptr wao(g.ao); - - aosSource = NULL; - - g.ai.reset(); - g.ao.reset(); - - while (! wai.expired() || ! wao.expired()) { - // Where is QThread::yield() ? - } - - g.s.qsAudioInput = qcbInput->currentText(); - g.s.qsAudioOutput = qcbOutput->currentText(); - - g.ai = AudioInputRegistrar::newFromChoice(g.s.qsAudioInput); - if (g.ai) - g.ai->start(QThread::HighestPriority); - g.ao = AudioOutputRegistrar::newFromChoice(g.s.qsAudioOutput); - if (g.ao) - g.ao->start(QThread::HighPriority); - - if (qgsScene) { - delete qgsScene; - qgiSource = NULL; - qgsScene = NULL; - } - - if (currentPage() == qwpPositional) - playChord(); -} - -void AudioWizard::reject() { - g.s = sOldSettings; - - g.s.lmLoopMode = Settings::None; - g.bEchoTest = false; - restartAudio(); - - QWizard::reject(); -} - -void AudioWizard::accept() { - if (! bTransmitChanged) - g.s.atTransmit = sOldSettings.atTransmit; - else if (qrPTT->isChecked()) - g.s.atTransmit = Settings::PushToTalk; - else - g.s.atTransmit = Settings::VAD; - - g.s.bMute = sOldSettings.bMute; - g.s.bDeaf = sOldSettings.bDeaf; - g.s.lmLoopMode = Settings::None; - - const QList &buttons = skwPTT->getShortcut(); - QList ql; - bool found = false; - foreach(Shortcut s, g.s.qlShortcuts) { - if (s.iIndex == g.mw->gsPushTalk->idx) { - if (buttons.isEmpty()) - continue; - else if (! found) { - s.qlButtons = buttons; - found = true; - } - } - ql << s; - } - if (! found && ! buttons.isEmpty()) { - Shortcut s; - s.iIndex = g.mw->gsPushTalk->idx; - s.bSuppress = false; - s.qlButtons = buttons; - ql << s; - } - g.s.qlShortcuts = ql; - - g.s.bUsage = qcbUsage->isChecked(); - g.bEchoTest = false; - g.bPosTest = false; - GlobalShortcutEngine::engine->bNeedRemap = true; - GlobalShortcutEngine::engine->needRemap(); - restartAudio(); - QWizard::accept(); -} - -bool AudioWizard::validateCurrentPage() { - if (currentId() == 1) { - if ((qcbInput->currentIndex() < 0) || (qcbOutput->currentIndex() < 0)) - return false; - } - return true; -} - -void AudioWizard::on_Ticker_timeout() { - AudioInputPtr ai = g.ai; - AudioOutputPtr ao = g.ao; - if (! ai || ! ao) - return; - - int iPeak = static_cast(ai->dMaxMic); - - if (iTicks++ >= 50) { - iMaxPeak = 0; - iTicks = 0; - } - if (iPeak > iMaxPeak) - iMaxPeak = iPeak; - - abAmplify->iBelow = qsMaxAmp->value(); - abAmplify->iValue = iPeak; - abAmplify->iPeak = iMaxPeak; - abAmplify->update(); - - abVAD->iBelow = qsMinVAD->value(); - abVAD->iAbove = qsMaxVAD->value(); - - if (g.s.vsVAD == Settings::Amplitude) { - abVAD->iValue = lroundf((32767.f/96.0f) * (96.0f + ai->dPeakMic)); - } else { - abVAD->iValue = static_cast(ai->fSpeechProb * 32767.0); - } - abVAD->update(); - - QString txt; - if (g.iAudioPathTime) - txt=tr("Audio path is %1ms long.").arg(g.iAudioPathTime*20); - else - txt=tr("Audio path cannot be determined. Input not recognized."); - qlAudioPath->setText(txt); - - if (! qgsScene) { - unsigned int nspeaker = 0; - const float *spos = ao->getSpeakerPos(nspeaker); - if ((nspeaker > 0) && spos) { - qgsScene = new QGraphicsScene(QRectF(-4.0f, -4.0f, 8.0f, 8.0f), this); - qgsScene->addEllipse(QRectF(-0.12f, -0.12f, 0.24f, 0.24f), QPen(Qt::black), QBrush(Qt::darkRed)); - for (unsigned int i=0;iaddEllipse(QRectF(spos[3*i] - 0.1f, spos[3*i+2] - 0.1f, 0.2f, 0.2f), QPen(Qt::black), QBrush(Qt::yellow)); - } - qgiSource = qgsScene->addEllipse(QRectF(-.15f, -.15f, 0.3f, 0.3f), QPen(Qt::black), QBrush(Qt::green)); - qgvView->setScene(qgsScene); - qgvView->fitInView(-4.0f, -4.0f, 8.0f, 8.0f, Qt::KeepAspectRatio); - } - } else if (currentPage() == qwpPositional) { - float xp, yp; - if ((fX == 0.0f) && (fY == 0.0f)) { - fAngle += 0.05f; - - xp = sinf(fAngle) * 2.0f; - yp = cosf(fAngle) * 2.0f; - } else { - xp = fX; - yp = fY; - } - - qgiSource->setPos(xp, yp); - aosSource->fPos[0] = xp; - aosSource->fPos[1] = 0; - aosSource->fPos[2] = yp; - } -} - -void AudioWizard::on_VADmin_valueChanged(int v) { - g.s.fVADmin = static_cast(v) / 32767.0f; -} - -void AudioWizard::on_VADmax_valueChanged(int v) { - g.s.fVADmax = static_cast(v) / 32767.0f; -} - -void AudioWizard::on_Holdtime_valueChanged(int v) { - g.s.iVoiceHold = v; - - float val = static_cast(v) * 20.0f; - val = val / 1000.0f; - qlHoldtime->setText(tr("%1 s").arg(val, 0, 'f', 2)); -} - -void AudioWizard::on_SNR_clicked(bool on) { - if (on) { - g.s.vsVAD = Settings::SignalToNoise; - updateTriggerWidgets(false); - bTransmitChanged = true; - } -} - -void AudioWizard::on_Amplitude_clicked(bool on) { - if (on) { - g.s.vsVAD = Settings::Amplitude; - updateTriggerWidgets(false); - bTransmitChanged = true; - } -} - -void AudioWizard::on_PTT_clicked(bool on) { - if (on) { - updateTriggerWidgets(true); - bTransmitChanged = true; - } -} - -void AudioWizard::on_PTTKey_keySet(bool valid) { - if (valid) - qrPTT->setChecked(true); - else if (qrPTT->isChecked()) - qrSNR->setChecked(true); - updateTriggerWidgets(valid); - bTransmitChanged = true; -} - -void AudioWizard::on_Echo_clicked(bool on) { - g.s.bEcho = on; - restartAudio(); -} - -void AudioWizard::on_Headphone_clicked(bool on) { - g.s.bPositionalHeadphone = on; - restartAudio(); -} - -void AudioWizard::on_Positional_clicked(bool on) { - g.s.bPositionalAudio = on; - restartAudio(); -} - -void AudioWizard::updateTriggerWidgets(bool ptt) { - qwVAD->setEnabled(!ptt); - qwpTrigger->setComplete(!ptt || (skwPTT->qlButtons.count() > 0)); -} +/* Copyright (C) 2005-2009, Thorvald Natvig + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + - Neither the name of the Mumble Developers nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "AudioWizard.h" +#include "AudioInput.h" +#include "Global.h" +#include "Settings.h" +#include "MainWindow.h" + +CompletablePage::CompletablePage(QWizard *p) : QWizardPage(p) { + bComplete = true; +} + +void CompletablePage::setComplete(bool b) { + bComplete = b; + emit completeChanged(); +} + +bool CompletablePage::isComplete() const { + return bComplete; +} + +AudioWizard::AudioWizard(QWidget *p) : QWizard(p) { + bInit = true; + + setOption(QWizard::NoCancelButton, false); +#ifndef Q_OS_MAC + resize(700, 500); +#endif + + addPage(qwpIntro = introPage()); + addPage(qwpDevice = devicePage()); + addPage(qwpDeviceTuning = deviceTuningPage()); + addPage(qwpVolume = volumePage()); + addPage(qwpTrigger = triggerPage()); + addPage(qwpPositional = positionalPage()); + addPage(qwpDone = donePage()); + setWindowTitle(tr("Audio Tuning Wizard")); + + updateTriggerWidgets(qrPTT->isChecked()); + + sOldSettings = g.s; + + g.s.lmLoopMode = Settings::Local; + g.s.dPacketLoss = 0.0; + g.s.dMaxPacketDelay = 0.0; + g.s.bMute = true; + g.s.bDeaf = false; + + g.s.atTransmit = Settings::Continous; + bTransmitChanged = false; + + iMaxPeak = 0; + iTicks = 0; + + g.bEchoTest = true; + + ticker = new QTimer(this); + ticker->setObjectName(QLatin1String("Ticker")); + + setObjectName(QLatin1String("Wizard")); + connect(this, SIGNAL(currentIdChanged(int)), this, SLOT(showPage(int))); + + QMetaObject::connectSlotsByName(this); + bInit = false; + + ticker->setSingleShot(false); + ticker->start(20); +} + +bool AudioWizard::eventFilter(QObject *obj, QEvent *evt) { + if ((evt->type() == QEvent::MouseButtonPress) || + (evt->type() == QEvent::MouseMove)) { + QMouseEvent *qme = dynamic_cast(evt); + if (qme) { + if (qme->buttons() & Qt::LeftButton) { + QPointF qpf = qgvView->mapToScene(qme->pos()); + fX = static_cast(qpf.x()); + fY = static_cast(qpf.y()); + } + } + } + return QWizard::eventFilter(obj, evt); +} + +CompletablePage *AudioWizard::introPage() { + CompletablePage *qwpage = new CompletablePage(this); + qwpage->setTitle(tr("Introduction")); + qwpage->setSubTitle(tr("Welcome to the Mumble Audio Wizard")); + QVBoxLayout *v=new QVBoxLayout(qwpage); + QLabel *label=new QLabel(tr("This is the audio tuning wizard for Mumble. This will help you correctly set the input levels of your sound card, and also set the correct " + "parameters for sound processing in Mumble."), qwpage); + label->setWordWrap(true); + v->addWidget(label); + + label=new QLabel(tr("Please be aware that as long as this wizard is active, audio will be looped locally to allow you to listen to it, and no audio will be sent to the " + "server."), qwpage); + label->setWordWrap(true); + v->addWidget(label); + + return qwpage; +} + +CompletablePage *AudioWizard::donePage() { + CompletablePage *qwpage = new CompletablePage(this); + qwpage->setTitle(tr("Finished")); + qwpage->setSubTitle(tr("Enjoy using Mumble")); + QVBoxLayout *v=new QVBoxLayout(qwpage); + + QLabel *label=new QLabel(tr("Congratulations. You should now be ready to enjoy a richer sound experience with Mumble."), qwpage); + label->setWordWrap(true); + v->addWidget(label); + + label=new QLabel(tr("Mumble is under continuous development, and the development team wants to focus on the features " + "that benefit the most users. To this end, Mumble supports submitting anonymous statistics " + "about your configuration to the developers. These statistcs are essential for future development, and also " + "make sure the features you use aren't deprecated.")); + label->setWordWrap(true); + v->addWidget(label); + + qcbUsage = new QCheckBox(tr("Submit anonymous statistics to the Mumble project")); + qcbUsage->setChecked(g.s.bUsage); + v->addWidget(qcbUsage); + + return qwpage; +} + +CompletablePage *AudioWizard::devicePage() { + CompletablePage *qwpage = new CompletablePage(this); + QLabel *l; + QGridLayout *grid; + + qwpage->setTitle(tr("Device selection")); + qwpage->setSubTitle(tr("Selecting the input and output device to use with Mumble.")); + + QGroupBox *qgbInput = new QGroupBox(tr("Input device"), qwpage); + grid = new QGridLayout(qgbInput); + + l = new QLabel(tr("This is the device your microphone is connected to."), qgbInput); + l->setWordWrap(true); + grid->addWidget(l, 0, 0, 1, 2); + + qcbInput = new QComboBox(qgbInput); + if (AudioInputRegistrar::qmNew) { + foreach(AudioInputRegistrar *air, *AudioInputRegistrar::qmNew) { + qcbInput->addItem(air->name); + if (air->name == AudioInputRegistrar::current) + qcbInput->setCurrentIndex(qcbInput->count() - 1); + QList ql= air->getDeviceChoices(); + } + } + + l = new QLabel(tr("System"), qgbInput); + l->setBuddy(qcbInput); + + qcbInput->setToolTip(tr("Input method for audio")); + qcbInput->setWhatsThis(tr("This is the input method to use for audio.
Most likely you want to use DirectSound.")); + qcbInput->setObjectName(QLatin1String("Input")); + + if (qcbInput->count() < 2) { + qcbInput->setEnabled(false); + } + + grid->addWidget(l, 1, 0); + grid->addWidget(qcbInput, 1, 1); + + + qcbInputDevice = new QComboBox(qgbInput); + l = new QLabel(tr("Device"), qgbInput); + l->setBuddy(qcbInputDevice); + + qcbInputDevice->setToolTip(tr("Input device to use")); + qcbInputDevice->setWhatsThis(tr("Selects which sound card to use for audio input.")); + qcbInputDevice->setObjectName(QLatin1String("InputDevice")); + + grid->addWidget(l, 2, 0); + grid->addWidget(qcbInputDevice, 2, 1); + + qcbEcho = new QCheckBox(tr("Use echo cancellation"), qgbInput); + qcbEcho->setToolTip(tr("Cancel echo from headset or speakers.")); + qcbEcho->setWhatsThis(tr("This enables echo cancellation of outgoing audio, which helps both on speakers and on headsets.")); + qcbEcho->setObjectName(QLatin1String("Echo")); + qcbEcho->setChecked(g.s.bEcho); + grid->addWidget(qcbEcho, 3, 1); + + QGroupBox *qgbOutput = new QGroupBox(tr("Output device"), qwpage); + grid = new QGridLayout(qgbOutput); + + l = new QLabel(tr("This is the device your speakers or headphones are connected to."), qgbOutput); + l->setWordWrap(true); + grid->addWidget(l, 0, 0, 1, 2); + + qcbOutput = new QComboBox(qgbOutput); + if (AudioOutputRegistrar::qmNew) { + foreach(AudioOutputRegistrar *aor, *AudioOutputRegistrar::qmNew) { + qcbOutput->addItem(aor->name); + if (aor->name == AudioOutputRegistrar::current) { + qcbOutput->setCurrentIndex(qcbOutput->count() - 1); + bDelay = aor->usesOutputDelay(); + } + QList ql= aor->getDeviceChoices(); + } + } + + l = new QLabel(tr("System"), qgbOutput); + l->setBuddy(qcbOutput); + + qcbOutput->setToolTip(tr("Output method for audio")); + qcbOutput->setWhatsThis(tr("This is the Output method to use for audio.
Most likely you want to use DirectSound.")); + qcbOutput->setObjectName(QLatin1String("Output")); + + if (qcbOutput->count() < 2) { + qcbOutput->setEnabled(false); + } + + grid->addWidget(l, 1, 0); + grid->addWidget(qcbOutput, 1, 1); + + + qcbOutputDevice = new QComboBox(qgbOutput); + l = new QLabel(tr("Device"), qgbOutput); + l->setBuddy(qcbOutputDevice); + + qcbOutputDevice->setToolTip(tr("Output device to use")); + qcbOutputDevice->setWhatsThis(tr("Selects which sound card to use for audio Output.")); + qcbOutputDevice->setObjectName(QLatin1String("OutputDevice")); + + grid->addWidget(l, 2, 0); + grid->addWidget(qcbOutputDevice, 2, 1); + + qcbPositional = new QCheckBox(tr("Enable positional audio"), qgbOutput); + qcbPositional->setToolTip(tr("Allows positioning of sound.")); + qcbPositional->setWhatsThis(tr("This allows Mumble to use positional audio to place voices.")); + qcbPositional->setObjectName(QLatin1String("Positional")); + qcbPositional->setChecked(g.s.bPositionalAudio); + grid->addWidget(qcbPositional, 3, 1); + + QVBoxLayout *v=new QVBoxLayout(qwpage); + v->addWidget(qgbInput); + v->addWidget(qgbOutput); + + on_Input_activated(qcbInput->currentIndex()); + on_Output_activated(qcbOutput->currentIndex()); + + return qwpage; +} + +CompletablePage *AudioWizard::positionalPage() { + CompletablePage *qwpage = new CompletablePage(this); + qwpage->setTitle(tr("Positional Audio")); + qwpage->setSubTitle(tr("Adjusting attenuation of positional audio.")); + + QVBoxLayout *v = new QVBoxLayout(qwpage); + QLabel *l; + + l = new QLabel(tr("Mumble supports positional audio for some games, and will position the voice of other players relative to their position in game. Depending on their position, the " + "volume of the voice will be changed between the speakers to simulate the direction and distance the other player is at. Such positioning depends on your speaker " + "configuration being correct in your operating system, so a test is done here."), qwpage); + l->setWordWrap(true); + v->addWidget(l); + l = new QLabel(tr("The graph below shows the position of you, the speakers and a moving sound source as if seen from above. You should hear the audio move between the channels."), qwpage); + l->setWordWrap(true); + v->addWidget(l); + + qcbHeadphone = new QCheckBox(tr("Use headphones"), qwpage); + qcbHeadphone->setToolTip(tr("Use headphones instead of speakers.")); + qcbHeadphone->setWhatsThis(tr("This ignores the OS speaker configuration and configures the positioning for headphones instead.")); + qcbHeadphone->setObjectName(QLatin1String("Headphone")); + qcbHeadphone->setChecked(g.s.bPositionalHeadphone); + v->addWidget(qcbHeadphone); + + + fAngle = 0.0f; + fX = fY = 0.0f; + qgsScene = NULL; + qgiSource = NULL; + aosSource = NULL; + qgvView = new QGraphicsView(qwpage); + qgvView->scale(1.0f, -1.0f); + qgvView->viewport()->installEventFilter(this); + + v->addWidget(qgvView); + + return qwpage; +} + +CompletablePage *AudioWizard::volumePage() { + CompletablePage *qwpage = new CompletablePage(this); + qwpage->setTitle(tr("Volume tuning")); + qwpage->setSubTitle(tr("Tuning microphone hardware volume to optimal settings.")); + + QVBoxLayout *v = new QVBoxLayout(qwpage); + QLabel *l; + + l = new QLabel(tr("Open your sound control panel and go to the recording settings. " + "Make sure the microphone is selected as active input with maximum recording volume. If there's an option to enable a \"Microphone boost\" make sure it's checked."), qwpage); + l->setWordWrap(true); + v->addWidget(l); + + l = new QLabel(tr("Speak loudly, as when you are annoyed or excited. " + "Decrease the volume in the sound control panel until the bar below stays as high as possible in the blue and green " + "but not the red " + "zone while you speak."), qwpage); + l->setWordWrap(true); + v->addWidget(l); + + abAmplify = new AudioBar(qwpage); + abAmplify->qcBelow = Qt::blue; + v->addWidget(abAmplify); + + l = new QLabel(tr("Now talk softly, as you would when talking late at night and you don't want to disturb anyone. " + "Adjust the slider below so that the bar moves into green when you talk, but stays blue while you're silent."), qwpage); + l->setWordWrap(true); + v->addWidget(l); + + qsMaxAmp = new QSlider(Qt::Horizontal, qwpage); + qsMaxAmp->setRange(1, 32767); + qsMaxAmp->setSingleStep(100); + qsMaxAmp->setPageStep(1000); + qsMaxAmp->setValue(g.s.iMinLoudness); + qsMaxAmp->setObjectName(QLatin1String("MaxAmp")); + + v->addWidget(qsMaxAmp); + + return qwpage; +} + +CompletablePage *AudioWizard::triggerPage() { + CompletablePage *qwpage = new CompletablePage(this); + qwpage->setTitle(tr("Voice Activity Detection")); + qwpage->setSubTitle(tr("Letting Mumble figure out when you're talking and when you're silent.")); + + QHBoxLayout *h; + QVBoxLayout *v; + + v= new QVBoxLayout(qwpage); + QLabel *l; + + l = new QLabel(tr("This will help Mumble figure out when you are talking. The first step is selecting which data value to use."), qwpage); + l->setWordWrap(true); + v->addWidget(l); + + h = new QHBoxLayout; + + qrPTT = new QRadioButton(tr("Push To Talk:"), qwpage); + qrPTT->setObjectName(QLatin1String("PTT")); + h->addWidget(qrPTT); + + skwPTT = new ShortcutKeyWidget(qwpage); + skwPTT->setObjectName(QLatin1String("PTTKey")); + foreach(const Shortcut &s, g.s.qlShortcuts) { + if (s.iIndex == g.mw->gsPushTalk->idx) { + skwPTT->setShortcut(s.qlButtons); + break; + } + } + h->addWidget(skwPTT); + + v->addLayout(h); + + qrSNR = new QRadioButton(tr("Signal-To-Noise ratio"), qwpage); + qrSNR->setObjectName(QLatin1String("SNR")); + v->addWidget(qrSNR); + + qrAmplitude = new QRadioButton(tr("Raw amplitude from input"), qwpage); + qrAmplitude->setObjectName(QLatin1String("Amplitude")); + v->addWidget(qrAmplitude); + + if (g.s.atTransmit == Settings::PushToTalk) + qrPTT->setChecked(true); + else if (g.s.vsVAD == Settings::Amplitude) + qrAmplitude->setChecked(true); + else + qrSNR->setChecked(true); + + qwVAD = new QWidget(qwpage); + v->addWidget(qwVAD); + + v = new QVBoxLayout(qwVAD); + + abVAD = new AudioBar(qwpage); + abVAD->qcBelow = Qt::red; + abVAD->qcInside = Qt::yellow; + abVAD->qcAbove = Qt::green; + + v->addWidget(abVAD); + + l = new QLabel(tr("Next you need to adjust the following two sliders. The first few utterances you say should end up in the " + "green area (definitive speech). While talking, you should stay inside the yellow (might be speech) and when you're " + "not talking, everything should be in the red (definitively not speech)."), qwpage); + l->setWordWrap(true); + v->addWidget(l); + + qsMinVAD = new QSlider(Qt::Horizontal, qwpage); + qsMinVAD->setRange(1, 32767); + qsMinVAD->setSingleStep(100); + qsMinVAD->setPageStep(1000); + qsMinVAD->setValue(static_cast(g.s.fVADmin * 32767.0)); + qsMinVAD->setObjectName(QLatin1String("VADmin")); + + v->addWidget(qsMinVAD); + + qsMaxVAD = new QSlider(Qt::Horizontal, qwpage); + qsMaxVAD->setRange(1, 32767); + qsMaxVAD->setSingleStep(100); + qsMaxVAD->setPageStep(1000); + qsMaxVAD->setValue(static_cast(g.s.fVADmax * 32767)); + qsMaxVAD->setObjectName(QLatin1String("VADmax")); + + v->addWidget(qsMaxVAD); + + l = new QLabel(tr("Last, you need to adjust the grace time. Mumble may detect this much non-speech and still keep transmitting before " + "it breaks off. This allows you to catch your breath while speaking."), qwpage); + l->setWordWrap(true); + v->addWidget(l); + + h = new QHBoxLayout; + + qsHoldtime = new QSlider(Qt::Horizontal, qwpage); + qsHoldtime->setRange(20, 250); + qsHoldtime->setSingleStep(5); + qsHoldtime->setPageStep(25); + qsHoldtime->setValue(g.s.iVoiceHold); + qsHoldtime->setObjectName(QLatin1String("Holdtime")); + + qlHoldtime = new QLabel(qwpage); + qlHoldtime->setMinimumWidth(40); + on_Holdtime_valueChanged(qsHoldtime->value()); + + h->addWidget(qsHoldtime); + h->addWidget(qlHoldtime); + + v->addLayout(h); + + return qwpage; +} + +CompletablePage *AudioWizard::deviceTuningPage() { + CompletablePage *qwpage = new CompletablePage(this); + qwpage->setTitle(tr("Device tuning")); + qwpage->setSubTitle(tr("Changing hardware output delays to their minimum value.")); + + QGridLayout *grid = new QGridLayout(qwpage); + + QLabel *l = new QLabel(tr("To keep latency to an absolute minium, it's important to buffer as little audio as possible on the soundcard. " + "However, many soundcards report that they require a much smaller buffer than what they can actually work with, " + "so the only way to set this value is to try and fail."), qwpage); + l->setWordWrap(true); + grid->addWidget(l, 0, 0, 1, 2); + + l = new QLabel(tr("You should hear a single tone that's changing in frequency. Change the slider below to the lowest value which gives no interruptions or jitter " + "in the sound. Please note that local echo is disabled during this test to " + "improve audio path recognition."), qwpage); + l->setWordWrap(true); + grid->addWidget(l, 1, 0, 1, 2); + + qsOutputDelay = new QSlider(Qt::Horizontal, qwpage); + qsOutputDelay->setRange(1, 6); + + qsOutputDelay->setSingleStep(1); + qsOutputDelay->setPageStep(2); + qsOutputDelay->setValue(g.s.iOutputDelay); + qsOutputDelay->setObjectName(QLatin1String("OutputDelay")); + + qlOutputDelay=new QLabel(qwpage); + qlOutputDelay->setMinimumWidth(30); + on_OutputDelay_valueChanged(qsOutputDelay->value()); + qsOutputDelay->setToolTip(tr("Amount of data to buffer.")); + qsOutputDelay->setWhatsThis(tr("This sets the amount of data to prebuffer in the output buffer. " + "Experiment with different values and set it to the lowest which doesn't " + "cause rapid jitter in the sound.")); + grid->addWidget(qsOutputDelay, 2, 1); + grid->addWidget(qlOutputDelay, 2, 2); + + l = new QLabel(tr("If you position the microphone so it can pick up the speakers or headset, Mumble will measure the total audio path " + "delay in your system; this means the delay from a sample is placed in an outbound buffer until it's found in " + "a matching incoming buffer."), qwpage); + l->setWordWrap(true); + grid->addWidget(l, 3, 0, 1, 2); + + qlAudioPath = new QLabel(qwpage); + grid->addWidget(qlAudioPath, 4, 0, 1, 2); + + return qwpage; +} + +void AudioWizard::on_Input_activated(int) { + qcbInputDevice->clear(); + + if (! AudioInputRegistrar::qmNew) + return; + + AudioInputRegistrar *air = AudioInputRegistrar::qmNew->value(qcbInput->currentText()); + QList ql = air->getDeviceChoices(); + + foreach(audioDevice d, ql) { + qcbInputDevice->addItem(d.first, d.second); + } + + qcbInputDevice->setEnabled(ql.count() > 1); + + on_InputDevice_activated(0); +} + +void AudioWizard::on_InputDevice_activated(int) { + if (bInit) + return; + + if (! AudioInputRegistrar::qmNew) + return; + + boost::weak_ptr wai(g.ai); + g.ai.reset(); + + while (! wai.expired()) { + } + + AudioInputRegistrar *air = AudioInputRegistrar::qmNew->value(qcbInput->currentText()); + int idx = qcbInputDevice->currentIndex(); + if (idx > -1) { + air->setDeviceChoice(qcbInputDevice->itemData(idx), g.s); + } + + qcbEcho->setEnabled(air->canEcho(qcbOutput->currentText())); + + g.ai = AudioInputPtr(air->create()); + g.ai->start(QThread::HighestPriority); +} + +void AudioWizard::on_Output_activated(int) { + qcbOutputDevice->clear(); + + if (! AudioOutputRegistrar::qmNew) + return; + + AudioOutputRegistrar *aor = AudioOutputRegistrar::qmNew->value(qcbOutput->currentText()); + QList ql = aor->getDeviceChoices(); + + foreach(audioDevice d, ql) { + qcbOutputDevice->addItem(d.first, d.second); + } + + qcbOutputDevice->setEnabled(ql.count() > 1); + + on_OutputDevice_activated(0); +} + +void AudioWizard::on_OutputDevice_activated(int) { + if (bInit) + return; + + if (! AudioOutputRegistrar::qmNew) + return; + + boost::weak_ptr wai(g.ao); + g.ao.reset(); + + while (! wai.expired()) { + } + + AudioOutputRegistrar *aor = AudioOutputRegistrar::qmNew->value(qcbOutput->currentText()); + int idx = qcbOutputDevice->currentIndex(); + if (idx > -1) { + aor->setDeviceChoice(qcbOutputDevice->itemData(idx), g.s); + bDelay = aor->usesOutputDelay(); + } + + AudioInputRegistrar *air = AudioInputRegistrar::qmNew->value(qcbInput->currentText()); + qcbEcho->setEnabled(air->canEcho(qcbOutput->currentText())); + + g.ao = AudioOutputPtr(aor->create()); + g.ao->start(QThread::HighPriority); +} + +void AudioWizard::on_OutputDelay_valueChanged(int v) { + qlOutputDelay->setText(tr("%1ms").arg(v*20)); + g.s.iOutputDelay = v; + on_OutputDevice_activated(0); + if (! bInit) { + AudioOutputPtr ao = g.ao; + ao->playSine(0.0f, 0.0f, 0xfffffff, 0.5f); + } +} + +void AudioWizard::on_MaxAmp_valueChanged(int v) { + g.s.iMinLoudness = qMin(v, 30000); +} + +void AudioWizard::showPage(int) { + CompletablePage *cp = qobject_cast(currentPage()); + + AudioOutputPtr ao = g.ao; + if (ao) + ao->wipe(); + aosSource = NULL; + + g.bPosTest = false; + + if (cp == qwpIntro) { + g.s.bMute = true; + } else if (cp == qwpDone) { + g.s.bMute = true; + } else if (cp == qwpDeviceTuning) { + g.s.bMute = true; + playChord(); + } else if (cp == qwpPositional) { + fX = fY = 0.0f; + g.s.bMute = true; + g.bPosTest = true; + if (qgsScene) { + delete qgsScene; + qgiSource = NULL; + qgsScene = NULL; + } + playChord(); + } else { + g.s.bMute = false; + } +} + +int AudioWizard::nextId() const { + AudioOutputPtr ao = g.ao; + + int nextid = QWizard::nextId(); + if (currentPage() == qwpTrigger && ! g.s.bPositionalAudio) + nextid++; + else if ((currentPage() == qwpDevice) && ! bDelay) + nextid++; + return nextid; +} + +void AudioWizard::playChord() { + AudioOutputPtr ao = g.ao; + if (! ao || aosSource) + return; + aosSource = ao->playSample(QLatin1String("skin:wb_male.spx"), true); +} + +void AudioWizard::restartAudio() { + boost::weak_ptr wai(g.ai); + boost::weak_ptr wao(g.ao); + + aosSource = NULL; + + g.ai.reset(); + g.ao.reset(); + + while (! wai.expired() || ! wao.expired()) { + // Where is QThread::yield() ? + } + + g.s.qsAudioInput = qcbInput->currentText(); + g.s.qsAudioOutput = qcbOutput->currentText(); + + g.ai = AudioInputRegistrar::newFromChoice(g.s.qsAudioInput); + if (g.ai) + g.ai->start(QThread::HighestPriority); + g.ao = AudioOutputRegistrar::newFromChoice(g.s.qsAudioOutput); + if (g.ao) + g.ao->start(QThread::HighPriority); + + if (qgsScene) { + delete qgsScene; + qgiSource = NULL; + qgsScene = NULL; + } + + if (currentPage() == qwpPositional) + playChord(); +} + +void AudioWizard::reject() { + g.s = sOldSettings; + + g.s.lmLoopMode = Settings::None; + g.bEchoTest = false; + restartAudio(); + + QWizard::reject(); +} + +void AudioWizard::accept() { + if (! bTransmitChanged) + g.s.atTransmit = sOldSettings.atTransmit; + else if (qrPTT->isChecked()) + g.s.atTransmit = Settings::PushToTalk; + else + g.s.atTransmit = Settings::VAD; + + g.s.bMute = sOldSettings.bMute; + g.s.bDeaf = sOldSettings.bDeaf; + g.s.lmLoopMode = Settings::None; + + const QList &buttons = skwPTT->getShortcut(); + QList ql; + bool found = false; + foreach(Shortcut s, g.s.qlShortcuts) { + if (s.iIndex == g.mw->gsPushTalk->idx) { + if (buttons.isEmpty()) + continue; + else if (! found) { + s.qlButtons = buttons; + found = true; + } + } + ql << s; + } + if (! found && ! buttons.isEmpty()) { + Shortcut s; + s.iIndex = g.mw->gsPushTalk->idx; + s.bSuppress = false; + s.qlButtons = buttons; + ql << s; + } + g.s.qlShortcuts = ql; + + g.s.bUsage = qcbUsage->isChecked(); + g.bEchoTest = false; + g.bPosTest = false; + GlobalShortcutEngine::engine->bNeedRemap = true; + GlobalShortcutEngine::engine->needRemap(); + restartAudio(); + QWizard::accept(); +} + +bool AudioWizard::validateCurrentPage() { + if (currentId() == 1) { + if ((qcbInput->currentIndex() < 0) || (qcbOutput->currentIndex() < 0)) + return false; + } + return true; +} + +void AudioWizard::on_Ticker_timeout() { + AudioInputPtr ai = g.ai; + AudioOutputPtr ao = g.ao; + if (! ai || ! ao) + return; + + int iPeak = static_cast(ai->dMaxMic); + + if (iTicks++ >= 50) { + iMaxPeak = 0; + iTicks = 0; + } + if (iPeak > iMaxPeak) + iMaxPeak = iPeak; + + abAmplify->iBelow = qsMaxAmp->value(); + abAmplify->iValue = iPeak; + abAmplify->iPeak = iMaxPeak; + abAmplify->update(); + + abVAD->iBelow = qsMinVAD->value(); + abVAD->iAbove = qsMaxVAD->value(); + + if (g.s.vsVAD == Settings::Amplitude) { + abVAD->iValue = lroundf((32767.f/96.0f) * (96.0f + ai->dPeakMic)); + } else { + abVAD->iValue = static_cast(ai->fSpeechProb * 32767.0); + } + abVAD->update(); + + QString txt; + if (g.iAudioPathTime) + txt=tr("Audio path is %1ms long.").arg(g.iAudioPathTime*20); + else + txt=tr("Audio path cannot be determined. Input not recognized."); + qlAudioPath->setText(txt); + + if (! qgsScene) { + unsigned int nspeaker = 0; + const float *spos = ao->getSpeakerPos(nspeaker); + if ((nspeaker > 0) && spos) { + qgsScene = new QGraphicsScene(QRectF(-4.0f, -4.0f, 8.0f, 8.0f), this); + qgsScene->addEllipse(QRectF(-0.12f, -0.12f, 0.24f, 0.24f), QPen(Qt::black), QBrush(Qt::darkRed)); + for (unsigned int i=0;iaddEllipse(QRectF(spos[3*i] - 0.1f, spos[3*i+2] - 0.1f, 0.2f, 0.2f), QPen(Qt::black), QBrush(Qt::yellow)); + } + qgiSource = qgsScene->addEllipse(QRectF(-.15f, -.15f, 0.3f, 0.3f), QPen(Qt::black), QBrush(Qt::green)); + qgvView->setScene(qgsScene); + qgvView->fitInView(-4.0f, -4.0f, 8.0f, 8.0f, Qt::KeepAspectRatio); + } + } else if (currentPage() == qwpPositional) { + float xp, yp; + if ((fX == 0.0f) && (fY == 0.0f)) { + fAngle += 0.05f; + + xp = sinf(fAngle) * 2.0f; + yp = cosf(fAngle) * 2.0f; + } else { + xp = fX; + yp = fY; + } + + qgiSource->setPos(xp, yp); + aosSource->fPos[0] = xp; + aosSource->fPos[1] = 0; + aosSource->fPos[2] = yp; + } +} + +void AudioWizard::on_VADmin_valueChanged(int v) { + g.s.fVADmin = static_cast(v) / 32767.0f; +} + +void AudioWizard::on_VADmax_valueChanged(int v) { + g.s.fVADmax = static_cast(v) / 32767.0f; +} + +void AudioWizard::on_Holdtime_valueChanged(int v) { + g.s.iVoiceHold = v; + + float val = static_cast(v) * 20.0f; + val = val / 1000.0f; + qlHoldtime->setText(tr("%1 s").arg(val, 0, 'f', 2)); +} + +void AudioWizard::on_SNR_clicked(bool on) { + if (on) { + g.s.vsVAD = Settings::SignalToNoise; + updateTriggerWidgets(false); + bTransmitChanged = true; + } +} + +void AudioWizard::on_Amplitude_clicked(bool on) { + if (on) { + g.s.vsVAD = Settings::Amplitude; + updateTriggerWidgets(false); + bTransmitChanged = true; + } +} + +void AudioWizard::on_PTT_clicked(bool on) { + if (on) { + updateTriggerWidgets(true); + bTransmitChanged = true; + } +} + +void AudioWizard::on_PTTKey_keySet(bool valid) { + if (valid) + qrPTT->setChecked(true); + else if (qrPTT->isChecked()) + qrSNR->setChecked(true); + updateTriggerWidgets(valid); + bTransmitChanged = true; +} + +void AudioWizard::on_Echo_clicked(bool on) { + g.s.bEcho = on; + restartAudio(); +} + +void AudioWizard::on_Headphone_clicked(bool on) { + g.s.bPositionalHeadphone = on; + restartAudio(); +} + +void AudioWizard::on_Positional_clicked(bool on) { + g.s.bPositionalAudio = on; + restartAudio(); +} + +void AudioWizard::updateTriggerWidgets(bool ptt) { + qwVAD->setEnabled(!ptt); + qwpTrigger->setComplete(!ptt || (skwPTT->qlButtons.count() > 0)); +} diff --git a/src/mumble/AudioWizard.h b/src/mumble/AudioWizard.h index 99cc814ad..1d6134178 100644 --- a/src/mumble/AudioWizard.h +++ b/src/mumble/AudioWizard.h @@ -1,140 +1,140 @@ -/* Copyright (C) 2005-2009, Thorvald Natvig - - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - - Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - Neither the name of the Mumble Developers nor the names of its - contributors may be used to endorse or promote products derived from this - software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef _AUDIOWIZARD_H -#define _AUDIOWIZARD_H - -#include "Settings.h" -#include "AudioStats.h" -#include "AudioOutput.h" -#include "GlobalShortcut.h" - -class CompletablePage : public QWizardPage { - Q_OBJECT - protected: - bool bComplete; - public: - CompletablePage(QWizard *p = NULL); - void setComplete(bool); - bool isComplete() const; -}; - -class AudioWizard: public QWizard { - private: - Q_OBJECT - Q_DISABLE_COPY(AudioWizard) - protected: - QComboBox *qcbInput, *qcbInputDevice; - QComboBox *qcbOutput, *qcbOutputDevice; - QCheckBox *qcbEcho; - QCheckBox *qcbPositional; - - QSlider *qsOutputDelay; - QLabel *qlOutputDelay; - - AudioBar *abAmplify; - QSlider *qsMaxAmp; - - QWidget *qwVAD; - AudioBar *abVAD; - QRadioButton *qrAmplitude, *qrSNR, *qrPTT; - QSlider *qsMinVAD, *qsMaxVAD; - ShortcutKeyWidget *skwPTT; - bool bTransmitChanged; - - QSlider *qsHoldtime; - QLabel *qlHoldtime; - - QLabel *qlAudioPath; - - QGraphicsView *qgvView; - QGraphicsScene *qgsScene; - QGraphicsItem *qgiSource; - QCheckBox *qcbHeadphone; - AudioOutputSample *aosSource; - float fAngle; - float fX, fY; - - QCheckBox *qcbUsage; - - CompletablePage *qwpIntro, *qwpDevice, *qwpVolume, *qwpTrigger, *qwpDeviceTuning, *qwpPositional, *qwpDone; - - CompletablePage *introPage(); - CompletablePage *devicePage(); - CompletablePage *volumePage(); - CompletablePage *triggerPage(); - CompletablePage *deviceTuningPage(); - CompletablePage *positionalPage(); - CompletablePage *donePage(); - - Settings sOldSettings; - - QTimer *ticker; - - bool bInit; - - bool bDelay; - - int iMaxPeak; - int iTicks; - - void restartAudio(); - void playChord(); - - bool eventFilter(QObject *, QEvent *); - public slots: - void on_Input_activated(int); - void on_InputDevice_activated(int); - void on_Output_activated(int); - void on_OutputDevice_activated(int); - void on_OutputDelay_valueChanged(int); - void on_MaxAmp_valueChanged(int); - void on_Ticker_timeout(); - void on_VADmin_valueChanged(int); - void on_VADmax_valueChanged(int); - void on_Holdtime_valueChanged(int); - void on_Amplitude_clicked(bool); - void on_SNR_clicked(bool); - void on_PTT_clicked(bool); - void on_Echo_clicked(bool); - void on_Headphone_clicked(bool); - void on_Positional_clicked(bool); - void on_PTTKey_keySet(bool); - void showPage(int); - void updateTriggerWidgets(bool); - public: - AudioWizard(QWidget *parent); - void reject(); - void accept(); - bool validateCurrentPage(); - virtual int nextId() const; -}; - -#endif +/* Copyright (C) 2005-2009, Thorvald Natvig + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + - Neither the name of the Mumble Developers nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _AUDIOWIZARD_H +#define _AUDIOWIZARD_H + +#include "Settings.h" +#include "AudioStats.h" +#include "AudioOutput.h" +#include "GlobalShortcut.h" + +class CompletablePage : public QWizardPage { + Q_OBJECT + protected: + bool bComplete; + public: + CompletablePage(QWizard *p = NULL); + void setComplete(bool); + bool isComplete() const; +}; + +class AudioWizard: public QWizard { + private: + Q_OBJECT + Q_DISABLE_COPY(AudioWizard) + protected: + QComboBox *qcbInput, *qcbInputDevice; + QComboBox *qcbOutput, *qcbOutputDevice; + QCheckBox *qcbEcho; + QCheckBox *qcbPositional; + + QSlider *qsOutputDelay; + QLabel *qlOutputDelay; + + AudioBar *abAmplify; + QSlider *qsMaxAmp; + + QWidget *qwVAD; + AudioBar *abVAD; + QRadioButton *qrAmplitude, *qrSNR, *qrPTT; + QSlider *qsMinVAD, *qsMaxVAD; + ShortcutKeyWidget *skwPTT; + bool bTransmitChanged; + + QSlider *qsHoldtime; + QLabel *qlHoldtime; + + QLabel *qlAudioPath; + + QGraphicsView *qgvView; + QGraphicsScene *qgsScene; + QGraphicsItem *qgiSource; + QCheckBox *qcbHeadphone; + AudioOutputSample *aosSource; + float fAngle; + float fX, fY; + + QCheckBox *qcbUsage; + + CompletablePage *qwpIntro, *qwpDevice, *qwpVolume, *qwpTrigger, *qwpDeviceTuning, *qwpPositional, *qwpDone; + + CompletablePage *introPage(); + CompletablePage *devicePage(); + CompletablePage *volumePage(); + CompletablePage *triggerPage(); + CompletablePage *deviceTuningPage(); + CompletablePage *positionalPage(); + CompletablePage *donePage(); + + Settings sOldSettings; + + QTimer *ticker; + + bool bInit; + + bool bDelay; + + int iMaxPeak; + int iTicks; + + void restartAudio(); + void playChord(); + + bool eventFilter(QObject *, QEvent *); + public slots: + void on_Input_activated(int); + void on_InputDevice_activated(int); + void on_Output_activated(int); + void on_OutputDevice_activated(int); + void on_OutputDelay_valueChanged(int); + void on_MaxAmp_valueChanged(int); + void on_Ticker_timeout(); + void on_VADmin_valueChanged(int); + void on_VADmax_valueChanged(int); + void on_Holdtime_valueChanged(int); + void on_Amplitude_clicked(bool); + void on_SNR_clicked(bool); + void on_PTT_clicked(bool); + void on_Echo_clicked(bool); + void on_Headphone_clicked(bool); + void on_Positional_clicked(bool); + void on_PTTKey_keySet(bool); + void showPage(int); + void updateTriggerWidgets(bool); + public: + AudioWizard(QWidget *parent); + void reject(); + void accept(); + bool validateCurrentPage(); + virtual int nextId() const; +}; + +#endif diff --git a/src/mumble/DBus.h b/src/mumble/DBus.h index 21904f6ac..0aaabbbb0 100644 --- a/src/mumble/DBus.h +++ b/src/mumble/DBus.h @@ -1,57 +1,57 @@ -/* Copyright (C) 2005-2009, Thorvald Natvig - - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - - Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - Neither the name of the Mumble Developers nor the names of its - contributors may be used to endorse or promote products derived from this - software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef _DBUS_H -#define _DBUS_H - -#include "mumble_pch.hpp" - -class MumbleDBus : public QDBusAbstractAdaptor { - private: - Q_OBJECT - Q_CLASSINFO("D-Bus Interface", "net.sourceforge.mumble.Mumble") - Q_DISABLE_COPY(MumbleDBus) - Q_PROPERTY(bool mute READ isSelfMuted WRITE setSelfMuted) - Q_PROPERTY(bool deaf READ isSelfDeaf WRITE setSelfDeaf) - public: - MumbleDBus(QObject *parent); - public slots: - void openUrl(const QString &url, const QDBusMessage &); - void getCurrentUrl(const QDBusMessage &); - void focus(); - void setSelfMuted(bool mute); - void setSelfDeaf(bool deafen); - bool isSelfMuted(); - bool isSelfDeaf(); -}; - -#else -class MumbleDBus; -#endif +/* Copyright (C) 2005-2009, Thorvald Natvig + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + - Neither the name of the Mumble Developers nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _DBUS_H +#define _DBUS_H + +#include "mumble_pch.hpp" + +class MumbleDBus : public QDBusAbstractAdaptor { + private: + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "net.sourceforge.mumble.Mumble") + Q_DISABLE_COPY(MumbleDBus) + Q_PROPERTY(bool mute READ isSelfMuted WRITE setSelfMuted) + Q_PROPERTY(bool deaf READ isSelfDeaf WRITE setSelfDeaf) + public: + MumbleDBus(QObject *parent); + public slots: + void openUrl(const QString &url, const QDBusMessage &); + void getCurrentUrl(const QDBusMessage &); + void focus(); + void setSelfMuted(bool mute); + void setSelfDeaf(bool deafen); + bool isSelfMuted(); + bool isSelfDeaf(); +}; + +#else +class MumbleDBus; +#endif diff --git a/src/mumble/GlobalShortcut.cpp b/src/mumble/GlobalShortcut.cpp index 2dc2e34ab..4dbe6f1af 100644 --- a/src/mumble/GlobalShortcut.cpp +++ b/src/mumble/GlobalShortcut.cpp @@ -1,461 +1,461 @@ -/* Copyright (C) 2005-2009, Thorvald Natvig - - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - - Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - Neither the name of the Mumble Developers nor the names of its - contributors may be used to endorse or promote products derived from this - software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include "GlobalShortcut.h" -#include "MainWindow.h" -#include "Global.h" - -GlobalShortcutEngine *GlobalShortcutEngine::engine = NULL; - -static ConfigWidget *GlobalShortcutConfigDialogNew(Settings &st) { - return new GlobalShortcutConfig(st); -} - -static ConfigRegistrar registrar(1200, GlobalShortcutConfigDialogNew); - -ShortcutKeyWidget::ShortcutKeyWidget(QWidget *p) : QLineEdit(p) { - setReadOnly(true); - clearFocus(); - bModified = false; - displayKeys(); -} - -QList ShortcutKeyWidget::getShortcut() const { - return qlButtons; -} - -void ShortcutKeyWidget::setShortcut(const QList &buttons) { - qlButtons = buttons; - displayKeys(); -} - -void ShortcutKeyWidget::focusInEvent(QFocusEvent *) { - setText(tr("Press Shortcut")); - - QPalette pal=parentWidget()->palette(); - pal.setColor(QPalette::Base, pal.color(QPalette::Base).dark(120)); - setPalette(pal); - - setForegroundRole(QPalette::Button); - GlobalShortcutEngine::engine->resetMap(); - connect(GlobalShortcutEngine::engine, SIGNAL(buttonPressed(bool)), this, SLOT(updateKeys(bool))); - installEventFilter(this); -} - -void ShortcutKeyWidget::focusOutEvent(QFocusEvent *e) { - if ((e->reason() == Qt::TabFocusReason) || (e->reason() == Qt::BacktabFocusReason)) - return; - - setPalette(parentWidget()->palette()); - clearFocus(); - disconnect(GlobalShortcutEngine::engine, SIGNAL(buttonPressed(bool)), this, SLOT(updateKeys(bool))); - displayKeys(); - removeEventFilter(this); -} - -bool ShortcutKeyWidget::eventFilter(QObject *, QEvent *evt) { - if ((evt->type() == QEvent::KeyPress) || (evt->type() == QEvent::MouseButtonPress)) - return true; - return false; -} - -void ShortcutKeyWidget::mouseDoubleClickEvent(QMouseEvent *) { - bModified = true; - qlButtons.clear(); - clearFocus(); - displayKeys(); -} - -void ShortcutKeyWidget::updateKeys(bool last) { - qlButtons = GlobalShortcutEngine::engine->qlActiveButtons; - bModified = true; - - if (qlButtons.isEmpty()) - return; - - if (last) - clearFocus(); - else - displayKeys(); -} - -void ShortcutKeyWidget::displayKeys() { - QStringList keys; - - foreach(QVariant button, qlButtons) { - QString id = GlobalShortcutEngine::engine->buttonName(button); - if (! id.isEmpty()) - keys << id; - } - setText(keys.join(QLatin1String(" + "))); - emit keySet(keys.count() > 0); -} - -ShortcutActionWidget::ShortcutActionWidget(QWidget *p) : QComboBox(p) { - int idx = 0; - - insertItem(idx, tr("Unassigned")); - setItemData(idx, -1); - - idx++; - - bool expert = true; - while (p) { - GlobalShortcutConfig *gsc = qobject_cast(p); - if (gsc) { - expert = gsc->bExpert; - break; - } - p = p->parentWidget(); - } - foreach(GlobalShortcut *gs, GlobalShortcutEngine::engine->qmShortcuts) { - if (expert || ! gs->bExpert) { - insertItem(idx, gs->name); - setItemData(idx, gs->idx); - if (! gs->qsToolTip.isEmpty()) - setItemData(idx, gs->qsToolTip, Qt::ToolTipRole); - if (! gs->qsWhatsThis.isEmpty()) - setItemData(idx, gs->qsWhatsThis, Qt::WhatsThisRole); - idx++; - } - } -} - -void ShortcutActionWidget::setIndex(int idx) { - setCurrentIndex(findData(idx)); -} - -int ShortcutActionWidget::index() const { - return itemData(currentIndex()).toInt(); -} - -ShortcutDelegate::ShortcutDelegate(QObject *p) : QStyledItemDelegate(p) { - QItemEditorFactory *factory = new QItemEditorFactory; - QItemEditorCreatorBase *shortcutCreator = new QStandardItemEditorCreator(); - QItemEditorCreatorBase *indexCreator = new QStandardItemEditorCreator(); - - factory->registerEditor(QVariant::List, shortcutCreator); - factory->registerEditor(QVariant::Int, indexCreator); - setItemEditorFactory(factory); -} - -ShortcutDelegate::~ShortcutDelegate() { - delete itemEditorFactory(); - setItemEditorFactory(NULL); -} - -QString ShortcutDelegate::displayText(const QVariant &item, const QLocale &loc) const { - if (item.type() == QVariant::List) - return GlobalShortcutEngine::buttonText(item.toList()); - else if (item.type() == QVariant::Int) { - GlobalShortcut *gs = GlobalShortcutEngine::engine->qmShortcuts.value(item.toInt()); - if (gs) - return gs->name; - else - return tr("Unassigned"); - } - - return QStyledItemDelegate::displayText(item,loc); -} - -GlobalShortcutConfig::GlobalShortcutConfig(Settings &st) : ConfigWidget(st) { - setupUi(this); - - bool canSuppress = GlobalShortcutEngine::engine->canSuppress(); - - qtwShortcuts->setColumnCount(canSuppress ? 3 : 2); - qtwShortcuts->setItemDelegate(new ShortcutDelegate(qtwShortcuts)); -} - -void GlobalShortcutConfig::on_qpbAdd_clicked(bool) { - Shortcut sc; - sc.iIndex = -1; - sc.bSuppress = false; - qlShortcuts << sc; - reload(); -} - -void GlobalShortcutConfig::on_qpbRemove_clicked(bool) { - QTreeWidgetItem *qtwi = qtwShortcuts->currentItem(); - if (! qtwi) - return; - qlShortcuts.removeAt(qtwShortcuts->indexOfTopLevelItem(qtwi)); - delete qtwi; -} - -void GlobalShortcutConfig::on_qtwShortcuts_currentItemChanged(QTreeWidgetItem *item, QTreeWidgetItem *) { - qpbRemove->setEnabled(item ? true : false); -} - -void GlobalShortcutConfig::on_qtwShortcuts_itemChanged(QTreeWidgetItem *item, int) { - int idx = qtwShortcuts->indexOfTopLevelItem(item); - Shortcut &sc = qlShortcuts[idx]; - sc.iIndex = item->data(0, Qt::DisplayRole).toInt(); - sc.qlButtons = item->data(1, Qt::DisplayRole).toList(); - sc.bSuppress = item->checkState(2) == Qt::Checked; -} - -QString GlobalShortcutConfig::title() const { - return tr("Shortcuts"); -} - -QIcon GlobalShortcutConfig::icon() const { - return QIcon(QLatin1String("skin:config_shortcuts.png")); -} - -void GlobalShortcutConfig::load(const Settings &r) { - qlShortcuts = r.qlShortcuts; - bExpert = r.bExpert; - reload(); -} - -void GlobalShortcutConfig::save() const { - s.qlShortcuts = qlShortcuts; -} - -QTreeWidgetItem *GlobalShortcutConfig::itemForShortcut(const Shortcut &sc) const { - QTreeWidgetItem *item = new QTreeWidgetItem(); - item->setData(0, Qt::DisplayRole, sc.iIndex); - item->setData(1, Qt::DisplayRole, sc.qlButtons); - item->setCheckState(2, sc.bSuppress ? Qt::Checked : Qt::Unchecked); - item->setFlags(item->flags() | Qt::ItemIsEditable); - - ::GlobalShortcut *gs = GlobalShortcutEngine::engine->qmShortcuts.value(sc.iIndex); - - if (gs) { - if (! gs->qsToolTip.isEmpty()) - item->setData(0, Qt::ToolTipRole, gs->qsToolTip); - if (! gs->qsWhatsThis.isEmpty()) - item->setData(0, Qt::WhatsThisRole, gs->qsWhatsThis); - } - - item->setData(1, Qt::ToolTipRole, tr("Shortcut button combination.")); - item->setData(1, Qt::WhatsThisRole, tr("This is the global shortcut key combination.
" - "Double-click this field and then the desired key/button combo " - "to rebind.")); - - item->setData(2, Qt::ToolTipRole, tr("Suppress keys from other applications")); - item->setData(2, Qt::WhatsThisRole, tr("This hides the button presses from other applications.
" - "Enabling this will hide the button (or the last button of a multi-button combo) " - "from other applications. Note that not all buttons can be suppressed.")); - - return item; -} - -void GlobalShortcutConfig::reload() { - qStableSort(qlShortcuts); - qtwShortcuts->clear(); - foreach(const Shortcut &sc, qlShortcuts) { - QTreeWidgetItem *item = itemForShortcut(sc); - ::GlobalShortcut *gs = GlobalShortcutEngine::engine->qmShortcuts.value(sc.iIndex); - qtwShortcuts->addTopLevelItem(item); - if (gs && gs->bExpert && ! bExpert) - item->setHidden(true); - } -} - -void GlobalShortcutConfig::accept() const { - GlobalShortcutEngine::engine->bNeedRemap = true; - GlobalShortcutEngine::engine->needRemap(); -} - -bool GlobalShortcutConfig::expert(bool exp) { - bExpert = exp; - reload(); - return true; -} - - -GlobalShortcutEngine::GlobalShortcutEngine(QObject *p) : QThread(p) { - bNeedRemap = true; - needRemap(); -} - -GlobalShortcutEngine::~GlobalShortcutEngine() { - QSet qs; - foreach(const QList &ql, qlShortcutList) - qs += ql.toSet(); - - foreach(ShortcutKey *sk, qs) - delete sk; -} - -void GlobalShortcutEngine::remap() { - bNeedRemap = false; - - QSet qs; - foreach(const QList &ql, qlShortcutList) - qs += ql.toSet(); - - foreach(ShortcutKey *sk, qs) - delete sk; - - qlButtonList.clear(); - qlShortcutList.clear(); - qlDownButtons.clear(); - - foreach(const Shortcut &sc, g.s.qlShortcuts) { - GlobalShortcut *gs = qmShortcuts.value(sc.iIndex); - if (gs && ! sc.qlButtons.isEmpty()) { - ShortcutKey *sk = new ShortcutKey; - sk->s = sc; - sk->iNumUp = sc.qlButtons.count(); - sk->gs = gs; - - foreach(const QVariant &button, sc.qlButtons) { - int idx = qlButtonList.indexOf(button); - if (idx == -1) { - qlButtonList << button; - qlShortcutList << QList(); - idx = qlButtonList.count() - 1; - } - qlShortcutList[idx] << sk; - } - } - } -} - -void GlobalShortcutEngine::run() { -} - -bool GlobalShortcutEngine::canSuppress() { - return false; -} - -void GlobalShortcutEngine::resetMap() { - tReset.restart(); - qlActiveButtons.clear(); -} - -void GlobalShortcutEngine::needRemap() { -} - -bool GlobalShortcutEngine::handleButton(const QVariant &button, bool down) { - bool already = qlDownButtons.contains(button); - if (already == down) - return qlSuppressed.contains(button); - if (down) - qlDownButtons << button; - else - qlDownButtons.removeAll(button); - - if (tReset.elapsed() > 100000) { - if (down) { - qlActiveButtons.removeAll(button); - qlActiveButtons << button; - } - emit buttonPressed(! down); - } - - int idx = qlButtonList.indexOf(button); - if (idx == -1) - return false; - - bool suppress = false; - - foreach(ShortcutKey *sk, qlShortcutList.at(idx)) { - if (down) { - sk->iNumUp--; - if (sk->iNumUp == 0) { - GlobalShortcut *gs = sk->gs; - if (sk->s.bSuppress) { - suppress = true; - qlSuppressed << button; - } - if (! gs->bActive) { - gs->bActive = true; - emit gs->triggered(sk->gs->bActive); - emit gs->down(); - } - } else if (sk->iNumUp < 0) { - sk->iNumUp = 0; - } - } else { - if (qlSuppressed.contains(button)) { - suppress = true; - qlSuppressed.removeAll(button); - } - sk->iNumUp++; - if (sk->iNumUp == 1) { - GlobalShortcut *gs = sk->gs; - if (gs->bActive) { - gs->bActive = false; - emit gs->triggered(sk->gs->bActive); - emit gs->up(); - } - } else if (sk->iNumUp > sk->s.qlButtons.count()) { - sk->iNumUp = sk->s.qlButtons.count(); - } - } - } - return suppress; -} - -void GlobalShortcutEngine::add(GlobalShortcut *gs) { - if (! GlobalShortcutEngine::engine) - GlobalShortcutEngine::engine = GlobalShortcutEngine::platformInit(); - - GlobalShortcutEngine::engine->qmShortcuts.insert(gs->idx, gs); - GlobalShortcutEngine::engine->bNeedRemap = true; - GlobalShortcutEngine::engine->needRemap(); -} - -void GlobalShortcutEngine::remove(GlobalShortcut *gs) { - engine->qmShortcuts.remove(gs->idx); - engine->bNeedRemap = true; - engine->needRemap(); - if (engine->qmShortcuts.isEmpty()) { - delete engine; - GlobalShortcutEngine::engine = NULL; - } -} - -QString GlobalShortcutEngine::buttonText(const QList &list) { - QStringList keys; - - foreach(QVariant button, list) { - QString id = GlobalShortcutEngine::engine->buttonName(button); - if (! id.isEmpty()) - keys << id; - } - return keys.join(QLatin1String(" + ")); -} - -GlobalShortcut::GlobalShortcut(QObject *p, int index, QString qsName, bool expert) : QObject(p) { - idx = index; - name=qsName; - bExpert = expert; - bActive = false; - GlobalShortcutEngine::add(this); -} - -GlobalShortcut::~GlobalShortcut() { - GlobalShortcutEngine::remove(this); -} +/* Copyright (C) 2005-2009, Thorvald Natvig + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + - Neither the name of the Mumble Developers nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "GlobalShortcut.h" +#include "MainWindow.h" +#include "Global.h" + +GlobalShortcutEngine *GlobalShortcutEngine::engine = NULL; + +static ConfigWidget *GlobalShortcutConfigDialogNew(Settings &st) { + return new GlobalShortcutConfig(st); +} + +static ConfigRegistrar registrar(1200, GlobalShortcutConfigDialogNew); + +ShortcutKeyWidget::ShortcutKeyWidget(QWidget *p) : QLineEdit(p) { + setReadOnly(true); + clearFocus(); + bModified = false; + displayKeys(); +} + +QList ShortcutKeyWidget::getShortcut() const { + return qlButtons; +} + +void ShortcutKeyWidget::setShortcut(const QList &buttons) { + qlButtons = buttons; + displayKeys(); +} + +void ShortcutKeyWidget::focusInEvent(QFocusEvent *) { + setText(tr("Press Shortcut")); + + QPalette pal=parentWidget()->palette(); + pal.setColor(QPalette::Base, pal.color(QPalette::Base).dark(120)); + setPalette(pal); + + setForegroundRole(QPalette::Button); + GlobalShortcutEngine::engine->resetMap(); + connect(GlobalShortcutEngine::engine, SIGNAL(buttonPressed(bool)), this, SLOT(updateKeys(bool))); + installEventFilter(this); +} + +void ShortcutKeyWidget::focusOutEvent(QFocusEvent *e) { + if ((e->reason() == Qt::TabFocusReason) || (e->reason() == Qt::BacktabFocusReason)) + return; + + setPalette(parentWidget()->palette()); + clearFocus(); + disconnect(GlobalShortcutEngine::engine, SIGNAL(buttonPressed(bool)), this, SLOT(updateKeys(bool))); + displayKeys(); + removeEventFilter(this); +} + +bool ShortcutKeyWidget::eventFilter(QObject *, QEvent *evt) { + if ((evt->type() == QEvent::KeyPress) || (evt->type() == QEvent::MouseButtonPress)) + return true; + return false; +} + +void ShortcutKeyWidget::mouseDoubleClickEvent(QMouseEvent *) { + bModified = true; + qlButtons.clear(); + clearFocus(); + displayKeys(); +} + +void ShortcutKeyWidget::updateKeys(bool last) { + qlButtons = GlobalShortcutEngine::engine->qlActiveButtons; + bModified = true; + + if (qlButtons.isEmpty()) + return; + + if (last) + clearFocus(); + else + displayKeys(); +} + +void ShortcutKeyWidget::displayKeys() { + QStringList keys; + + foreach(QVariant button, qlButtons) { + QString id = GlobalShortcutEngine::engine->buttonName(button); + if (! id.isEmpty()) + keys << id; + } + setText(keys.join(QLatin1String(" + "))); + emit keySet(keys.count() > 0); +} + +ShortcutActionWidget::ShortcutActionWidget(QWidget *p) : QComboBox(p) { + int idx = 0; + + insertItem(idx, tr("Unassigned")); + setItemData(idx, -1); + + idx++; + + bool expert = true; + while (p) { + GlobalShortcutConfig *gsc = qobject_cast(p); + if (gsc) { + expert = gsc->bExpert; + break; + } + p = p->parentWidget(); + } + foreach(GlobalShortcut *gs, GlobalShortcutEngine::engine->qmShortcuts) { + if (expert || ! gs->bExpert) { + insertItem(idx, gs->name); + setItemData(idx, gs->idx); + if (! gs->qsToolTip.isEmpty()) + setItemData(idx, gs->qsToolTip, Qt::ToolTipRole); + if (! gs->qsWhatsThis.isEmpty()) + setItemData(idx, gs->qsWhatsThis, Qt::WhatsThisRole); + idx++; + } + } +} + +void ShortcutActionWidget::setIndex(int idx) { + setCurrentIndex(findData(idx)); +} + +int ShortcutActionWidget::index() const { + return itemData(currentIndex()).toInt(); +} + +ShortcutDelegate::ShortcutDelegate(QObject *p) : QStyledItemDelegate(p) { + QItemEditorFactory *factory = new QItemEditorFactory; + QItemEditorCreatorBase *shortcutCreator = new QStandardItemEditorCreator(); + QItemEditorCreatorBase *indexCreator = new QStandardItemEditorCreator(); + + factory->registerEditor(QVariant::List, shortcutCreator); + factory->registerEditor(QVariant::Int, indexCreator); + setItemEditorFactory(factory); +} + +ShortcutDelegate::~ShortcutDelegate() { + delete itemEditorFactory(); + setItemEditorFactory(NULL); +} + +QString ShortcutDelegate::displayText(const QVariant &item, const QLocale &loc) const { + if (item.type() == QVariant::List) + return GlobalShortcutEngine::buttonText(item.toList()); + else if (item.type() == QVariant::Int) { + GlobalShortcut *gs = GlobalShortcutEngine::engine->qmShortcuts.value(item.toInt()); + if (gs) + return gs->name; + else + return tr("Unassigned"); + } + + return QStyledItemDelegate::displayText(item,loc); +} + +GlobalShortcutConfig::GlobalShortcutConfig(Settings &st) : ConfigWidget(st) { + setupUi(this); + + bool canSuppress = GlobalShortcutEngine::engine->canSuppress(); + + qtwShortcuts->setColumnCount(canSuppress ? 3 : 2); + qtwShortcuts->setItemDelegate(new ShortcutDelegate(qtwShortcuts)); +} + +void GlobalShortcutConfig::on_qpbAdd_clicked(bool) { + Shortcut sc; + sc.iIndex = -1; + sc.bSuppress = false; + qlShortcuts << sc; + reload(); +} + +void GlobalShortcutConfig::on_qpbRemove_clicked(bool) { + QTreeWidgetItem *qtwi = qtwShortcuts->currentItem(); + if (! qtwi) + return; + qlShortcuts.removeAt(qtwShortcuts->indexOfTopLevelItem(qtwi)); + delete qtwi; +} + +void GlobalShortcutConfig::on_qtwShortcuts_currentItemChanged(QTreeWidgetItem *item, QTreeWidgetItem *) { + qpbRemove->setEnabled(item ? true : false); +} + +void GlobalShortcutConfig::on_qtwShortcuts_itemChanged(QTreeWidgetItem *item, int) { + int idx = qtwShortcuts->indexOfTopLevelItem(item); + Shortcut &sc = qlShortcuts[idx]; + sc.iIndex = item->data(0, Qt::DisplayRole).toInt(); + sc.qlButtons = item->data(1, Qt::DisplayRole).toList(); + sc.bSuppress = item->checkState(2) == Qt::Checked; +} + +QString GlobalShortcutConfig::title() const { + return tr("Shortcuts"); +} + +QIcon GlobalShortcutConfig::icon() const { + return QIcon(QLatin1String("skin:config_shortcuts.png")); +} + +void GlobalShortcutConfig::load(const Settings &r) { + qlShortcuts = r.qlShortcuts; + bExpert = r.bExpert; + reload(); +} + +void GlobalShortcutConfig::save() const { + s.qlShortcuts = qlShortcuts; +} + +QTreeWidgetItem *GlobalShortcutConfig::itemForShortcut(const Shortcut &sc) const { + QTreeWidgetItem *item = new QTreeWidgetItem(); + item->setData(0, Qt::DisplayRole, sc.iIndex); + item->setData(1, Qt::DisplayRole, sc.qlButtons); + item->setCheckState(2, sc.bSuppress ? Qt::Checked : Qt::Unchecked); + item->setFlags(item->flags() | Qt::ItemIsEditable); + + ::GlobalShortcut *gs = GlobalShortcutEngine::engine->qmShortcuts.value(sc.iIndex); + + if (gs) { + if (! gs->qsToolTip.isEmpty()) + item->setData(0, Qt::ToolTipRole, gs->qsToolTip); + if (! gs->qsWhatsThis.isEmpty()) + item->setData(0, Qt::WhatsThisRole, gs->qsWhatsThis); + } + + item->setData(1, Qt::ToolTipRole, tr("Shortcut button combination.")); + item->setData(1, Qt::WhatsThisRole, tr("This is the global shortcut key combination.
" + "Double-click this field and then the desired key/button combo " + "to rebind.")); + + item->setData(2, Qt::ToolTipRole, tr("Suppress keys from other applications")); + item->setData(2, Qt::WhatsThisRole, tr("This hides the button presses from other applications.
" + "Enabling this will hide the button (or the last button of a multi-button combo) " + "from other applications. Note that not all buttons can be suppressed.")); + + return item; +} + +void GlobalShortcutConfig::reload() { + qStableSort(qlShortcuts); + qtwShortcuts->clear(); + foreach(const Shortcut &sc, qlShortcuts) { + QTreeWidgetItem *item = itemForShortcut(sc); + ::GlobalShortcut *gs = GlobalShortcutEngine::engine->qmShortcuts.value(sc.iIndex); + qtwShortcuts->addTopLevelItem(item); + if (gs && gs->bExpert && ! bExpert) + item->setHidden(true); + } +} + +void GlobalShortcutConfig::accept() const { + GlobalShortcutEngine::engine->bNeedRemap = true; + GlobalShortcutEngine::engine->needRemap(); +} + +bool GlobalShortcutConfig::expert(bool exp) { + bExpert = exp; + reload(); + return true; +} + + +GlobalShortcutEngine::GlobalShortcutEngine(QObject *p) : QThread(p) { + bNeedRemap = true; + needRemap(); +} + +GlobalShortcutEngine::~GlobalShortcutEngine() { + QSet qs; + foreach(const QList &ql, qlShortcutList) + qs += ql.toSet(); + + foreach(ShortcutKey *sk, qs) + delete sk; +} + +void GlobalShortcutEngine::remap() { + bNeedRemap = false; + + QSet qs; + foreach(const QList &ql, qlShortcutList) + qs += ql.toSet(); + + foreach(ShortcutKey *sk, qs) + delete sk; + + qlButtonList.clear(); + qlShortcutList.clear(); + qlDownButtons.clear(); + + foreach(const Shortcut &sc, g.s.qlShortcuts) { + GlobalShortcut *gs = qmShortcuts.value(sc.iIndex); + if (gs && ! sc.qlButtons.isEmpty()) { + ShortcutKey *sk = new ShortcutKey; + sk->s = sc; + sk->iNumUp = sc.qlButtons.count(); + sk->gs = gs; + + foreach(const QVariant &button, sc.qlButtons) { + int idx = qlButtonList.indexOf(button); + if (idx == -1) { + qlButtonList << button; + qlShortcutList << QList(); + idx = qlButtonList.count() - 1; + } + qlShortcutList[idx] << sk; + } + } + } +} + +void GlobalShortcutEngine::run() { +} + +bool GlobalShortcutEngine::canSuppress() { + return false; +} + +void GlobalShortcutEngine::resetMap() { + tReset.restart(); + qlActiveButtons.clear(); +} + +void GlobalShortcutEngine::needRemap() { +} + +bool GlobalShortcutEngine::handleButton(const QVariant &button, bool down) { + bool already = qlDownButtons.contains(button); + if (already == down) + return qlSuppressed.contains(button); + if (down) + qlDownButtons << button; + else + qlDownButtons.removeAll(button); + + if (tReset.elapsed() > 100000) { + if (down) { + qlActiveButtons.removeAll(button); + qlActiveButtons << button; + } + emit buttonPressed(! down); + } + + int idx = qlButtonList.indexOf(button); + if (idx == -1) + return false; + + bool suppress = false; + + foreach(ShortcutKey *sk, qlShortcutList.at(idx)) { + if (down) { + sk->iNumUp--; + if (sk->iNumUp == 0) { + GlobalShortcut *gs = sk->gs; + if (sk->s.bSuppress) { + suppress = true; + qlSuppressed << button; + } + if (! gs->bActive) { + gs->bActive = true; + emit gs->triggered(sk->gs->bActive); + emit gs->down(); + } + } else if (sk->iNumUp < 0) { + sk->iNumUp = 0; + } + } else { + if (qlSuppressed.contains(button)) { + suppress = true; + qlSuppressed.removeAll(button); + } + sk->iNumUp++; + if (sk->iNumUp == 1) { + GlobalShortcut *gs = sk->gs; + if (gs->bActive) { + gs->bActive = false; + emit gs->triggered(sk->gs->bActive); + emit gs->up(); + } + } else if (sk->iNumUp > sk->s.qlButtons.count()) { + sk->iNumUp = sk->s.qlButtons.count(); + } + } + } + return suppress; +} + +void GlobalShortcutEngine::add(GlobalShortcut *gs) { + if (! GlobalShortcutEngine::engine) + GlobalShortcutEngine::engine = GlobalShortcutEngine::platformInit(); + + GlobalShortcutEngine::engine->qmShortcuts.insert(gs->idx, gs); + GlobalShortcutEngine::engine->bNeedRemap = true; + GlobalShortcutEngine::engine->needRemap(); +} + +void GlobalShortcutEngine::remove(GlobalShortcut *gs) { + engine->qmShortcuts.remove(gs->idx); + engine->bNeedRemap = true; + engine->needRemap(); + if (engine->qmShortcuts.isEmpty()) { + delete engine; + GlobalShortcutEngine::engine = NULL; + } +} + +QString GlobalShortcutEngine::buttonText(const QList &list) { + QStringList keys; + + foreach(QVariant button, list) { + QString id = GlobalShortcutEngine::engine->buttonName(button); + if (! id.isEmpty()) + keys << id; + } + return keys.join(QLatin1String(" + ")); +} + +GlobalShortcut::GlobalShortcut(QObject *p, int index, QString qsName, bool expert) : QObject(p) { + idx = index; + name=qsName; + bExpert = expert; + bActive = false; + GlobalShortcutEngine::add(this); +} + +GlobalShortcut::~GlobalShortcut() { + GlobalShortcutEngine::remove(this); +} diff --git a/src/mumble/GlobalShortcut_win.cpp b/src/mumble/GlobalShortcut_win.cpp index 30bfc8f23..0130b07de 100644 --- a/src/mumble/GlobalShortcut_win.cpp +++ b/src/mumble/GlobalShortcut_win.cpp @@ -1,300 +1,300 @@ -/* Copyright (C) 2005-2009, Thorvald Natvig - - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - - Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - Neither the name of the Mumble Developers nor the names of its - contributors may be used to endorse or promote products derived from this - software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include "GlobalShortcut_win.h" -#include "MainWindow.h" -#include "Global.h" - -#undef FAILED -#define FAILED(Status) (static_cast(Status)<0) - -#define DX_SAMPLE_BUFFER_SIZE 512 - -uint qHash(const GUID &a) { - uint val = a.Data1 ^ a.Data2 ^ a.Data3; - for (int i=0;i<8;i++) - val += a.Data4[i]; - return val; -} - -GlobalShortcutEngine *GlobalShortcutEngine::platformInit() { - return new GlobalShortcutWin(); -} - -GlobalShortcutWin::GlobalShortcutWin() { - pDI = NULL; - moveToThread(this); - start(QThread::LowestPriority); -} - -GlobalShortcutWin::~GlobalShortcutWin() { - quit(); - wait(); -} - -void GlobalShortcutWin::run() { - HMODULE hSelf; - HRESULT hr; - QTimer *timer; - - if (FAILED(hr = DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, reinterpret_cast(&pDI), NULL))) { - qFatal("GlobalShortcutWin: Failed to create d8input"); - return; - } - - /* - * Wait for MainWindow's constructor to finish before we enumerate DirectInput devices. - * We need to do this because adding a new device requires a Window handle. (SetCooperativeLevel()) - */ - while (! g.mw) - this->msleep(20); - - pDI->EnumDevices(DI8DEVCLASS_ALL, EnumDevicesCB, static_cast(this), DIEDFL_ALLDEVICES); - - GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (wchar_t *) &HookKeyboard, &hSelf); - hhKeyboard = SetWindowsHookEx(WH_KEYBOARD_LL, HookKeyboard, hSelf, 0); - hhMouse = SetWindowsHookEx(WH_MOUSE_LL, HookMouse, hSelf, 0); - - timer = new QTimer(this); - connect(timer, SIGNAL(timeout()), this, SLOT(timeTicked())); - timer->start(20); - - setPriority(QThread::TimeCriticalPriority); - - exec(); - - UnhookWindowsHookEx(hhKeyboard); - UnhookWindowsHookEx(hhMouse); - - foreach(InputDevice *id, qhInputDevices) { - if (id->pDID) { - id->pDID->Unacquire(); - id->pDID->Release(); - } - } - pDI->Release(); -} - -LRESULT CALLBACK GlobalShortcutWin::HookKeyboard(int nCode, WPARAM wParam, LPARAM lParam) { - GlobalShortcutWin *gsw=static_cast(engine); - KBDLLHOOKSTRUCT *key=reinterpret_cast(lParam); - if (nCode >= 0) { - QList ql; - unsigned int keyid = static_cast((key->scanCode << 8) | 0x4); - if (key->flags & LLKHF_EXTENDED) - keyid |= 0x8000U; - ql << keyid; - ql << QVariant(QUuid(GUID_SysKeyboard)); - if (gsw->handleButton(ql, !(key->flags & LLKHF_UP))) - return 1; - } - return CallNextHookEx(gsw->hhKeyboard, nCode, wParam, lParam); -} - -LRESULT CALLBACK GlobalShortcutWin::HookMouse(int nCode, WPARAM wParam, LPARAM lParam) { - GlobalShortcutWin *gsw=static_cast(engine); - MSLLHOOKSTRUCT *mouse=reinterpret_cast(lParam); - if (nCode >= 0) { - bool down = false; - unsigned int btn = 0; - switch (wParam) { - case WM_LBUTTONDOWN: - down = true; - case WM_LBUTTONUP: - btn = 3; - break; - case WM_RBUTTONDOWN: - down = true; - case WM_RBUTTONUP: - btn = 4; - break; - case WM_MBUTTONDOWN: - down = true; - case WM_MBUTTONUP: - btn = 5; - break; - case WM_XBUTTONDOWN: - down = true; - case WM_XBUTTONUP: - btn = 5 + (mouse->mouseData >> 16); - default: - break; - } - if (btn) { - QList ql; - ql << static_cast((btn << 8) | 0x4); - ql << QVariant(QUuid(GUID_SysMouse)); - if (gsw->handleButton(ql, down)) - return 1; - } - } - return CallNextHookEx(gsw->hhMouse, nCode, wParam, lParam); -} - -BOOL CALLBACK GlobalShortcutWin::EnumDeviceObjectsCallback(LPCDIDEVICEOBJECTINSTANCE lpddoi, LPVOID pvRef) { - InputDevice *id=static_cast(pvRef); - QString name = QString::fromUtf16(reinterpret_cast(lpddoi->tszName)); - id->qhNames[lpddoi->dwType] = name; - - return DIENUM_CONTINUE; -} - -BOOL GlobalShortcutWin::EnumDevicesCB(LPCDIDEVICEINSTANCE pdidi, LPVOID pContext) { - GlobalShortcutWin *cbgsw=static_cast(pContext); - HRESULT hr; - - QString name = QString::fromUtf16(reinterpret_cast(pdidi->tszProductName)); - QString sname = QString::fromUtf16(reinterpret_cast(pdidi->tszInstanceName)); - - InputDevice *id = new InputDevice; - id->name = name; - id->guid = pdidi->guidInstance; - id->vguid = QVariant(QUuid(id->guid).toString()); - - if (FAILED(hr = cbgsw->pDI->CreateDevice(pdidi->guidInstance, &id->pDID, NULL))) - qFatal("GlobalShortcutWin: CreateDevice: %lx", hr); - - if (FAILED(hr = id->pDID->EnumObjects(EnumDeviceObjectsCallback, static_cast(id), DIDFT_BUTTON))) - qFatal("GlobalShortcutWin: EnumObjects: %lx", hr); - - if (id->qhNames.count() > 0) { - QList types = id->qhNames.keys(); - qSort(types); - - int nbuttons = types.count(); - DIOBJECTDATAFORMAT *rgodf = new DIOBJECTDATAFORMAT[nbuttons]; - DIDATAFORMAT df; - ZeroMemory(&df, sizeof(df)); - df.dwSize = sizeof(df); - df.dwObjSize = sizeof(DIOBJECTDATAFORMAT); - df.dwFlags=DIDF_ABSAXIS; - df.dwDataSize = (nbuttons + 3) & (~0x3); - df.dwNumObjs = nbuttons; - df.rgodf = rgodf; - for (int i=0;iqhOfsToType[dwOfs] = dwType; - id->qhTypeToOfs[dwType] = dwOfs; - } - - if (FAILED(hr = id->pDID->SetCooperativeLevel(g.mw->winId(), DISCL_NONEXCLUSIVE|DISCL_BACKGROUND))) - qFatal("GlobalShortcutWin: SetCooperativeLevel: %lx", hr); - - if (FAILED(hr = id->pDID->SetDataFormat(&df))) - qFatal("GlobalShortcutWin: SetDataFormat: %lx", hr); - - DIPROPDWORD dipdw; - - dipdw.diph.dwSize = sizeof(DIPROPDWORD); - dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER); - dipdw.diph.dwObj = 0; - dipdw.diph.dwHow = DIPH_DEVICE; - dipdw.dwData = DX_SAMPLE_BUFFER_SIZE; - - if (FAILED(hr = id->pDID->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph))) - qFatal("GlobalShortcutWin::SetProperty"); - - qWarning("Adding device %s %s %s:%d", qPrintable(QUuid(id->guid).toString()),qPrintable(name),qPrintable(sname),id->qhNames.count()); - - cbgsw->qhInputDevices[id->guid] = id; - } else { - id->pDID->Release(); - delete(id); - } - - return DIENUM_CONTINUE; -} - -void GlobalShortcutWin::timeTicked() { - if (bNeedRemap) - remap(); - - foreach(InputDevice *id, qhInputDevices) { - DIDEVICEOBJECTDATA rgdod[DX_SAMPLE_BUFFER_SIZE]; - DWORD dwItems = DX_SAMPLE_BUFFER_SIZE; - HRESULT hr; - - id->pDID->Acquire(); - id->pDID->Poll(); - - hr = id->pDID->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), rgdod, &dwItems, 0); - if (FAILED(hr)) - continue; - - if (dwItems <= 0) - continue; - - for (DWORD j=0; j ql; - - quint32 uiType = id->qhOfsToType.value(rgdod[j].dwOfs); - ql << uiType; - ql << id->vguid; - handleButton(ql, rgdod[j].dwData & 0x80); - } - } -} - -QString GlobalShortcutWin::buttonName(const QVariant &v) { - GlobalShortcutWin *gsw = static_cast(GlobalShortcutEngine::engine); - - const QList &sublist = v.toList(); - if (sublist.count() != 2) - return QString(); - - bool ok = false; - DWORD type = sublist.at(0).toUInt(&ok); - QUuid guid(sublist.at(1).toString()); - - if (guid.isNull() || (!ok)) - return QString(); - - QString device=guid.toString(); - QString name=QLatin1String("Unknown"); - InputDevice *id = gsw->qhInputDevices.value(guid); - if (guid == GUID_SysMouse) - device=QLatin1String("M:"); - else if (guid == GUID_SysKeyboard) - device=QLatin1String("K:"); - else if (id) - device=id->name+QLatin1String(":"); - if (id) { - name=id->qhNames.value(type); - } - return device+name; -} - -bool GlobalShortcutWin::canSuppress() { - return true; -} +/* Copyright (C) 2005-2009, Thorvald Natvig + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + - Neither the name of the Mumble Developers nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "GlobalShortcut_win.h" +#include "MainWindow.h" +#include "Global.h" + +#undef FAILED +#define FAILED(Status) (static_cast(Status)<0) + +#define DX_SAMPLE_BUFFER_SIZE 512 + +uint qHash(const GUID &a) { + uint val = a.Data1 ^ a.Data2 ^ a.Data3; + for (int i=0;i<8;i++) + val += a.Data4[i]; + return val; +} + +GlobalShortcutEngine *GlobalShortcutEngine::platformInit() { + return new GlobalShortcutWin(); +} + +GlobalShortcutWin::GlobalShortcutWin() { + pDI = NULL; + moveToThread(this); + start(QThread::LowestPriority); +} + +GlobalShortcutWin::~GlobalShortcutWin() { + quit(); + wait(); +} + +void GlobalShortcutWin::run() { + HMODULE hSelf; + HRESULT hr; + QTimer *timer; + + if (FAILED(hr = DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, reinterpret_cast(&pDI), NULL))) { + qFatal("GlobalShortcutWin: Failed to create d8input"); + return; + } + + /* + * Wait for MainWindow's constructor to finish before we enumerate DirectInput devices. + * We need to do this because adding a new device requires a Window handle. (SetCooperativeLevel()) + */ + while (! g.mw) + this->msleep(20); + + pDI->EnumDevices(DI8DEVCLASS_ALL, EnumDevicesCB, static_cast(this), DIEDFL_ALLDEVICES); + + GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (wchar_t *) &HookKeyboard, &hSelf); + hhKeyboard = SetWindowsHookEx(WH_KEYBOARD_LL, HookKeyboard, hSelf, 0); + hhMouse = SetWindowsHookEx(WH_MOUSE_LL, HookMouse, hSelf, 0); + + timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(timeTicked())); + timer->start(20); + + setPriority(QThread::TimeCriticalPriority); + + exec(); + + UnhookWindowsHookEx(hhKeyboard); + UnhookWindowsHookEx(hhMouse); + + foreach(InputDevice *id, qhInputDevices) { + if (id->pDID) { + id->pDID->Unacquire(); + id->pDID->Release(); + } + } + pDI->Release(); +} + +LRESULT CALLBACK GlobalShortcutWin::HookKeyboard(int nCode, WPARAM wParam, LPARAM lParam) { + GlobalShortcutWin *gsw=static_cast(engine); + KBDLLHOOKSTRUCT *key=reinterpret_cast(lParam); + if (nCode >= 0) { + QList ql; + unsigned int keyid = static_cast((key->scanCode << 8) | 0x4); + if (key->flags & LLKHF_EXTENDED) + keyid |= 0x8000U; + ql << keyid; + ql << QVariant(QUuid(GUID_SysKeyboard)); + if (gsw->handleButton(ql, !(key->flags & LLKHF_UP))) + return 1; + } + return CallNextHookEx(gsw->hhKeyboard, nCode, wParam, lParam); +} + +LRESULT CALLBACK GlobalShortcutWin::HookMouse(int nCode, WPARAM wParam, LPARAM lParam) { + GlobalShortcutWin *gsw=static_cast(engine); + MSLLHOOKSTRUCT *mouse=reinterpret_cast(lParam); + if (nCode >= 0) { + bool down = false; + unsigned int btn = 0; + switch (wParam) { + case WM_LBUTTONDOWN: + down = true; + case WM_LBUTTONUP: + btn = 3; + break; + case WM_RBUTTONDOWN: + down = true; + case WM_RBUTTONUP: + btn = 4; + break; + case WM_MBUTTONDOWN: + down = true; + case WM_MBUTTONUP: + btn = 5; + break; + case WM_XBUTTONDOWN: + down = true; + case WM_XBUTTONUP: + btn = 5 + (mouse->mouseData >> 16); + default: + break; + } + if (btn) { + QList ql; + ql << static_cast((btn << 8) | 0x4); + ql << QVariant(QUuid(GUID_SysMouse)); + if (gsw->handleButton(ql, down)) + return 1; + } + } + return CallNextHookEx(gsw->hhMouse, nCode, wParam, lParam); +} + +BOOL CALLBACK GlobalShortcutWin::EnumDeviceObjectsCallback(LPCDIDEVICEOBJECTINSTANCE lpddoi, LPVOID pvRef) { + InputDevice *id=static_cast(pvRef); + QString name = QString::fromUtf16(reinterpret_cast(lpddoi->tszName)); + id->qhNames[lpddoi->dwType] = name; + + return DIENUM_CONTINUE; +} + +BOOL GlobalShortcutWin::EnumDevicesCB(LPCDIDEVICEINSTANCE pdidi, LPVOID pContext) { + GlobalShortcutWin *cbgsw=static_cast(pContext); + HRESULT hr; + + QString name = QString::fromUtf16(reinterpret_cast(pdidi->tszProductName)); + QString sname = QString::fromUtf16(reinterpret_cast(pdidi->tszInstanceName)); + + InputDevice *id = new InputDevice; + id->name = name; + id->guid = pdidi->guidInstance; + id->vguid = QVariant(QUuid(id->guid).toString()); + + if (FAILED(hr = cbgsw->pDI->CreateDevice(pdidi->guidInstance, &id->pDID, NULL))) + qFatal("GlobalShortcutWin: CreateDevice: %lx", hr); + + if (FAILED(hr = id->pDID->EnumObjects(EnumDeviceObjectsCallback, static_cast(id), DIDFT_BUTTON))) + qFatal("GlobalShortcutWin: EnumObjects: %lx", hr); + + if (id->qhNames.count() > 0) { + QList types = id->qhNames.keys(); + qSort(types); + + int nbuttons = types.count(); + DIOBJECTDATAFORMAT *rgodf = new DIOBJECTDATAFORMAT[nbuttons]; + DIDATAFORMAT df; + ZeroMemory(&df, sizeof(df)); + df.dwSize = sizeof(df); + df.dwObjSize = sizeof(DIOBJECTDATAFORMAT); + df.dwFlags=DIDF_ABSAXIS; + df.dwDataSize = (nbuttons + 3) & (~0x3); + df.dwNumObjs = nbuttons; + df.rgodf = rgodf; + for (int i=0;iqhOfsToType[dwOfs] = dwType; + id->qhTypeToOfs[dwType] = dwOfs; + } + + if (FAILED(hr = id->pDID->SetCooperativeLevel(g.mw->winId(), DISCL_NONEXCLUSIVE|DISCL_BACKGROUND))) + qFatal("GlobalShortcutWin: SetCooperativeLevel: %lx", hr); + + if (FAILED(hr = id->pDID->SetDataFormat(&df))) + qFatal("GlobalShortcutWin: SetDataFormat: %lx", hr); + + DIPROPDWORD dipdw; + + dipdw.diph.dwSize = sizeof(DIPROPDWORD); + dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER); + dipdw.diph.dwObj = 0; + dipdw.diph.dwHow = DIPH_DEVICE; + dipdw.dwData = DX_SAMPLE_BUFFER_SIZE; + + if (FAILED(hr = id->pDID->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph))) + qFatal("GlobalShortcutWin::SetProperty"); + + qWarning("Adding device %s %s %s:%d", qPrintable(QUuid(id->guid).toString()),qPrintable(name),qPrintable(sname),id->qhNames.count()); + + cbgsw->qhInputDevices[id->guid] = id; + } else { + id->pDID->Release(); + delete(id); + } + + return DIENUM_CONTINUE; +} + +void GlobalShortcutWin::timeTicked() { + if (bNeedRemap) + remap(); + + foreach(InputDevice *id, qhInputDevices) { + DIDEVICEOBJECTDATA rgdod[DX_SAMPLE_BUFFER_SIZE]; + DWORD dwItems = DX_SAMPLE_BUFFER_SIZE; + HRESULT hr; + + id->pDID->Acquire(); + id->pDID->Poll(); + + hr = id->pDID->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), rgdod, &dwItems, 0); + if (FAILED(hr)) + continue; + + if (dwItems <= 0) + continue; + + for (DWORD j=0; j ql; + + quint32 uiType = id->qhOfsToType.value(rgdod[j].dwOfs); + ql << uiType; + ql << id->vguid; + handleButton(ql, rgdod[j].dwData & 0x80); + } + } +} + +QString GlobalShortcutWin::buttonName(const QVariant &v) { + GlobalShortcutWin *gsw = static_cast(GlobalShortcutEngine::engine); + + const QList &sublist = v.toList(); + if (sublist.count() != 2) + return QString(); + + bool ok = false; + DWORD type = sublist.at(0).toUInt(&ok); + QUuid guid(sublist.at(1).toString()); + + if (guid.isNull() || (!ok)) + return QString(); + + QString device=guid.toString(); + QString name=QLatin1String("Unknown"); + InputDevice *id = gsw->qhInputDevices.value(guid); + if (guid == GUID_SysMouse) + device=QLatin1String("M:"); + else if (guid == GUID_SysKeyboard) + device=QLatin1String("K:"); + else if (id) + device=id->name+QLatin1String(":"); + if (id) { + name=id->qhNames.value(type); + } + return device+name; +} + +bool GlobalShortcutWin::canSuppress() { + return true; +} diff --git a/src/mumble/LookConfig.h b/src/mumble/LookConfig.h index 867f3d33d..45982c6b3 100644 --- a/src/mumble/LookConfig.h +++ b/src/mumble/LookConfig.h @@ -1,57 +1,57 @@ -/* Copyright (C) 2005-2009, Thorvald Natvig - - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - - Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - Neither the name of the Mumble Developers nor the names of its - contributors may be used to endorse or promote products derived from this - software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef _LOOKCONFIG_H -#define _LOOKCONFIG_H - -#include "ConfigDialog.h" - -#include "ui_LookConfig.h" - -class LookConfig : public ConfigWidget, Ui::LookConfig { - private: - Q_OBJECT - Q_DISABLE_COPY(LookConfig) - public: - LookConfig(Settings &st); - virtual QString title() const; - virtual QIcon icon() const; - public slots: - void accept() const; - void save() const; - void load(const Settings &r); - bool expert(bool); - void on_qpbSkinFile_clicked(bool); - void on_qcbExpert_clicked(bool); -}; - -#else -class LookConfig; -#endif +/* Copyright (C) 2005-2009, Thorvald Natvig + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + - Neither the name of the Mumble Developers nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _LOOKCONFIG_H +#define _LOOKCONFIG_H + +#include "ConfigDialog.h" + +#include "ui_LookConfig.h" + +class LookConfig : public ConfigWidget, Ui::LookConfig { + private: + Q_OBJECT + Q_DISABLE_COPY(LookConfig) + public: + LookConfig(Settings &st); + virtual QString title() const; + virtual QIcon icon() const; + public slots: + void accept() const; + void save() const; + void load(const Settings &r); + bool expert(bool); + void on_qpbSkinFile_clicked(bool); + void on_qcbExpert_clicked(bool); +}; + +#else +class LookConfig; +#endif diff --git a/src/mumble/MainWindow.cpp b/src/mumble/MainWindow.cpp index ab483c90d..45aceea80 100644 --- a/src/mumble/MainWindow.cpp +++ b/src/mumble/MainWindow.cpp @@ -227,10 +227,6 @@ void MainWindow::createActions() { qstiIcon->setToolTip(tr("Mumble")); qstiIcon->setObjectName(QLatin1String("Icon")); - qmTray = new QMenu(this); - qmTray->addAction(tr("&Quit"), this, SLOT(on_qaQuit_triggered())); - qstiIcon->setContextMenu(qmTray); - updateTrayIcon(); #ifndef Q_OS_MAC qstiIcon->show(); #endif @@ -291,6 +287,14 @@ void MainWindow::setupGui() { qtvPlayers->header()->restoreState(g.s.qbaHeaderState); setupView(false); + + qmTray = new QMenu(this); + qmTray->addAction(qaAudioMute); + qmTray->addAction(qaAudioDeaf); + qmTray->addSeparator(); + qmTray->addAction(qaQuit); + qstiIcon->setContextMenu(qmTray); + updateTrayIcon(); } MainWindow::~MainWindow() { diff --git a/src/mumble/MumbleScript.cpp b/src/mumble/MumbleScript.cpp index 8d86ec257..d3c0d1c6a 100644 --- a/src/mumble/MumbleScript.cpp +++ b/src/mumble/MumbleScript.cpp @@ -1,311 +1,311 @@ -/* Copyright (C) 2005-2009, Thorvald Natvig - - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - - Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - Neither the name of the Mumble Developers nor the names of its - contributors may be used to endorse or promote products derived from this - software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include "MumbleScript.h" -#include "Global.h" -#include "Log.h" -#include "ServerHandler.h" - -ScriptPlayer::ScriptPlayer(ClientPlayer *p) : QObject(p) { -} - -QString ScriptPlayer::getName() const { - ClientPlayer *cp = qobject_cast(parent()); - return cp->qsName; -} - -unsigned int ScriptPlayer::getSession() const { - ClientPlayer *cp = qobject_cast(parent()); - return cp->uiSession; -} - -int ScriptPlayer::getId() const { - ClientPlayer *cp = qobject_cast(parent()); - return cp->iId; -} - -QScriptValue ScriptPlayer::getChannel() const { - ClientPlayer *cp = qobject_cast(parent()); - Channel *c = cp->cChannel; - QScriptEngine *qse = engine(); - MumbleScript *ms = qobject_cast(qse->parent()); - return qse->newQObject(ms->qmChannels.value(Channel::get(0)), QScriptEngine::QtOwnership, QScriptEngine::PreferExistingWrapperObject); -} - -bool ScriptPlayer::isMute() const { - ClientPlayer *cp = qobject_cast(parent()); - return cp->bMute || cp->bDeaf; -} - -bool ScriptPlayer::isDeaf() const { - ClientPlayer *cp = qobject_cast(parent()); - return cp->bDeaf; -} - -void ScriptPlayer::setMute(bool m) { - ClientPlayer *cp = qobject_cast(parent()); - qWarning("Want to set mute to %d", m); -} - -void ScriptPlayer::setDeaf(bool m) { - ClientPlayer *cp = qobject_cast(parent()); - qWarning("Want to set deaf to %d", m); -} - -void ScriptPlayer::setChannel(const QScriptValue &sv) { - ClientPlayer *cp = qobject_cast(parent()); - ScriptChannel *sc = qobject_cast(sv.toQObject()); - if (sc) { - Channel *c = qobject_cast(sc->parent()); - qWarning("Want to set channel to %d", c->iId); - } else { - context()->throwError(QScriptContext::ReferenceError, QLatin1String("argument is not a channel")); - } -} - -void ScriptPlayer::sendMessage(const QString &msg) { - ClientPlayer *cp = qobject_cast(parent()); - qWarning("Want to send msg %s", qPrintable(msg)); -} - -ScriptChannel::ScriptChannel(Channel *c) : QObject(c) { -} - -QString ScriptChannel::getName() const { - Channel *c = qobject_cast(parent()); - return c->qsName; -} - -int ScriptChannel::getId() const { - Channel *c = qobject_cast(parent()); - return c->iId; -} - -int ScriptChannel::getParent() const { - Channel *c = qobject_cast(parent()); - return c->cParent ? c->cParent->iId : -1; -} - -void ScriptChannel::setName(const QString &name) { - Channel *c = qobject_cast(parent()); - qWarning() << "ScriptChannel::setName" << name; -} - -void ScriptChannel::setParent(int id) { - Channel *c = qobject_cast(parent()); - qWarning() << "ScriptChannel::setParent" << id; -} - -void ScriptChannel::sendMessage(const QString &msg, bool tree) { - Channel *c = qobject_cast(parent()); - qWarning() << "ScriptChannel::sendMessage" << msg << tree; -} - -QScriptValue ScriptChannel::getPlayers() const { - Channel *c = qobject_cast(parent()); - QScriptEngine *qse = engine(); - MumbleScript *ms = qobject_cast(qse->parent()); - - QScriptValue a = engine()->newArray(c->qlPlayers.count()); - - quint32 idx = 0; - - foreach(Player *p, c->qlPlayers) { - ClientPlayer *cp = static_cast(p); - QScriptValue sv = qse->newQObject(ms->qmPlayers.value(cp), QScriptEngine::QtOwnership, QScriptEngine::PreferExistingWrapperObject); - a.setProperty(idx, sv); - idx++; - } - return a; -} - -QScriptValue ScriptChannel::getChildren() const { - Channel *c = qobject_cast(parent()); - QScriptEngine *qse = engine(); - MumbleScript *ms = qobject_cast(qse->parent()); - - QScriptValue a = engine()->newArray(c->qlChannels.count()); - - quint32 idx = 0; - - foreach(Channel *child, c->qlChannels) { - QScriptValue sv = qse->newQObject(ms->qmChannels.value(child), QScriptEngine::QtOwnership, QScriptEngine::PreferExistingWrapperObject); - a.setProperty(idx, sv); - idx++; - } - return a; -} - -ScriptServer::ScriptServer(QObject *p) : QObject(p) { -} - -QScriptValue ScriptServer::getRoot() const { - qWarning("getRoot())"); - Channel *c = qobject_cast(parent()); - QScriptEngine *qse = engine(); - MumbleScript *ms = qobject_cast(qse->parent()); - return qse->newQObject(ms->qmChannels.value(Channel::get(0)), QScriptEngine::QtOwnership, QScriptEngine::PreferExistingWrapperObject); -} - -QScriptValue ScriptServer::loadUi(const QString &name) { - QUiLoader *ui = new QUiLoader(); - - qWarning() << "ScriptServer::loadUi" << name; - return QScriptValue(); -} - -void ScriptServer::sendMsg(const QString &keyword, const QStringList &args) { - qWarning() << "ScriptServer::sendMsg" << keyword << args; -} - -QScriptValue mumbleScriptPrint(QScriptContext *context, QScriptEngine *engine) { - QString result; - - for (int i = 0; i < context->argumentCount(); ++i) { - if (i > 0) - result.append(QLatin1String(" ")); - result.append(context->argument(i).toString()); - } - - qWarning() << result; - g.l->log(Log::ScriptError, result, MumbleScript::tr("Script Print")); - - return engine->undefinedValue(); -} - -MumbleScript::MumbleScript(MumbleScripts *p) : QObject(p) { - qseEngine = new QScriptEngine(this); - ssServer = new ScriptServer(qseEngine); - bLocal = true; - - qseEngine->globalObject().setProperty(QLatin1String("server"), qseEngine->newQObject(ssServer, QScriptEngine::QtOwnership, QScriptEngine::PreferExistingWrapperObject), QScriptValue::ReadOnly | QScriptValue::Undeletable); - qseEngine->globalObject().setProperty(QLatin1String("print"), qseEngine->newFunction(mumbleScriptPrint), QScriptValue::ReadOnly | QScriptValue::Undeletable); - - connect(qseEngine, SIGNAL(signalHandlerException(const QScriptValue &)), this, SLOT(errorHandler(const QScriptValue &))); - - connect(g.sh, SIGNAL(disconnected(QString)), ssServer, SIGNAL(disconnected(QString))); -} - -MumbleScript::~MumbleScript() { - foreach(ScriptPlayer *sp, qmPlayers) - delete sp; -} - -void MumbleScript::load(const QString &) { -} - -void MumbleScript::evaluate(const QString &code) { - qseEngine->evaluate(code); - if (qseEngine->hasUncaughtException()) - errorHandler(qseEngine->uncaughtException()); -} - -void MumbleScript::errorHandler(const QScriptValue &exception) { - g.l->log(Log::ScriptError, exception.toString() + qseEngine->uncaughtExceptionBacktrace().join(QLatin1String("\n")), tr("Script Error")); - g.ms->qlScripts.removeAll(this); - delete this; -} - -void MumbleScript::addPlayer(ClientPlayer *p) { - connect(p, SIGNAL(destroyed(QObject *)), this, SLOT(playerDeleted(QObject *))); - ScriptPlayer *sp = new ScriptPlayer(p); - qmPlayers.insert(p, sp); - emit ssServer->newPlayer(qseEngine->newQObject(sp, QScriptEngine::QtOwnership, QScriptEngine::PreferExistingWrapperObject)); -} - -void MumbleScript::movePlayer(ClientPlayer *p) { - ScriptPlayer *sp = qmPlayers.value(p); - emit sp->moved(); -} - -void MumbleScript::addChannel(Channel *c) { - connect(c, SIGNAL(destroyed(QObject *)), this, SLOT(channelDeleted(QObject *))); - ScriptChannel *sc = new ScriptChannel(c); - qmChannels.insert(c, sc); - emit ssServer->newChannel(qseEngine->newQObject(sc, QScriptEngine::QtOwnership, QScriptEngine::PreferExistingWrapperObject)); -} - -void MumbleScript::moveChannel(Channel *c) { - ScriptChannel *sc = qmChannels.value(c); - emit sc->moved(); -} - -void MumbleScript::playerDeleted(QObject *obj) { - ClientPlayer *cp=qobject_cast(obj); - qmPlayers.remove(cp); -} - -void MumbleScript::channelDeleted(QObject *obj) { - Channel *c=qobject_cast(obj); - qmChannels.remove(c); -} - -void MumbleScript::connected() { - emit ssServer->connected(); -} - -MumbleScripts::MumbleScripts(QObject *p) : QObject(p) { -} - -MumbleScripts::~MumbleScripts() { - foreach(MumbleScript *ms, qlScripts) - delete ms; -} - -void MumbleScripts::createEvaluate(const QString &qs) { - MumbleScript *ms = new MumbleScript(this); - qlScripts << ms; - ms->evaluate(qs); -} - -void MumbleScripts::addPlayer(ClientPlayer *p) { - foreach(MumbleScript *ms, qlScripts) - ms->addPlayer(p); -} - -void MumbleScripts::movePlayer(ClientPlayer *p) { - foreach(MumbleScript *ms, qlScripts) - ms->movePlayer(p); -} - -void MumbleScripts::addChannel(Channel *c) { - foreach(MumbleScript *ms, qlScripts) - ms->addChannel(c); -} - -void MumbleScripts::moveChannel(Channel *c) { - foreach(MumbleScript *ms, qlScripts) - ms->moveChannel(c); -} - -void MumbleScripts::connected() { - foreach(MumbleScript *ms, qlScripts) - ms->connected(); -} +/* Copyright (C) 2005-2009, Thorvald Natvig + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + - Neither the name of the Mumble Developers nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "MumbleScript.h" +#include "Global.h" +#include "Log.h" +#include "ServerHandler.h" + +ScriptPlayer::ScriptPlayer(ClientPlayer *p) : QObject(p) { +} + +QString ScriptPlayer::getName() const { + ClientPlayer *cp = qobject_cast(parent()); + return cp->qsName; +} + +unsigned int ScriptPlayer::getSession() const { + ClientPlayer *cp = qobject_cast(parent()); + return cp->uiSession; +} + +int ScriptPlayer::getId() const { + ClientPlayer *cp = qobject_cast(parent()); + return cp->iId; +} + +QScriptValue ScriptPlayer::getChannel() const { + ClientPlayer *cp = qobject_cast(parent()); + Channel *c = cp->cChannel; + QScriptEngine *qse = engine(); + MumbleScript *ms = qobject_cast(qse->parent()); + return qse->newQObject(ms->qmChannels.value(Channel::get(0)), QScriptEngine::QtOwnership, QScriptEngine::PreferExistingWrapperObject); +} + +bool ScriptPlayer::isMute() const { + ClientPlayer *cp = qobject_cast(parent()); + return cp->bMute || cp->bDeaf; +} + +bool ScriptPlayer::isDeaf() const { + ClientPlayer *cp = qobject_cast(parent()); + return cp->bDeaf; +} + +void ScriptPlayer::setMute(bool m) { + ClientPlayer *cp = qobject_cast(parent()); + qWarning("Want to set mute to %d", m); +} + +void ScriptPlayer::setDeaf(bool m) { + ClientPlayer *cp = qobject_cast(parent()); + qWarning("Want to set deaf to %d", m); +} + +void ScriptPlayer::setChannel(const QScriptValue &sv) { + ClientPlayer *cp = qobject_cast(parent()); + ScriptChannel *sc = qobject_cast(sv.toQObject()); + if (sc) { + Channel *c = qobject_cast(sc->parent()); + qWarning("Want to set channel to %d", c->iId); + } else { + context()->throwError(QScriptContext::ReferenceError, QLatin1String("argument is not a channel")); + } +} + +void ScriptPlayer::sendMessage(const QString &msg) { + ClientPlayer *cp = qobject_cast(parent()); + qWarning("Want to send msg %s", qPrintable(msg)); +} + +ScriptChannel::ScriptChannel(Channel *c) : QObject(c) { +} + +QString ScriptChannel::getName() const { + Channel *c = qobject_cast(parent()); + return c->qsName; +} + +int ScriptChannel::getId() const { + Channel *c = qobject_cast(parent()); + return c->iId; +} + +int ScriptChannel::getParent() const { + Channel *c = qobject_cast(parent()); + return c->cParent ? c->cParent->iId : -1; +} + +void ScriptChannel::setName(const QString &name) { + Channel *c = qobject_cast(parent()); + qWarning() << "ScriptChannel::setName" << name; +} + +void ScriptChannel::setParent(int id) { + Channel *c = qobject_cast(parent()); + qWarning() << "ScriptChannel::setParent" << id; +} + +void ScriptChannel::sendMessage(const QString &msg, bool tree) { + Channel *c = qobject_cast(parent()); + qWarning() << "ScriptChannel::sendMessage" << msg << tree; +} + +QScriptValue ScriptChannel::getPlayers() const { + Channel *c = qobject_cast(parent()); + QScriptEngine *qse = engine(); + MumbleScript *ms = qobject_cast(qse->parent()); + + QScriptValue a = engine()->newArray(c->qlPlayers.count()); + + quint32 idx = 0; + + foreach(Player *p, c->qlPlayers) { + ClientPlayer *cp = static_cast(p); + QScriptValue sv = qse->newQObject(ms->qmPlayers.value(cp), QScriptEngine::QtOwnership, QScriptEngine::PreferExistingWrapperObject); + a.setProperty(idx, sv); + idx++; + } + return a; +} + +QScriptValue ScriptChannel::getChildren() const { + Channel *c = qobject_cast(parent()); + QScriptEngine *qse = engine(); + MumbleScript *ms = qobject_cast(qse->parent()); + + QScriptValue a = engine()->newArray(c->qlChannels.count()); + + quint32 idx = 0; + + foreach(Channel *child, c->qlChannels) { + QScriptValue sv = qse->newQObject(ms->qmChannels.value(child), QScriptEngine::QtOwnership, QScriptEngine::PreferExistingWrapperObject); + a.setProperty(idx, sv); + idx++; + } + return a; +} + +ScriptServer::ScriptServer(QObject *p) : QObject(p) { +} + +QScriptValue ScriptServer::getRoot() const { + qWarning("getRoot())"); + Channel *c = qobject_cast(parent()); + QScriptEngine *qse = engine(); + MumbleScript *ms = qobject_cast(qse->parent()); + return qse->newQObject(ms->qmChannels.value(Channel::get(0)), QScriptEngine::QtOwnership, QScriptEngine::PreferExistingWrapperObject); +} + +QScriptValue ScriptServer::loadUi(const QString &name) { + QUiLoader *ui = new QUiLoader(); + + qWarning() << "ScriptServer::loadUi" << name; + return QScriptValue(); +} + +void ScriptServer::sendMsg(const QString &keyword, const QStringList &args) { + qWarning() << "ScriptServer::sendMsg" << keyword << args; +} + +QScriptValue mumbleScriptPrint(QScriptContext *context, QScriptEngine *engine) { + QString result; + + for (int i = 0; i < context->argumentCount(); ++i) { + if (i > 0) + result.append(QLatin1String(" ")); + result.append(context->argument(i).toString()); + } + + qWarning() << result; + g.l->log(Log::ScriptError, result, MumbleScript::tr("Script Print")); + + return engine->undefinedValue(); +} + +MumbleScript::MumbleScript(MumbleScripts *p) : QObject(p) { + qseEngine = new QScriptEngine(this); + ssServer = new ScriptServer(qseEngine); + bLocal = true; + + qseEngine->globalObject().setProperty(QLatin1String("server"), qseEngine->newQObject(ssServer, QScriptEngine::QtOwnership, QScriptEngine::PreferExistingWrapperObject), QScriptValue::ReadOnly | QScriptValue::Undeletable); + qseEngine->globalObject().setProperty(QLatin1String("print"), qseEngine->newFunction(mumbleScriptPrint), QScriptValue::ReadOnly | QScriptValue::Undeletable); + + connect(qseEngine, SIGNAL(signalHandlerException(const QScriptValue &)), this, SLOT(errorHandler(const QScriptValue &))); + + connect(g.sh, SIGNAL(disconnected(QString)), ssServer, SIGNAL(disconnected(QString))); +} + +MumbleScript::~MumbleScript() { + foreach(ScriptPlayer *sp, qmPlayers) + delete sp; +} + +void MumbleScript::load(const QString &) { +} + +void MumbleScript::evaluate(const QString &code) { + qseEngine->evaluate(code); + if (qseEngine->hasUncaughtException()) + errorHandler(qseEngine->uncaughtException()); +} + +void MumbleScript::errorHandler(const QScriptValue &exception) { + g.l->log(Log::ScriptError, exception.toString() + qseEngine->uncaughtExceptionBacktrace().join(QLatin1String("\n")), tr("Script Error")); + g.ms->qlScripts.removeAll(this); + delete this; +} + +void MumbleScript::addPlayer(ClientPlayer *p) { + connect(p, SIGNAL(destroyed(QObject *)), this, SLOT(playerDeleted(QObject *))); + ScriptPlayer *sp = new ScriptPlayer(p); + qmPlayers.insert(p, sp); + emit ssServer->newPlayer(qseEngine->newQObject(sp, QScriptEngine::QtOwnership, QScriptEngine::PreferExistingWrapperObject)); +} + +void MumbleScript::movePlayer(ClientPlayer *p) { + ScriptPlayer *sp = qmPlayers.value(p); + emit sp->moved(); +} + +void MumbleScript::addChannel(Channel *c) { + connect(c, SIGNAL(destroyed(QObject *)), this, SLOT(channelDeleted(QObject *))); + ScriptChannel *sc = new ScriptChannel(c); + qmChannels.insert(c, sc); + emit ssServer->newChannel(qseEngine->newQObject(sc, QScriptEngine::QtOwnership, QScriptEngine::PreferExistingWrapperObject)); +} + +void MumbleScript::moveChannel(Channel *c) { + ScriptChannel *sc = qmChannels.value(c); + emit sc->moved(); +} + +void MumbleScript::playerDeleted(QObject *obj) { + ClientPlayer *cp=qobject_cast(obj); + qmPlayers.remove(cp); +} + +void MumbleScript::channelDeleted(QObject *obj) { + Channel *c=qobject_cast(obj); + qmChannels.remove(c); +} + +void MumbleScript::connected() { + emit ssServer->connected(); +} + +MumbleScripts::MumbleScripts(QObject *p) : QObject(p) { +} + +MumbleScripts::~MumbleScripts() { + foreach(MumbleScript *ms, qlScripts) + delete ms; +} + +void MumbleScripts::createEvaluate(const QString &qs) { + MumbleScript *ms = new MumbleScript(this); + qlScripts << ms; + ms->evaluate(qs); +} + +void MumbleScripts::addPlayer(ClientPlayer *p) { + foreach(MumbleScript *ms, qlScripts) + ms->addPlayer(p); +} + +void MumbleScripts::movePlayer(ClientPlayer *p) { + foreach(MumbleScript *ms, qlScripts) + ms->movePlayer(p); +} + +void MumbleScripts::addChannel(Channel *c) { + foreach(MumbleScript *ms, qlScripts) + ms->addChannel(c); +} + +void MumbleScripts::moveChannel(Channel *c) { + foreach(MumbleScript *ms, qlScripts) + ms->moveChannel(c); +} + +void MumbleScripts::connected() { + foreach(MumbleScript *ms, qlScripts) + ms->connected(); +} diff --git a/src/mumble/MumbleScript.h b/src/mumble/MumbleScript.h index bd8e87508..eda15c439 100644 --- a/src/mumble/MumbleScript.h +++ b/src/mumble/MumbleScript.h @@ -1,160 +1,160 @@ -/* Copyright (C) 2005-2009, Thorvald Natvig - - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - - Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - Neither the name of the Mumble Developers nor the names of its - contributors may be used to endorse or promote products derived from this - software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef _SCRIPT_H -#define _SCRIPT_H - -#include "mumble_pch.hpp" -#include "Player.h" -#include "Channel.h" - -class ScriptPlayer; -class ScriptChannel; -class MumbleScript; -class MumbleScripts; - -class ScriptPlayer : public QObject, protected QScriptable { - friend class MumbleScript; - Q_OBJECT - Q_DISABLE_COPY(ScriptPlayer) - Q_PROPERTY(QString name READ getName) - Q_PROPERTY(unsigned int session READ getSession) - Q_PROPERTY(int id READ getId) - Q_PROPERTY(QScriptValue channel READ getChannel WRITE setChannel) - Q_PROPERTY(bool mute READ isMute WRITE setMute) - Q_PROPERTY(bool deaf READ isDeaf WRITE setDeaf) - public: - ScriptPlayer(ClientPlayer *p); - QString getName() const; - unsigned int getSession() const; - int getId() const; - QScriptValue getChannel() const; - bool isMute() const; - bool isDeaf() const; - public slots: - void setMute(bool); - void setDeaf(bool); - void setChannel(const QScriptValue &); - void sendMessage(const QString &); - signals: - void moved(); -}; - -class ScriptChannel : public QObject, protected QScriptable { - friend class MumbleScript; - Q_OBJECT - Q_DISABLE_COPY(ScriptChannel) - Q_PROPERTY(QString name READ getName WRITE setName) - Q_PROPERTY(int id READ getId) - Q_PROPERTY(int parent READ getParent WRITE setParent) - Q_PROPERTY(QScriptValue children READ getChildren) - Q_PROPERTY(QScriptValue players READ getPlayers) - public: - ScriptChannel(Channel *c); - QString getName() const; - int getId() const; - int getParent() const; - public slots: - void setName(const QString &); - void setParent(int); - QScriptValue getPlayers() const; - QScriptValue getChildren() const; - void sendMessage(const QString &, bool); - signals: - void moved(); -}; - -class ScriptServer : public QObject, protected QScriptable { - friend class MumbleScript; - Q_OBJECT - Q_DISABLE_COPY(ScriptServer) - Q_PROPERTY(QScriptValue playerContextMenu) - Q_PROPERTY(QScriptValue channelContextMenu) - Q_PROPERTY(QScriptValue msgHandler) - Q_PROPERTY(QScriptValue root READ getRoot) - public: - ScriptServer(QObject *p); - QScriptValue getRoot() const; - public slots: - QScriptValue loadUi(const QString &); - void sendMsg(const QString &, const QStringList &); - signals: - void newPlayer(QScriptValue); - void newChannel(QScriptValue); - void connected(); - void disconnected(QString reason); -}; - -class MumbleScript : public QObject { - Q_OBJECT - Q_DISABLE_COPY(MumbleScript) - public: - QScriptEngine *qseEngine; - ScriptServer *ssServer; - QString qsFilename; - QMap qmPlayers; - QMap qmChannels; - QMap qmResources; - bool bLocal; - - MumbleScript(MumbleScripts *p); - ~MumbleScript(); - void load(const QString &source); - void evaluate(const QString &code); - - void addPlayer(ClientPlayer *p); - void movePlayer(ClientPlayer *p); - void addChannel(Channel *c); - void moveChannel(Channel *c); - void connected(); - public slots: - void playerDeleted(QObject *); - void channelDeleted(QObject *); - void errorHandler(const QScriptValue &); -}; - -class MumbleScripts : public QObject { - friend class MumbleScript; - Q_OBJECT - Q_DISABLE_COPY(MumbleScripts) - protected: - QList qlScripts; - public: - MumbleScripts(QObject *p); - ~MumbleScripts(); - void createEvaluate(const QString &code); - void addPlayer(ClientPlayer *p); - void movePlayer(ClientPlayer *p); - void addChannel(Channel *c); - void moveChannel(Channel *c); - void connected(); -}; - -#endif +/* Copyright (C) 2005-2009, Thorvald Natvig + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + - Neither the name of the Mumble Developers nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _SCRIPT_H +#define _SCRIPT_H + +#include "mumble_pch.hpp" +#include "Player.h" +#include "Channel.h" + +class ScriptPlayer; +class ScriptChannel; +class MumbleScript; +class MumbleScripts; + +class ScriptPlayer : public QObject, protected QScriptable { + friend class MumbleScript; + Q_OBJECT + Q_DISABLE_COPY(ScriptPlayer) + Q_PROPERTY(QString name READ getName) + Q_PROPERTY(unsigned int session READ getSession) + Q_PROPERTY(int id READ getId) + Q_PROPERTY(QScriptValue channel READ getChannel WRITE setChannel) + Q_PROPERTY(bool mute READ isMute WRITE setMute) + Q_PROPERTY(bool deaf READ isDeaf WRITE setDeaf) + public: + ScriptPlayer(ClientPlayer *p); + QString getName() const; + unsigned int getSession() const; + int getId() const; + QScriptValue getChannel() const; + bool isMute() const; + bool isDeaf() const; + public slots: + void setMute(bool); + void setDeaf(bool); + void setChannel(const QScriptValue &); + void sendMessage(const QString &); + signals: + void moved(); +}; + +class ScriptChannel : public QObject, protected QScriptable { + friend class MumbleScript; + Q_OBJECT + Q_DISABLE_COPY(ScriptChannel) + Q_PROPERTY(QString name READ getName WRITE setName) + Q_PROPERTY(int id READ getId) + Q_PROPERTY(int parent READ getParent WRITE setParent) + Q_PROPERTY(QScriptValue children READ getChildren) + Q_PROPERTY(QScriptValue players READ getPlayers) + public: + ScriptChannel(Channel *c); + QString getName() const; + int getId() const; + int getParent() const; + public slots: + void setName(const QString &); + void setParent(int); + QScriptValue getPlayers() const; + QScriptValue getChildren() const; + void sendMessage(const QString &, bool); + signals: + void moved(); +}; + +class ScriptServer : public QObject, protected QScriptable { + friend class MumbleScript; + Q_OBJECT + Q_DISABLE_COPY(ScriptServer) + Q_PROPERTY(QScriptValue playerContextMenu) + Q_PROPERTY(QScriptValue channelContextMenu) + Q_PROPERTY(QScriptValue msgHandler) + Q_PROPERTY(QScriptValue root READ getRoot) + public: + ScriptServer(QObject *p); + QScriptValue getRoot() const; + public slots: + QScriptValue loadUi(const QString &); + void sendMsg(const QString &, const QStringList &); + signals: + void newPlayer(QScriptValue); + void newChannel(QScriptValue); + void connected(); + void disconnected(QString reason); +}; + +class MumbleScript : public QObject { + Q_OBJECT + Q_DISABLE_COPY(MumbleScript) + public: + QScriptEngine *qseEngine; + ScriptServer *ssServer; + QString qsFilename; + QMap qmPlayers; + QMap qmChannels; + QMap qmResources; + bool bLocal; + + MumbleScript(MumbleScripts *p); + ~MumbleScript(); + void load(const QString &source); + void evaluate(const QString &code); + + void addPlayer(ClientPlayer *p); + void movePlayer(ClientPlayer *p); + void addChannel(Channel *c); + void moveChannel(Channel *c); + void connected(); + public slots: + void playerDeleted(QObject *); + void channelDeleted(QObject *); + void errorHandler(const QScriptValue &); +}; + +class MumbleScripts : public QObject { + friend class MumbleScript; + Q_OBJECT + Q_DISABLE_COPY(MumbleScripts) + protected: + QList qlScripts; + public: + MumbleScripts(QObject *p); + ~MumbleScripts(); + void createEvaluate(const QString &code); + void addPlayer(ClientPlayer *p); + void movePlayer(ClientPlayer *p); + void addChannel(Channel *c); + void moveChannel(Channel *c); + void connected(); +}; + +#endif diff --git a/src/mumble/NetworkConfig.h b/src/mumble/NetworkConfig.h index 1524862f1..5bce8b52d 100644 --- a/src/mumble/NetworkConfig.h +++ b/src/mumble/NetworkConfig.h @@ -1,59 +1,59 @@ -/* Copyright (C) 2005-2009, Thorvald Natvig - Copyright (C) 2008-2009, Mikkel Krautz - - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - - Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - Neither the name of the Mumble Developers nor the names of its - contributors may be used to endorse or promote products derived from this - software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef _NETWORKCONFIG_H -#define _NETWORKCONFIG_H - -#include "ConfigDialog.h" -#include "ui_NetworkConfig.h" - -class NetworkConfig : public ConfigWidget, Ui::NetworkConfig { - private: - Q_OBJECT - Q_DISABLE_COPY(NetworkConfig) - public: - NetworkConfig(Settings &st); - virtual QString title() const; - virtual QIcon icon() const; - static void SetupProxy(); - static bool TcpModeEnabled(); - public slots: - void accept() const; - void save() const; - void load(const Settings &r); - bool expert(bool); - - void on_qcbType_currentIndexChanged(int v); -}; - -#else -class NetworkConfig; -#endif +/* Copyright (C) 2005-2009, Thorvald Natvig + Copyright (C) 2008-2009, Mikkel Krautz + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + - Neither the name of the Mumble Developers nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _NETWORKCONFIG_H +#define _NETWORKCONFIG_H + +#include "ConfigDialog.h" +#include "ui_NetworkConfig.h" + +class NetworkConfig : public ConfigWidget, Ui::NetworkConfig { + private: + Q_OBJECT + Q_DISABLE_COPY(NetworkConfig) + public: + NetworkConfig(Settings &st); + virtual QString title() const; + virtual QIcon icon() const; + static void SetupProxy(); + static bool TcpModeEnabled(); + public slots: + void accept() const; + void save() const; + void load(const Settings &r); + bool expert(bool); + + void on_qcbType_currentIndexChanged(int v); +}; + +#else +class NetworkConfig; +#endif diff --git a/src/mumble/Overlay.cpp b/src/mumble/Overlay.cpp index 1e4d50301..26954caf9 100644 --- a/src/mumble/Overlay.cpp +++ b/src/mumble/Overlay.cpp @@ -1,542 +1,542 @@ -/* Copyright (C) 2005-2009, Thorvald Natvig - - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - - Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - Neither the name of the Mumble Developers nor the names of its - contributors may be used to endorse or promote products derived from this - software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include "Overlay.h" -#include "Player.h" -#include "Channel.h" -#include "Global.h" -#include "Message.h" -#include "ServerHandler.h" - -static ConfigWidget *OverlayConfigDialogNew(Settings &st) { - return new OverlayConfig(st); -} - -static ConfigRegistrar registrar(6000, OverlayConfigDialogNew); - -OverlayConfig::OverlayConfig(Settings &st) : ConfigWidget(st) { - setupUi(this); - - qcbShow->addItem(tr("Show no one"), Settings::Nothing); - qcbShow->addItem(tr("Show only talking"), Settings::Talking); - qcbShow->addItem(tr("Show everyone"), Settings::All); -} - -void OverlayConfig::load(const Settings &r) { - loadCheckBox(qcbEnable, r.bOverlayEnable); - loadCheckBox(qcbAlwaysSelf, r.bOverlayAlwaysSelf); - loadCheckBox(qcbUserTextures, r.bOverlayUserTextures); - loadComboBox(qcbShow, r.osOverlay); - - loadCheckBox(qcbLeft, r.bOverlayLeft); - loadCheckBox(qcbRight, r.bOverlayRight); - loadCheckBox(qcbTop, r.bOverlayTop); - loadCheckBox(qcbBottom, r.bOverlayBottom); - - loadSlider(qsX, static_cast(lroundf(r.fOverlayX * 100.0f))); - loadSlider(qsY, 100 - static_cast(lroundf(r.fOverlayY * 100.0f))); - qfFont = r.qfOverlayFont; - qcPlayer = r.qcOverlayPlayer; - qcTalking= r.qcOverlayTalking; - qcAltTalking= r.qcOverlayAltTalking; - qcChannel = r.qcOverlayChannel; - qcChannelTalking = r.qcOverlayChannelTalking; - qlCurrentFont->setText(qfFont.family()); - loadSlider(qsMaxHeight, static_cast(lroundf(r.fOverlayHeight * 1000.0f))); - setColorLabel(qlPlayer, qcPlayer); - setColorLabel(qlTalking, qcTalking); - setColorLabel(qlAltTalking, qcAltTalking); - setColorLabel(qlChannel, qcChannel); - setColorLabel(qlChannelTalking, qcChannelTalking); -} - -bool OverlayConfig::expert(bool) { - return false; -} - -void OverlayConfig::setColorLabel(QLabel *label, QColor col) { - label->setText(col.name()); - - QPalette palette; - palette.setColor(label->foregroundRole(), col); - label->setPalette(palette); -} - -void OverlayConfig::on_qpbSetFont_clicked() { - bool ok; - QFont qf = QFontDialog::getFont(&ok, qfFont, this); - if (ok) { - qfFont = qf; - qlCurrentFont->setText(qfFont.family()); - qsMaxHeight->setValue(qfFont.pointSize()); - } -} - -void OverlayConfig::on_qsMaxHeight_valueChanged(int v) { - qlMaxHeight->setText(QString::fromLatin1("%1%").arg(v/10.0, 0, 'f', 1)); - qfFont.setPointSize(v); -} - - -void OverlayConfig::on_qpbPlayer_clicked() { - bool ok; - QRgb rgb=QColorDialog::getRgba(qcPlayer.rgba(), &ok, this); - if (ok) { - qcPlayer = QColor::fromRgba(rgb); - setColorLabel(qlPlayer, qcPlayer); - } -} - -void OverlayConfig::on_qpbTalking_clicked() { - bool ok; - QRgb rgb=QColorDialog::getRgba(qcTalking.rgba(), &ok, this); - if (ok) { - qcTalking = QColor::fromRgba(rgb); - setColorLabel(qlTalking, qcTalking); - } -} - -void OverlayConfig::on_qpbAltTalking_clicked() { - bool ok; - QRgb rgb=QColorDialog::getRgba(qcAltTalking.rgba(), &ok, this); - if (ok) { - qcAltTalking = QColor::fromRgba(rgb); - setColorLabel(qlAltTalking, qcAltTalking); - } -} - -void OverlayConfig::on_qpbChannel_clicked() { - bool ok; - QRgb rgb=QColorDialog::getRgba(qcChannel.rgba(), &ok, this); - if (ok) { - qcChannel = QColor::fromRgba(rgb); - setColorLabel(qlChannel, qcChannel); - } -} - -void OverlayConfig::on_qpbChannelTalking_clicked() { - bool ok; - QRgb rgb=QColorDialog::getRgba(qcChannelTalking.rgba(), &ok, this); - if (ok) { - qcChannelTalking = QColor::fromRgba(rgb); - setColorLabel(qlChannelTalking, qcChannelTalking); - } -} - -QString OverlayConfig::title() const { - return tr("Overlay"); -} - -QIcon OverlayConfig::icon() const { - return QIcon(QLatin1String("skin:config_osd.png")); -} - -void OverlayConfig::save() const { - s.bOverlayEnable = qcbEnable->isChecked(); - s.osOverlay = static_cast(qcbShow->currentIndex()); - s.bOverlayAlwaysSelf = qcbAlwaysSelf->isChecked(); - s.bOverlayUserTextures = qcbUserTextures->isChecked(); - s.fOverlayX = static_cast(qsX->value()) / 100.0f; - s.fOverlayY = 1.0f - static_cast(qsY->value()) / 100.0f; - s.bOverlayLeft = qcbLeft->isChecked(); - s.bOverlayRight = qcbRight->isChecked(); - s.bOverlayTop = qcbTop->isChecked(); - s.bOverlayBottom = qcbBottom->isChecked(); - s.qfOverlayFont = qfFont; - s.fOverlayHeight = static_cast(qsMaxHeight->value()) / 1000.0f; - s.qcOverlayPlayer = qcPlayer; - s.qcOverlayTalking = qcTalking; - s.qcOverlayAltTalking = qcAltTalking; - s.qcOverlayChannel = qcChannel; - s.qcOverlayChannelTalking = qcChannelTalking; -} - -void OverlayConfig::accept() const { - g.o->forceSettings(); - g.o->setActive(s.bOverlayEnable); -} - -Overlay::Overlay() : QObject() { - qlOverlay = new QLibrary(this); - -#ifdef Q_OS_WIN - QString path=QString::fromLatin1("%1/mumble_ol.dll").arg(qApp->applicationDirPath()); - - qlOverlay->setFileName(path); - if (! qlOverlay->load()) { - QMessageBox::critical(NULL, tr("Mumble"), tr("Failed to load overlay library. This means either that:\n" - "- the library (mumble_ol.dll) wasn't found in the directory you ran Mumble from\n" - "- you're on an OS earlier than WinXP SP2\n" - "- you do not have the June 2007 updated version of DX9.0c"), QMessageBox::Ok, QMessageBox::NoButton); - qWarning("Overlay failure"); - } else { - sm.resolve(qlOverlay); - } -#else - sm.resolve(qlOverlay); -#endif - -#ifndef QT_NO_DEBUG - if (sm.sm) - sm.sm->bDebug = true; -#else - if (sm.sm) - sm.sm->bDebug = false; -#endif - - if (sm.sm) { - sm.sm->version[0] = OVERLAY_VERSION_MAJ; - sm.sm->version[1] = OVERLAY_VERSION_MIN; - sm.sm->version[2] = OVERLAY_VERSION_PATCH; - sm.sm->version[3] = OVERLAY_VERSION_SUB; - } - - QImage img; - img.load(QLatin1String("skin:muted_overlay.png")); - qbaMuted = QByteArray(reinterpret_cast(img.bits()), img.numBytes()); - - img.load(QLatin1String("skin:deafened_overlay.png")); - qbaDeafened = QByteArray(reinterpret_cast(img.bits()), img.numBytes()); - - qtTimer=new QTimer(this); - qtTimer->setObjectName(QLatin1String("Timer")); - qtTimer->start(1000); - - platformInit(); - forceSettings(); - - QMetaObject::connectSlotsByName(this); -} - -Overlay::~Overlay() { - setActive(false); - qlOverlay->unload(); -} - -bool Overlay::isActive() const { - if (! sm.sm) - return false; - - return sm.sm->bHooked; -} - -void Overlay::toggleShow() { - Settings::OverlayShow ns; - - switch (g.s.osOverlay) { - case Settings::Nothing: - ns = Settings::All; - break; - case Settings::All: - ns = Settings::Talking; - break; - default: - ns = Settings::All; - break; - } - g.s.osOverlay = ns; - - if (sm.tryLock()) { - sm.sm->bShow = (g.s.osOverlay != Settings::Nothing); - sm.unlock(); - } - updateOverlay(); -} - -void Overlay::forceSettings() { - QString str; - - if (! sm.sm) - return; - - fixFont(); - - if (sm.tryLock()) { - sm.sm->fX = g.s.fOverlayX; - sm.sm->fY = g.s.fOverlayY; - sm.sm->bTop = g.s.bOverlayTop; - sm.sm->bBottom = g.s.bOverlayBottom; - sm.sm->bLeft = g.s.bOverlayLeft; - sm.sm->bRight = g.s.bOverlayRight; - sm.sm->bReset = true; - sm.sm->bShow = (g.s.osOverlay != Settings::Nothing); - sm.sm->fFontSize = g.s.fOverlayHeight; - sm.unlock(); - } - updateOverlay(); -} - -void Overlay::textureResponse(int id, const QByteArray &texture) { - QString s = qhQueried.value(id); - if (s.isEmpty()) - return; - - QByteArray t = qUncompress(texture); - - if (t.size() != TEXTURE_SIZE) - return; - - const unsigned char *data = reinterpret_cast(t.constData()); - - int width = 0; - for (int y=0;y width) && (data[(y*TEXT_WIDTH+x)*4] != 0x00)) - width = x; - } - } - qhUserTextures[id] = UserTexture(width, t); - qsForce.insert(id); - setTexts(qlCurrentTexts); -} - -typedef QPair qpChanCol; - -void Overlay::updateOverlay() { - quint32 colPlayer = g.s.qcOverlayPlayer.rgba(); - quint32 colTalking = g.s.qcOverlayTalking.rgba(); - quint32 colAltTalking = g.s.qcOverlayAltTalking.rgba(); - quint32 colChannel = g.s.qcOverlayChannel.rgba(); - quint32 colChannelTalking = g.s.qcOverlayChannelTalking.rgba(); - QString str; - QList linkchans; - - if (! isActive()) - return; - - if (g.uiSession) { - Channel *home = ClientPlayer::get(g.uiSession)->cChannel; - foreach(Channel *c, home->allLinks()) { - if (home == c) - continue; - - bool act = false; - foreach(Player *p, c->qlPlayers) { - act = act || p->bTalking; - if (p->bTalking) - linkchans << qpChanCol(p->qsName + QString::fromLatin1("[") + c->qsName + QString::fromLatin1("]"), colChannelTalking); - } - if (! act) - linkchans << qpChanCol(c->qsName, colChannel); - } - qSort(linkchans); - } - - QList lines; - - if (g.uiSession) { - - if (g.s.bOverlayTop) { - foreach(qpChanCol cc, linkchans) { - if ((g.s.osOverlay == Settings::All) || (cc.second == colChannelTalking)) { - lines << TextLine(cc.first, cc.second); - } - } - if (linkchans.count() > 0) { - lines << TextLine(QString(), 0); - } - } - - foreach(Player *p, ClientPlayer::get(g.uiSession)->cChannel->qlPlayers) { - if ((g.s.osOverlay == Settings::All) || p->bTalking || ((p == ClientPlayer::get(g.uiSession)) && g.s.bOverlayAlwaysSelf)) { - if (g.s.bOverlayUserTextures && (p->iId >= 0) && (! qhQueried.contains(p->iId))) { - qhQueried.insert(p->iId, p->qsName); - MessageTexture mt; - mt.iPlayerId = p->iId; - g.sh->sendMessage(&mt); - } - QString name = p->qsName; - Decoration dec = None; - if (p->bDeaf || p->bSelfDeaf) - dec = Deafened; - else if (p->bMute || p->bSelfMute || p->bLocalMute) - dec = Muted; - lines << TextLine(name, p->bTalking ? (p->bAltSpeak ? colAltTalking : colTalking) : colPlayer, p->iId, dec); - } - } - - if (! g.s.bOverlayTop) { - if (linkchans.count() > 0) { - lines << TextLine(QString(), 0); - } - foreach(qpChanCol cc, linkchans) { - if ((g.s.osOverlay == Settings::All) || (cc.second == colChannelTalking)) { - lines << TextLine(cc.first, cc.second); - } - } - } - } else { - qhUserTextures.clear(); - clearCache(); - } - setTexts(lines); -} - -/* - * Here's the thing. The painterpath, and what you get if you just use painter. They're different. - * The font metrics? They must be for martian charaters; human ones fit inside a much smaller box. - * - * So. If at first you don't succeed, try and try again. - */ - -void Overlay::fixFont() { - qfFont = g.s.qfOverlayFont; - - qfFont.setStyleStrategy(QFont::ForceOutline); - - int psize = TEXT_HEIGHT; - - QRectF br; - - do { - qfFont.setPixelSize(psize--); - QPainterPath qp; - qp.addText(0, 0, qfFont, QLatin1String("Üy")); - br=qp.boundingRect(); - } while ((br.height()+2) > TEXT_HEIGHT); - - fFontBase = static_cast(fabs(br.top())); - - clearCache(); - - qlCurrentTexts.clear(); -} - -void Overlay::clearCache() { - foreach(unsigned char *ptr, qhTextures) - delete [] ptr; - - qhTextures.clear(); - qhWidths.clear(); - qhQueried.clear(); -} - -void Overlay::setTexts(const QList &lines) { - foreach(const TextLine &e, lines) { - if ((! e.qsText.isEmpty()) && (! qhTextures.contains(e.qsText)) && (! qhUserTextures.contains(e.iPlayer))) { - unsigned char *td = new unsigned char[TEXTURE_SIZE]; - memset(td, 0, TEXTURE_SIZE); - - QImage qi(td, TEXT_WIDTH, TEXT_HEIGHT, QImage::Format_ARGB32); - - QPainterPath qp; - qp.addText(2, fFontBase, qfFont, e.qsText); - - QPainter p(&qi); - p.setRenderHint(QPainter::Antialiasing); - p.setRenderHint(QPainter::TextAntialiasing); - p.setBrush(Qt::white); - - // Draw with big border, this will be the "outline" - p.setPen(QPen(Qt::black, 3, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); - p.drawPath(qp); - - // And again, all white with no border. This avoids thin fonts being just black outline. - p.setPen(Qt::NoPen); - p.drawPath(qp); - - qhTextures[e.qsText] = td; - qhWidths[e.qsText] = qMin(static_cast(qp.boundingRect().width()+6), static_cast(TEXT_WIDTH)); - } - } - - if (! sm.tryLock()) - return; - - int i; - - for (i=0;i= NUM_TEXTS) - break; - - const TextLine &tl = lines.at(i); - TextEntry *te = & sm.sm->texts[i]; - - tl.qsText.left(127).toWCharArray(te->text); - te->color = lines[i].uiColor; - - if ((i >= qlCurrentTexts.count()) || (qlCurrentTexts[i].dDecor != tl.dDecor) || (qlCurrentTexts[i].qsText != tl.qsText) || qsForce.contains(tl.iPlayer)) { - if (tl.qsText.isNull()) { - te->width = 0; - } else { - int width = 0; - const unsigned char *src = NULL; - - if (qhUserTextures.contains(tl.iPlayer)) { - const UserTexture &ut=qhUserTextures.value(tl.iPlayer); - width = ut.first; - src = reinterpret_cast(ut.second.constData()); - } else { - width = qhWidths[tl.qsText]; - src = qhTextures[tl.qsText]; - } - - unsigned char * dst = NULL; - - if (tl.dDecor != None) { - unsigned char * decdst; - const unsigned char * decsrc = reinterpret_cast((tl.dDecor == Muted) ? qbaMuted.constData() : qbaDeafened.constData()); - - width = qMin(TEXT_WIDTH - TEXT_HEIGHT, width); - if (g.s.bOverlayLeft) { - dst = sm.sm->texts[i].texture + TEXT_HEIGHT * 4; - decdst = sm.sm->texts[i].texture; - } else { - dst = sm.sm->texts[i].texture; - decdst = sm.sm->texts[i].texture + width * 4; - } - for (int j=0;jtexts[i].texture; - } - - for (int j=0;jwidth = static_cast(width); - te->uiCounter++; - } - qsForce.remove(tl.iPlayer); - } - } - - for (;itexts[i].width = -1; - } - - qlCurrentTexts = lines; - sm.unlock(); -} +/* Copyright (C) 2005-2009, Thorvald Natvig + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + - Neither the name of the Mumble Developers nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "Overlay.h" +#include "Player.h" +#include "Channel.h" +#include "Global.h" +#include "Message.h" +#include "ServerHandler.h" + +static ConfigWidget *OverlayConfigDialogNew(Settings &st) { + return new OverlayConfig(st); +} + +static ConfigRegistrar registrar(6000, OverlayConfigDialogNew); + +OverlayConfig::OverlayConfig(Settings &st) : ConfigWidget(st) { + setupUi(this); + + qcbShow->addItem(tr("Show no one"), Settings::Nothing); + qcbShow->addItem(tr("Show only talking"), Settings::Talking); + qcbShow->addItem(tr("Show everyone"), Settings::All); +} + +void OverlayConfig::load(const Settings &r) { + loadCheckBox(qcbEnable, r.bOverlayEnable); + loadCheckBox(qcbAlwaysSelf, r.bOverlayAlwaysSelf); + loadCheckBox(qcbUserTextures, r.bOverlayUserTextures); + loadComboBox(qcbShow, r.osOverlay); + + loadCheckBox(qcbLeft, r.bOverlayLeft); + loadCheckBox(qcbRight, r.bOverlayRight); + loadCheckBox(qcbTop, r.bOverlayTop); + loadCheckBox(qcbBottom, r.bOverlayBottom); + + loadSlider(qsX, static_cast(lroundf(r.fOverlayX * 100.0f))); + loadSlider(qsY, 100 - static_cast(lroundf(r.fOverlayY * 100.0f))); + qfFont = r.qfOverlayFont; + qcPlayer = r.qcOverlayPlayer; + qcTalking= r.qcOverlayTalking; + qcAltTalking= r.qcOverlayAltTalking; + qcChannel = r.qcOverlayChannel; + qcChannelTalking = r.qcOverlayChannelTalking; + qlCurrentFont->setText(qfFont.family()); + loadSlider(qsMaxHeight, static_cast(lroundf(r.fOverlayHeight * 1000.0f))); + setColorLabel(qlPlayer, qcPlayer); + setColorLabel(qlTalking, qcTalking); + setColorLabel(qlAltTalking, qcAltTalking); + setColorLabel(qlChannel, qcChannel); + setColorLabel(qlChannelTalking, qcChannelTalking); +} + +bool OverlayConfig::expert(bool) { + return false; +} + +void OverlayConfig::setColorLabel(QLabel *label, QColor col) { + label->setText(col.name()); + + QPalette palette; + palette.setColor(label->foregroundRole(), col); + label->setPalette(palette); +} + +void OverlayConfig::on_qpbSetFont_clicked() { + bool ok; + QFont qf = QFontDialog::getFont(&ok, qfFont, this); + if (ok) { + qfFont = qf; + qlCurrentFont->setText(qfFont.family()); + qsMaxHeight->setValue(qfFont.pointSize()); + } +} + +void OverlayConfig::on_qsMaxHeight_valueChanged(int v) { + qlMaxHeight->setText(QString::fromLatin1("%1%").arg(v/10.0, 0, 'f', 1)); + qfFont.setPointSize(v); +} + + +void OverlayConfig::on_qpbPlayer_clicked() { + bool ok; + QRgb rgb=QColorDialog::getRgba(qcPlayer.rgba(), &ok, this); + if (ok) { + qcPlayer = QColor::fromRgba(rgb); + setColorLabel(qlPlayer, qcPlayer); + } +} + +void OverlayConfig::on_qpbTalking_clicked() { + bool ok; + QRgb rgb=QColorDialog::getRgba(qcTalking.rgba(), &ok, this); + if (ok) { + qcTalking = QColor::fromRgba(rgb); + setColorLabel(qlTalking, qcTalking); + } +} + +void OverlayConfig::on_qpbAltTalking_clicked() { + bool ok; + QRgb rgb=QColorDialog::getRgba(qcAltTalking.rgba(), &ok, this); + if (ok) { + qcAltTalking = QColor::fromRgba(rgb); + setColorLabel(qlAltTalking, qcAltTalking); + } +} + +void OverlayConfig::on_qpbChannel_clicked() { + bool ok; + QRgb rgb=QColorDialog::getRgba(qcChannel.rgba(), &ok, this); + if (ok) { + qcChannel = QColor::fromRgba(rgb); + setColorLabel(qlChannel, qcChannel); + } +} + +void OverlayConfig::on_qpbChannelTalking_clicked() { + bool ok; + QRgb rgb=QColorDialog::getRgba(qcChannelTalking.rgba(), &ok, this); + if (ok) { + qcChannelTalking = QColor::fromRgba(rgb); + setColorLabel(qlChannelTalking, qcChannelTalking); + } +} + +QString OverlayConfig::title() const { + return tr("Overlay"); +} + +QIcon OverlayConfig::icon() const { + return QIcon(QLatin1String("skin:config_osd.png")); +} + +void OverlayConfig::save() const { + s.bOverlayEnable = qcbEnable->isChecked(); + s.osOverlay = static_cast(qcbShow->currentIndex()); + s.bOverlayAlwaysSelf = qcbAlwaysSelf->isChecked(); + s.bOverlayUserTextures = qcbUserTextures->isChecked(); + s.fOverlayX = static_cast(qsX->value()) / 100.0f; + s.fOverlayY = 1.0f - static_cast(qsY->value()) / 100.0f; + s.bOverlayLeft = qcbLeft->isChecked(); + s.bOverlayRight = qcbRight->isChecked(); + s.bOverlayTop = qcbTop->isChecked(); + s.bOverlayBottom = qcbBottom->isChecked(); + s.qfOverlayFont = qfFont; + s.fOverlayHeight = static_cast(qsMaxHeight->value()) / 1000.0f; + s.qcOverlayPlayer = qcPlayer; + s.qcOverlayTalking = qcTalking; + s.qcOverlayAltTalking = qcAltTalking; + s.qcOverlayChannel = qcChannel; + s.qcOverlayChannelTalking = qcChannelTalking; +} + +void OverlayConfig::accept() const { + g.o->forceSettings(); + g.o->setActive(s.bOverlayEnable); +} + +Overlay::Overlay() : QObject() { + qlOverlay = new QLibrary(this); + +#ifdef Q_OS_WIN + QString path=QString::fromLatin1("%1/mumble_ol.dll").arg(qApp->applicationDirPath()); + + qlOverlay->setFileName(path); + if (! qlOverlay->load()) { + QMessageBox::critical(NULL, tr("Mumble"), tr("Failed to load overlay library. This means either that:\n" + "- the library (mumble_ol.dll) wasn't found in the directory you ran Mumble from\n" + "- you're on an OS earlier than WinXP SP2\n" + "- you do not have the June 2007 updated version of DX9.0c"), QMessageBox::Ok, QMessageBox::NoButton); + qWarning("Overlay failure"); + } else { + sm.resolve(qlOverlay); + } +#else + sm.resolve(qlOverlay); +#endif + +#ifndef QT_NO_DEBUG + if (sm.sm) + sm.sm->bDebug = true; +#else + if (sm.sm) + sm.sm->bDebug = false; +#endif + + if (sm.sm) { + sm.sm->version[0] = OVERLAY_VERSION_MAJ; + sm.sm->version[1] = OVERLAY_VERSION_MIN; + sm.sm->version[2] = OVERLAY_VERSION_PATCH; + sm.sm->version[3] = OVERLAY_VERSION_SUB; + } + + QImage img; + img.load(QLatin1String("skin:muted_overlay.png")); + qbaMuted = QByteArray(reinterpret_cast(img.bits()), img.numBytes()); + + img.load(QLatin1String("skin:deafened_overlay.png")); + qbaDeafened = QByteArray(reinterpret_cast(img.bits()), img.numBytes()); + + qtTimer=new QTimer(this); + qtTimer->setObjectName(QLatin1String("Timer")); + qtTimer->start(1000); + + platformInit(); + forceSettings(); + + QMetaObject::connectSlotsByName(this); +} + +Overlay::~Overlay() { + setActive(false); + qlOverlay->unload(); +} + +bool Overlay::isActive() const { + if (! sm.sm) + return false; + + return sm.sm->bHooked; +} + +void Overlay::toggleShow() { + Settings::OverlayShow ns; + + switch (g.s.osOverlay) { + case Settings::Nothing: + ns = Settings::All; + break; + case Settings::All: + ns = Settings::Talking; + break; + default: + ns = Settings::All; + break; + } + g.s.osOverlay = ns; + + if (sm.tryLock()) { + sm.sm->bShow = (g.s.osOverlay != Settings::Nothing); + sm.unlock(); + } + updateOverlay(); +} + +void Overlay::forceSettings() { + QString str; + + if (! sm.sm) + return; + + fixFont(); + + if (sm.tryLock()) { + sm.sm->fX = g.s.fOverlayX; + sm.sm->fY = g.s.fOverlayY; + sm.sm->bTop = g.s.bOverlayTop; + sm.sm->bBottom = g.s.bOverlayBottom; + sm.sm->bLeft = g.s.bOverlayLeft; + sm.sm->bRight = g.s.bOverlayRight; + sm.sm->bReset = true; + sm.sm->bShow = (g.s.osOverlay != Settings::Nothing); + sm.sm->fFontSize = g.s.fOverlayHeight; + sm.unlock(); + } + updateOverlay(); +} + +void Overlay::textureResponse(int id, const QByteArray &texture) { + QString s = qhQueried.value(id); + if (s.isEmpty()) + return; + + QByteArray t = qUncompress(texture); + + if (t.size() != TEXTURE_SIZE) + return; + + const unsigned char *data = reinterpret_cast(t.constData()); + + int width = 0; + for (int y=0;y width) && (data[(y*TEXT_WIDTH+x)*4] != 0x00)) + width = x; + } + } + qhUserTextures[id] = UserTexture(width, t); + qsForce.insert(id); + setTexts(qlCurrentTexts); +} + +typedef QPair qpChanCol; + +void Overlay::updateOverlay() { + quint32 colPlayer = g.s.qcOverlayPlayer.rgba(); + quint32 colTalking = g.s.qcOverlayTalking.rgba(); + quint32 colAltTalking = g.s.qcOverlayAltTalking.rgba(); + quint32 colChannel = g.s.qcOverlayChannel.rgba(); + quint32 colChannelTalking = g.s.qcOverlayChannelTalking.rgba(); + QString str; + QList linkchans; + + if (! isActive()) + return; + + if (g.uiSession) { + Channel *home = ClientPlayer::get(g.uiSession)->cChannel; + foreach(Channel *c, home->allLinks()) { + if (home == c) + continue; + + bool act = false; + foreach(Player *p, c->qlPlayers) { + act = act || p->bTalking; + if (p->bTalking) + linkchans << qpChanCol(p->qsName + QString::fromLatin1("[") + c->qsName + QString::fromLatin1("]"), colChannelTalking); + } + if (! act) + linkchans << qpChanCol(c->qsName, colChannel); + } + qSort(linkchans); + } + + QList lines; + + if (g.uiSession) { + + if (g.s.bOverlayTop) { + foreach(qpChanCol cc, linkchans) { + if ((g.s.osOverlay == Settings::All) || (cc.second == colChannelTalking)) { + lines << TextLine(cc.first, cc.second); + } + } + if (linkchans.count() > 0) { + lines << TextLine(QString(), 0); + } + } + + foreach(Player *p, ClientPlayer::get(g.uiSession)->cChannel->qlPlayers) { + if ((g.s.osOverlay == Settings::All) || p->bTalking || ((p == ClientPlayer::get(g.uiSession)) && g.s.bOverlayAlwaysSelf)) { + if (g.s.bOverlayUserTextures && (p->iId >= 0) && (! qhQueried.contains(p->iId))) { + qhQueried.insert(p->iId, p->qsName); + MessageTexture mt; + mt.iPlayerId = p->iId; + g.sh->sendMessage(&mt); + } + QString name = p->qsName; + Decoration dec = None; + if (p->bDeaf || p->bSelfDeaf) + dec = Deafened; + else if (p->bMute || p->bSelfMute || p->bLocalMute) + dec = Muted; + lines << TextLine(name, p->bTalking ? (p->bAltSpeak ? colAltTalking : colTalking) : colPlayer, p->iId, dec); + } + } + + if (! g.s.bOverlayTop) { + if (linkchans.count() > 0) { + lines << TextLine(QString(), 0); + } + foreach(qpChanCol cc, linkchans) { + if ((g.s.osOverlay == Settings::All) || (cc.second == colChannelTalking)) { + lines << TextLine(cc.first, cc.second); + } + } + } + } else { + qhUserTextures.clear(); + clearCache(); + } + setTexts(lines); +} + +/* + * Here's the thing. The painterpath, and what you get if you just use painter. They're different. + * The font metrics? They must be for martian charaters; human ones fit inside a much smaller box. + * + * So. If at first you don't succeed, try and try again. + */ + +void Overlay::fixFont() { + qfFont = g.s.qfOverlayFont; + + qfFont.setStyleStrategy(QFont::ForceOutline); + + int psize = TEXT_HEIGHT; + + QRectF br; + + do { + qfFont.setPixelSize(psize--); + QPainterPath qp; + qp.addText(0, 0, qfFont, QLatin1String("Üy")); + br=qp.boundingRect(); + } while ((br.height()+2) > TEXT_HEIGHT); + + fFontBase = static_cast(fabs(br.top())); + + clearCache(); + + qlCurrentTexts.clear(); +} + +void Overlay::clearCache() { + foreach(unsigned char *ptr, qhTextures) + delete [] ptr; + + qhTextures.clear(); + qhWidths.clear(); + qhQueried.clear(); +} + +void Overlay::setTexts(const QList &lines) { + foreach(const TextLine &e, lines) { + if ((! e.qsText.isEmpty()) && (! qhTextures.contains(e.qsText)) && (! qhUserTextures.contains(e.iPlayer))) { + unsigned char *td = new unsigned char[TEXTURE_SIZE]; + memset(td, 0, TEXTURE_SIZE); + + QImage qi(td, TEXT_WIDTH, TEXT_HEIGHT, QImage::Format_ARGB32); + + QPainterPath qp; + qp.addText(2, fFontBase, qfFont, e.qsText); + + QPainter p(&qi); + p.setRenderHint(QPainter::Antialiasing); + p.setRenderHint(QPainter::TextAntialiasing); + p.setBrush(Qt::white); + + // Draw with big border, this will be the "outline" + p.setPen(QPen(Qt::black, 3, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); + p.drawPath(qp); + + // And again, all white with no border. This avoids thin fonts being just black outline. + p.setPen(Qt::NoPen); + p.drawPath(qp); + + qhTextures[e.qsText] = td; + qhWidths[e.qsText] = qMin(static_cast(qp.boundingRect().width()+6), static_cast(TEXT_WIDTH)); + } + } + + if (! sm.tryLock()) + return; + + int i; + + for (i=0;i= NUM_TEXTS) + break; + + const TextLine &tl = lines.at(i); + TextEntry *te = & sm.sm->texts[i]; + + tl.qsText.left(127).toWCharArray(te->text); + te->color = lines[i].uiColor; + + if ((i >= qlCurrentTexts.count()) || (qlCurrentTexts[i].dDecor != tl.dDecor) || (qlCurrentTexts[i].qsText != tl.qsText) || qsForce.contains(tl.iPlayer)) { + if (tl.qsText.isNull()) { + te->width = 0; + } else { + int width = 0; + const unsigned char *src = NULL; + + if (qhUserTextures.contains(tl.iPlayer)) { + const UserTexture &ut=qhUserTextures.value(tl.iPlayer); + width = ut.first; + src = reinterpret_cast(ut.second.constData()); + } else { + width = qhWidths[tl.qsText]; + src = qhTextures[tl.qsText]; + } + + unsigned char * dst = NULL; + + if (tl.dDecor != None) { + unsigned char * decdst; + const unsigned char * decsrc = reinterpret_cast((tl.dDecor == Muted) ? qbaMuted.constData() : qbaDeafened.constData()); + + width = qMin(TEXT_WIDTH - TEXT_HEIGHT, width); + if (g.s.bOverlayLeft) { + dst = sm.sm->texts[i].texture + TEXT_HEIGHT * 4; + decdst = sm.sm->texts[i].texture; + } else { + dst = sm.sm->texts[i].texture; + decdst = sm.sm->texts[i].texture + width * 4; + } + for (int j=0;jtexts[i].texture; + } + + for (int j=0;jwidth = static_cast(width); + te->uiCounter++; + } + qsForce.remove(tl.iPlayer); + } + } + + for (;itexts[i].width = -1; + } + + qlCurrentTexts = lines; + sm.unlock(); +} diff --git a/src/mumble/TextMessage.cpp b/src/mumble/TextMessage.cpp index 6d9133b1e..b22807277 100644 --- a/src/mumble/TextMessage.cpp +++ b/src/mumble/TextMessage.cpp @@ -1,107 +1,107 @@ -/* Copyright (C) 2005-2009, Thorvald Natvig - - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - - Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - Neither the name of the Mumble Developers nor the names of its - contributors may be used to endorse or promote products derived from this - software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include "TextMessage.h" - -TextMessage::TextMessage(QWidget *p) : QDialog(p) { - setupUi(this); - qtbPreview->document()->setDefaultStyleSheet(qApp->styleSheet()); - qteEdit->installEventFilter(this); - qteEdit->setFocus(); -} - -void TextMessage::on_qcbRawMessage_stateChanged(int) { - on_qteEdit_textChanged(); -} - -void TextMessage::on_qteEdit_textChanged() { - qsRep = qteEdit->toPlainText(); - - if (qcbRawMessage->isChecked()) { - qsRep = Qt::convertFromPlainText(qsRep); - } - - if (! Qt::mightBeRichText(qsRep) && !qcbRawMessage->isChecked()) { - QRegExp qr; - qr.setMinimal(true); - qr.setPatternSyntax(QRegExp::RegExp2); - qr.setCaseSensitivity(Qt::CaseInsensitive); - - qr.setPattern(QLatin1String("[\\r\\n]+")); - qsRep.replace(qr, QLatin1String("
")); - - qr.setPattern(QLatin1String("\\*(\\w+)\\*")); - qsRep.replace(qr, QLatin1String("\\1")); - - qr.setPattern(QLatin1String("\"([^\"]+)\"")); - qsRep.replace(qr, QLatin1String("\"\\1\"")); - - qr.setPattern(QLatin1String("[a-z]+://[^ <$]*")); - qr.setMinimal(false); - - int idx = 0; - do { - idx = qr.indexIn(qsRep, idx); - if (idx >= 0) { - QString url = qr.capturedTexts().at(0); - QUrl u(url); - if (u.isValid()) { - int len = qr.matchedLength(); - QString replacement = QString::fromLatin1("%1").arg(url); - qsRep.replace(idx, len, replacement); - idx += replacement.length(); - } else { - idx++; - } - } - } while (idx >= 0); - } - - qtbPreview->setHtml(qsRep); -} - -QString TextMessage::message() { - return qsRep; -} - -bool TextMessage::eventFilter(QObject *obj, QEvent *evt) { - if (obj != qteEdit) - return false; - if (evt->type() == QEvent::KeyPress) { - QKeyEvent *keyEvent = static_cast(evt); - if (((keyEvent->key() == Qt::Key_Enter) || (keyEvent->key() == Qt::Key_Return)) && - (keyEvent->modifiers() == Qt::NoModifier)) { - accept(); - return true; - } - } - return false; -} - +/* Copyright (C) 2005-2009, Thorvald Natvig + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + - Neither the name of the Mumble Developers nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "TextMessage.h" + +TextMessage::TextMessage(QWidget *p) : QDialog(p) { + setupUi(this); + qtbPreview->document()->setDefaultStyleSheet(qApp->styleSheet()); + qteEdit->installEventFilter(this); + qteEdit->setFocus(); +} + +void TextMessage::on_qcbRawMessage_stateChanged(int) { + on_qteEdit_textChanged(); +} + +void TextMessage::on_qteEdit_textChanged() { + qsRep = qteEdit->toPlainText(); + + if (qcbRawMessage->isChecked()) { + qsRep = Qt::convertFromPlainText(qsRep); + } + + if (! Qt::mightBeRichText(qsRep) && !qcbRawMessage->isChecked()) { + QRegExp qr; + qr.setMinimal(true); + qr.setPatternSyntax(QRegExp::RegExp2); + qr.setCaseSensitivity(Qt::CaseInsensitive); + + qr.setPattern(QLatin1String("[\\r\\n]+")); + qsRep.replace(qr, QLatin1String("
")); + + qr.setPattern(QLatin1String("\\*(\\w+)\\*")); + qsRep.replace(qr, QLatin1String("\\1")); + + qr.setPattern(QLatin1String("\"([^\"]+)\"")); + qsRep.replace(qr, QLatin1String("\"\\1\"")); + + qr.setPattern(QLatin1String("[a-z]+://[^ <$]*")); + qr.setMinimal(false); + + int idx = 0; + do { + idx = qr.indexIn(qsRep, idx); + if (idx >= 0) { + QString url = qr.capturedTexts().at(0); + QUrl u(url); + if (u.isValid()) { + int len = qr.matchedLength(); + QString replacement = QString::fromLatin1("%1").arg(url); + qsRep.replace(idx, len, replacement); + idx += replacement.length(); + } else { + idx++; + } + } + } while (idx >= 0); + } + + qtbPreview->setHtml(qsRep); +} + +QString TextMessage::message() { + return qsRep; +} + +bool TextMessage::eventFilter(QObject *obj, QEvent *evt) { + if (obj != qteEdit) + return false; + if (evt->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = static_cast(evt); + if (((keyEvent->key() == Qt::Key_Enter) || (keyEvent->key() == Qt::Key_Return)) && + (keyEvent->modifiers() == Qt::NoModifier)) { + accept(); + return true; + } + } + return false; +} + diff --git a/src/mumble/TextMessage.h b/src/mumble/TextMessage.h index d153f5c61..36d859bef 100644 --- a/src/mumble/TextMessage.h +++ b/src/mumble/TextMessage.h @@ -1,52 +1,52 @@ -/* Copyright (C) 2005-2009, Thorvald Natvig - - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - - Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - Neither the name of the Mumble Developers nor the names of its - contributors may be used to endorse or promote products derived from this - software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef _TEXTMESSAGE_H -#define _TEXTMESSAGE_H - -#include "mumble_pch.hpp" -#include "ui_TextMessage.h" - -class TextMessage : public QDialog, public Ui::TextMessage { - private: - Q_OBJECT - Q_DISABLE_COPY(TextMessage) - protected: - QString qsRep; - public: - TextMessage(QWidget *parent = NULL); - QString message(); - bool eventFilter(QObject *obj, QEvent *event); - public slots: - void on_qteEdit_textChanged(); - void on_qcbRawMessage_stateChanged(int); -}; - -#endif +/* Copyright (C) 2005-2009, Thorvald Natvig + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + - Neither the name of the Mumble Developers nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _TEXTMESSAGE_H +#define _TEXTMESSAGE_H + +#include "mumble_pch.hpp" +#include "ui_TextMessage.h" + +class TextMessage : public QDialog, public Ui::TextMessage { + private: + Q_OBJECT + Q_DISABLE_COPY(TextMessage) + protected: + QString qsRep; + public: + TextMessage(QWidget *parent = NULL); + QString message(); + bool eventFilter(QObject *obj, QEvent *event); + public slots: + void on_qteEdit_textChanged(); + void on_qcbRawMessage_stateChanged(int); +}; + +#endif diff --git a/src/mumble/ViewCert.cpp b/src/mumble/ViewCert.cpp index 7a6d2730f..54511169a 100644 --- a/src/mumble/ViewCert.cpp +++ b/src/mumble/ViewCert.cpp @@ -1,97 +1,97 @@ -/* Copyright (C) 2005-2009, Thorvald Natvig - - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - - Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - Neither the name of the Mumble Developers nor the names of its - contributors may be used to endorse or promote products derived from this - software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include "ViewCert.h" - -ViewCert::ViewCert(QList cl, QWidget *p) : QDialog(p) { - qlCerts = cl; - - setWindowTitle(tr("Certificate Chain Details")); - - QHBoxLayout *h; - QVBoxLayout *v; - QGroupBox *qcbChain, *qcbDetails; - - qcbChain=new QGroupBox(tr("Certificate chain"), this); - h = new QHBoxLayout(qcbChain); - qlwChain = new QListWidget(qcbChain); - qlwChain->setObjectName(QLatin1String("Chain")); - - foreach(QSslCertificate c, qlCerts) - qlwChain->addItem(tr("%1 %2").arg(c.subjectInfo(QSslCertificate::CommonName)).arg(c.subjectInfo(QSslCertificate::Organization))); - h->addWidget(qlwChain); - - qcbDetails=new QGroupBox(tr("Certificate details"), this); - h = new QHBoxLayout(qcbDetails); - qlwCert = new QListWidget(qcbDetails); - h->addWidget(qlwCert); - - QDialogButtonBox *qdbb = new QDialogButtonBox(QDialogButtonBox::Ok, Qt::Horizontal, this); - - v = new QVBoxLayout(this); - v->addWidget(qcbChain); - v->addWidget(qcbDetails); - v->addWidget(qdbb); - - QMetaObject::connectSlotsByName(this); - connect(qdbb, SIGNAL(accepted()), this, SLOT(accept())); - - resize(500,300); -} - -void ViewCert::on_Chain_currentRowChanged(int idx) { - qlwCert->clear(); - if ((idx < 0) || (idx >= qlCerts.size())) - return; - - QStringList l; - const QSslCertificate &c=qlCerts.at(idx); - l << tr("Common Name: %1").arg(c.subjectInfo(QSslCertificate::CommonName)); - l << tr("Organization: %1").arg(c.subjectInfo(QSslCertificate::Organization)); - l << tr("Subunit: %1").arg(c.subjectInfo(QSslCertificate::OrganizationalUnitName)); - l << tr("Country: %1").arg(c.subjectInfo(QSslCertificate::CountryName)); - l << tr("Locality: %1").arg(c.subjectInfo(QSslCertificate::LocalityName)); - l << tr("State: %1").arg(c.subjectInfo(QSslCertificate::StateOrProvinceName)); - l << tr("Valid from: %1").arg(c.effectiveDate().toString()); - l << tr("Valid to: %1").arg(c.expiryDate().toString()); - l << tr("Serial: %1").arg(QString::fromLatin1(c.serialNumber().toHex())); - l << tr("Public Key: %1 bits %2").arg(c.publicKey().length()).arg((c.publicKey().algorithm() == QSsl::Rsa) ? tr("RSA") : tr("DSA")); - l << tr("Digest (MD5): %1").arg(QString::fromLatin1(c.digest().toHex())); - l << QString(); - l << tr("Issued by:"); - l << tr("Common Name: %1").arg(c.issuerInfo(QSslCertificate::CommonName)); - l << tr("Organization: %1").arg(c.issuerInfo(QSslCertificate::Organization)); - l << tr("Unit Name: %1").arg(c.issuerInfo(QSslCertificate::OrganizationalUnitName)); - l << tr("Country: %1").arg(c.issuerInfo(QSslCertificate::CountryName)); - l << tr("Locality: %1").arg(c.issuerInfo(QSslCertificate::LocalityName)); - l << tr("State: %1").arg(c.issuerInfo(QSslCertificate::StateOrProvinceName)); - - qlwCert->addItems(l); -} +/* Copyright (C) 2005-2009, Thorvald Natvig + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + - Neither the name of the Mumble Developers nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "ViewCert.h" + +ViewCert::ViewCert(QList cl, QWidget *p) : QDialog(p) { + qlCerts = cl; + + setWindowTitle(tr("Certificate Chain Details")); + + QHBoxLayout *h; + QVBoxLayout *v; + QGroupBox *qcbChain, *qcbDetails; + + qcbChain=new QGroupBox(tr("Certificate chain"), this); + h = new QHBoxLayout(qcbChain); + qlwChain = new QListWidget(qcbChain); + qlwChain->setObjectName(QLatin1String("Chain")); + + foreach(QSslCertificate c, qlCerts) + qlwChain->addItem(tr("%1 %2").arg(c.subjectInfo(QSslCertificate::CommonName)).arg(c.subjectInfo(QSslCertificate::Organization))); + h->addWidget(qlwChain); + + qcbDetails=new QGroupBox(tr("Certificate details"), this); + h = new QHBoxLayout(qcbDetails); + qlwCert = new QListWidget(qcbDetails); + h->addWidget(qlwCert); + + QDialogButtonBox *qdbb = new QDialogButtonBox(QDialogButtonBox::Ok, Qt::Horizontal, this); + + v = new QVBoxLayout(this); + v->addWidget(qcbChain); + v->addWidget(qcbDetails); + v->addWidget(qdbb); + + QMetaObject::connectSlotsByName(this); + connect(qdbb, SIGNAL(accepted()), this, SLOT(accept())); + + resize(500,300); +} + +void ViewCert::on_Chain_currentRowChanged(int idx) { + qlwCert->clear(); + if ((idx < 0) || (idx >= qlCerts.size())) + return; + + QStringList l; + const QSslCertificate &c=qlCerts.at(idx); + l << tr("Common Name: %1").arg(c.subjectInfo(QSslCertificate::CommonName)); + l << tr("Organization: %1").arg(c.subjectInfo(QSslCertificate::Organization)); + l << tr("Subunit: %1").arg(c.subjectInfo(QSslCertificate::OrganizationalUnitName)); + l << tr("Country: %1").arg(c.subjectInfo(QSslCertificate::CountryName)); + l << tr("Locality: %1").arg(c.subjectInfo(QSslCertificate::LocalityName)); + l << tr("State: %1").arg(c.subjectInfo(QSslCertificate::StateOrProvinceName)); + l << tr("Valid from: %1").arg(c.effectiveDate().toString()); + l << tr("Valid to: %1").arg(c.expiryDate().toString()); + l << tr("Serial: %1").arg(QString::fromLatin1(c.serialNumber().toHex())); + l << tr("Public Key: %1 bits %2").arg(c.publicKey().length()).arg((c.publicKey().algorithm() == QSsl::Rsa) ? tr("RSA") : tr("DSA")); + l << tr("Digest (MD5): %1").arg(QString::fromLatin1(c.digest().toHex())); + l << QString(); + l << tr("Issued by:"); + l << tr("Common Name: %1").arg(c.issuerInfo(QSslCertificate::CommonName)); + l << tr("Organization: %1").arg(c.issuerInfo(QSslCertificate::Organization)); + l << tr("Unit Name: %1").arg(c.issuerInfo(QSslCertificate::OrganizationalUnitName)); + l << tr("Country: %1").arg(c.issuerInfo(QSslCertificate::CountryName)); + l << tr("Locality: %1").arg(c.issuerInfo(QSslCertificate::LocalityName)); + l << tr("State: %1").arg(c.issuerInfo(QSslCertificate::StateOrProvinceName)); + + qlwCert->addItems(l); +} diff --git a/src/mumble/ViewCert.h b/src/mumble/ViewCert.h index c8a2023e2..5d7683bdb 100644 --- a/src/mumble/ViewCert.h +++ b/src/mumble/ViewCert.h @@ -1,49 +1,49 @@ -/* Copyright (C) 2005-2009, Thorvald Natvig - - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - - Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - Neither the name of the Mumble Developers nor the names of its - contributors may be used to endorse or promote products derived from this - software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef _VIEWCERT_H -#define _VIEWCERT_H - -#include "mumble_pch.hpp" - -class ViewCert : public QDialog { - private: - Q_OBJECT - Q_DISABLE_COPY(ViewCert) - protected: - QList qlCerts; - QListWidget *qlwChain, *qlwCert; - protected slots: - void on_Chain_currentRowChanged(int); - public: - ViewCert(QList c, QWidget *p); -}; - -#endif +/* Copyright (C) 2005-2009, Thorvald Natvig + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + - Neither the name of the Mumble Developers nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _VIEWCERT_H +#define _VIEWCERT_H + +#include "mumble_pch.hpp" + +class ViewCert : public QDialog { + private: + Q_OBJECT + Q_DISABLE_COPY(ViewCert) + protected: + QList qlCerts; + QListWidget *qlwChain, *qlwCert; + protected slots: + void on_Chain_currentRowChanged(int); + public: + ViewCert(QList c, QWidget *p); +}; + +#endif diff --git a/src/mumble/mumble.pro b/src/mumble/mumble.pro index 93adc7f56..a38577d61 100644 --- a/src/mumble/mumble.pro +++ b/src/mumble/mumble.pro @@ -8,7 +8,7 @@ SOURCES = BanEditor.cpp ACLEditor.cpp Log.cpp AudioConfigDialog.cpp AudioStats. HEADERS *= ../ACL.h ../Group.h ../Channel.h ../Connection.h ../Player.h SOURCES *= ../ACL.cpp ../Group.cpp ../Channel.cpp ../Message.cpp ../Connection.cpp ../Player.cpp ../Timer.cpp ../CryptState.cpp ../OSInfo.cpp SOURCES *= smallft.cpp -DIST *= licenses.h smallft.h mumble.ico mumble.xpm plugins/mumble_plugin.h mumble-overlay mumble.desktop mumble.protocol murmur_pch.h 11-input-mumble-policy.fdi mumble.plist +DIST *= ../../icons/mumble.ico licenses.h smallft.h ../../icons/mumble.xpm murmur_pch.h mumble.plist RESOURCES *= mumble.qrc FORMS *= ConfigDialog.ui MainWindow.ui ConnectDialog.ui BanEditor.ui ACLEditor.ui Plugins.ui Overlay.ui LookConfig.ui AudioInput.ui AudioOutput.ui Log.ui TextMessage.ui AudioStats.ui NetworkConfig.ui LCD.ui GlobalShortcut.ui TRANSLATIONS = mumble_en.ts mumble_es.ts mumble_de.ts mumble_tr.ts mumble_fr.ts mumble_ru.ts mumble_cs.ts mumble_ja.ts mumble_pl.ts diff --git a/src/mumble/murmur_pch.h b/src/mumble/murmur_pch.h index bc99930b4..36b3ae1f3 100644 --- a/src/mumble/murmur_pch.h +++ b/src/mumble/murmur_pch.h @@ -1 +1 @@ -#include "mumble_pch.hpp" +#include "mumble_pch.hpp" diff --git a/src/murmur/Cert.cpp b/src/murmur/Cert.cpp index 3d12337b8..d2840c982 100644 --- a/src/murmur/Cert.cpp +++ b/src/murmur/Cert.cpp @@ -1,166 +1,166 @@ -/* Copyright (C) 2005-2009, Thorvald Natvig - - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - - Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - Neither the name of the Mumble Developers nor the names of its - contributors may be used to endorse or promote products derived from this - software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include "Server.h" -#include "Meta.h" - -int add_ext(X509 * crt, int nid, char *value) { - X509_EXTENSION *ex; - X509V3_CTX ctx; - X509V3_set_ctx_nodb(&ctx); - X509V3_set_ctx(&ctx, crt, crt, NULL, NULL, 0); - ex = X509V3_EXT_conf_nid(NULL, &ctx, nid, value); - if (!ex) - return 0; - - X509_add_ext(crt, ex, -1); - X509_EXTENSION_free(ex); - return 1; -} - -void Server::initializeCert() { - QByteArray crt, key, pass; - - if (! QSslSocket::supportsSsl()) { - qFatal("Qt without SSL Support"); - } - - crt = getConf("certificate", QString()).toByteArray(); - key = getConf("key", QString()).toByteArray(); - pass = getConf("passphrase", QByteArray()).toByteArray(); - - if (! crt.isEmpty()) { - qscCert = QSslCertificate(crt); - if (qscCert.isNull()) { - log("Failed to parse certificate."); - } else if (qscCert.issuerInfo(QSslCertificate::CommonName) == QLatin1String("Murmur Autogenerated Certificate")) { - log("Old autogenerated certificate is unusable for registration, invalidating it"); - qscCert = QSslCertificate(); - } - } - - if (! key.isEmpty() && qscCert.isNull()) { - qscCert = QSslCertificate(key); - if (! qscCert.isNull()) { - log("Using certificate from key."); - } - } - - if (! qscCert.isNull()) { - QSsl::KeyAlgorithm alg = qscCert.publicKey().algorithm(); - - if (! key.isEmpty()) { - qskKey = QSslKey(key, alg, QSsl::Pem, QSsl::PrivateKey, pass); - if (qskKey.isNull()) { - log("Failed to parse key."); - } - } - - if (! crt.isEmpty() && qskKey.isNull()) { - qskKey = QSslKey(crt, alg, QSsl::Pem, QSsl::PrivateKey, pass); - if (! qskKey.isNull()) { - log("Using key from certificate."); - } - } - - } - - if (qscCert.isNull() || qskKey.isNull()) { - if (! key.isEmpty() || ! crt.isEmpty()) { - log("Certificate specified, but failed to load."); - } - qskKey = Meta::mp.qskKey; - qscCert = Meta::mp.qscCert; - if (qscCert.isNull() || qskKey.isNull()) { - log("Generating new server certificate."); - - BIO *bio_err; - - CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON); - - bio_err=BIO_new_fp(stderr, BIO_NOCLOSE); - - X509 *x509 = X509_new(); - EVP_PKEY *pkey = EVP_PKEY_new(); - RSA *rsa = RSA_generate_key(1024,RSA_F4,NULL,NULL); - EVP_PKEY_assign_RSA(pkey, rsa); - - X509_set_version(x509, 2); - ASN1_INTEGER_set(X509_get_serialNumber(x509),1); - X509_gmtime_adj(X509_get_notBefore(x509),0); - X509_gmtime_adj(X509_get_notAfter(x509),60*60*24*365); - X509_set_pubkey(x509, pkey); - - X509_NAME *name=X509_get_subject_name(x509); - - X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, reinterpret_cast(const_cast("Murmur Autogenerated Certificate v2")), -1, -1, 0); - X509_set_issuer_name(x509, name); - add_ext(x509, NID_basic_constraints, "critical,CA:FALSE"); - add_ext(x509, NID_ext_key_usage, "serverAuth,clientAuth"); - add_ext(x509, NID_subject_key_identifier, "hash"); - add_ext(x509, NID_netscape_comment, "Generated from murmur"); - - X509_sign(x509, pkey, EVP_md5()); - - crt.resize(i2d_X509(x509, NULL)); - unsigned char *dptr=reinterpret_cast(crt.data()); - i2d_X509(x509, &dptr); - - qscCert = QSslCertificate(crt, QSsl::Der); - if (qscCert.isNull()) - log("Certificate generation failed"); - - key.resize(i2d_PrivateKey(pkey, NULL)); - dptr=reinterpret_cast(key.data()); - i2d_PrivateKey(pkey, &dptr); - - qskKey = QSslKey(key, QSsl::Rsa, QSsl::Der); - if (qskKey.isNull()) - log("Key generation failed"); - - setConf("certificate", qscCert.toPem()); - setConf("key", qskKey.toPem()); - } - } - - QList pref; - foreach(QSslCipher c, QSslSocket::defaultCiphers()) { - if (c.usedBits() < 128) - continue; - pref << c; - } - if (pref.isEmpty()) - qFatal("No ciphers of at least 128 bit found"); - QSslSocket::setDefaultCiphers(pref); -} - -const QString Server::getDigest() const { - return QString::fromLatin1(qscCert.digest().toHex()); -} +/* Copyright (C) 2005-2009, Thorvald Natvig + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + - Neither the name of the Mumble Developers nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "Server.h" +#include "Meta.h" + +int add_ext(X509 * crt, int nid, char *value) { + X509_EXTENSION *ex; + X509V3_CTX ctx; + X509V3_set_ctx_nodb(&ctx); + X509V3_set_ctx(&ctx, crt, crt, NULL, NULL, 0); + ex = X509V3_EXT_conf_nid(NULL, &ctx, nid, value); + if (!ex) + return 0; + + X509_add_ext(crt, ex, -1); + X509_EXTENSION_free(ex); + return 1; +} + +void Server::initializeCert() { + QByteArray crt, key, pass; + + if (! QSslSocket::supportsSsl()) { + qFatal("Qt without SSL Support"); + } + + crt = getConf("certificate", QString()).toByteArray(); + key = getConf("key", QString()).toByteArray(); + pass = getConf("passphrase", QByteArray()).toByteArray(); + + if (! crt.isEmpty()) { + qscCert = QSslCertificate(crt); + if (qscCert.isNull()) { + log("Failed to parse certificate."); + } else if (qscCert.issuerInfo(QSslCertificate::CommonName) == QLatin1String("Murmur Autogenerated Certificate")) { + log("Old autogenerated certificate is unusable for registration, invalidating it"); + qscCert = QSslCertificate(); + } + } + + if (! key.isEmpty() && qscCert.isNull()) { + qscCert = QSslCertificate(key); + if (! qscCert.isNull()) { + log("Using certificate from key."); + } + } + + if (! qscCert.isNull()) { + QSsl::KeyAlgorithm alg = qscCert.publicKey().algorithm(); + + if (! key.isEmpty()) { + qskKey = QSslKey(key, alg, QSsl::Pem, QSsl::PrivateKey, pass); + if (qskKey.isNull()) { + log("Failed to parse key."); + } + } + + if (! crt.isEmpty() && qskKey.isNull()) { + qskKey = QSslKey(crt, alg, QSsl::Pem, QSsl::PrivateKey, pass); + if (! qskKey.isNull()) { + log("Using key from certificate."); + } + } + + } + + if (qscCert.isNull() || qskKey.isNull()) { + if (! key.isEmpty() || ! crt.isEmpty()) { + log("Certificate specified, but failed to load."); + } + qskKey = Meta::mp.qskKey; + qscCert = Meta::mp.qscCert; + if (qscCert.isNull() || qskKey.isNull()) { + log("Generating new server certificate."); + + BIO *bio_err; + + CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON); + + bio_err=BIO_new_fp(stderr, BIO_NOCLOSE); + + X509 *x509 = X509_new(); + EVP_PKEY *pkey = EVP_PKEY_new(); + RSA *rsa = RSA_generate_key(1024,RSA_F4,NULL,NULL); + EVP_PKEY_assign_RSA(pkey, rsa); + + X509_set_version(x509, 2); + ASN1_INTEGER_set(X509_get_serialNumber(x509),1); + X509_gmtime_adj(X509_get_notBefore(x509),0); + X509_gmtime_adj(X509_get_notAfter(x509),60*60*24*365); + X509_set_pubkey(x509, pkey); + + X509_NAME *name=X509_get_subject_name(x509); + + X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, reinterpret_cast(const_cast("Murmur Autogenerated Certificate v2")), -1, -1, 0); + X509_set_issuer_name(x509, name); + add_ext(x509, NID_basic_constraints, "critical,CA:FALSE"); + add_ext(x509, NID_ext_key_usage, "serverAuth,clientAuth"); + add_ext(x509, NID_subject_key_identifier, "hash"); + add_ext(x509, NID_netscape_comment, "Generated from murmur"); + + X509_sign(x509, pkey, EVP_md5()); + + crt.resize(i2d_X509(x509, NULL)); + unsigned char *dptr=reinterpret_cast(crt.data()); + i2d_X509(x509, &dptr); + + qscCert = QSslCertificate(crt, QSsl::Der); + if (qscCert.isNull()) + log("Certificate generation failed"); + + key.resize(i2d_PrivateKey(pkey, NULL)); + dptr=reinterpret_cast(key.data()); + i2d_PrivateKey(pkey, &dptr); + + qskKey = QSslKey(key, QSsl::Rsa, QSsl::Der); + if (qskKey.isNull()) + log("Key generation failed"); + + setConf("certificate", qscCert.toPem()); + setConf("key", qskKey.toPem()); + } + } + + QList pref; + foreach(QSslCipher c, QSslSocket::defaultCiphers()) { + if (c.usedBits() < 128) + continue; + pref << c; + } + if (pref.isEmpty()) + qFatal("No ciphers of at least 128 bit found"); + QSslSocket::setDefaultCiphers(pref); +} + +const QString Server::getDigest() const { + return QString::fromLatin1(qscCert.digest().toHex()); +} diff --git a/src/murmur/Tray.cpp b/src/murmur/Tray.cpp index 3b1360488..219dc5f3e 100644 --- a/src/murmur/Tray.cpp +++ b/src/murmur/Tray.cpp @@ -1,97 +1,97 @@ -/* Copyright (C) 2005-2009, Thorvald Natvig - - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - - Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - Neither the name of the Mumble Developers nor the names of its - contributors may be used to endorse or promote products derived from this - software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include "Tray.h" -#include "Server.h" -#include "Version.h" - -Tray::Tray(QObject *p, LogEmitter *logger) : QObject(p) { - le = logger; - - qsti = new QSystemTrayIcon(qApp->windowIcon(), this); - qsti->setObjectName(QLatin1String("Tray")); - qsti->setToolTip(tr("Murmur")); - - qaQuit = new QAction(tr("&Quit Murmur"), this); - qaQuit->setShortcut(tr("Ctrl+Q", "Quit")); - qaQuit->setObjectName(QLatin1String("Quit")); - - qaShowLog = new QAction(tr("&Show Log"), this); - qaShowLog->setShortcut(tr("Ctrl+L", "Quit")); - qaShowLog->setObjectName(QLatin1String("ShowLog")); - - // Can't construct a QMenu which decends from QObject, and qsti is a QObject. - // Qt bug? - qm = new QMenu(tr("Murmur"), NULL); - qm->addAction(qaQuit); - qm->addSeparator(); - qm->addAction(qaShowLog); - qsti->setContextMenu(qm); - - qsti->show(); - - connect(le, SIGNAL(newLogEntry(const QString &)), this, SLOT(addLogMessage(const QString &))); - - QMetaObject::connectSlotsByName(this); -} - -void Tray::on_Tray_activated(QSystemTrayIcon::ActivationReason r) { - if (r == QSystemTrayIcon::Trigger) { - qsti->showMessage(tr("Murmur"), tr("%1 server running.").arg(meta->qhServers.count()), QSystemTrayIcon::Information, 5000); - } -} - -void Tray::on_Quit_triggered() { - if (QMessageBox::question(NULL, tr("Murmur"), tr("Are you sure you wish to quit murmur?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) { - qApp->quit(); - } -} - -void Tray::on_ShowLog_triggered() { - QMainWindow *mw = new QMainWindow(); - mw->setAttribute(Qt::WA_DeleteOnClose); - QTextBrowser *tb = new QTextBrowser(); - mw->setCentralWidget(tb); - mw->setWindowTitle(QString::fromLatin1("Murmur -- %1").arg(MUMBLE_RELEASE)); - - connect(le, SIGNAL(newLogEntry(const QString &)), tb, SLOT(append(const QString &))); - - foreach(const QString &m, qlLog) - tb->append(m); - - mw->show(); -} - -void Tray::addLogMessage(const QString &msg) { - if (qlLog.count() >= 1000) - qlLog.removeFirst(); - - qlLog.append(msg); -} +/* Copyright (C) 2005-2009, Thorvald Natvig + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + - Neither the name of the Mumble Developers nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "Tray.h" +#include "Server.h" +#include "Version.h" + +Tray::Tray(QObject *p, LogEmitter *logger) : QObject(p) { + le = logger; + + qsti = new QSystemTrayIcon(qApp->windowIcon(), this); + qsti->setObjectName(QLatin1String("Tray")); + qsti->setToolTip(tr("Murmur")); + + qaQuit = new QAction(tr("&Quit Murmur"), this); + qaQuit->setShortcut(tr("Ctrl+Q", "Quit")); + qaQuit->setObjectName(QLatin1String("Quit")); + + qaShowLog = new QAction(tr("&Show Log"), this); + qaShowLog->setShortcut(tr("Ctrl+L", "Quit")); + qaShowLog->setObjectName(QLatin1String("ShowLog")); + + // Can't construct a QMenu which decends from QObject, and qsti is a QObject. + // Qt bug? + qm = new QMenu(tr("Murmur"), NULL); + qm->addAction(qaQuit); + qm->addSeparator(); + qm->addAction(qaShowLog); + qsti->setContextMenu(qm); + + qsti->show(); + + connect(le, SIGNAL(newLogEntry(const QString &)), this, SLOT(addLogMessage(const QString &))); + + QMetaObject::connectSlotsByName(this); +} + +void Tray::on_Tray_activated(QSystemTrayIcon::ActivationReason r) { + if (r == QSystemTrayIcon::Trigger) { + qsti->showMessage(tr("Murmur"), tr("%1 server running.").arg(meta->qhServers.count()), QSystemTrayIcon::Information, 5000); + } +} + +void Tray::on_Quit_triggered() { + if (QMessageBox::question(NULL, tr("Murmur"), tr("Are you sure you wish to quit murmur?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) { + qApp->quit(); + } +} + +void Tray::on_ShowLog_triggered() { + QMainWindow *mw = new QMainWindow(); + mw->setAttribute(Qt::WA_DeleteOnClose); + QTextBrowser *tb = new QTextBrowser(); + mw->setCentralWidget(tb); + mw->setWindowTitle(QString::fromLatin1("Murmur -- %1").arg(MUMBLE_RELEASE)); + + connect(le, SIGNAL(newLogEntry(const QString &)), tb, SLOT(append(const QString &))); + + foreach(const QString &m, qlLog) + tb->append(m); + + mw->show(); +} + +void Tray::addLogMessage(const QString &msg) { + if (qlLog.count() >= 1000) + qlLog.removeFirst(); + + qlLog.append(msg); +} diff --git a/src/murmur/Tray.h b/src/murmur/Tray.h index aa6cd447c..a82743a65 100644 --- a/src/murmur/Tray.h +++ b/src/murmur/Tray.h @@ -1,57 +1,57 @@ -/* Copyright (C) 2005-2009, Thorvald Natvig - - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - - Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - Neither the name of the Mumble Developers nor the names of its - contributors may be used to endorse or promote products derived from this - software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef _TRAY_H -#define _TRAY_H - -#include "murmur_pch.h" -#include "Server.h" - -class Tray : public QObject { - private: - Q_OBJECT - Q_DISABLE_COPY(Tray); - protected: - QSystemTrayIcon *qsti; - QMenu *qm; - QAction *qaQuit; - QAction *qaShowLog; - QStringList qlLog; - LogEmitter *le; - public slots: - void on_Tray_activated(QSystemTrayIcon::ActivationReason); - void on_Quit_triggered(); - void on_ShowLog_triggered(); - void addLogMessage(const QString &); - public: - Tray(QObject *parent, LogEmitter *le); -}; - -#endif +/* Copyright (C) 2005-2009, Thorvald Natvig + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + - Neither the name of the Mumble Developers nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _TRAY_H +#define _TRAY_H + +#include "murmur_pch.h" +#include "Server.h" + +class Tray : public QObject { + private: + Q_OBJECT + Q_DISABLE_COPY(Tray); + protected: + QSystemTrayIcon *qsti; + QMenu *qm; + QAction *qaQuit; + QAction *qaShowLog; + QStringList qlLog; + LogEmitter *le; + public slots: + void on_Tray_activated(QSystemTrayIcon::ActivationReason); + void on_Quit_triggered(); + void on_ShowLog_triggered(); + void addLogMessage(const QString &); + public: + Tray(QObject *parent, LogEmitter *le); +}; + +#endif diff --git a/src/murmur/UnixMurmur.cpp b/src/murmur/UnixMurmur.cpp index 478a0b570..fc337d1a8 100644 --- a/src/murmur/UnixMurmur.cpp +++ b/src/murmur/UnixMurmur.cpp @@ -209,6 +209,23 @@ void UnixMurmur::handleSigTerm() { void UnixMurmur::setuid() { if (Meta::mp.uiUid != 0) { +#ifdef Q_OS_DARWIN + qCritical("WARNING: You are launching murmurd as root on Mac OS X or Darwin. Murmur does not need " + "special privileges to set itself up on these systems, so this behavior is highly discouraged."); + + if (::setgid(Meta::mp.uiGid) != 0) + qFatal("Failed to switch to gid %d", Meta::mp.uiGid); + if (::setuid(Meta::mp.uiUid) != 0) + qFatal("Failed to switch to uid %d", Meta::mp.uiUid); + + uid_t uid = getuid(), euid = geteuid(); + gid_t gid = getgid(), egid = getegid(); + if (uid == Meta::mp.uiUid && euid == Meta::mp.uiUid + && gid == Meta::mp.uiGid && egid == Meta::mp.uiGid) { + qCritical("Successfully switched to uid %d", Meta::mp.uiUid); + } else + qFatal("Couldn't switch uid/gid."); +#else if (setregid(Meta::mp.uiGid, Meta::mp.uiGid) != 0) qCritical("Failed to switch to gid %d", Meta::mp.uiGid); if (setresuid(Meta::mp.uiUid, Meta::mp.uiUid, 0) != 0) { @@ -217,6 +234,7 @@ void UnixMurmur::setuid() { qCritical("Successfully switched to uid %d", Meta::mp.uiUid); initialcap(); } +#endif } else if (bRoot) { qCritical("WARNING: You are running murmurd as root, without setting a uname in the ini file. This might be a security risk."); } diff --git a/src/murmur/UnixMurmur.h b/src/murmur/UnixMurmur.h index c11288f0c..f47f05e02 100644 --- a/src/murmur/UnixMurmur.h +++ b/src/murmur/UnixMurmur.h @@ -1,71 +1,71 @@ -/* Copyright (C) 2005-2009, Thorvald Natvig - - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - - Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - Neither the name of the Mumble Developers nor the names of its - contributors may be used to endorse or promote products derived from this - software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef _UNIXMURMUR_H -#define _UNIXMURMUR_H - -#include "murmur_pch.h" - -class LimitTest : public QThread { - Q_OBJECT - Q_DISABLE_COPY(LimitTest) - protected: - static QMutex *qm; - static QWaitCondition *qw, *qstartw; - LimitTest(); - public: - int tid; - void run(); - static void testLimits(QCoreApplication &); -}; - -class UnixMurmur : public QObject { - Q_OBJECT - Q_DISABLE_COPY(UnixMurmur) - protected: - bool bRoot; - static int iHupFd[2], iTermFd[2]; - QSocketNotifier *qsnHup, *qsnTerm; - - static void hupSignalHandler(int); - static void termSignalHandler(int); - public slots: - void handleSigHup(); - void handleSigTerm(); - public: - void setuid(); - void initialcap(); - void finalcap(); - - UnixMurmur(); - ~UnixMurmur(); -}; - -#endif +/* Copyright (C) 2005-2009, Thorvald Natvig + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + - Neither the name of the Mumble Developers nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _UNIXMURMUR_H +#define _UNIXMURMUR_H + +#include "murmur_pch.h" + +class LimitTest : public QThread { + Q_OBJECT + Q_DISABLE_COPY(LimitTest) + protected: + static QMutex *qm; + static QWaitCondition *qw, *qstartw; + LimitTest(); + public: + int tid; + void run(); + static void testLimits(QCoreApplication &); +}; + +class UnixMurmur : public QObject { + Q_OBJECT + Q_DISABLE_COPY(UnixMurmur) + protected: + bool bRoot; + static int iHupFd[2], iTermFd[2]; + QSocketNotifier *qsnHup, *qsnTerm; + + static void hupSignalHandler(int); + static void termSignalHandler(int); + public slots: + void handleSigHup(); + void handleSigTerm(); + public: + void setuid(); + void initialcap(); + void finalcap(); + + UnixMurmur(); + ~UnixMurmur(); +}; + +#endif diff --git a/src/murmur/murmur.pro b/src/murmur/murmur.pro index 7eeb96f9f..97aeb5228 100644 --- a/src/murmur/murmur.pro +++ b/src/murmur/murmur.pro @@ -17,9 +17,8 @@ SOURCES = main.cpp Server.cpp ServerDB.cpp Register.cpp Cert.cpp Messages.cpp Me HEADERS *= ../ACL.h ../Group.h ../Channel.h ../Connection.h ../Player.h SOURCES *= ../ACL.cpp ../Group.cpp ../Channel.cpp ../Message.cpp ../Connection.cpp ../Player.cpp ../Timer.cpp ../CryptState.cpp ../OSInfo.cpp -DIST = DBus.h ServerDB.h murmur.ico Murmur.ice MurmurI.h MurmurIceWrapper.cpp +DIST = DBus.h ServerDB.h ../../icons/murmur.ico Murmur.ice MurmurI.h MurmurIceWrapper.cpp PRECOMPILED_HEADER = murmur_pch.h -DIST *= murmur.pl murmur.ini murmur.ini.system murmur.init murmur.conf murmur.logrotate dbusauth.pl weblist.pl weblist.php icedemo.php murmur-user-wrapper !CONFIG(no-ice) { CONFIG *= ice