src/java.desktop/macosx/native/libsplashscreen/splashscreen_sys.m
changeset 47216 71c04702a3d5
parent 47020 2c55106dc37b
child 55189 fdaf7287ea3a
child 58678 9cf78a70fa4f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.desktop/macosx/native/libsplashscreen/splashscreen_sys.m	Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,471 @@
+/*
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "splashscreen_impl.h"
+
+#import <Cocoa/Cocoa.h>
+#import <objc/objc-auto.h>
+
+#import <JavaNativeFoundation/JavaNativeFoundation.h>
+#import "NSApplicationAWT.h"
+
+#include <sys/time.h>
+#include <pthread.h>
+#include <iconv.h>
+#include <langinfo.h>
+#include <locale.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <unistd.h>
+#include <dlfcn.h>
+
+#include <sizecalc.h>
+#import "ThreadUtilities.h"
+
+NSString* findScaledImageName(NSString *fileName,
+                              NSUInteger dotIndex,
+                              NSString *strToAppend);
+
+static NSScreen* SplashNSScreen()
+{
+    return [[NSScreen screens] objectAtIndex: 0];
+}
+
+static void SplashCenter(Splash * splash)
+{
+    NSRect screenFrame = [SplashNSScreen() frame];
+
+    splash->x = (screenFrame.size.width - splash->width) / 2;
+    splash->y = (screenFrame.size.height - splash->height) / 2 + screenFrame.origin.y;
+}
+
+unsigned
+SplashTime(void) {
+    struct timeval tv;
+    struct timezone tz;
+    unsigned long long msec;
+
+    gettimeofday(&tv, &tz);
+    msec = (unsigned long long) tv.tv_sec * 1000 +
+        (unsigned long long) tv.tv_usec / 1000;
+
+    return (unsigned) msec;
+}
+
+/* Could use npt but decided to cut down on linked code size */
+char* SplashConvertStringAlloc(const char* in, int* size) {
+    const char     *codeset;
+    const char     *codeset_out;
+    iconv_t         cd;
+    size_t          rc;
+    char           *buf = NULL, *out;
+    size_t          bufSize, inSize, outSize;
+    const char* old_locale;
+
+    if (!in) {
+        return NULL;
+    }
+    old_locale = setlocale(LC_ALL, "");
+
+    codeset = nl_langinfo(CODESET);
+    if ( codeset == NULL || codeset[0] == 0 ) {
+        goto done;
+    }
+    /* we don't need BOM in output so we choose native BE or LE encoding here */
+    codeset_out = (platformByteOrder()==BYTE_ORDER_MSBFIRST) ?
+        "UCS-2BE" : "UCS-2LE";
+
+    cd = iconv_open(codeset_out, codeset);
+    if (cd == (iconv_t)-1 ) {
+        goto done;
+    }
+    inSize = strlen(in);
+    buf = SAFE_SIZE_ARRAY_ALLOC(malloc, inSize, 2);
+    if (!buf) {
+        return NULL;
+    }
+    bufSize = inSize*2; // need 2 bytes per char for UCS-2, this is
+                        // 2 bytes per source byte max
+    out = buf; outSize = bufSize;
+    /* linux iconv wants char** source and solaris wants const char**...
+       cast to void* */
+    rc = iconv(cd, (void*)&in, &inSize, &out, &outSize);
+    iconv_close(cd);
+
+    if (rc == (size_t)-1) {
+        free(buf);
+        buf = NULL;
+    } else {
+        if (size) {
+            *size = (bufSize-outSize)/2; /* bytes to wchars */
+        }
+    }
+done:
+    setlocale(LC_ALL, old_locale);
+    return buf;
+}
+
+BOOL isSWTRunning() {
+    char envVar[80];
+    // If this property is present we are running SWT
+    snprintf(envVar, sizeof(envVar), "JAVA_STARTED_ON_FIRST_THREAD_%d", getpid());
+    return getenv(envVar) != NULL;
+}
+
+jboolean SplashGetScaledImageName(const char* jar, const char* file,
+                                  float *scaleFactor, char *scaledFile,
+                                  const size_t scaledImageLength) {
+    *scaleFactor = 1;
+
+    if(isSWTRunning()){
+        return JNI_FALSE;
+    }
+
+    NSAutoreleasePool *pool = [NSAutoreleasePool new];
+    __block float screenScaleFactor = 1;
+
+    [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
+        // initialize NSApplication and AWT stuff
+        [NSApplicationAWT sharedApplication];
+        screenScaleFactor = [SplashNSScreen() backingScaleFactor];
+    }];
+
+    if (screenScaleFactor > 1) {
+        NSString *fileName = [NSString stringWithUTF8String: file];
+        NSUInteger length = [fileName length];
+        NSRange range = [fileName rangeOfString: @"."
+                                        options:NSBackwardsSearch];
+        NSUInteger dotIndex = range.location;
+        NSString *fileName2x = nil;
+
+        fileName2x = findScaledImageName(fileName, dotIndex, @"@2x");
+        if(![[NSFileManager defaultManager]
+                fileExistsAtPath: fileName2x]) {
+            fileName2x = findScaledImageName(fileName, dotIndex, @"@200pct");
+        }
+        if (jar || [[NSFileManager defaultManager]
+                fileExistsAtPath: fileName2x]){
+            if (strlen([fileName2x UTF8String]) > scaledImageLength) {
+                [pool drain];
+                return JNI_FALSE;
+            }
+            *scaleFactor = 2;
+            strcpy(scaledFile, [fileName2x UTF8String]);
+            [pool drain];
+            return JNI_TRUE;
+        }
+    }
+    [pool drain];
+    return JNI_FALSE;
+}
+
+void
+SplashInitPlatform(Splash * splash) {
+    pthread_mutex_init(&splash->lock, NULL);
+
+    splash->maskRequired = 0;
+
+    
+    //TODO: the following is too much of a hack but should work in 90% cases.
+    //      besides we don't use device-dependent drawing, so probably
+    //      that's very fine indeed
+    splash->byteAlignment = 1;
+    initFormat(&splash->screenFormat, 0xff << 8,
+            0xff << 16, 0xff << 24, 0xff << 0);
+    splash->screenFormat.byteOrder = 1 ?  BYTE_ORDER_LSBFIRST : BYTE_ORDER_MSBFIRST;
+    splash->screenFormat.depthBytes = 4;
+
+    // If we are running SWT we should not start a runLoop
+    if (!isSWTRunning()) {
+        [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^() {
+            [NSApplicationAWT runAWTLoopWithApp:[NSApplicationAWT sharedApplication]];
+        }];
+    }
+}
+
+void
+SplashCleanupPlatform(Splash * splash) {
+    splash->maskRequired = 0;
+}
+
+void
+SplashDonePlatform(Splash * splash) {
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+    pthread_mutex_destroy(&splash->lock);
+    [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){
+        if (splash->window) {
+            [splash->window orderOut:nil];
+            [splash->window release];
+        }
+    }];
+    [pool drain];
+}
+
+void
+SplashLock(Splash * splash) {
+    pthread_mutex_lock(&splash->lock);
+}
+
+void
+SplashUnlock(Splash * splash) {
+    pthread_mutex_unlock(&splash->lock);
+}
+
+void
+SplashInitFrameShape(Splash * splash, int imageIndex) {
+    // No shapes, we rely on alpha compositing
+}
+
+void * SplashScreenThread(void *param);
+void
+SplashCreateThread(Splash * splash) {
+    pthread_t thr;
+    pthread_attr_t attr;
+    int rc;
+
+    pthread_attr_init(&attr);
+    rc = pthread_create(&thr, &attr, SplashScreenThread, (void *) splash);
+}
+
+void
+SplashRedrawWindow(Splash * splash) {
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+    SplashUpdateScreenData(splash);
+
+    [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){
+        // NSDeviceRGBColorSpace vs. NSCalibratedRGBColorSpace ?
+        NSBitmapImageRep * rep = [[NSBitmapImageRep alloc]
+            initWithBitmapDataPlanes: (unsigned char**)&splash->screenData
+                          pixelsWide: splash->width
+                          pixelsHigh: splash->height
+                       bitsPerSample: 8
+                     samplesPerPixel: 4
+                            hasAlpha: YES
+                            isPlanar: NO
+                      colorSpaceName: NSDeviceRGBColorSpace
+                        bitmapFormat: NSAlphaFirstBitmapFormat | NSAlphaNonpremultipliedBitmapFormat
+                         bytesPerRow: splash->width * 4
+                        bitsPerPixel: 32];
+
+        NSImage * image = [[NSImage alloc]
+            initWithSize: NSMakeSize(splash->width, splash->height)];
+        [image setBackgroundColor: [NSColor clearColor]];
+
+        [image addRepresentation: rep];
+        float scaleFactor = splash->scaleFactor;
+        if (scaleFactor > 0 && scaleFactor != 1) {
+            NSSize size = [image size];
+            size.width /= scaleFactor;
+            size.height /= scaleFactor;
+            [image setSize: size];
+        }
+        
+        NSImageView * view = [[NSImageView alloc] init];
+
+        [view setImage: image];
+        [view setEditable: NO];
+        //NOTE: we don't set a 'wait cursor' for the view because:
+        //      1. The Cocoa GUI guidelines suggest to avoid it, and use a progress
+        //         bar instead.
+        //      2. There simply isn't an instance of NSCursor that represent
+        //         the 'wait cursor'. So that is undoable.
+
+        //TODO: only the first image in an animated gif preserves transparency.
+        //      Loos like the splash->screenData contains inappropriate data
+        //      for all but the first frame.
+
+        [image release];
+        [rep release];
+
+        [splash->window setContentView: view];
+        [splash->window orderFrontRegardless];
+    }];
+
+    [pool drain];
+}
+
+void SplashReconfigureNow(Splash * splash) {
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+    [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){
+        SplashCenter(splash);
+
+        if (!splash->window) {
+            return;
+        }
+
+        [splash->window orderOut:nil];
+        [splash->window setFrame: NSMakeRect(splash->x, splash->y, splash->width, splash->height)
+                         display: NO];
+    }];
+
+    [pool drain];
+
+    SplashRedrawWindow(splash);
+}
+
+void
+SplashEventLoop(Splash * splash) {
+
+    /* we should have splash _locked_ on entry!!! */
+
+    while (1) {
+        struct pollfd pfd[1];
+        int timeout = -1;
+        int ctl = splash->controlpipe[0];
+        int rc;
+        int pipes_empty;
+
+        pfd[0].fd = ctl;
+        pfd[0].events = POLLIN | POLLPRI;
+
+        errno = 0;
+        if (splash->isVisible>0 && SplashIsStillLooping(splash)) {
+            timeout = splash->time + splash->frames[splash->currentFrame].delay
+                - SplashTime();
+            if (timeout < 0) {
+                timeout = 0;
+            }
+        }
+        SplashUnlock(splash);
+        rc = poll(pfd, 1, timeout);
+        SplashLock(splash);
+        if (splash->isVisible > 0 && splash->currentFrame >= 0 &&
+                SplashTime() >= splash->time + splash->frames[splash->currentFrame].delay) {
+            SplashNextFrame(splash);
+            SplashRedrawWindow(splash);
+        }
+        if (rc <= 0) {
+            errno = 0;
+            continue;
+        }
+        pipes_empty = 0;
+        while(!pipes_empty) {
+            char buf;
+
+            pipes_empty = 1;
+            if (read(ctl, &buf, sizeof(buf)) > 0) {
+                pipes_empty = 0;
+                switch (buf) {
+                case SPLASHCTL_UPDATE:
+                    if (splash->isVisible>0) {
+                        SplashRedrawWindow(splash);
+                    }
+                    break;
+                case SPLASHCTL_RECONFIGURE:
+                    if (splash->isVisible>0) {
+                        SplashReconfigureNow(splash);
+                    }
+                    break;
+                case SPLASHCTL_QUIT:
+                    return;
+                }
+            }
+        }
+    }
+}
+
+void *
+SplashScreenThread(void *param) {
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    Splash *splash = (Splash *) param;
+
+    SplashLock(splash);
+    pipe(splash->controlpipe);
+    fcntl(splash->controlpipe[0], F_SETFL,
+        fcntl(splash->controlpipe[0], F_GETFL, 0) | O_NONBLOCK);
+    splash->time = SplashTime();
+    splash->currentFrame = 0;
+    [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){
+        SplashCenter(splash);
+
+        splash->window = (void*) [[NSWindow alloc]
+            initWithContentRect: NSMakeRect(splash->x, splash->y, splash->width, splash->height)
+                      styleMask: NSBorderlessWindowMask
+                        backing: NSBackingStoreBuffered
+                          defer: NO
+                         screen: SplashNSScreen()];
+
+        [splash->window setOpaque: NO];
+        [splash->window setBackgroundColor: [NSColor clearColor]];
+    }];
+    fflush(stdout);
+    if (splash->window) {
+        [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){
+            [splash->window orderFrontRegardless];
+        }];
+        SplashRedrawWindow(splash);
+        SplashEventLoop(splash);
+    }
+    SplashUnlock(splash);
+    SplashDone(splash);
+
+    splash->isVisible=-1;
+
+    [pool drain];
+
+    return 0;
+}
+
+void
+sendctl(Splash * splash, char code) {
+    if (splash && splash->controlpipe[1]) {
+        write(splash->controlpipe[1], &code, 1);
+    }
+}
+
+void
+SplashClosePlatform(Splash * splash) {
+    sendctl(splash, SPLASHCTL_QUIT);
+}
+
+void
+SplashUpdate(Splash * splash) {
+    sendctl(splash, SPLASHCTL_UPDATE);
+}
+
+void
+SplashReconfigure(Splash * splash) {
+    sendctl(splash, SPLASHCTL_RECONFIGURE);
+}
+
+NSString* findScaledImageName(NSString *fileName, NSUInteger dotIndex, NSString *strToAppend) {
+    NSString *fileName2x = nil;
+    if (dotIndex == NSNotFound) {
+        fileName2x = [fileName stringByAppendingString: strToAppend];
+    } else {
+        fileName2x = [fileName substringToIndex: dotIndex];
+        fileName2x = [fileName2x stringByAppendingString: strToAppend];
+        fileName2x = [fileName2x stringByAppendingString:
+                      [fileName substringFromIndex: dotIndex]];
+    }
+    return fileName2x;
+}
+