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