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