1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.quake;
18/*
19 * Copyright (C) 2008 The Android Open Source Project
20 *
21 * Licensed under the Apache License, Version 2.0 (the "License");
22 * you may not use this file except in compliance with the License.
23 * You may obtain a copy of the License at
24 *
25 *      http://www.apache.org/licenses/LICENSE-2.0
26 *
27 * Unless required by applicable law or agreed to in writing, software
28 * distributed under the License is distributed on an "AS IS" BASIS,
29 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
30 * See the License for the specific language governing permissions and
31 * limitations under the License.
32 */
33
34
35import android.content.Context;
36import android.opengl.GLSurfaceView;
37import android.util.AttributeSet;
38import android.util.Log;
39import android.view.KeyEvent;
40import android.view.MotionEvent;
41
42import javax.microedition.khronos.egl.EGL10;
43import javax.microedition.khronos.egl.EGLConfig;
44import javax.microedition.khronos.opengles.GL10;
45/**
46 * An implementation of SurfaceView that uses the dedicated surface for
47 * displaying an OpenGL animation.  This allows the animation to run in a
48 * separate thread, without requiring that it be driven by the update mechanism
49 * of the view hierarchy.
50 *
51 * The application-specific rendering code is delegated to a GLView.Renderer
52 * instance.
53 */
54class QuakeView extends GLSurfaceView {
55    QuakeView(Context context) {
56        super(context);
57        init();
58    }
59
60    public QuakeView(Context context, AttributeSet attrs) {
61        super(context, attrs);
62        init();
63    }
64
65    private void init() {
66        // We want events.
67        setFocusable(true);
68        setFocusableInTouchMode(true);
69        requestFocus();
70    }
71
72    public void setQuakeLib(QuakeLib quakeLib) {
73        mQuakeLib = quakeLib;
74        setRenderer(new QuakeRenderer());
75    }
76
77    @Override
78    public boolean onKeyDown(int keyCode, KeyEvent event) {
79        if (!weWantThisKeyCode(keyCode)) {
80            return super.onKeyDown(keyCode, event);
81        }
82        switch (keyCode) {
83        case KeyEvent.KEYCODE_ALT_RIGHT:
84        case KeyEvent.KEYCODE_ALT_LEFT:
85            mAltKeyPressed = true;
86            break;
87        case KeyEvent.KEYCODE_SHIFT_RIGHT:
88        case KeyEvent.KEYCODE_SHIFT_LEFT:
89            mShiftKeyPressed = true;
90            break;
91        }
92        queueKeyEvent(QuakeLib.KEY_PRESS,
93                keyCodeToQuakeCode(keyCode));
94        return true;
95    }
96
97    @Override
98    public boolean onKeyUp(int keyCode, KeyEvent event) {
99        if (!weWantThisKeyCode(keyCode)) {
100            return super.onKeyUp(keyCode, event);
101        }
102        switch (keyCode) {
103        case KeyEvent.KEYCODE_ALT_RIGHT:
104        case KeyEvent.KEYCODE_ALT_LEFT:
105            mAltKeyPressed = false;
106            break;
107        case KeyEvent.KEYCODE_SHIFT_RIGHT:
108        case KeyEvent.KEYCODE_SHIFT_LEFT:
109            mShiftKeyPressed = false;
110            break;        }
111        queueKeyEvent(QuakeLib.KEY_RELEASE,
112                keyCodeToQuakeCode(keyCode));
113        return true;
114    }
115
116    @Override
117    public boolean onTrackballEvent(MotionEvent event) {
118        if (!mGameMode) {
119            return super.onTrackballEvent(event);
120        }
121        queueTrackballEvent(event);
122        return true;
123    }
124
125    private boolean weWantThisKeyCode(int keyCode) {
126        return (keyCode != KeyEvent.KEYCODE_VOLUME_UP) &&
127            (keyCode != KeyEvent.KEYCODE_VOLUME_DOWN) &&
128            (keyCode != KeyEvent.KEYCODE_SEARCH);
129    }
130
131    @Override
132    public boolean dispatchTouchEvent(MotionEvent ev) {
133        queueMotionEvent(ev);
134        return true;
135    }
136
137    private int keyCodeToQuakeCode(int keyCode) {
138        int key = 0;
139        if (keyCode >= sKeyCodeToQuakeCode.length) {
140            return 0;
141        }
142        if (mAltKeyPressed) {
143            key = sKeyCodeToQuakeCodeAlt[keyCode];
144            if (key == 0) {
145                key = sKeyCodeToQuakeCodeShift[keyCode];
146                if (key == 0) {
147                    key = sKeyCodeToQuakeCode[keyCode];
148                }
149            }
150        } else if (mShiftKeyPressed) {
151            key = sKeyCodeToQuakeCodeShift[keyCode];
152            if (key == 0) {
153                key = sKeyCodeToQuakeCode[keyCode];
154            }
155        } else {
156            key = sKeyCodeToQuakeCode[keyCode];
157        }
158        if (key == 0) {
159            key = '$';
160        }
161        return key;
162    }
163
164    public void queueKeyEvent(final int type, final int keyCode) {
165        queueEvent(
166            new Runnable() {
167                public void run() {
168                    mQuakeLib.event(type, keyCode);
169                }
170            });
171    }
172
173    public void queueMotionEvent(final MotionEvent ev) {
174        queueEvent(
175            new Runnable() {
176                public void run() {
177                    mQuakeLib.motionEvent(ev.getEventTime(),
178                            ev.getAction(),
179                            ev.getX(), ev.getY(),
180                            ev.getPressure(), ev.getSize(),
181                            ev.getDeviceId());
182                }
183            });
184    }
185
186    public void queueTrackballEvent(final MotionEvent ev) {
187        queueEvent(
188            new Runnable() {
189                public void run() {
190                    mQuakeLib.trackballEvent(ev.getEventTime(),
191                            ev.getAction(),
192                            ev.getX(), ev.getY());
193                }
194            });
195    }
196
197    private boolean mShiftKeyPressed;
198    private boolean mAltKeyPressed;
199
200    private static final int[] sKeyCodeToQuakeCode = {
201        '$', QuakeLib.K_ESCAPE, '$', '$',  QuakeLib.K_ESCAPE, '$', '$', '0', //  0.. 7
202        '1', '2', '3', '4',  '5', '6', '7', '8', //  8..15
203        '9', '$', '$', QuakeLib.K_UPARROW,  QuakeLib.K_DOWNARROW, QuakeLib.K_LEFTARROW, QuakeLib.K_RIGHTARROW, QuakeLib.K_ENTER, // 16..23
204        '$', '$', '$', QuakeLib.K_HOME,  '$', 'a', 'b', 'c', // 24..31
205
206        'd', 'e', 'f', 'g',  'h', 'i', 'j', 'k', // 32..39
207        'l', 'm', 'n', 'o',  'p', 'q', 'r', 's', // 40..47
208        't', 'u', 'v', 'w',  'x', 'y', 'z', ',', // 48..55
209        '.', QuakeLib.K_ALT, QuakeLib.K_ALT, QuakeLib.K_SHIFT,  QuakeLib.K_SHIFT, QuakeLib.K_TAB, ' ', '$', // 56..63
210        '$', '$', QuakeLib.K_ENTER, QuakeLib.K_BACKSPACE, '`', '-',  '=', '[', // 64..71
211        ']', '\\', ';', '\'', '/', QuakeLib.K_CTRL,  '#', '$', // 72..79
212        QuakeLib.K_HOME, '$', QuakeLib.K_ESCAPE, '$',  '$', 0, 0, 0, 0,             // 80..
213        0, 0, QuakeLib.K_PGUP, QuakeLib.K_PGDN, 0,  0, 0, 0, 0, 0,             // 90..
214        0, 0, 0, 0, 0,  0, 0, 0, 0, 0,             // 100..
215        0, QuakeLib.K_ESCAPE, QuakeLib.K_DEL, QuakeLib.K_CTRL, QuakeLib.K_CTRL,  0, 0, 0, 0, 0,             // 110..
216        0, 0, QuakeLib.K_HOME, QuakeLib.K_END, QuakeLib.K_INS,  0, 0, 0, 0, 0,             // 120..
217        0, QuakeLib.K_F1, QuakeLib.K_F2, QuakeLib.K_F3, QuakeLib.K_F4,                    // 130..134
218        QuakeLib.K_F5, QuakeLib.K_F6, QuakeLib.K_F7, QuakeLib.K_F8, QuakeLib.K_F9,        // 135..139
219        QuakeLib.K_F10, QuakeLib.K_F11, QuakeLib.K_F12, 0, 0,  0, 0, 0, 0, 0,             // 140..
220        0, 0, 0, 0, 0,  0, 0, 0, 0, 0,             // 150..
221        0, 0, 0, 0, 0,  0, 0, 0, 0, 0,             // 160
222        0, 0, 0, 0, 0,  0, 0, 0, 0, 0,             // 170..
223        0, 0, 0, 0, 0,  0, 0, 0, 0, 0              // 180..
224    };
225
226    private static final int sKeyCodeToQuakeCodeShift[] =
227    {
228        0, 0, 0, 0,  0, 0, 0, ')', //  0.. 7
229        '!', '@', '#', '$',  '%', '^', '&', '*', //  8..15
230        '(', 0, 0, 0,  0, 0, 0, 0, // 16..23
231        0, 0, 0, 0,  0, 0, ']', 0, // 24..31
232
233        '\\', '_', '{', '}',  ':', '-', ';', '"', // 32..39
234        '\'', '>', '<', '+',  '=', 0, 0, '|', // 40..47
235        0, 0, '[', '`',  0, 0, QuakeLib.K_PAUSE, ';', // 48..55
236        0, 0, 0, 0,  0, 0, 0, 0, // 56..63
237        0, 0, 0, 0,  0, 0, 0, 0, // 64..71
238        0, 0, '?', '0',  0, QuakeLib.K_CTRL, 0, 0, // 72..79
239        0, 0, 0, 0, 0,  0, 0, 0, 0, 0,             // 80..
240        0, 0, QuakeLib.K_PGUP, QuakeLib.K_PGDN, 0,  0, 0, 0, 0, 0,             // 90..
241        0, 0, 0, 0, 0,  0, 0, 0, 0, 0,             // 100..
242        0, QuakeLib.K_ESCAPE, QuakeLib.K_DEL, QuakeLib.K_CTRL, QuakeLib.K_CTRL,  0, 0, 0, 0, 0,             // 110..
243        0, 0, QuakeLib.K_HOME, QuakeLib.K_END, QuakeLib.K_INS,  0, 0, 0, 0, 0,             // 120..
244        0, QuakeLib.K_F1, QuakeLib.K_F2, QuakeLib.K_F3, QuakeLib.K_F4,                    // 130..134
245        QuakeLib.K_F5, QuakeLib.K_F6, QuakeLib.K_F7, QuakeLib.K_F8, QuakeLib.K_F9,        // 135..139
246        QuakeLib.K_F10, QuakeLib.K_F11, QuakeLib.K_F12, 0, 0,  0, 0, 0, 0, 0,             // 140..
247        0, 0, 0, 0, 0,  0, 0, 0, 0, 0,             // 150..
248        0, 0, 0, 0, 0,  0, 0, 0, 0, 0,             // 160
249        0, 0, 0, 0, 0,  0, 0, 0, 0, 0,             // 170..
250        0, 0, 0, 0, 0,  0, 0, 0, 0, 0              // 180..
251    };
252
253    private static final int sKeyCodeToQuakeCodeAlt[] =
254    {
255        0, 0, 0, 0,  0, 0, 0, QuakeLib.K_F10, //  0.. 7
256        QuakeLib.K_F1, QuakeLib.K_F2, QuakeLib.K_F3, QuakeLib.K_F4,  QuakeLib.K_F5, QuakeLib.K_F6, QuakeLib.K_F7, QuakeLib.K_F8, //  8..15
257        QuakeLib.K_F9, 0, 0, 0,  0, 0, 0, 0, // 16..23
258        0, 0, 0, 0,  0, 0, 0, 0, // 24..31
259
260        0, 0, 0, 0,  0, 0, 0, 0, // 32..39
261        0, 0, 0, 0,  0, 0, 0, 0, // 40..47
262        QuakeLib.K_F11, 0, 0, 0,  0, QuakeLib.K_F12, 0, 0, // 48..55
263        0, 0, 0, 0,  0, 0, 0, 0, // 56..63
264        0, 0, 0, 0,  0, 0, 0, 0, // 64..71
265        0, 0, 0, 0,  0, 0, 0, 0, // 72..79
266        0, 0, 0, 0,  0, 0, 0, 0, 0,             // 80..
267        0, 0, QuakeLib.K_PGUP, QuakeLib.K_PGDN, 0,  0, 0, 0, 0, 0,             // 90..
268        0, 0, 0, 0, 0,  0, 0, 0, 0, 0,             // 100..
269        0, QuakeLib.K_ESCAPE, QuakeLib.K_DEL, QuakeLib.K_CTRL, QuakeLib.K_CTRL,  0, 0, 0, 0, 0,             // 110..
270        0, 0, QuakeLib.K_HOME, QuakeLib.K_END, QuakeLib.K_INS,  0, 0, 0, 0, 0,             // 120..
271        0, QuakeLib.K_F1, QuakeLib.K_F2, QuakeLib.K_F3, QuakeLib.K_F4,                    // 130..134
272        QuakeLib.K_F5, QuakeLib.K_F6, QuakeLib.K_F7, QuakeLib.K_F8, QuakeLib.K_F9,        // 135..139
273        QuakeLib.K_F10, QuakeLib.K_F11, QuakeLib.K_F12, 0, 0,  0, 0, 0, 0, 0,             // 140..
274        0, 0, 0, 0, 0,  0, 0, 0, 0, 0,             // 150..
275        0, 0, 0, 0, 0,  0, 0, 0, 0, 0,             // 160
276        0, 0, 0, 0, 0,  0, 0, 0, 0, 0,             // 170..
277        0, 0, 0, 0, 0,  0, 0, 0, 0, 0             // 180..
278    };
279
280    private class QuakeRenderer implements GLSurfaceView.Renderer {
281        private static final String TAG = "QuakeRenderer";
282        public void onDrawFrame(GL10 gl) {
283            if (mWidth != 0 &&  mHeight != 0) {
284                mGameMode = mQuakeLib.step(mWidth, mHeight);
285            }
286        }
287
288        public void onSurfaceChanged(GL10 gl, int width, int height) {
289            mWidth = width;
290            mHeight = height;
291            mQuakeLib.init();
292        }
293
294        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
295            // Do nothing.
296        }
297        private int mWidth;
298        private int mHeight;
299    }
300
301    private QuakeLib mQuakeLib;
302    private boolean mGameMode;
303}
304
305