Path: blob/master/src/java.desktop/macosx/native/libsplashscreen/splashscreen_sys.m
41149 views
/*1* Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425#include "splashscreen_impl.h"2627#import <Cocoa/Cocoa.h>28#import <objc/objc-auto.h>2930#include <Security/AuthSession.h>31#import "NSApplicationAWT.h"3233#include <sys/time.h>34#include <pthread.h>35#include <iconv.h>36#include <langinfo.h>37#include <locale.h>38#include <fcntl.h>39#include <poll.h>40#include <errno.h>41#include <sys/types.h>42#include <signal.h>43#include <unistd.h>44#include <dlfcn.h>4546#include <sizecalc.h>47#import "ThreadUtilities.h"4849NSString* findScaledImageName(NSString *fileName,50NSUInteger dotIndex,51NSString *strToAppend);5253static NSScreen* SplashNSScreen()54{55return [[NSScreen screens] objectAtIndex: 0];56}5758static void SplashCenter(Splash * splash)59{60NSRect screenFrame = [SplashNSScreen() frame];6162splash->x = (screenFrame.size.width - splash->width) / 2;63splash->y = (screenFrame.size.height - splash->height) / 2 + screenFrame.origin.y;64}6566unsigned67SplashTime(void) {68struct timeval tv;69struct timezone tz;70unsigned long long msec;7172gettimeofday(&tv, &tz);73msec = (unsigned long long) tv.tv_sec * 1000 +74(unsigned long long) tv.tv_usec / 1000;7576return (unsigned) msec;77}7879/* Could use npt but decided to cut down on linked code size */80char* SplashConvertStringAlloc(const char* in, int* size) {81const char *codeset;82const char *codeset_out;83iconv_t cd;84size_t rc;85char *buf = NULL, *out;86size_t bufSize, inSize, outSize;87const char* old_locale;8889if (!in) {90return NULL;91}92old_locale = setlocale(LC_ALL, "");9394codeset = nl_langinfo(CODESET);95if ( codeset == NULL || codeset[0] == 0 ) {96goto done;97}98/* we don't need BOM in output so we choose native BE or LE encoding here */99codeset_out = (platformByteOrder()==BYTE_ORDER_MSBFIRST) ?100"UCS-2BE" : "UCS-2LE";101102cd = iconv_open(codeset_out, codeset);103if (cd == (iconv_t)-1 ) {104goto done;105}106inSize = strlen(in);107buf = SAFE_SIZE_ARRAY_ALLOC(malloc, inSize, 2);108if (!buf) {109return NULL;110}111bufSize = inSize*2; // need 2 bytes per char for UCS-2, this is112// 2 bytes per source byte max113out = buf; outSize = bufSize;114/* linux iconv wants char** source and solaris wants const char**...115cast to void* */116rc = iconv(cd, (void*)&in, &inSize, &out, &outSize);117iconv_close(cd);118119if (rc == (size_t)-1) {120free(buf);121buf = NULL;122} else {123if (size) {124*size = (bufSize-outSize)/2; /* bytes to wchars */125}126}127done:128setlocale(LC_ALL, old_locale);129return buf;130}131132BOOL isSWTRunning() {133char envVar[80];134// If this property is present we are running SWT135snprintf(envVar, sizeof(envVar), "JAVA_STARTED_ON_FIRST_THREAD_%d", getpid());136return getenv(envVar) != NULL;137}138139jboolean SplashGetScaledImageName(const char* jar, const char* file,140float *scaleFactor, char *scaledFile,141const size_t scaledImageLength) {142*scaleFactor = 1;143144if(isSWTRunning()){145return JNI_FALSE;146}147148NSAutoreleasePool *pool = [NSAutoreleasePool new];149__block float screenScaleFactor = 1;150151[ThreadUtilities performOnMainThreadWaiting:YES block:^(){152// initialize NSApplication and AWT stuff153[NSApplicationAWT sharedApplication];154screenScaleFactor = [SplashNSScreen() backingScaleFactor];155}];156157if (screenScaleFactor > 1) {158NSString *fileName = [NSString stringWithUTF8String: file];159NSUInteger length = [fileName length];160NSRange range = [fileName rangeOfString: @"."161options:NSBackwardsSearch];162NSUInteger dotIndex = range.location;163NSString *fileName2x = nil;164165fileName2x = findScaledImageName(fileName, dotIndex, @"@2x");166if(![[NSFileManager defaultManager]167fileExistsAtPath: fileName2x]) {168fileName2x = findScaledImageName(fileName, dotIndex, @"@200pct");169}170if (jar || [[NSFileManager defaultManager]171fileExistsAtPath: fileName2x]){172if (strlen([fileName2x UTF8String]) > scaledImageLength) {173[pool drain];174return JNI_FALSE;175}176*scaleFactor = 2;177strcpy(scaledFile, [fileName2x UTF8String]);178[pool drain];179return JNI_TRUE;180}181}182[pool drain];183return JNI_FALSE;184}185186static int isInAquaSession() {187// environment variable to bypass the aqua session check188char *ev = getenv("AWT_FORCE_HEADFUL");189if (ev && (strncasecmp(ev, "true", 4) == 0)) {190// if "true" then tell the caller we're in191// an Aqua session without actually checking192return 1;193}194// Is the WindowServer available?195SecuritySessionId session_id;196SessionAttributeBits session_info;197OSStatus status = SessionGetInfo(callerSecuritySession, &session_id, &session_info);198if (status == noErr) {199if (session_info & sessionHasGraphicAccess) {200return 1;201}202}203return 0;204}205206int207SplashInitPlatform(Splash * splash) {208if (!isInAquaSession()) {209return 0;210}211pthread_mutex_init(&splash->lock, NULL);212213splash->maskRequired = 0;214215216//TODO: the following is too much of a hack but should work in 90% cases.217// besides we don't use device-dependent drawing, so probably218// that's very fine indeed219splash->byteAlignment = 1;220initFormat(&splash->screenFormat, 0xff << 8,2210xff << 16, 0xff << 24, 0xff << 0);222splash->screenFormat.byteOrder = 1 ? BYTE_ORDER_LSBFIRST : BYTE_ORDER_MSBFIRST;223splash->screenFormat.depthBytes = 4;224225// If we are running SWT we should not start a runLoop226if (!isSWTRunning()) {227[ThreadUtilities performOnMainThreadWaiting:NO block:^() {228[NSApplicationAWT runAWTLoopWithApp:[NSApplicationAWT sharedApplication]];229}];230}231return 1;232}233234void235SplashCleanupPlatform(Splash * splash) {236splash->maskRequired = 0;237}238239void240SplashDonePlatform(Splash * splash) {241NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];242243pthread_mutex_destroy(&splash->lock);244[ThreadUtilities performOnMainThreadWaiting:YES block:^(){245if (splash->window) {246[splash->window orderOut:nil];247[splash->window release];248}249}];250[pool drain];251}252253void254SplashLock(Splash * splash) {255pthread_mutex_lock(&splash->lock);256}257258void259SplashUnlock(Splash * splash) {260pthread_mutex_unlock(&splash->lock);261}262263void264SplashInitFrameShape(Splash * splash, int imageIndex) {265// No shapes, we rely on alpha compositing266}267268void * SplashScreenThread(void *param);269void270SplashCreateThread(Splash * splash) {271pthread_t thr;272pthread_attr_t attr;273int rc;274275pthread_attr_init(&attr);276rc = pthread_create(&thr, &attr, SplashScreenThread, (void *) splash);277}278279void280SplashRedrawWindow(Splash * splash) {281NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];282283[ThreadUtilities performOnMainThreadWaiting:YES block:^(){284// drop the reference to the old view and image285[splash->window setContentView: nil];286SplashUpdateScreenData(splash);287288// NSDeviceRGBColorSpace vs. NSCalibratedRGBColorSpace ?289NSBitmapImageRep * rep = [[NSBitmapImageRep alloc]290initWithBitmapDataPlanes: (unsigned char**)&splash->screenData291pixelsWide: splash->width292pixelsHigh: splash->height293bitsPerSample: 8294samplesPerPixel: 4295hasAlpha: YES296isPlanar: NO297colorSpaceName: NSDeviceRGBColorSpace298bitmapFormat: NSAlphaFirstBitmapFormat | NSAlphaNonpremultipliedBitmapFormat299bytesPerRow: splash->width * 4300bitsPerPixel: 32];301302NSImage * image = [[NSImage alloc]303initWithSize: NSMakeSize(splash->width, splash->height)];304[image setBackgroundColor: [NSColor clearColor]];305306[image addRepresentation: rep];307float scaleFactor = splash->scaleFactor;308if (scaleFactor > 0 && scaleFactor != 1) {309NSSize size = [image size];310size.width /= scaleFactor;311size.height /= scaleFactor;312[image setSize: size];313}314315NSImageView * view = [[NSImageView alloc] init];316317[view setImage: image];318[view setEditable: NO];319//NOTE: we don't set a 'wait cursor' for the view because:320// 1. The Cocoa GUI guidelines suggest to avoid it, and use a progress321// bar instead.322// 2. There simply isn't an instance of NSCursor that represent323// the 'wait cursor'. So that is undoable.324325//TODO: only the first image in an animated gif preserves transparency.326// Loos like the splash->screenData contains inappropriate data327// for all but the first frame.328329[image release];330[rep release];331332[splash->window setContentView: view];333[splash->window orderFrontRegardless];334}];335336[pool drain];337}338339void SplashReconfigureNow(Splash * splash) {340NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];341342[ThreadUtilities performOnMainThreadWaiting:YES block:^(){343SplashCenter(splash);344345if (!splash->window) {346return;347}348349[splash->window orderOut:nil];350[splash->window setFrame: NSMakeRect(splash->x, splash->y, splash->width, splash->height)351display: NO];352}];353354[pool drain];355356SplashRedrawWindow(splash);357}358359void360SplashEventLoop(Splash * splash) {361362/* we should have splash _locked_ on entry!!! */363364while (1) {365struct pollfd pfd[1];366int timeout = -1;367int ctl = splash->controlpipe[0];368int rc;369int pipes_empty;370371pfd[0].fd = ctl;372pfd[0].events = POLLIN | POLLPRI;373374errno = 0;375if (splash->isVisible>0 && SplashIsStillLooping(splash)) {376timeout = splash->time + splash->frames[splash->currentFrame].delay377- SplashTime();378if (timeout < 0) {379timeout = 0;380}381}382SplashUnlock(splash);383rc = poll(pfd, 1, timeout);384SplashLock(splash);385if (splash->isVisible > 0 && splash->currentFrame >= 0 &&386SplashTime() >= splash->time + splash->frames[splash->currentFrame].delay) {387SplashNextFrame(splash);388SplashRedrawWindow(splash);389}390if (rc <= 0) {391errno = 0;392continue;393}394pipes_empty = 0;395while(!pipes_empty) {396char buf;397398pipes_empty = 1;399if (read(ctl, &buf, sizeof(buf)) > 0) {400pipes_empty = 0;401switch (buf) {402case SPLASHCTL_UPDATE:403if (splash->isVisible>0) {404SplashRedrawWindow(splash);405}406break;407case SPLASHCTL_RECONFIGURE:408if (splash->isVisible>0) {409SplashReconfigureNow(splash);410}411break;412case SPLASHCTL_QUIT:413return;414}415}416}417}418}419420void *421SplashScreenThread(void *param) {422NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];423Splash *splash = (Splash *) param;424425SplashLock(splash);426pipe(splash->controlpipe);427fcntl(splash->controlpipe[0], F_SETFL,428fcntl(splash->controlpipe[0], F_GETFL, 0) | O_NONBLOCK);429splash->time = SplashTime();430splash->currentFrame = 0;431[ThreadUtilities performOnMainThreadWaiting:YES block:^(){432SplashCenter(splash);433434splash->window = (void*) [[NSWindow alloc]435initWithContentRect: NSMakeRect(splash->x, splash->y, splash->width, splash->height)436styleMask: NSBorderlessWindowMask437backing: NSBackingStoreBuffered438defer: NO439screen: SplashNSScreen()];440441[splash->window setOpaque: NO];442[splash->window setBackgroundColor: [NSColor clearColor]];443}];444fflush(stdout);445if (splash->window) {446[ThreadUtilities performOnMainThreadWaiting:YES block:^(){447[splash->window orderFrontRegardless];448}];449SplashRedrawWindow(splash);450SplashEventLoop(splash);451}452SplashUnlock(splash);453SplashDone(splash);454455splash->isVisible=-1;456457[pool drain];458459return 0;460}461462void463sendctl(Splash * splash, char code) {464if (splash && splash->controlpipe[1]) {465write(splash->controlpipe[1], &code, 1);466}467}468469void470SplashClosePlatform(Splash * splash) {471sendctl(splash, SPLASHCTL_QUIT);472}473474void475SplashUpdate(Splash * splash) {476sendctl(splash, SPLASHCTL_UPDATE);477}478479void480SplashReconfigure(Splash * splash) {481sendctl(splash, SPLASHCTL_RECONFIGURE);482}483484NSString* findScaledImageName(NSString *fileName, NSUInteger dotIndex, NSString *strToAppend) {485NSString *fileName2x = nil;486if (dotIndex == NSNotFound) {487fileName2x = [fileName stringByAppendingString: strToAppend];488} else {489fileName2x = [fileName substringToIndex: dotIndex];490fileName2x = [fileName2x stringByAppendingString: strToAppend];491fileName2x = [fileName2x stringByAppendingString:492[fileName substringFromIndex: dotIndex]];493}494return fileName2x;495}496497498499