1 /*
   2  * Copyright (c) 2010, 2018, 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 package com.sun.glass.ui;
  26 
  27 import java.lang.annotation.Native;
  28 
  29 /**
  30  * A high-resolution timer.
  31  *
  32  * An application may either override its run() method, or pass a Runnable
  33  * object to the constructor of the Timer class.
  34  * <p>
  35  * The run() method may be invoked on a thread other than the UI thread. If
  36  * a developer wants to process timer events on the UI thread, they can use
  37  * the Application.invokeLater/invokeAndWait() API.
  38  */
  39 public abstract class Timer {
  40 
  41     @Native private final static double UNSET_PERIOD = -1.0; // 0 is valid value, so can't use it here
  42     @Native private final static double SET_PERIOD   = -2.0; // token value for vsync timer
  43 
  44     private final Runnable runnable;
  45     private long ptr;
  46     private double period = UNSET_PERIOD;
  47 
  48     protected abstract long _start(Runnable runnable);
  49     protected abstract long _start(Runnable runnable, int period);
  50     protected abstract void _stop(long timer);
  51     protected abstract void _pause(long timer);
  52     protected abstract void _resume(long timer);
  53 
  54     /**
  55      * Constructs a new timer.
  56      *
  57      * If the application overrides the Timer.run(), it should call super.run()
  58      * in order to run the runnable passed to the constructor.
  59      */
  60     protected Timer(Runnable runnable) {
  61         if (runnable == null) {
  62             throw new IllegalArgumentException("runnable shouldn't be null");
  63         }
  64         this.runnable = runnable;
  65     }
  66 
  67     /**
  68      * Returns the minimum timer period supported by the native system.
  69      */
  70     public static int getMinPeriod() {
  71         return Application.GetApplication().staticTimer_getMinPeriod();
  72     }
  73 
  74     /**
  75      * Returns the maximum timer period supported by the native system.
  76      */
  77     public static int getMaxPeriod() {
  78         return Application.GetApplication().staticTimer_getMaxPeriod();
  79     }
  80 
  81     /**
  82      * Starts the timer.
  83      * The period must be in the range getMinPeriod() .. getMaxPeriod().
  84      * If the timer is currently started, it gets stopped before re-starting.
  85      * If starting the timer fails, the RuntimeException is thrown.
  86      */
  87     public synchronized void start(int period) {
  88         if (period < getMinPeriod() || period > getMaxPeriod()) {
  89             throw new IllegalArgumentException("period is out of range");
  90         }
  91 
  92         if (this.ptr != 0L) {
  93             stop();
  94         }
  95 
  96         this.ptr = _start(this.runnable, period);
  97         if (this.ptr == 0L) {
  98             this.period = UNSET_PERIOD;
  99             throw new RuntimeException("Failed to start the timer");
 100         } else {
 101             this.period = (double)period;
 102         }
 103     }
 104 
 105     /**
 106      * Start a vsync-based timer if the system supports it.
 107      *
 108      * A RuntimeException is thrown if the system does not support
 109      * vsync-based timer or if there was an issue starting the timer.
 110      */
 111     public synchronized void start() {
 112         if (this.ptr != 0L) {
 113             stop();
 114         }
 115 
 116         this.ptr = _start(this.runnable);
 117         if (this.ptr == 0L) {
 118             this.period = UNSET_PERIOD;
 119             throw new RuntimeException("Failed to start the timer");
 120         } else {
 121             this.period = SET_PERIOD;
 122         }
 123     }
 124 
 125     /**
 126      * Stops the timer.  If a vsync-based timer is stopped, all of the
 127      * vsync timers currently running will be stopped.
 128      */
 129     public synchronized void stop() {
 130         if (this.ptr != 0L) {
 131             _stop(this.ptr);
 132             this.ptr = 0L;
 133             this.period = UNSET_PERIOD;
 134         }
 135     }
 136 
 137     /**
 138      * Pauses the timer. See JDK-8189926.
 139      * Currently implemented only for mac platform.
 140      * Timer can be paused or resumed from two different threads.
 141      */
 142     public synchronized void pause() {
 143         if (ptr != 0L) {
 144             _pause(ptr);
 145         }
 146     }
 147 
 148     /**
 149      * Resumes the timer. See JDK-8189926
 150      */
 151     public synchronized void resume() {
 152         if (ptr != 0L) {
 153             _resume(ptr);
 154         }
 155     }
 156 
 157 
 158     /**
 159      * Returns true if the timer is currently running
 160      * (convenience API: might not need it)
 161      */
 162     public synchronized boolean isRunning() {
 163         return (this.period != UNSET_PERIOD);
 164     }
 165 }