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_NONPACKAGED_ADDONS_DIRECTORY, 122 B_USER_ADDONS_DIRECTORY, 123 B_COMMON_NONPACKAGED_ADDONS_DIRECTORY, 124 B_COMMON_ADDONS_DIRECTORY, 125 B_SYSTEM_ADDONS_DIRECTORY, 126 }; 127 BPath path; 128 129 for (uint32 i = 0; i < sizeof(which) / sizeof(which[0]); i++) { 130 if (find_directory(which[i], &path, false) != B_OK) 131 continue; 132 133 path.Append("Screen Savers"); 134 path.Append(fSettings.ModuleName()); 135 136 fAddonImage = load_add_on(path.Path()); 137 if (fAddonImage >= B_OK) 138 break; 139 } 140 141 if (fAddonImage < B_OK) { 142 printf("Unable to open add-on: %s: %s\n", path.Path(), strerror(fAddonImage)); 143 } else { 144 // Look for the one C function that should exist. 145 if (get_image_symbol(fAddonImage, "instantiate_screen_saver", 146 B_SYMBOL_TYPE_TEXT, (void **)&instantiate) != B_OK) { 147 printf("Unable to find the instantiator\n"); 148 } else { 149 BMessage state; 150 fSettings.GetModuleState(fSettings.ModuleName(), &state); 151 fSaver = instantiate(&state, fAddonImage); 152 } 153 154 if (fSaver->InitCheck() != B_OK) { 155 printf("ScreenSaver initialization failed: %s!\n", strerror(fSaver->InitCheck())); 156 _CleanUp(); 157 } 158 } 159 160 Resume(); 161 } 162 163 164 void 165 ScreenSaverRunner::_CleanUp() 166 { 167 delete fSaver; 168 fSaver = NULL; 169 170 if (fAddonImage >= 0) { 171 unload_add_on(fAddonImage); 172 fAddonImage = -1; 173 } 174 } 175 176 177 void 178 ScreenSaverRunner::_Run() 179 { 180 static const uint32 kInitialTickRate = 50000; 181 182 if (fWindow->Lock()) { 183 fView->SetViewColor(0, 0, 0); 184 fView->SetLowColor(0, 0, 0); 185 if (fSaver) 186 fHasStarted = fSaver->StartSaver(fView, fPreview) == B_OK; 187 fWindow->Unlock(); 188 } 189 190 // TODO: This code is getting awfully complicated and should 191 // probably be refactored. 192 uint32 tickBase = kInitialTickRate; 193 int32 snoozeCount = 0; 194 int32 frame = 0; 195 bigtime_t lastTickTime = 0; 196 bigtime_t tick = fSaver ? fSaver->TickSize() : tickBase; 197 198 while (!fQuitting) { 199 // break the idle time up into ticks so that we can evaluate 200 // the quit condition with greater responsiveness 201 // otherwise a screen saver that sets, say, a 30 second tick 202 // will result in the screen saver not responding to deactivation 203 // for that length of time 204 snooze(tickBase); 205 if (system_time() - lastTickTime < tick) { 206 continue; 207 } else { 208 // re-evaluate the tick time after each successful wakeup - 209 // screensavers can adjust it on the fly, and we must be 210 // prepared to accomodate that 211 tick = fSaver ? fSaver->TickSize() : tickBase; 212 213 if (tick < tickBase) { 214 if (tick < 0) 215 tick = 0; 216 tickBase = tick; 217 } else if (tickBase < kInitialTickRate && tick >= kInitialTickRate) { 218 tickBase = kInitialTickRate; 219 } 220 221 lastTickTime = system_time(); 222 } 223 224 if (snoozeCount) { 225 // if we are sleeping, do nothing 226 snoozeCount--; 227 } else if (fSaver != NULL && fHasStarted) { 228 if (fSaver->LoopOnCount() && frame >= fSaver->LoopOnCount()) { 229 // Time to nap 230 frame = 0; 231 snoozeCount = fSaver->LoopOffCount(); 232 } else if (fWindow->LockWithTimeout(5000LL) == B_OK) { 233 if (!fQuitting) { 234 // NOTE: R5 really calls DirectDraw() 235 // and then Draw() for the same frame 236 if (fDirectWindow) 237 fSaver->DirectDraw(frame); 238 fSaver->Draw(fView, frame); 239 fView->Sync(); 240 frame++; 241 } 242 fWindow->Unlock(); 243 } 244 } else 245 snoozeCount = 1000; 246 } 247 248 if (fSaver) 249 fSaver->StopSaver(); 250 } 251 252 253 status_t 254 ScreenSaverRunner::_ThreadFunc(void *data) 255 { 256 ScreenSaverRunner* runner = (ScreenSaverRunner*)data; 257 runner->_Run(); 258 return B_OK; 259 } 260 261