1 /*
2  *  Copyright (C) 2005-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #include "Screenshot.h"
10 
11 #include "ServiceBroker.h"
12 #include "URL.h"
13 #include "Util.h"
14 #include "filesystem/File.h"
15 #include "guilib/LocalizeStrings.h"
16 #include "pictures/Picture.h"
17 #include "settings/SettingPath.h"
18 #include "settings/Settings.h"
19 #include "settings/SettingsComponent.h"
20 #include "settings/windows/GUIControlSettings.h"
21 #include "utils/JobManager.h"
22 #include "utils/URIUtils.h"
23 #include "utils/log.h"
24 
25 using namespace XFILE;
26 
27 std::vector<std::function<std::unique_ptr<IScreenshotSurface>()>> CScreenShot::m_screenShotSurfaces;
28 
Register(const std::function<std::unique_ptr<IScreenshotSurface> ()> & createFunc)29 void CScreenShot::Register(const std::function<std::unique_ptr<IScreenshotSurface>()>& createFunc)
30 {
31   m_screenShotSurfaces.emplace_back(createFunc);
32 }
33 
TakeScreenshot(const std::string & filename,bool sync)34 void CScreenShot::TakeScreenshot(const std::string& filename, bool sync)
35 {
36   auto surface = m_screenShotSurfaces.back()();
37 
38   if (!surface)
39   {
40     CLog::Log(LOGERROR, "failed to create screenshot surface");
41     return;
42   }
43 
44   if (!surface->Capture())
45   {
46     CLog::Log(LOGERROR, "Screenshot %s failed", CURL::GetRedacted(filename).c_str());
47     return;
48   }
49 
50   surface->CaptureVideo(true);
51 
52   CLog::Log(LOGDEBUG, "Saving screenshot %s", CURL::GetRedacted(filename).c_str());
53 
54   //set alpha byte to 0xFF
55   for (int y = 0; y < surface->GetHeight(); y++)
56   {
57     unsigned char* alphaptr = surface->GetBuffer() - 1 + y * surface->GetStride();
58     for (int x = 0; x < surface->GetWidth(); x++)
59       *(alphaptr += 4) = 0xFF;
60   }
61 
62   //if sync is true, the png file needs to be completely written when this function returns
63   if (sync)
64   {
65     if (!CPicture::CreateThumbnailFromSurface(surface->GetBuffer(), surface->GetWidth(), surface->GetHeight(), surface->GetStride(), filename))
66       CLog::Log(LOGERROR, "Unable to write screenshot %s", CURL::GetRedacted(filename).c_str());
67 
68     surface->ReleaseBuffer();
69   }
70   else
71   {
72     //make sure the file exists to avoid concurrency issues
73     XFILE::CFile file;
74     if (file.OpenForWrite(filename))
75       file.Close();
76     else
77       CLog::Log(LOGERROR, "Unable to create file %s", CURL::GetRedacted(filename).c_str());
78 
79     //write .png file asynchronous with CThumbnailWriter, prevents stalling of the render thread
80     //buffer is deleted from CThumbnailWriter
81     CThumbnailWriter* thumbnailwriter = new CThumbnailWriter(surface->GetBuffer(), surface->GetWidth(), surface->GetHeight(), surface->GetStride(), filename);
82     CJobManager::GetInstance().AddJob(thumbnailwriter, NULL);
83   }
84 }
85 
TakeScreenshot()86 void CScreenShot::TakeScreenshot()
87 {
88   std::shared_ptr<CSettingPath> screenshotSetting = std::static_pointer_cast<CSettingPath>(CServiceBroker::GetSettingsComponent()->GetSettings()->GetSetting(CSettings::SETTING_DEBUG_SCREENSHOTPATH));
89   if (!screenshotSetting)
90     return;
91 
92   std::string strDir = screenshotSetting->GetValue();
93   if (strDir.empty())
94   {
95     if (!CGUIControlButtonSetting::GetPath(screenshotSetting, &g_localizeStrings))
96       return;
97 
98     strDir = screenshotSetting->GetValue();
99   }
100 
101   URIUtils::RemoveSlashAtEnd(strDir);
102 
103   if (!strDir.empty())
104   {
105     std::string file = CUtil::GetNextFilename(URIUtils::AddFileToFolder(strDir, "screenshot%05d.png"), 65535);
106 
107     if (!file.empty())
108     {
109       TakeScreenshot(file, false);
110     }
111     else
112     {
113       CLog::Log(LOGWARNING, "Too many screen shots or invalid folder");
114     }
115   }
116 }
117