1/* 2 * Copyright 2003-2009, Haiku. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Michael Phipps 7 * Jérôme Duval, jerome.duval@free.fr 8 * Axel Dörfler, axeld@pinc-software.de 9 * Ryan Leavengood, leavengood@gmail.com 10 */ 11 12 13#include "ScreenSaverFilter.h" 14 15#include <Application.h> 16#include <Autolock.h> 17#include <FindDirectory.h> 18#include <MessageRunner.h> 19#include <NodeMonitor.h> 20#include <OS.h> 21#include <Roster.h> 22#include <Screen.h> 23 24#include <new> 25#include <strings.h> 26#include <syslog.h> 27 28 29static const int32 kNeverBlankCornerSize = 10; 30static const int32 kBlankCornerSize = 5; 31static const bigtime_t kCornerDelay = 1000000LL; 32 // one second 33 34static const int32 kMsgCheckTime = 'SSCT'; 35static const int32 kMsgCornerInvoke = 'Scin'; 36 37 38extern "C" _EXPORT BInputServerFilter* instantiate_input_filter(); 39 40 41/** Required C func to build the IS Filter */ 42BInputServerFilter* 43instantiate_input_filter() 44{ 45 return new (std::nothrow) ScreenSaverFilter(); 46} 47 48 49// #pragma mark - 50 51 52ScreenSaverController::ScreenSaverController(ScreenSaverFilter *filter) 53 : BLooper("screensaver controller", B_LOW_PRIORITY), 54 fFilter(filter) 55{ 56} 57 58 59void 60ScreenSaverController::MessageReceived(BMessage *message) 61{ 62 switch (message->what) { 63 case B_NODE_MONITOR: 64 fFilter->ReloadSettings(); 65 break; 66 case B_SOME_APP_LAUNCHED: 67 case B_SOME_APP_QUIT: 68 { 69 const char *signature; 70 if (message->FindString("be:signature", &signature) == B_OK 71 && strcasecmp(signature, SCREEN_BLANKER_SIG) == 0) 72 fFilter->SetIsRunning(message->what == B_SOME_APP_LAUNCHED); 73 break; 74 } 75 76 case kMsgCheckTime: 77 fFilter->CheckTime(); 78 break; 79 80 case kMsgCornerInvoke: 81 fFilter->CheckCornerInvoke(); 82 break; 83 84 default: 85 BLooper::MessageReceived(message); 86 } 87} 88 89 90// #pragma mark - 91 92 93ScreenSaverFilter::ScreenSaverFilter() 94 : BLocker("screen saver filter"), 95 fLastEventTime(system_time()), 96 fBlankTime(0), 97 fSnoozeTime(0), 98 fCurrentCorner(NO_CORNER), 99 fFrameNum(0), 100 fRunner(NULL), 101 fCornerRunner(NULL), 102 fWatchingDirectory(false), 103 fWatchingFile(false), 104 fIsRunning(false) 105{ 106 fController = new (std::nothrow) ScreenSaverController(this); 107 if (fController == NULL) 108 return; 109 110 BAutolock _(this); 111 112 fController->Run(); 113 114 ReloadSettings(); 115 be_roster->StartWatching(fController); 116} 117 118 119ScreenSaverFilter::~ScreenSaverFilter() 120{ 121 be_roster->StopWatching(fController); 122 123 if (fWatchingFile || fWatchingDirectory) 124 watch_node(&fNodeRef, B_STOP_WATCHING, fController); 125 126 // We must quit our controller without being locked, or else we might 127 // deadlock; when the controller is gone, there is no reason to lock 128 // anymore, anyway. 129 if (fController->Lock()) 130 fController->Quit(); 131 132 delete fCornerRunner; 133 delete fRunner; 134} 135 136 137/*! Starts watching the settings file, or if that doesn't exist, the directory 138 the settings file will be placed into. 139*/ 140void 141ScreenSaverFilter::_WatchSettings() 142{ 143 BEntry entry(fSettings.Path().Path()); 144 if (entry.Exists()) { 145 if (fWatchingFile) 146 return; 147 148 if (fWatchingDirectory) { 149 watch_node(&fNodeRef, B_STOP_WATCHING, fController); 150 fWatchingDirectory = false; 151 } 152 if (entry.GetNodeRef(&fNodeRef) == B_OK 153 && watch_node(&fNodeRef, B_WATCH_ALL, fController) == B_OK) 154 fWatchingFile = true; 155 } else { 156 if (fWatchingDirectory) 157 return; 158 159 if (fWatchingFile) { 160 watch_node(&fNodeRef, B_STOP_WATCHING, fController); 161 fWatchingFile = false; 162 } 163 BEntry dir; 164 if (entry.GetParent(&dir) == B_OK 165 && dir.GetNodeRef(&fNodeRef) == B_OK 166 && watch_node(&fNodeRef, B_WATCH_DIRECTORY, fController) == B_OK) 167 fWatchingDirectory = true; 168 } 169} 170 171 172/*! Starts the screen saver if allowed */ 173void 174ScreenSaverFilter::_Invoke() 175{ 176 if ((fCurrentCorner == fNeverBlankCorner && fNeverBlankCorner != NO_CORNER) 177 || (fSettings.TimeFlags() & ENABLE_SAVER) == 0 178 || fIsRunning 179 || be_roster->IsRunning(SCREEN_BLANKER_SIG)) 180 return; 181 182 if (be_roster->Launch(SCREEN_BLANKER_SIG) == B_OK) { 183 // Already set the running state to avoid launching 184 // the blanker twice in any case. 185 fIsRunning = true; 186 return; 187 } 188 189 // Try really hard to launch it. It's very likely that this fails, 190 // when we run from the CD and there is only an incomplete mime 191 // database for example... 192 BPath path; 193 if (find_directory(B_SYSTEM_BIN_DIRECTORY, &path) != B_OK 194 || path.Append("screen_blanker") != B_OK) 195 path.SetTo("/bin/screen_blanker"); 196 BEntry entry(path.Path()); 197 entry_ref ref; 198 if (entry.GetRef(&ref) == B_OK 199 && be_roster->Launch(&ref) == B_OK) 200 fIsRunning = true; 201} 202 203 204void 205ScreenSaverFilter::ReloadSettings() 206{ 207 BAutolock _(this); 208 bool isFirst = !fWatchingDirectory && !fWatchingFile; 209 210 _WatchSettings(); 211 212 if (fWatchingDirectory && !isFirst) { 213 // there is no settings file yet 214 return; 215 } 216 217 delete fCornerRunner; 218 delete fRunner; 219 fRunner = fCornerRunner = NULL; 220 221 fSettings.Load(); 222 223 fBlankCorner = fSettings.BlankCorner(); 224 fNeverBlankCorner = fSettings.NeverBlankCorner(); 225 fBlankTime = fSnoozeTime = fSettings.BlankTime(); 226 CheckTime(); 227 228 if (fBlankCorner != NO_CORNER || fNeverBlankCorner != NO_CORNER) { 229 BMessage invoke(kMsgCornerInvoke); 230 fCornerRunner = new (std::nothrow) BMessageRunner(fController, 231 &invoke, B_INFINITE_TIMEOUT); 232 // will be reset in Filter() 233 } 234 235 BMessage check(kMsgCheckTime); 236 fRunner = new (std::nothrow) BMessageRunner(fController, &check, 237 fSnoozeTime); 238 if (fRunner == NULL || fRunner->InitCheck() != B_OK) 239 syslog(LOG_ERR, "screen saver filter runner init failed\n"); 240} 241 242 243void 244ScreenSaverFilter::SetIsRunning(bool isRunning) 245{ 246 // called from the controller BLooper 247 BAutolock _(this); 248 fIsRunning = isRunning; 249} 250 251 252void 253ScreenSaverFilter::CheckTime() 254{ 255 BAutolock _(this); 256 257 bigtime_t now = system_time(); 258 if (now >= fLastEventTime + fBlankTime) 259 _Invoke(); 260 261 // TODO: this doesn't work correctly - since the BMessageRunner is not 262 // restarted, the next check will be too far away 263 264 // If the screen saver is on OR it was time to come on but it didn't (corner), 265 // snooze for blankTime. 266 // Otherwise, there was an event in the middle of the last snooze, so snooze 267 // for the remainder. 268 if (fIsRunning || fLastEventTime + fBlankTime <= now) 269 fSnoozeTime = fBlankTime; 270 else 271 fSnoozeTime = fLastEventTime + fBlankTime - now; 272 273 if (fRunner != NULL) 274 fRunner->SetInterval(fSnoozeTime); 275} 276 277 278void 279ScreenSaverFilter::CheckCornerInvoke() 280{ 281 BAutolock _(this); 282 283 bigtime_t inactivity = system_time() - fLastEventTime; 284 285 if (fCurrentCorner == fBlankCorner && fBlankCorner != NO_CORNER 286 && inactivity >= kCornerDelay) 287 _Invoke(); 288} 289 290 291void 292ScreenSaverFilter::_UpdateRectangles() 293{ 294 fBlankRect = _ScreenCorner(fBlankCorner, kBlankCornerSize); 295 fNeverBlankRect = _ScreenCorner(fNeverBlankCorner, kNeverBlankCornerSize); 296} 297 298 299BRect 300ScreenSaverFilter::_ScreenCorner(screen_corner corner, uint32 cornerSize) 301{ 302 BRect frame = BScreen().Frame(); 303 304 switch (corner) { 305 case UP_LEFT_CORNER: 306 return BRect(frame.left, frame.top, frame.left + cornerSize - 1, 307 frame.top + cornerSize - 1); 308 309 case UP_RIGHT_CORNER: 310 return BRect(frame.right - cornerSize + 1, frame.top, frame.right, 311 frame.top + cornerSize - 1); 312 313 case DOWN_RIGHT_CORNER: 314 return BRect(frame.right - cornerSize + 1, frame.bottom - cornerSize + 1, 315 frame.right, frame.bottom); 316 317 case DOWN_LEFT_CORNER: 318 return BRect(frame.left, frame.bottom - cornerSize + 1, 319 frame.left + cornerSize - 1, frame.bottom); 320 321 default: 322 // return an invalid rectangle 323 return BRect(-1, -1, -2, -2); 324 } 325} 326 327 328filter_result 329ScreenSaverFilter::Filter(BMessage *message, BList *outList) 330{ 331 BAutolock _(this); 332 333 fLastEventTime = system_time(); 334 335 switch (message->what) { 336 case B_MOUSE_MOVED: 337 { 338 BPoint where; 339 if (message->FindPoint("where", &where) != B_OK) 340 break; 341 342 if ((fFrameNum++ % 32) == 0) { 343 // Every so many frames, update 344 // Used so that we don't update the screen coord's so often 345 // Ideally, we would get a message when the screen changes. 346 // R5 doesn't do this. 347 _UpdateRectangles(); 348 } 349 350 if (fBlankRect.Contains(where)) { 351 fCurrentCorner = fBlankCorner; 352 353 // start screen blanker after one second of inactivity 354 if (fCornerRunner != NULL 355 && fCornerRunner->SetInterval(kCornerDelay) != B_OK) 356 _Invoke(); 357 break; 358 } 359 360 if (fNeverBlankRect.Contains(where)) 361 fCurrentCorner = fNeverBlankCorner; 362 else 363 fCurrentCorner = NO_CORNER; 364 break; 365 } 366 } 367 368 return B_DISPATCH_MESSAGE; 369} 370 371