Esempio n. 1
0
InReaction HotkeyInputHandler( const SDL_Event_* ev )
{
	int keycode = 0;

	switch( ev->ev.type )
	{
	case SDL_KEYDOWN:
	case SDL_KEYUP:
		keycode = (int)ev->ev.key.keysym.sym;
		break;

	case SDL_MOUSEBUTTONDOWN:
	case SDL_MOUSEBUTTONUP:
#if SDL_VERSION_ATLEAST(2, 0, 0)
		if ((int)ev->ev.button.button <= SDL_BUTTON_RIGHT)
#elif SDL_VERSION_ATLEAST(1, 2, 13)
		if ((int)ev->ev.button.button <= SDL_BUTTON_X2)
#else
		if ((int)ev->ev.button.button <= SDL_BUTTON_WHEELDOWN)
#endif
		{
			keycode = CUSTOM_SDL_KEYCODE + (int)ev->ev.button.button;
			break;
		}
		return IN_PASS;

#if SDL_VERSION_ATLEAST(2, 0, 0)
	case SDL_MOUSEWHEEL:
		if (ev->ev.wheel.y > 0)
		{
			keycode = MOUSE_WHEELUP;
			break;
		}
		else if (ev->ev.wheel.y < 0)
		{
			keycode = MOUSE_WHEELDOWN;
			break;
		}
		return IN_PASS;
#endif

	case SDL_HOTKEYDOWN:
		g_HotkeyStatus[static_cast<const char*>(ev->ev.user.data1)] = true;
		return IN_PASS;

	case SDL_HOTKEYUP:
		g_HotkeyStatus[static_cast<const char*>(ev->ev.user.data1)] = false;
		return IN_PASS;

	default:
		return IN_PASS;
	}

	// Rather ugly hack to make the '"' key work better on a MacBook Pro on Windows so it doesn't
	// always close the console. (Maybe this would be better handled in wsdl or something?)
	if (keycode == SDLK_BACKQUOTE && (ev->ev.key.keysym.unicode == '\'' || ev->ev.key.keysym.unicode == '"'))
		keycode = ev->ev.key.keysym.unicode;

	// Somewhat hackish:
	// Create phantom 'unified-modifier' events when left- or right- modifier keys are pressed
	// Just send them to this handler; don't let the imaginary event codes leak back to real SDL.

	SDL_Event_ phantom;
	phantom.ev.type = ( ( ev->ev.type == SDL_KEYDOWN ) || ( ev->ev.type == SDL_MOUSEBUTTONDOWN ) ) ? SDL_KEYDOWN : SDL_KEYUP;
	if( ( keycode == SDLK_LSHIFT ) || ( keycode == SDLK_RSHIFT ) )
	{
		phantom.ev.key.keysym.sym = (SDLKEY)UNIFIED_SHIFT;
		unified[0] = ( phantom.ev.type == SDL_KEYDOWN );
		HotkeyInputHandler( &phantom );
	}
	else if( ( keycode == SDLK_LCTRL ) || ( keycode == SDLK_RCTRL ) )
	{
		phantom.ev.key.keysym.sym = (SDLKEY)UNIFIED_CTRL;
		unified[1] = ( phantom.ev.type == SDL_KEYDOWN );
		HotkeyInputHandler( &phantom );
	}
	else if( ( keycode == SDLK_LALT ) || ( keycode == SDLK_RALT ) )
	{
		phantom.ev.key.keysym.sym = (SDLKEY)UNIFIED_ALT;
		unified[2] = ( phantom.ev.type == SDL_KEYDOWN );
		HotkeyInputHandler( &phantom );
	}
#if SDL_VERSION_ATLEAST(2, 0, 0)
	else if( ( keycode == SDLK_LGUI ) || ( keycode == SDLK_RGUI ) )
#else // SDL 1.2
	else if( ( keycode == SDLK_LSUPER ) || ( keycode == SDLK_RSUPER ) || ( keycode == SDLK_LMETA ) || ( keycode == SDLK_RMETA) )
#endif
	{
		phantom.ev.key.keysym.sym = (SDLKEY)UNIFIED_SUPER;
		unified[3] = ( phantom.ev.type == SDL_KEYDOWN );
		HotkeyInputHandler( &phantom );
	}

	// Check whether we have any hotkeys registered for this particular keycode
	if( g_HotkeyMap.find(keycode) == g_HotkeyMap.end() )
		return( IN_PASS );

	// Inhibit the dispatch of hotkey events caused by real keys (not fake mouse button
	// events) while the console is up.

	bool consoleCapture = false;

	if( g_Console->IsActive() && keycode < CUSTOM_SDL_KEYCODE )
		consoleCapture = true;

	// Here's an interesting bit:
	// If you have an event bound to, say, 'F', and another to, say, 'Ctrl+F', pressing
	// 'F' while control is down would normally fire off both.

	// To avoid this, set the modifier keys for /all/ events this key would trigger
	// (Ctrl, for example, is both group-save and bookmark-save)
	// but only send a HotkeyDown event for the event with bindings most precisely
	// matching the conditions (i.e. the event with the highest number of auxiliary
	// keys, providing they're all down)

#if SDL_VERSION_ATLEAST(2, 0, 0)
	bool typeKeyDown = ( ev->ev.type == SDL_KEYDOWN ) || ( ev->ev.type == SDL_MOUSEBUTTONDOWN ) || (ev->ev.type == SDL_MOUSEWHEEL);
#else
	bool typeKeyDown = ( ev->ev.type == SDL_KEYDOWN ) || ( ev->ev.type == SDL_MOUSEBUTTONDOWN );
#endif
	
	// -- KEYDOWN SECTION -- 

	std::vector<const char*> closestMapNames;
	size_t closestMapMatch = 0;

	for( std::vector<SHotkeyMapping>::iterator it = g_HotkeyMap[keycode].begin(); it < g_HotkeyMap[keycode].end(); ++it )
	{
		// If a key has been pressed, and this event triggers on its release, skip it.
		// Similarly, if the key's been released and the event triggers on a keypress, skip it.
		if( it->negated == typeKeyDown )
			continue;

		// Check to see if all auxiliary keys are down
		
		bool accept = true;

		for( std::vector<SKey>::iterator itKey = it->requires.begin(); itKey != it->requires.end(); ++itKey )
		{
			bool rqdState = !itKey->negated;

			if( (int)itKey->code < CUSTOM_SDL_KEYCODE )
			{
				if( g_keys[itKey->code] != rqdState ) accept = false;
			}
			else if( (int)itKey->code < UNIFIED_SHIFT )
			{
				if( g_mouse_buttons[itKey->code - CUSTOM_SDL_KEYCODE] != rqdState ) accept = false;
			}
			else if( (int)itKey->code < UNIFIED_LAST )
			{
				if( unified[itKey->code - UNIFIED_SHIFT] != rqdState ) accept = false;
			}
		}

		if( accept && !( consoleCapture && it->name != "console.toggle" ) )
		{
			// Check if this is an equally precise or more precise match
			if( it->requires.size() + 1 >= closestMapMatch )
			{
				// Check if more precise
				if( it->requires.size() + 1 > closestMapMatch )
				{
					// Throw away the old less-precise matches
					closestMapNames.clear();
					closestMapMatch = it->requires.size() + 1;
				}

				closestMapNames.push_back(it->name.c_str());
			}
		}
	}

	for (size_t i = 0; i < closestMapNames.size(); ++i)
	{
		SDL_Event_ hotkeyNotification;
		hotkeyNotification.ev.type = SDL_HOTKEYDOWN;
		hotkeyNotification.ev.user.data1 = const_cast<char*>(closestMapNames[i]);
		in_push_priority_event(&hotkeyNotification);
	}

	// -- KEYUP SECTION --

	for( std::vector<SHotkeyMapping>::iterator it = g_HotkeyMap[keycode].begin(); it < g_HotkeyMap[keycode].end(); ++it )
	{
		// If it's a keydown event, won't cause HotKeyUps in anything that doesn't
		// use this key negated => skip them
		// If it's a keyup event, won't cause HotKeyUps in anything that does use
		// this key negated => skip them too.
		if( it->negated != typeKeyDown )
			continue;

		// Check to see if all auxiliary keys are down

		bool accept = true;

		for( std::vector<SKey>::iterator itKey = it->requires.begin(); itKey != it->requires.end(); ++itKey )
		{
			bool rqdState = !itKey->negated;

			if( (int)itKey->code < CUSTOM_SDL_KEYCODE )
			{
				if( g_keys[itKey->code] != rqdState ) accept = false;
			}
			else if( (int)itKey->code < UNIFIED_SHIFT )
			{
				if( g_mouse_buttons[itKey->code - CUSTOM_SDL_KEYCODE] != rqdState ) accept = false;
			}
			else if( (int)itKey->code < UNIFIED_LAST )
			{
				if( unified[itKey->code - UNIFIED_SHIFT] != rqdState ) accept = false;
			}
		}

		if( accept )
		{
			SDL_Event_ hotkeyNotification;
			hotkeyNotification.ev.type = SDL_HOTKEYUP;
			hotkeyNotification.ev.user.data1 = const_cast<char*>(it->name.c_str());
			in_push_priority_event(&hotkeyNotification);
		}
	}

	return( IN_PASS );
}
Esempio n. 2
0
File: CGUI.cpp Progetto: 2asoft/0ad
InReaction CGUI::HandleEvent(const SDL_Event_* ev)
{
	InReaction ret = IN_PASS;

	if (ev->ev.type == SDL_HOTKEYDOWN || ev->ev.type == SDL_HOTKEYUP)
	{
		const char* hotkey = static_cast<const char*>(ev->ev.user.data1);

		std::map<CStr, std::vector<IGUIObject*> >::iterator it = m_HotkeyObjects.find(hotkey);
		if (it != m_HotkeyObjects.end())
			for (IGUIObject* const& obj : it->second)
			{
				// Update hotkey status before sending the event,
				// else the status will be outdated when processing the GUI event.
				HotkeyInputHandler(ev);
				ret = IN_HANDLED;

				if (ev->ev.type == SDL_HOTKEYDOWN)
					obj->SendEvent(GUIM_PRESSED, "press");
				else
					obj->SendEvent(GUIM_RELEASED, "release");
			}
	}

	else if (ev->ev.type == SDL_MOUSEMOTION)
	{
		// Yes the mouse position is stored as float to avoid
		//  constant conversions when operating in a
		//  float-based environment.
		m_MousePos = CPos((float)ev->ev.motion.x * g_GuiScale, (float)ev->ev.motion.y * g_GuiScale);

		SGUIMessage msg(GUIM_MOUSE_MOTION);
		GUI<SGUIMessage>::RecurseObject(GUIRR_HIDDEN | GUIRR_GHOST, m_BaseObject,
										&IGUIObject::HandleMessage,
										msg);
	}

	// Update m_MouseButtons. (BUTTONUP is handled later.)
	else if (ev->ev.type == SDL_MOUSEBUTTONDOWN)
	{
		switch (ev->ev.button.button)
		{
		case SDL_BUTTON_LEFT:
		case SDL_BUTTON_RIGHT:
		case SDL_BUTTON_MIDDLE:
			m_MouseButtons |= Bit<unsigned int>(ev->ev.button.button);
			break;
		default:
			break;
		}
	}

	// Update m_MousePos (for delayed mouse button events)
	CPos oldMousePos = m_MousePos;
	if (ev->ev.type == SDL_MOUSEBUTTONDOWN || ev->ev.type == SDL_MOUSEBUTTONUP)
	{
		m_MousePos = CPos((float)ev->ev.button.x * g_GuiScale, (float)ev->ev.button.y * g_GuiScale);
	}

	// Only one object can be hovered
	IGUIObject* pNearest = NULL;

	// TODO Gee: (2004-09-08) Big TODO, don't do the below if the SDL_Event is something like a keypress!
	try
	{
		PROFILE("mouse events");
		// TODO Gee: Optimizations needed!
		//  these two recursive function are quite overhead heavy.

		// pNearest will after this point at the hovered object, possibly NULL
		pNearest = FindObjectUnderMouse();

		// Now we'll call UpdateMouseOver on *all* objects,
		//  we'll input the one hovered, and they will each
		//  update their own data and send messages accordingly
		GUI<IGUIObject*>::RecurseObject(GUIRR_HIDDEN | GUIRR_GHOST,
			m_BaseObject, &IGUIObject::UpdateMouseOver, pNearest);

		if (ev->ev.type == SDL_MOUSEBUTTONDOWN)
		{
			switch (ev->ev.button.button)
			{
			case SDL_BUTTON_LEFT:
				// Focus the clicked object (or focus none if nothing clicked on)
				SetFocusedObject(pNearest);

				if (pNearest)
					ret = pNearest->SendEvent(GUIM_MOUSE_PRESS_LEFT, "mouseleftpress");
				break;

			case SDL_BUTTON_RIGHT:
				if (pNearest)
					ret = pNearest->SendEvent(GUIM_MOUSE_PRESS_RIGHT, "mouserightpress");
				break;

			default:
				break;
			}
		}
		else if (ev->ev.type == SDL_MOUSEWHEEL && pNearest)
		{
			if (ev->ev.wheel.y < 0)
				ret = pNearest->SendEvent(GUIM_MOUSE_WHEEL_DOWN, "mousewheeldown");
			else if (ev->ev.wheel.y > 0)
				ret = pNearest->SendEvent(GUIM_MOUSE_WHEEL_UP, "mousewheelup");
		}
		else if (ev->ev.type == SDL_MOUSEBUTTONUP)
		{
			switch (ev->ev.button.button)
			{
			case SDL_BUTTON_LEFT:
				if (pNearest)
				{
					double timeElapsed = timer_Time() - pNearest->m_LastClickTime[SDL_BUTTON_LEFT];
					pNearest->m_LastClickTime[SDL_BUTTON_LEFT] = timer_Time();

					if (timeElapsed < SELECT_DBLCLICK_RATE)
						ret = pNearest->SendEvent(GUIM_MOUSE_DBLCLICK_LEFT, "mouseleftdoubleclick");
					else
						ret = pNearest->SendEvent(GUIM_MOUSE_RELEASE_LEFT, "mouseleftrelease");
				}
				break;
			case SDL_BUTTON_RIGHT:
				if (pNearest)
				{
					double timeElapsed = timer_Time() - pNearest->m_LastClickTime[SDL_BUTTON_RIGHT];
					pNearest->m_LastClickTime[SDL_BUTTON_RIGHT] = timer_Time();

					if (timeElapsed < SELECT_DBLCLICK_RATE)
						ret = pNearest->SendEvent(GUIM_MOUSE_DBLCLICK_RIGHT, "mouserightdoubleclick");
					else
						ret = pNearest->SendEvent(GUIM_MOUSE_RELEASE_RIGHT, "mouserightrelease");
				}
				break;
			}

			// Reset all states on all visible objects
			GUI<>::RecurseObject(GUIRR_HIDDEN, m_BaseObject,
									&IGUIObject::ResetStates);

			// Since the hover state will have been reset, we reload it.
			GUI<IGUIObject*>::RecurseObject(GUIRR_HIDDEN | GUIRR_GHOST,
				m_BaseObject, &IGUIObject::UpdateMouseOver, pNearest);
		}
	}
	catch (PSERROR_GUI& e)
	{
		UNUSED2(e);
		debug_warn(L"CGUI::HandleEvent error");
		// TODO Gee: Handle
	}

	// BUTTONUP's effect on m_MouseButtons is handled after
	// everything else, so that e.g. 'press' handlers (activated
	// on button up) see which mouse button had been pressed.
	if (ev->ev.type == SDL_MOUSEBUTTONUP)
	{
		switch (ev->ev.button.button)
		{
		case SDL_BUTTON_LEFT:
		case SDL_BUTTON_RIGHT:
		case SDL_BUTTON_MIDDLE:
			m_MouseButtons &= ~Bit<unsigned int>(ev->ev.button.button);
			break;
		default:
			break;
		}
	}

	// Restore m_MousePos (for delayed mouse button events)
	if (ev->ev.type == SDL_MOUSEBUTTONDOWN || ev->ev.type == SDL_MOUSEBUTTONUP)
		m_MousePos = oldMousePos;

	// Handle keys for input boxes
	if (GetFocusedObject())
	{
		if ((ev->ev.type == SDL_KEYDOWN &&
		     ev->ev.key.keysym.sym != SDLK_ESCAPE &&
		     !g_keys[SDLK_LCTRL] && !g_keys[SDLK_RCTRL] &&
		     !g_keys[SDLK_LALT] && !g_keys[SDLK_RALT]) ||
		    ev->ev.type == SDL_HOTKEYDOWN ||
		    ev->ev.type == SDL_TEXTINPUT ||
		    ev->ev.type == SDL_TEXTEDITING)
		{
			ret = GetFocusedObject()->ManuallyHandleEvent(ev);
		}
		// else will return IN_PASS because we never used the button.
	}

	return ret;
}