1 /* 2 * Copyright 2003-2006, 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 */ 10 11 12 #include "ScreenSaverRunner.h" 13 #include "ScreenSaverSettings.h" 14 15 #include <FindDirectory.h> 16 #include <Screen.h> 17 #include <ScreenSaver.h> 18 #include <View.h> 19 20 #include <stdio.h> 21 22 ScreenSaverRunner::ScreenSaverRunner(BWindow* window, BView* view, 23 bool preview, ScreenSaverSettings& settings) 24 : 25 fSaver(NULL), 26 fWindow(window), 27 fView(view), 28 fSettings(settings), 29 fPreview(preview), 30 fAddonImage(-1), 31 fThread(-1), 32 fQuitting(false) 33 { 34 fDirectWindow = dynamic_cast<BDirectWindow *>(fWindow); 35 _LoadAddOn(); 36 } 37 38 39 ScreenSaverRunner::~ScreenSaverRunner() 40 { 41 if (!fQuitting) 42 Quit(); 43 44 _CleanUp(); 45 } 46 47 48 BScreenSaver* 49 ScreenSaverRunner::ScreenSaver() const 50 { 51 return fSaver; 52 } 53 54 55 bool 56 ScreenSaverRunner::HasStarted() const 57 { 58 return fHasStarted; 59 } 60 61 62 status_t 63 ScreenSaverRunner::Run() 64 { 65 fThread = spawn_thread(&_ThreadFunc, "ScreenSaverRenderer", B_LOW_PRIORITY, this); 66 Resume(); 67 68 return fThread >= B_OK ? B_OK : fThread; 69 } 70 71 72 void 73 ScreenSaverRunner::Quit() 74 { 75 fQuitting = true; 76 Resume(); 77 78 if (fThread >= 0) { 79 status_t returnValue; 80 wait_for_thread(fThread, &returnValue); 81 } 82 } 83 84 85 void 86 ScreenSaverRunner::Suspend() 87 { 88 suspend_thread(fThread); 89 } 90 91 92 void 93 ScreenSaverRunner::Resume() 94 { 95 resume_thread(fThread); 96 } 97 98 99 void 100 ScreenSaverRunner::_LoadAddOn() 101 { 102 // This is a new set of preferences. Free up what we did have 103 // TODO: this is currently not meant to be used after creation 104 if (fThread >= B_OK) { 105 Suspend(); 106 if (fSaver != NULL) 107 fSaver->StopSaver(); 108 } 109 _CleanUp(); 110 111 if (!strcmp("", fSettings.ModuleName())) { 112 Resume(); 113 return; 114 } 115 116 BScreenSaver* (*instantiate)(BMessage*, image_id); 117 118 // try all directories until the first one succeeds 119 120 directory_which which[] = { 121 B_USER_ADDONS_DIRECTORY, 122 B_COMMON_ADDONS_DIRECTORY, 123 B_SYSTEM_ADDONS_DIRECTORY, 124 }; 125 BPath path; 126 127 for (uint32 i = 0; i < sizeof(which) / sizeof(which[0]); i++) { 128 if (find_directory(which[i], &path, false) != B_OK) 129 continue; 130 131 path.Append("Screen Savers"); 132 path.Append(fSettings.ModuleName()); 133 134 fAddonImage = load_add_on(path.Path()); 135 if (fAddonImage >= B_OK) 136 break; 137 } 138 139 if (fAddonImage < B_OK) { 140 printf("Unable to open add-on: %s: %s\n", path.Path(), strerror(fAddonImage)); 141 } else { 142 // Look for the one C function that should exist. 143 if (get_image_symbol(fAddonImage, "instantiate_screen_saver", 144 B_SYMBOL_TYPE_TEXT, (void **)&instantiate) != B_OK) { 145 printf("Unable to find the instantiator\n"); 146 } else { 147 BMessage state; 148 fSettings.GetModuleState(fSettings.ModuleName(), &state); 149 fSaver = instantiate(&state, fAddonImage); 150 } 151 152 if (fSaver->InitCheck() != B_OK) { 153 printf("ScreenSaver initialization failed: %s!\n", strerror(fSaver->InitCheck())); 154 _CleanUp(); 155 } 156 } 157 158 Resume(); 159 } 160 161 162 void 163 ScreenSaverRunner::_CleanUp() 164 { 165 delete fSaver; 166 fSaver = NULL; 167 168 if (fAddonImage >= 0) { 169 unload_add_on(fAddonImage); 170 fAddonImage = -1; 171 } 172 } 173 174 175 void 176 ScreenSaverRunner::_Run() 177 { 178 static const uint32 kInitialTickRate = 50000; 179 180 if (fWindow->Lock()) { 181 fView->SetViewColor(0, 0, 0); 182 fView->SetLowColor(0, 0, 0); 183 if (fSaver) 184 fHasStarted = fSaver->StartSaver(fView, fPreview) == B_OK; 185 fWindow->Unlock(); 186 } 187 188 // TODO: This code is getting awfully complicated and should 189 // probably be refactored. 190 uint32 tickBase = kInitialTickRate; 191 int32 snoozeCount = 0; 192 int32 frame = 0; 193 bigtime_t lastTickTime = 0; 194 bigtime_t tick = fSaver ? fSaver->TickSize() : tickBase; 195 196 while (!fQuitting) { 197 // break the idle time up into ticks so that we can evaluate 198 // the quit condition with greater responsiveness 199 // otherwise a screen saver that sets, say, a 30 second tick 200 // will result in the screen saver not responding to deactivation 201 // for that length of time 202 snooze(tickBase); 203 if (system_time() - lastTickTime < tick) { 204 continue; 205 } else { 206 // re-evaluate the tick time after each successful wakeup - 207 // screensavers can adjust it on the fly, and we must be 208 // prepared to accomodate that 209 tick = fSaver ? fSaver->TickSize() : tickBase; 210 211 if (tick < tickBase) { 212 if (tick < 0) 213 tick = 0; 214 tickBase = tick; 215 } else if (tickBase < kInitialTickRate && tick >= kInitialTickRate) { 216 tickBase = kInitialTickRate; 217 } 218 219 lastTickTime = system_time(); 220 } 221 222 if (snoozeCount) { 223 // if we are sleeping, do nothing 224 snoozeCount--; 225 } else if (fSaver != NULL && fHasStarted) { 226 if (fSaver->LoopOnCount() && frame >= fSaver->LoopOnCount()) { 227 // Time to nap 228 frame = 0; 229 snoozeCount = fSaver->LoopOffCount(); 230 } else if (fWindow->LockWithTimeout(5000LL) == B_OK) { 231 if (!fQuitting) { 232 // NOTE: R5 really calls DirectDraw() 233 // and then Draw() for the same frame 234 if (fDirectWindow) 235 fSaver->DirectDraw(frame); 236 fSaver->Draw(fView, frame); 237 fView->Sync(); 238 frame++; 239 } 240 fWindow->Unlock(); 241 } 242 } else 243 snoozeCount = 1000; 244 } 245 246 if (fSaver) 247 fSaver->StopSaver(); 248 } 249 250 251 status_t 252 ScreenSaverRunner::_ThreadFunc(void *data) 253 { 254 ScreenSaverRunner* runner = (ScreenSaverRunner*)data; 255 runner->_Run(); 256 return B_OK; 257 } 258 259