package psychWithJava;
/*
* Normal window version of FullScreen.java
*
* corresponds to the Final FullScreen version stripped of
* Full Screen Exclusive Mode specific methods,
* such as the ones related to DisplayMode and BufferStrategy.
* New methods added for passive rendering.
*
*/
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import javax.swing.JPanel;
/**
* NormalWindow
class provides methods to display visual stimuli
* and interact with the observer in a normal window environment.
*
* Unlike FullScreen
, NormalWindow doesn't operate in
* Full Screen Exclusive Mode. However the method signitures are identical
* to those in FullScreen class, which makes it simple to convert a
* FullScreen application into a normal window application. A normal window
* application has several benefits. One benefit is that once an experiment is
* converted into a normal window application it can
* easily be shared with collegues and placed on a web page.
*
* Some methods of FullScreen class do not exist in NormalWindow. * Those methods specific to Full Screen Exclusive Mode are: setNBuffers(), * getNBuffers(), closeScreen(), isFullScreenSupported(), * setDisplayMode(), isDisplayModeAvailable(), reportDisplayMode(), * getDisplayMode(), isDisplayChangeSupported(), reportDisplayModes(), * getDisplayModes(). Also there is no constructor in NormalWindow where one * can specify the screenID. *
* On the other hand there are additional methods specific to NormalWindow class. * Most notably setPassiveRendering() determines whether to use passive * rendering or active rendering. By default a NormalWindow object uses * passive rendering, whereas rendering in FullScreen is always active. In * active rendering the programmer has the complete control of the graphics * interface. In passive rendering the operating system and JVM (Java virtual * machine) may intervene and redraw the client's window when necessary. This is * useful in a normal window application because windows can be minimized * and maximized back, or can get hidden and visible again. In passive rendering * JVM updates the client's window automatically whenever necessary, * for example when it gets visible again after being hidden behind other * windows or minimized. This is useful in normal window but not a concern in * Full Screen Mode. Other specific method is isPassiveRendering(), which * reports whether or not the NormalWindow object uses passive rendering. *
* Whether passive or active rendering, NormalWindow always uses double * buffering. *
* Converting a FullScreen experiment into NormalWindow application. * NormalWindow inherits from JPanel of core Java library. A JPanel object has * to be placed in a lower level container before displayed on screen. A * suitable container is JFrame. The program can create a JFrame and then * place the new NormalWindow object inside it. Also the invokation of * FullScreen specific methods should be eliminated (setNBuffers() etc.). * In the end FullScreen's closeScreen() method should be eliminated * and the JFrame's dispose() method should be invoked. For example: *
// HPWindow extends NormalWindow, instead of FullScreen public class HPWindow extends NormalWindow implements Runnable { static JFrame mainFrame; static final int XO = 100; static final int YO = 100; static final int W = 612; static final int H = 612; public static void main(String[] args) { HPWindow nw = new HPWindow(); // The only addition/change to FullScreen is here: mainFrame = new JFrame(); mainFrame.setBounds(XO,YO,W,H); mainFrame.add(nw); mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); // Next two lines are optional mainFrame.setResizable(false); mainFrame.setTitle("Hello Psychophysicist"); // you must set it visible mainFrame.setVisible(true); // up to here // ... } public void run(){ try { // methods are identical to those in FullScreen displayText("Hello Psychophysicist (Normal Window)"); // .... } finally { // and replace this //fs.closeScreen(); mainFrame.dispose(); } } }*
* * Some demos using the NormalWindow class can be found here. * See also the implementation in greater * detail in * Chapter 11: Applets, normal window applications, * packaging and sharing your work * in * The Guide to psychophysics programming with Java. *
* Matlab and Mathematica development:
*
* If the specified wait time is
* positive: This either (i) method returns the top element in the
* keyTyped queue immediately if there is
* at least one element in the keyTyped event queue or
* (ii) waits up to the specified amount of time for an element
* to become available. If no key is typed
* within the specified amount of time
* it returns null.
*
* If the specified time is zero: Returns the top element in
* the keyTyped event queue or null if queue is empty.
*
* If the specified wait time is negative: This method
* either returns the top element in the queue or if the queue is
* empty it waits indefinetely untill the observer types.
*
* In all cases, the element returned is removed from the event queue.
*
*
* General principles of event handling in FullScreen:
* FullScreen captures the key events in a seperate Thread and stores
* them in Thread safe BlockingQueue objects. Anytime observer
* types, presses or releases a key, that key and the time of the
* event are inserted to the end (tail)
* of the respective queues. When one of the getKeyTyped(), getKeyPressed()
* or getKeyReleased() methods is invoked, the top (head) of the respective
* queue is retrived and removed. Similarly, getWhenKeyTyped(),
* getWhenKeyPressed() and getWhenKeyReleased() methods retrive and remove
* the head in event time queues. flushKeyTyped(), flushKeyPressed() and
* flushKeyReleased() methods clear all queues, including the event time queues.
*
* For more information see
* Chapter 6: Getting observer response of the
* Guide to Psychophysics programming with Java.
*
* @param ms time in milliseconds to wait for a response
*
* @return the key typed
*
* @see #getKeyTyped()
* @see #flushKeyTyped()
* @see #getWhenKeyTyped()
*/
public String getKeyTyped(long ms){
String c = null;
try {
if(ms < 0)
c = keyTyped.take();
else
c = keyTyped.poll(ms, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
return c;
}
/**
* Returns the key typed by the observer.
* Returns the top element in keyTyped
* queue or null if queue is empty. Equivelent to invoking getKeyTyped(0).
*
* The element returned is removed from the event queue. See also
* general principles of event handling in FullScreen
* above.
*
* For more information see
* Chapter 6: Getting observer response of the
* Guide to Psychophysics programming with Java.
*
* @return the key typed
*
* @see #getKeyTyped(long)
* @see #flushKeyTyped()
* @see #getWhenKeyTyped()
*/
public String getKeyTyped(){
return keyTyped.poll();
}
/**
* Returns the time of the key typed event.
* Returns the top element in the whenKeyTyped queue,
* null if queue is empty.
*
* The element returned is removed from the event queue. See also
* general principles of event handling in FullScreen
* above.
*
* For more information see
* Chapter 6: Getting observer response of the
* Guide to Psychophysics programming with Java.
*
* @return the time of key typed event
*
* @see #getKeyTyped(long)
* @see #flushKeyTyped()
*/
public Long getWhenKeyTyped(){
return whenKeyTyped.poll();
}
/**
* Clears both keyTyped and whenKeyTyped queues. See also
* general principles of event handling in FullScreen
* above.
*
* For more information see
* Chapter 6: Getting observer response of the
* Guide to Psychophysics programming with Java.
*
* @see #getKeyTyped(long)
* @see #getWhenKeyTyped()
*/
public void flushKeyTyped(){
keyTyped.clear();
whenKeyTyped.clear();
}
/**
* Returns the key pressed by the observer.
*
* If the specified wait time is
* positive: This method either (i) returns the top element in the
* keyPressed queue immediately if there is
* at least one element in the keyPressed event queue or
* (ii) waits up to the specified amount of time for an element
* to become available. If no key is pressed
* within the specified amount of time
* it returns null.
*
* If the specified time is zero: Returns the top element in
* the keyPressed event queue or null if queue is empty.
*
* If the specified wait time is negative: This method
* either returns the top element in the queue or if the queue is
* empty it waits indefinetely untill the observer presses a key.
*
* In all cases, the element returned is removed from the event queue.
* See also
* general principles of event handling in FullScreen
* above.
*
* For more information see
* Chapter 6: Getting observer response of the
* Guide to Psychophysics programming with Java.
*
* @param ms time in milliseconds to wait for a response
*
* @return numeric code of the key pressed
*
* @see #getKeyPressed()
* @see #flushKeyPressed
* @see #getWhenKeyPressed()
*/
public Integer getKeyPressed(long ms){
Integer c = null;
try {
if(ms < 0)
c = keyPressed.take();
else
c = keyPressed.poll(ms, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
return c;
}
/**
* Returns the key pressed by the observer.
* Returns the top element in keyPressed
* queue or null if queue is empty. Equivelent to invoking getKeyPressed(0).
*
* The element returned is removed from the event queue. See also
* general principles of event handling in FullScreen
* above.
*
* For more information see
* Chapter 6: Getting observer response of the
* Guide to Psychophysics programming with Java.
*
* @return numeric code of the key pressed
*
* @see #getKeyPressed(long)
* @see #flushKeyPressed()
* @see #getWhenKeyPressed()
*/
public Integer getKeyPressed(){
return keyPressed.poll();
}
/**
* Returns the time of the key pressed event.
* Returns the top element in the whenKeyPressed queue,
* null if queue is empty.
*
* The element returned is removed from the event queue. See also
* general principles of event handling in FullScreen
* above.
*
* For more information see
* Chapter 6: Getting observer response of the
* Guide to Psychophysics programming with Java.
*
* @return the time of key pressed event
*
* @see #getKeyPressed(long)
* @see #flushKeyPressed()
*/
public Long getWhenKeyPressed(){
return whenKeyPressed.poll();
}
/**
* Clears both key pressed and when key pressed queues.
* See also
* general principles of event handling in FullScreen
* above.
*
* For more information see
* Chapter 6: Getting observer response of the
* Guide to Psychophysics programming with Java.
*
* @see #getKeyPressed(long)
* @see #getWhenKeyPressed()
*/
public void flushKeyPressed(){
keyPressed.clear();
whenKeyPressed.clear();
}
/**
* Returns the key released by the observer.
*
* If the specified wait time is
* positive: This method either (i) returns the top element in the
* keyReleased queue immediately if there is
* at least one element in the keyReleased event queue or
* (ii) waits up to the specified amount of time for an element
* to become available. If no key is released
* within the specified amount of time
* it returns null.
*
* If the specified time is zero: Returns the top element in
* the keyReleased event queue or null if queue is empty.
*
* If the specified wait time is negative: This method
* either returns the top element in the queue or if the queue is
* empty it waits indefinetely untill the observer releases a key.
*
* In all cases, the element returned is removed from the event queue.
* See also
* general principles of event handling in FullScreen
* above.
*
* For more information see
* Chapter 6: Getting observer response of the
* Guide to Psychophysics programming with Java.
*
* @param ms time in milliseconds to wait for a response
*
* @return numerical code of the key released
*
* @see #getKeyReleased()
* @see #flushKeyReleased
* @see #getWhenKeyReleased()
*/
public Integer getKeyReleased(long ms){
Integer c = null;
try {
if(ms < 0 )
c = keyReleased.take();
else
c = keyReleased.poll(ms, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
return c;
}
/**
* Returns the key released by the observer.
* Returns the top element in keyReleased
* queue or null if queue is empty. Equivelent to invoking getKeyReleased(0).
*
* The element returned is removed from the event queue. See also
* general principles of event handling in FullScreen
* above.
*
* For more information see
* Chapter 6: Getting observer response of the
* Guide to Psychophysics programming with Java.
*
* @return numerical code of the key released
*
* @see #getKeyReleased(long)
* @see #flushKeyReleased()
* @see #getWhenKeyReleased()
*/
public Integer getKeyReleased(){
return keyReleased.poll();
}
/**
* Returns the time of the key released event.
* Returns the top element in the whenKeyReleased queue,
* null if queue is empty.
*
* The element returned is removed from the event queue. See also
* general principles of event handling in FullScreen
* above.
*
* For more information see
* Chapter 6: Getting observer response of the
* Guide to Psychophysics programming with Java.
*
* @return the time of key released event
*
* @see #getKeyReleased(long)
* @see #flushKeyReleased()
*/
public Long getWhenKeyReleased(){
return whenKeyReleased.poll();
}
/**
* Clears both key released and when key released queues.
* See also
* general principles of event handling in FullScreen
* above.
*
* For more information see
* Chapter 6: Getting observer response of the
* Guide to Psychophysics programming with Java.
*
* @see #getKeyReleased(long)
* @see #getWhenKeyReleased()
*/
public void flushKeyReleased(){
keyReleased.clear();
whenKeyReleased.clear();
}
}
* It is possible to create Java objects from within Matlab and Mathematica. You
* can, therefore, create a NormalWindow object (and a JFrame)
* in Matlab or Mathematica and
* invoke its methods. In other words, if you choose, you could use this class
* as a tool for psychophysics programming much like the well known Psychtoolbox
* package.
*
* Instructions on Matlab and Mathematica development can be found here.
*
* @see FullScreen
* @author boyaci
*
*/
public class NormalWindow extends JPanel implements KeyListener{
private static final GraphicsEnvironment gEnvironment = GraphicsEnvironment
.getLocalGraphicsEnvironment();
private static final GraphicsDevice gDevice = gEnvironment
.getDefaultScreenDevice();
private static final GraphicsConfiguration gConfiguration = gDevice
.getDefaultConfiguration();
private BufferedImage screenImage;
private boolean passiveRendering = true;
private final static Color defaultBgColor = Color.BLACK;
private final static Color defaultFgColor = Color.LIGHT_GRAY;
private final Font defaultFont = new Font("SansSerif", Font.BOLD, 26);
private BlockingQueue