1 /* 2 * Copyright (c) 2011, 2013, 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 #include <sizecalc.h> 48 49 static NSScreen* SplashNSScreen() 50 { 51 return [[NSScreen screens] objectAtIndex: 0]; 52 } 53 54 static void SplashCenter(Splash * splash) 55 { 56 NSRect screenFrame = [SplashNSScreen() frame]; 57 58 splash->x = (screenFrame.size.width - splash->width) / 2; 59 splash->y = (screenFrame.size.height - splash->height) / 2 + screenFrame.origin.y; 60 } 61 62 unsigned 63 SplashTime(void) { 64 struct timeval tv; 65 struct timezone tz; 66 unsigned long long msec; 67 68 gettimeofday(&tv, &tz); 69 msec = (unsigned long long) tv.tv_sec * 1000 + 70 (unsigned long long) tv.tv_usec / 1000; 71 72 return (unsigned) msec; 73 } 74 75 /* Could use npt but decided to cut down on linked code size */ 76 char* SplashConvertStringAlloc(const char* in, int* size) { 77 const char *codeset; 78 const char *codeset_out; 79 iconv_t cd; 80 size_t rc; 81 char *buf = NULL, *out; 82 size_t bufSize, inSize, outSize; 83 const char* old_locale; 84 85 if (!in) { 86 return NULL; 87 } 88 old_locale = setlocale(LC_ALL, ""); 89 90 codeset = nl_langinfo(CODESET); 91 if ( codeset == NULL || codeset[0] == 0 ) { 92 goto done; 93 } 94 /* we don't need BOM in output so we choose native BE or LE encoding here */ 95 codeset_out = (platformByteOrder()==BYTE_ORDER_MSBFIRST) ? 96 "UCS-2BE" : "UCS-2LE"; 97 98 cd = iconv_open(codeset_out, codeset); 99 if (cd == (iconv_t)-1 ) { 100 goto done; 101 } 102 inSize = strlen(in); 103 buf = SAFE_SIZE_ARRAY_ALLOC(malloc, inSize, 2); 104 if (!buf) { 105 return NULL; 106 } 107 bufSize = inSize*2; // need 2 bytes per char for UCS-2, this is 108 // 2 bytes per source byte max 109 out = buf; outSize = bufSize; 110 /* linux iconv wants char** source and solaris wants const char**... 111 cast to void* */ 112 rc = iconv(cd, (void*)&in, &inSize, &out, &outSize); 113 iconv_close(cd); 114 115 if (rc == (size_t)-1) { 116 free(buf); 117 buf = NULL; 118 } else { 119 if (size) { 120 *size = (bufSize-outSize)/2; /* bytes to wchars */ 121 } 122 } 123 done: 124 setlocale(LC_ALL, old_locale); 125 return buf; 126 } 127 128 char* SplashGetScaledImageName(const char* jar, const char* file, 129 float *scaleFactor) { 130 NSAutoreleasePool *pool = [NSAutoreleasePool new]; 131 *scaleFactor = 1; 132 char* scaledFile = nil; 133 __block float screenScaleFactor = 1; 134 135 [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ 136 screenScaleFactor = [SplashNSScreen() backingScaleFactor]; 137 }]; 138 139 if (screenScaleFactor > 1) { 140 NSString *fileName = [NSString stringWithUTF8String: file]; 141 NSUInteger length = [fileName length]; 142 NSRange range = [fileName rangeOfString: @"." 143 options:NSBackwardsSearch]; 144 NSUInteger dotIndex = range.location; 145 NSString *fileName2x = nil; 146 147 if (dotIndex == NSNotFound) { 148 fileName2x = [fileName stringByAppendingString: @"@2x"]; 149 } else { 150 fileName2x = [fileName substringToIndex: dotIndex]; 151 fileName2x = [fileName2x stringByAppendingString: @"@2x"]; 152 fileName2x = [fileName2x stringByAppendingString: 153 [fileName substringFromIndex: dotIndex]]; 154 } 155 156 if ((fileName2x != nil) && (jar || [[NSFileManager defaultManager] 157 fileExistsAtPath: fileName2x])){ 158 *scaleFactor = 2; 159 scaledFile = strdup([fileName2x UTF8String]); 160 } 161 } 162 [pool drain]; 163 return scaledFile; 164 } 165 166 void 167 SplashInitPlatform(Splash * splash) { 168 pthread_mutex_init(&splash->lock, NULL); 169 170 splash->maskRequired = 0; 171 172 173 //TODO: the following is too much of a hack but should work in 90% cases. 174 // besides we don't use device-dependant drawing, so probably 175 // that's very fine indeed 176 splash->byteAlignment = 1; 177 initFormat(&splash->screenFormat, 0xff << 8, 178 0xff << 16, 0xff << 24, 0xff << 0); 179 splash->screenFormat.byteOrder = 1 ? BYTE_ORDER_LSBFIRST : BYTE_ORDER_MSBFIRST; 180 splash->screenFormat.depthBytes = 4; 181 182 // If this property is present we are running SWT and should not start a runLoop 183 // Can't check if running SWT in webstart, so splash screen in webstart SWT 184 // applications is not supported 185 char envVar[80]; 186 snprintf(envVar, sizeof(envVar), "JAVA_STARTED_ON_FIRST_THREAD_%d", getpid()); 187 if (getenv(envVar) == NULL) { 188 [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^() { 189 [NSApplicationAWT runAWTLoopWithApp:[NSApplicationAWT sharedApplication]]; 190 }]; 191 } 192 } 193 194 void 195 SplashCleanupPlatform(Splash * splash) { 196 splash->maskRequired = 0; 197 } 198 199 void 200 SplashDonePlatform(Splash * splash) { 201 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 202 203 pthread_mutex_destroy(&splash->lock); 204 [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ 205 if (splash->window) { 206 [splash->window orderOut:nil]; 207 [splash->window release]; 208 } 209 }]; 210 [pool drain]; 211 } 212 213 void 214 SplashLock(Splash * splash) { 215 pthread_mutex_lock(&splash->lock); 216 } 217 218 void 219 SplashUnlock(Splash * splash) { 220 pthread_mutex_unlock(&splash->lock); 221 } 222 223 void 224 SplashInitFrameShape(Splash * splash, int imageIndex) { 225 // No shapes, we rely on alpha compositing 226 } 227 228 void * SplashScreenThread(void *param); 229 void 230 SplashCreateThread(Splash * splash) { 231 pthread_t thr; 232 pthread_attr_t attr; 233 int rc; 234 235 pthread_attr_init(&attr); 236 rc = pthread_create(&thr, &attr, SplashScreenThread, (void *) splash); 237 } 238 239 void 240 SplashRedrawWindow(Splash * splash) { 241 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 242 243 SplashUpdateScreenData(splash); 244 245 [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ 246 // NSDeviceRGBColorSpace vs. NSCalibratedRGBColorSpace ? 247 NSBitmapImageRep * rep = [[NSBitmapImageRep alloc] 248 initWithBitmapDataPlanes: (unsigned char**)&splash->screenData 249 pixelsWide: splash->width 250 pixelsHigh: splash->height 251 bitsPerSample: 8 252 samplesPerPixel: 4 253 hasAlpha: YES 254 isPlanar: NO 255 colorSpaceName: NSDeviceRGBColorSpace 256 bitmapFormat: NSAlphaFirstBitmapFormat | NSAlphaNonpremultipliedBitmapFormat 257 bytesPerRow: splash->width * 4 258 bitsPerPixel: 32]; 259 260 NSImage * image = [[NSImage alloc] 261 initWithSize: NSMakeSize(splash->width, splash->height)]; 262 [image setBackgroundColor: [NSColor clearColor]]; 263 264 [image addRepresentation: rep]; 265 float scaleFactor = splash->scaleFactor; 266 if (scaleFactor > 0 && scaleFactor != 1) { 267 [image setScalesWhenResized:YES]; 268 NSSize size = [image size]; 269 size.width /= scaleFactor; 270 size.height /= scaleFactor; 271 [image setSize: size]; 272 } 273 274 NSImageView * view = [[NSImageView alloc] init]; 275 276 [view setImage: image]; 277 [view setEditable: NO]; 278 //NOTE: we don't set a 'wait cursor' for the view because: 279 // 1. The Cocoa GUI guidelines suggest to avoid it, and use a progress 280 // bar instead. 281 // 2. There simply isn't an instance of NSCursor that represent 282 // the 'wait cursor'. So that is undoable. 283 284 //TODO: only the first image in an animated gif preserves transparency. 285 // Loos like the splash->screenData contains inappropriate data 286 // for all but the first frame. 287 288 [image release]; 289 [rep release]; 290 291 [splash->window setContentView: view]; 292 [splash->window orderFrontRegardless]; 293 }]; 294 295 [pool drain]; 296 } 297 298 void SplashReconfigureNow(Splash * splash) { 299 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 300 301 [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ 302 SplashCenter(splash); 303 304 if (!splash->window) { 305 return; 306 } 307 308 [splash->window orderOut:nil]; 309 [splash->window setFrame: NSMakeRect(splash->x, splash->y, splash->width, splash->height) 310 display: NO]; 311 }]; 312 313 [pool drain]; 314 315 SplashRedrawWindow(splash); 316 } 317 318 void 319 SplashEventLoop(Splash * splash) { 320 321 /* we should have splash _locked_ on entry!!! */ 322 323 while (1) { 324 struct pollfd pfd[1]; 325 int timeout = -1; 326 int ctl = splash->controlpipe[0]; 327 int rc; 328 int pipes_empty; 329 330 pfd[0].fd = ctl; 331 pfd[0].events = POLLIN | POLLPRI; 332 333 errno = 0; 334 if (splash->isVisible>0 && SplashIsStillLooping(splash)) { 335 timeout = splash->time + splash->frames[splash->currentFrame].delay 336 - SplashTime(); 337 if (timeout < 0) { 338 timeout = 0; 339 } 340 } 341 SplashUnlock(splash); 342 rc = poll(pfd, 1, timeout); 343 SplashLock(splash); 344 if (splash->isVisible > 0 && splash->currentFrame >= 0 && 345 SplashTime() >= splash->time + splash->frames[splash->currentFrame].delay) { 346 SplashNextFrame(splash); 347 SplashRedrawWindow(splash); 348 } 349 if (rc <= 0) { 350 errno = 0; 351 continue; 352 } 353 pipes_empty = 0; 354 while(!pipes_empty) { 355 char buf; 356 357 pipes_empty = 1; 358 if (read(ctl, &buf, sizeof(buf)) > 0) { 359 pipes_empty = 0; 360 switch (buf) { 361 case SPLASHCTL_UPDATE: 362 if (splash->isVisible>0) { 363 SplashRedrawWindow(splash); 364 } 365 break; 366 case SPLASHCTL_RECONFIGURE: 367 if (splash->isVisible>0) { 368 SplashReconfigureNow(splash); 369 } 370 break; 371 case SPLASHCTL_QUIT: 372 return; 373 } 374 } 375 } 376 } 377 } 378 379 void * 380 SplashScreenThread(void *param) { 381 objc_registerThreadWithCollector(); 382 383 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 384 Splash *splash = (Splash *) param; 385 386 SplashLock(splash); 387 pipe(splash->controlpipe); 388 fcntl(splash->controlpipe[0], F_SETFL, 389 fcntl(splash->controlpipe[0], F_GETFL, 0) | O_NONBLOCK); 390 splash->time = SplashTime(); 391 splash->currentFrame = 0; 392 [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ 393 SplashCenter(splash); 394 395 splash->window = (void*) [[NSWindow alloc] 396 initWithContentRect: NSMakeRect(splash->x, splash->y, splash->width, splash->height) 397 styleMask: NSBorderlessWindowMask 398 backing: NSBackingStoreBuffered 399 defer: NO 400 screen: SplashNSScreen()]; 401 402 [splash->window setOpaque: NO]; 403 [splash->window setBackgroundColor: [NSColor clearColor]]; 404 }]; 405 fflush(stdout); 406 if (splash->window) { 407 [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ 408 [splash->window orderFrontRegardless]; 409 }]; 410 SplashRedrawWindow(splash); 411 SplashEventLoop(splash); 412 } 413 SplashUnlock(splash); 414 SplashDone(splash); 415 416 splash->isVisible=-1; 417 418 [pool drain]; 419 420 return 0; 421 } 422 423 void 424 sendctl(Splash * splash, char code) { 425 if (splash && splash->controlpipe[1]) { 426 write(splash->controlpipe[1], &code, 1); 427 } 428 } 429 430 void 431 SplashClosePlatform(Splash * splash) { 432 sendctl(splash, SPLASHCTL_QUIT); 433 } 434 435 void 436 SplashUpdate(Splash * splash) { 437 sendctl(splash, SPLASHCTL_UPDATE); 438 } 439 440 void 441 SplashReconfigure(Splash * splash) { 442 sendctl(splash, SPLASHCTL_RECONFIGURE); 443 } 444