967 lines
25 KiB
C++
967 lines
25 KiB
C++
|
|
// PTZControlDlg.cpp : implementation file
|
|
//
|
|
|
|
#include "pch.h"
|
|
#include "framework.h"
|
|
#include "PTZControl.h"
|
|
#include "PTZControlDlg.h"
|
|
#include "SettingsDlg.h"
|
|
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#endif
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
// Array of supported cameras
|
|
// Allow the cameras with the following tags in the device name.
|
|
// This will match all Logitech PRT Pro, PTZ Pro 2 and Rally cameras.
|
|
// Also the ConferenceCam CC3000e Camera will be detected.
|
|
// Remember: Just a partial token must match the name.
|
|
static const LPCTSTR g_aCameras[] =
|
|
{
|
|
_T("PTZ Pro"),
|
|
_T("Logi Rally"),
|
|
_T("ConferenceCam"),
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
// We have several layouts for the Buttons
|
|
// 0-1 cameras (dialog will shrink by one column)
|
|
// E |
|
|
// S |
|
|
// 2 cameras
|
|
// S E
|
|
// - -
|
|
// 1 2
|
|
// 3 cameras
|
|
// 1 E
|
|
// 2 S
|
|
// 3
|
|
|
|
struct SPosition
|
|
{
|
|
UINT nId; // Id of button
|
|
bool bShow; // Show or hide
|
|
int x, y; // outside raster, left two columns
|
|
};
|
|
|
|
struct SLayout
|
|
{
|
|
int cxDelta; // Num to shrink / grow the dialog
|
|
const SPosition* pButtons; // List of entries terminated with nId==0.
|
|
};
|
|
|
|
static const SPosition layoutBtns1[] // 0 or 1 camera (uses only 1 column)
|
|
{
|
|
IDC_BT_EXIT, true, 0, 0, // one columns
|
|
IDC_BT_SETTINGS, true, 0, 1,
|
|
// Invisible
|
|
IDC_BT_WEBCAM1, false, 0, 0,
|
|
IDC_BT_WEBCAM2, false, 0, 0,
|
|
IDC_BT_WEBCAM3, false, 0, 0,
|
|
IDC_BT_WEBCAM4, false, 0, 0,
|
|
0
|
|
};
|
|
|
|
static const SPosition layoutBtns2[] // 2 cameras (uses 2 columns)
|
|
{
|
|
IDC_BT_SETTINGS, true, 0, 0, // Top row
|
|
IDC_BT_EXIT, true, 1, 0,
|
|
IDC_BT_WEBCAM1, true, 0, 2, // bottom row
|
|
IDC_BT_WEBCAM2, true, 1, 2,
|
|
// Invisible
|
|
IDC_BT_WEBCAM3, false, 0, 0,
|
|
IDC_BT_WEBCAM4, false, 0, 0,
|
|
0
|
|
};
|
|
|
|
static const SPosition layoutBtns3[] // 3 cameras
|
|
{
|
|
IDC_BT_EXIT, true, 1, 0, // Outer left column
|
|
IDC_BT_SETTINGS, true, 1, 1,
|
|
IDC_BT_WEBCAM1, true, 0, 0, // all in one column left (top down)
|
|
IDC_BT_WEBCAM2, true, 0, 1,
|
|
IDC_BT_WEBCAM3, true, 0, 2,
|
|
IDC_BT_WEBCAM4, true, 0, 0,
|
|
0
|
|
};
|
|
|
|
static const SPosition layoutBtns4[] // 4 cameras
|
|
{
|
|
IDC_BT_EXIT, true, 1, 0, // Outer left column
|
|
IDC_BT_SETTINGS, true, 1, 1,
|
|
IDC_BT_WEBCAM1, true, 0, 0, // all in one column left (top down)
|
|
IDC_BT_WEBCAM2, true, 0, 1,
|
|
IDC_BT_WEBCAM3, true, 0, 2,
|
|
IDC_BT_WEBCAM4, true, 1, 2,
|
|
0
|
|
};
|
|
|
|
const SLayout g_layout[CPTZControlDlg::NUM_MAX_WEBCAMS+1] =
|
|
{
|
|
-1, layoutBtns1, // 0 or 1 camera (shrink dialog)
|
|
-1, layoutBtns1, // 0 or 1 camera (shrink dialog)
|
|
0, layoutBtns2, // 2 cameras (keep size)
|
|
0, layoutBtns3, // 3 cameras (keep size)
|
|
0, layoutBtns4, // 4 cameras (keep size)
|
|
};
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
// Special new notification
|
|
|
|
#define ON_BN_UNPUSHED(id, memberFxn) \
|
|
ON_CONTROL(BN_UNPUSHED, id, memberFxn)
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
// CPTZButton
|
|
|
|
IMPLEMENT_DYNAMIC(CPTZButton,CMFCButton)
|
|
|
|
CPTZButton::CPTZButton()
|
|
: m_bAutoRepeat(false)
|
|
, m_uiSent(0)
|
|
{
|
|
|
|
}
|
|
|
|
void CPTZButton::PreSubclassWindow()
|
|
{
|
|
__super::PreSubclassWindow();
|
|
SetTooltip(_T(""));
|
|
// Setting delay time doesn't seam to work.
|
|
GetToolTipCtrl().SetDelayTime(0);
|
|
}
|
|
|
|
void CPTZButton::OnDrawBorder(CDC* pDC, CRect& rectClient, UINT uiState)
|
|
{
|
|
__super::OnDrawBorder(pDC,rectClient,uiState);
|
|
|
|
// Give the button our button face. But only when the mouse isn't over
|
|
if (m_bWinXPTheme && m_clrFace!=COLORREF(-1)/* && !m_bHover && !m_bHighlighted*/)
|
|
pDC->FillSolidRect(rectClient, m_clrFace);
|
|
}
|
|
|
|
void CPTZButton::OnDrawFocusRect(CDC* pDC, const CRect& rectClient)
|
|
{
|
|
UNUSED_ALWAYS(pDC); UNUSED_ALWAYS(rectClient);
|
|
}
|
|
|
|
void CPTZButton::OnLButtonDown(UINT nFlags, CPoint point)
|
|
{
|
|
if (m_bAutoRepeat)
|
|
{
|
|
SetTimer(TIMER_AUTO_REPEAT, AUTO_REPEAT_INITIAL_DELAY, NULL);
|
|
m_uiSent = 0;
|
|
}
|
|
__super::OnLButtonDown(nFlags, point);
|
|
}
|
|
|
|
void CPTZButton::OnLButtonUp(UINT nFlags, CPoint point)
|
|
{
|
|
if (m_bAutoRepeat)
|
|
{
|
|
KillTimer(TIMER_AUTO_REPEAT);
|
|
|
|
if (GetCapture() != NULL)
|
|
{
|
|
// If we never sent a message we do it once.
|
|
if (m_uiSent==0 && (GetState() & BST_PUSHED) != 0)
|
|
GetParent()->SendMessage(WM_COMMAND, MAKELONG(GetDlgCtrlID(), BN_CLICKED), (LPARAM)m_hWnd);
|
|
else
|
|
GetParent()->SendMessage(WM_COMMAND, MAKELONG(GetDlgCtrlID(), BN_UNPUSHED), (LPARAM)m_hWnd);
|
|
// release capture
|
|
ReleaseCapture();
|
|
}
|
|
}
|
|
else
|
|
// Never call the default in auto repeat
|
|
__super::OnLButtonUp(nFlags, point);
|
|
}
|
|
|
|
void CPTZButton::OnTimer(UINT_PTR nIDEvent)
|
|
{
|
|
if (nIDEvent==TIMER_AUTO_REPEAT)
|
|
{
|
|
if (m_bAutoRepeat)
|
|
{
|
|
if ((GetState() & BST_PUSHED) == 0)
|
|
return;
|
|
++m_uiSent;
|
|
SetTimer(TIMER_AUTO_REPEAT, AUTO_REPEAT_DELAY, NULL);
|
|
GetParent()->SendMessage(WM_COMMAND, MAKELONG(GetDlgCtrlID(), BN_CLICKED), (LPARAM)m_hWnd);
|
|
}
|
|
}
|
|
|
|
__super::OnTimer(nIDEvent);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL AdjustVisibleWindowRect(LPRECT lpRect, HWND hWndParent=NULL)
|
|
{
|
|
RECT rcOrg(*lpRect);
|
|
|
|
RECT rcBase;
|
|
if (hWndParent)
|
|
// Any parent given?
|
|
::GetClientRect(hWndParent,&rcBase);
|
|
else
|
|
{
|
|
::MONITORINFO mi;
|
|
mi.cbSize = sizeof(mi);
|
|
if (!AfxGetMainWnd())
|
|
// If we have no main window we just try to use the main screen point 0,0
|
|
::GetMonitorInfo(::MonitorFromPoint(CPoint(0,0), MONITOR_DEFAULTTONEAREST), &mi);
|
|
else
|
|
// Get the main window monitor
|
|
::GetMonitorInfo(::MonitorFromWindow(AfxGetMainWnd()->m_hWnd, MONITOR_DEFAULTTONEAREST), &mi);
|
|
rcBase = mi.rcWork;
|
|
}
|
|
|
|
// To large?
|
|
if (lpRect->bottom-lpRect->top>rcBase.bottom-rcBase.top)
|
|
lpRect->bottom -= (lpRect->bottom-lpRect->top)-(rcBase.bottom-rcBase.top);
|
|
if (lpRect->right-lpRect->left>rcBase.right-rcBase.left)
|
|
lpRect->right -= (lpRect->right-lpRect->left)-(rcBase.right-rcBase.left);
|
|
|
|
// Check if rect is visible
|
|
if (lpRect->bottom>rcBase.bottom)
|
|
::OffsetRect(lpRect,0,rcBase.bottom-lpRect->bottom);
|
|
if (lpRect->top<rcBase.top)
|
|
::OffsetRect(lpRect,0,rcBase.top-lpRect->top);
|
|
if (lpRect->right>rcBase.right)
|
|
::OffsetRect(lpRect,rcBase.right-lpRect->right,0);
|
|
if (lpRect->left<rcBase.left)
|
|
::OffsetRect(lpRect,rcBase.left-lpRect->left,0);
|
|
|
|
// Check if coords changes
|
|
return !EqualRect(lpRect,&rcOrg);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
// CPTZControlDlg dialog
|
|
|
|
CPTZControlDlg::CPTZControlDlg(CWnd* pParent /*=nullptr*/)
|
|
: CDialogEx(IDD_PTZCONTROL_DIALOG, pParent)
|
|
, m_hAccel(NULL)
|
|
, m_iCurrentWebCam(0)
|
|
, m_iNumWebCams(0)
|
|
, m_evTerminating(FALSE,TRUE)
|
|
, m_pGuardThread(nullptr)
|
|
{
|
|
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
|
|
m_hAccel = ::LoadAccelerators(AfxFindResourceHandle(IDR_ACCELERATOR, RT_ACCELERATOR), MAKEINTRESOURCE(IDR_ACCELERATOR));
|
|
}
|
|
|
|
CPTZControlDlg::~CPTZControlDlg()
|
|
{
|
|
for (auto &webcam : m_aWebCams)
|
|
webcam.CloseDevice();
|
|
}
|
|
|
|
void CPTZControlDlg::PostNcDestroy()
|
|
{
|
|
__super::PostNcDestroy();
|
|
|
|
// Cleanup the guard thread.
|
|
m_evTerminating.SetEvent();
|
|
if (m_pGuardThread)
|
|
::WaitForSingleObject(m_pGuardThread->m_hThread,INFINITE);
|
|
m_pGuardThread = nullptr;
|
|
|
|
// Finally delete the application.
|
|
delete this;
|
|
}
|
|
|
|
BOOL CPTZControlDlg::OnCommand(WPARAM wParam, LPARAM lParam)
|
|
{
|
|
return __super::OnCommand(wParam,lParam);
|
|
}
|
|
|
|
|
|
BOOL CPTZControlDlg::PreTranslateMessage(MSG* pMsg)
|
|
{
|
|
if (pMsg->message>=WM_KEYFIRST && pMsg->message<=WM_KEYLAST)
|
|
{
|
|
if (m_hAccel)
|
|
{
|
|
if (::TranslateAccelerator(m_hWnd, m_hAccel, pMsg))
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return __super::PreTranslateMessage(pMsg);
|
|
}
|
|
|
|
void CPTZControlDlg::DoDataExchange(CDataExchange* pDX)
|
|
{
|
|
__super::DoDataExchange(pDX);
|
|
DDX_Control(pDX, IDC_BT_ZOOM_IN, m_btZoomIn);
|
|
DDX_Control(pDX, IDC_BT_ZOOM_OUT, m_btZoomOut);
|
|
DDX_Control(pDX, IDC_BT_UP, m_btUp);
|
|
DDX_Control(pDX, IDC_BT_DOWN, m_btDown);
|
|
DDX_Control(pDX, IDC_BT_HOME, m_btHome);
|
|
DDX_Control(pDX, IDC_BT_LEFT, m_btLeft);
|
|
DDX_Control(pDX, IDC_BT_RIGHT, m_btRight);
|
|
DDX_Control(pDX, IDC_BT_MEMORY, m_btMemory);
|
|
DDX_Control(pDX, IDC_BT_PRESET1, m_btPreset[0]);
|
|
DDX_Control(pDX, IDC_BT_PRESET2, m_btPreset[1]);
|
|
DDX_Control(pDX, IDC_BT_PRESET3, m_btPreset[2]);
|
|
DDX_Control(pDX, IDC_BT_PRESET4, m_btPreset[3]);
|
|
DDX_Control(pDX, IDC_BT_PRESET5, m_btPreset[4]);
|
|
DDX_Control(pDX, IDC_BT_PRESET6, m_btPreset[5]);
|
|
DDX_Control(pDX, IDC_BT_PRESET7, m_btPreset[6]);
|
|
DDX_Control(pDX, IDC_BT_PRESET8, m_btPreset[7]);
|
|
DDX_Control(pDX, IDC_BT_EXIT, m_btExit);
|
|
DDX_Control(pDX, IDC_BT_SETTINGS, m_btSettings);
|
|
DDX_Control(pDX, IDC_BT_WEBCAM1, m_btWebCam[0]);
|
|
DDX_Control(pDX, IDC_BT_WEBCAM2, m_btWebCam[1]);
|
|
DDX_Control(pDX, IDC_BT_WEBCAM3, m_btWebCam[2]);
|
|
DDX_Control(pDX, IDC_BT_WEBCAM4, m_btWebCam[3]);
|
|
}
|
|
|
|
BEGIN_MESSAGE_MAP(CPTZControlDlg, CDialogEx)
|
|
ON_WM_PAINT()
|
|
ON_WM_QUERYDRAGICON()
|
|
ON_WM_CLOSE()
|
|
ON_WM_NCHITTEST()
|
|
ON_BN_CLICKED(IDC_BT_EXIT, &CPTZControlDlg::OnBtExit)
|
|
ON_WM_SETFOCUS()
|
|
ON_BN_CLICKED(IDC_BT_MEMORY, &CPTZControlDlg::OnBtMemory)
|
|
ON_COMMAND_EX(IDC_BT_WEBCAM1, &CPTZControlDlg::OnBtWebCam)
|
|
ON_COMMAND_EX(IDC_BT_WEBCAM2, &CPTZControlDlg::OnBtWebCam)
|
|
ON_COMMAND_EX(IDC_BT_WEBCAM3, &CPTZControlDlg::OnBtWebCam)
|
|
ON_COMMAND_EX(IDC_BT_WEBCAM4, &CPTZControlDlg::OnBtWebCam)
|
|
ON_COMMAND_EX(IDC_BT_PRESET1, &CPTZControlDlg::OnBtPreset)
|
|
ON_COMMAND_EX(IDC_BT_PRESET2, &CPTZControlDlg::OnBtPreset)
|
|
ON_COMMAND_EX(IDC_BT_PRESET3, &CPTZControlDlg::OnBtPreset)
|
|
ON_COMMAND_EX(IDC_BT_PRESET4, &CPTZControlDlg::OnBtPreset)
|
|
ON_COMMAND_EX(IDC_BT_PRESET5, &CPTZControlDlg::OnBtPreset)
|
|
ON_COMMAND_EX(IDC_BT_PRESET6, &CPTZControlDlg::OnBtPreset)
|
|
ON_COMMAND_EX(IDC_BT_PRESET7, &CPTZControlDlg::OnBtPreset)
|
|
ON_COMMAND_EX(IDC_BT_PRESET8, &CPTZControlDlg::OnBtPreset)
|
|
ON_BN_CLICKED(IDC_BT_ZOOM_IN, &CPTZControlDlg::OnBtZoomIn)
|
|
ON_BN_CLICKED(IDC_BT_ZOOM_OUT, &CPTZControlDlg::OnBtZoomOut)
|
|
ON_BN_CLICKED(IDC_BT_UP, &CPTZControlDlg::OnBtUp)
|
|
ON_BN_CLICKED(IDC_BT_DOWN, &CPTZControlDlg::OnBtDown)
|
|
ON_BN_CLICKED(IDC_BT_LEFT, &CPTZControlDlg::OnBtLeft)
|
|
ON_BN_CLICKED(IDC_BT_HOME, &CPTZControlDlg::OnBtHome)
|
|
ON_BN_CLICKED(IDC_BT_RIGHT, &CPTZControlDlg::OnBtRight)
|
|
ON_BN_CLICKED(IDC_BT_SETTINGS, &CPTZControlDlg::OnBtSettings)
|
|
ON_BN_UNPUSHED(IDC_BT_UP, &CPTZControlDlg::OnBtUnpushed)
|
|
ON_BN_UNPUSHED(IDC_BT_DOWN, &CPTZControlDlg::OnBtUnpushed)
|
|
ON_BN_UNPUSHED(IDC_BT_LEFT, &CPTZControlDlg::OnBtUnpushed)
|
|
ON_BN_UNPUSHED(IDC_BT_RIGHT, &CPTZControlDlg::OnBtUnpushed)
|
|
ON_WM_TIMER()
|
|
END_MESSAGE_MAP()
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
// CPTZControlDlg message handlers
|
|
|
|
void CPTZControlDlg::ResetAllColors()
|
|
{
|
|
for (CWnd* pWnd = GetWindow(GW_CHILD); pWnd; pWnd = pWnd->GetWindow(GW_HWNDNEXT))
|
|
{
|
|
auto *pButton = DYNAMIC_DOWNCAST(CPTZButton, pWnd);
|
|
|
|
// Don't reset color for web cam button
|
|
bool bIsWebCamBtn = false;
|
|
for (auto &btn : m_btWebCam)
|
|
bIsWebCamBtn |= pButton==&btn;
|
|
|
|
// Reset color for all other buttons
|
|
if (pButton && !bIsWebCamBtn)
|
|
pButton->SetFaceColor(COLORREF(-1), TRUE);
|
|
}
|
|
}
|
|
|
|
void CPTZControlDlg::ResetMemButton()
|
|
{
|
|
// Clear the mem button
|
|
KillTimer(TIMER_CLEAR_MEMORY);
|
|
m_btMemory.SetCheck(0);
|
|
m_btMemory.SetFaceColor(COLORREF(-1));
|
|
}
|
|
|
|
CWebcamController& CPTZControlDlg::GetCurrentWebCam()
|
|
{
|
|
return m_aWebCams[m_iCurrentWebCam];
|
|
}
|
|
|
|
void CPTZControlDlg::SetActiveCam(int iCam)
|
|
{
|
|
if (iCam >= 0 && iCam < m_iNumWebCams)
|
|
{
|
|
// Clear mem button
|
|
ResetMemButton();
|
|
|
|
// Save the colors of the current buttons and set the old ones
|
|
for (CWnd* pWnd = GetWindow(GW_CHILD); pWnd; pWnd = pWnd->GetWindow(GW_HWNDNEXT))
|
|
{
|
|
auto* pButton = DYNAMIC_DOWNCAST(CPTZButton, pWnd);
|
|
if (pButton)
|
|
{
|
|
// Save the color of the current buttons for the current web cam and get
|
|
// the saved color from the map we have for the new cam.
|
|
auto nId = pButton->GetDlgCtrlID();
|
|
m_aMapBtnColors[m_iCurrentWebCam][nId] = pButton->GetFaceColor();
|
|
auto it = m_aMapBtnColors[iCam].find(nId);
|
|
if (it!=m_aMapBtnColors[iCam].end())
|
|
pButton->SetFaceColor(it->second);
|
|
}
|
|
}
|
|
|
|
// Set the tooltips
|
|
for (int i=0; i<CWebcamController::NUM_PRESETS; ++i)
|
|
m_btPreset[i].SetTooltip(m_strTooltips[iCam][i]);
|
|
|
|
// Set the new webcam
|
|
m_iCurrentWebCam = iCam;
|
|
auto Enable = [&](CPTZButton &btn, bool bActive)
|
|
{
|
|
btn.SetCheck(bActive);
|
|
btn.SetFaceColor(bActive ? COLOR_ORANGE : -1, TRUE);
|
|
};
|
|
int iWebCam = 0;
|
|
for (auto &btn : m_btWebCam)
|
|
Enable(btn,m_iCurrentWebCam==iWebCam++);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// It seams that in some cases a camera my block.
|
|
// This thread should help that the blocking thread is detected. and the
|
|
// application terminates.
|
|
|
|
UINT AFX_CDECL CPTZControlDlg::GuardThread(LPVOID p)
|
|
{
|
|
auto *pWnd = static_cast<CPTZControlDlg*>(p);
|
|
|
|
// This thread end when the application exists.
|
|
// For ever all 2000 seconds check if the application still runs and accepts messages.
|
|
for (;;)
|
|
{
|
|
// Check if we have an event, wait for 1 sec
|
|
CSingleLock lock(&pWnd->m_evTerminating,FALSE);
|
|
if (lock.Lock(1000))
|
|
// Event is set.
|
|
return 0;
|
|
|
|
DWORD_PTR dwResult = 0;
|
|
// Check if the application is blocking
|
|
if (::SendMessageTimeout(pWnd->GetSafeHwnd(), WM_NULL, 0, 0, SMTO_ABORTIFHUNG | SMTO_NORMAL, 1000, &dwResult)==0)
|
|
{
|
|
// Exit the current process
|
|
::ExitProcess(10);
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL CPTZControlDlg::OnInitDialog()
|
|
{
|
|
__super::OnInitDialog();
|
|
|
|
//---------------------------------------------------------------------
|
|
// INIT MFC STUFF
|
|
|
|
// Set the icon for this dialog. The framework does this automatically
|
|
// when the application's main window is not a dialog
|
|
SetIcon(m_hIcon, TRUE); // Set big icon
|
|
SetIcon(m_hIcon, FALSE); // Set small icon
|
|
|
|
// Load the bitmap
|
|
{
|
|
CMFCToolBarImages images;
|
|
images.SetImageSize(CSize(16, 16));
|
|
images.Load(IDB_BUTTONS);
|
|
|
|
int iImage = 0;
|
|
CPTZButton* apButtons[] =
|
|
{
|
|
&m_btDown,
|
|
&m_btLeft,
|
|
&m_btRight,
|
|
&m_btUp,
|
|
&m_btHome,
|
|
&m_btZoomIn,
|
|
&m_btZoomOut,
|
|
&m_btMemory,
|
|
&m_btPreset[0],
|
|
&m_btPreset[1],
|
|
&m_btPreset[2],
|
|
&m_btPreset[3],
|
|
&m_btPreset[4],
|
|
&m_btPreset[5],
|
|
&m_btPreset[6],
|
|
&m_btPreset[7],
|
|
&m_btExit,
|
|
&m_btSettings,
|
|
&m_btWebCam[0],
|
|
&m_btWebCam[1],
|
|
&m_btWebCam[2],
|
|
&m_btWebCam[3],
|
|
};
|
|
for (auto* pBtn : apButtons)
|
|
{
|
|
HICON hIcon = images.ExtractIcon(iImage++);
|
|
pBtn->SetImage(hIcon);
|
|
// we have a graphic now, we skip the text
|
|
pBtn->SetWindowText(_T(""));
|
|
}
|
|
}
|
|
|
|
// Set auto repeat buttons
|
|
m_btLeft.SetAutoRepeat(true);
|
|
m_btRight.SetAutoRepeat(true);
|
|
m_btDown.SetAutoRepeat(true);
|
|
m_btUp.SetAutoRepeat(true);
|
|
m_btZoomIn.SetAutoRepeat(true);
|
|
m_btZoomOut.SetAutoRepeat(true);
|
|
|
|
// This is a check box style
|
|
m_btMemory.SetCheckStyle();
|
|
for (auto &btn : m_btWebCam)
|
|
btn.SetCheckStyle();
|
|
|
|
//---------------------------------------------------------------------
|
|
// INIT AND FIND WEB CAMS
|
|
//
|
|
// Try to find the Device list
|
|
CStringArray aDevices;
|
|
CWebcamController::ListDevices(aDevices);
|
|
|
|
auto OpenWebCam = [](CWebcamController& webCam, CString strDevToken)->bool
|
|
{
|
|
HRESULT hr = webCam.OpenDevice(CComBSTR(strDevToken), 0, 0);
|
|
if (FAILED(hr))
|
|
{
|
|
AfxMessageBox(IDP_ERR_OPENFAILED);
|
|
return false;
|
|
}
|
|
|
|
// Load the default setting
|
|
webCam.UseLogitechMotionControl(theApp.GetProfileInt(REG_DEVICE, REG_USELOGOTECHMOTIONCONTROL, FALSE) != 0);
|
|
webCam.SetMotorIntervalTimer(theApp.GetProfileInt(REG_DEVICE, REG_MOTORINTERVALTIMER, DEFAULT_MOTOR_INTERVAL_TIMER));
|
|
return true;
|
|
};
|
|
|
|
// Find the devices to search for. We have some default devices if no other is set in
|
|
// the registry o on the command line.
|
|
CStringArray aStrCameraNameToSearch;
|
|
for (const auto *p : g_aCameras)
|
|
aStrCameraNameToSearch.Add(p);
|
|
CString strCameraNameToSearch = theApp.GetProfileString(REG_DEVICE, REG_DEVICENAME, _T(""));
|
|
if (!strCameraNameToSearch.IsEmpty())
|
|
aStrCameraNameToSearch.Add(strCameraNameToSearch);
|
|
if (!theApp.m_strDevName.IsEmpty())
|
|
aStrCameraNameToSearch.Add(theApp.m_strDevName);
|
|
|
|
// Search for the PTZ PRO 2
|
|
CString strDevToken, strCameras;
|
|
for (int i = 0; i<aDevices.GetCount() && m_iNumWebCams<NUM_MAX_WEBCAMS; ++i)
|
|
{
|
|
CString strDevice = aDevices[i], strCameraName, strCameraDevice;
|
|
int iPos = strDevice.Find(_T('\t'));
|
|
if (iPos != -1)
|
|
{
|
|
// Get name and device token
|
|
strCameraName = strDevice.Left(iPos);
|
|
strDevToken = strDevice.Mid(iPos + 1);
|
|
|
|
// Build list of names found
|
|
if (!strCameras.IsEmpty())
|
|
strCameras += _T("\r\n");
|
|
strCameras += strCameraName;
|
|
|
|
// Check if the name matches
|
|
for (int j=0; j<aStrCameraNameToSearch.GetCount(); ++j)
|
|
{
|
|
// Check if we have a asterisk. Or a partial name match.
|
|
if (aStrCameraNameToSearch[j]==_T("*") || strCameraName.Find(aStrCameraNameToSearch[j]) != -1)
|
|
{
|
|
if (OpenWebCam(m_aWebCams[m_iNumWebCams], strDevToken))
|
|
++m_iNumWebCams;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// save the camera names.
|
|
m_strCameraDeviceNames = strCameras;
|
|
|
|
// On SHIFT and CTRL key down we show all found camera device names.
|
|
if (theApp.m_bShowDevices || (::GetAsyncKeyState(VK_SHIFT) & 0x8000) && (::GetAsyncKeyState(VK_CONTROL) & 0x8000))
|
|
{
|
|
CString strMsg(MAKEINTRESOURCE(IDP_TXT_CAMERAS)), strText;
|
|
strText.FormatMessage(strMsg, strCameras.GetString());
|
|
AfxMessageBox(strText);
|
|
}
|
|
|
|
// Check how many web cams we found
|
|
if (m_iNumWebCams==0)
|
|
{
|
|
// If we have no cam, show message
|
|
AfxMessageBox(IDP_ERR_NO_CAMERA, MB_ICONERROR);
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// ADJUST THE DIALOG
|
|
|
|
// Calculate the with an position of the Grid.
|
|
CRect rectBtn11, rectBtn12, rectBtn21;
|
|
m_btWebCam[0].GetWindowRect(rectBtn11);
|
|
m_btWebCam[1].GetWindowRect(rectBtn12);
|
|
m_btExit.GetWindowRect(rectBtn21);
|
|
ScreenToClient(rectBtn11);
|
|
ScreenToClient(rectBtn12);
|
|
ScreenToClient(rectBtn21);
|
|
|
|
CPoint pointBase { rectBtn11.TopLeft() };
|
|
CSize sizeRaster { rectBtn21.left-rectBtn11.left, rectBtn12.top-rectBtn11.top };
|
|
|
|
// Get the required layout
|
|
const auto &layout = g_layout[m_iNumWebCams];
|
|
|
|
for (const auto* pLayoutBtn = layout.pButtons; pLayoutBtn->nId; ++pLayoutBtn)
|
|
{
|
|
CWnd *pWnd = GetDlgItem(pLayoutBtn->nId);
|
|
// Move the button and hide or show the button
|
|
pWnd->SetWindowPos(nullptr,
|
|
pointBase.x + pLayoutBtn->x * sizeRaster.cx, pointBase.y + pLayoutBtn->y * sizeRaster.cy,
|
|
0, 0, SWP_NOSIZE|SWP_NOZORDER|(pLayoutBtn->bShow ? SWP_SHOWWINDOW : SWP_HIDEWINDOW)
|
|
);
|
|
pWnd->EnableWindow(pLayoutBtn->bShow);
|
|
}
|
|
|
|
// First Center
|
|
CenterWindow();
|
|
|
|
// Adjust the window
|
|
CRect rect;
|
|
GetWindowRect(rect);
|
|
CPoint pt = rect.TopLeft();
|
|
rect.OffsetRect(-pt);
|
|
pt.x = theApp.GetProfileInt(REG_WINDOW, REG_WINDOW_POSX, pt.x);
|
|
pt.y = theApp.GetProfileInt(REG_WINDOW, REG_WINDOW_POSY, pt.y);
|
|
rect.OffsetRect(pt);
|
|
AdjustVisibleWindowRect(rect);
|
|
|
|
// Move it
|
|
SetWindowPos(&CWnd::wndTopMost, rect.left, rect.top, rect.Width()+layout.cxDelta*sizeRaster.cx, rect.Height(), 0);
|
|
|
|
// Set the tooltips for all presets
|
|
for (int i = 0; i < CWebcamController::NUM_PRESETS; ++i)
|
|
{
|
|
for (int j = 0; j < CPTZControlDlg::NUM_MAX_WEBCAMS; ++j)
|
|
{
|
|
CString str;
|
|
str.Format(REG_TOOLTIP, i + 1 + j*100);
|
|
m_strTooltips[j][i] = theApp.GetProfileString(REG_WINDOW, str);
|
|
}
|
|
}
|
|
|
|
EnableToolTips(TRUE);
|
|
|
|
// Start a guard thread that takes care about a blocking app.
|
|
if (!theApp.m_bNoGuard)
|
|
m_pGuardThread = AfxBeginThread(&GuardThread,this);
|
|
|
|
// Set a time to move the focus to the parent window
|
|
SetTimer(TIMER_FOCUS_CHECK,FOCUS_CHECK_DELAY,nullptr);
|
|
SetFocus();
|
|
|
|
// Move all cams to home position. And WebCam 0 will be the active one.
|
|
if (theApp.m_bNoReset)
|
|
{
|
|
// Activate camera 0.
|
|
if (m_iNumWebCams>0)
|
|
SetActiveCam(0);
|
|
}
|
|
else
|
|
{
|
|
// Reset the cameras to home position. and leave the first camera
|
|
// (index 0) the active one ((loop backwards).
|
|
for (int i = m_iNumWebCams; i > 0; --i)
|
|
{
|
|
SetActiveCam(i - 1);
|
|
OnBtHome();
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// If you add a minimize button to your dialog, you will need the code below
|
|
// to draw the icon. For MFC applications using the document/view model,
|
|
// this is automatically done for you by the framework.
|
|
|
|
void CPTZControlDlg::OnPaint()
|
|
{
|
|
if (IsIconic())
|
|
{
|
|
CPaintDC dc(this); // device context for painting
|
|
|
|
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
|
|
|
|
// Center icon in client rectangle
|
|
int cxIcon = GetSystemMetrics(SM_CXICON);
|
|
int cyIcon = GetSystemMetrics(SM_CYICON);
|
|
CRect rect;
|
|
GetClientRect(&rect);
|
|
int x = (rect.Width() - cxIcon + 1) / 2;
|
|
int y = (rect.Height() - cyIcon + 1) / 2;
|
|
|
|
// Draw the icon
|
|
dc.DrawIcon(x, y, m_hIcon);
|
|
}
|
|
else
|
|
{
|
|
__super::OnPaint();
|
|
}
|
|
}
|
|
|
|
// The system calls this function to obtain the cursor to display while the user drags
|
|
// the minimized window.
|
|
HCURSOR CPTZControlDlg::OnQueryDragIcon()
|
|
{
|
|
return static_cast<HCURSOR>(m_hIcon);
|
|
}
|
|
|
|
LRESULT CPTZControlDlg::OnNcHitTest(CPoint point)
|
|
{
|
|
// Allow drag & drop
|
|
LRESULT lResult = __super::OnNcHitTest(point);
|
|
if (lResult == HTCLIENT)
|
|
lResult = HTCAPTION;
|
|
return lResult;
|
|
}
|
|
|
|
void CPTZControlDlg::OnClose()
|
|
{
|
|
CRect rect;
|
|
GetWindowRect(rect);
|
|
theApp.WriteProfileInt(REG_WINDOW,REG_WINDOW_POSX,rect.left);
|
|
theApp.WriteProfileInt(REG_WINDOW,REG_WINDOW_POSY,rect.top);
|
|
|
|
DestroyWindow();
|
|
}
|
|
|
|
void CPTZControlDlg::OnBtExit()
|
|
{
|
|
PostMessage(WM_CLOSE);
|
|
}
|
|
|
|
|
|
void CPTZControlDlg::OnOK()
|
|
{
|
|
// Ignore
|
|
}
|
|
|
|
|
|
void CPTZControlDlg::OnCancel()
|
|
{
|
|
// Ignore
|
|
}
|
|
|
|
|
|
|
|
void CPTZControlDlg::OnBtMemory()
|
|
{
|
|
ResetAllColors();
|
|
bool bCheck = !m_btMemory.GetCheck();
|
|
m_btMemory.SetCheck(bCheck);
|
|
if (bCheck)
|
|
{
|
|
// Remember the mem button, but clear it after some delay.
|
|
m_btMemory.SetFaceColor(COLOR_RED, TRUE);
|
|
SetTimer(TIMER_CLEAR_MEMORY,CLEAR_MEMORY_DELAY,nullptr);
|
|
}
|
|
else
|
|
m_btMemory.SetFaceColor(COLORREF(-1),TRUE);
|
|
}
|
|
|
|
BEGIN_MESSAGE_MAP(CPTZButton, CMFCButton)
|
|
ON_WM_LBUTTONDOWN()
|
|
ON_WM_TIMER()
|
|
ON_WM_LBUTTONUP()
|
|
END_MESSAGE_MAP()
|
|
|
|
|
|
BOOL CPTZControlDlg::OnBtPreset(UINT nId)
|
|
{
|
|
UINT uiPreset = 0;
|
|
while (uiPreset<CWebcamController::NUM_PRESETS)
|
|
{
|
|
if (static_cast<UINT>(m_btPreset[uiPreset].GetDlgCtrlID())==nId)
|
|
break;
|
|
++uiPreset;
|
|
}
|
|
|
|
if (uiPreset<CWebcamController::NUM_PRESETS)
|
|
{
|
|
ResetAllColors();
|
|
bool bStore = m_btMemory.GetCheck();
|
|
if (bStore)
|
|
{
|
|
// Save as new preset
|
|
GetCurrentWebCam().SavePreset(uiPreset);
|
|
ResetMemButton();
|
|
}
|
|
else
|
|
GetCurrentWebCam().GotoPreset(uiPreset);
|
|
|
|
// Set color
|
|
STATIC_DOWNCAST(CPTZButton,GetDlgItem(nId))->SetFaceColor(COLOR_GREEN,TRUE);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CPTZControlDlg::OnBtWebCam(UINT nId)
|
|
{
|
|
SetActiveCam(nId==IDC_BT_WEBCAM1 ? 0 :
|
|
nId==IDC_BT_WEBCAM2 ? 1 :
|
|
nId==IDC_BT_WEBCAM3 ? 2 : 3);
|
|
return 1;
|
|
}
|
|
|
|
void CPTZControlDlg::OnBtHome()
|
|
{
|
|
ResetAllColors();
|
|
GetCurrentWebCam().GotoHome();
|
|
m_btHome.SetFaceColor(COLOR_GREEN,TRUE);
|
|
}
|
|
|
|
|
|
void CPTZControlDlg::OnBtZoomIn()
|
|
{
|
|
ResetAllColors();
|
|
GetCurrentWebCam().Zoom(1);
|
|
}
|
|
|
|
|
|
void CPTZControlDlg::OnBtZoomOut()
|
|
{
|
|
ResetAllColors();
|
|
GetCurrentWebCam().Zoom(-1);
|
|
}
|
|
|
|
|
|
void CPTZControlDlg::OnBtDown()
|
|
{
|
|
ResetAllColors();
|
|
if (m_btDown.InAutoRepeat())
|
|
GetCurrentWebCam().Tilt(-1);
|
|
else
|
|
GetCurrentWebCam().MoveTilt(-1);
|
|
}
|
|
|
|
void CPTZControlDlg::OnBtUp()
|
|
{
|
|
ResetAllColors();
|
|
if (m_btUp.InAutoRepeat())
|
|
GetCurrentWebCam().Tilt(1);
|
|
else
|
|
GetCurrentWebCam().MoveTilt(1);
|
|
}
|
|
|
|
|
|
void CPTZControlDlg::OnBtLeft()
|
|
{
|
|
ResetAllColors();
|
|
if (m_btLeft.InAutoRepeat())
|
|
GetCurrentWebCam().Pan(-1);
|
|
else
|
|
GetCurrentWebCam().MovePan(-1);
|
|
}
|
|
|
|
|
|
void CPTZControlDlg::OnBtRight()
|
|
{
|
|
ResetAllColors();
|
|
if (m_btRight.InAutoRepeat())
|
|
GetCurrentWebCam().Pan(1);
|
|
else
|
|
GetCurrentWebCam().MovePan(1);
|
|
}
|
|
|
|
|
|
void CPTZControlDlg::OnSetFocus(CWnd* pOldWnd)
|
|
{
|
|
// Do nothing. Never set the focus to a child.
|
|
UNUSED_ALWAYS(pOldWnd);
|
|
}
|
|
|
|
|
|
|
|
void CPTZControlDlg::OnTimer(UINT_PTR nIDEvent)
|
|
{
|
|
if (nIDEvent == TIMER_FOCUS_CHECK)
|
|
{
|
|
CWnd* pWndFocus = GetFocus();
|
|
if (pWndFocus &&
|
|
pWndFocus->GetParent()==this &&
|
|
(GetAsyncKeyState(VK_LBUTTON) & 0x8000)==0)
|
|
// only move the focus, when a button has the focus and the mouse is not down.
|
|
SetFocus();
|
|
}
|
|
else if (nIDEvent == TIMER_CLEAR_MEMORY)
|
|
{
|
|
// Clear the mem button after some delay
|
|
ResetMemButton();
|
|
}
|
|
|
|
__super::OnTimer(nIDEvent);
|
|
}
|
|
|
|
void CPTZControlDlg::OnBtUnpushed()
|
|
{
|
|
GetCurrentWebCam().Pan(0);
|
|
GetCurrentWebCam().Tilt(0);
|
|
}
|
|
|
|
void CPTZControlDlg::OnBtSettings()
|
|
{
|
|
CSettingsDlg dlg;
|
|
dlg.m_strCameraName = m_strCameraDeviceNames;
|
|
dlg.m_strCameraName.Replace(_T("\r\n"), _T(", "));
|
|
dlg.m_bLogitechCameraControl = GetCurrentWebCam().UseLogitechMotionControl();
|
|
dlg.m_iMotorIntervalTimer = GetCurrentWebCam().GetMotorIntervalTimer();
|
|
|
|
// Get a copy of the tooltips
|
|
for (int i = 0; i < CWebcamController::NUM_PRESETS; ++i)
|
|
{
|
|
for (int j = 0; j < CPTZControlDlg::NUM_MAX_WEBCAMS; ++j)
|
|
{
|
|
dlg.m_strTooltip[j][i] = m_strTooltips[j][i];
|
|
}
|
|
}
|
|
|
|
if (dlg.DoModal()!=IDOK)
|
|
return;
|
|
|
|
// Copy back and save
|
|
for (int i = 0; i < CWebcamController::NUM_PRESETS; ++i)
|
|
{
|
|
for (int j = 0; j < CPTZControlDlg::NUM_MAX_WEBCAMS; ++j)
|
|
{
|
|
m_strTooltips[j][i] = dlg.m_strTooltip[j][i];
|
|
CString str;
|
|
str.Format(REG_TOOLTIP, i + 1 + j*100);
|
|
theApp.WriteProfileString(REG_WINDOW, str, m_strTooltips[j][i]);
|
|
}
|
|
}
|
|
|
|
// Camera control
|
|
GetCurrentWebCam().UseLogitechMotionControl(dlg.m_bLogitechCameraControl!=0);
|
|
theApp.WriteProfileInt(REG_DEVICE, REG_USELOGOTECHMOTIONCONTROL, dlg.m_bLogitechCameraControl);
|
|
GetCurrentWebCam().SetMotorIntervalTimer(dlg.m_iMotorIntervalTimer);
|
|
theApp.WriteProfileInt(REG_DEVICE, REG_USELOGOTECHMOTIONCONTROL, dlg.m_bLogitechCameraControl);
|
|
|
|
// Set tooltips again
|
|
SetActiveCam(m_iCurrentWebCam);
|
|
}
|
|
|