07ec16cbe2833fc3c6dfcf9ccfefb0d1e434a8c9
[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 NDEBUG
35 #define LOGI(...)
36 #endif
37
38 #ifdef __PLAYBOOK__
39 #include <sys/slog.h>
40 #ifndef LOGI
41 #define LOGI(...) ((void)slogf(1337, _SLOG_INFO, __VA_ARGS__))
42 #endif
43 #define LOGW(...) ((void)slogf(1337, _SLOG_WARNING, __VA_ARGS__))
44 #ifndef SLOG2_FA_SIGNED
45 #define SLOG2_FA_SIGNED(x) (x)
46 #endif
47 #else
48 #include <slog2.h>
49 #ifndef LOGI
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 #ifdef __PLAYBOOK__
357         /* Get the keyboard height before doing anything since we otherwise don't get it until it changes */
358         if(window->State.pWState.keyboardHeight == 0) {
359             virtualkeyboard_get_height(&window->State.pWState.keyboardHeight);
360         }
361 #endif
362         domain = bps_event_get_domain(fgDisplay.pDisplay.event);
363         if (domain == screen_get_domain()) {
364             int eventType;
365             int mod;
366             screen_event_t screenEvent = screen_event_get_event(fgDisplay.pDisplay.event);
367             screen_get_event_property_iv(screenEvent, SCREEN_PROPERTY_TYPE, &eventType);
368             switch (eventType) {
369
370             //Mostly from fg_main_android
371             case SCREEN_EVENT_MTOUCH_TOUCH:
372             case SCREEN_EVENT_MTOUCH_RELEASE:
373             case SCREEN_EVENT_MTOUCH_MOVE:
374             {
375                 mtouch_event_t touchEvent;
376                 screen_get_mtouch_event(screenEvent, &touchEvent, 0);
377 #ifndef __PLAYBOOK__
378                 screen_get_event_property_iv(screenEvent, SCREEN_PROPERTY_KEY_MODIFIERS, &mod);
379 #else
380                 mod = 0;
381 #endif
382
383                 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));
384
385                 /* Remember the current modifiers state so user can query it from their callback */
386                 fgState.Modifiers = fgPlatformGetModifiers(mod);
387
388                 if(touchEvent.contact_id == 0) {
389                     int size[2];
390                     screen_get_window_property_iv(window->Window.Handle, SCREEN_PROPERTY_BUFFER_SIZE, size);
391                     handle_left_mouse(touchEvent.x, touchEvent.y, size[1], eventType, window);
392                 }
393
394                 //Now handle mutlitouch (adapted from fg_main_windows)
395                 if (eventType == SCREEN_EVENT_MTOUCH_TOUCH) {
396                     INVOKE_WCB( *window, MultiEntry,  ( touchEvent.contact_id, GLUT_ENTERED ) );
397                     INVOKE_WCB( *window, MultiButton, ( touchEvent.contact_id, touchEvent.x, touchEvent.y, 0, GLUT_DOWN ) );
398                 } else if (eventType == SCREEN_EVENT_MTOUCH_MOVE) {
399                     INVOKE_WCB( *window, MultiMotion, ( touchEvent.contact_id, touchEvent.x, touchEvent.y ) );
400                     //XXX No motion is performed without contact, thus MultiPassive is never used
401                 } else if (eventType == SCREEN_EVENT_MTOUCH_RELEASE) {
402                     INVOKE_WCB( *window, MultiButton, ( touchEvent.contact_id, touchEvent.x, touchEvent.y, 0, GLUT_UP ) );
403                     INVOKE_WCB( *window, MultiEntry,  ( touchEvent.contact_id, GLUT_LEFT ) );
404                 }
405
406                 fgState.Modifiers = INVALID_MODIFIERS;
407                 break;
408             }
409
410             case SCREEN_EVENT_POINTER:
411             {
412                 //Based off/part taken from GamePlay3d PlatformBlackBerry
413                 static int mouse_pressed = 0;
414                 int buttons;
415                 int position[2];
416                 int wheel;
417                 // A move event will be fired unless a button state changed.
418                 bool move = true;
419                 bool left_move = false;
420                 // This is a mouse move event, it is applicable to a device with a usb mouse or simulator.
421                 screen_get_event_property_iv(screenEvent, SCREEN_PROPERTY_BUTTONS, &buttons);
422                 screen_get_event_property_iv(screenEvent, SCREEN_PROPERTY_SOURCE_POSITION, position);
423 #ifndef __PLAYBOOK__
424                 screen_get_event_property_iv(screenEvent, SCREEN_PROPERTY_MOUSE_WHEEL, &wheel);
425                 screen_get_event_property_iv(screenEvent, SCREEN_PROPERTY_KEY_MODIFIERS, &mod);
426 #else
427                 wheel = mod = 0;
428 #endif
429                 int size[2];
430                 screen_get_window_property_iv(window->Window.Handle, SCREEN_PROPERTY_BUFFER_SIZE, size);
431
432                 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));
433
434                 //XXX Is multitouch be handled in a good way?
435
436                 /* Remember the current modifiers state so user can query it from their callback */
437                 fgState.Modifiers = fgPlatformGetModifiers(mod);
438
439                 // Handle left mouse. Interpret as touch if the left mouse event is not consumed.
440                 if (buttons & SCREEN_LEFT_MOUSE_BUTTON) {
441                     if (mouse_pressed & SCREEN_LEFT_MOUSE_BUTTON) {
442                         left_move = true;
443                     } else {
444                         move = false;
445                         mouse_pressed |= SCREEN_LEFT_MOUSE_BUTTON;
446                         handle_left_mouse(position[0], position[1], size[1], SCREEN_EVENT_MTOUCH_TOUCH, window);
447                     }
448                 } else if (mouse_pressed & SCREEN_LEFT_MOUSE_BUTTON) {
449                     move = false;
450                     mouse_pressed &= ~SCREEN_LEFT_MOUSE_BUTTON;
451                     handle_left_mouse(position[0], position[1], size[1], SCREEN_EVENT_MTOUCH_RELEASE, window);
452                 }
453
454                 // Handle right mouse.
455                 if (buttons & SCREEN_RIGHT_MOUSE_BUTTON) {
456                     if ((mouse_pressed & SCREEN_RIGHT_MOUSE_BUTTON) == 0) {
457                         move = false;
458                         mouse_pressed |= SCREEN_RIGHT_MOUSE_BUTTON;
459                         INVOKE_WCB(*window, Mouse, (GLUT_RIGHT_BUTTON, GLUT_DOWN, position[0], position[1]));
460                     }
461                 } else if (mouse_pressed & SCREEN_RIGHT_MOUSE_BUTTON) {
462                     move = false;
463                     mouse_pressed &= ~SCREEN_RIGHT_MOUSE_BUTTON;
464                     INVOKE_WCB(*window, Mouse, (GLUT_RIGHT_BUTTON, GLUT_UP, position[0], position[1]));
465                 }
466
467                 // Handle middle mouse.
468                 if (buttons & SCREEN_MIDDLE_MOUSE_BUTTON) {
469                     if ((mouse_pressed & SCREEN_MIDDLE_MOUSE_BUTTON) == 0) {
470                         move = false;
471                         mouse_pressed |= SCREEN_MIDDLE_MOUSE_BUTTON;
472                         INVOKE_WCB(*window, Mouse, (GLUT_MIDDLE_BUTTON, GLUT_DOWN, position[0], position[1]));
473                     }
474                 } else if (mouse_pressed & SCREEN_MIDDLE_MOUSE_BUTTON) {
475                     move = false;
476                     mouse_pressed &= ~SCREEN_MIDDLE_MOUSE_BUTTON;
477                     INVOKE_WCB(*window, Mouse, (GLUT_MIDDLE_BUTTON, GLUT_UP, position[0], position[1]));
478                 }
479
480                 // Fire a move event if none of the buttons changed.
481                 if (left_move || move) {
482                     handle_left_mouse(position[0], position[1], size[1], SCREEN_EVENT_MTOUCH_MOVE, window);
483                 }
484
485                 if (wheel) {
486                     /* Very slightly modified from fg_main_mswin.
487                      * Because we don't want MouseWheel to be called every. single. time.
488                      * That the action occurs, we mimic the Windows version with "wheel deltas"
489                      * XXX Do we even want this?
490                      * XXX If we want this, it's possible to get horizontal scroll as well.
491                      * XXX -Vertical scroll=wheel 0, horizontal=wheel 1? */
492                     fgState.MouseWheelTicks -= wheel;
493                     if (abs(fgState.MouseWheelTicks) >= WHEEL_DELTA)
494                     {
495                         int wheel_number = 0;
496                         int direction = (fgState.MouseWheelTicks > 0) ? -1 : 1;
497
498                         if (!FETCH_WCB(*window, MouseWheel) && !FETCH_WCB(*window, Mouse))
499                             break;
500
501                         //XXX fgSetWindow(window);
502
503                         while(abs(fgState.MouseWheelTicks) >= WHEEL_DELTA)
504                         {
505                             if (FETCH_WCB(*window, MouseWheel))
506                                 INVOKE_WCB(*window, MouseWheel, (wheel_number, direction, window->State.MouseX, window->State.MouseY));
507                             else /* No mouse wheel, call the mouse button callback twice */
508                             {
509                                 /*
510                                  * Map wheel zero to button 3 and 4; +1 to 3, -1 to 4
511                                  *  "    "   one                     +1 to 5, -1 to 6, ...
512                                  *
513                                  * XXX The below assumes that you have no more than 3 mouse
514                                  * XXX buttons.  Sorry.
515                                  */
516                                 int button = wheel_number * 2 + 3;
517                                 if (direction < 0)
518                                     ++button;
519                                 INVOKE_WCB(*window, Mouse, (button, GLUT_DOWN, window->State.MouseX, window->State.MouseY));
520                                 INVOKE_WCB(*window, Mouse, (button, GLUT_UP, window->State.MouseX, window->State.MouseY));
521                             }
522
523                             fgState.MouseWheelTicks -= WHEEL_DELTA * direction;
524                         }
525                     }
526                 }
527
528                 fgState.Modifiers = INVALID_MODIFIERS;
529                 break;
530             }
531
532             //Based off fg_main_android
533             case SCREEN_EVENT_KEYBOARD:
534             {
535                 int flags;
536                 int value;
537                 screen_get_event_property_iv(screenEvent, SCREEN_PROPERTY_KEY_FLAGS, &flags);
538                 screen_get_event_property_iv(screenEvent, SCREEN_PROPERTY_KEY_SYM, &value);
539                 screen_get_event_property_iv(screenEvent, SCREEN_PROPERTY_KEY_MODIFIERS, &mod);
540
541                 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));
542
543                 /* Suppress key repeats if desired. Based off fg_main_mswin */
544                 if ((flags & KEY_REPEAT) == 0 || (fgState.KeyRepeat == GLUT_KEY_REPEAT_OFF && fgStructure.CurrentWindow->State.IgnoreKeyRepeat == GL_TRUE)) {
545                     unsigned int keypress = 0;
546                     unsigned char ascii = 0;
547
548                     /* Remember the current modifiers state so user can query it from their callback */
549                     fgState.Modifiers = fgPlatformGetModifiers(mod);
550
551                     /* Process keys */
552                     if ((keypress = key_special(value))) {
553                         if(flags & KEY_DOWN) {
554                             INVOKE_WCB(*window, Special, (keypress, window->State.MouseX, window->State.MouseY));
555                         } else {
556                             INVOKE_WCB(*window, SpecialUp, (keypress, window->State.MouseX, window->State.MouseY));
557                         }
558                     } else if((flags & KEY_SYM_VALID) && (ascii = key_ascii(value))) {
559                         if(flags & KEY_DOWN) {
560                             INVOKE_WCB(*window, Keyboard, (ascii, window->State.MouseX, window->State.MouseY));
561                         } else {
562                             INVOKE_WCB(*window, KeyboardUp, (ascii, window->State.MouseX, window->State.MouseY));
563                         }
564                     } else {
565                         LOGW("fgPlatformProcessSingleEvent: SCREEN_EVENT_KEYBOARD. Unhandled key event");
566                     }
567
568                     fgState.Modifiers = INVALID_MODIFIERS;
569                 }
570                 break;
571             }
572
573             case SCREEN_EVENT_PROPERTY:
574             case SCREEN_EVENT_IDLE:
575                 break;
576
577             default:
578                 LOGW("fgPlatformProcessSingleEvent: unknown screen event: 0x%X", SLOG2_FA_SIGNED(eventType));
579                 break;
580             }
581         } else if (domain == navigator_get_domain()) {
582             unsigned int eventType = bps_event_get_code(fgDisplay.pDisplay.event);
583             switch (eventType) {
584
585             case NAVIGATOR_WINDOW_STATE:
586             {
587                 LOGI("fgPlatformProcessSingleEvent: NAVIGATOR_WINDOW_STATE");
588
589                 /* Covered only happens due to keyboard. When the app is minimized, the keyboard is closed.
590                    When the keyboard is open, and the app is fullscreened, the keyboard is also closed.
591                    If a window is covered and the app is minimized, the state will be set and the keyboard event
592                    will adjust the screen size and change window status. */
593                 navigator_window_state_t state = navigator_event_get_window_state(fgDisplay.pDisplay.event);
594                 if(window->State.pWState.windowCovered == GL_FALSE)
595                 {
596                     switch (state)
597                     {
598                     case NAVIGATOR_WINDOW_FULLSCREEN:
599                         LOGI("fgPlatformProcessSingleEvent: NAVIGATOR_WINDOW_STATE-NAVIGATOR_WINDOW_FULLSCREEN");
600                         window->State.Visible = GL_TRUE;
601                         INVOKE_WCB(*window, WindowStatus, (GLUT_FULLY_RETAINED));
602                         break;
603                     case NAVIGATOR_WINDOW_THUMBNAIL:
604                         LOGI("fgPlatformProcessSingleEvent: NAVIGATOR_WINDOW_STATE-NAVIGATOR_WINDOW_THUMBNAIL");
605                         window->State.Visible = GL_TRUE;
606                         INVOKE_WCB(*window, WindowStatus, (GLUT_PARTIALLY_RETAINED));
607                         break;
608                     case NAVIGATOR_WINDOW_INVISIBLE:
609                         LOGI("fgPlatformProcessSingleEvent: NAVIGATOR_WINDOW_STATE-NAVIGATOR_WINDOW_INVISIBLE");
610                         window->State.Visible = GL_FALSE;
611                         INVOKE_WCB(*window, WindowStatus, (GLUT_HIDDEN));
612                         break;
613                     default:
614                         LOGW("fgPlatformProcessSingleEvent: NAVIGATOR_WINDOW_STATE unknown: 0x%X", SLOG2_FA_SIGNED(state));
615                         break;
616                     }
617                 }
618                 window->State.pWState.windowState = state;
619                 break;
620             }
621
622             case NAVIGATOR_EXIT:
623             {
624                 LOGI("fgPlatformProcessSingleEvent: NAVIGATOR_EXIT");
625
626                 fgPlatformMainLoopPostWork();
627
628                 /* User closed the application for good, let's kill the window */
629                 SFG_Window* window = fgStructure.CurrentWindow;
630                 if (window != NULL) {
631                     fgDestroyWindow(window);
632                 } else {
633                     LOGW("NAVIGATOR_EXIT: No current window");
634                 }
635
636                 //XXX Should this be a bit more "forceful" so that it doesn't continue to loop through events?
637                 break;
638             }
639
640             case NAVIGATOR_SWIPE_DOWN:
641                 /* XXX Open app menu */
642                 break;
643
644             /* Orientation is a bunch of handshakes.
645                - First the app get's asked if it wants to rotate (NAVIGATOR_ORIENTATION_CHECK)
646                - If the app wants to rotate, then it will be told what size it will be after rotate (NAVIGATOR_ORIENTATION_SIZE).
647                - Once the OS confirms that it's ready to rotate, it tells the app to handle rotation (NAVIGATOR_ORIENTATION).
648                - Once rotation is complete, the OS tells the app it's done (NAVIGATOR_ORIENTATION_DONE) */
649             case NAVIGATOR_ORIENTATION_CHECK:
650                 LOGI("fgPlatformProcessSingleEvent: NAVIGATOR_ORIENTATION_CHECK");
651
652                 /* Reset sizes */
653                 window->State.pWState.newWidth = 0;
654                 window->State.pWState.newHeight = 0;
655
656 #ifdef __PLAYBOOK__
657                 /* On rotation, the keyboard is closed. This prevents two resize calls */
658                 window->State.pWState.keyboardOpen = GL_FALSE;
659 #endif
660
661                 /* Notify that we want to rotate */
662                 navigator_orientation_check_response(fgDisplay.pDisplay.event, true);
663                 break;
664
665             case NAVIGATOR_ORIENTATION:
666                 LOGI("fgPlatformProcessSingleEvent: NAVIGATOR_ORIENTATION");
667
668                 /* NAVIGATOR_ORIENTATION occurs before NAVIGATOR_KEYBOARD_POSITION */
669
670                 /* Rotate and resize the window */
671                 fgPlatformRotateWindow(window, navigator_event_get_orientation_angle(fgDisplay.pDisplay.event));
672                 fgPlatformFlushCommands();
673 #ifdef __PLAYBOOK__
674                 /* PlayBook doesn't indicate what the new size will be, so we need to retrieve it from the window itself */
675                 window->State.pWState.newWidth = glutGet(GLUT_WINDOW_WIDTH);
676                 window->State.pWState.newHeight = glutGet(GLUT_WINDOW_HEIGHT);
677 #endif
678                 fghOnReshapeNotify(window, window->State.pWState.newWidth, window->State.pWState.newHeight, GL_FALSE);
679
680                 /* Reset sizes */
681                 window->State.pWState.newWidth = 0;
682                 window->State.pWState.newHeight = 0;
683
684                 /* Done rotating */
685                 navigator_done_orientation(fgDisplay.pDisplay.event);
686                 break;
687
688             case NAVIGATOR_BACK:
689                 LOGI("fgPlatformProcessSingleEvent: NAVIGATOR_BACK");
690                 INVOKE_WCB(*window, Keyboard, (ESCAPE_BUTTON_KEY, window->State.MouseX, window->State.MouseY));
691                 INVOKE_WCB(*window, KeyboardUp, (ESCAPE_BUTTON_KEY, window->State.MouseX, window->State.MouseY));
692                 break;
693
694             case NAVIGATOR_WINDOW_ACTIVE:
695                 LOGI("fgPlatformProcessSingleEvent: NAVIGATOR_WINDOW_ACTIVE");
696                 INVOKE_WCB(*window, AppStatus, (GLUT_APPSTATUS_RESUME));
697                 break;
698
699             case NAVIGATOR_WINDOW_INACTIVE:
700                 LOGI("fgPlatformProcessSingleEvent: NAVIGATOR_WINDOW_INACTIVE");
701                 INVOKE_WCB(*window, AppStatus, (GLUT_APPSTATUS_PAUSE));
702                 break;
703
704             case NAVIGATOR_ORIENTATION_DONE:
705             case NAVIGATOR_ORIENTATION_RESULT:
706                 LOGI("fgPlatformProcessSingleEvent: NAVIGATOR_ORIENTATION_DONE/NAVIGATOR_ORIENTATION_RESULT");
707                 break;
708
709 #ifndef __PLAYBOOK__
710             case NAVIGATOR_KEYBOARD_STATE:
711             {
712                 LOGI("fgPlatformProcessSingleEvent: NAVIGATOR_KEYBOARD_STATE");
713
714                 navigator_keyboard_state_t state = navigator_event_get_keyboard_state(fgDisplay.pDisplay.event);
715                 switch (state)
716                 {
717                 case NAVIGATOR_KEYBOARD_CLOSED:
718                     LOGI("fgPlatformProcessSingleEvent: NAVIGATOR_KEYBOARD_STATE-NAVIGATOR_KEYBOARD_CLOSED");
719                     /* NAVIGATOR_KEYBOARD_POSITION only occurs on open, so on keyboard close we need to reset the keyboard height */
720                     fgPlatformHandleKeyboardHeight(window, 0);
721                     break;
722                 case NAVIGATOR_KEYBOARD_OPENING:
723                 case NAVIGATOR_KEYBOARD_OPENED:
724                 case NAVIGATOR_KEYBOARD_CLOSING:
725                     break;
726                 case NAVIGATOR_KEYBOARD_UNRECOGNIZED:
727                     LOGW("fgPlatformProcessSingleEvent: NAVIGATOR_KEYBOARD_STATE-NAVIGATOR_KEYBOARD_UNRECOGNIZED");
728                     break;
729                 default:
730                     LOGW("fgPlatformProcessSingleEvent: NAVIGATOR_KEYBOARD_STATE unknown: 0x%X", SLOG2_FA_SIGNED(state));
731                     break;
732                 }
733                 break;
734             }
735
736             case NAVIGATOR_KEYBOARD_POSITION:
737             {
738                 /* Occurs only when keyboard has opened or resizes */
739                 LOGI("fgPlatformProcessSingleEvent: NAVIGATOR_KEYBOARD_POSITION");
740
741                 int keyboardOffset = navigator_event_get_keyboard_position(fgDisplay.pDisplay.event);
742                 if(keyboardOffset == BPS_FAILURE) {
743                     LOGW("fgPlatformProcessSingleEvent: NAVIGATOR_KEYBOARD_POSITION: getting keyboard offset failed");
744                 } else {
745                     /* keyboardOffset is the offset from the top of the screen to the top of the keyboard, AKA the size of the uncovered screen
746                        We want the height of the keyboard. So instead of determining the orientation, getting the right display size, and subtracting;
747                        we just get the keyboard height which may be slower but easier to understand and work with */
748                     virtualkeyboard_get_height(&keyboardOffset);
749                     fgPlatformHandleKeyboardHeight(window, keyboardOffset);
750                 }
751                 break;
752             }
753
754             case NAVIGATOR_DEVICE_LOCK_STATE:
755                 break;
756
757             case NAVIGATOR_WINDOW_COVER:
758             case NAVIGATOR_WINDOW_COVER_ENTER:
759             case NAVIGATOR_WINDOW_COVER_EXIT:
760                 /* BlackBerry specific. Let app status and window status take care of everything */
761                 break;
762
763             case NAVIGATOR_APP_STATE:
764                 /* Can do the same as NAVIGATOR_WINDOW_ACTIVE/NAVIGATOR_WINDOW_INACTIVE but
765                    seems like it doesn't work when the app comes to the foreground. Might be a bug */
766                 break;
767
768             case NAVIGATOR_ORIENTATION_SIZE:
769                 LOGI("fgPlatformProcessSingleEvent: NAVIGATOR_ORIENTATION_SIZE");
770
771                 /* Get new window size */
772                 window->State.pWState.newWidth = navigator_event_get_orientation_size_width(fgDisplay.pDisplay.event);
773                 window->State.pWState.newHeight = navigator_event_get_orientation_size_height(fgDisplay.pDisplay.event);
774                 break;
775 #endif
776
777             case 0: //Doesn't exist in header, but shows up when keyboard shows and resizes
778                 break;
779
780             default:
781                 LOGW("fgPlatformProcessSingleEvent: unknown navigator event: 0x%X", SLOG2_FA_SIGNED(eventType));
782                 break;
783             }
784         }
785 #ifdef __PLAYBOOK__
786         /* While this could be used for non-PlayBook, BlackBerry 10 will still get navigator events, so use those. They are a bit more exact. */
787         else if(domain == virtualkeyboard_get_domain()) {
788             unsigned int eventType = bps_event_get_code(fgDisplay.pDisplay.event);
789             switch (eventType) {
790             case VIRTUALKEYBOARD_EVENT_VISIBLE:
791                 LOGI("fgPlatformProcessSingleEvent: VIRTUALKEYBOARD_EVENT_VISIBLE");
792                 if(window->State.pWState.keyboardOpen != GL_TRUE) {
793                     window->State.pWState.keyboardOpen = GL_TRUE;
794                     fgPlatformHandleKeyboardHeight(window, window->State.pWState.keyboardHeight);
795                 }
796                 break;
797
798             case VIRTUALKEYBOARD_EVENT_HIDDEN:
799                 LOGI("fgPlatformProcessSingleEvent: VIRTUALKEYBOARD_EVENT_HIDDEN");
800                 if(window->State.pWState.keyboardOpen != GL_FALSE) {
801                     window->State.pWState.keyboardOpen = GL_FALSE;
802                     fgPlatformHandleKeyboardHeight(window, 0);
803                 }
804                 break;
805
806             case VIRTUALKEYBOARD_EVENT_INFO:
807                 LOGI("fgPlatformProcessSingleEvent: VIRTUALKEYBOARD_EVENT_INFO");
808                 window->State.pWState.keyboardHeight = virtualkeyboard_event_get_height(fgDisplay.pDisplay.event);
809                 if(window->State.pWState.keyboardOpen == GL_TRUE) {
810                     fgPlatformHandleKeyboardHeight(window, window->State.pWState.keyboardHeight);
811                 }
812                 break;
813
814             default:
815                 LOGW("fgPlatformProcessSingleEvent: unknown virtualkeyboard event: 0x%X", eventType);
816                 break;
817             }
818         }
819 #endif
820     } while(bps_get_event(&fgDisplay.pDisplay.event, 1) == BPS_SUCCESS && fgDisplay.pDisplay.event != NULL);
821
822     /* Reset event to reduce chances of triggering something */
823     fgDisplay.pDisplay.event = NULL;
824 }
825
826 void fgPlatformMainLoopPreliminaryWork ( void )
827 {
828     LOGI("fgPlatformMainLoopPreliminaryWork");
829
830     /* Request navigator events */
831     navigator_request_events(0);
832
833     /* Allow rotation */
834     navigator_rotation_lock(false);
835
836 #ifdef __PLAYBOOK__
837     /* Request keyboard events */
838     virtualkeyboard_request_events(0);
839 #endif
840
841     /* Request window events */
842     screen_request_events(fgDisplay.pDisplay.screenContext);
843 }
844
845 void fgPlatformMainLoopPostWork ( void )
846 {
847     LOGI("fgPlatformMainLoopPostWork");
848
849     /* Stop all events */
850     screen_stop_events(fgDisplay.pDisplay.screenContext);
851
852 #ifndef __PLAYBOOK__
853     navigator_stop_events(0);
854 #endif
855 }
856
857 /* deal with work list items */
858 void fgPlatformInitWork(SFG_Window* window)
859 {
860     LOGI("fgPlatformInitWork");
861
862     /* Position callback, always at 0,0 */
863     fghOnPositionNotify(window, 0, 0, GL_TRUE);
864
865     /* Get window size */
866     int size[2];
867     screen_get_window_property_iv(window->Window.Handle, SCREEN_PROPERTY_BUFFER_SIZE, size);
868     fghOnReshapeNotify(window, size[0], size[1], GL_FALSE);
869
870     /* Size gets notified on window creation with size detection in mainloop above
871      * XXX CHECK: does this messages happen too early like on windows,
872      * so client code cannot have registered a callback yet and the message
873      * is thus never received by client?
874      */
875 }
876
877 void fgPlatformPosResZordWork(SFG_Window* window, unsigned int workMask)
878 {
879     if (workMask & GLUT_FULL_SCREEN_WORK)
880         fgPlatformFullScreenToggle( window );
881     if (workMask & GLUT_POSITION_WORK)
882         fgPlatformPositionWindow( window, window->State.DesiredXpos, window->State.DesiredYpos );
883     if (workMask & GLUT_SIZE_WORK)
884         fgPlatformReshapeWindow ( window, window->State.DesiredWidth, window->State.DesiredHeight );
885     if (workMask & GLUT_ZORDER_WORK)
886     {
887         if (window->State.DesiredZOrder < 0)
888             fgPlatformPushWindow( window );
889         else
890             fgPlatformPopWindow( window );
891     }
892 }
893
894 void fgPlatformVisibilityWork(SFG_Window* window)
895 {
896     /* Visibility status of window should get updated in the window message handlers
897      * For now, none of these functions called below do anything, so don't worry
898      * about it
899      */
900     SFG_Window *win = window;
901     switch (window->State.DesiredVisibility)
902     {
903     case DesireHiddenState:
904         fgPlatformHideWindow( window );
905         break;
906     case DesireIconicState:
907         /* Call on top-level window */
908         while (win->Parent)
909             win = win->Parent;
910         fgPlatformIconifyWindow( win );
911         break;
912     case DesireNormalState:
913         fgPlatformShowWindow( window );
914         break;
915     }
916 }