1 /* 2 * Copyright 2007-2014, 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 <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> 26 27 28 #undef B_TRANSLATION_CONTEXT 29 #define B_TRANSLATION_CONTEXT "Screensaver Message" 30 31 32 // Double brackets to satisfy a compiler warning 33 const pattern kCheckered = { { 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0x33, 0x33 } }; 34 35 // Get a clever message from fortune 36 BString *get_message() 37 { 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); 46 } 47 pclose(file); 48 } 49 50 // Just in case 51 if (result->Length() <= 0) { 52 result->Append(B_TRANSLATE("Insert clever anecdote or phrase here!")); 53 } 54 55 return result; 56 } 57 58 59 int 60 get_lines(BString *message, BStringList& list, int *longestLine) 61 { 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; 73 } 74 } 75 return count; 76 } 77 return 0; 78 } 79 80 81 // Inspired by the classic BeOS screensaver, of course. 82 // Thanks to Jon Watte for writing the original. 83 class Message : public BScreenSaver 84 { 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); 91 92 private: 93 BObjectList<font_family> fFontFamilies; 94 float fScaleFactor; 95 bool fPreview; 96 }; 97 98 99 BScreenSaver *instantiate_screen_saver(BMessage *msg, image_id image) 100 { 101 return new Message(msg, image); 102 } 103 104 105 Message::Message(BMessage *archive, image_id id) 106 : BScreenSaver(archive, id) 107 { 108 } 109 110 111 Message::~Message() 112 { 113 for (int32 i = 0; i < fFontFamilies.CountItems(); i++) { 114 if (fFontFamilies.ItemAt(i)) 115 delete fFontFamilies.ItemAt(i); 116 } 117 } 118 119 120 void 121 Message::StartConfig(BView *view) 122 { 123 BPrivate::BuildDefaultSettingsView(view, "Message", 124 B_TRANSLATE("by Ryan Leavengood")); 125 } 126 127 128 status_t 129 Message::StartSaver(BView *view, bool preview) 130 { 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; 135 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; 147 } 148 149 // Seed the random number generator 150 srand((int)system_time()); 151 152 // Set tick size to 30,000,000 microseconds = 30 seconds 153 SetTickSize(30000000); 154 155 return B_OK; 156 } 157 158 159 void 160 Message::Draw(BView *view, int32 frame) 161 { 162 if (view == NULL || view->Window() == NULL || !view->Window()->IsLocked()) 163 return; 164 165 BScreen screen(view->Window()); 166 if (!screen.IsValid()) 167 return; 168 169 // Double-buffered drawing 170 BBitmap buffer(view->Bounds(), screen.ColorSpace(), true); 171 if (buffer.InitCheck() != B_OK) 172 return; 173 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 = {(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), 193 }; 194 195 offscreen.SetDrawingMode(B_OP_OVER); 196 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); 203 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", ' '); 210 211 int height = (int) offscreen.Bounds().Height(); 212 int width = (int) offscreen.Bounds().Width(); 213 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); 228 229 // Randomly set drawing location 230 int x = (rand() % width) - (rand() % width/((rand() % 8)+1)); 231 int y = rand() % height; 232 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; 249 } 250 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; 261 262 BStringList lines; 263 int longestLine = 0; 264 int32 count = get_lines(origMessage, lines, &longestLine); 265 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); 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.StringAt(i).String(), start); 280 start.y += lineHeight; 281 } 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