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