001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package javax.microedition.lcdui.game;
028:
029: import javax.microedition.lcdui.Graphics;
030: import javax.microedition.lcdui.Canvas;
031: import com.sun.midp.lcdui.GameMap;
032: import com.sun.midp.lcdui.GameCanvasLFImpl;
033: import com.sun.midp.lcdui.GameAccess;
034:
035: /**
036: * The GameCanvas class provides the basis for a game user interface. In
037: * addition to the features inherited from Canvas (commands, input events,
038: * etc.) it also provides game-specific capabilities such as an
039: * off-screen graphics buffer and the ability to query key status.
040: * <p>
041: * A dedicated buffer is created for each GameCanvas instance. Since a
042: * unique buffer is provided for each GameCanvas instance, it is preferable
043: * to re-use a single GameCanvas instance in the interests of minimizing
044: * heap usage. The developer can assume that the contents of this buffer
045: * are modified only by calls to the Graphics object(s) obtained from the
046: * GameCanvas instance; the contents are not modified by external sources
047: * such as other MIDlets or system-level notifications. The buffer is
048: * initially filled with white pixels.
049: * <p>
050: * The buffer's size is set to the maximum dimensions of the GameCanvas.
051: * However, the area that may be flushed is limited by the current
052: * dimensions of the GameCanvas (as influenced by the presence of a Ticker,
053: * Commands, etc.) when the flush is requested. The current dimensions of
054: * the GameCanvas may be obtained by calling
055: * {@link javax.microedition.lcdui.Canvas#getWidth getWidth} and
056: * {@link javax.microedition.lcdui.Canvas#getHeight getHeight}.
057: * <p>
058: * A game may provide its own thread to run the game loop. A typical loop
059: * will check for input, implement the game logic, and then render the updated
060: * user interface. The following code illustrates the structure of a typical
061: * game loop: <code>
062: * <pre>
063: * // Get the Graphics object for the off-screen buffer
064: * Graphics g = getGraphics();
065: *
066: * while (true) {
067: * // Check user input and update positions if necessary
068: * int keyState = getKeyStates();
069: * if ((keyState & LEFT_PRESSED) != 0) {
070: * sprite.move(-1, 0);
071: * }
072: * else if ((keyState & RIGHT_PRESSED) != 0) {
073: * sprite.move(1, 0);
074: * }
075: *
076: * // Clear the background to white
077: * g.setColor(0xFFFFFF);
078: * g.fillRect(0,0,getWidth(), getHeight());
079: *
080: * // Draw the Sprite
081: * sprite.paint(g);
082: *
083: * // Flush the off-screen buffer
084: * flushGraphics();
085: * }
086: * </pre>
087: * </code>
088: * <P>
089: **/
090:
091: public abstract class GameCanvas extends Canvas {
092: /** The look&feel implementation associated with this GameCanvas */
093: private GameCanvasLFImpl gameCanvasLF;
094:
095: /** Implementor of GameAccess interface handed out to external packages */
096: private static GameAccess gameAccess;
097:
098: /**
099: * The bit representing the UP key. This constant has a value of
100: * <code>0x0002</code> (1 << Canvas.UP).
101: */
102: public static final int UP_PRESSED = 1 << Canvas.UP;
103:
104: /**
105: * The bit representing the DOWN key. This constant has a value of
106: * <code>0x0040</code> (1 << Canvas.DOWN).
107: */
108: public static final int DOWN_PRESSED = 1 << Canvas.DOWN;
109:
110: /**
111: * The bit representing the LEFT key. This constant has a value of
112: * <code>0x0004</code> (1 << Canvas.LEFT).
113: */
114: public static final int LEFT_PRESSED = 1 << Canvas.LEFT;
115:
116: /**
117: * The bit representing the RIGHT key. This constant has a value of
118: * <code>0x0020</code> (1 << Canvas.RIGHT).
119: */
120: public static final int RIGHT_PRESSED = 1 << Canvas.RIGHT;
121:
122: /**
123: * The bit representing the FIRE key. This constant has a value of
124: * <code>0x0100</code> (1 << Canvas.FIRE).
125: */
126: public static final int FIRE_PRESSED = 1 << Canvas.FIRE;
127:
128: /**
129: * The bit representing the GAME_A key (may not be supported on all
130: * devices). This constant has a value of
131: * <code>0x0200</code> (1 << Canvas.GAME_A).
132: */
133: public static final int GAME_A_PRESSED = 1 << Canvas.GAME_A;
134:
135: /**
136: * The bit representing the GAME_B key (may not be supported on all
137: * devices). This constant has a value of
138: * <code>0x0400</code> (1 << Canvas.GAME_B).
139: */
140: public static final int GAME_B_PRESSED = 1 << Canvas.GAME_B;
141:
142: /**
143: * The bit representing the GAME_C key (may not be supported on all
144: * devices). This constant has a value of
145: * <code>0x0800</code> (1 << Canvas.GAME_C).
146: */
147: public static final int GAME_C_PRESSED = 1 << Canvas.GAME_C;
148:
149: /**
150: * The bit representing the GAME_D key (may not be supported on all
151: * devices). This constant has a value of
152: * <code>0x1000</code> (1 << Canvas.GAME_D).
153: */
154: public static final int GAME_D_PRESSED = 1 << Canvas.GAME_D;
155:
156: /**
157: * Creates a new instance of a GameCanvas. A new buffer is also created
158: * for the GameCanvas and is initially filled with white pixels.
159: * <p>
160: * If the developer only needs to query key status using the getKeyStates
161: * method, the regular key event mechanism can be suppressed for game keys
162: * while this GameCanvas is shown. If not needed by the application, the
163: * suppression of key events may improve performance by eliminating
164: * unnecessary system calls to keyPressed, keyRepeated and keyReleased
165: * methods.
166: * <p>
167: * If requested, key event suppression for a given GameCanvas is started
168: * when it is shown (i.e. when showNotify is called) and stopped when it
169: * is hidden (i.e. when hideNotify is called). Since the showing and
170: * hiding of screens is serialized with the event queue, this arrangement
171: * ensures that the suppression effects only those key events intended for
172: * the corresponding GameCanvas. Thus, if key events are being generated
173: * while another screen is still shown, those key events will continue to
174: * be queued and dispatched until that screen is hidden and the GameCanvas
175: * has replaced it.
176: * <p>
177: * Note that key events can be suppressed only for the defined game keys
178: * (UP, DOWN, FIRE, etc.); key events are always generated for all other
179: * keys.
180: * <p>
181: * @param suppressKeyEvents <code>true</code> to suppress the regular
182: * key event mechanism for game keys, otherwise <code>false</code>.
183: */
184: protected GameCanvas(boolean suppressKeyEvents) {
185: // Create and offscreen Image object that
186: // acts as the offscreen buffer to which we draw to.
187: // the contents of this buffer are flushed to the display
188: // only when flushGraphics() has been called.
189: super ();
190: setSuppressKeyEvents((Canvas) this , suppressKeyEvents);
191: gameCanvasLF = new GameCanvasLFImpl(this );
192:
193: // Create and hand out game accessor tunnel instance
194: if (gameAccess == null) {
195: gameAccess = new GameAccessImpl();
196: GameMap.registerGameAccess(gameAccess);
197: }
198: }
199:
200: /**
201: * Gets look&feel implementation associated with this GameCanvas
202: * @return GameCanvasLFImpl instance of this GameCanvas
203: */
204: GameCanvasLFImpl getLFImpl() {
205: return gameCanvasLF;
206: }
207:
208: /**
209: * Obtains the Graphics object for rendering a GameCanvas. The returned
210: * Graphics object renders to the off-screen buffer belonging to this
211: * GameCanvas.
212: * <p>
213: * Rendering operations do not appear on the display until flushGraphics()
214: * is called; flushing the buffer does not change its contents (the pixels
215: * are not cleared as a result of the flushing operation).
216: * <p>
217: * A new Graphics object is created and returned each time this method is
218: * called; therefore, the needed Graphics object(s) should be obtained
219: * before the game starts then re-used while the game is running.
220: * For each GameCanvas instance, all of the provided graphics objects will
221: * render to the same off-screen buffer.
222: * <P>
223: * <P>The newly created Graphics object has the following properties:
224: * </P>
225: * <ul>
226: * <LI>the destination is this GameCanvas' buffer;
227: * <LI>the clip region encompasses the entire buffer;
228: * <LI>the current color is black;
229: * <LI>the font is the same as the font returned by
230: * {@link javax.microedition.lcdui.Font#getDefaultFont
231: * Font.getDefaultFont()};
232: * <LI>the stroke style is {@link Graphics#SOLID SOLID}; and
233: * <LI>the origin of the coordinate system is located at the upper-left
234: * corner of the buffer.
235: * </ul>
236: * <p>
237: * @return the Graphics object that renders to this GameCanvas'
238: * off-screen buffer
239: * @see #flushGraphics()
240: * @see #flushGraphics(int, int, int, int)
241: */
242: protected Graphics getGraphics() {
243: return gameCanvasLF.getGraphics();
244: }
245:
246: /**
247: * Gets the states of the physical game keys. Each bit in the returned
248: * integer represents a specific key on the device. A key's bit will be
249: * 1 if the key is currently down or has been pressed at least once since
250: * the last time this method was called. The bit will be 0 if the key
251: * is currently up and has not been pressed at all since the last time
252: * this method was called. This latching behavior ensures that a rapid
253: * key press and release will always be caught by the game loop,
254: * regardless of how slowly the loop runs.
255: * <p>
256: * For example:<code><pre>
257: *
258: * // Get the key state and store it
259: * int keyState = getKeyStates();
260: * if ((keyState & LEFT_KEY) != 0) {
261: * positionX--;
262: * }
263: * else if ((keyState & RIGHT_KEY) != 0) {
264: * positionX++;
265: * }
266: *
267: * </pre></code>
268: * <p>
269: * Calling this method has the side effect of clearing any latched state.
270: * Another call to getKeyStates immediately after a prior call will
271: * therefore report the system's best idea of the current state of the
272: * keys, the latched bits having been cleared by the first call.
273: * <p>
274: * Some devices may not be able to query the keypad hardware directly and
275: * therefore, this method may be implemented by monitoring key press and
276: * release events instead. Thus the state reported by getKeyStates might
277: * lag the actual state of the physical keys since the timeliness
278: * of the key information is be subject to the capabilities of each
279: * device. Also, some devices may be incapable of detecting simultaneous
280: * presses of multiple keys.
281: * <p>
282: * This method returns 0 unless the GameCanvas is currently visible as
283: * reported by {@link javax.microedition.lcdui.Displayable#isShown}.
284: * Upon becoming visible, a GameCanvas will initially indicate that
285: * all keys are unpressed (0); if a key is held down while the GameCanvas
286: * is being shown, the key must be first released and then pressed in
287: * order for the key press to be reported by the GameCanvas.
288: * <p>
289: * @see #UP_PRESSED
290: * @see #DOWN_PRESSED
291: * @see #LEFT_PRESSED
292: * @see #RIGHT_PRESSED
293: * @see #FIRE_PRESSED
294: * @see #GAME_A_PRESSED
295: * @see #GAME_B_PRESSED
296: * @see #GAME_C_PRESSED
297: * @see #GAME_D_PRESSED
298: * @return An integer containing the key state information (one bit per
299: * key), or 0 if the GameCanvas is not currently shown.
300: */
301: public int getKeyStates() {
302: return gameCanvasLF.getKeyStates();
303: }
304:
305: /**
306: * Paints this GameCanvas. By default, this method renders the
307: * the off-screen buffer at (0,0). Rendering of the buffer is
308: * subject to the clip region and origin translation of the Graphics
309: * object.
310: * @param g the Graphics object with which to render the screen.
311: * @throws NullPointerException if <code>g</code> is <code>null</code>
312: */
313: public void paint(Graphics g) {
314: gameCanvasLF.drawBuffer(g);
315: }
316:
317: /**
318: * Flushes the specified region of the off-screen buffer to the display.
319: * The contents of the off-screen buffer are not changed as a result of
320: * the flush operation. This method does not return until the flush has
321: * been completed, so the app may immediately begin to render the next
322: * frame to the same buffer once this method returns.
323: * <p>
324: * If the specified region extends beyond the current bounds of the
325: * GameCanvas, only the intersecting region is flushed. No pixels are
326: * flushed if the specified width or height is less than 1.
327: * <p>
328: * This method does nothing and returns immediately if the GameCanvas is
329: * not currently shown or the flush request cannot be honored because the
330: * system is busy.
331: * <p>
332: * @see #flushGraphics()
333: * @param x the left edge of the region to be flushed
334: * @param y the top edge of the region to be flushed
335: * @param width the width of the region to be flushed
336: * @param height the height of the region to be flushed
337: */
338: public void flushGraphics(int x, int y, int width, int height) {
339: gameCanvasLF.flushGraphics(x, y, width, height);
340: }
341:
342: /**
343: * Flushes the off-screen buffer to the display. The size of the flushed
344: * area is equal to the size of the GameCanvas. The contents
345: * of the off-screen buffer are not changed as a result of the flush
346: * operation. This method does not return until the flush has been
347: * completed, so the app may immediately begin to render the next frame
348: * to the same buffer once this method returns.
349: * <p>
350: * This method does nothing and returns immediately if the GameCanvas is
351: * not currently shown or the flush request cannot be honored because the
352: * system is busy.
353: * <p>
354: * @see #flushGraphics(int,int,int,int)
355: */
356: public void flushGraphics() {
357: gameCanvasLF.flushGraphics();
358: }
359:
360: /**
361: * Set a private field in the <code>Canvas</code> object. We use a
362: * native method to work around the package boundary.
363: * @param c this <code>GameCanvas</code> cast to a <code>Canvas</code>
364: * @param suppressKeyEvents whether or not to suppress key events
365: */
366: private native void setSuppressKeyEvents(Canvas c,
367: boolean suppressKeyEvents);
368: }
|