1 /* 2 * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 #include "splashscreen_impl.h" 27 28 #import <Cocoa/Cocoa.h> 29 #import <objc/objc-auto.h> 30 31 #import <JavaNativeFoundation/JavaNativeFoundation.h> 32 #import "NSApplicationAWT.h" 33 34 #include <sys/time.h> 35 #include <pthread.h> 36 #include <iconv.h> 37 #include <langinfo.h> 38 #include <locale.h> 39 #include <fcntl.h> 40 #include <poll.h> 41 #include <errno.h> 42 #include <sys/types.h> 43 #include <signal.h> 44 #include <unistd.h> 45 #include <dlfcn.h> 46 47 48 static NSScreen* SplashNSScreen() 49 { 50 return [[NSScreen screens] objectAtIndex: 0]; 51 } 52 53 static void SplashCenter(Splash * splash) 54 { 55 NSRect screenFrame = [SplashNSScreen() frame]; 56 57 splash->x = (screenFrame.size.width - splash->width) / 2; 58 splash->y = (screenFrame.size.height - splash->height) / 2 + screenFrame.origin.y; 59 } 60 61 unsigned 62 SplashTime(void) { 63 struct timeval tv; 64 struct timezone tz; 65 unsigned long long msec; 66 67 gettimeofday(&tv, &tz); 68 msec = (unsigned long long) tv.tv_sec * 1000 + 69 (unsigned long long) tv.tv_usec / 1000; 70 71 return (unsigned) msec; 72 } 73 74 /* Could use npt but decided to cut down on linked code size */ 75 char* SplashConvertStringAlloc(const char* in, int* size) { 76 const char *codeset; 77 const char *codeset_out; 78 iconv_t cd; 79 size_t rc; 80 char *buf = NULL, *out; 81 size_t bufSize, inSize, outSize; 82 const char* old_locale; 83 84 if (!in) { 85 return NULL; 86 } 87 old_locale = setlocale(LC_ALL, ""); 88 89 codeset = nl_langinfo(CODESET); 90 if ( codeset == NULL || codeset[0] == 0 ) { 91 goto done; 92 } 93 /* we don't need BOM in output so we choose native BE or LE encoding here */ 94 codeset_out = (platformByteOrder()==BYTE_ORDER_MSBFIRST) ? 95 "UCS-2BE" : "UCS-2LE"; 96 97 cd = iconv_open(codeset_out, codeset); 98 if (cd == (iconv_t)-1 ) { 99 goto done; 100 } 101 inSize = strlen(in); 102 bufSize = inSize*2; // need 2 bytes per char for UCS-2, this is 103 // 2 bytes per source byte max 104 buf = malloc(bufSize); 105 out = buf; outSize = bufSize; 106 /* linux iconv wants char** source and solaris wants const char**... 107 cast to void* */ 108 rc = iconv(cd, (void*)&in, &inSize, &out, &outSize); 109 iconv_close(cd); 110 111 if (rc == (size_t)-1) { 112 free(buf); 113 buf = NULL; 114 } else { 115 if (size) { 116 *size = (bufSize-outSize)/2; /* bytes to wchars */ 117 } 118 } 119 done: 120 setlocale(LC_ALL, old_locale); 121 return buf; 122 } 123 124 125 void 126 SplashInitPlatform(Splash * splash) { 127 pthread_mutex_init(&splash->lock, NULL); 128 129 splash->maskRequired = 0; 130 131 132 //TODO: the following is too much of a hack but should work in 90% cases. 133 // besides we don't use device-dependant drawing, so probably 134 // that's very fine indeed 135 splash->byteAlignment = 1; 136 initFormat(&splash->screenFormat, 0xff << 8, 137 0xff << 16, 0xff << 24, 0xff << 0); 138 splash->screenFormat.byteOrder = 1 ? BYTE_ORDER_LSBFIRST : BYTE_ORDER_MSBFIRST; 139 splash->screenFormat.depthBytes = 4; 140 141 dispatch_async(dispatch_get_main_queue(), ^(void) { 142 NSApplication * app = [NSApplicationAWT sharedApplication]; 143 [NSApplicationAWT runAWTLoopWithApp: app]; 144 }); 145 } 146 147 void 148 SplashCleanupPlatform(Splash * splash) { 149 splash->maskRequired = 0; 150 } 151 152 void 153 SplashDonePlatform(Splash * splash) { 154 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 155 156 pthread_mutex_destroy(&splash->lock); 157 [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ 158 if (splash->window) { 159 [splash->window orderOut:nil]; 160 [splash->window release]; 161 } 162 }]; 163 [pool drain]; 164 } 165 166 void 167 SplashLock(Splash * splash) { 168 pthread_mutex_lock(&splash->lock); 169 } 170 171 void 172 SplashUnlock(Splash * splash) { 173 pthread_mutex_unlock(&splash->lock); 174 } 175 176 void 177 SplashInitFrameShape(Splash * splash, int imageIndex) { 178 // No shapes, we rely on alpha compositing 179 } 180 181 void * SplashScreenThread(void *param); 182 void 183 SplashCreateThread(Splash * splash) { 184 pthread_t thr; 185 pthread_attr_t attr; 186 int rc; 187 188 pthread_attr_init(&attr); 189 rc = pthread_create(&thr, &attr, SplashScreenThread, (void *) splash); 190 } 191 192 void 193 SplashRedrawWindow(Splash * splash) { 194 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 195 196 SplashUpdateScreenData(splash); 197 198 [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ 199 // NSDeviceRGBColorSpace vs. NSCalibratedRGBColorSpace ? 200 NSBitmapImageRep * rep = [[NSBitmapImageRep alloc] 201 initWithBitmapDataPlanes: (unsigned char**)&splash->screenData 202 pixelsWide: splash->width 203 pixelsHigh: splash->height 204 bitsPerSample: 8 205 samplesPerPixel: 4 206 hasAlpha: YES 207 isPlanar: NO 208 colorSpaceName: NSDeviceRGBColorSpace 209 bitmapFormat: NSAlphaFirstBitmapFormat | NSAlphaNonpremultipliedBitmapFormat 210 bytesPerRow: splash->width * 4 211 bitsPerPixel: 32]; 212 213 NSImage * image = [[NSImage alloc] 214 initWithSize: NSMakeSize(splash->width, splash->height)]; 215 [image setBackgroundColor: [NSColor clearColor]]; 216 217 [image addRepresentation: rep]; 218 219 NSImageView * view = [[NSImageView alloc] init]; 220 221 [view setImage: image]; 222 [view setEditable: NO]; 223 //NOTE: we don't set a 'wait cursor' for the view because: 224 // 1. The Cocoa GUI guidelines suggest to avoid it, and use a progress 225 // bar instead. 226 // 2. There simply isn't an instance of NSCursor that represent 227 // the 'wait cursor'. So that is undoable. 228 229 //TODO: only the first image in an animated gif preserves transparency. 230 // Loos like the splash->screenData contains inappropriate data 231 // for all but the first frame. 232 233 [image release]; 234 [rep release]; 235 236 [splash->window setContentView: view]; 237 [splash->window orderFrontRegardless]; 238 }]; 239 240 [pool drain]; 241 } 242 243 void SplashReconfigureNow(Splash * splash) { 244 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 245 246 [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ 247 SplashCenter(splash); 248 249 if (!splash->window) { 250 return; 251 } 252 253 [splash->window orderOut:nil]; 254 [splash->window setFrame: NSMakeRect(splash->x, splash->y, splash->width, splash->height) 255 display: NO]; 256 }]; 257 258 [pool drain]; 259 260 SplashRedrawWindow(splash); 261 } 262 263 void 264 SplashEventLoop(Splash * splash) { 265 266 /* we should have splash _locked_ on entry!!! */ 267 268 while (1) { 269 struct pollfd pfd[1]; 270 int timeout = -1; 271 int ctl = splash->controlpipe[0]; 272 int rc; 273 int pipes_empty; 274 275 pfd[0].fd = ctl; 276 pfd[0].events = POLLIN | POLLPRI; 277 278 errno = 0; 279 if (splash->isVisible>0 && SplashIsStillLooping(splash)) { 280 timeout = splash->time + splash->frames[splash->currentFrame].delay 281 - SplashTime(); 282 if (timeout < 0) { 283 timeout = 0; 284 } 285 } 286 SplashUnlock(splash); 287 rc = poll(pfd, 1, timeout); 288 SplashLock(splash); 289 if (splash->isVisible>0 && SplashTime() >= splash->time + 290 splash->frames[splash->currentFrame].delay) { 291 SplashNextFrame(splash); 292 SplashRedrawWindow(splash); 293 } 294 if (rc <= 0) { 295 errno = 0; 296 continue; 297 } 298 pipes_empty = 0; 299 while(!pipes_empty) { 300 char buf; 301 302 pipes_empty = 1; 303 if (read(ctl, &buf, sizeof(buf)) > 0) { 304 pipes_empty = 0; 305 switch (buf) { 306 case SPLASHCTL_UPDATE: 307 if (splash->isVisible>0) { 308 SplashRedrawWindow(splash); 309 } 310 break; 311 case SPLASHCTL_RECONFIGURE: 312 if (splash->isVisible>0) { 313 SplashReconfigureNow(splash); 314 } 315 break; 316 case SPLASHCTL_QUIT: 317 return; 318 } 319 } 320 } 321 } 322 } 323 324 void * 325 SplashScreenThread(void *param) { 326 objc_registerThreadWithCollector(); 327 328 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 329 Splash *splash = (Splash *) param; 330 331 SplashLock(splash); 332 pipe(splash->controlpipe); 333 fcntl(splash->controlpipe[0], F_SETFL, 334 fcntl(splash->controlpipe[0], F_GETFL, 0) | O_NONBLOCK); 335 splash->time = SplashTime(); 336 splash->currentFrame = 0; 337 [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ 338 SplashCenter(splash); 339 340 splash->window = (void*) [[NSWindow alloc] 341 initWithContentRect: NSMakeRect(splash->x, splash->y, splash->width, splash->height) 342 styleMask: NSBorderlessWindowMask 343 backing: NSBackingStoreBuffered 344 defer: NO 345 screen: SplashNSScreen()]; 346 347 [splash->window setOpaque: NO]; 348 [splash->window setBackgroundColor: [NSColor clearColor]]; 349 }]; 350 fflush(stdout); 351 if (splash->window) { 352 [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ 353 [splash->window orderFrontRegardless]; 354 }]; 355 SplashRedrawWindow(splash); 356 SplashEventLoop(splash); 357 } 358 SplashUnlock(splash); 359 SplashDone(splash); 360 361 splash->isVisible=-1; 362 363 [pool drain]; 364 365 return 0; 366 } 367 368 void 369 sendctl(Splash * splash, char code) { 370 if (splash && splash->controlpipe[1]) { 371 write(splash->controlpipe[1], &code, 1); 372 } 373 } 374 375 void 376 SplashClosePlatform(Splash * splash) { 377 sendctl(splash, SPLASHCTL_QUIT); 378 } 379 380 void 381 SplashUpdate(Splash * splash) { 382 sendctl(splash, SPLASHCTL_UPDATE); 383 } 384 385 void 386 SplashReconfigure(Splash * splash) { 387 sendctl(splash, SPLASHCTL_RECONFIGURE); 388 } 389