/*************************************************************************** * Copyright (C) 2009 by Jeff Gibbons * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; version 2 of the License. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "lib/HelperMacX.h" #include static pid_t keepassxPID; #ifdef GLOBAL_AUTOTYPE #include "lib/AutoTypeGlobalMacX.h" uint HelperMacX::getShortcutModifierMask(const Shortcut& s){ AutoTypeGlobalMacX* autoTypeGlobal = static_cast(autoType); uint mod = 0; if (s.ctrl) mod |= autoTypeGlobal->maskCtrl(); if (s.shift) mod |= autoTypeGlobal->maskShift(); if (s.alt) mod |= autoTypeGlobal->maskAlt(); if (s.altgr) mod |= autoTypeGlobal->maskAltGr(); if (s.win) mod |= autoTypeGlobal->maskMeta(); return mod; } Boolean HelperMacX::isFrontProcess(pid_t pid){ Boolean result; ProcessSerialNumber pidPSN; ProcessSerialNumber frontPSN; OSStatus status = GetProcessForPID(pid, &pidPSN); if (noErr != status) { qWarning("HelperMacX::isFrontProcess: GetProcessForPID error for pid %d: %d", pid, (int)status); return false; } GetFrontProcess(&frontPSN); SameProcess(&pidPSN, &frontPSN, &result); return result; } #endif pid_t HelperMacX::getKeepassxPID(){ if (0 == keepassxPID) { ProcessSerialNumber processSerialNumber; GetCurrentProcess(&processSerialNumber); GetProcessPID(&processSerialNumber, &keepassxPID); } return keepassxPID; } Boolean HelperMacX::getTargetWindowInfo(pid_t *pidPtr, int *windowNumberPtr, char* windowName, int maxWindowNameSize){ char windowNameBuffer[256]; if (NULL == windowName) { windowName = windowNameBuffer; maxWindowNameSize = sizeof(windowNameBuffer); } // get info for on screen windows (excluding desktop elements) in top to bottom order CFArrayRef windowInfo = CGWindowListCopyWindowInfo( kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements, kCGNullWindowID); CFIndex windowCount = CFArrayGetCount(windowInfo); for (CFIndex i = 0; i < windowCount; i++){ CFDictionaryRef window = (CFDictionaryRef)CFArrayGetValueAtIndex(windowInfo, i); // only want windows in layer 0 CFNumberRef windowLayerRef = (CFNumberRef)CFDictionaryGetValue(window, kCGWindowLayer); int windowLayer = -1; CFNumberGetValue(windowLayerRef, kCFNumberIntType, &windowLayer); if (0 != windowLayer) continue; // get the pid owning this window CFNumberRef pidRef = (CFNumberRef)CFDictionaryGetValue(window, kCGWindowOwnerPID); pid_t pid = -1; CFNumberGetValue(pidRef, kCFNumberIntType, &pid); // skip KeePassX windows if (getKeepassxPID() == pid) continue; // get window name; continue if no name CFStringRef windowNameRef = (CFStringRef)CFDictionaryGetValue(window, kCGWindowName); if (!windowNameRef) continue; windowName[0] = 0; if (!CFStringGetCString(windowNameRef, windowName, maxWindowNameSize, kCFStringEncodingUTF8) || (0 == windowName[0])) continue; if (NULL != pidPtr) *pidPtr = pid; if (NULL != windowNumberPtr) { CFNumberRef windowNumberRef = (CFNumberRef)CFDictionaryGetValue(window, kCGWindowNumber); CFNumberGetValue(windowNumberRef, kCGWindowIDCFNumberType, windowNumberPtr); } CFRelease(windowInfo); return true; } CFRelease(windowInfo); return false; } OSType HelperMacX::getProcessSignature(pid_t pid){ OSErr err; ProcessSerialNumber processSerialNumber; ProcessInfoRec processInfoRec; memset(&processInfoRec, 0, sizeof(processInfoRec)); processInfoRec.processInfoLength = sizeof(processInfoRec); err = GetProcessForPID(pid, &processSerialNumber); if (noErr != err) { qWarning("HelperMacX::getProcessSignature: GetProcessForPID error for pid %d: %d", pid, err); return 0; } err = GetProcessInformation(&processSerialNumber, &processInfoRec); if (noErr != err) { qWarning("HelperMacX::getProcessSignature: GetProcessInformation error for pid %d: %d\n", pid, err); return 0; } return processInfoRec.processSignature; } static uint orderedModifiers[] = { 0, ( shiftKey ) >> 8, (controlKey ) >> 8, ( optionKey ) >> 8, ( cmdKey ) >> 8, ( shiftKey | controlKey ) >> 8, ( shiftKey | optionKey ) >> 8, ( shiftKey | cmdKey ) >> 8, (controlKey | optionKey ) >> 8, (controlKey | cmdKey ) >> 8, ( optionKey | cmdKey ) >> 8, ( shiftKey | controlKey | optionKey ) >> 8, ( shiftKey | controlKey | cmdKey ) >> 8, ( shiftKey | optionKey | cmdKey ) >> 8, (controlKey | optionKey | cmdKey ) >> 8, ( shiftKey | controlKey | optionKey | cmdKey ) >> 8 }; static std::map unicodeToKeycodeWithModsMap; void HelperMacX::initUnicodeToKeycodeWithModsMap(){ unicodeToKeycodeWithModsMap.clear(); TISInputSourceRef inputSourceRef = TISCopyCurrentKeyboardInputSource(); if (NULL == inputSourceRef) { qWarning("HelperMacX::initUnicodeToKeycodeWithModsMap: inputSourceRef is NULL"); return; } CFDataRef unicodeKeyLayoutDataRef = (CFDataRef)TISGetInputSourceProperty(inputSourceRef, kTISPropertyUnicodeKeyLayoutData); if (NULL == unicodeKeyLayoutDataRef) { qWarning("HelperMacX::initUnicodeToKeycodeWithModsMap: unicodeKeyLayoutDataRef is NULL"); return; } UCKeyboardLayout *unicodeKeyLayoutDataPtr = (UCKeyboardLayout*)CFDataGetBytePtr(unicodeKeyLayoutDataRef); UInt32 deadKeyState; UniChar unicodeString[8]; UniCharCount len; for (int m = 0; m < 16; m++) { uint mods = orderedModifiers[m]; for (uint keycode = 0; keycode < 0x80; keycode++) { deadKeyState = 0; len = 0; OSStatus status = UCKeyTranslate(unicodeKeyLayoutDataPtr, keycode, kUCKeyActionDown, mods, LMGetKbdType(), kUCKeyTranslateNoDeadKeysMask, &deadKeyState, sizeof(unicodeString), &len, unicodeString); if (noErr != status) { qWarning("HelperMacX::initUnicodeToKeycodeWithModsMap: UCKeyTranslate error: %d keycode 0x%02X modifiers 0x%02X", (int)status, keycode, mods); continue; } // store if only one char and not already in store if ((1 != len) || (0 < unicodeToKeycodeWithModsMap.count(unicodeString[0]))) continue; unicodeToKeycodeWithModsMap[unicodeString[0]] = (KeycodeWithMods){ keycode, mods }; } } } KeySym HelperMacX::keycodeToKeysym(uint keycode){ TISInputSourceRef inputSourceRef = TISCopyCurrentKeyboardInputSource(); if (NULL == inputSourceRef) { qWarning("HelperMacX::keycodeToKeysym: inputSourceRef is NULL"); return NoSymbol; } CFDataRef unicodeKeyLayoutDataRef = (CFDataRef)TISGetInputSourceProperty(inputSourceRef, kTISPropertyUnicodeKeyLayoutData); if (NULL == unicodeKeyLayoutDataRef) { CFRelease(inputSourceRef); qWarning("HelperMacX::keycodeToKeysym: unicodeKeyLayoutDataRef is NULL"); return NoSymbol; } UCKeyboardLayout *unicodeKeyLayoutDataPtr = (UCKeyboardLayout*)CFDataGetBytePtr(unicodeKeyLayoutDataRef); UInt32 deadKeyState = 0; UniChar unicodeString[8]; UniCharCount len = 0; OSStatus status = UCKeyTranslate(unicodeKeyLayoutDataPtr, keycode, kUCKeyActionDown, 0, LMGetKbdType(), kUCKeyTranslateNoDeadKeysMask, &deadKeyState, sizeof(unicodeString), &len, unicodeString); CFRelease(inputSourceRef); if (noErr != status) { qWarning("HelperMacX::keycodeToKeysym: UCKeyTranslate error: %d", (int)status); return NoSymbol; } return 1 != len ? NoSymbol : unicodeString[0]; } uint HelperMacX::keysymToKeycode(KeySym keysym){ return keysymToKeycodeWithMods(keysym).keycode; } static const KeycodeWithMods NoKeycodeWithMods = (KeycodeWithMods){ NoKeycode, 0 }; KeycodeWithMods HelperMacX::keysymToKeycodeWithMods(KeySym keysym){ return 0 == unicodeToKeycodeWithModsMap.count(keysym) ? NoKeycodeWithMods : unicodeToKeycodeWithModsMap[keysym]; } void HelperMacX::processToFront(pid_t pid){ OSStatus status; ProcessSerialNumber processSerialNumber; status = GetProcessForPID(pid, &processSerialNumber); if (noErr != status) { qWarning("HelperMacX::processToFront: GetProcessForPID error for pid %d: %d", pid, (int)status); return; } status = SetFrontProcessWithOptions(&processSerialNumber, kSetFrontProcessFrontWindowOnly); if (noErr != status) { qWarning("HelperMacX::processToFront: SetFrontProcessWithOptions for pid %d: %d", pid, (int)status); return; } }