/* Neutrino-GUI - DBoxII-Project Copyright (C) 2001 Steffen Hehn 'McClean' Homepage: http://dbox.cyberphoria.org/ Copyright (C) 2012-2013 defans@bluepeercrew.us License: GPL 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 2 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 . */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include // sort #include "audiomute.h" #include "screensaver.h" #include #include "radiotext_window.h" #include #include #include #include #include #include #include #include extern cVideo * videoDecoder; //NI InfoIcons #include static CComponentsFrmClock *scr_clock = NULL; using namespace std; CScreenSaver::CScreenSaver() { thrScreenSaver = NULL; m_frameBuffer = CFrameBuffer::getInstance(); m_viewer = new CPictureViewer(); index = 0; status_mute = CAudioMute::getInstance()->getStatus(); status_icons = CInfoIcons::getInstance()->getStatus(); //NI clr.i_color = COL_DARK_GRAY; pip_channel_id = 0; idletime = time(NULL); force_refresh = false; thr_exit = false; sl_scr_stop = sigc::mem_fun(*this, &CScreenSaver::thrExit); CNeutrinoApp::getInstance()->OnBeforeRestart.connect(sl_scr_stop); CNeutrinoApp::getInstance()->OnShutDown.connect(sl_scr_stop); } CScreenSaver::~CScreenSaver() { thrExit(); delete m_viewer; if (scr_clock){ delete scr_clock; scr_clock = NULL; } } CScreenSaver* CScreenSaver::getInstance() { static CScreenSaver * screenSaver = NULL; if (!screenSaver) screenSaver = new CScreenSaver(); return screenSaver; } void CScreenSaver::thrExit() { if(thrScreenSaver) { dprintf(DEBUG_NORMAL,"[%s] %s: exit screensaver thread\n", __file__, __func__); thr_exit = true; thrScreenSaver->join(); delete thrScreenSaver; thrScreenSaver = NULL; dprintf(DEBUG_NORMAL,"\033[32m[CScreenSaver] [%s - %d] screensaver thread stopped\033[0m\n", __func__, __LINE__); } } void CScreenSaver::Start() { if (!OnBeforeStart.empty()) OnBeforeStart(); status_mute = CAudioMute::getInstance()->getStatus(); CAudioMute::getInstance()->enableMuteIcon(false); if(!CInfoClock::getInstance()->isBlocked()) CInfoClock::getInstance()->block(); //NI status_icons = CInfoIcons::getInstance()->getStatus(); CInfoIcons::getInstance()->enableInfoIcons(false); if (g_RadiotextWin) g_Radiotext->OnAfterDecodeLine.block(); #ifdef ENABLE_PIP pip_channel_id = CZapit::getInstance()->GetPipChannelID(); if (pip_channel_id) g_Zapit->stopPip(); #endif m_viewer->SetScaling((CPictureViewer::ScalingMode)g_settings.picviewer_scaling); m_viewer->SetVisible(g_settings.screen_StartX, g_settings.screen_EndX, g_settings.screen_StartY, g_settings.screen_EndY); if (g_settings.video_Format == 3) m_viewer->SetAspectRatio(float(16.0/9)); else m_viewer->SetAspectRatio(float(4.0/3)); m_viewer->Cleanup(); #if HAVE_ARM_HARDWARE /* Hack to get sure we have a blank screen. stopFrame()-function seems not work correctly on ARM_HARDWARE */ m_frameBuffer->showFrame("blackscreen.jpg"); #endif m_frameBuffer->stopFrame(); if(!thrScreenSaver) { dprintf(DEBUG_NORMAL,"[%s] %s: starting thread\n", __file__, __func__); thr_exit = false; thrScreenSaver = new std::thread (ScreenSaverPrg, this); std::string tn = "screen_saver"; set_threadname(tn.c_str()); dprintf(DEBUG_NORMAL,"\033[32m[CScreenSaver] [%s - %d] thread [%p] [%s] started\033[0m\n", __func__, __LINE__, thrScreenSaver, tn.c_str()); } if (!OnAfterStart.empty()) OnAfterStart(); } void CScreenSaver::Stop() { //exit thread thrExit(); resetIdleTime(); if (scr_clock){ std::lock_guard g(scr_mutex); scr_clock->Stop(); delete scr_clock; scr_clock = NULL; } #ifdef ENABLE_PIP if(pip_channel_id) { CNeutrinoApp::getInstance()->StartPip(pip_channel_id); pip_channel_id = 0; } #endif m_frameBuffer->paintBackground(); //clear entire screen CAudioMute::getInstance()->enableMuteIcon(status_mute); CInfoClock::getInstance()->ClearDisplay(); //provokes reinit CInfoClock::getInstance()->enableInfoClock(); CInfoIcons::getInstance()->enableInfoIcons(status_icons); //NI if (g_RadiotextWin) g_Radiotext->OnAfterDecodeLine.unblock(); if (!OnAfterStop.empty()) OnAfterStop(); } void CScreenSaver::ScreenSaverPrg(CScreenSaver *scr) { scr->m_frameBuffer->Clear(); if (g_settings.screensaver_timeout) { while(!scr->thr_exit) { if (g_settings.screensaver_mode == SCR_MODE_IMAGE) scr->ReadDir(); scr->paint(); int corr = 1; #if HAVE_CST_HARDWARE //time offset corr = 10; #endif int t = 1000/corr * g_settings.screensaver_timeout; //sleep and exit handle while (t > 0) { if (!scr->thr_exit) this_thread::sleep_for(std::chrono::milliseconds(1)); t--; if (scr->force_refresh) //NOTE: Do we really need this ? { scr->force_refresh = false; break; } } } } else scr->paint(); } bool CScreenSaver::ReadDir() { bool show_audiocover = false; if (CNeutrinoApp::getInstance()->getMode() == NeutrinoModes::mode_audio && g_settings.audioplayer_cover_as_screensaver) { if (access(COVERDIR_TMP, F_OK) == 0) { struct dirent **coverlist; int n = scandir(COVERDIR_TMP, &coverlist, 0, alphasort); if (n > 2){ // we always have the "." and ".." entrys show_audiocover = true; } if(n > -1) { for (int i = 0; i < n; i++) { free(coverlist[i]); } free(coverlist); } } } string d; if (show_audiocover) d = COVERDIR_TMP; else d = g_settings.screensaver_dir; if (d.length() > 1) { //remove trailing slash string::iterator it = d.end() - 1; if (*it == '/') d.erase(it); } char *dir_name = (char *) d.c_str(); struct dirent *dirpointer; DIR *dir; char curr_ext[6]; bool ret = false; v_bg_files.clear(); /* open dir */ if((dir=opendir(dir_name)) == NULL) { fprintf(stderr,"[CScreenSaver] %s - %d : error open dir...\n", __func__, __LINE__); return ret; } /* read complete dir */ while((dirpointer=readdir(dir)) != NULL) // TODO: use threadsave readdir_r instead readdir { int curr_lenght = strlen((*dirpointer).d_name); string str = dir_name; //printf("%d\n",curr_lenght); if(curr_lenght > 4) { strncpy(curr_ext,(*dirpointer).d_name+(curr_lenght-4),sizeof(curr_ext)-1); //printf("%s\n",curr_ext); if (strcasecmp(".jpg",curr_ext) && strcasecmp(".png",curr_ext)) continue; str += "/"; str += (*dirpointer).d_name; if ((string) dir_name == ICONSDIR) { /* backward compatiblity: just add the standard mp3-?.jpg pictures to get the same behavior as usual */ if (str.find("/mp3-") == string::npos) continue; } v_bg_files.push_back(str); } } sort(v_bg_files.begin(), v_bg_files.end()); /* close pointer */ if(closedir(dir) == -1) dprintf(DEBUG_NORMAL, "[CScreenSaver] %s - %d : Error no closed %s\n", __func__, __LINE__, dir_name); if(!v_bg_files.empty()) ret = true; #if 0 else dprintf(DEBUG_NORMAL, "[CScreenSaver] %s - %d : no picture found\n", __func__, __LINE__); #endif return ret; } void CScreenSaver::paint() { if (scr_clock) { if (g_settings.screensaver_mode != SCR_MODE_CLOCK_COLOR && g_settings.screensaver_mode != SCR_MODE_CLOCK) { std::lock_guard g(scr_mutex); delete scr_clock; scr_clock = NULL; } else { scr_clock->kill(0); scr_clock->clearSavedScreen(); scr_clock->doPaintBg(false); } } if (g_settings.screensaver_mode == SCR_MODE_IMAGE && v_bg_files.empty()) m_frameBuffer->paintBackground(); if (g_settings.screensaver_mode == SCR_MODE_IMAGE && !v_bg_files.empty()) { if( (index >= v_bg_files.size()) || (access(v_bg_files.at(index).c_str(), F_OK))) { ReadDir(); index = 0; return; } dprintf(DEBUG_INFO, "[CScreenSaver] %s - %d : %s\n", __func__, __LINE__, v_bg_files.at(index).c_str()); //m_frameBuffer->showFrame(v_bg_files.at(index), CFrameBuffer::SHOW_FRAME_FALLBACK_MODE_IMAGE_UNSCALED); m_viewer->ShowImage(v_bg_files.at(index).c_str(), false /*unscaled*/); handleRadioText(g_settings.screensaver_mode_text); if (!g_settings.screensaver_random) index++; else index = rand_r(&seed[0]) % v_bg_files.size(); if(index == v_bg_files.size()) index = 0; } else { if (!scr_clock) { scr_clock = new CComponentsFrmClock(1, 1, NULL, "%H:%M:%S", "%H:%M.%S", false, 1, NULL, CC_SHADOW_OFF, COL_BLACK, COL_BLACK); scr_clock->setItemName("scr_clock"); scr_clock->setCornerType(CORNER_NONE); scr_clock->setClockFont(g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_NUMBER]); scr_clock->enableSaveBg(); scr_clock->doPaintBg(false); #if HAVE_CST_HARDWARE paintImage("blackscreen.jpg", 0, 0, m_frameBuffer->getScreenWidth(true), m_frameBuffer->getScreenHeight(true)); #endif } #if !HAVE_CST_HARDWARE #if 0 //example for callback m_frameBuffer->OnFallbackShowFrame.connect(sigc::bind(sigc::mem_fun(CFrameBuffer::getInstance(), &CFrameBuffer::paintBoxRel), scr_clock->getXPos(), scr_clock->getYPos(), scr_clock->getWidth(), scr_clock->getHeight(), COL_BLACK, 0, CORNER_ALL) ); #endif m_frameBuffer->showFrame("blackscreen.jpg", CFrameBuffer::SHOW_FRAME_FALLBACK_MODE_CALLBACK | CFrameBuffer::SHOW_FRAME_FALLBACK_MODE_BLACKSCREEN); #endif handleRadioText(g_settings.screensaver_mode_text); if (scr_clock) { scr_clock->setTextColor(clr.i_color); //check position and size use only possible available screen size int x_cl, y_cl, w_cl, h_cl; scr_clock->getDimensions(&x_cl, &y_cl, &w_cl, &h_cl); int x_random = rand_r(&seed[1]) % ((g_settings.screen_EndX - w_cl - g_settings.screen_StartX) + 1) + g_settings.screen_StartX; int y_random = rand_r(&seed[2]) % ((g_settings.screen_EndY - h_cl - g_settings.screen_StartY) + 1) + g_settings.screen_StartY; if (g_RadiotextWin) { // avoid overlapping of clock and radio text window int y_min = g_RadiotextWin->getYPos() - scr_clock->getHeight(); int y_max = g_RadiotextWin->getYPos() + g_RadiotextWin->getHeight(); while (y_random > (y_min - scr_clock->getHeight()) && y_random < y_max) y_random = rand_r(&seed[2]) % ((g_settings.screen_EndY - h_cl - g_settings.screen_StartY) + 1) + g_settings.screen_StartY; } scr_clock->setPos(x_random, y_random); if (!scr_clock->isRun()) scr_clock->Start(); else scr_clock->paint(true); if (g_RadiotextWin && g_settings.screensaver_mode_text) scr_clock->allowPaint(g_RadiotextWin->isPainted()); else scr_clock->allowPaint(true); } if (g_settings.screensaver_mode == SCR_MODE_CLOCK_COLOR) { srand (time(NULL)); uint32_t brightness; // sorcery, no darkness do { clr.i_color = rand_r(&seed[3]); brightness = (unsigned int)clr.uc_color.r * 19595 + (unsigned int)clr.uc_color.g * 38469 + (unsigned int)clr.uc_color.b * 7471; //printf("[%s] %s: brightness: %d\n", __file__, __FUNCTION__, brightness>> 16); } while(brightness >> 16 < 80); clr.i_color &= 0x00FFFFFF; //printf("[%s] %s: clr.i_color: r %02x g %02x b %02x a %02x\n", __file__, __FUNCTION__, clr.uc_color.r, clr.uc_color.g, clr.uc_color.b, clr.uc_color.a); } else clr.i_color = COL_DARK_GRAY; } } void CScreenSaver::handleRadioText(bool enable_paint) { return; // disable display of radiotext if (!g_RadiotextWin) return; g_RadiotextWin->clearSavedScreen(); if (g_settings.screensaver_mode != SCR_MODE_IMAGE) g_RadiotextWin->kill(/*COL_BLACK*/); //ensure black paintBackground before repaint //check position and size, use only possible available screen size int x_rt, y_rt, w_rt, h_rt; g_RadiotextWin->getDimensions(&x_rt, &y_rt, &w_rt, &h_rt); //int rt_x_random = rand_r(&seed[4]) % ((g_settings.screen_EndX - w_rt - g_settings.screen_StartX - OFFSET_SHADOW) + 1) + g_settings.screen_StartX; int rt_x_random = g_InfoViewer->BoxStartX; int rt_y_random = rand_r(&seed[5]) % ((g_settings.screen_EndY - h_rt - g_settings.screen_StartY - OFFSET_SHADOW) + 1) + g_settings.screen_StartY; g_RadiotextWin->setPos(rt_x_random, rt_y_random); g_RadiotextWin->allowPaint(true); if (g_RadiotextWin->hasLines()) { if (scr_clock) scr_clock->cl_sl_show.block(); if (enable_paint) g_RadiotextWin->CRadioTextGUI::paint(CC_SAVE_SCREEN_YES); if (scr_clock) scr_clock->cl_sl_show.unblock(); } m_frameBuffer->OnFallbackShowFrame.connect(sigc::mem_fun(g_RadiotextWin, &CRadioTextGUI::hide)); } void CScreenSaver::hideRadioText() { if (!g_RadiotextWin) return; g_RadiotextWin->sl_after_decode_line.block(); if (scr_clock) scr_clock->cl_sl_show.block(); g_RadiotextWin->kill(); g_RadiotextWin->hide(); if (scr_clock) scr_clock->cl_sl_show.unblock(); } bool CScreenSaver::canStart() { if (g_settings.screensaver_delay && (time(NULL) - idletime > g_settings.screensaver_delay*60)) return true; return false; } bool CScreenSaver::isActive() { if(thrScreenSaver) return true; return false; } bool CScreenSaver::ignoredMsg(neutrino_msg_t msg) { /* screensaver will ignore these msgs */ if ( msg == NeutrinoMessages::EVT_CURRENTEPG || msg == NeutrinoMessages::EVT_NEXTEPG || msg == NeutrinoMessages::EVT_CURRENTNEXT_EPG || msg == NeutrinoMessages::EVT_NOEPG_YET || msg == NeutrinoMessages::EVT_TIMESET || msg == NeutrinoMessages::EVT_PROGRAMLOCKSTATUS || msg == NeutrinoMessages::EVT_ZAP_GOT_SUBSERVICES || msg == NeutrinoMessages::EVT_ZAP_GOTAPIDS || msg == NeutrinoMessages::EVT_ZAP_GOTPIDS || msg == NeutrinoMessages::EVT_EIT_COMPLETE || msg == NeutrinoMessages::EVT_BACK_ZAP_COMPLETE || msg == CSectionsdClient::EVT_GOT_CN_EPG ) return true; return false; } CComponentsFrmClock* CScreenSaver::getClockObject() { return scr_clock; }