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