Hey all, So it seems AWT in 1.6 will include support for splash-screens. This will require a little (optional) VM support, so here's a proposal and some prototype code for this I'd like feedback on. This is done by either: 1) Giving the VM a command line option and filename (JPEG/GIF/PNG is supported) 2) Including such an image in a JAR and using an option in the manifest. (In the case of both, the former has precedence.) The idea is that the splashscreen should be loaded and shown as quickly as possible. Once the first AWT window is opened by the app, the splashscreen is closed. But once user code is running the program can update the splashscreen prior to that, using a new SplashScreen class. * For VM implementors: Ok, so I've got a proposal for how to do this. For the VM part, see splashscreen.h. There are two native functions to call: int cp_awt_splashscreen_load_file( char *filename ); int cp_awt_splashscreen_load_data( char *url, char *imagedata ); Corresponding to loading from the command line and from a jar, respectively. These methods should load the file, create a frameless window, display it, and return as fast as they can, using the native toolkit instead of java. Strictly speaking we only need one function, but since the command-line option takes precedence we can use that to show the splash earlier. On failure, the VM can display a message or silently ignore it. * For peer implementors: A new ClasspathToolkit method: public abstract SplashWindow getSplashWindow(); The public SplashScreen class is implemented on top of our SplashWindow class. This is more or less an ordinary Window. But more on this later, first the native side of things: The native methods above set a global variable (cp_splashscreen) which is a struct splashscreenhandle { void *nativeWindowHandle; void *nativeImageHandle; } If set (the variable is null if no splashscreen is available or if loading failed), the structure should be populated with platform-specific values for the window handle and image data. On X/GTK/Qt, this should be understood to mean XIDs for the window and a pixmap, respectively. NOT a toolkit-specific structure (See below). On Windows (athough we don't have peers for that yet) it'd be a HWND and HBITMAP, and so on. The reason why we want to use X structures here is this: We cannot know from the start which set of peers the user is using, and therefore we don't know which peers will want to create the SplashWindow instance. The idea here is that the native splashscreen code should be compiled to use the default toolkit (or none). If the user has chosen a different toolkit at runtime, the thing should still work. Q and A.. Q: What does the peer need to do, then? A: Implement getSplashWindow. What this should do, is retrive the cp_splashscreen structure and use the X handles to wrap it in the native toolit (e.g. gdk_window_foreign_new on GTK). This object should then be wrapped with a WindowPeer from which a SplashWindow implementation can be created. (This is a lot less code than it sounds like) Q: What does the VM need to do, then? A: Nothing. But if it wants splashscreens it should. Call cp_awt_splashscreen_load_file or cp_awt_splashscreen_load_data, and do so as soon as it can (after parsing the command line and reading the jar manifest, respectively). Q: Native toolkit?! But what about JNode/IKVM/Other pure-java peers? A: In those cases the VM is already loaded, or will need to be. In which case displaying a splash-screen during VM loading is rather pointless. However, a pure-java implementation which the VM can show at some point of its choice is simple to do, and should be done. /Sven
/******** PROTOTYPE CODE ***************/ /* SplashScreen.java -- Copyright (C) 2006 Free Software Foundation This file is part of GNU Classpath. GNU Classpath is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GNU Classpath 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 for more details. You should have received a copy of the GNU General Public License along with GNU Classpath; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Linking this library statically or dynamically with other modules is making a combined work based on this library. Thus, the terms and conditions of the GNU General Public License cover the whole combination. As a special exception, the copyright holders of this library give you permission to link this library with independent modules to produce an executable, regardless of the license terms of these independent modules, and to copy and distribute the resulting executable under terms of your choice, provided that you also meet, for each linked independent module, the terms and conditions of the license of that module. An independent module is a module which is not derived from or based on this library. If you modify this library, you may extend this exception to your version of the library, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ package java.awt; import gnu.java.awt.ClasspathToolkit; import gnu.java.awt.SplashWindow; public final class SplashScreen { private static SplashScreen splash; /** * Get the native splashscreen object. */ static { SplashWindow win = ((ClasspathToolkit)Toolkit.getDefaultToolkit()) .getSplashWindow(); if( win != null ) splash = new SplashScreen( win ); } public static SplashScreen getSplashScreen() { return splash; } /** * The SplashScreen window. */ private SplashWindow win; /** * Constructor used by the static code above. */ private SplashScreen(SplashWindow win) { this.win = win; } public void close() { if( win == null ) throw new IllegalStateException("SplashScreen instance closed."); win.hide(); win.dispose(); win = null; } public Graphics2D createGraphics() { if( win == null ) throw new IllegalStateException("SplashScreen instance closed."); return win.createGraphics(); } public Rectangle getBounds() { if( win == null ) throw new IllegalStateException("SplashScreen instance closed."); return win.getBounds(); } public URL getImageURL() { if( win == null ) throw new IllegalStateException("SplashScreen instance closed."); return win.getImageURL(); } public Dimension getSize() { if( win == null ) throw new IllegalStateException("SplashScreen instance closed."); return win.getSize(); } public boolean isVisible() { if( win == null ) throw new IllegalStateException("SplashScreen instance closed."); return win.isVisible(); } public void setImageURL(URL imageURL) { if( win == null ) throw new IllegalStateException("SplashScreen instance closed."); win.setImageURL( imageURL ); } public void update() { if( win == null ) throw new IllegalStateException("SplashScreen instance closed."); win.update(); } }
/******** PROTOTYPE CODE ***************/ /* SplashWindow.java -- Copyright (C) 2006 Free Software Foundation This file is part of GNU Classpath. GNU Classpath is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GNU Classpath 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 for more details. You should have received a copy of the GNU General Public License along with GNU Classpath; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Linking this library statically or dynamically with other modules is making a combined work based on this library. Thus, the terms and conditions of the GNU General Public License cover the whole combination. As a special exception, the copyright holders of this library give you permission to link this library with independent modules to produce an executable, regardless of the license terms of these independent modules, and to copy and distribute the resulting executable under terms of your choice, provided that you also meet, for each linked independent module, the terms and conditions of the license of that module. An independent module is a module which is not derived from or based on this library. If you modify this library, you may extend this exception to your version of the library, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ package gnu.java.awt; import java.awt.peer.ComponentPeer; import java.awt.Rectangle; import java.awt.Component; import java.awt.Image; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Window; import java.net.URL; public abstract class SplashWindow extends Window { private URL imageURL; private Image image; private Image overlay; private int w, h; public SplashWindow( ComponentPeer peer, URL imageURL, Image img ) { this.peer = peer; // splashscreens are visible by the time the object is created. this.visible = true; setAlwaysOnTop( true ); // (they already should be) this.imageURL = imageURL; this.image = img; createOverlay(); } private void createOverlay() { w = image.getWidth( null ); h = image.getHeight( null ); overlay = createImage( w, h ); Graphics g = overlay.getGraphics(); g.setColor(0, 0, 0, 0); // fill transparent g.fillRect(0, 0, w, h); } public Graphics2D createGraphics() { Graphics g = overlay.getGraphics(); g.setColor(0, 0, 0, 0); // fill transparent g.fillRect(0, 0, w, h); return (Graphics2D)g; } public void paint( Graphics g ) { g.drawImage( image, 0, 0, null ); g.drawImage( overlay, 0, 0, null ); } public URL getImageURL() { return imageURL; } public void setImageURL(URL imageURL) { // Load the image Image newImage = Toolkit.getDefaultToolkit().createImage( imageURL ); // Center on screen int w = newImage.getWidth( null ); int h = newImage.getHeight( null ); Rectangle screenBounds = GraphicsEnvironment.getLocalGraphicsEnvironment(). getDefaultScreenDevice().getDefaultConfiguration().getBounds(); Rectangle windowBounds = new Rectangle(); windowBounds.x = screenBounds.x + ((screenBounds.width - w) >> 1); windowBounds.y = screenBounds.y + ((screenBounds.height - h) >> 1); windowBounds.width = w; windowBounds.height = h; setBounds( windowBounds ); this.imageURL = imageURL; createOverlay(); } public void update() { repaint(); } }
#ifndef __SPLASHSCREEN_H__ #define __SPLASHSCREEN_H__ struct splashscreenhandle { void *nativeWindowHandle; void *nativeImageHandle; }; /** * Global variable */ extern struct splashscreenhandle cp_splashscreen; /** * Loads the splashscreen from a file. * filename is a null-terminated string * The return value is zero on success, nonzero otherwise. */ int cp_awt_splashscreen_load_file( char *filename ); /** * Loads an image from data. The data can and should be freed by the VM * afterwards. The image URL is only for passing on to the interface, * it is not used by the code. * * url is a null-terminated utf-8 string * imagedata is a pointer to the image file data. * The return value is zero on success, nonzero otherwise. */ int cp_awt_splashscreen_load_data( char *url, void *imagedata ); #endif