/////////////////////////////////////////////////////////////////////////////
// Name: viacamcontroller.cpp
// Purpose:
// Author: Cesar Mauri Loba (cesar at crea-si dot com)
// Modified by:
// Created:
// Copyright: (C) 2008-14 Cesar Mauri Loba - CREA Software Systems
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
/////////////////////////////////////////////////////////////////////////////
#include "viacamcontroller.h"
#include "wviacam.h"
#include "crvcamera_enum.h"
#include "crvcamera.h"
#include "pointeraction.h"
#include "camwindow.h"
#include "wconfiguration.h"
#include "cmotioncalibration.h"
#include "wcameradialog.h"
#include "eviacamdefs.h"
#include "configmanager.h"
#include "cautostart.h"
#include "hotkeymanager.h"
#include "simplelog.h"
#include "checkupdates_manager.h"
#include
#include
#include
#include
#include
using namespace eviacam;
CViacamController::CViacamController(void)
: m_pMainWindow(NULL)
, m_pCamera(NULL)
, m_pCaptureThread(NULL)
, m_pointerAction(NULL)
, m_visionPipeline()
, m_hotKeyManager(NULL)
, m_configManager(NULL)
, m_locale(NULL)
, m_pAutostart(NULL)
, m_pConfiguration(NULL)
, m_pMotionCalibration(NULL)
, m_wizardManager()
, m_pCameraDialog(NULL)
, m_wConfigurationListener(*this)
, m_wCameraDialogListener(*this)
, m_pCheckUpdateManager(NULL)
, m_cameraName()
, m_enabled(false)
, m_enabledAtStartup(false)
, m_languageId(wxLANGUAGE_DEFAULT)
, m_onScreenKeyboardCommand()
, m_frameRate(0)
, m_motionCalibrationEnabled(false)
, m_runWizardAtStartup(false)
, m_checkUpdatesAtStartup(true)
, m_minimisedAtStartup(false)
{
m_locale= new wxLocale ();
m_configManager= new CConfigManager(this);
InitDefaults();
}
void CViacamController::InitDefaults()
{
m_runWizardAtStartup= true;
m_languageId= wxLANGUAGE_DEFAULT;
m_enabledAtStartup= false;
m_minimisedAtStartup = false;
#if defined(__WXMSW__)
m_onScreenKeyboardCommand= _T("osk.exe");
#endif
}
CViacamController::~CViacamController(void)
{
assert (!m_pCaptureThread && !m_pCamera && !m_pCaptureThread);
delete m_pointerAction;
#if defined(__WXGTK__)
delete m_pAutostart;
#endif
delete m_locale;
delete m_configManager;
delete m_pMotionCalibration;
delete m_hotKeyManager;
}
void CViacamController::SetUpLanguage ()
{
assert (m_locale);
m_languageId= m_configManager->ReadLanguage ();
m_locale->AddCatalogLookupPathPrefix(wxT("."));
#if defined(__WXGTK__)
wxString prefix= wxStandardPaths::Get().GetInstallPrefix();
prefix+= wxT("/share/locale/");
m_locale->AddCatalogLookupPathPrefix(prefix);
#endif
if (!m_locale->Init(m_languageId))
slog_write (SLOG_PRIO_WARNING, "Cannot load locale. Switching to default locale.\n");
m_locale->AddCatalog(wxT("wxstd"));
m_locale->AddCatalog(wxT("eviacam"));
}
void CViacamController::SetLanguage (const int id)
{
// Simply store new language id
if (id!= m_languageId) {
// Check if valid
const wxLanguageInfo *info= wxLocale::GetLanguageInfo (id);
if (info) {
m_languageId= id;
}
else {
assert (false);
}
}
}
bool testCamera(CCamera* cam) {
if (cam == NULL) return false;
IplImage* img = NULL;
if (cam->Open()) {
/*
Try to capture one frame
*/
SLOG_DEBUG("Camera successfully openned. Try to capture 1 frame...");
for (int i = 0; i < 10; i++) {
SLOG_DEBUG(" Wait a little bit");
wxMilliSleep(500); // Try not the stress the camera too much
SLOG_DEBUG(" Call QueryFrane");
img = cam->QueryFrame();
if (img) {
SLOG_DEBUG(" Frame capture: SUCCESS");
break;
}
else {
SLOG_DEBUG(" Frame capture: FAIL");
}
}
cam->Close();
}
else {
SLOG_DEBUG("Camera open failed");
}
wxMilliSleep(1000); // Try not the stress the camera too much
return (img != NULL);
}
CCamera* CViacamController::SetUpCamera()
{
// Load app local data
ReadAppData(wxConfigBase::Get());
/*
Check if cameras detected
*/
int driverId = 0;
for (; driverId < CCameraEnum::NUM_DRIVERS; driverId++) {
if (CCameraEnum::getNumDevices(driverId) > 0) break;
}
if (driverId == CCameraEnum::NUM_DRIVERS) {
/*
No cameras found, aborting.
*/
wxMessageDialog errorMsg (NULL, _("Not detected any camera. Aborting"),
_T("Enable Viacam"), wxOK | wxICON_ERROR);
errorMsg.ShowModal();
return NULL;
}
/*
Try to find previously used camera with the same name
(only check native driver)
*/
int camId = -1;
const char * camName= NULL;
if (m_cameraName.Length()> 0) {
SLOG_INFO("Previous used camera: %s... ", (const char *) m_cameraName.mb_str());
for (camId = 0; camId < CCameraEnum::getNumDevices(0); camId++) {
camName = CCameraEnum::getDeviceName(0, camId);
if (wxString(camName, wxConvLibc) == m_cameraName) {
SLOG_INFO("FOUND");
break;
}
}
if (camId == CCameraEnum::getNumDevices(0)) {
SLOG_INFO("NOT FOUND");
camId = -1;
}
}
/*
Show selection dialog when needed (use native driver names)
*/
SLOG_INFO("Detected %d camera(s)", CCameraEnum::getNumDevices(0));
if (camId == -1) {
if (CCameraEnum::getNumDevices(0) > 1) {
wxArrayString strArray;
for (int i = 0; i < CCameraEnum::getNumDevices(0); i++) {
strArray.Add(wxString(CCameraEnum::getDeviceName(0, i), wxConvLibc));
}
wxSingleChoiceDialog choiceDlg(
NULL, _("Choose the camera to use"), _T("Enable Viacam"), strArray,
(char**)NULL, wxDEFAULT_DIALOG_STYLE | wxOK | wxCANCEL | wxCENTRE);
if (choiceDlg.ShowModal ()!= wxID_OK) return NULL;
camId= choiceDlg.GetSelection();
m_cameraName= choiceDlg.GetStringSelection ();
}
else {
camId= 0;
m_cameraName = wxString(CCameraEnum::getDeviceName(0, camId), wxConvLibc);
}
}
/*
Try to open the camera to make sure it works
*/
SLOG_INFO("Selected camera: %d", camId);
SLOG_INFO("Try to open the camera to make sure it works...");
// native driver first
CCamera* cam = CCameraEnum::getCamera(1, camId);
/*
SLOG_INFO("Testing with WDM driver...");
if (!testCamera(cam)) {
SLOG_INFO("Failed. Now try the OpenCV driver...");
delete cam;
cam = CCameraEnum::getCamera(1, camId);
if (!testCamera(cam)) {
SLOG_INFO("Failed. Can not initialize the camera.");
delete cam;
cam = NULL;
wxMessageDialog errorMsg(
NULL, _("Can not initialize the camera.\nPerhaps is being used by other application."),
_T("Enable Viacam"), wxOK | wxICON_ERROR);
errorMsg.ShowModal();
ChangeCamera();
}
}
*/
if (cam) cam->SetHorizontalFlip(true);
WriteAppData(wxConfigBase::Get());
wxConfigBase::Get()->Flush();
return cam;
}
bool CViacamController::Initialize ()
{
bool retval= true;
assert (!m_pMainWindow && !m_pCamera && !m_pCaptureThread);
SetUpLanguage ();
// Create camera object
m_pCamera= SetUpCamera();
if (m_pCamera== NULL) retval= false;
if (retval) {
// TODO: check this!!!
assert (!m_pMotionCalibration);
m_pMotionCalibration= new CMotionCalibration(); //this);
#if defined(__WXGTK__)
assert (!m_pAutostart);
m_pAutostart = new CAutostart(wxT("eviacam.desktop"));
#endif
}
// Create main window
if (retval) {
m_pMainWindow = new WViacam( NULL, ID_WVIACAM );
assert (m_pMainWindow);
}
// Create hotkey manager
if (retval) {
assert (!m_hotKeyManager);
m_hotKeyManager= new eviacam::HotkeyManager();
}
// Create pointer action object
if (retval) {
assert (!m_pointerAction);
m_pointerAction= new CPointerAction();
}
// Create and start worker thread
if (retval) {
m_pCaptureThread = new CCaptureThread (m_pCamera, m_pMainWindow->GetCamWindow(), this, wxTHREAD_JOINABLE );
assert (m_pCaptureThread);
if (m_pCaptureThread->Create()!= wxTHREAD_NO_ERROR) retval= false;
if (retval)
{
// Fire thread
m_pCaptureThread->SetPriority (WXTHREAD_MAX_PRIORITY);
if (m_pCaptureThread->Run() != wxTHREAD_NO_ERROR) retval= false;
}
}
// Register track area
if (retval)
m_pMainWindow->GetCamWindow()->RegisterControl (m_visionPipeline.GetTrackAreaControl());
// Load configuration
if (retval) m_configManager->ReadAll ();
// Show main window unless it was set to be minimised at startup
if (retval && !m_minimisedAtStartup) m_pMainWindow->Show(true);
// Enable pointeraction object
if (retval && m_enabledAtStartup) SetEnabled(true);
if (retval) m_pointerAction->SetEnabled(true);
#if defined(ENABLE_UPDATES_CHECK)
// Check for updates
if (retval && m_checkUpdatesAtStartup) {
assert(m_pCheckUpdateManager == NULL);
m_pCheckUpdateManager = new CheckUpdatesManager(m_pMainWindow);
m_pCheckUpdateManager->LaunchBackground();
}
#endif
// Run the wizard at startup
if (retval && m_runWizardAtStartup)
StartWizard();
return retval;
}
void CViacamController::Finalize ()
{
SetEnabled (false, true);
if (m_pCaptureThread) {
// Save config
m_configManager->WriteAll();
m_pCaptureThread->Finish();
m_pCaptureThread->Wait();
delete m_pCaptureThread;
m_pCaptureThread= NULL;
}
if (m_pCamera) {
delete m_pCamera;
m_pCamera= NULL;
}
if (m_pointerAction) {
m_pointerAction->SetEnabled(false);
delete m_pointerAction;
m_pointerAction= NULL;
}
if (m_hotKeyManager) {
delete m_hotKeyManager;
m_hotKeyManager= NULL;
}
if (m_pCheckUpdateManager) {
delete m_pCheckUpdateManager;
m_pCheckUpdateManager = NULL;
}
if (m_pMainWindow) {
WViacam* mainWin= m_pMainWindow;
m_pMainWindow= NULL;
mainWin->GetCamWindow()->UnregisterControl (m_visionPipeline.GetTrackAreaControl());
mainWin->Close (true);
// Main window is self-destroyed
}
}
void CViacamController::WriteAppData(wxConfigBase* pConfObj)
{
// General options
m_configManager->WriteLanguage (m_languageId);
pConfObj->Write(_T("cameraName"), m_cameraName);
pConfObj->Write(_T("checkUpdatesAtStartup"), m_checkUpdatesAtStartup);
}
void CViacamController::WriteProfileData(wxConfigBase* pConfObj)
{
pConfObj->Write(_T("enabledAtStartup"), m_enabledAtStartup);
pConfObj->Write(_T("onScreenKeyboardCommand"), m_onScreenKeyboardCommand);
pConfObj->Write(_T("runWizardAtStartup"), m_runWizardAtStartup);
pConfObj->Write(_T("minimisedAtStartup"), m_minimisedAtStartup);
// Propagates calls
m_pointerAction->WriteProfileData (pConfObj);
m_visionPipeline.WriteProfileData (pConfObj);
m_hotKeyManager->WriteProfileData (pConfObj);
}
void CViacamController::ReadAppData(wxConfigBase* pConfObj)
{
// General options
SetLanguage (m_configManager->ReadLanguage()); // Only load, dont't apply
pConfObj->Read(_T("cameraName"), &m_cameraName);
pConfObj->Read(_T("checkUpdatesAtStartup"), &m_checkUpdatesAtStartup);
}
void CViacamController::ReadProfileData(wxConfigBase* pConfObj)
{
pConfObj->Read(_T("enabledAtStartup"), &m_enabledAtStartup);
pConfObj->Read(_T("onScreenKeyboardCommand"), &m_onScreenKeyboardCommand);
pConfObj->Read(_T("runWizardAtStartup"), &m_runWizardAtStartup);
pConfObj->Read(_T("minimisedAtStartup"), &m_minimisedAtStartup);
// Propagates calls
m_pointerAction->ReadProfileData (pConfObj);
m_visionPipeline.ReadProfileData (pConfObj);
m_hotKeyManager->ReadProfileData (pConfObj);
}
void CViacamController::SetEnabled (bool value, bool silent, wxWindow* parent)
{
if (value!= m_enabled) {
if (value== false && !silent) {
wxMessageDialog dlg (parent, _("This action will disable eViacam.\nAre you sure?"), _("eViacam warning"), wxICON_EXCLAMATION | wxYES_NO );
if (dlg.ShowModal()== wxID_NO) return;
}
if (value) {
// Enable, reset counters
m_pointerAction->ResetCounters();
}
m_enabled= value;
}
}
void CViacamController::OpenConfiguration()
{
if (!m_pConfiguration) {
m_pConfiguration = new WConfiguration(m_pMainWindow); //, this);
m_pConfiguration->Connect (
wxEVT_DESTROY,
wxWindowDestroyEventHandler(CViacamController::WConfigurationListener::OnDestroy),
NULL,
&m_wConfigurationListener);
}
m_pConfiguration->Show(true);
}
void CViacamController::OpenOnScreenKeyboard()
{
if (m_onScreenKeyboardCommand.IsEmpty())
{
wxMessageDialog dlg (NULL, _("On-screen keyboard command not set.\nPlease, configure it through options dialog."), _("eViacam error"), wxOK | wxICON_ERROR);
dlg.ShowModal();
}
else
{
if (wxExecute (m_onScreenKeyboardCommand)== 0)
{
wxString errMsg;
errMsg.Printf (_("Can't execute on-screen keyboard program.\nCommand %s failed.\nPlease, configure it through options dialog."), m_onScreenKeyboardCommand.c_str());
#if defined(__WXMSW__)
errMsg.Append (_("\n\nOn Windows Vista/7 several problems have been reported\nwith the system's default on-screen keyboard (osk). These\nissues are under investigation. Meanwhile you might want\nto disable User Account Control (UAC) to avoid such problems\nor try another osk, e.g. Click-N-Type."));
#endif
wxMessageDialog dlg (NULL, errMsg, _("eViacam error"), wxOK | wxICON_ERROR);
dlg.ShowModal();
}
}
}
void CViacamController::ProcessImage (IplImage *pImage)
{
WViacam::EFPSCondition cond;
long iFrameRate;
float vx, vy;
CIplImage image(pImage);
bool allowedByFaceLocalizationSystem;
// Refresh fps meter
m_frameRate= 0.5f * m_pCamera->GetRealFrameRate() + 0.5f * m_frameRate;
iFrameRate= (long) (m_frameRate + 0.5f);
if (iFrameRate< 20)
cond= WViacam::POOR;
else if (iFrameRate< 25)
cond= WViacam::FAIR;
else
cond= WViacam::GOOD;
m_pMainWindow->SetFPS (iFrameRate, cond);
// Proces frame
allowedByFaceLocalizationSystem = m_visionPipeline.ProcessImage (image, vx, vy);
if ((m_enabled && allowedByFaceLocalizationSystem) || m_motionCalibrationEnabled) {
// Send mouse motion
BEGIN_GUI_CALL_MUTEX()
if (m_motionCalibrationEnabled)
m_pMotionCalibration->ComputeMotionRange (-vx, vy, !(m_visionPipeline.GetTrackFace() && !m_visionPipeline.IsFaceDetected()));
else
m_pointerAction->ProcessMotion (-vx, vy);
END_GUI_CALL_MUTEX()
}
m_hotKeyManager->CheckKeyboardStatus();
}
bool CViacamController::StartMotionCalibration (void)
{
return m_pMotionCalibration->InitMotionCalibration();
}
void CViacamController::StartWizard()
{
m_wizardManager.Open (m_pMainWindow);
}
const wxString& CViacamController::GetCameraName () const
{
return m_cameraName;
}
const bool CViacamController::CameraHasSettingsDialog () const
{
return (m_pCamera->HasSettingsDialog() || m_pCamera->HasCameraControls());
}
void CViacamController::ChangeCamera ()
{
m_cameraName.Clear();
}
void CViacamController::ShowCameraSettingsDialog ()
{
if (m_enabled && !m_pCameraDialog) {
wxMessageDialog dlg (
NULL,
_("Note that adjusting the camera controls while eViacam\nis enabled may cause loss of pointer control.\nAre you sure you want to continue?"),
_T("Enable Viacam"),
wxICON_EXCLAMATION | wxYES_NO );
if (dlg.ShowModal()== wxID_NO) return;
}
if (m_pCamera->HasSettingsDialog()) m_pCamera->ShowSettingsDialog ();
if (m_pCamera->HasCameraControls()) {
if (!m_pCameraDialog) {
m_pCameraDialog = new WCameraDialog(m_pMainWindow, m_pCamera);
m_pCameraDialog->Connect (
wxEVT_DESTROY,
wxWindowDestroyEventHandler(CViacamController::WCameraDialogListener::OnDestroy),
NULL,
&m_wCameraDialogListener);
m_pCameraDialog->Show(true);
}
m_pCameraDialog->Raise();
}
}