vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / screen_savers / message / Message.cpp
blobcd6eb9f84a244e354358f0bfb2e84fd5c88bf2b5
1 /*
2 * Copyright 2007-2014, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Ryan Leavengood
7 */
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
14 #include <Bitmap.h>
15 #include <Catalog.h>
16 #include <DefaultSettingsView.h>
17 #include <Font.h>
18 #include <ObjectList.h>
19 #include <Picture.h>
20 #include <Screen.h>
21 #include <ScreenSaver.h>
22 #include <String.h>
23 #include <StringList.h>
24 #include <TextView.h>
25 #include <View.h>
28 #undef B_TRANSLATION_CONTEXT
29 #define B_TRANSLATION_CONTEXT "Screensaver Message"
32 // Double brackets to satisfy a compiler warning
33 const pattern kCheckered = { { 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0x33, 0x33 } };
35 // Get a clever message from fortune
36 BString *get_message()
38 BString *result = new BString();
39 FILE *file = popen("/bin/fortune", "r");
40 if (file) {
41 char buf[512];
42 int bytesRead;
43 while (!feof(file)) {
44 bytesRead = fread(buf, 1, 512, file);
45 result->Append(buf, bytesRead);
47 pclose(file);
50 // Just in case
51 if (result->Length() <= 0) {
52 result->Append(B_TRANSLATE("Insert clever anecdote or phrase here!"));
55 return result;
59 int
60 get_lines(BString *message, BStringList& list, int *longestLine)
62 BString copy(*message);
63 // Convert tabs to 4 spaces
64 copy.ReplaceAll("\t", " ");
65 if (copy.Split("\n", false, list)) {
66 int maxLength = 0;
67 int32 count = list.CountStrings();
68 for (int32 i = 0; i < count; i++) {
69 int32 length = list.StringAt(i).Length();
70 if (length > maxLength) {
71 maxLength = length;
72 *longestLine = i;
75 return count;
77 return 0;
81 // Inspired by the classic BeOS screensaver, of course.
82 // Thanks to Jon Watte for writing the original.
83 class Message : public BScreenSaver
85 public:
86 Message(BMessage *archive, image_id);
87 ~Message();
88 void Draw(BView *view, int32 frame);
89 void StartConfig(BView *view);
90 status_t StartSaver(BView *view, bool preview);
92 private:
93 BObjectList<font_family> fFontFamilies;
94 float fScaleFactor;
95 bool fPreview;
99 BScreenSaver *instantiate_screen_saver(BMessage *msg, image_id image)
101 return new Message(msg, image);
105 Message::Message(BMessage *archive, image_id id)
106 : BScreenSaver(archive, id)
111 Message::~Message()
113 for (int32 i = 0; i < fFontFamilies.CountItems(); i++) {
114 if (fFontFamilies.ItemAt(i))
115 delete fFontFamilies.ItemAt(i);
120 void
121 Message::StartConfig(BView *view)
123 BPrivate::BuildDefaultSettingsView(view, "Message",
124 B_TRANSLATE("by Ryan Leavengood"));
128 status_t
129 Message::StartSaver(BView *view, bool preview)
131 fPreview = preview;
132 // Scale factor is based on the system I developed this on, in
133 // other words other factors below depend on this.
134 fScaleFactor = view->Bounds().Height() / 1024;
136 // Get font families
137 int numFamilies = count_font_families();
138 for (int32 i = 0; i < numFamilies; i++) {
139 font_family* family = new font_family[1];
140 uint32 flags;
141 if (get_font_family(i, family, &flags) == B_OK
142 && (flags & B_IS_FIXED) == 0) {
143 // Do not add fixed fonts
144 fFontFamilies.AddItem(family);
145 } else
146 delete[] family;
149 // Seed the random number generator
150 srand((int)system_time());
152 // Set tick size to 30,000,000 microseconds = 30 seconds
153 SetTickSize(30000000);
155 return B_OK;
159 void
160 Message::Draw(BView *view, int32 frame)
162 if (view == NULL || view->Window() == NULL || !view->Window()->IsLocked())
163 return;
165 BScreen screen(view->Window());
166 if (!screen.IsValid())
167 return;
169 // Double-buffered drawing
170 BBitmap buffer(view->Bounds(), screen.ColorSpace(), true);
171 if (buffer.InitCheck() != B_OK)
172 return;
174 BView offscreen(view->Bounds(), NULL, 0, 0);
175 buffer.AddChild(&offscreen);
176 buffer.Lock();
178 // Set up the colors
179 rgb_color base_color = {(uint8)(rand() % 25), (uint8)(rand() % 25),
180 (uint8)(rand() % 25)};
181 offscreen.SetHighColor(base_color);
182 offscreen.SetLowColor(tint_color(base_color, 0.815F));
183 offscreen.FillRect(offscreen.Bounds(), kCheckered);
184 rgb_color colors[8] = {
185 tint_color(base_color, B_LIGHTEN_1_TINT),
186 tint_color(base_color, 0.795F),
187 tint_color(base_color, 0.851F),
188 tint_color(base_color, 0.926F),
189 tint_color(base_color, 1.05F),
190 tint_color(base_color, B_DARKEN_1_TINT),
191 tint_color(base_color, B_DARKEN_2_TINT),
192 tint_color(base_color, B_DARKEN_3_TINT),
195 offscreen.SetDrawingMode(B_OP_OVER);
197 // Set the basic font parameters, including random font family
198 BFont font;
199 offscreen.GetFont(&font);
200 font.SetFace(B_BOLD_FACE);
201 font.SetFamilyAndStyle(*(fFontFamilies.ItemAt(rand() % fFontFamilies.CountItems())), NULL);
202 offscreen.SetFont(&font);
204 // Get the message
205 BString *message = get_message();
206 BString *origMessage = new BString();
207 message->CopyInto(*origMessage, 0, message->Length());
208 // Replace newlines and tabs with spaces
209 message->ReplaceSet("\n\t", ' ');
211 int height = (int) offscreen.Bounds().Height();
212 int width = (int) offscreen.Bounds().Width();
214 // From 14 to 22 iterations
215 int32 iterations = (rand() % 8) + 14;
216 for (int32 i = 0; i < iterations; i++) {
217 // Randomly set font size and shear
218 BFont font;
219 offscreen.GetFont(&font);
220 float fontSize = ((rand() % 320) + 42) * fScaleFactor;
221 font.SetSize(fontSize);
222 // Set the shear off 90 about 1/2 of the time
223 if (rand() % 2 == 1)
224 font.SetShear((float) ((rand() % 135) + (rand() % 45)));
225 else
226 font.SetShear(90.0);
227 offscreen.SetFont(&font);
229 // Randomly set drawing location
230 int x = (rand() % width) - (rand() % width/((rand() % 8)+1));
231 int y = rand() % height;
233 // Draw new text
234 offscreen.SetHighColor(colors[rand() % 8]);
235 int strLength = message->Length();
236 // See how wide this string is with the current font
237 float strWidth = offscreen.StringWidth(message->String());
238 int drawingLength = (int) (strLength * (width / strWidth));
239 int start = 0;
240 if (drawingLength >= strLength)
241 drawingLength = strLength;
242 else
243 start = rand() % (strLength - drawingLength);
244 char *toDraw = new char[drawingLength+1];
245 strncpy(toDraw, message->String()+start, drawingLength);
246 toDraw[drawingLength] = 0;
247 offscreen.DrawString(toDraw, BPoint(x, y));
248 delete[] toDraw;
251 // Now draw the full message in a nice translucent box, but only
252 // if this isn't preview mode
253 if (!fPreview) {
254 BFont font(be_fixed_font);
255 font.SetSize(14.0);
256 offscreen.SetFont(&font);
257 font_height fontHeight;
258 font.GetHeight(&fontHeight);
259 float lineHeight = fontHeight.ascent + fontHeight.descent
260 + fontHeight.leading;
262 BStringList lines;
263 int longestLine = 0;
264 int32 count = get_lines(origMessage, lines, &longestLine);
266 float stringWidth = font.StringWidth(lines.StringAt(longestLine).String());
267 BRect box(0, 0, stringWidth + 20, (lineHeight * count) + 20);
268 box.OffsetTo((width - box.Width()) / 2, height - box.Height() - 40);
270 offscreen.SetDrawingMode(B_OP_ALPHA);
271 base_color.alpha = 128;
272 offscreen.SetHighColor(base_color);
273 offscreen.FillRoundRect(box, 8, 8);
274 offscreen.SetHighColor(205, 205, 205);
275 BPoint start = box.LeftTop();
276 start.x += 10;
277 start.y += 10 + fontHeight.ascent + fontHeight.leading;
278 for (int i = 0; i < count; i++) {
279 offscreen.DrawString(lines.StringAt(i).String(), start);
280 start.y += lineHeight;
284 delete origMessage;
285 delete message;
287 offscreen.Sync();
288 buffer.Unlock();
289 view->DrawBitmap(&buffer);
290 buffer.RemoveChild(&offscreen);