Browse Source

Added ability to set custom mouse cursors. Not hardware accelerated yet.

Juan Linietsky 10 years ago
parent
commit
82a3304458

+ 1 - 306
core/os/input.cpp

@@ -64,6 +64,7 @@ void Input::_bind_methods() {
 	ObjectTypeDB::bind_method(_MD("warp_mouse_pos","to"),&Input::warp_mouse_pos);
 	ObjectTypeDB::bind_method(_MD("action_press"),&Input::action_press);
 	ObjectTypeDB::bind_method(_MD("action_release"),&Input::action_release);
+	ObjectTypeDB::bind_method(_MD("set_custom_mouse_cursor","image:Texture","hotspot"),&Input::set_custom_mouse_cursor,DEFVAL(Vector2()));
 
 	BIND_CONSTANT( MOUSE_MODE_VISIBLE );
 	BIND_CONSTANT( MOUSE_MODE_HIDDEN );
@@ -104,309 +105,3 @@ Input::Input() {
 
 //////////////////////////////////////////////////////////
 
-
-void InputDefault::SpeedTrack::update(const Vector2& p_delta_p) {
-
-	uint64_t tick = OS::get_singleton()->get_ticks_usec();
-	uint32_t tdiff = tick-last_tick;
-	float delta_t = tdiff / 1000000.0;
-	last_tick=tick;
-
-
-	accum+=p_delta_p;
-	accum_t+=delta_t;
-
-	if (accum_t>max_ref_frame*10)
-		accum_t=max_ref_frame*10;
-
-	while( accum_t>=min_ref_frame ) {
-
-		float slice_t = min_ref_frame / accum_t;
-		Vector2 slice = accum*slice_t;
-		accum=accum-slice;
-		accum_t-=min_ref_frame;
-
-		speed=(slice/min_ref_frame).linear_interpolate(speed,min_ref_frame/max_ref_frame);
-	}
-
-
-
-}
-
-void InputDefault::SpeedTrack::reset() {
-	last_tick = OS::get_singleton()->get_ticks_usec();
-	speed=Vector2();
-	accum_t=0;
-}
-
-InputDefault::SpeedTrack::SpeedTrack() {
-
-	 min_ref_frame=0.1;
-	 max_ref_frame=0.3;
-	 reset();
-}
-
-bool InputDefault::is_key_pressed(int p_scancode) {
-
-	_THREAD_SAFE_METHOD_
-	return keys_pressed.has(p_scancode);
-}
-
-bool InputDefault::is_mouse_button_pressed(int p_button) {
-
-	_THREAD_SAFE_METHOD_
-	return (mouse_button_mask&(1<<p_button))!=0;
-}
-
-
-static int _combine_device(int p_value,int p_device) {
-
-	return p_value|(p_device<<20);
-}
-
-bool InputDefault::is_joy_button_pressed(int p_device, int p_button) {
-
-	_THREAD_SAFE_METHOD_
-	return joy_buttons_pressed.has(_combine_device(p_button,p_device));
-}
-
-bool InputDefault::is_action_pressed(const StringName& p_action) {
-
-	if (custom_action_press.has(p_action))
-		return true; //simpler
-
-	const List<InputEvent> *alist = InputMap::get_singleton()->get_action_list(p_action);
-	if (!alist)
-		return NULL;
-
-
-	for (const List<InputEvent>::Element *E=alist->front();E;E=E->next()) {
-
-
-		int device=E->get().device;
-
-		switch(E->get().type) {
-
-			case InputEvent::KEY: {
-
-				const InputEventKey &iek=E->get().key;
-				if ((keys_pressed.has(iek.scancode)))
-					return true;
-			} break;
-			case InputEvent::MOUSE_BUTTON: {
-
-				const InputEventMouseButton &iemb=E->get().mouse_button;
-				 if(mouse_button_mask&(1<<iemb.button_index))
-					 return true;
-			} break;
-			case InputEvent::JOYSTICK_BUTTON: {
-
-				const InputEventJoystickButton &iejb=E->get().joy_button;
-				int c = _combine_device(iejb.button_index,device);
-				if (joy_buttons_pressed.has(c))
-					return true;
-			} break;
-		}
-	}
-
-	return false;
-}
-
-float InputDefault::get_joy_axis(int p_device,int p_axis) {
-
-	_THREAD_SAFE_METHOD_
-	int c = _combine_device(p_axis,p_device);
-	if (joy_axis.has(c)) {
-		return joy_axis[c];
-	} else {
-		return 0;
-	}
-}
-
-String InputDefault::get_joy_name(int p_idx) {
-
-	_THREAD_SAFE_METHOD_
-	return joy_names[p_idx];
-};
-
-void InputDefault::joy_connection_changed(int p_idx, bool p_connected, String p_name) {
-
-	_THREAD_SAFE_METHOD_
-	joy_names[p_idx] = p_connected ? p_name : "";
-
-	emit_signal("joy_connection_changed", p_idx, p_connected);
-};
-
-Vector3 InputDefault::get_accelerometer() {
-
-	_THREAD_SAFE_METHOD_
-	return accelerometer;
-}
-
-void InputDefault::parse_input_event(const InputEvent& p_event) {
-
-	_THREAD_SAFE_METHOD_
-	switch(p_event.type) {
-
-		case InputEvent::KEY: {
-
-			if (p_event.key.echo)
-				break;
-			if (p_event.key.scancode==0)
-				break;
-
-		//	print_line(p_event);
-
-			if (p_event.key.pressed)
-				keys_pressed.insert(p_event.key.scancode);
-			else
-				keys_pressed.erase(p_event.key.scancode);
-		} break;
-		case InputEvent::MOUSE_BUTTON: {
-
-			if (p_event.mouse_button.doubleclick)
-				break;
-
-			if (p_event.mouse_button.pressed)
-				mouse_button_mask|=(1<<p_event.mouse_button.button_index);
-			else
-				mouse_button_mask&=~(1<<p_event.mouse_button.button_index);
-
-			if (main_loop && emulate_touch && p_event.mouse_button.button_index==1) {
-				InputEventScreenTouch touch_event;
-				touch_event.index=0;
-				touch_event.pressed=p_event.mouse_button.pressed;
-				touch_event.x=p_event.mouse_button.x;
-				touch_event.y=p_event.mouse_button.y;
-				InputEvent ev;
-				ev.type=InputEvent::SCREEN_TOUCH;
-				ev.screen_touch=touch_event;
-				main_loop->input_event(ev);
-			}
-		} break;
-		case InputEvent::MOUSE_MOTION: {
-
-			if (main_loop && emulate_touch && p_event.mouse_motion.button_mask&1) {
-				InputEventScreenDrag drag_event;
-				drag_event.index=0;
-				drag_event.x=p_event.mouse_motion.x;
-				drag_event.y=p_event.mouse_motion.y;
-				drag_event.relative_x=p_event.mouse_motion.relative_x;
-				drag_event.relative_y=p_event.mouse_motion.relative_y;
-				drag_event.speed_x=p_event.mouse_motion.speed_x;
-				drag_event.speed_y=p_event.mouse_motion.speed_y;
-
-				InputEvent ev;
-				ev.type=InputEvent::SCREEN_DRAG;
-				ev.screen_drag=drag_event;
-
-				main_loop->input_event(ev);
-			}
-
-		} break;
-		case InputEvent::JOYSTICK_BUTTON: {
-
-			int c = _combine_device(p_event.joy_button.button_index,p_event.device);
-
-			if (p_event.joy_button.pressed)
-				joy_buttons_pressed.insert(c);
-			else
-				joy_buttons_pressed.erase(c);
-		} break;
-		case InputEvent::JOYSTICK_MOTION: {
-			set_joy_axis(p_event.device, p_event.joy_motion.axis, p_event.joy_motion.axis_value);
-		} break;
-
-	}
-
-	if (main_loop)
-		main_loop->input_event(p_event);
-
-}
-
-void InputDefault::set_joy_axis(int p_device,int p_axis,float p_value) {
-
-	_THREAD_SAFE_METHOD_
-	int c = _combine_device(p_axis,p_device);
-	joy_axis[c]=p_value;
-}
-
-void InputDefault::set_accelerometer(const Vector3& p_accel) {
-
-	_THREAD_SAFE_METHOD_
-
-	accelerometer=p_accel;
-
-}
-
-void InputDefault::set_main_loop(MainLoop *p_main_loop) {
-	main_loop=p_main_loop;
-
-}
-
-void InputDefault::set_mouse_pos(const Point2& p_posf) {
-
-	mouse_speed_track.update(p_posf-mouse_pos);
-	mouse_pos=p_posf;
-}
-
-Point2 InputDefault::get_mouse_pos() const {
-
-	return mouse_pos;
-}
-Point2 InputDefault::get_mouse_speed() const {
-
-	return mouse_speed_track.speed;
-}
-
-int InputDefault::get_mouse_button_mask() const {
-
-	return OS::get_singleton()->get_mouse_button_state();
-}
-
-void InputDefault::warp_mouse_pos(const Vector2& p_to) {
-
-	OS::get_singleton()->warp_mouse_pos(p_to);
-}
-
-
-void InputDefault::iteration(float p_step) {
-
-
-}
-
-void InputDefault::action_press(const StringName& p_action) {
-
-	if (custom_action_press.has(p_action)) {
-
-		custom_action_press[p_action]++;
-	} else {
-		custom_action_press[p_action]=1;
-	}
-}
-
-void InputDefault::action_release(const StringName& p_action){
-
-	ERR_FAIL_COND(!custom_action_press.has(p_action));
-	custom_action_press[p_action]--;
-	if (custom_action_press[p_action]==0) {
-		custom_action_press.erase(p_action);
-	}
-}
-
-void InputDefault::set_emulate_touch(bool p_emulate) {
-
-	emulate_touch=p_emulate;
-}
-
-bool InputDefault::is_emulating_touchscreen() const {
-
-	return emulate_touch;
-}
-
-InputDefault::InputDefault() {
-
-	mouse_button_mask=0;
-	emulate_touch=false;
-	main_loop=NULL;
-}

+ 2 - 71
core/os/input.h

@@ -80,82 +80,13 @@ public:
 
 	virtual bool is_emulating_touchscreen() const=0;
 
+	virtual void set_custom_mouse_cursor(const RES& p_cursor,const Vector2& p_hotspot=Vector2())=0;
+	virtual void set_mouse_in_window(bool p_in_window)=0;
 
 	Input();
 };
 
 VARIANT_ENUM_CAST(Input::MouseMode);
 
-class InputDefault : public Input {
-
-	OBJ_TYPE( InputDefault, Input );
-	_THREAD_SAFE_CLASS_
-
-	int mouse_button_mask;
-	Set<int> keys_pressed;
-	Set<int> joy_buttons_pressed;
-	Map<int,float> joy_axis;
-	Map<StringName,int> custom_action_press;
-	Map<int, String> joy_names;
-	Vector3 accelerometer;
-	Vector2 mouse_pos;
-	MainLoop *main_loop;
-
-	bool emulate_touch;
-
-	struct SpeedTrack {
-
-		uint64_t last_tick;
-		Vector2 speed;
-		Vector2 accum;
-		float accum_t;
-		float min_ref_frame;
-		float max_ref_frame;
-
-		void update(const Vector2& p_delta_p);
-		void reset();
-		SpeedTrack();
-	};
-
-	SpeedTrack mouse_speed_track;
-
-public:
-
-	virtual bool is_key_pressed(int p_scancode);
-	virtual bool is_mouse_button_pressed(int p_button);
-	virtual bool is_joy_button_pressed(int p_device, int p_button);
-	virtual bool is_action_pressed(const StringName& p_action);
-
-	virtual float get_joy_axis(int p_device,int p_axis);
-	String get_joy_name(int p_idx);
-	void joy_connection_changed(int p_idx, bool p_connected, String p_name);
-
-	virtual Vector3 get_accelerometer();
-
-	virtual Point2 get_mouse_pos() const;
-	virtual Point2 get_mouse_speed() const;
-	virtual int get_mouse_button_mask() const;
-
-	virtual void warp_mouse_pos(const Vector2& p_to);
-
-
-	void parse_input_event(const InputEvent& p_event);
-	void set_accelerometer(const Vector3& p_accel);
-	void set_joy_axis(int p_device,int p_axis,float p_value);
-
-	void set_main_loop(MainLoop *main_loop);
-	void set_mouse_pos(const Point2& p_posf);
-
-	void action_press(const StringName& p_action);
-	void action_release(const StringName& p_action);
-
-	void iteration(float p_step);
-
-	void set_emulate_touch(bool p_emulate);
-	virtual bool is_emulating_touchscreen() const;
-
-	InputDefault();
-
-};
 
 #endif // INPUT_H

+ 2 - 1
core/os/main_loop.cpp

@@ -45,7 +45,8 @@ void MainLoop::_bind_methods() {
 	BIND_VMETHOD( MethodInfo("_idle",PropertyInfo(Variant::REAL,"delta")) );
 	BIND_VMETHOD( MethodInfo("_finalize") );
 
-
+	BIND_CONSTANT(NOTIFICATION_WM_MOUSE_ENTER);
+	BIND_CONSTANT(NOTIFICATION_WM_MOUSE_EXIT);
 	BIND_CONSTANT(NOTIFICATION_WM_FOCUS_IN);
 	BIND_CONSTANT(NOTIFICATION_WM_FOCUS_OUT);
 	BIND_CONSTANT(NOTIFICATION_WM_QUIT_REQUEST);

+ 2 - 0
core/os/main_loop.h

@@ -47,6 +47,8 @@ protected:
 public:	
 
 	enum {
+		NOTIFICATION_WM_MOUSE_ENTER = 3,
+		NOTIFICATION_WM_MOUSE_EXIT = 4,
 		NOTIFICATION_WM_FOCUS_IN = 5,
 		NOTIFICATION_WM_FOCUS_OUT = 6,
 		NOTIFICATION_WM_QUIT_REQUEST = 7,

+ 343 - 0
main/input_default.cpp

@@ -0,0 +1,343 @@
+#include "input_default.h"
+#include "servers/visual_server.h"
+#include "os/os.h"
+#include "input_map.h"
+
+void InputDefault::SpeedTrack::update(const Vector2& p_delta_p) {
+
+	uint64_t tick = OS::get_singleton()->get_ticks_usec();
+	uint32_t tdiff = tick-last_tick;
+	float delta_t = tdiff / 1000000.0;
+	last_tick=tick;
+
+
+	accum+=p_delta_p;
+	accum_t+=delta_t;
+
+	if (accum_t>max_ref_frame*10)
+		accum_t=max_ref_frame*10;
+
+	while( accum_t>=min_ref_frame ) {
+
+		float slice_t = min_ref_frame / accum_t;
+		Vector2 slice = accum*slice_t;
+		accum=accum-slice;
+		accum_t-=min_ref_frame;
+
+		speed=(slice/min_ref_frame).linear_interpolate(speed,min_ref_frame/max_ref_frame);
+	}
+
+}
+
+void InputDefault::SpeedTrack::reset() {
+	last_tick = OS::get_singleton()->get_ticks_usec();
+	speed=Vector2();
+	accum_t=0;
+}
+
+InputDefault::SpeedTrack::SpeedTrack() {
+
+	 min_ref_frame=0.1;
+	 max_ref_frame=0.3;
+	 reset();
+}
+
+bool InputDefault::is_key_pressed(int p_scancode) {
+
+	_THREAD_SAFE_METHOD_
+	return keys_pressed.has(p_scancode);
+}
+
+bool InputDefault::is_mouse_button_pressed(int p_button) {
+
+	_THREAD_SAFE_METHOD_
+	return (mouse_button_mask&(1<<p_button))!=0;
+}
+
+
+static int _combine_device(int p_value,int p_device) {
+
+	return p_value|(p_device<<20);
+}
+
+bool InputDefault::is_joy_button_pressed(int p_device, int p_button) {
+
+	_THREAD_SAFE_METHOD_
+	return joy_buttons_pressed.has(_combine_device(p_button,p_device));
+}
+
+bool InputDefault::is_action_pressed(const StringName& p_action) {
+
+	if (custom_action_press.has(p_action))
+		return true; //simpler
+
+	const List<InputEvent> *alist = InputMap::get_singleton()->get_action_list(p_action);
+	if (!alist)
+		return NULL;
+
+
+	for (const List<InputEvent>::Element *E=alist->front();E;E=E->next()) {
+
+
+		int device=E->get().device;
+
+		switch(E->get().type) {
+
+			case InputEvent::KEY: {
+
+				const InputEventKey &iek=E->get().key;
+				if ((keys_pressed.has(iek.scancode)))
+					return true;
+			} break;
+			case InputEvent::MOUSE_BUTTON: {
+
+				const InputEventMouseButton &iemb=E->get().mouse_button;
+				 if(mouse_button_mask&(1<<iemb.button_index))
+					 return true;
+			} break;
+			case InputEvent::JOYSTICK_BUTTON: {
+
+				const InputEventJoystickButton &iejb=E->get().joy_button;
+				int c = _combine_device(iejb.button_index,device);
+				if (joy_buttons_pressed.has(c))
+					return true;
+			} break;
+		}
+	}
+
+	return false;
+}
+
+float InputDefault::get_joy_axis(int p_device,int p_axis) {
+
+	_THREAD_SAFE_METHOD_
+	int c = _combine_device(p_axis,p_device);
+	if (joy_axis.has(c)) {
+		return joy_axis[c];
+	} else {
+		return 0;
+	}
+}
+
+String InputDefault::get_joy_name(int p_idx) {
+
+	_THREAD_SAFE_METHOD_
+	return joy_names[p_idx];
+};
+
+void InputDefault::joy_connection_changed(int p_idx, bool p_connected, String p_name) {
+
+	_THREAD_SAFE_METHOD_
+	joy_names[p_idx] = p_connected ? p_name : "";
+
+	emit_signal("joy_connection_changed", p_idx, p_connected);
+};
+
+Vector3 InputDefault::get_accelerometer() {
+
+	_THREAD_SAFE_METHOD_
+	return accelerometer;
+}
+
+void InputDefault::parse_input_event(const InputEvent& p_event) {
+
+	_THREAD_SAFE_METHOD_
+	switch(p_event.type) {
+
+		case InputEvent::KEY: {
+
+			if (p_event.key.echo)
+				break;
+			if (p_event.key.scancode==0)
+				break;
+
+		//	print_line(p_event);
+
+			if (p_event.key.pressed)
+				keys_pressed.insert(p_event.key.scancode);
+			else
+				keys_pressed.erase(p_event.key.scancode);
+		} break;
+		case InputEvent::MOUSE_BUTTON: {
+
+			if (p_event.mouse_button.doubleclick)
+				break;
+
+			if (p_event.mouse_button.pressed)
+				mouse_button_mask|=(1<<p_event.mouse_button.button_index);
+			else
+				mouse_button_mask&=~(1<<p_event.mouse_button.button_index);
+
+			if (main_loop && emulate_touch && p_event.mouse_button.button_index==1) {
+				InputEventScreenTouch touch_event;
+				touch_event.index=0;
+				touch_event.pressed=p_event.mouse_button.pressed;
+				touch_event.x=p_event.mouse_button.x;
+				touch_event.y=p_event.mouse_button.y;
+				InputEvent ev;
+				ev.type=InputEvent::SCREEN_TOUCH;
+				ev.screen_touch=touch_event;
+				main_loop->input_event(ev);
+			}
+		} break;
+		case InputEvent::MOUSE_MOTION: {
+
+			if (main_loop && emulate_touch && p_event.mouse_motion.button_mask&1) {
+				InputEventScreenDrag drag_event;
+				drag_event.index=0;
+				drag_event.x=p_event.mouse_motion.x;
+				drag_event.y=p_event.mouse_motion.y;
+				drag_event.relative_x=p_event.mouse_motion.relative_x;
+				drag_event.relative_y=p_event.mouse_motion.relative_y;
+				drag_event.speed_x=p_event.mouse_motion.speed_x;
+				drag_event.speed_y=p_event.mouse_motion.speed_y;
+
+				InputEvent ev;
+				ev.type=InputEvent::SCREEN_DRAG;
+				ev.screen_drag=drag_event;
+
+				main_loop->input_event(ev);
+			}
+
+		} break;
+		case InputEvent::JOYSTICK_BUTTON: {
+
+			int c = _combine_device(p_event.joy_button.button_index,p_event.device);
+
+			if (p_event.joy_button.pressed)
+				joy_buttons_pressed.insert(c);
+			else
+				joy_buttons_pressed.erase(c);
+		} break;
+		case InputEvent::JOYSTICK_MOTION: {
+			set_joy_axis(p_event.device, p_event.joy_motion.axis, p_event.joy_motion.axis_value);
+		} break;
+
+	}
+
+	if (main_loop)
+		main_loop->input_event(p_event);
+
+}
+
+void InputDefault::set_joy_axis(int p_device,int p_axis,float p_value) {
+
+	_THREAD_SAFE_METHOD_
+	int c = _combine_device(p_axis,p_device);
+	joy_axis[c]=p_value;
+}
+
+void InputDefault::set_accelerometer(const Vector3& p_accel) {
+
+	_THREAD_SAFE_METHOD_
+
+	accelerometer=p_accel;
+
+}
+
+void InputDefault::set_main_loop(MainLoop *p_main_loop) {
+	main_loop=p_main_loop;
+
+}
+
+void InputDefault::set_mouse_pos(const Point2& p_posf) {
+
+	mouse_speed_track.update(p_posf-mouse_pos);
+	mouse_pos=p_posf;
+	if (custom_cursor.is_valid()) {
+		VisualServer::get_singleton()->cursor_set_pos(get_mouse_pos());
+	}
+}
+
+Point2 InputDefault::get_mouse_pos() const {
+
+	return mouse_pos;
+}
+Point2 InputDefault::get_mouse_speed() const {
+
+	return mouse_speed_track.speed;
+}
+
+int InputDefault::get_mouse_button_mask() const {
+
+	return OS::get_singleton()->get_mouse_button_state();
+}
+
+void InputDefault::warp_mouse_pos(const Vector2& p_to) {
+
+	OS::get_singleton()->warp_mouse_pos(p_to);
+}
+
+
+void InputDefault::iteration(float p_step) {
+
+
+}
+
+void InputDefault::action_press(const StringName& p_action) {
+
+	if (custom_action_press.has(p_action)) {
+
+		custom_action_press[p_action]++;
+	} else {
+		custom_action_press[p_action]=1;
+	}
+}
+
+void InputDefault::action_release(const StringName& p_action){
+
+	ERR_FAIL_COND(!custom_action_press.has(p_action));
+	custom_action_press[p_action]--;
+	if (custom_action_press[p_action]==0) {
+		custom_action_press.erase(p_action);
+	}
+}
+
+void InputDefault::set_emulate_touch(bool p_emulate) {
+
+	emulate_touch=p_emulate;
+}
+
+bool InputDefault::is_emulating_touchscreen() const {
+
+	return emulate_touch;
+}
+
+void InputDefault::set_custom_mouse_cursor(const RES& p_cursor,const Vector2& p_hotspot) {
+	if (custom_cursor==p_cursor)
+		return;
+
+	custom_cursor=p_cursor;
+
+	if (p_cursor.is_null()) {
+		set_mouse_mode(MOUSE_MODE_VISIBLE);
+		VisualServer::get_singleton()->cursor_set_visible(false);
+	} else {
+		set_mouse_mode(MOUSE_MODE_HIDDEN);
+		VisualServer::get_singleton()->cursor_set_visible(true);
+		VisualServer::get_singleton()->cursor_set_texture(custom_cursor->get_rid(),p_hotspot);
+		VisualServer::get_singleton()->cursor_set_pos(get_mouse_pos());
+	}
+}
+
+void InputDefault::set_mouse_in_window(bool p_in_window) {
+
+	if (custom_cursor.is_valid()) {
+
+		if (p_in_window) {
+			set_mouse_mode(MOUSE_MODE_HIDDEN);
+			VisualServer::get_singleton()->cursor_set_visible(true);
+		} else {
+			set_mouse_mode(MOUSE_MODE_VISIBLE);
+			VisualServer::get_singleton()->cursor_set_visible(false);
+		}
+
+	}
+}
+
+InputDefault::InputDefault() {
+
+	mouse_button_mask=0;
+	emulate_touch=false;
+	main_loop=NULL;
+}

+ 83 - 0
main/input_default.h

@@ -0,0 +1,83 @@
+#ifndef INPUT_DEFAULT_H
+#define INPUT_DEFAULT_H
+
+#include "os/input.h"
+
+class InputDefault : public Input {
+
+	OBJ_TYPE( InputDefault, Input );
+	_THREAD_SAFE_CLASS_
+
+	int mouse_button_mask;
+	Set<int> keys_pressed;
+	Set<int> joy_buttons_pressed;
+	Map<int,float> joy_axis;
+	Map<StringName,int> custom_action_press;
+	Map<int, String> joy_names;
+	Vector3 accelerometer;
+	Vector2 mouse_pos;
+	MainLoop *main_loop;
+
+	bool emulate_touch;
+
+	struct SpeedTrack {
+
+		uint64_t last_tick;
+		Vector2 speed;
+		Vector2 accum;
+		float accum_t;
+		float min_ref_frame;
+		float max_ref_frame;
+
+		void update(const Vector2& p_delta_p);
+		void reset();
+		SpeedTrack();
+	};
+
+	SpeedTrack mouse_speed_track;
+
+	RES custom_cursor;
+
+public:
+
+	virtual bool is_key_pressed(int p_scancode);
+	virtual bool is_mouse_button_pressed(int p_button);
+	virtual bool is_joy_button_pressed(int p_device, int p_button);
+	virtual bool is_action_pressed(const StringName& p_action);
+
+	virtual float get_joy_axis(int p_device,int p_axis);
+	String get_joy_name(int p_idx);
+	void joy_connection_changed(int p_idx, bool p_connected, String p_name);
+
+	virtual Vector3 get_accelerometer();
+
+	virtual Point2 get_mouse_pos() const;
+	virtual Point2 get_mouse_speed() const;
+	virtual int get_mouse_button_mask() const;
+
+	virtual void warp_mouse_pos(const Vector2& p_to);
+
+
+	void parse_input_event(const InputEvent& p_event);
+	void set_accelerometer(const Vector3& p_accel);
+	void set_joy_axis(int p_device,int p_axis,float p_value);
+
+	void set_main_loop(MainLoop *main_loop);
+	void set_mouse_pos(const Point2& p_posf);
+
+	void action_press(const StringName& p_action);
+	void action_release(const StringName& p_action);
+
+	void iteration(float p_step);
+
+	void set_emulate_touch(bool p_emulate);
+	virtual bool is_emulating_touchscreen() const;
+
+	virtual void set_custom_mouse_cursor(const RES& p_cursor,const Vector2& p_hotspot=Vector2());
+	virtual void set_mouse_in_window(bool p_in_window);
+
+	InputDefault();
+
+};
+
+#endif // INPUT_DEFAULT_H

+ 18 - 1
main/main.cpp

@@ -75,7 +75,7 @@
 #include "core/io/file_access_zip.h"
 #include "translation.h"
 #include "version.h"
-#include "os/input.h"
+#include "main/input_default.h"
 #include "performance.h"
 
 static Globals *globals=NULL;
@@ -920,6 +920,9 @@ Error Main::setup2() {
 				id->set_emulate_touch(true);
 		}
 	}
+
+
+
 	MAIN_PRINT("Main: Load Remaps");
 
 	MAIN_PRINT("Main: Load Scene Types");
@@ -927,6 +930,20 @@ Error Main::setup2() {
 	register_scene_types();
 	register_server_types();
 
+	GLOBAL_DEF("display/custom_mouse_cursor",String());
+	GLOBAL_DEF("display/custom_mouse_cursor_hotspot",Vector2());
+	Globals::get_singleton()->set_custom_property_info("display/custom_mouse_cursor",PropertyInfo(Variant::STRING,"display/custom_mouse_cursor",PROPERTY_HINT_FILE,"*.png,*.webp"));
+
+	if (String(Globals::get_singleton()->get("display/custom_mouse_cursor"))!=String()) {
+
+		print_line("use custom cursor");
+		Ref<Texture> cursor=ResourceLoader::load(Globals::get_singleton()->get("display/custom_mouse_cursor"));
+		if (cursor.is_valid()) {
+			print_line("loaded ok");
+			Vector2 hotspot = Globals::get_singleton()->get("display/custom_mouse_cursor_hotspot");
+			Input::get_singleton()->set_custom_mouse_cursor(cursor,hotspot);
+		}
+	}
 #ifdef TOOLS_ENABLED
 	EditorNode::register_editor_types();
 	ObjectTypeDB::register_type<PCKPacker>(); // todo: move somewhere else

+ 1 - 1
platform/android/os_android.h

@@ -39,7 +39,7 @@
 #include "servers/physics_2d/physics_2d_server_sw.h"
 #include "servers/physics_2d/physics_2d_server_wrap_mt.h"
 #include "servers/visual/rasterizer.h"
-
+#include "main/input_default.h"
 
 //#ifdef USE_JAVA_FILE_ACCESS
 

+ 1 - 0
platform/flash/os_flash.h

@@ -11,6 +11,7 @@
 #include "servers/spatial_sound_2d/spatial_sound_2d_server_sw.h"
 #include "servers/audio/audio_server_sw.h"
 #include "servers/physics_2d/physics_2d_server_sw.h"
+#include "main/input_default.h"
 
 class OSFlash : public OS_Unix {
 

+ 2 - 0
platform/iphone/os_iphone.h

@@ -43,9 +43,11 @@
 #include "servers/audio/sample_manager_sw.h"
 #include "servers/spatial_sound/spatial_sound_server_sw.h"
 #include "servers/spatial_sound_2d/spatial_sound_2d_server_sw.h"
+#include "main/input_default.h"
 #include "game_center.h"
 #include "in_app_store.h"
 
+
 class AudioDriverIphone;
 class RasterizerGLES2;
 

+ 1 - 0
platform/javascript/os_javascript.h

@@ -40,6 +40,7 @@
 #include "servers/visual/rasterizer.h"
 #include "audio_server_javascript.h"
 #include "audio_driver_javascript.h"
+#include "main/input_default.h"
 
 typedef void (*GFXInitFunc)(void *ud,bool gl2,int w, int h, bool fs);
 typedef int (*OpenURIFunc)(const String&);

+ 1 - 1
platform/osx/os_osx.h

@@ -32,7 +32,7 @@
 
 #include "os/input.h"
 #include "drivers/unix/os_unix.h"
-
+#include "main/input_default.h"
 #include "servers/visual_server.h"
 #include "servers/visual/visual_server_wrap_mt.h"
 #include "servers/visual/rasterizer.h"

+ 14 - 0
platform/osx/os_osx.mm

@@ -512,12 +512,26 @@ static int button_mask=0;
 
 - (void)mouseExited:(NSEvent *)event
 {
+	if (!OS_OSX::singleton)
+		return;
+
+	if (OS_OSX::singleton->main_loop && OS_OSX::singleton->mouse_mode!=OS::MOUSE_MODE_CAPTURED)
+		OS_OSX::singleton->main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_EXIT);
+	if (OS_OSX::singleton->input)
+		OS_OSX::singleton->input->set_mouse_in_window(false);
    // _glfwInputCursorEnter(window, GL_FALSE);
 }
 
 - (void)mouseEntered:(NSEvent *)event
 {
   //  _glfwInputCursorEnter(window, GL_TRUE);
+	if (!OS_OSX::singleton)
+		return;
+	if (OS_OSX::singleton->main_loop && OS_OSX::singleton->mouse_mode!=OS::MOUSE_MODE_CAPTURED)
+		OS_OSX::singleton->main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_ENTER);
+	if (OS_OSX::singleton->input)
+		OS_OSX::singleton->input->set_mouse_in_window(true);
+
 }
 
 - (void)viewDidChangeBackingProperties

+ 1 - 1
platform/server/os_server.h

@@ -30,7 +30,7 @@
 #define OS_SERVER_H
 
 
-#include "os/input.h"
+#include "main/input_default.h"
 #include "drivers/unix/os_unix.h"
 #include "servers/visual_server.h"
 #include "servers/visual/rasterizer.h"

+ 10 - 0
platform/windows/os_windows.cpp

@@ -323,11 +323,21 @@ LRESULT OS_Windows::WndProc(HWND hWnd,UINT uMsg, WPARAM	wParam,	LPARAM	lParam) {
 
 			old_invalid=true;
 			outside=true;
+			if (main_loop && mouse_mode!=MOUSE_MODE_CAPTURED)
+				main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_EXIT);
+			if (input)
+				input->set_mouse_in_window(false);
 
 		} break;
 		case WM_MOUSEMOVE: {
 
 			if (outside) {
+				//mouse enter
+
+				if (main_loop && mouse_mode!=MOUSE_MODE_CAPTURED)
+					main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_ENTER);
+				if (input)
+					input->set_mouse_in_window(true);
 
 				CursorShape c=cursor_shape;
 				cursor_shape=CURSOR_MAX;

+ 1 - 0
platform/windows/os_windows.h

@@ -47,6 +47,7 @@
 #include "servers/physics_2d/physics_2d_server_sw.h"
 #include "servers/physics_2d/physics_2d_server_wrap_mt.h"
 
+#include "main/input_default.h"
 
 #include <windows.h>
 

+ 2 - 0
platform/winrt/os_winrt.h

@@ -50,6 +50,8 @@
 
 #include <fcntl.h>
 #include <stdio.h>
+#include "main/input_default.h"
+
 /**
 	@author Juan Linietsky <[email protected]>
 */

+ 15 - 1
platform/x11/os_x11.cpp

@@ -1181,8 +1181,22 @@ void OS_X11::process_xevents() {
 			XVisibilityEvent * visibility = (XVisibilityEvent *)&event;
 			minimized = (visibility->state == VisibilityFullyObscured);
 		} break;
+		case LeaveNotify: {
 
-		case FocusIn: 
+			if (main_loop && mouse_mode!=MOUSE_MODE_CAPTURED)
+				main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_EXIT);
+			if (input)
+				input->set_mouse_in_window(false);
+
+		} break;
+		case EnterNotify: {
+
+			if (main_loop && mouse_mode!=MOUSE_MODE_CAPTURED)
+				main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_ENTER);
+			if (input)
+				input->set_mouse_in_window(true);
+		} break;
+		case FocusIn:
 			minimized = false;
 #ifdef NEW_WM_API
 			if(current_videomode.fullscreen) {

+ 1 - 0
platform/x11/os_x11.h

@@ -46,6 +46,7 @@
 #include "drivers/pulseaudio/audio_driver_pulseaudio.h"
 #include "servers/physics_2d/physics_2d_server_sw.h"
 #include "servers/physics_2d/physics_2d_server_wrap_mt.h"
+#include "main/input_default.h"
 
 #include <X11/keysym.h>
 #include <X11/Xlib.h>

+ 82 - 17
scene/3d/skeleton.cpp

@@ -187,30 +187,59 @@ void Skeleton::_notification(int p_what) {
 			
 				Bone &b=bonesptr[i];
 		
-				if (b.enabled) {
+				if (b.disable_rest) {
+					if (b.enabled) {
 
-					Transform pose=b.pose;
-					if (b.custom_pose_enable) {
+						Transform pose=b.pose;
+						if (b.custom_pose_enable) {
 
-						pose = b.custom_pose * pose;
-					}
+							pose = b.custom_pose * pose;
+						}
+
+						if (b.parent>=0) {
+
+							b.pose_global=bonesptr[b.parent].pose_global * pose;
+						} else {
 
-					if (b.parent>=0) {
-					
-						b.pose_global=bonesptr[b.parent].pose_global * (b.rest * pose);
+							b.pose_global=pose;
+						}
 					} else {
-					
-						b.pose_global=b.rest * pose;
+
+						if (b.parent>=0) {
+
+							b.pose_global=bonesptr[b.parent].pose_global;
+						} else {
+
+							b.pose_global=Transform();
+						}
 					}
+
 				} else {
-				
-					if (b.parent>=0) {
-					
-						b.pose_global=bonesptr[b.parent].pose_global * b.rest;
+					if (b.enabled) {
+
+						Transform pose=b.pose;
+						if (b.custom_pose_enable) {
+
+							pose = b.custom_pose * pose;
+						}
+
+						if (b.parent>=0) {
+
+							b.pose_global=bonesptr[b.parent].pose_global * (b.rest * pose);
+						} else {
+
+							b.pose_global=b.rest * pose;
+						}
 					} else {
-					
-						b.pose_global=b.rest;				
-					}				
+
+						if (b.parent>=0) {
+
+							b.pose_global=bonesptr[b.parent].pose_global * b.rest;
+						} else {
+
+							b.pose_global=b.rest;
+						}
+					}
 				}
 				
 				vs->skeleton_bone_set_transform( skeleton, i,  b.pose_global * b.rest_global_inverse );
@@ -315,6 +344,37 @@ void Skeleton::set_bone_parent(int p_bone, int p_parent) {
 	_make_dirty();
 }
 
+void Skeleton::unparent_bone_and_rest(int p_bone) {
+
+	ERR_FAIL_INDEX( p_bone, bones.size() );
+
+	int parent=bones[p_bone].parent;
+	while(parent>=0) {
+		bones[p_bone].rest = bones[parent].rest * bones[p_bone].rest;
+		parent=bones[parent].parent;
+	}
+
+	bones[p_bone].parent=-1;
+	bones[p_bone].rest_global_inverse=bones[p_bone].rest.affine_inverse(); //same thing
+
+	_make_dirty();
+
+}
+
+void Skeleton::set_bone_disable_rest(int p_bone, bool p_disable) {
+
+	ERR_FAIL_INDEX( p_bone, bones.size() );
+	bones[p_bone].disable_rest=p_disable;
+
+}
+
+bool Skeleton::is_bone_rest_disabled(int p_bone) const {
+
+	ERR_FAIL_INDEX_V( p_bone, bones.size(), false );
+	return bones[p_bone].disable_rest;
+}
+
+
 int Skeleton::get_bone_parent(int p_bone) const {
 
 	ERR_FAIL_INDEX_V( p_bone, bones.size(), -1 );
@@ -522,9 +582,14 @@ void Skeleton::_bind_methods() {
 	
 	ObjectTypeDB::bind_method(_MD("get_bone_count"),&Skeleton::get_bone_count);
 
+	ObjectTypeDB::bind_method(_MD("unparent_bone_and_rest","bone_idx"),&Skeleton::unparent_bone_and_rest);
+
 	ObjectTypeDB::bind_method(_MD("get_bone_rest","bone_idx"),&Skeleton::get_bone_rest);
 	ObjectTypeDB::bind_method(_MD("set_bone_rest","bone_idx","rest"),&Skeleton::set_bone_rest);
 
+	ObjectTypeDB::bind_method(_MD("set_bone_disable_rest","bone_idx","disable"),&Skeleton::set_bone_disable_rest);
+	ObjectTypeDB::bind_method(_MD("is_bone_rest_disabled","bone_idx"),&Skeleton::is_bone_rest_disabled);
+
 	ObjectTypeDB::bind_method(_MD("bind_child_node_to_bone","bone_idx","node:Node"),&Skeleton::bind_child_node_to_bone);
 	ObjectTypeDB::bind_method(_MD("unbind_child_node_from_bone","bone_idx","node:Node"),&Skeleton::unbind_child_node_from_bone);
 	ObjectTypeDB::bind_method(_MD("get_bound_child_nodes_to_bone","bone_idx"),&Skeleton::_get_bound_child_nodes_to_bone);

+ 7 - 1
scene/3d/skeleton.h

@@ -46,6 +46,7 @@ class Skeleton : public Spatial {
 		bool enabled;
 		int parent;
 
+		bool disable_rest;
 		Transform rest;
 		Transform rest_global_inverse;
 		 
@@ -57,7 +58,7 @@ class Skeleton : public Spatial {
 		
 		List<uint32_t> nodes_bound;
 		
-		Bone() { parent=-1; enabled=true; custom_pose_enable=false; }
+		Bone() { parent=-1; enabled=true; custom_pose_enable=false; disable_rest=false; }
 	};
 
 	bool rest_global_inverse_dirty;
@@ -111,6 +112,11 @@ public:
 	void set_bone_parent(int p_bone, int p_parent);
 	int get_bone_parent(int p_bone) const;
 
+	void unparent_bone_and_rest(int p_idx);
+
+	void set_bone_disable_rest(int p_bone, bool p_disable);
+	bool is_bone_rest_disabled(int p_bone) const;
+
 	int get_bone_count() const;
 	
 	void set_bone_rest(int p_bone, const Transform& p_rest);

+ 12 - 1
tools/editor/editor_import_export.cpp

@@ -842,6 +842,17 @@ Error EditorExportPlatform::export_project_files(EditorExportSaveFunction p_func
 			boot_splash=splash;
 		}
 	}
+	StringName custom_cursor;
+	{
+		String splash=Globals::get_singleton()->get("display/custom_mouse_cursor"); //avoid splash from being converted
+		splash=splash.strip_edges();
+		if (splash!=String()) {
+			if (!splash.begins_with("res://"))
+				splash="res://"+splash;
+			splash=splash.simplify_path();
+			custom_cursor=splash;
+		}
+	}
 
 
 
@@ -853,7 +864,7 @@ Error EditorExportPlatform::export_project_files(EditorExportSaveFunction p_func
 		String src=files[i];
 		Vector<uint8_t> buf;
 
-		if (src==boot_splash)
+		if (src==boot_splash || src==custom_cursor)
 			buf = get_exported_file_default(src); //bootsplash must be kept if used
 		else
 			buf = get_exported_file(src);

+ 9 - 5
tools/editor/editor_node.cpp

@@ -93,7 +93,7 @@
 #include "plugins/light_occluder_2d_editor_plugin.h"
 #include "plugins/color_ramp_editor_plugin.h"
 #include "plugins/collision_shape_2d_editor_plugin.h"
-#include "os/input.h"
+#include "main/input_default.h"
 // end
 #include "tools/editor/io_plugins/editor_texture_import_plugin.h"
 #include "tools/editor/io_plugins/editor_scene_import_plugin.h"
@@ -4390,11 +4390,15 @@ EditorNode::EditorNode() {
 
 	EditorHelp::generate_doc(); //before any editor classes are crated
 
-	if (!OS::get_singleton()->has_touchscreen_ui_hint() && Input::get_singleton()) {
-		//only if no touchscreen ui hint, set emulation
-		InputDefault *id = Input::get_singleton()->cast_to<InputDefault>();
-		if (id)
+	InputDefault *id = Input::get_singleton()->cast_to<InputDefault>();
+
+	if (id) {
+
+		if (!OS::get_singleton()->has_touchscreen_ui_hint() && Input::get_singleton()) {
+			//only if no touchscreen ui hint, set emulation
 			id->set_emulate_touch(false); //just disable just in case
+		}
+		id->set_custom_mouse_cursor(RES());
 	}