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