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