void
CMSWindowsKeyState::fakeKey(const Keystroke& keystroke)
{
	switch (keystroke.m_type) {
	case Keystroke::kButton: {
		LOG((CLOG_DEBUG1 "  %03x (%08x) %s", keystroke.m_data.m_button.m_button, keystroke.m_data.m_button.m_client, keystroke.m_data.m_button.m_press ? "down" : "up"));
		KeyButton button = keystroke.m_data.m_button.m_button;

		// windows doesn't send key ups for key repeats
		if (keystroke.m_data.m_button.m_repeat &&
			!keystroke.m_data.m_button.m_press) {
			LOG((CLOG_DEBUG1 "  discard key repeat release"));
			break;
		}

		// get the virtual key for the button
		UINT vk = keystroke.m_data.m_button.m_client;

		// special handling of VK_SNAPSHOT
		if (vk == VK_SNAPSHOT) {
			if ((getActiveModifiers() & KeyModifierAlt) != 0) {
				// snapshot active window
				button = 1;
			}
			else {
				// snapshot full screen
				button = 0;
			}
		}

		// synthesize event
		m_desks->fakeKeyEvent(button, vk,
								keystroke.m_data.m_button.m_press,
								keystroke.m_data.m_button.m_repeat);
		break;
	}

	case Keystroke::kGroup:
		// we don't restore the group.  we'd like to but we can't be
		// sure the restoring group change will be processed after the
		// key events.
		if (!keystroke.m_data.m_group.m_restore) {
			if (keystroke.m_data.m_group.m_absolute) {
				LOG((CLOG_DEBUG1 "  group %d", keystroke.m_data.m_group.m_group));
				setWindowGroup(keystroke.m_data.m_group.m_group);
			}
			else {
				LOG((CLOG_DEBUG1 "  group %+d", keystroke.m_data.m_group.m_group));
				setWindowGroup(getEffectiveGroup(pollActiveGroup(),
									keystroke.m_data.m_group.m_group));
			}
		}
		break;
	}
}
KeyID
CMSWindowsKeyState::mapKeyFromEvent(WPARAM charAndVirtKey,
				LPARAM info, KeyModifierMask* maskOut) const
{
	static const KeyModifierMask s_controlAlt =
		KeyModifierControl | KeyModifierAlt;

	// extract character, virtual key, and if we didn't use AltGr
	char c       = (char)((charAndVirtKey & 0xff00u) >> 8);
	UINT vkCode  = (charAndVirtKey & 0xffu);
	bool noAltGr = ((charAndVirtKey & 0xff0000u) != 0);

	// handle some keys via table lookup
	KeyID id     = getKeyID(vkCode, (KeyButton)((info >> 16) & 0x1ffu));

	// check if not in table;  map character to key id
	if (id == kKeyNone && c != 0) {
		if ((c & 0x80u) == 0) {
			// ASCII
			id = static_cast<KeyID>(c) & 0xffu;
		}
		else {
			// character is not really ASCII.  instead it's some
			// character in the current ANSI code page.  try to
			// convert that to a Unicode character.  if we fail
			// then use the single byte character as is.
			char src = c;
			wchar_t unicode;
			if (MultiByteToWideChar(CP_THREAD_ACP, MB_PRECOMPOSED,
										&src, 1, &unicode, 1) > 0) {
				id = static_cast<KeyID>(unicode);
			}
			else {
				id = static_cast<KeyID>(c) & 0xffu;
			}
		}
	}

	// set modifier mask
	if (maskOut != NULL) {
		KeyModifierMask active = getActiveModifiers();
		if (!noAltGr && (active & s_controlAlt) == s_controlAlt) {
			// if !noAltGr then we're only interested in matching the
			// key, not the AltGr.  AltGr is down (i.e. control and alt
			// are down) but we don't want the client to have to match
			// that so we clear it.
			active &= ~s_controlAlt;
		}
		*maskOut = active;
	}

	return id;
}
void
CMSWindowsKeyState::useSavedModifiers(bool enable)
{
	if (enable != m_useSavedModifiers) {
		m_useSavedModifiers = enable;
		if (!m_useSavedModifiers) {
			// transfer any modifier state changes to CKeyState's state
			KeyModifierMask mask = m_originalSavedModifiers ^ m_savedModifiers;
			getActiveModifiersRValue() =
				(getActiveModifiers() & ~mask) | (m_savedModifiers & mask);
		}
	}
}
Example #4
0
void
COSXKeyState::fixStickyKeys()
{
	KeyModifierMask synergyMask = getActiveModifiers();
	KeyModifierMask hardwareMask = pollActiveModifiers();
	if (synergyMask != hardwareMask) {
		
		// modifier key stuck
		// compute changed modifiers
		KeyModifierMask changed = (hardwareMask ^ synergyMask);
		
		if (changed) {
			KeyButton kb;
			CString keyFixed;
			// synthesize changed modifier keys
			if ((changed & KeyModifierShift) != 0) {
				kb = mapVirtualKeyToKeyButton(s_shiftVK);
				fakeKeyUp(kb);
				keyFixed.append("shift ");
			}
			if ((changed & KeyModifierControl) != 0) {
				kb = mapVirtualKeyToKeyButton(s_controlVK);
				fakeKeyUp(kb);
				keyFixed.append("ctrl ");
			}
			if ((changed & KeyModifierAlt) != 0) {
				kb = mapVirtualKeyToKeyButton(s_altVK);
				fakeKeyUp(kb);
				keyFixed.append("alt ");
			}
			if ((changed & KeyModifierSuper) != 0) {
				kb = mapVirtualKeyToKeyButton(s_superVK);
				fakeKeyUp(kb);
				keyFixed.append("cmd ");
			}
			
			LOG((CLOG_DEBUG "fixed stuck modifier key: %s", keyFixed.c_str()));
		}
	}
}
Example #5
0
bool
COSXScreen::onKey(CGEventRef event)
{
	CGEventType eventKind = CGEventGetType(event);

	// get the key and active modifiers
	UInt32 virtualKey = CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode);
	CGEventFlags macMask = CGEventGetFlags(event);
	LOG((CLOG_DEBUG1 "event: Key event kind: %d, keycode=%d", eventKind, virtualKey));

	// Special handling to track state of modifiers
	if (eventKind == kCGEventFlagsChanged) {
		// get old and new modifier state
		KeyModifierMask oldMask = getActiveModifiers();
		KeyModifierMask newMask = m_keyState->mapModifiersFromOSX(macMask);
		m_keyState->handleModifierKeys(getEventTarget(), oldMask, newMask);

		// if the current set of modifiers exactly matches a modifiers-only
		// hot key then generate a hot key down event.
		if (m_activeModifierHotKey == 0) {
			if (m_modifierHotKeys.count(newMask) > 0) {
				m_activeModifierHotKey     = m_modifierHotKeys[newMask];
				m_activeModifierHotKeyMask = newMask;
				m_events->addEvent(CEvent(m_events->forIPrimaryScreen().hotKeyDown(),
								getEventTarget(),
								CHotKeyInfo::alloc(m_activeModifierHotKey)));
			}
		}

		// if a modifiers-only hot key is active and should no longer be
		// then generate a hot key up event.
		else if (m_activeModifierHotKey != 0) {
			KeyModifierMask mask = (newMask & m_activeModifierHotKeyMask);
			if (mask != m_activeModifierHotKeyMask) {
				m_events->addEvent(CEvent(m_events->forIPrimaryScreen().hotKeyUp(),
								getEventTarget(),
								CHotKeyInfo::alloc(m_activeModifierHotKey)));
				m_activeModifierHotKey     = 0;
				m_activeModifierHotKeyMask = 0;
			}
		}
			
		return true;
	}

	// check for hot key.  when we're on a secondary screen we disable
	// all hotkeys so we can capture the OS defined hot keys as regular
	// keystrokes but that means we don't get our own hot keys either.
	// so we check for a key/modifier match in our hot key map.
	if (!m_isOnScreen) {
		HotKeyToIDMap::const_iterator i =
			m_hotKeyToIDMap.find(CHotKeyItem(virtualKey, 
											 m_keyState->mapModifiersToCarbon(macMask) 
											 & 0xff00u));
		if (i != m_hotKeyToIDMap.end()) {
			UInt32 id = i->second;
	
			// determine event type
			CEvent::Type type;
			//UInt32 eventKind = GetEventKind(event);
			if (eventKind == kCGEventKeyDown) {
				type = m_events->forIPrimaryScreen().hotKeyDown();
			}
			else if (eventKind == kCGEventKeyUp) {
				type = m_events->forIPrimaryScreen().hotKeyUp();
			}
			else {
				return false;
			}
	
			m_events->addEvent(CEvent(type, getEventTarget(),
										CHotKeyInfo::alloc(id)));
		
			return true;
		}
	}

	// decode event type
	bool down	  = (eventKind == kCGEventKeyDown);
	bool up		  = (eventKind == kCGEventKeyUp);
	bool isRepeat = (CGEventGetIntegerValueField(event, kCGKeyboardEventAutorepeat) == 1);

	// map event to keys
	KeyModifierMask mask;
	COSXKeyState::CKeyIDs keys;
	KeyButton button = m_keyState->mapKeyFromEvent(keys, &mask, event);
	if (button == 0) {
		return false;
	}

	// check for AltGr in mask.  if set we send neither the AltGr nor
	// the super modifiers to clients then remove AltGr before passing
	// the modifiers to onKey.
	KeyModifierMask sendMask = (mask & ~KeyModifierAltGr);
	if ((mask & KeyModifierAltGr) != 0) {
		sendMask &= ~KeyModifierSuper;
	}
	mask &= ~KeyModifierAltGr;

	// update button state
	if (down) {
		m_keyState->onKey(button, true, mask);
	}
	else if (up) {
		if (!m_keyState->isKeyDown(button)) {
			// up event for a dead key.  throw it away.
			return false;
		}
		m_keyState->onKey(button, false, mask);
	}

	// send key events
	for (COSXKeyState::CKeyIDs::const_iterator i = keys.begin();
							i != keys.end(); ++i) {
		m_keyState->sendKeyEvent(getEventTarget(), down, isRepeat,
							*i, sendMask, 1, button);
	}

	return true;
}
void
CMSWindowsKeyState::saveModifiers()
{
	m_savedModifiers         = getActiveModifiers();
	m_originalSavedModifiers = m_savedModifiers;
}
void
CMSWindowsKeyState::fixKeys()
{
	// fake key releases for the windows keys if we think they're
	// down but they're really up.  we have to do this because if the
	// user presses and releases a windows key without pressing any
	// other key while it's down then the system will eat the key
	// release.  if we don't detect that and synthesize the release
	// then the client won't take the usual windows key release action
	// (which on windows is to show the start menu).
	//
	// only check on the windows 95 family since the NT family reports
	// the key releases as usual.
	if (!m_is95Family) {
		return;
	}

	KeyButton leftButton  = virtualKeyToButton(VK_LWIN);
	KeyButton rightButton = virtualKeyToButton(VK_RWIN);
	bool leftDown         = isKeyDown(leftButton);
	bool rightDown        = isKeyDown(rightButton);
	bool fix              = (leftDown || rightDown);
	if (fix) {
		// check if either button is not really down
		bool leftAsyncDown  = ((GetAsyncKeyState(VK_LWIN) & 0x8000) != 0);
		bool rightAsyncDown = ((GetAsyncKeyState(VK_RWIN) & 0x8000) != 0);

		if (leftAsyncDown != leftDown || rightAsyncDown != rightDown) {
			KeyModifierMask state = getActiveModifiers();
			if (!leftAsyncDown && !rightAsyncDown) {
				// no win keys are down so remove super modifier
				state &= ~KeyModifierSuper;
			}

			// report up events
			if (leftDown  && !leftAsyncDown) {
				LOG((CLOG_DEBUG1 "event: fake key release left windows key (0x%03x)", leftButton));
				CKeyState::onKey(leftButton, false, state);
				CKeyState::sendKeyEvent(m_eventTarget, false, false,
								kKeySuper_L, state, 1, leftButton);
			}
			if (rightDown && !rightAsyncDown) {
				LOG((CLOG_DEBUG1 "event: fake key release right windows key (0x%03x)", rightButton));
				CKeyState::onKey(rightButton, false, state);
				CKeyState::sendKeyEvent(m_eventTarget, false, false,
								kKeySuper_R, state, 1, rightButton);
			}
		}
	}

	if (fix && m_fixTimer == NULL) {
		// schedule check
		m_fixTimer = EVENTQUEUE->newTimer(0.1, NULL);
		EVENTQUEUE->adoptHandler(CEvent::kTimer, m_fixTimer,
							new TMethodEventJob<CMSWindowsKeyState>(
								this, &CMSWindowsKeyState::handleFixKeys));
	}
	else if (!fix && m_fixTimer != NULL) {
		// remove scheduled check
		EVENTQUEUE->removeHandler(CEvent::kTimer, m_fixTimer);
		EVENTQUEUE->deleteTimer(m_fixTimer);
		m_fixTimer = NULL;
	}
}
Example #8
0
KeyButton 
COSXKeyState::mapKeyFromEvent(CKeyIDs& ids,
				KeyModifierMask* maskOut, EventRef event) const
{
	ids.clear();

	// map modifier key
	if (maskOut != NULL) {
		KeyModifierMask activeMask = getActiveModifiers();
		activeMask &= ~KeyModifierAltGr;
		*maskOut    = activeMask;
	}

	// get virtual key
	UInt32 vkCode;
	GetEventParameter(event, kEventParamKeyCode, typeUInt32,
							NULL, sizeof(vkCode), NULL, &vkCode);

	// handle up events
	UInt32 eventKind = GetEventKind(event);
	if (eventKind == kEventRawKeyUp) {
		// the id isn't used.  we just need the same button we used on
		// the key press.  note that we don't use or reset the dead key
		// state;  up events should not affect the dead key state.
		ids.push_back(kKeyNone);
		return mapVirtualKeyToKeyButton(vkCode);
	}

	// check for special keys
	CVirtualKeyMap::const_iterator i = m_virtualKeyMap.find(vkCode);
	if (i != m_virtualKeyMap.end()) {
		m_deadKeyState = 0;
		ids.push_back(i->second);
		return mapVirtualKeyToKeyButton(vkCode);
	}

	// get keyboard info
	KeyboardLayoutRef keyboardLayout;
	OSStatus status = KLGetCurrentKeyboardLayout(&keyboardLayout);
	if (status != noErr) {
		return kKeyNone;
	}

	// get the event modifiers and remove the command and control
	// keys.  note if we used them though.
	UInt32 modifiers;
	GetEventParameter(event, kEventParamKeyModifiers, typeUInt32,
								NULL, sizeof(modifiers), NULL, &modifiers);
	static const UInt32 s_commandModifiers =
		cmdKey | controlKey | rightControlKey;
	bool isCommand = ((modifiers & s_commandModifiers) != 0);
	modifiers &= ~s_commandModifiers;

	// if we've used a command key then we want the glyph produced without
	// the option key (i.e. the base glyph).
	if (isCommand) {
		modifiers &= ~optionKey;
	}

	// translate via uchr resource
	const void* resource;
	if (KLGetKeyboardLayoutProperty(keyboardLayout,
								kKLuchrData, &resource) == noErr) {
		// choose action
		UInt16 action;
		switch (eventKind) {
		case kEventRawKeyDown:
			action = kUCKeyActionDown;
			break;

		case kEventRawKeyRepeat:
			action = kUCKeyActionAutoKey;
			break;

		default:
			return 0;
		}

		// translate key
		UniCharCount count;
		UniChar chars[2];
		OSStatus status = UCKeyTranslate((const UCKeyboardLayout*)resource,
							vkCode & 0xffu, action,
							(modifiers >> 8) & 0xffu,
							LMGetKbdType(), 0, &m_deadKeyState,
							sizeof(chars) / sizeof(chars[0]), &count, chars);

		// get the characters
		if (status == 0) {
			if (count != 0 || m_deadKeyState == 0) {
				m_deadKeyState = 0;
				for (UniCharCount i = 0; i < count; ++i) {
					ids.push_back(CKeyResource::unicharToKeyID(chars[i]));
				}
				adjustAltGrModifier(ids, maskOut, isCommand);
				return mapVirtualKeyToKeyButton(vkCode);
			}
			return 0;
		}
	}
void ShellSystemInterface::HandleScrollWheel(int np){
	int delta = (wheel_pos - np); // may need a - here for Apple "natural scrolling"
	wheel_pos = np;
	uiHandle->ProcessMouseWheel(delta, getActiveModifiers());
}
void ShellSystemInterface::HandleMouseToggle(int b, int s){
	(uiHandle.get()->*mouseResponse[s])(b,getActiveModifiers());
}
void ShellSystemInterface::HandleMousePosition(int x, int y){
	uiHandle->ProcessMouseMove(x, y, getActiveModifiers());
}
void ShellSystemInterface::HandleKeyToggle(int k, int s){
	// This seems like a recipe for badness
	// How does Rocket handle KeyIdentifiers with potentially invalid values?
	(uiHandle.get()->*keyResponse[s])((Rocket::Core::Input::KeyIdentifier)(keymap[k]),getActiveModifiers());
}
Example #13
0
KeyButton 
OSXKeyState::mapKeyFromEvent(KeyIDs& ids,
				KeyModifierMask* maskOut, CGEventRef event) const
{
	ids.clear();

	// map modifier key
	if (maskOut != NULL) {
		KeyModifierMask activeMask = getActiveModifiers();
		activeMask &= ~KeyModifierAltGr;
		*maskOut    = activeMask;
	}

	// get virtual key
	UInt32 vkCode = CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode);

	// handle up events
	UInt32 eventKind = CGEventGetType(event);
	if (eventKind == kCGEventKeyUp) {
		// the id isn't used.  we just need the same button we used on
		// the key press.  note that we don't use or reset the dead key
		// state;  up events should not affect the dead key state.
		ids.push_back(kKeyNone);
		return mapVirtualKeyToKeyButton(vkCode);
	}

	// check for special keys
	VirtualKeyMap::const_iterator i = m_virtualKeyMap.find(vkCode);
	if (i != m_virtualKeyMap.end()) {
		m_deadKeyState = 0;
		ids.push_back(i->second);
		return mapVirtualKeyToKeyButton(vkCode);
	}

	// get keyboard info

#if defined(MAC_OS_X_VERSION_10_5)
	TISInputSourceRef currentKeyboardLayout = TISCopyCurrentKeyboardLayoutInputSource(); 
#else
	KeyboardLayoutRef currentKeyboardLayout;
	OSStatus status = KLGetCurrentKeyboardLayout(&currentKeyboardLayout);
#endif
	if (currentKeyboardLayout == NULL) {
		return kKeyNone;
	}

	// get the event modifiers and remove the command and control
	// keys.  note if we used them though.
	// UCKeyTranslate expects old-style Carbon modifiers, so convert.
	UInt32 modifiers;
	modifiers = mapModifiersToCarbon(CGEventGetFlags(event));
	static const UInt32 s_commandModifiers =
		cmdKey | controlKey | rightControlKey;
	bool isCommand = ((modifiers & s_commandModifiers) != 0);
	modifiers &= ~s_commandModifiers;

	// if we've used a command key then we want the glyph produced without
	// the option key (i.e. the base glyph).
	//if (isCommand) {
		modifiers &= ~optionKey;
	//}

	// choose action
	UInt16 action;
	if(eventKind==kCGEventKeyDown) {
		action = kUCKeyActionDown;
	}
	else if(CGEventGetIntegerValueField(event, kCGKeyboardEventAutorepeat)==1) {
		action = kUCKeyActionAutoKey;
	}
	else {
		return 0;
	}

	// translate via uchr resource
#if defined(MAC_OS_X_VERSION_10_5)
	CFDataRef ref = (CFDataRef) TISGetInputSourceProperty(currentKeyboardLayout,
								kTISPropertyUnicodeKeyLayoutData);
	const UCKeyboardLayout* layout = (const UCKeyboardLayout*) CFDataGetBytePtr(ref);
	const bool layoutValid = (layout != NULL);
#else
	const void* resource;
	int err = KLGetKeyboardLayoutProperty(currentKeyboardLayout, kKLuchrData, &resource);
	const bool layoutValid = (err == noErr);
	const UCKeyboardLayout* layout = (const UCKeyboardLayout*)resource;
#endif

	if (layoutValid) {
		// translate key
		UniCharCount count;
		UniChar chars[2];
		LOG((CLOG_DEBUG2 "modifiers: %08x", modifiers & 0xffu));
		OSStatus status = UCKeyTranslate(layout,
							vkCode & 0xffu, action,
							(modifiers >> 8) & 0xffu,
							LMGetKbdType(), 0, &m_deadKeyState,
							sizeof(chars) / sizeof(chars[0]), &count, chars);

		// get the characters
		if (status == 0) {
			if (count != 0 || m_deadKeyState == 0) {
				m_deadKeyState = 0;
				for (UniCharCount i = 0; i < count; ++i) {
					ids.push_back(KeyResource::unicharToKeyID(chars[i]));
				}
				adjustAltGrModifier(ids, maskOut, isCommand);
				return mapVirtualKeyToKeyButton(vkCode);
			}
			return 0;
		}
	}

	return 0;
}
Example #14
0
bool
COSXScreen::onKey(EventRef event)
{
	UInt32 eventKind = GetEventKind(event);

	// get the key and active modifiers
	UInt32 virtualKey, macMask;
	GetEventParameter(event, kEventParamKeyCode, typeUInt32,
							NULL, sizeof(virtualKey), NULL, &virtualKey);
	GetEventParameter(event, kEventParamKeyModifiers, typeUInt32,
							NULL, sizeof(macMask), NULL, &macMask);
	LOG((CLOG_DEBUG1 "event: Key event kind: %d, keycode=%d", eventKind, virtualKey));

	// sadly, OS X doesn't report the virtualKey for modifier keys.
	// virtualKey will be zero for modifier keys.  since that's not good
	// enough we'll have to figure out what the key was.
	if (virtualKey == 0 && eventKind == kEventRawKeyModifiersChanged) {
		// get old and new modifier state
		KeyModifierMask oldMask = getActiveModifiers();
		KeyModifierMask newMask = m_keyState->mapModifiersFromOSX(macMask);
		m_keyState->handleModifierKeys(getEventTarget(), oldMask, newMask);

		// if the current set of modifiers exactly matches a modifiers-only
		// hot key then generate a hot key down event.
		if (m_activeModifierHotKey == 0) {
			if (m_modifierHotKeys.count(newMask) > 0) {
				m_activeModifierHotKey     = m_modifierHotKeys[newMask];
				m_activeModifierHotKeyMask = newMask;
				EVENTQUEUE->addEvent(CEvent(getHotKeyDownEvent(),
								getEventTarget(),
								CHotKeyInfo::alloc(m_activeModifierHotKey)));
			}
		}

		// if a modifiers-only hot key is active and should no longer be
		// then generate a hot key up event.
		else if (m_activeModifierHotKey != 0) {
			KeyModifierMask mask = (newMask & m_activeModifierHotKeyMask);
			if (mask != m_activeModifierHotKeyMask) {
				EVENTQUEUE->addEvent(CEvent(getHotKeyUpEvent(),
								getEventTarget(),
								CHotKeyInfo::alloc(m_activeModifierHotKey)));
				m_activeModifierHotKey     = 0;
				m_activeModifierHotKeyMask = 0;
			}
		}
			
		return true;
	}

	// check for hot key.  when we're on a secondary screen we disable
	// all hotkeys so we can capture the OS defined hot keys as regular
	// keystrokes but that means we don't get our own hot keys either.
	// so we check for a key/modifier match in our hot key map.
	if (!m_isOnScreen) {
		HotKeyToIDMap::const_iterator i =
			m_hotKeyToIDMap.find(CHotKeyItem(virtualKey, macMask & 0xff00u));
		if (i != m_hotKeyToIDMap.end()) {
			UInt32 id = i->second;
	
			// determine event type
			CEvent::Type type;
			UInt32 eventKind = GetEventKind(event);
			if (eventKind == kEventRawKeyDown) {
				type = getHotKeyDownEvent();
			}
			else if (eventKind == kEventRawKeyUp) {
				type = getHotKeyUpEvent();
			}
			else {
				return false;
			}
	
			EVENTQUEUE->addEvent(CEvent(type, getEventTarget(),
										CHotKeyInfo::alloc(id)));
		
			return true;
		}
	}

	// decode event type
	bool down	  = (eventKind == kEventRawKeyDown);
	bool up		  = (eventKind == kEventRawKeyUp);
	bool isRepeat = (eventKind == kEventRawKeyRepeat);

	// map event to keys
	KeyModifierMask mask;
	COSXKeyState::CKeyIDs keys;
	KeyButton button = m_keyState->mapKeyFromEvent(keys, &mask, event);
	if (button == 0) {
		return false;
	}

	// check for AltGr in mask.  if set we send neither the AltGr nor
	// the super modifiers to clients then remove AltGr before passing
	// the modifiers to onKey.
	KeyModifierMask sendMask = (mask & ~KeyModifierAltGr);
	if ((mask & KeyModifierAltGr) != 0) {
		sendMask &= ~KeyModifierSuper;
	}
	mask &= ~KeyModifierAltGr;

	// update button state
	if (down) {
		m_keyState->onKey(button, true, mask);
	}
	else if (up) {
		if (!m_keyState->isKeyDown(button)) {
			// up event for a dead key.  throw it away.
			return false;
		}
		m_keyState->onKey(button, false, mask);
	}

	// send key events
	for (COSXKeyState::CKeyIDs::const_iterator i = keys.begin();
							i != keys.end(); ++i) {
		m_keyState->sendKeyEvent(getEventTarget(), down, isRepeat,
							*i, sendMask, 1, button);
	}

	return true;
}