Updating quick-start guide and performed svn add for OS X auto-type files that were previously left out in revision 383.

git-svn-id: https://svn.code.sf.net/p/keepassx/code/trunk@384 b624d157-de02-0410-bad0-e51aec6abb33
master
bdmayes 14 years ago
parent c5c10b6220
commit 794a378927
  1. 266
      src/lib/AutoTypeGlobalMacX.cpp
  2. 52
      src/lib/AutoTypeGlobalMacX.h
  3. 554
      src/lib/AutoTypeMacX.cpp
  4. 83
      src/lib/AutoTypeMacX.h
  5. 238
      src/lib/HelperMacX.cpp
  6. 40
      src/lib/HelperMacX.h
  7. 3
      src/res/docs/quickstart.html

@ -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<IEntryHandle*> validEntries;
QList<int> entryNumbers;
QList<IEntryHandle*> entries = mainWin->db->entries();
QRegExp lineMatch("Auto-Type-Window(?:-(\\d+)|):([^\\n]+)", Qt::CaseInsensitive, QRegExp::RegExp2);
QDateTime now = QDateTime::currentDateTime();
for (int i=0; i<entries.size(); i++){
if ( (entries[i]->expire()!=Date_Never && entries[i]->expire()<now) ||
(getRootGroupName(entries[i]).compare("backup",Qt::CaseInsensitive)==0)
){
continue;
}
bool hasWindowEntry=false;
QString comment = entries[i]->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; i<level; i++)
group = group->parent();
return group->title();
}

@ -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_

@ -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<AutoTypeAction> Keys;
for(int i=0;i<str.size();i++){
if(str[i]=='{'){
QString tmpl;
i++;
while(str[i]!='}' && i<str.size()){
tmpl += str[i];
i++;
}
if(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;i<Keys.size();i++){
int currentWindowNumber;
if (!HelperMacX::getTargetWindowInfo(NULL, &currentWindowNumber, NULL, 0)
|| (windowNumber != currentWindowNumber)) {
qWarning("Focus window changed, interrupting auto-type");
unicodePtr = 0;
break;
}
if (Keys[i].type==SendKeycodeAction){
sendKeycode(Keys[i].keycodeWithMods);
}
else if (Keys[i].type==SendUnicodeAction){
sendUnicode(Keys[i].data);
}
else if (Keys[i].type==DelayAction){
flushUnicode();
QApplication::processEvents();
sleepTime(Keys[i].data);
}
}
flushUnicode();
if (config->lockOnMinimize()){
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<AutoTypeAction>& 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<AutoTypeAction>& KeySymList){
for(int i=0; i<string.length();i++)
KeySymList << AutoTypeAction(SendUnicodeAction, string[i].unicode());
}
void AutoTypeMacX::keyDownUp(CGEventRef theEvent){
// posting Key Down/Up events also annoyingly sets mouse location so must first get
// current mouse location and set it in the event
CGEventRef eventLocation = CGEventCreate(NULL);
CGEventSetLocation(theEvent, CGEventGetLocation(eventLocation));
CFRelease(eventLocation);
CGEventSetType(theEvent, kCGEventKeyDown);
CGEventPost(kCGHIDEventTap, theEvent);
CGEventSetType(theEvent, kCGEventKeyUp);
CGEventPost(kCGHIDEventTap, theEvent);
}
void AutoTypeMacX::sendKeycode(KeycodeWithMods keycodeWithMods){
flushUnicode();
uint keycode = keycodeWithMods.keycode;
uint mods = keycodeWithMods.mods << 8;
uint flags = 0;
if (0 != ( shiftKey & mods)) flags |= kCGEventFlagMaskShift;
if (0 != (controlKey & mods)) flags |= kCGEventFlagMaskControl;
if (0 != ( optionKey & mods)) flags |= kCGEventFlagMaskAlternate;
if (0 != ( cmdKey & mods)) flags |= kCGEventFlagMaskCommand;
CGEventSetIntegerValueField(keyEvent, kCGKeyboardEventKeycode, (int64_t)keycode);
CGEventSetFlags(keyEvent, flags);
keyDownUp(keyEvent);
sleepKeyStrokeDelay();
}
void AutoTypeMacX::sendUnicode(KeySym keysym){
if (onlySendKeycodes) {
KeycodeWithMods keycodeWithMods = HelperMacX::keysymToKeycodeWithMods(keysym);
if (NoKeycode == keycodeWithMods.keycode) return;
sendKeycode(keycodeWithMods);
return;
}
unicodeBuffer[unicodePtr++] = keysym;
if (UNICODE_BUFFER_SIZE == unicodePtr) flushUnicode();
}
void AutoTypeMacX::flushUnicode(){
if (0 == unicodePtr) return;
CGEventKeyboardSetUnicodeString(unicodeEvent, unicodePtr, unicodeBuffer);
keyDownUp(unicodeEvent);
unicodePtr = 0;
sleepKeyStrokeDelay();
}
void AutoTypeMacX::checkWindowType(){
// sendUnicode does not work with X11 windows so revert to only send keycodes
// this can be extended if other types of windows prove to fail for unicode
onlySendKeycodes = 'x11a' == appSignature;
}

@ -0,0 +1,83 @@
/***************************************************************************
* 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 _AUTOTYPEMACX_H_
#define _AUTOTYPEMACX_H_
#include "lib/AutoType.h"
#include <Carbon/Carbon.h>
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<AutoTypeAction>& KeySymList,IEntryHandle* entry);
void stringToKeysyms(const QString& string,QList<AutoTypeAction>& 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_

@ -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 <map>
static pid_t keepassxPID;
#ifdef GLOBAL_AUTOTYPE
#include "lib/AutoTypeGlobalMacX.h"
uint HelperMacX::getShortcutModifierMask(const Shortcut& s){
AutoTypeGlobalMacX* autoTypeGlobal = static_cast<AutoTypeGlobalMacX*>(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<uint,KeycodeWithMods> 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;
}
}

@ -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

@ -234,7 +234,8 @@
</p>
<p>
Also note that the use of <tt>{CLEARFIELD}</tt> 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.
</p>
<!--