001/*
002 * JPPF.
003 * Copyright (C) 2005-2018 JPPF Team.
004 * http://www.jppf.org
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019package org.jppf.node.screensaver;
020
021import static org.jppf.utils.configuration.JPPFProperties.*;
022
023import java.awt.*;
024import java.awt.event.*;
025import java.awt.geom.Rectangle2D;
026import java.awt.image.BufferedImage;
027
028import javax.swing.*;
029
030import org.jppf.node.initialization.InitializationHook;
031import org.jppf.node.screensaver.impl.JPPFScreenSaverImpl;
032import org.jppf.utils.*;
033
034/**
035 * Main entry point for starting the screen saver.
036 * @author Laurent Cohen
037 * @since 4.0
038 */
039public class ScreenSaverMain implements InitializationHook
040{
041  /**
042   * The singleton instance of this class.
043   */
044  private static ScreenSaverMain instance = null;
045  /**
046   * The JPPF configuration.
047   */
048  private TypedProperties config;
049  /**
050   * The screensaver implementation.
051   */
052  private JPPFScreenSaver screensaver = null;
053
054  /**
055   * Test the screen saver stadalone (not part of a node).
056   * @param args not used.
057   */
058  public static void main(final String[] args) {
059    try {
060      Thread.setDefaultUncaughtExceptionHandler(new JPPFDefaultUncaughtExceptionHandler());
061      new ScreenSaverMain().startScreenSaver(JPPFConfiguration.getProperties());
062    } catch (final Exception e) {
063      e.printStackTrace();
064    }
065  }
066
067  @Override
068  public void initializing(final UnmodifiableTypedProperties initialConfiguration) {
069    try {
070      if (instance != null) return;
071      if (initialConfiguration.get(SCREENSAVER_ENABLED)) startScreenSaver(initialConfiguration);
072    } catch (final Exception e) {
073      e.printStackTrace();
074    }
075  }
076
077  /**
078   * Start the screen saver using the specified JPPF configuration.
079   * @param config the configuration to use.
080   * @throws Exception if any error occurs.
081   */
082  private void startScreenSaver(final TypedProperties config) throws Exception {
083    instance = this;
084    this.config = config;
085    createUI();
086  }
087
088  /**
089   * Create and initialize the UI and graphics.
090   * @throws Exception if any error occurs.
091   */
092  private void createUI() throws Exception {
093    if (GraphicsEnvironment.isHeadless()) {
094      System.err.println("This is a headless graphics environment - cannot run in full screen");
095      return;
096    }
097    final boolean fullscreenRequested = config.get(SCREENSAVER_FULLSCREEN);
098    final GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
099    final GraphicsDevice[] devices = env.getScreenDevices();
100    boolean fullscreenSupported = true;
101    final Rectangle2D result = new Rectangle2D.Double();
102    for (GraphicsDevice gd : devices) {
103      fullscreenSupported &= gd.isFullScreenSupported();
104      for (GraphicsConfiguration graphicsConfiguration : gd.getConfigurations()) {
105        Rectangle2D.union(result, graphicsConfiguration.getBounds(), result);
106      }
107    }
108
109    if (fullscreenRequested && !fullscreenSupported) System.err.println("Full screen is not supported by the current graphics device");
110    final String title = config.get(SCREENSAVER_TITLE);
111    final JFrame frame = new FocusedJFrame(title);
112    ImageIcon icon = loadImage(config.get(SCREENSAVER_ICON));
113    if (icon == null) icon = loadImage(SCREENSAVER_ICON.getDefaultValue());
114    frame.setIconImage(icon.getImage());
115    if (fullscreenRequested && fullscreenSupported) {
116      frame.setUndecorated(true);
117      frame.setSize((int) result.getWidth(), (int) result.getHeight());
118      frame.setResizable(false);
119      // hide the mouse cursor over the JFrame
120      final BufferedImage cursorImg = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB); // Transparent 16 x 16 pixel cursor image.
121      final Cursor blankCursor = Toolkit.getDefaultToolkit().createCustomCursor(cursorImg, new Point(0, 0), "blank cursor"); // Create a new blank cursor.
122      frame.getContentPane().setCursor(blankCursor); // Set the blank cursor to the JFrame.
123    } else {
124      final Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
125      int w = JPPFConfiguration.get(SCREENSAVER_WIDTH);
126      int h = JPPFConfiguration.get(SCREENSAVER_HEIGHT);
127      if ((w <= 0) || (w > d.width)) w = 1000;
128      if ((h <= 0) || (h > d.height)) h = 800;
129      frame.setSize(w, h);
130    }
131    configureFrameListeners(frame, fullscreenRequested && fullscreenSupported);
132    frame.setBackground(Color.BLACK);
133    frame.getContentPane().setBackground(Color.BLACK);
134    createScreenSaver();
135    frame.add(screensaver.getComponent());
136    screensaver.getComponent().setSize(frame.getSize());
137    final int screenX = config.get(SCREENSAVER_LOCATION_X);
138    final int screenY = config.get(SCREENSAVER_LOCATION_Y);
139    frame.setLocation(screenX, screenY);
140    screensaver.init(config, fullscreenRequested && fullscreenSupported);
141    frame.setVisible(true);
142    java.awt.EventQueue.invokeLater(new Runnable() {
143      @Override public void run() {
144        frame.toFront();
145      }
146    });
147  }
148
149  /**
150   * Create a screen saver object based on the configuration.
151   * @return a {@link JPPFScreenSaver} instance, or <code>null</code> if no screen saver could be created.
152   * @throws Exception if any error occurs.
153   */
154  private JPPFScreenSaver createScreenSaver() throws Exception {
155    try {
156      final String name = JPPFConfiguration.get(SCREENSAVER_CLASS);
157      final Class<?> clazz = Class.forName(name);
158      screensaver = (JPPFScreenSaver) clazz.newInstance();
159    } catch(@SuppressWarnings("unused") final Exception e) {
160      screensaver = new JPPFScreenSaverImpl();
161    }
162    return screensaver;
163  }
164
165  /**
166   * Configure the mouse and keyboard listener for the frame.
167   * @param frame the frame to configure.
168   * @param fullscreen whether the frame is displayed in full screen mode.
169   */
170  private void configureFrameListeners(final JFrame frame, final boolean fullscreen) {
171    if (fullscreen) {
172      frame.addKeyListener(new KeyAdapter() {
173        @Override public void keyPressed(final KeyEvent e) {
174          doOnclose();
175        }
176      });
177      frame.addMouseListener(new MouseAdapter() {
178        @Override public void mousePressed(final MouseEvent e) {
179          doOnclose();
180        }
181      });
182      if (config.get(SCREENSAVER_MOUSE_MOTION_CLOSE)) {
183        final long mouseMotionDelay = config.get(SCREENSAVER_MOUSE_MOTION_DELAY);
184        final long start = System.nanoTime();
185        frame.addMouseMotionListener(new MouseAdapter() {
186          @Override
187          public void mouseMoved(final MouseEvent e) {
188            if (DateTimeUtils.elapsedFrom(start) > mouseMotionDelay) doOnclose();
189          }
190        });
191      }
192    }
193    frame.addWindowListener(new WindowAdapter() {
194      @Override public void windowClosing(final WindowEvent e) {
195        doOnclose();
196      }
197    });
198  }
199
200  /**
201   * Get the screensaver implementation.
202   * @return a {@link JPPFScreenSaver} instance.
203   */
204  JPPFScreenSaver getScreenSaver() {
205    return screensaver;
206  }
207
208  /**
209   * Get the singleton instance of this class.
210   * @return a {@link ScreenSaverMain} instance.
211   */
212  static ScreenSaverMain getInstance() {
213    return instance;
214  }
215
216  /**
217   * Get the JPPF configuration.
218   * @return a {@link TypedProperties} instance.
219   */
220  TypedProperties getConfig() {
221    return config;
222  }
223
224  /**
225   * Action performed when the frame is closed, or upon key pressed, mouse click
226   * or mouse motion events in full screen.
227   */
228  private void doOnclose() {
229    if (screensaver != null) screensaver.destroy();
230    System.exit(0);
231  }
232
233  /**
234   * Load an icon from the specified path.
235   * @param file the file to get the icon from.
236   * @return an <code>ImageIcon</code> instance.
237   */
238  public static ImageIcon loadImage(final String file) {
239    byte[] buf = null;
240    try {
241      buf = FileUtils.getPathAsByte(file);
242    } catch (final Exception e) {
243      System.err.println("Could not load image '" + file + "' : " + ExceptionUtils.getStackTrace(e));
244    }
245    return (buf == null) ? null : new ImageIcon(Toolkit.getDefaultToolkit().createImage(buf));
246  }
247}