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