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