Made glutGet, for window width and height, return width and height determined by...
[freeglut] / src / blackberry / fg_main_blackberry.c
1 /*
2  * fg_main_blackberry.c
3  *
4  * The BlackBerry-specific windows message processing methods.
5  *
6  * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved.
7  * Written by Pawel W. Olszta, <olszta@sourceforge.net>
8  * Copied for Platform code by Evan Felix <karcaw at gmail.com>
9  * Copyright (C) 2012  Sylvain Beucler
10  * Copyright (C) 2013  Vincent Simonetti
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a
13  * copy of this software and associated documentation files (the "Software"),
14  * to deal in the Software without restriction, including without limitation
15  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16  * and/or sell copies of the Software, and to permit persons to whom the
17  * Software is furnished to do so, subject to the following conditions:
18  *
19  * The above copyright notice and this permission notice shall be included
20  * in all copies or substantial portions of the Software.
21  *
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
25  * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
26  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
27  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28  */
29
30 #include <GL/freeglut.h>
31 #include "fg_internal.h"
32 #include "egl/fg_window_egl.h"
33
34 #include <slog2.h>
35 #define LOGI(...) ((void)slog2fa(NULL, 1337, SLOG2_INFO, __VA_ARGS__, SLOG2_FA_END))
36 #define LOGW(...) ((void)slog2fa(NULL, 1337, SLOG2_WARNING, __VA_ARGS__, SLOG2_FA_END))
37 #include <sys/keycodes.h>
38 #include <input/screen_helpers.h>
39 #include <bps/bps.h>
40 #include <bps/event.h>
41 #include <bps/screen.h>
42 #include <bps/navigator.h>
43 #include <bps/virtualkeyboard.h>
44
45 extern void fghOnReshapeNotify(SFG_Window *window, int width, int height, GLboolean forceNotify);
46 extern void fghOnPositionNotify(SFG_Window *window, int x, int y, GLboolean forceNotify);
47 extern void fgPlatformFullScreenToggle( SFG_Window *win );
48 extern void fgPlatformPositionWindow( SFG_Window *window, int x, int y );
49 extern void fgPlatformReshapeWindow ( SFG_Window *window, int width, int height );
50 extern void fgPlatformPushWindow( SFG_Window *window );
51 extern void fgPlatformPopWindow( SFG_Window *window );
52 extern void fgPlatformHideWindow( SFG_Window *window );
53 extern void fgPlatformIconifyWindow( SFG_Window *window );
54 extern void fgPlatformShowWindow( SFG_Window *window );
55 extern void fgPlatformMainLoopPostWork ( void );
56 extern void fgPlatformRotateWindow( SFG_Window *window, int rotation );
57 extern void fgPlatformFlushCommands ( void );
58
59 static struct touchscreen touchscreen;
60
61 #define ESCAPE_BUTTON_KEY 0x001B
62
63 unsigned int key_special(int qnxKeycode)
64 {
65     switch(qnxKeycode) {
66     case KEYCODE_F1:
67         return GLUT_KEY_F1;
68     case KEYCODE_F2:
69         return GLUT_KEY_F2;
70     case KEYCODE_F3:
71         return GLUT_KEY_F3;
72     case KEYCODE_F4:
73         return GLUT_KEY_F4;
74     case KEYCODE_F5:
75         return GLUT_KEY_F5;
76     case KEYCODE_F6:
77         return GLUT_KEY_F6;
78     case KEYCODE_F7:
79         return GLUT_KEY_F7;
80     case KEYCODE_F8:
81         return GLUT_KEY_F8;
82     case KEYCODE_F9:
83         return GLUT_KEY_F9;
84     case KEYCODE_F10:
85         return GLUT_KEY_F10;
86     case KEYCODE_F11:
87         return GLUT_KEY_F11;
88     case KEYCODE_F12:
89         return GLUT_KEY_F12;
90     case KEYCODE_PG_UP:
91         return GLUT_KEY_PAGE_UP;
92     case KEYCODE_PG_DOWN:
93         return GLUT_KEY_PAGE_DOWN;
94     case KEYCODE_HOME:
95         return GLUT_KEY_HOME;
96     case KEYCODE_END:
97         return GLUT_KEY_END;
98     case KEYCODE_INSERT:
99         return GLUT_KEY_INSERT;
100     case KEYCODE_UP:
101     //case KEYCODE_KP_UP:
102         return GLUT_KEY_UP;
103     case KEYCODE_DOWN:
104     //case KEYCODE_KP_DOWN:
105         return GLUT_KEY_DOWN;
106     case KEYCODE_LEFT:
107     //case KEYCODE_KP_LEFT:
108         return GLUT_KEY_LEFT;
109     case KEYCODE_RIGHT:
110     //case KEYCODE_KP_RIGHT:
111         return GLUT_KEY_RIGHT;
112     case KEYCODE_NUM_LOCK:
113         return GLUT_KEY_NUM_LOCK;
114     case KEYCODE_LEFT_ALT:
115         return GLUT_KEY_ALT_L;
116     case KEYCODE_RIGHT_ALT:
117         return GLUT_KEY_ALT_R;
118     case KEYCODE_LEFT_SHIFT:
119         return GLUT_KEY_SHIFT_L;
120     case KEYCODE_RIGHT_SHIFT:
121         return GLUT_KEY_SHIFT_R;
122     case KEYCODE_LEFT_CTRL:
123         return GLUT_KEY_CTRL_L;
124     case KEYCODE_RIGHT_CTRL:
125         return GLUT_KEY_CTRL_R;
126     }
127     return 0;
128 }
129
130 unsigned char key_ascii(int qnxKeycode)
131 {
132     if (qnxKeycode >= KEYCODE_PC_KEYS && qnxKeycode <= UNICODE_PRIVATE_USE_AREA_LAST) {
133         switch (qnxKeycode) {
134         case KEYCODE_BACKSPACE:
135             return 0x0008;
136         case KEYCODE_TAB:
137             return 0x0009;
138         case KEYCODE_KP_ENTER:
139         case KEYCODE_RETURN:
140             return 0x000A;
141         case KEYCODE_ESCAPE:
142             return ESCAPE_BUTTON_KEY;
143         }
144     }
145     return qnxKeycode;
146 }
147
148 //From fg_main_x11
149 fg_time_t fgPlatformSystemTime ( void )
150 {
151 #ifdef CLOCK_MONOTONIC
152     struct timespec now;
153     clock_gettime(CLOCK_MONOTONIC, &now);
154     return now.tv_nsec/1000000 + now.tv_sec*1000;
155 #elif defined(HAVE_GETTIMEOFDAY)
156     struct timeval now;
157     gettimeofday( &now, NULL );
158     return now.tv_usec/1000 + now.tv_sec*1000;
159 #endif
160 }
161
162 /*
163  * Does the magic required to relinquish the CPU until something interesting
164  * happens.
165  */
166 void fgPlatformSleepForEvents( fg_time_t msec )
167 {
168     if(fgStructure.CurrentWindow && fgDisplay.pDisplay.event == NULL && bps_get_event(&fgDisplay.pDisplay.event, (int)msec) != BPS_SUCCESS) {
169         LOGW("BPS couldn't get event");
170     }
171 }
172
173 void handle_left_mouse(int x, int y, int height, int eventType, SFG_Window* window)
174 {
175     bool handled = false;
176     /* Virtual arrows PAD */
177     /* Don't interfere with existing mouse move event */
178     if (!touchscreen.in_mmotion) {
179         struct vpad_state prev_vpad = touchscreen.vpad;
180         touchscreen.vpad.left = touchscreen.vpad.right = touchscreen.vpad.up = touchscreen.vpad.down = false;
181
182         if (eventType == SCREEN_EVENT_MTOUCH_TOUCH || eventType == SCREEN_EVENT_MTOUCH_MOVE) {
183             if ((x > 0 && x < 100) && (y > (height - 100) && y < height))
184             {
185                 touchscreen.vpad.left = true;
186             }
187             if ((x > 200 && x < 300) && (y > (height - 100) && y < height))
188             {
189                 touchscreen.vpad.right = true;
190             }
191             if ((x > 100 && x < 200) && (y > (height - 100) && y < height))
192             {
193                 touchscreen.vpad.down = true;
194             }
195             if ((x > 100 && x < 200) && (y > (height - 200) && y < (height - 100)))
196             {
197                 touchscreen.vpad.up = true;
198             }
199         }
200
201         if (eventType == SCREEN_EVENT_MTOUCH_TOUCH &&
202                 (touchscreen.vpad.left || touchscreen.vpad.right || touchscreen.vpad.down || touchscreen.vpad.up)) {
203             touchscreen.vpad.on = true;
204         }
205         if (eventType == SCREEN_EVENT_MTOUCH_RELEASE) {
206             touchscreen.vpad.on = false;
207         }
208
209         if (prev_vpad.left != touchscreen.vpad.left
210                 || prev_vpad.right != touchscreen.vpad.right
211                 || prev_vpad.up != touchscreen.vpad.up
212                 || prev_vpad.down != touchscreen.vpad.down
213                 || prev_vpad.on != touchscreen.vpad.on) {
214             if (FETCH_WCB(*window, Special)) {
215                 if (prev_vpad.left == false && touchscreen.vpad.left == true) {
216                     INVOKE_WCB(*window, Special, (GLUT_KEY_LEFT, x, y));
217                 }
218                 else if (prev_vpad.right == false && touchscreen.vpad.right == true) {
219                     INVOKE_WCB(*window, Special, (GLUT_KEY_RIGHT, x, y));
220                 }
221                 else if (prev_vpad.up == false && touchscreen.vpad.up == true) {
222                     INVOKE_WCB(*window, Special, (GLUT_KEY_UP, x, y));
223                 }
224                 else if (prev_vpad.down == false && touchscreen.vpad.down == true) {
225                     INVOKE_WCB(*window, Special, (GLUT_KEY_DOWN, x, y));
226                 }
227             }
228             if (FETCH_WCB(*window, SpecialUp)) {
229                 if (prev_vpad.left == true && touchscreen.vpad.left == false) {
230                     INVOKE_WCB(*window, SpecialUp, (GLUT_KEY_LEFT, x, y));
231                 }
232                 if (prev_vpad.right == true && touchscreen.vpad.right == false) {
233                     INVOKE_WCB(*window, SpecialUp, (GLUT_KEY_RIGHT, x, y));
234                 }
235                 if (prev_vpad.up == true && touchscreen.vpad.up == false) {
236                     INVOKE_WCB(*window, SpecialUp, (GLUT_KEY_UP, x, y));
237                 }
238                 if (prev_vpad.down == true && touchscreen.vpad.down == false) {
239                     INVOKE_WCB(*window, SpecialUp, (GLUT_KEY_DOWN, x, y));
240                 }
241             }
242             handled = true;
243         }
244     }
245
246     /* Normal mouse events */
247     if (!handled && !touchscreen.vpad.on) {
248         window->State.MouseX = x;
249         window->State.MouseY = y;
250
251         if(eventType == SCREEN_EVENT_MTOUCH_MOVE) {
252             INVOKE_WCB(*window, Motion, (x, y));
253         } else if(FETCH_WCB(*window, Mouse)) {
254             touchscreen.in_mmotion = eventType == SCREEN_EVENT_MTOUCH_TOUCH;
255             int glutTouchType = eventType == SCREEN_EVENT_MTOUCH_TOUCH ? GLUT_DOWN : GLUT_UP;
256             INVOKE_WCB(*window, Mouse, (GLUT_LEFT_BUTTON, glutTouchType, x, y));
257         }
258     }
259 }
260
261 /*
262  * Determine a GLUT modifier mask based on BlackBerry modifier info.
263  */
264 int fgPlatformGetModifiers (int mod)
265 {
266     return (((mod & KEYMOD_SHIFT) ? GLUT_ACTIVE_SHIFT : 0) |
267             ((mod & KEYMOD_CTRL) ? GLUT_ACTIVE_CTRL : 0) |
268             ((mod & KEYMOD_ALT) ? GLUT_ACTIVE_ALT : 0));
269 }
270
271 void fgPlatformHandleKeyboardHeight(SFG_Window* window, int height)
272 {
273     int size[2];
274     int screenHeight;
275     int nScreenHeight = -1;
276
277     screenHeight = glutGet(GLUT_WINDOW_HEIGHT); //Using this takes rotation into account
278     if(height == 0) {
279         nScreenHeight = screenHeight;
280     }
281     else if(!screen_get_window_property_iv(window->Window.Handle, SCREEN_PROPERTY_POSITION, size)) {
282         /* Calculate the new screen size */ //XXX Make sure to use display size instead of screen size
283         nScreenHeight = ((size[1] + screenHeight) - height) - size[1];
284     }
285
286     if(nScreenHeight != -1) {
287         /* If nScreenHeight is less then zero then window is covered. If nScreenHeight == height, then no change in size. Else, change in size */
288
289         int screenWidth = glutGet(GLUT_WINDOW_WIDTH);
290         if(nScreenHeight < 0) {
291             LOGI("fgPlatformHandleKeyboardHeight: Covered window state");
292             window->State.Visible = GL_FALSE;
293             window->State.pWState.windowCovered = GL_TRUE;
294             INVOKE_WCB(*window, WindowStatus, (GLUT_FULLY_COVERED));
295             fghOnReshapeNotify(window, screenWidth, 0, GL_FALSE);
296         } else {
297             if(window->State.pWState.windowCovered == GL_TRUE) {
298                 LOGI("fgPlatformHandleKeyboardHeight: Resetting window state");
299
300                 /* Reset window status if it was previously covered */
301                 switch(window->State.pWState.windowState) {
302                 case NAVIGATOR_WINDOW_FULLSCREEN:
303                     window->State.Visible = GL_TRUE;
304                     INVOKE_WCB(*window, WindowStatus, (GLUT_FULLY_RETAINED));
305                     break;
306                 case NAVIGATOR_WINDOW_THUMBNAIL:
307                     window->State.Visible = GL_TRUE;
308                     INVOKE_WCB(*window, WindowStatus, (GLUT_PARTIALLY_RETAINED));
309                     break;
310                 case NAVIGATOR_WINDOW_INVISIBLE:
311                     window->State.Visible = GL_FALSE;
312                     INVOKE_WCB(*window, WindowStatus, (GLUT_HIDDEN));
313                     break;
314                 }
315                 window->State.pWState.windowCovered = GL_FALSE;
316             }
317             fghOnReshapeNotify(window, screenWidth, nScreenHeight, GL_FALSE);
318         }
319     }
320 }
321
322 void fgPlatformProcessSingleEvent ( void )
323 {
324     if(fgStructure.CurrentWindow == NULL) {
325         //XXX Is this right? Would this just cause a whole lot of busy looping while we wait for events?
326         LOGW("fgPlatformProcessSingleEvent: Missing current window. Skipping event processing");
327         return;
328     }
329
330     if(fgDisplay.pDisplay.event == NULL)
331         /* Nothing to do */
332         return;
333
334     int domain;
335     do
336     {
337         SFG_Window* window = fgStructure.CurrentWindow;
338         domain = bps_event_get_domain(fgDisplay.pDisplay.event);
339         if (domain == screen_get_domain()) {
340             int eventType;
341             int mod;
342             screen_event_t screenEvent = screen_event_get_event(fgDisplay.pDisplay.event);
343             screen_get_event_property_iv(screenEvent, SCREEN_PROPERTY_TYPE, &eventType);
344             switch (eventType) {
345
346             //Mostly from fg_main_android
347             case SCREEN_EVENT_MTOUCH_TOUCH:
348             case SCREEN_EVENT_MTOUCH_RELEASE:
349             case SCREEN_EVENT_MTOUCH_MOVE:
350             {
351                 mtouch_event_t touchEvent;
352                 screen_get_mtouch_event(screenEvent, &touchEvent, 0);
353                 screen_get_event_property_iv(screenEvent, SCREEN_PROPERTY_KEY_MODIFIERS, &mod);
354
355                 LOGI("fgPlatformProcessSingleEvent: SCREEN_EVENT_MTOUCH_*: Type: 0x%X, X: %d, Y: %d, Contact Id: %d, Mod: 0x%X", SLOG2_FA_SIGNED(eventType), SLOG2_FA_SIGNED(touchEvent.x), SLOG2_FA_SIGNED(touchEvent.y), SLOG2_FA_SIGNED(touchEvent.contact_id), SLOG2_FA_SIGNED(mod));
356
357                 /* Remember the current modifiers state so user can query it from their callback */
358                 fgState.Modifiers = fgPlatformGetModifiers(mod);
359
360                 if(touchEvent.contact_id == 0) {
361                     int size[2];
362                     screen_get_window_property_iv(window->Window.Handle, SCREEN_PROPERTY_BUFFER_SIZE, size);
363                     handle_left_mouse(touchEvent.x, touchEvent.y, size[1], eventType, window);
364                 }
365
366                 //Now handle mutlitouch (adapted from fg_main_windows)
367                 if (eventType == SCREEN_EVENT_MTOUCH_TOUCH) {
368                     INVOKE_WCB( *window, MultiEntry,  ( touchEvent.contact_id, GLUT_ENTERED ) );
369                     INVOKE_WCB( *window, MultiButton, ( touchEvent.contact_id, touchEvent.x, touchEvent.y, 0, GLUT_DOWN ) );
370                 } else if (eventType == SCREEN_EVENT_MTOUCH_MOVE) {
371                     INVOKE_WCB( *window, MultiMotion, ( touchEvent.contact_id, touchEvent.x, touchEvent.y ) );
372                     //XXX No motion is performed without contact, thus MultiPassive is never used
373                 } else if (eventType == SCREEN_EVENT_MTOUCH_RELEASE) {
374                     INVOKE_WCB( *window, MultiButton, ( touchEvent.contact_id, touchEvent.x, touchEvent.y, 0, GLUT_UP ) );
375                     INVOKE_WCB( *window, MultiEntry,  ( touchEvent.contact_id, GLUT_LEFT ) );
376                 }
377
378                 fgState.Modifiers = INVALID_MODIFIERS;
379                 break;
380             }
381
382             case SCREEN_EVENT_POINTER:
383             {
384                 //Based off/part taken from GamePlay3d PlatformBlackBerry
385                 static int mouse_pressed = 0;
386                 int buttons;
387                 int position[2];
388                 int wheel;
389                 // A move event will be fired unless a button state changed.
390                 bool move = true;
391                 bool left_move = false;
392                 // This is a mouse move event, it is applicable to a device with a usb mouse or simulator.
393                 screen_get_event_property_iv(screenEvent, SCREEN_PROPERTY_BUTTONS, &buttons);
394                 screen_get_event_property_iv(screenEvent, SCREEN_PROPERTY_SOURCE_POSITION, position);
395                 screen_get_event_property_iv(screenEvent, SCREEN_PROPERTY_MOUSE_WHEEL, &wheel);
396                 screen_get_event_property_iv(screenEvent, SCREEN_PROPERTY_KEY_MODIFIERS, &mod);
397                 int size[2];
398                 screen_get_window_property_iv(window->Window.Handle, SCREEN_PROPERTY_BUFFER_SIZE, size);
399
400                 LOGI("fgPlatformProcessSingleEvent: SCREEN_EVENT_POINTER: Buttons: 0x%X, X: %d, Y: %d, Wheel: %d, Mod: 0x%X", SLOG2_FA_SIGNED(buttons), SLOG2_FA_SIGNED(position[0]), SLOG2_FA_SIGNED(position[1]), SLOG2_FA_SIGNED(wheel), SLOG2_FA_SIGNED(mod));
401
402                 //XXX Is multitouch be handled in a good way?
403
404                 /* Remember the current modifiers state so user can query it from their callback */
405                 fgState.Modifiers = fgPlatformGetModifiers(mod);
406
407                 // Handle left mouse. Interpret as touch if the left mouse event is not consumed.
408                 if (buttons & SCREEN_LEFT_MOUSE_BUTTON) {
409                     if (mouse_pressed & SCREEN_LEFT_MOUSE_BUTTON) {
410                         left_move = true;
411                     } else {
412                         move = false;
413                         mouse_pressed |= SCREEN_LEFT_MOUSE_BUTTON;
414                         handle_left_mouse(position[0], position[1], size[1], SCREEN_EVENT_MTOUCH_TOUCH, window);
415                     }
416                 } else if (mouse_pressed & SCREEN_LEFT_MOUSE_BUTTON) {
417                     move = false;
418                     mouse_pressed &= ~SCREEN_LEFT_MOUSE_BUTTON;
419                     handle_left_mouse(position[0], position[1], size[1], SCREEN_EVENT_MTOUCH_RELEASE, window);
420                 }
421
422                 // Handle right mouse.
423                 if (buttons & SCREEN_RIGHT_MOUSE_BUTTON) {
424                     if ((mouse_pressed & SCREEN_RIGHT_MOUSE_BUTTON) == 0) {
425                         move = false;
426                         mouse_pressed |= SCREEN_RIGHT_MOUSE_BUTTON;
427                         INVOKE_WCB(*window, Mouse, (GLUT_RIGHT_BUTTON, GLUT_DOWN, position[0], position[1]));
428                     }
429                 } else if (mouse_pressed & SCREEN_RIGHT_MOUSE_BUTTON) {
430                     move = false;
431                     mouse_pressed &= ~SCREEN_RIGHT_MOUSE_BUTTON;
432                     INVOKE_WCB(*window, Mouse, (GLUT_RIGHT_BUTTON, GLUT_UP, position[0], position[1]));
433                 }
434
435                 // Handle middle mouse.
436                 if (buttons & SCREEN_MIDDLE_MOUSE_BUTTON) {
437                     if ((mouse_pressed & SCREEN_MIDDLE_MOUSE_BUTTON) == 0) {
438                         move = false;
439                         mouse_pressed |= SCREEN_MIDDLE_MOUSE_BUTTON;
440                         INVOKE_WCB(*window, Mouse, (GLUT_MIDDLE_BUTTON, GLUT_DOWN, position[0], position[1]));
441                     }
442                 } else if (mouse_pressed & SCREEN_MIDDLE_MOUSE_BUTTON) {
443                     move = false;
444                     mouse_pressed &= ~SCREEN_MIDDLE_MOUSE_BUTTON;
445                     INVOKE_WCB(*window, Mouse, (GLUT_MIDDLE_BUTTON, GLUT_UP, position[0], position[1]));
446                 }
447
448                 // Fire a move event if none of the buttons changed.
449                 if (left_move || move) {
450                     handle_left_mouse(position[0], position[1], size[1], SCREEN_EVENT_MTOUCH_MOVE, window);
451                 }
452
453                 if (wheel) {
454                     /* Very slightly modified from fg_main_mswin.
455                      * Because we don't want MouseWheel to be called every. single. time.
456                      * That the action occurs, we mimic the Windows version with "wheel deltas"
457                      * XXX Do we even want this?
458                      * XXX If we want this, it's possible to get horizontal scroll as well.
459                      * XXX -Vertical scroll=wheel 0, horizontal=wheel 1? */
460                     fgState.MouseWheelTicks -= wheel;
461                     if (abs(fgState.MouseWheelTicks) >= WHEEL_DELTA)
462                     {
463                         int wheel_number = 0;
464                         int direction = (fgState.MouseWheelTicks > 0) ? -1 : 1;
465
466                         if (!FETCH_WCB(*window, MouseWheel) && !FETCH_WCB(*window, Mouse))
467                             break;
468
469                         //XXX fgSetWindow(window);
470
471                         while(abs(fgState.MouseWheelTicks) >= WHEEL_DELTA)
472                         {
473                             if (FETCH_WCB(*window, MouseWheel))
474                                 INVOKE_WCB(*window, MouseWheel, (wheel_number, direction, window->State.MouseX, window->State.MouseY));
475                             else /* No mouse wheel, call the mouse button callback twice */
476                             {
477                                 /*
478                                  * Map wheel zero to button 3 and 4; +1 to 3, -1 to 4
479                                  *  "    "   one                     +1 to 5, -1 to 6, ...
480                                  *
481                                  * XXX The below assumes that you have no more than 3 mouse
482                                  * XXX buttons.  Sorry.
483                                  */
484                                 int button = wheel_number * 2 + 3;
485                                 if (direction < 0)
486                                     ++button;
487                                 INVOKE_WCB(*window, Mouse, (button, GLUT_DOWN, window->State.MouseX, window->State.MouseY));
488                                 INVOKE_WCB(*window, Mouse, (button, GLUT_UP, window->State.MouseX, window->State.MouseY));
489                             }
490
491                             fgState.MouseWheelTicks -= WHEEL_DELTA * direction;
492                         }
493                     }
494                 }
495
496                 fgState.Modifiers = INVALID_MODIFIERS;
497                 break;
498             }
499
500             //Based off fg_main_android
501             case SCREEN_EVENT_KEYBOARD:
502             {
503                 int flags;
504                 int value;
505                 screen_get_event_property_iv(screenEvent, SCREEN_PROPERTY_KEY_FLAGS, &flags);
506                 screen_get_event_property_iv(screenEvent, SCREEN_PROPERTY_KEY_SYM, &value);
507                 screen_get_event_property_iv(screenEvent, SCREEN_PROPERTY_KEY_MODIFIERS, &mod);
508
509                 LOGI("fgPlatformProcessSingleEvent: SCREEN_EVENT_KEYBOARD. Flags: 0x%X, Sym: 0x%X, Mod: 0x%X", SLOG2_FA_SIGNED(flags), SLOG2_FA_SIGNED(value), SLOG2_FA_SIGNED(mod));
510
511                 /* Suppress key repeats if desired. Based off fg_main_mswin */
512                 if ((flags & KEY_REPEAT) == 0 || (fgState.KeyRepeat == GLUT_KEY_REPEAT_OFF && fgStructure.CurrentWindow->State.IgnoreKeyRepeat == GL_TRUE)) {
513                     unsigned int keypress = 0;
514                     unsigned char ascii = 0;
515
516                     /* Remember the current modifiers state so user can query it from their callback */
517                     fgState.Modifiers = fgPlatformGetModifiers(mod);
518
519                     /* Process keys */
520                     if ((keypress = key_special(value))) {
521                         if(flags & KEY_DOWN) {
522                             INVOKE_WCB(*window, Special, (keypress, window->State.MouseX, window->State.MouseY));
523                         } else {
524                             INVOKE_WCB(*window, SpecialUp, (keypress, window->State.MouseX, window->State.MouseY));
525                         }
526                     } else if((flags & KEY_SYM_VALID) && (ascii = key_ascii(value))) {
527                         if(flags & KEY_DOWN) {
528                             INVOKE_WCB(*window, Keyboard, (ascii, window->State.MouseX, window->State.MouseY));
529                         } else {
530                             INVOKE_WCB(*window, KeyboardUp, (ascii, window->State.MouseX, window->State.MouseY));
531                         }
532                     } else {
533                         LOGW("fgPlatformProcessSingleEvent: SCREEN_EVENT_KEYBOARD. Unhandled key event");
534                     }
535
536                     fgState.Modifiers = INVALID_MODIFIERS;
537                 }
538                 break;
539             }
540
541             case SCREEN_EVENT_PROPERTY:
542             case SCREEN_EVENT_IDLE:
543                 break;
544
545             default:
546                 LOGW("fgPlatformProcessSingleEvent: unknown screen event: 0x%X", SLOG2_FA_SIGNED(eventType));
547                 break;
548             }
549         } else if (domain == navigator_get_domain()) {
550             int eventType = bps_event_get_code(fgDisplay.pDisplay.event);
551             switch (eventType) {
552
553             case NAVIGATOR_WINDOW_STATE:
554             {
555                 LOGI("fgPlatformProcessSingleEvent: NAVIGATOR_WINDOW_STATE");
556
557                 /* Covered only happens due to keyboard. When the app is minimized, the keyboard is closed.
558                    When the keyboard is open, and the app is fullscreened, the keyboard is also closed.
559                    If a window is covered and the app is minimized, the state will be set and the keyboard event
560                    will adjust the screen size and change window status. */
561                 navigator_window_state_t state = navigator_event_get_window_state(fgDisplay.pDisplay.event);
562                 if(window->State.pWState.windowCovered == GL_FALSE)
563                 {
564                     switch (state)
565                     {
566                     case NAVIGATOR_WINDOW_FULLSCREEN:
567                         LOGI("fgPlatformProcessSingleEvent: NAVIGATOR_WINDOW_STATE-NAVIGATOR_WINDOW_FULLSCREEN");
568                         window->State.Visible = GL_TRUE;
569                         INVOKE_WCB(*window, WindowStatus, (GLUT_FULLY_RETAINED));
570                         break;
571                     case NAVIGATOR_WINDOW_THUMBNAIL:
572                         LOGI("fgPlatformProcessSingleEvent: NAVIGATOR_WINDOW_STATE-NAVIGATOR_WINDOW_THUMBNAIL");
573                         window->State.Visible = GL_TRUE;
574                         INVOKE_WCB(*window, WindowStatus, (GLUT_PARTIALLY_RETAINED));
575                         break;
576                     case NAVIGATOR_WINDOW_INVISIBLE:
577                         LOGI("fgPlatformProcessSingleEvent: NAVIGATOR_WINDOW_STATE-NAVIGATOR_WINDOW_INVISIBLE");
578                         window->State.Visible = GL_FALSE;
579                         INVOKE_WCB(*window, WindowStatus, (GLUT_HIDDEN));
580                         break;
581                     default:
582                         LOGW("fgPlatformProcessSingleEvent: NAVIGATOR_WINDOW_STATE unknown: 0x%X", SLOG2_FA_SIGNED(state));
583                         break;
584                     }
585                 }
586                 window->State.pWState.windowState = state;
587                 break;
588             }
589
590             case NAVIGATOR_EXIT:
591             {
592                 LOGI("fgPlatformProcessSingleEvent: NAVIGATOR_EXIT");
593
594                 fgPlatformMainLoopPostWork();
595
596                 /* User closed the application for good, let's kill the window */
597                 SFG_Window* window = fgStructure.CurrentWindow;
598                 if (window != NULL) {
599                     fgDestroyWindow(window);
600                 } else {
601                     LOGW("NAVIGATOR_EXIT: No current window");
602                 }
603                 break;
604             }
605
606             case NAVIGATOR_SWIPE_DOWN:
607                 /* XXX Open app menu */
608                 break;
609
610             /* Orientation is a bunch of handshakes.
611                - First the app get's asked if it wants to rotate (NAVIGATOR_ORIENTATION_CHECK)
612                - If the app wants to rotate, then it will be told what size it will be after rotate (NAVIGATOR_ORIENTATION_SIZE).
613                - Once the OS confirms that it's ready to rotate, it tells the app to handle rotation (NAVIGATOR_ORIENTATION).
614                - Once rotation is complete, the OS tells the app it's done (NAVIGATOR_ORIENTATION_DONE) */
615             case NAVIGATOR_ORIENTATION_CHECK:
616                 LOGI("fgPlatformProcessSingleEvent: NAVIGATOR_ORIENTATION_CHECK");
617
618                 /* Reset sizes */
619                 window->State.pWState.newWidth = 0;
620                 window->State.pWState.newHeight = 0;
621
622                 /* Notify that we want to rotate */
623                 navigator_orientation_check_response(fgDisplay.pDisplay.event, true);
624                 break;
625
626             case NAVIGATOR_ORIENTATION:
627                 LOGI("fgPlatformProcessSingleEvent: NAVIGATOR_ORIENTATION");
628
629                 /* NAVIGATOR_ORIENTATION occurs before NAVIGATOR_KEYBOARD_POSITION */
630
631                 /* Rotate and resize the window */
632                 fgPlatformRotateWindow(window, navigator_event_get_orientation_angle(fgDisplay.pDisplay.event));
633                 fgPlatformFlushCommands();
634                 fghOnReshapeNotify(window, window->State.pWState.newWidth, window->State.pWState.newHeight, GL_FALSE);
635
636                 /* Reset sizes */
637                 window->State.pWState.newWidth = 0;
638                 window->State.pWState.newHeight = 0;
639
640                 /* Done rotating */
641                 navigator_done_orientation(fgDisplay.pDisplay.event);
642                 break;
643
644             case NAVIGATOR_BACK:
645                 LOGI("fgPlatformProcessSingleEvent: NAVIGATOR_BACK");
646                 INVOKE_WCB(*window, Keyboard, (ESCAPE_BUTTON_KEY, window->State.MouseX, window->State.MouseY));
647                 INVOKE_WCB(*window, KeyboardUp, (ESCAPE_BUTTON_KEY, window->State.MouseX, window->State.MouseY));
648                 break;
649
650             case NAVIGATOR_WINDOW_ACTIVE:
651                 LOGI("fgPlatformProcessSingleEvent: NAVIGATOR_WINDOW_ACTIVE");
652                 INVOKE_WCB(*window, AppStatus, (GLUT_APPSTATUS_RESUME));
653                 break;
654
655             case NAVIGATOR_WINDOW_INACTIVE:
656                 LOGI("fgPlatformProcessSingleEvent: NAVIGATOR_WINDOW_INACTIVE");
657                 INVOKE_WCB(*window, AppStatus, (GLUT_APPSTATUS_PAUSE));
658                 break;
659
660             case NAVIGATOR_ORIENTATION_DONE:
661             case NAVIGATOR_ORIENTATION_RESULT:
662                 LOGI("fgPlatformProcessSingleEvent: NAVIGATOR_ORIENTATION_DONE/NAVIGATOR_ORIENTATION_RESULT");
663                 break;
664
665             case NAVIGATOR_KEYBOARD_STATE:
666             {
667                 LOGI("fgPlatformProcessSingleEvent: NAVIGATOR_KEYBOARD_STATE");
668
669                 navigator_keyboard_state_t state = navigator_event_get_keyboard_state(fgDisplay.pDisplay.event);
670                 switch (state)
671                 {
672                 case NAVIGATOR_KEYBOARD_CLOSED:
673                     LOGI("fgPlatformProcessSingleEvent: NAVIGATOR_KEYBOARD_STATE-NAVIGATOR_KEYBOARD_CLOSED");
674                     /* NAVIGATOR_KEYBOARD_POSITION only occurs on open, so on keyboard close we need to reset the keyboard height */
675                     fgPlatformHandleKeyboardHeight(window, 0);
676                     break;
677                 case NAVIGATOR_KEYBOARD_OPENING:
678                 case NAVIGATOR_KEYBOARD_OPENED:
679                 case NAVIGATOR_KEYBOARD_CLOSING:
680                     break;
681                 case NAVIGATOR_KEYBOARD_UNRECOGNIZED:
682                     LOGW("fgPlatformProcessSingleEvent: NAVIGATOR_KEYBOARD_STATE-NAVIGATOR_KEYBOARD_UNRECOGNIZED");
683                     break;
684                 default:
685                     LOGW("fgPlatformProcessSingleEvent: NAVIGATOR_KEYBOARD_STATE unknown: 0x%X", SLOG2_FA_SIGNED(state));
686                     break;
687                 }
688                 break;
689             }
690
691             case NAVIGATOR_KEYBOARD_POSITION:
692             {
693                 /* Occurs only when keyboard has opened or resizes */
694                 LOGI("fgPlatformProcessSingleEvent: NAVIGATOR_KEYBOARD_POSITION");
695
696                 int keyboardOffset = navigator_event_get_keyboard_position(fgDisplay.pDisplay.event);
697                 if(keyboardOffset == BPS_FAILURE) {
698                     LOGW("fgPlatformProcessSingleEvent: NAVIGATOR_KEYBOARD_POSITION: getting keyboard offset failed");
699                 } else {
700                     /* keyboardOffset is the offset from the top of the screen to the top of the keyboard, AKA the size of the uncovered screen
701                        We want the height of the keyboard. So instead of determining the orientation, getting the right display size, and subtracting;
702                        we just get the keyboard height which may be slower but easier to understand and work with */
703                     virtualkeyboard_get_height(&keyboardOffset);
704                     fgPlatformHandleKeyboardHeight(window, keyboardOffset);
705                 }
706                 break;
707             }
708
709             case NAVIGATOR_DEVICE_LOCK_STATE:
710                 break;
711
712             case NAVIGATOR_WINDOW_COVER:
713             case NAVIGATOR_WINDOW_COVER_ENTER:
714             case NAVIGATOR_WINDOW_COVER_EXIT:
715                 /* BlackBerry specific. Let app status and window status take care of everything */
716                 break;
717
718             case NAVIGATOR_APP_STATE:
719                 /* Can do the same as NAVIGATOR_WINDOW_ACTIVE/NAVIGATOR_WINDOW_INACTIVE but
720                    seems like it doesn't work when the app comes to the foreground. Might be a bug */
721                 break;
722
723             case NAVIGATOR_ORIENTATION_SIZE:
724                 LOGI("fgPlatformProcessSingleEvent: NAVIGATOR_ORIENTATION_SIZE");
725
726                 /* Get new window size */
727                 window->State.pWState.newWidth = navigator_event_get_orientation_size_width(fgDisplay.pDisplay.event);
728                 window->State.pWState.newHeight = navigator_event_get_orientation_size_height(fgDisplay.pDisplay.event);
729                 break;
730
731             case 0: //Doesn't exist in header, but shows up when keyboard shows and resizes
732                 break;
733
734             default:
735                 LOGW("fgPlatformProcessSingleEvent: unknown navigator event: 0x%X", SLOG2_FA_SIGNED(eventType));
736                 break;
737             }
738         }
739     } while(bps_get_event(&fgDisplay.pDisplay.event, 1) == BPS_SUCCESS && fgDisplay.pDisplay.event != NULL);
740
741     /* Reset event to reduce chances of triggering something */
742     fgDisplay.pDisplay.event = NULL;
743 }
744
745 void fgPlatformMainLoopPreliminaryWork ( void )
746 {
747     LOGI("fgPlatformMainLoopPreliminaryWork");
748
749     /* Request navigator events */
750     navigator_request_events(0);
751
752     /* Allow rotation */
753     navigator_rotation_lock(false);
754
755     /* Request window events */
756     screen_request_events(fgDisplay.pDisplay.screenContext);
757 }
758
759 void fgPlatformMainLoopPostWork ( void )
760 {
761     LOGI("fgPlatformMainLoopPostWork");
762
763     /* Stop all events */
764     screen_stop_events(fgDisplay.pDisplay.screenContext);
765
766     navigator_stop_events(0);
767 }
768
769 /* deal with work list items */
770 void fgPlatformInitWork(SFG_Window* window)
771 {
772     LOGI("fgPlatformInitWork");
773
774     /* Position callback, always at 0,0 */
775     fghOnPositionNotify(window, 0, 0, GL_TRUE);
776
777     /* Get window size */
778     int size[2];
779     screen_get_window_property_iv(window->Window.Handle, SCREEN_PROPERTY_BUFFER_SIZE, size);
780     fghOnReshapeNotify(window, size[0], size[1], GL_FALSE);
781
782     /* Size gets notified on window creation with size detection in mainloop above
783      * XXX CHECK: does this messages happen too early like on windows,
784      * so client code cannot have registered a callback yet and the message
785      * is thus never received by client?
786      */
787 }
788
789 void fgPlatformPosResZordWork(SFG_Window* window, unsigned int workMask)
790 {
791     if (workMask & GLUT_FULL_SCREEN_WORK)
792         fgPlatformFullScreenToggle( window );
793     if (workMask & GLUT_POSITION_WORK)
794         fgPlatformPositionWindow( window, window->State.DesiredXpos, window->State.DesiredYpos );
795     if (workMask & GLUT_SIZE_WORK)
796         fgPlatformReshapeWindow ( window, window->State.DesiredWidth, window->State.DesiredHeight );
797     if (workMask & GLUT_ZORDER_WORK)
798     {
799         if (window->State.DesiredZOrder < 0)
800             fgPlatformPushWindow( window );
801         else
802             fgPlatformPopWindow( window );
803     }
804 }
805
806 void fgPlatformVisibilityWork(SFG_Window* window)
807 {
808     /* Visibility status of window should get updated in the window message handlers
809      * For now, none of these functions called below do anything, so don't worry
810      * about it
811      */
812     SFG_Window *win = window;
813     switch (window->State.DesiredVisibility)
814     {
815     case DesireHiddenState:
816         fgPlatformHideWindow( window );
817         break;
818     case DesireIconicState:
819         /* Call on top-level window */
820         while (win->Parent)
821             win = win->Parent;
822         fgPlatformIconifyWindow( win );
823         break;
824     case DesireNormalState:
825         fgPlatformShowWindow( window );
826         break;
827     }
828 }