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