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