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