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