diff --git a/src/lib/AutoTypeGlobalMacX.cpp b/src/lib/AutoTypeGlobalMacX.cpp new file mode 100644 index 0000000..a821d73 --- /dev/null +++ b/src/lib/AutoTypeGlobalMacX.cpp @@ -0,0 +1,266 @@ +/*************************************************************************** + * Copyright (C) 2009 Jeff Gibbons * + * Copyright (C) 2005-2008 by Tarek Saidi, Felix Geyer * + * tarek.saidi@arcor.de * + * * + * 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/AutoTypeGlobalMacX.h" +#include "mainwindow.h" +#include "lib/HelperMacX.h" +#include "dialogs/AutoTypeDlg.h" + +AutoTypeGlobal* autoType = NULL; + +static bool inHotKeyEvent = false; + +static OSStatus hotKeyHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData); + +void initAutoType(KeepassMainWindow* mainWin){ + autoType = new AutoTypeGlobalMacX(mainWin); +} + +AutoTypeGlobalMacX::AutoTypeGlobalMacX(KeepassMainWindow* mainWin) : AutoTypeMacX(mainWin){ + shortcut.key = 0; + oldCode = 0; + oldMod = 0; + inGlobalAutoType = false; + + // initialize hot key handling + hotKeyRef = NULL; + hotKeyID.signature = 'kpsx'; + hotKeyID.id = 1; + EventTypeSpec eventType; + eventType.eventClass = kEventClassKeyboard; + eventType.eventKind = kEventHotKeyPressed; + InstallApplicationEventHandler(&hotKeyHandler, 1, &eventType, this, NULL); +} + +OSStatus hotKeyHandler(EventHandlerCallRef, EventRef, void *userData){ + // ignore nextHandler - should not be called + if ((inHotKeyEvent) || HelperMacX::isFrontProcess(HelperMacX::getKeepassxPID())) return noErr; + inHotKeyEvent = true; + ((AutoTypeGlobalMacX*)userData)->performGlobal(); + inHotKeyEvent = false; + return noErr; +} + +void AutoTypeGlobalMacX::cancelled(){ + pid_t pid; + if (HelperMacX::getTargetWindowInfo(&pid, NULL, NULL, 0)) + HelperMacX::processToFront(pid); + else HelperMacX::processToFront(targetPID); + targetPID = 0; +} + +void AutoTypeGlobalMacX::updateKeymap(){ + AutoTypeMacX::updateKeymap(); + registerGlobalShortcut(shortcut); +} + +void AutoTypeGlobalMacX::perform(IEntryHandle* entry, bool hideWindow, int nr, bool wasLocked){ + if (inGlobalAutoType) + return; + inGlobalAutoType = true; + AutoTypeMacX::perform(entry, hideWindow, nr, wasLocked); + inGlobalAutoType = false; +} + +QStringList AutoTypeGlobalMacX::getAllWindowTitles(){ + QStringList titleList; + char windowName[256]; + pid_t keepassxPID = HelperMacX::getKeepassxPID(); + 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 (keepassxPID == 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, sizeof(windowName), kCFStringEncodingUTF8) || + (0 == windowName[0])) + continue; + titleList.append(QString::fromUtf8(windowName)); + } + CFRelease(windowInfo); + return titleList; +} + +void AutoTypeGlobalMacX::performGlobal(){ + if (AutoTypeDlg::isDialogVisible()) { + qWarning("Already performing auto-type, ignoring this one"); + return; + } + char titleUtf8[256]; + titleUtf8[0] = 0; + if (!HelperMacX::getTargetWindowInfo(&targetPID, &windowNumber, titleUtf8, sizeof(titleUtf8)) + || (0 == titleUtf8[0])) { + targetPID = 0; + return; + } + + bool wasLocked = mainWin->isLocked(); + if (wasLocked) + mainWin->OnUnLockWorkspace(); + + if (!mainWin->isOpened()) { + targetPID = 0; + return; + } + QString title = QString::fromUtf8(titleUtf8).toLower(); + + QList validEntries; + QList entryNumbers; + QList entries = mainWin->db->entries(); + QRegExp lineMatch("Auto-Type-Window(?:-(\\d+)|):([^\\n]+)", Qt::CaseInsensitive, QRegExp::RegExp2); + QDateTime now = QDateTime::currentDateTime(); + for (int i=0; iexpire()!=Date_Never && entries[i]->expire()comment(); + int offset = 0; + while ( (offset=lineMatch.indexIn(comment, offset))!=-1 ){ + QStringList captured = lineMatch.capturedTexts(); + offset += captured[0].length(); + int nr; + QString entryWindow; + bool valid; + if (captured.size()==2){ + nr = 0; + entryWindow = captured[1].trimmed().toLower(); + } + else{ + nr = captured[1].toInt(); + entryWindow = captured[2].trimmed().toLower(); + } + if (entryWindow.length()==0) continue; + + hasWindowEntry = true; + bool wildStart = (entryWindow[0]=='*'); + bool wildEnd = (entryWindow[entryWindow.size()-1]=='*'); + if (wildStart&&wildEnd){ + entryWindow.remove(0,1); + if (entryWindow.length()!=0){ + entryWindow.remove(entryWindow.size()-1,1); + valid = title.contains(entryWindow); + } + else + valid = true; + } + else if (wildStart){ + entryWindow.remove(0,1); + valid = title.endsWith(entryWindow); + } + else if (wildEnd){ + entryWindow.remove(entryWindow.size()-1,1); + valid = title.startsWith(entryWindow); + } + else { + valid = (title==entryWindow); + } + + if (valid){ + validEntries << entries[i]; + entryNumbers << nr; + break; + } + } + + if (!hasWindowEntry && config->entryTitlesMatch()){ + QString entryTitle = entries[i]->title().toLower(); + if (!entryTitle.isEmpty() && title.contains(entryTitle)){ + validEntries << entries[i]; + entryNumbers << 0; + } + } + } + + if (validEntries.size()==1){ + perform(validEntries[0],wasLocked,entryNumbers[0],wasLocked); + } + else if (validEntries.size()>1){ + AutoTypeDlg* dlg = new AutoTypeDlg(validEntries, entryNumbers, wasLocked); + HelperMacX::processToFront(HelperMacX::getKeepassxPID()); + dlg->show(); + } +} + +bool AutoTypeGlobalMacX::registerGlobalShortcut(const Shortcut& s){ + + if (s.key == 0) + return false; + + int code=HelperMacX::keysymToKeycode(s.key); + uint mod=HelperMacX::getShortcutModifierMask(s); + + if (s.key==shortcut.key && s.ctrl==shortcut.ctrl && s.shift==shortcut.shift && s.alt==shortcut.alt + && s.altgr==shortcut.altgr && s.win==shortcut.win && code==oldCode && mod==oldMod) + return true; + + // need to unregister old before registering new + unregisterGlobalShortcut(); + OSStatus status = RegisterEventHotKey(code, mod, hotKeyID, GetApplicationEventTarget(), 0, &hotKeyRef); + if (noErr == status) { + shortcut = s; + oldCode = code; + oldMod = mod; + return true; + } else { + qWarning("Error registering global shortcut: %d", (int)status); + RegisterEventHotKey(oldCode, oldMod, hotKeyID, GetApplicationEventTarget(), 0, &hotKeyRef); + return false; + } +} + +void AutoTypeGlobalMacX::unregisterGlobalShortcut(){ + if (shortcut.key==0) return; + + if (NULL != hotKeyRef) { + UnregisterEventHotKey(hotKeyRef); + hotKeyRef = NULL; + } + + shortcut.key = 0; + oldCode = 0; + oldMod = 0; +} + +QString AutoTypeGlobalMacX::getRootGroupName(IEntryHandle* entry){ + IGroupHandle* group = entry->group(); + int level = group->level(); + for (int i=0; iparent(); + + return group->title(); +} diff --git a/src/lib/AutoTypeGlobalMacX.h b/src/lib/AutoTypeGlobalMacX.h new file mode 100644 index 0000000..f2d85fd --- /dev/null +++ b/src/lib/AutoTypeGlobalMacX.h @@ -0,0 +1,52 @@ +/*************************************************************************** + * Copyright (C) 2009 Jeff Gibbons * + * Copyright (C) 2005-2008 by Tarek Saidi, Felix Geyer * + * tarek.saidi@arcor.de * + * * + * 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. * + ***************************************************************************/ + +#ifndef _AUTOTYPEGLOBALMACX_H_ +#define _AUTOTYPEGLOBALMACX_H_ + +#include "lib/AutoTypeMacX.h" + +class AutoTypeGlobalMacX : public AutoTypeMacX, public AutoTypeGlobal{ + public: + AutoTypeGlobalMacX(KeepassMainWindow* mainWin); + void perform(IEntryHandle* entry, bool hideWindow=true, int nr=0, bool wasLocked=false); + void performGlobal(); + bool registerGlobalShortcut(const Shortcut& s); + void unregisterGlobalShortcut(); + QStringList getAllWindowTitles(); + void cancelled(); + void updateKeymap(); + inline int maskShift() { return shift_mask; }; + inline int maskCtrl() { return ctrl_mask; }; + inline int maskAlt() { return alt_mask; }; + inline int maskAltGr() { return altgr_mask; }; + inline int maskMeta() { return meta_mask; }; + + private: + QString getRootGroupName(IEntryHandle* entry); + + int oldCode; + uint oldMod; + + EventHotKeyRef hotKeyRef; + EventHotKeyID hotKeyID; +}; + +#endif // _AUTOTYPEGLOBALMACX_H_ diff --git a/src/lib/AutoTypeMacX.cpp b/src/lib/AutoTypeMacX.cpp new file mode 100644 index 0000000..a7050db --- /dev/null +++ b/src/lib/AutoTypeMacX.cpp @@ -0,0 +1,554 @@ +/*************************************************************************** + * Copyright (C) 2009-2010 Jeff Gibbons * + * Copyright (C) 2005-2008 by Tarek Saidi, Felix Geyer * + * tarek.saidi@arcor.de * + * * + * 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/AutoTypeMacX.h" +#include "lib/HelperMacX.h" +#include "mainwindow.h" + +#ifndef GLOBAL_AUTOTYPE +AutoType* autoType = NULL; + +void initAutoType(KeepassMainWindow* mainWin) { + autoType = new AutoTypeMacX(mainWin); +} +#endif + +// resusable events +static CGEventRef keyEvent = CGEventCreateKeyboardEvent(NULL, 0, true); +static CGEventRef unicodeEvent = CGEventCreateKeyboardEvent(NULL, 0, true); + +#define UNICODE_BUFFER_SIZE 20 // max from documentation +static UniChar unicodeBuffer[UNICODE_BUFFER_SIZE]; +static UniCharCount unicodePtr = 0; + +AutoTypeAction::AutoTypeAction(AutoTypeActionType t, quint16 data) + : type(t), data(data){} +AutoTypeAction::AutoTypeAction(AutoTypeActionType t, KeycodeWithMods keycodeWithMods) + : type(t), keycodeWithMods(keycodeWithMods){} + +AutoTypeMacX::AutoTypeMacX(KeepassMainWindow* mainWin){ + this->mainWin = mainWin; + inAutoType = false; + targetPID = 0; + + ctrl_mask = controlKey; + shift_mask = shiftKey; + alt_mask = optionKey; + meta_mask = cmdKey; + altgr_mask = 0; + + updateKeymap(); +} + +void AutoTypeMacX::updateKeymap(){ + HelperMacX::initUnicodeToKeycodeWithModsMap(); +} + +void AutoTypeMacX::perform(IEntryHandle* entry, bool hideWindow, int nr, bool wasLocked){ + if (inAutoType) + return; + if ((0 == targetPID) && !HelperMacX::getTargetWindowInfo(&targetPID, &windowNumber, NULL, 0)) { + targetPID = 0; + return; + } + inAutoType = true; + + QString indexStr; + if (nr==0) + indexStr = "Auto-Type:"; + else + indexStr = QString("Auto-Type-%1:").arg(nr); + QString str; + QString comment=entry->comment(); + int c=comment.count(indexStr, Qt::CaseInsensitive); + if(c>1) { + qWarning("More than one 'Auto-Type:' key sequence found.\nAllowed is only one per entry."); + targetPID = 0; + inAutoType = false; + return; + } + else if (c==1) { + int start = comment.indexOf(indexStr,0,Qt::CaseInsensitive) + indexStr.length(); + int end = comment.indexOf("\n", start); + if (end == -1) + end = comment.length(); + + str=comment.mid(start,end-start).trimmed(); + if (str.isEmpty()) { + targetPID = 0; + inAutoType = false; + return; + } + } + else { + bool usernameEmpty = entry->username().trimmed().isEmpty(); + SecString password=entry->password(); + password.unlock(); + bool passwordEmpty = password.string().trimmed().isEmpty(); + if (usernameEmpty && passwordEmpty) { + targetPID = 0; + inAutoType = false; + return; + } + else if (usernameEmpty) + str="{PASSWORD}{ENTER}"; + else if (passwordEmpty) + str="{USERNAME}{ENTER}"; + else + str="{USERNAME}{TAB}{PASSWORD}{ENTER}"; + } + + HelperMacX::processToFront(targetPID); + appSignature = HelperMacX::getProcessSignature(targetPID); + checkWindowType(); + targetPID = 0; + if (hideWindow) + mainWin->hide(); + + QList Keys; + for(int i=0;i=str.size()){ + qWarning("Syntax Error in Auto-Type sequence near character %d\nFound '{' without closing '}'", i+10); + break; + } + templateToKeysyms(tmpl.toLower(),Keys,entry); + continue; + } + else{ + Keys << AutoTypeAction(SendUnicodeAction, str[i].unicode()); + } + } + + QApplication::processEvents(); + sleepTime(config->autoTypePreGap()); + + QString type; + for(int i=0;ilockOnMinimize()){ + if (hideWindow || wasLocked){ + if ( !(config->showSysTrayIcon() && config->minimizeTray()) ) + mainWin->showMinimized(); + else + mainWin->OnUnLockWorkspace(); + } + } + else{ + if (hideWindow && !(config->showSysTrayIcon() && config->minimizeTray()) ) + mainWin->showMinimized(); + } + + inAutoType = false; +} + +void AutoTypeMacX::sleepTime(int msec){ + if (msec==0) return; + timespec timeOut, remains; + timeOut.tv_sec = msec/1000; + timeOut.tv_nsec = (msec%1000)*1000000; + nanosleep(&timeOut, &remains); +} + +void AutoTypeMacX::templateToKeysyms(const QString& tmpl, QList& keys,IEntryHandle* entry){ + //tmpl must be lower case!!! + if(!tmpl.compare("title")){ + stringToKeysyms(entry->title(),keys); + return; + } + if(!tmpl.compare("username")){ + stringToKeysyms(entry->username(),keys); + return; + } + if(!tmpl.compare("url")){ + stringToKeysyms(entry->url(),keys); + return; + } + if(!tmpl.compare("password")){ + SecString password=entry->password(); + password.unlock(); + stringToKeysyms(password,keys); + return; + } + if(!tmpl.compare("space")){ + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_Space, 0}); + return; + } + + if(!tmpl.compare("backspace") || !tmpl.compare("bs") || !tmpl.compare("bksp")){ + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_Delete, 0}); + return; + } + +// if(!tmpl.compare("break")){ +// keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){?, 0}); +// return; +// } + + if(!tmpl.compare("capslock")){ + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_CapsLock, 0}); + return; + } + + if(!tmpl.compare("del") || !tmpl.compare("delete")){ + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_ForwardDelete, 0}); + return; + } + + if(!tmpl.compare("end")){ + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_End, 0}); + return; + } + + if(!tmpl.compare("enter")){ + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_Return, 0}); + return; + } + + if(!tmpl.compare("esc")){ + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_Escape, 0}); + return; + } + + if(!tmpl.compare("help")){ + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_Help, 0}); + return; + } + + if(!tmpl.compare("home")){ + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_Home, 0}); + return; + } + + if(!tmpl.compare("insert") || !tmpl.compare("ins")){ + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_Help, 0}); + return; + } + + if(!tmpl.compare("numlock")){ + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_ANSI_KeypadClear, 0}); + return; + } + +// if(!tmpl.compare("scroll")){ +// keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){?, 0}); +// return; +// } + + if(!tmpl.compare("pgdn")){ + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_PageDown, 0}); + return; + } + + if(!tmpl.compare("pgup")){ + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_PageUp, 0}); + return; + } + + if(!tmpl.compare("prtsc")){ + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_F13, 0}); + return; + } + + if(!tmpl.compare("up")){ + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_UpArrow, 0}); + return; + } + + if(!tmpl.compare("down")){ + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_DownArrow, 0}); + return; + } + + if(!tmpl.compare("left")){ + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_LeftArrow, 0}); + return; + } + + if(!tmpl.compare("right")){ + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_RightArrow, 0}); + return; + } + + if(!tmpl.compare("f1")){ + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_F1, 0}); + return; + } + + if(!tmpl.compare("f2")){ + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_F2, 0}); + return; + } + + if(!tmpl.compare("f3")){ + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_F3, 0}); + return; + } + + if(!tmpl.compare("f4")){ + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_F4, 0}); + return; + } + + if(!tmpl.compare("f5")){ + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_F5, 0}); + return; + } + + if(!tmpl.compare("f6")){ + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_F6, 0}); + return; + } + + if(!tmpl.compare("f7")){ + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_F7, 0}); + return; + } + + if(!tmpl.compare("f8")){ + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_F8, 0}); + return; + } + + if(!tmpl.compare("f9")){ + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_F9, 0}); + return; + } + + if(!tmpl.compare("f10")){ + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_F10, 0}); + return; + } + + if(!tmpl.compare("f11")){ + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_F11, 0}); + return; + } + + if(!tmpl.compare("f12")){ + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_F12, 0}); + return; + } + + if(!tmpl.compare("f13")){ + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_F13, 0}); + return; + } + + if(!tmpl.compare("f14")){ + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_F14, 0}); + return; + } + + if(!tmpl.compare("f15")){ + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_F15, 0}); + return; + } + + if(!tmpl.compare("f16")){ + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_F16, 0}); + return; + } + + if(!tmpl.compare("add") || !tmpl.compare("plus")){ + keys << AutoTypeAction(SendUnicodeAction, '+'); + return; + } + + if(!tmpl.compare("subtract")){ + keys << AutoTypeAction(SendUnicodeAction, '-'); + return; + } + + if(!tmpl.compare("multiply")){ + keys << AutoTypeAction(SendUnicodeAction, '*'); + return; + } + + if(!tmpl.compare("divide")){ + keys << AutoTypeAction(SendUnicodeAction, '/'); + return; + } + + if(!tmpl.compare("at")){ + keys << AutoTypeAction(SendUnicodeAction, '@'); + return; + } + + if(!tmpl.compare("percent")){ + keys << AutoTypeAction(SendUnicodeAction, '%'); + return; + } + + if(!tmpl.compare("caret")){ + keys << AutoTypeAction(SendUnicodeAction, '^'); + return; + } + + if(!tmpl.compare("tilde")){ + keys << AutoTypeAction(SendUnicodeAction, '~'); + return; + } + + if(!tmpl.compare("leftbrace")){ + keys << AutoTypeAction(SendUnicodeAction, '{'); + return; + } + + if(!tmpl.compare("rightbrace")){ + keys << AutoTypeAction(SendUnicodeAction, '}'); + return; + } + + if(!tmpl.compare("leftparen")){ + keys << AutoTypeAction(SendUnicodeAction, '('); + return; + } + + if(!tmpl.compare("rightparen")){ + keys << AutoTypeAction(SendUnicodeAction, ')'); + return; + } + + if(!tmpl.compare("tab")){ + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_Tab, 0}); + return; + } + + if(tmpl.startsWith("unicode ") && tmpl.length() > 8){ + char* str = tmpl.right(tmpl.length() - 8).toUtf8().data(); + int n; + uint16 unicode; + while (1 == sscanf(str, " %hi%n", &unicode, &n)) { + keys << AutoTypeAction(SendUnicodeAction, unicode); + str += n; + } + return; + } + + if(!tmpl.compare("clearfield")){ + if(('x11a' == appSignature) || ('????' == appSignature)){ + // ^A to beginning of line then ^K kill to end of line for X11 or Terminal.app + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_ANSI_A, controlKey >> 8}); + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_ANSI_K, controlKey >> 8}); + } else { + // Cmd-A to select all then delete for everything else + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_ANSI_A, cmdKey >> 8}); + keys << AutoTypeAction(SendKeycodeAction, (KeycodeWithMods){kVK_ForwardDelete, 0}); + } + return; + } + + if(tmpl.startsWith("macsendkeycodes")){ + onlySendKeycodes = true; + } + + if(tmpl.startsWith("delay ") && tmpl.length()>6){ + bool ok; + quint16 delay = tmpl.right(tmpl.length()-6).toInt(&ok); + if (ok && delay>0 && delay<=10000) + keys << AutoTypeAction(DelayAction, delay); + return; + } +} + +void AutoTypeMacX::stringToKeysyms(const QString& string,QList& KeySymList){ + for(int i=0; i + +typedef quint32 KeySym; +#define NoSymbol (KeySym)0 +#define NoKeycode (uint16)-1 + +struct KeycodeWithMods { + uint16 keycode; + uint16 mods; +}; + +enum AutoTypeActionType{ + SendUnicodeAction, SendKeycodeAction, DelayAction +}; + +struct AutoTypeAction{ + AutoTypeAction(AutoTypeActionType t, quint16 data); + AutoTypeAction(AutoTypeActionType t, KeycodeWithMods keycodeWithMods); + AutoTypeActionType type; + union { + quint16 data; + KeycodeWithMods keycodeWithMods; + }; +}; + +class AutoTypeMacX : public AutoType { + public: + AutoTypeMacX(KeepassMainWindow* mainWin); + void perform(IEntryHandle* entry, bool hideWindow=true, int nr=0, bool wasLocked=false); + virtual void updateKeymap(); + + protected: + void sleepTime(int msec); + inline void sleepKeyStrokeDelay(){ sleepTime(config->autoTypeKeyStrokeDelay()); }; + void templateToKeysyms(const QString& Template, QList& KeySymList,IEntryHandle* entry); + void stringToKeysyms(const QString& string,QList& KeySymList); + void sendKeycode(KeycodeWithMods keycodeWithMods); + void sendUnicode(KeySym keysym); + void flushUnicode(); + + KeepassMainWindow* mainWin; + + int ctrl_mask; + int shift_mask; + int alt_mask; + int meta_mask; + int altgr_mask; + bool inGlobalAutoType; + pid_t targetPID; + int windowNumber; + + private: + bool inAutoType; + OSType appSignature; + bool onlySendKeycodes; + void checkWindowType(); + void keyDownUp(CGEventRef theEvent); +}; + +#endif // _AUTOTYPEMACX_H_ diff --git a/src/lib/HelperMacX.cpp b/src/lib/HelperMacX.cpp new file mode 100644 index 0000000..c1d1abe --- /dev/null +++ b/src/lib/HelperMacX.cpp @@ -0,0 +1,238 @@ +/*************************************************************************** + * 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; + processInfoRec.processInfoLength = sizeof(processInfoRec); + processInfoRec.processAppSpec = NULL; + processInfoRec.processName = NULL; + 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; + } +} diff --git a/src/lib/HelperMacX.h b/src/lib/HelperMacX.h new file mode 100644 index 0000000..3b5031b --- /dev/null +++ b/src/lib/HelperMacX.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * 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. * + ***************************************************************************/ + +#ifndef HELPERMACX_H +#define HELPERMACX_H + +#include "lib/AutoTypeMacX.h" + +class HelperMacX{ + public: +#ifdef GLOBAL_AUTOTYPE + static uint getShortcutModifierMask(const Shortcut& s); + static Boolean isFrontProcess(pid_t pid); +#endif + static pid_t getKeepassxPID(); + static Boolean getTargetWindowInfo(pid_t *pidPtr, int *windowNumberPtr, char* windowName, int maxWindowNameSize); + static OSType getProcessSignature(pid_t pid); + static void initUnicodeToKeycodeWithModsMap(); + static KeySym keycodeToKeysym(uint keycode); + static uint keysymToKeycode(KeySym keysym); + static KeycodeWithMods keysymToKeycodeWithMods(KeySym keysym); + static void processToFront(pid_t pid); +}; + +#endif // HELPERMACX_H diff --git a/src/res/docs/quickstart.html b/src/res/docs/quickstart.html index 338210e..50922a3 100644 --- a/src/res/docs/quickstart.html +++ b/src/res/docs/quickstart.html @@ -234,7 +234,8 @@

Also note that the use of {CLEARFIELD} may require the user to define - a somewhat larger Key Stroke Delay in Preferences. + a somewhat larger Key Stroke Delay in Preferences when specified for a site + with flash-based login fields.