vfs: check userland buffers before reading them.
[haiku.git] / src / apps / haikudepot / ui / ScreenshotWindow.cpp
blob48a341e4858f27ebc878ee3185e9b2ca214f02e8
1 /*
2 * Copyright 2014, Stephan Aßmus <superstippi@gmx.de>.
3 * Copyright 2017, Julian Harnath <julian.harnath@rwth-aachen.de>.
4 * All rights reserved. Distributed under the terms of the MIT License.
5 */
7 #include "ScreenshotWindow.h"
9 #include <algorithm>
10 #include <stdio.h>
12 #include <Autolock.h>
13 #include <Catalog.h>
14 #include <LayoutBuilder.h>
15 #include <MessageRunner.h>
16 #include <StringView.h>
18 #include "BarberPole.h"
19 #include "BitmapView.h"
20 #include "WebAppInterface.h"
23 #undef B_TRANSLATION_CONTEXT
24 #define B_TRANSLATION_CONTEXT "ScreenshotWindow"
27 static const rgb_color kBackgroundColor = { 51, 102, 152, 255 };
28 // Drawn as a border around the screenshots and also what's behind their
29 // transparent regions
31 static BitmapRef sNextButtonIcon(
32 new(std::nothrow) SharedBitmap(505), true);
33 static BitmapRef sPreviousButtonIcon(
34 new(std::nothrow) SharedBitmap(506), true);
37 ScreenshotWindow::ScreenshotWindow(BWindow* parent, BRect frame)
39 BWindow(frame, B_TRANSLATE("Screenshot"),
40 B_FLOATING_WINDOW_LOOK, B_FLOATING_SUBSET_WINDOW_FEEL,
41 B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS),
42 fBarberPoleShown(false),
43 fDownloadPending(false),
44 fWorkerThread(-1)
46 AddToSubset(parent);
48 atomic_set(&fCurrentScreenshotIndex, 0);
50 fBarberPole = new BarberPole("barber pole");
51 fBarberPole->SetExplicitMaxSize(BSize(100, B_SIZE_UNLIMITED));
52 fBarberPole->Hide();
54 fIndexView = new BStringView("screenshot index", NULL);
56 fToolBar = new BToolBar();
57 fToolBar->AddAction(MSG_PREVIOUS_SCREENSHOT, this,
58 sNextButtonIcon->Bitmap(SharedBitmap::SIZE_22),
59 NULL, NULL);
60 fToolBar->AddAction(MSG_NEXT_SCREENSHOT, this,
61 sPreviousButtonIcon->Bitmap(SharedBitmap::SIZE_22),
62 NULL, NULL);
63 fToolBar->AddView(fIndexView);
64 fToolBar->AddGlue();
65 fToolBar->AddView(fBarberPole);
67 fScreenshotView = new BitmapView("screenshot view");
68 fScreenshotView->SetExplicitMaxSize(
69 BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED));
70 fScreenshotView->SetScaleBitmap(false);
72 BGroupView* groupView = new BGroupView(B_VERTICAL);
73 groupView->SetViewColor(kBackgroundColor);
75 // Build layout
76 BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
77 .SetInsets(0, 3, 0, 0)
78 .Add(fToolBar)
79 .AddStrut(3)
80 .AddGroup(groupView)
81 .Add(fScreenshotView)
82 .SetInsets(B_USE_WINDOW_INSETS)
83 .End()
86 fScreenshotView->SetLowColor(kBackgroundColor);
87 // Set after attaching all views to prevent it from being overriden
88 // again by BitmapView::AllAttached()
90 CenterOnScreen();
94 ScreenshotWindow::~ScreenshotWindow()
96 BAutolock locker(&fLock);
98 if (fWorkerThread >= 0)
99 wait_for_thread(fWorkerThread, NULL);
103 bool
104 ScreenshotWindow::QuitRequested()
106 if (fOnCloseTarget.IsValid() && fOnCloseMessage.what != 0)
107 fOnCloseTarget.SendMessage(&fOnCloseMessage);
109 Hide();
110 return false;
114 void
115 ScreenshotWindow::MessageReceived(BMessage* message)
117 switch (message->what) {
118 case MSG_NEXT_SCREENSHOT:
120 atomic_add(&fCurrentScreenshotIndex, 1);
121 _UpdateToolBar();
122 _DownloadScreenshot();
123 break;
126 case MSG_PREVIOUS_SCREENSHOT:
127 atomic_add(&fCurrentScreenshotIndex, -1);
128 _UpdateToolBar();
129 _DownloadScreenshot();
130 break;
132 case MSG_DOWNLOAD_START:
133 if (!fBarberPoleShown) {
134 fBarberPole->Start();
135 fBarberPole->Show();
136 fBarberPoleShown = true;
138 break;
140 case MSG_DOWNLOAD_STOP:
141 if (fBarberPoleShown) {
142 fBarberPole->Hide();
143 fBarberPole->Stop();
144 fBarberPoleShown = true;
146 break;
148 default:
149 BWindow::MessageReceived(message);
150 break;
155 void
156 ScreenshotWindow::SetOnCloseMessage(
157 const BMessenger& messenger, const BMessage& message)
159 fOnCloseTarget = messenger;
160 fOnCloseMessage = message;
164 void
165 ScreenshotWindow::SetPackage(const PackageInfoRef& package)
167 if (fPackage == package)
168 return;
170 fPackage = package;
172 BString title = B_TRANSLATE("Screenshot");
173 if (package.Get() != NULL) {
174 title = package->Title();
175 _DownloadScreenshot();
177 SetTitle(title);
179 atomic_set(&fCurrentScreenshotIndex, 0);
181 _UpdateToolBar();
185 /* static */ void
186 ScreenshotWindow::CleanupIcons()
188 sNextButtonIcon.Unset();
189 sPreviousButtonIcon.Unset();
193 // #pragma mark - private
196 void
197 ScreenshotWindow::_DownloadScreenshot()
199 BAutolock locker(&fLock);
201 if (fWorkerThread >= 0) {
202 fDownloadPending = true;
203 return;
206 thread_id thread = spawn_thread(&_DownloadThreadEntry,
207 "Screenshot Loader", B_NORMAL_PRIORITY, this);
208 if (thread >= 0)
209 _SetWorkerThread(thread);
213 void
214 ScreenshotWindow::_SetWorkerThread(thread_id thread)
216 if (!Lock())
217 return;
219 // bool enabled = thread < 0;
221 // fPreviewsButton->SetEnabled(enabled);
222 // fNextButton->SetEnabled(enabled);
223 // fCloseButton->SetEnabled(enabled);
225 if (thread >= 0) {
226 fWorkerThread = thread;
227 resume_thread(fWorkerThread);
228 } else {
229 fWorkerThread = -1;
231 if (fDownloadPending) {
232 _DownloadScreenshot();
233 fDownloadPending = false;
237 Unlock();
241 int32
242 ScreenshotWindow::_DownloadThreadEntry(void* data)
244 ScreenshotWindow* window
245 = reinterpret_cast<ScreenshotWindow*>(data);
246 window->_DownloadThread();
247 window->_SetWorkerThread(-1);
248 return 0;
252 void
253 ScreenshotWindow::_DownloadThread()
255 printf("_DownloadThread()\n");
256 if (!Lock()) {
257 printf(" failed to lock screenshot window\n");
258 return;
261 fScreenshotView->UnsetBitmap();
263 ScreenshotInfoList screenshotInfos;
264 if (fPackage.Get() != NULL)
265 screenshotInfos = fPackage->ScreenshotInfos();
267 Unlock();
269 if (screenshotInfos.CountItems() == 0) {
270 printf(" package has no screenshots\n");
271 return;
274 // Obtain the correct code for the screenshot to display
275 // TODO: Once navigation buttons are added, we could use the
276 // ScreenshotInfo at the "current" index.
277 const ScreenshotInfo& info = screenshotInfos.ItemAtFast(
278 atomic_get(&fCurrentScreenshotIndex));
280 BMallocIO buffer;
281 WebAppInterface interface;
283 // Only indicate being busy with the download if it takes a little while
284 BMessenger messenger(this);
285 BMessageRunner delayedMessenger(messenger,
286 new BMessage(MSG_DOWNLOAD_START),
287 kProgressIndicatorDelay, 1);
289 // Retrieve screenshot from web-app
290 status_t status = interface.RetrieveScreenshot(info.Code(),
291 info.Width(), info.Height(), &buffer);
293 delayedMessenger.SetCount(0);
294 messenger.SendMessage(MSG_DOWNLOAD_STOP);
296 if (status == B_OK && Lock()) {
297 printf("got screenshot");
298 fScreenshot = BitmapRef(new(std::nothrow)SharedBitmap(buffer), true);
299 fScreenshotView->SetBitmap(fScreenshot);
300 _ResizeToFitAndCenter();
301 Unlock();
302 } else {
303 printf(" failed to download screenshot\n");
308 void
309 ScreenshotWindow::_ResizeToFitAndCenter()
311 // Find out dimensions of the largest screenshot of this package
312 ScreenshotInfoList screenshotInfos;
313 if (fPackage.Get() != NULL)
314 screenshotInfos = fPackage->ScreenshotInfos();
316 int32 largestScreenshotWidth = 0;
317 int32 largestScreenshotHeight = 0;
319 const uint32 numScreenshots = fPackage->ScreenshotInfos().CountItems();
320 for (uint32 i = 0; i < numScreenshots; i++) {
321 const ScreenshotInfo& info = screenshotInfos.ItemAtFast(i);
322 if (info.Width() > largestScreenshotWidth)
323 largestScreenshotWidth = info.Width();
324 if (info.Height() > largestScreenshotHeight)
325 largestScreenshotHeight = info.Height();
328 fScreenshotView->SetExplicitMinSize(
329 BSize(largestScreenshotWidth, largestScreenshotHeight));
330 Layout(false);
332 // TODO: Limit window size to screen size (with a little margin),
333 // the image should then become scrollable.
335 float minWidth;
336 float minHeight;
337 GetSizeLimits(&minWidth, NULL, &minHeight, NULL);
338 ResizeTo(minWidth, minHeight);
339 CenterOnScreen();
343 void
344 ScreenshotWindow::_UpdateToolBar()
346 const int32 numScreenshots = fPackage->ScreenshotInfos().CountItems();
347 const int32 currentIndex = atomic_get(&fCurrentScreenshotIndex);
349 fToolBar->SetActionEnabled(MSG_PREVIOUS_SCREENSHOT,
350 currentIndex > 0);
351 fToolBar->SetActionEnabled(MSG_NEXT_SCREENSHOT,
352 currentIndex < numScreenshots - 1);
354 BString text;
355 text << currentIndex + 1;
356 text << " / ";
357 text << numScreenshots;
358 fIndexView->SetText(text);