1 /* 2 * Copyright 2007-2008, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Ryan Leavengood 7 */ 8 9 10 #include <Bitmap.h> 11 #include <Font.h> 12 #include <ObjectList.h> 13 #include <Picture.h> 14 #include <Screen.h> 15 #include <ScreenSaver.h> 16 #include <String.h> 17 #include <StringView.h> 18 #include <View.h> 19 20 #include <stdio.h> 21 #include <stdlib.h> 22 #include <string.h> 23 24 // Double brackets to satisfy a compiler warning 25 const pattern kCheckered = { { 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0x33, 0x33 } }; 26 27 // Get a clever message from fortune 28 BString *get_message() 29 { 30 BString *result = new BString(); 31 FILE *file = popen("/bin/fortune", "r"); 32 if (file) { 33 char buf[512]; 34 int bytesRead; 35 while (!feof(file)) { 36 bytesRead = fread(buf, 1, 512, file); 37 result->Append(buf, bytesRead); 38 } 39 fclose(file); 40 } 41 42 // Just in case 43 if (result->Length() <= 0) { 44 result->Append("Insert clever anecdote or phrase here!"); 45 } 46 47 return result; 48 } 49 50 51 int get_lines(BString *message, BString*** result, int *longestLine) 52 { 53 // First count how many newlines there are 54 int count = 0; 55 int start = 0; 56 while ((start = message->FindFirst('\n', start)) != B_ERROR) { 57 start++; // To move past the new line 58 count++; 59 } 60 61 // Now break the string up and put in result 62 BString **lines = new BString*[count]; 63 start = 0; 64 int end = 0; 65 int maxLength = 0; 66 for (int i = 0; ((end = message->FindFirst('\n', start)) != B_ERROR) && i < count; i++) { 67 lines[i] = new BString(); 68 message->CopyInto(*lines[i], start, end - start); 69 // Convert tabs to 4 spaces 70 lines[i]->ReplaceAll("\t", " "); 71 // Look for longest line 72 if (lines[i]->Length() > maxLength) { 73 maxLength = lines[i]->Length(); 74 *longestLine = i; 75 } 76 start = end + 1; 77 } 78 *result = lines; 79 80 return count; 81 } 82 83 84 // Inspired by the classic BeOS screensaver, of course. 85 // Thanks to Jon Watte for writing the original. 86 class Message : public BScreenSaver 87 { 88 public: 89 Message(BMessage *archive, image_id); 90 ~Message(); 91 void Draw(BView *view, int32 frame); 92 void StartConfig(BView *view); 93 status_t StartSaver(BView *view, bool preview); 94 95 private: 96 BObjectList<font_family> fFontFamilies; 97 float fScaleFactor; 98 bool fPreview; 99 }; 100 101 102 BScreenSaver *instantiate_screen_saver(BMessage *msg, image_id image) 103 { 104 return new Message(msg, image); 105 } 106 107 108 Message::Message(BMessage *archive, image_id id) 109 : BScreenSaver(archive, id) 110 { 111 } 112 113 114 Message::~Message() 115 { 116 for (int32 i = 0; i < fFontFamilies.CountItems(); i++) { 117 if (fFontFamilies.ItemAt(i)) 118 delete fFontFamilies.ItemAt(i); 119 } 120 } 121 122 123 void 124 Message::StartConfig(BView *view) 125 { 126 view->AddChild(new BStringView(BRect(20, 10, 200, 35), "", "Message, by Ryan Leavengood")); 127 view->AddChild(new BStringView(BRect(20, 40, 200, 65), "", "Inspired by Jon Watte's Original")); 128 } 129 130 131 status_t 132 Message::StartSaver(BView *view, bool preview) 133 { 134 fPreview = preview; 135 // Scale factor is based on the system I developed this on, in 136 // other words other factors below depend on this. 137 fScaleFactor = view->Bounds().Height() / 1024; 138 139 // Get font families 140 int numFamilies = count_font_families(); 141 for (int32 i = 0; i < numFamilies; i++) { 142 font_family *family = new font_family[1]; 143 uint32 flags; 144 if (get_font_family(i, family, &flags) == B_OK) { 145 // Do not add fixed fonts 146 if (!(flags & B_IS_FIXED)) 147 fFontFamilies.AddItem(family); 148 } 149 } 150 151 // Seed the random number generator 152 srand((int)system_time()); 153 154 // Set tick size to 30,000,000 microseconds = 30 seconds 155 SetTickSize(30000000); 156 157 return B_OK; 158 } 159 160 161 void 162 Message::Draw(BView *view, int32 frame) 163 { 164 // Double-buffered drawing 165 BScreen screen; 166 BBitmap buffer(view->Bounds(), screen.ColorSpace(), true); 167 BView offscreen(view->Bounds(), NULL, 0, 0); 168 buffer.AddChild(&offscreen); 169 buffer.Lock(); 170 171 // Set up the colors 172 rgb_color base_color = {rand() % 25, rand() % 25, rand() % 25}; 173 offscreen.SetHighColor(base_color); 174 offscreen.SetLowColor(tint_color(base_color, 0.815F)); 175 offscreen.FillRect(offscreen.Bounds(), kCheckered); 176 rgb_color colors[8] = { 177 tint_color(base_color, B_LIGHTEN_1_TINT), 178 tint_color(base_color, 0.795F), 179 tint_color(base_color, 0.851F), 180 tint_color(base_color, 0.926F), 181 tint_color(base_color, 1.05F), 182 tint_color(base_color, B_DARKEN_1_TINT), 183 tint_color(base_color, B_DARKEN_2_TINT), 184 tint_color(base_color, B_DARKEN_3_TINT), 185 }; 186 187 offscreen.SetDrawingMode(B_OP_OVER); 188 189 // Set the basic font parameters, including random font family 190 BFont font; 191 offscreen.GetFont(&font); 192 font.SetFace(B_BOLD_FACE); 193 font.SetFamilyAndStyle(*(fFontFamilies.ItemAt(rand() % fFontFamilies.CountItems())), NULL); 194 offscreen.SetFont(&font); 195 196 // Get the message 197 BString *message = get_message(); 198 BString *origMessage = new BString(); 199 message->CopyInto(*origMessage, 0, message->Length()); 200 // Replace newlines and tabs with spaces 201 message->ReplaceSet("\n\t", ' '); 202 203 int height = (int) offscreen.Bounds().Height(); 204 int width = (int) offscreen.Bounds().Width(); 205 206 // From 14 to 22 iterations 207 int32 iterations = (rand() % 8) + 14; 208 for (int32 i = 0; i < iterations; i++) { 209 // Randomly set font size and shear 210 BFont font; 211 offscreen.GetFont(&font); 212 float fontSize = ((rand() % 320) + 42) * fScaleFactor; 213 font.SetSize(fontSize); 214 // Set the shear off 90 about 1/2 of the time 215 if (rand() % 2 == 1) 216 font.SetShear((float) ((rand() % 135) + (rand() % 45))); 217 else 218 font.SetShear(90.0); 219 offscreen.SetFont(&font); 220 221 // Randomly set drawing location 222 int x = (rand() % width) - (rand() % width/((rand() % 8)+1)); 223 int y = rand() % height; 224 225 // Draw new text 226 offscreen.SetHighColor(colors[rand() % 8]); 227 int strLength = message->Length(); 228 // See how wide this string is with the current font 229 float strWidth = offscreen.StringWidth(message->String()); 230 int drawingLength = (int) (strLength * (width / strWidth)); 231 int start = 0; 232 if (drawingLength >= strLength) 233 drawingLength = strLength; 234 else 235 start = rand() % (strLength - drawingLength); 236 char *toDraw = new char[drawingLength+1]; 237 strncpy(toDraw, message->String()+start, drawingLength); 238 toDraw[drawingLength] = 0; 239 offscreen.DrawString(toDraw, BPoint(x, y)); 240 delete toDraw; 241 } 242 243 // Now draw the full message in a nice translucent box, but only 244 // if this isn't preview mode 245 if (!fPreview) { 246 BFont font(be_fixed_font); 247 font.SetSize(14.0); 248 offscreen.SetFont(&font); 249 font_height fontHeight; 250 font.GetHeight(&fontHeight); 251 float lineHeight = fontHeight.ascent + fontHeight.descent + fontHeight.leading; 252 253 BString **lines = NULL; 254 int longestLine = 0; 255 int count = get_lines(origMessage, &lines, &longestLine); 256 257 float stringWidth = font.StringWidth(lines[longestLine]->String()); 258 BRect box(0, 0, stringWidth + 20, (lineHeight * count) + 20); 259 box.OffsetTo((width - box.Width()) / 2, height - box.Height() - 40); 260 261 offscreen.SetDrawingMode(B_OP_ALPHA); 262 base_color.alpha = 128; 263 offscreen.SetHighColor(base_color); 264 offscreen.FillRoundRect(box, 8, 8); 265 offscreen.SetHighColor(205, 205, 205); 266 BPoint start = box.LeftTop(); 267 start.x += 10; 268 start.y += 10 + fontHeight.ascent + fontHeight.leading; 269 for (int i = 0; i < count; i++) { 270 offscreen.DrawString(lines[i]->String(), start); 271 start.y += lineHeight; 272 delete lines[i]; 273 } 274 delete[] lines; 275 } 276 277 delete origMessage; 278 delete message; 279 280 offscreen.Sync(); 281 buffer.Unlock(); 282 view->DrawBitmap(&buffer); 283 buffer.RemoveChild(&offscreen); 284 } 285 286