text: /*************************************************************************/
  /*  os_bb10.cpp                                                          */
  /*************************************************************************/
  /*                       This file is part of:                           */
  /*                           GODOT ENGINE                                */
  /*                    http://www.godotengine.org                         */
  /*************************************************************************/
  /* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur.                 */
  /*                                                                       */
  /* Permission is hereby granted, free of charge, to any person obtaining */
  /* a copy of this software and associated documentation files (the       */
  /* "Software"), to deal in the Software without restriction, including   */
  /* without limitation the rights to use, copy, modify, merge, publish,   */
  /* distribute, sublicense, and/or sell copies of the Software, and to    */
  /* permit persons to whom the Software is furnished to do so, subject to */
  /* the following conditions:                                             */
  /*                                                                       */
  /* The above copyright notice and this permission notice shall be        */
  /* included in all copies or substantial portions of the Software.       */
  /*                                                                       */
  /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
  /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
  /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
  /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
  /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
  /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
  /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
  /*************************************************************************/
  #include "os_bb10.h"
  
  #include "drivers/gles2/rasterizer_gles2.h"
  #include "drivers/gles1/rasterizer_gles1.h"
  #include "servers/visual/visual_server_raster.h"
  #include "core/os/dir_access.h"
  
  #include "core/globals.h"
  #include "main/main.h"
  #include "bbutil.h"
  #include <stdlib.h>
  #include <stdbool.h>
  #include <assert.h>
  #include "core/os/keyboard.h"
  
  #include "bps/bps.h"
  #include "bps/screen.h"
  #include "bps/navigator.h"
  #include "bps/accelerometer.h"
  #include "bps/orientation.h"
  #include "bps/virtualkeyboard.h"
  #include "bps/audiodevice.h"
  
  #ifdef BB10_SCORELOOP_ENABLED
  #include "modules/scoreloop/scoreloop_bb10.h"
  #endif
  
  static char launch_dir[512];
  char* launch_dir_ptr;
  
  int OSBB10::get_video_driver_count() const {
  
  	return 1;
  }
  const char * OSBB10::get_video_driver_name(int p_driver) const {
  
  	return "GLES2";
  }
  
  OS::VideoMode OSBB10::get_default_video_mode() const {
  
  	return OS::VideoMode();
  }
  
  int OSBB10::get_audio_driver_count() const {
  
  	return 1;
  }
  const char * OSBB10::get_audio_driver_name(int p_driver) const {
  
  	return "BB10";
  }
  
  void OSBB10::initialize(const VideoMode& p_desired,int p_video_driver,int p_audio_driver) {
  
  	data_dir = getenv("HOME");
  
  	//Create a screen context that will be used to create an EGL surface to to receive libscreen events
  	screen_create_context(&screen_cxt,0);
  
  	//Initialize BPS library
  	bps_initialize();
  
  	//Use utility code to initialize EGL for 2D rendering with GL ES 1.1
  	enum RENDERING_API api = GL_ES_2;
  	#ifdef BB10_LGLES_OVERRIDE
  	api = GL_ES_1;
  	#endif
  	if (EXIT_SUCCESS != bbutil_init(screen_cxt, api)) {
  		bbutil_terminate();
  		screen_destroy_context(screen_cxt);
  		return;
  	};
  
  	EGLint surface_width, surface_height;
  
  	eglQuerySurface(egl_disp, egl_surf, EGL_WIDTH, &surface_width);
  	eglQuerySurface(egl_disp, egl_surf, EGL_HEIGHT, &surface_height);
  	printf("screen size: %ix%i\n", surface_width, surface_height);
  	VideoMode mode;
  	mode.width = surface_width;
  	mode.height = surface_height;
  	mode.fullscreen = true;
  	mode.resizable = false;
  	set_video_mode(mode);
  
  	//Signal BPS library that navigator and screen events will be requested
  	screen_request_events(screen_cxt);
  	navigator_request_events(0);
  	virtualkeyboard_request_events(0);
  	audiodevice_request_events(0);
  
  	#ifdef DEBUG_ENABLED
  	bps_set_verbosity(3);
  	#endif
  
  	accel_supported = accelerometer_is_supported();
  	if (accel_supported)
  		accelerometer_set_update_frequency(FREQ_40_HZ);
  	pitch = 0;
  	roll = 0;
  
  	#ifdef BB10_LGLES_OVERRIDE
  	rasterizer = memnew( RasterizerGLES1(false) );
  	#else
  	rasterizer = memnew( RasterizerGLES2(false, false) );
  	#endif
  
  	visual_server = memnew( VisualServerRaster(rasterizer) );
  	visual_server->init();
  	visual_server->cursor_set_visible(false, 0);
  
  	audio_driver = memnew(AudioDriverBB10);
  	audio_driver->set_singleton();
  	audio_driver->init(NULL);
  
  	sample_manager = memnew( SampleManagerMallocSW );
  	audio_server = memnew( AudioServerSW(sample_manager) );
  	audio_server->set_mixer_params(AudioMixerSW::INTERPOLATION_LINEAR,false);
  	audio_server->init();
  
  	spatial_sound_server = memnew( SpatialSoundServerSW );
  	spatial_sound_server->init();
  
  	spatial_sound_2d_server = memnew( SpatialSound2DServerSW );
  	spatial_sound_2d_server->init();
  
  	//
  	physics_server = memnew( PhysicsServerSW );
  	physics_server->init();
  	physics_2d_server = memnew( Physics2DServerSW );
  	physics_2d_server->init();
  
  	input = memnew( InputDefault );
  
  	#ifdef PAYMENT_SERVICE_ENABLED
  	payment_service = memnew(PaymentService);
  	Globals::get_singleton()->add_singleton(Globals::Singleton("InAppStore", payment_service));
  	#endif
  
  }
  
  void OSBB10::set_main_loop( MainLoop * p_main_loop ) {
  
  	input->set_main_loop(p_main_loop);
  	main_loop=p_main_loop;
  }
  
  void OSBB10::delete_main_loop() {
  
  	memdelete( main_loop );
  	main_loop = NULL;
  }
  
  void OSBB10::finalize() {
  
  	if(main_loop)
  		memdelete(main_loop);
  	main_loop=NULL;
  
  	spatial_sound_server->finish();
  	memdelete(spatial_sound_server);
  	spatial_sound_2d_server->finish();
  	memdelete(spatial_sound_2d_server);
  
  	//if (debugger_connection_console) {
  //		memdelete(debugger_connection_console);
  //}
  
  	memdelete(sample_manager);
  
  	audio_server->finish();
  	memdelete(audio_server);
  
  	visual_server->finish();
  	memdelete(visual_server);
  	memdelete(rasterizer);
  
  	physics_server->finish();
  	memdelete(physics_server);
  
  	physics_2d_server->finish();
  	memdelete(physics_2d_server);
  
  	#ifdef PAYMENT_SERVICE_ENABLED
  	memdelete(payment_service);
  	#endif
  
  	memdelete(input);
  
  	bbutil_terminate();
  	screen_destroy_context(screen_cxt);
  
  	bps_shutdown();
  }
  
  void OSBB10::set_mouse_show(bool p_show) {
  
  	//android has no mouse...
  }
  
  void OSBB10::set_mouse_grab(bool p_grab) {
  
  	//it really has no mouse...!
  }
  
  bool OSBB10::is_mouse_grab_enabled() const {
  
  	//*sigh* technology has evolved so much since i was a kid..
  	return false;
  }
  Point2 OSBB10::get_mouse_pos() const {
  
  	return Point2();
  }
  int OSBB10::get_mouse_button_state() const {
  
  	return 0;
  }
  void OSBB10::set_window_title(const String& p_title) {
  
  
  }
  
  //interesting byt not yet
  //void set_clipboard(const String& p_text);
  //String get_clipboard() const;
  
  void OSBB10::set_video_mode(const VideoMode& p_video_mode,int p_screen) {
  
  	default_videomode = p_video_mode;
  }
  
  OS::VideoMode OSBB10::get_video_mode(int p_screen) const {
  
  	return default_videomode;
  }
  void OSBB10::get_fullscreen_mode_list(List<VideoMode> *p_list,int p_screen) const {
  
  	p_list->push_back(default_videomode);
  }
  
  String OSBB10::get_name() {
  
  	return "BlackBerry 10";
  }
  
  MainLoop *OSBB10::get_main_loop() const {
  
  	return main_loop;
  }
  
  bool OSBB10::can_draw() const {
  
  	return !minimized;
  }
  
  void OSBB10::set_cursor_shape(CursorShape p_shape) {
  
  	//android really really really has no mouse.. how amazing..
  }
  
  void OSBB10::handle_screen_event(bps_event_t *event) {
  
  	screen_event_t screen_event = screen_event_get_event(event);
  
  	int screen_val;
  	screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_TYPE, &screen_val);
  
  	int pos[2];
  
  	switch (screen_val) {
  	case SCREEN_EVENT_MTOUCH_TOUCH:
  	case SCREEN_EVENT_MTOUCH_RELEASE: {
  
  		screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_POSITION, pos);
  
  		InputEvent ievent;
  		ievent.type = InputEvent::SCREEN_TOUCH;
  		ievent.ID = ++last_id;
  		ievent.device = 0;
  		ievent.screen_touch.pressed = (screen_val == SCREEN_EVENT_MTOUCH_TOUCH);
  		ievent.screen_touch.x = pos[0];
  		ievent.screen_touch.y = pos[1];
  		Point2 mpos(ievent.screen_touch.x, ievent.screen_touch.y);
  
  		screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_TOUCH_ID, &pos[0]);
  		ievent.screen_touch.index = pos[0];
  
  		last_touch_x[pos[0]] = ievent.screen_touch.x;
  		last_touch_y[pos[0]] = ievent.screen_touch.y;
  
  		input->parse_input_event( ievent );
  
  		if (ievent.screen_touch.index == 0) {
  
  			InputEvent ievent;
  			ievent.type = InputEvent::MOUSE_BUTTON;
  			ievent.ID = ++last_id;
  			ievent.device = 0;
  			ievent.mouse_button.pressed = (screen_val == SCREEN_EVENT_MTOUCH_TOUCH);
  			ievent.mouse_button.button_index = BUTTON_LEFT;
  			ievent.mouse_button.doubleclick = 0;
  			ievent.mouse_button.x = ievent.mouse_button.global_x = mpos.x;
  			ievent.mouse_button.y = ievent.mouse_button.global_y = mpos.y;
  			input->parse_input_event( ievent );
  		};
  
  
  	} break;
  	case SCREEN_EVENT_MTOUCH_MOVE: {
  
  		screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_POSITION, pos);
  
  		InputEvent ievent;
  		ievent.type = InputEvent::SCREEN_DRAG;
  		ievent.ID = ++last_id;
  		ievent.device = 0;
  		ievent.screen_drag.x = pos[0];
  		ievent.screen_drag.y = pos[1];
  
  		/*
  		screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_SOURCE_POSITION, pos);
  		ievent.screen_drag.relative_x = ievent.screen_drag.x - pos[0];
  		ievent.screen_drag.relative_y = ievent.screen_drag.y - pos[1];
  		*/
  
  		screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_TOUCH_ID, &pos[0]);
  		ievent.screen_drag.index = pos[0];
  
  
  		ievent.screen_drag.relative_x = ievent.screen_drag.x - last_touch_x[ievent.screen_drag.index];
  		ievent.screen_drag.relative_y = ievent.screen_drag.y - last_touch_y[ievent.screen_drag.index];
  
  		last_touch_x[ievent.screen_drag.index] = ievent.screen_drag.x;
  		last_touch_y[ievent.screen_drag.index] = ievent.screen_drag.y;
  
  		Point2 mpos(ievent.screen_drag.x, ievent.screen_drag.y);
  		Point2 mrel(ievent.screen_drag.relative_x, ievent.screen_drag.relative_y);
  
  		input->parse_input_event( ievent );
  
  		if (ievent.screen_touch.index == 0) {
  
  			InputEvent ievent;
  			ievent.type = InputEvent::MOUSE_MOTION;
  			ievent.ID = ++last_id;
  			ievent.device = 0;
  			ievent.mouse_motion.x = ievent.mouse_motion.global_x = mpos.x;
  			ievent.mouse_motion.y = ievent.mouse_motion.global_y = mpos.y;
  			input->set_mouse_pos(Point2(ievent.mouse_motion.x,ievent.mouse_motion.y));
  			ievent.mouse_motion.speed_x=input->get_mouse_speed().x;
  			ievent.mouse_motion.speed_y=input->get_mouse_speed().y;
  			ievent.mouse_motion.relative_x = mrel.x;
  			ievent.mouse_motion.relative_y = mrel.y;
  			ievent.mouse_motion.button_mask = 1; // pressed
  
  			input->parse_input_event( ievent );
  		};
  	} break;
  
  	case SCREEN_EVENT_KEYBOARD: {
  
  		InputEvent ievent;
  		ievent.type = InputEvent::KEY;
  		ievent.ID = ++last_id;
  		ievent.device = 0;
  		int val = 0;
  		screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_KEY_SCAN, &val);
  		ievent.key.scancode = val;
  		screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_KEY_SYM, &val);
  		ievent.key.unicode = val;
  		if (val == 61448) {
  			ievent.key.scancode = KEY_BACKSPACE;
  			ievent.key.unicode = KEY_BACKSPACE;
  		};
  		if (val == 61453) {
  			ievent.key.scancode = KEY_ENTER;
  			ievent.key.unicode = KEY_ENTER;
  		};
  
  		int flags;
  		screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_KEY_FLAGS, &flags);
  		ievent.key.pressed = flags & 1; // bit 1 is pressed apparently
  
  		int mod;
  		screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_KEY_MODIFIERS, &mod);
  
  		input->parse_input_event( ievent );
  	} break;
  
  	default:
  		break;
  
  	}
  };
  
  void OSBB10::handle_accelerometer() {
  
  	if (!accel_supported)
  		return;
  
  	if (!fullscreen)
  		return;
  
  	double force_x, force_y, force_z;
  	accelerometer_read_forces(&force_x, &force_y, &force_z);
  	Vector3 accel = Vector3(force_x, flip_accelerometer ? force_y : -force_y, force_z);
  	input->set_accelerometer(accel);
  	// rotate 90 degrees
  	//input->set_accelerometer(Vector3(force_y, flip_accelerometer?force_x:(-force_x), force_z));
  };
  
  
  void OSBB10::_resize(bps_event_t* event) {
  
  	int angle = navigator_event_get_orientation_angle(event);
  	bbutil_rotate_screen_surface(angle);
  
  	EGLint surface_width, surface_height;
  	eglQuerySurface(egl_disp, egl_surf, EGL_WIDTH, &surface_width);
  	eglQuerySurface(egl_disp, egl_surf, EGL_HEIGHT, &surface_height);
  
  	VideoMode mode;
  	mode.width = surface_width;
  	mode.height = surface_height;
  	mode.fullscreen = true;
  	mode.resizable = false;
  	set_video_mode(mode);
  };
  
  void OSBB10::process_events() {
  
  	handle_accelerometer();
  
  	bps_event_t *event = NULL;
  
  	do {
  		int rc = bps_get_event(&event, 0);
  		assert(rc == BPS_SUCCESS);
  
  		if (!event) break;
  
  		#ifdef BB10_SCORELOOP_ENABLED
  		ScoreloopBB10* sc = Globals::get_singleton()->get_singleton_object("Scoreloop")->cast_to<ScoreloopBB10>();
  		if (sc->handle_event(event))
  			continue;
  		#endif
  
  		#ifdef PAYMENT_SERVICE_ENABLED
  		if (payment_service->handle_event(event))
  			continue;
  		#endif
  
  		int domain = bps_event_get_domain(event);
  		if (domain == screen_get_domain()) {
  
  			handle_screen_event(event);
  
  		} else if (domain == navigator_get_domain()) {
  
  			if (NAVIGATOR_EXIT == bps_event_get_code(event)) {
  				if (main_loop)
  					main_loop->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST);
  				bps_event_destroy(event);
  				exit(0);
  				return;
  			/*
  			} else if (bps_event_get_code(event) == NAVIGATOR_ORIENTATION_CHECK) {
  
  				int angle = navigator_event_get_orientation_angle(event);
  				navigator_orientation_check_response(event, false);
  
  			} else if (bps_event_get_code(event) == NAVIGATOR_ORIENTATION) {
  
  				_resize(event);
  			*/
  			} else if (bps_event_get_code(event) == NAVIGATOR_WINDOW_STATE) {
  
  				int state = navigator_event_get_window_state(event);
  				bool was_fullscreen = fullscreen;
  				minimized = state == NAVIGATOR_WINDOW_INVISIBLE;
  				fullscreen = state == NAVIGATOR_WINDOW_FULLSCREEN;
  				set_low_processor_usage_mode(!fullscreen);
  				if (fullscreen != was_fullscreen) {
  					if (fullscreen) {
  						audio_server->set_fx_global_volume_scale(fullscreen_mixer_volume);
  						audio_server->set_stream_global_volume_scale(fullscreen_stream_volume);
  					} else {
  						fullscreen_mixer_volume = audio_server->get_fx_global_volume_scale();
  						fullscreen_stream_volume = audio_server->get_stream_global_volume_scale();
  						audio_server->set_fx_global_volume_scale(0);
  						audio_server->set_stream_global_volume_scale(0);
  					};
  				};
  			};
  		} else if (domain == audiodevice_get_domain()) {
  
  			const char * audiodevice_path = audiodevice_event_get_path(event);
  			printf("************* got audiodevice event, path %s\n", audiodevice_path);
  			audio_driver->finish();
  			audio_driver->init(audiodevice_path);
  			audio_driver->start();
  		};
  
  		//bps_event_destroy(event);
  	} while (event);
  };
  
  bool OSBB10::has_virtual_keyboard() const {
  
  	return true;
  };
  
  void OSBB10::show_virtual_keyboard(const String& p_existing_text,const Rect2& p_screen_rect) {
  
  	virtualkeyboard_show();
  };
  
  void OSBB10::hide_virtual_keyboard() {
  
  	virtualkeyboard_hide();
  };
  
  void OSBB10::run() {
  
  	if (!main_loop)
  		return;
  
  	main_loop->init();
  
  	int flip = bbutil_is_flipped();
  	int rot = bbutil_get_rotation();
  	flip_accelerometer = rot == 90;
  	printf("**************** rot is %i, flip %i\n", rot, (int)flip_accelerometer);
  	/*
  	orientation_direction_t orientation;
  	int angle;
  	orientation_get(&orientation, &angle);
  	printf("******************** orientation %i, %i, %i\n", orientation, ORIENTATION_BOTTOM_UP, ORIENTATION_TOP_UP);
  	if (orientation == ORIENTATION_BOTTOM_UP) {
  		flip_accelerometer = true;
  	};
  	*/
  
  
  	while (true) {
  
  		process_events(); // get rid of pending events
  		if (Main::iteration()==true)
  			break;
  		bbutil_swap();
  		//#ifdef DEBUG_ENABLED
  		fflush(stdout);
  		//#endif
  	};
  
  	main_loop->finish();
  
  };
  
  bool OSBB10::has_touchscreen_ui_hint() const {
  
  	return true;
  }
  
  Error OSBB10::shell_open(String p_uri) {
  
  	char* msg = NULL;
  	int ret = navigator_invoke(p_uri.utf8().get_data(), &msg);
  
  	return ret == BPS_SUCCESS ? OK : FAILED;
  };
  
  String OSBB10::get_data_dir() const {
  
  	return data_dir;
  };
  
  
  OSBB10::OSBB10() {
  
  	main_loop=NULL;
  	last_id=1;
  	minimized = false;
  	fullscreen = true;
  	flip_accelerometer = true;
  	fullscreen_mixer_volume = 1;
  	fullscreen_stream_volume = 1;
  
  
  	printf("godot bb10!\n");
  	getcwd(launch_dir, sizeof(launch_dir));
  	printf("launch dir %s\n", launch_dir);
  	chdir("app/native");
  	launch_dir_ptr = launch_dir;
  }
  
  OSBB10::~OSBB10() {
  
  
  }
  
  