Logitech R800 mit Apple Keynote

Der Logitech R800 ist ein kleiner schicker Presenter, der auch gut funktioniert. Am Computer gibt er sich einfach als normale Tastatur aus und schickt F5, ESC oder B, je nach dem welche Taste man drückt.

Normalerweise bin ich ja unglaublich glücklich, wenn USB Geräte keinen eigenen Treiber brauchen und damit dann weniger Probleme bereiten (FTDI Serial sei da mal angeprangert). Doch leider sind diese Tastenkombinationen inkompatible mit Keynote.

Glücklicherweise gibt es KeyRemap4MacBook, welches beliebiges Remapping erlaubt. Ziemlich schick.

Nach der Installation packt man einfach folgendes in ~/Library/Application Support/KeyRemap4MacBook/private.xml und aktiviert es in den KeyRemap4MacBook Einstellungen.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0"?>
<root>
    <devicevendordef>
        <vendorname>LOGITECH</vendorname>
        <vendorid>0x046d</vendorid>
    </devicevendordef>

    <deviceproductdef>
        <productname>R800</productname>
        <productid>0x0c52d</productid>
    </deviceproductdef>

    <item>
        <name>Logitec R800 for Keynote</name>
        <identifier>private.logitech.r800</identifier>
        <device_only>DeviceVendor::LOGITECH, DeviceProduct::R800</device_only>
        <autogen>__KeyToKey__  KeyCode::F5, KeyCode::P, ModifierFlag::COMMAND_L | ModifierFlag::OPTION_L</autogen>
        <autogen>__KeyToKey__  KeyCode::DOT, KeyCode::B</autogen>
    </item>
</root>

Volla, die Tasten passen für Keynote. Es werden im übrigen nur die Tasten für den R800 umgemappt, so das normale Tastaturen ohne Probleme weiter verwendet werden können. Das ganze sollte sich auch ohne Umstände auf andere Presenter übertragen lassen.

Fröhliches Präsentieren :)

Gestern bin ich wohl dem Alptraum eines Programmierers: begegnet Das Programm macht unvorhersehbare Sachen. Aufgefallen war es mir als eine globale Variable in dem Programm (iOS-App) auf einmal 0 wurde. An sich nicht weiter verwunderlich, aber wenn es genau 5 Zeilen im ganzen Programm gibt, die darauf zugreifen können, ist das schon etwas seltsam. An meinem Mac wurde es dann noch lustiger… ein harmloser Methoden Aufruf endete tief im PrinterKit.

Nach dem ich gemerkt habe, das ich keinen Schimmer habe wo dieses komische Problem herkommt habe ich mich an einen guten Freund gewendet: git.

Ich wusste, dass der Fehler irgendwann noch nicht existiert hat und hier konnte ich mit git bisect ansetzen.

Mit git bisect sagt man git welche Version garantiert funktioniert und welche ebend nicht. Git hilft dir nun herauszufinden mit welchem Commit der Fehler eingeschleppt wurde.

Das ganze geht auch ziemlich flott. Ca. 5 Schritte waren für die 33 Commits zwischen meinem Bad-Commit und Good-Commit nötig, in der Schule haben wir das liebevoll Intervall-Halbierungs-Verfahren genannt. Aber genug vom vorgeblabber, so geht’s:

Als erstes initialisieren wir das git bisect system.

1
2
$ git bisect start
$ git bisect good <commit>

Bei dem zweiten Befehl geben wir einen Commit an, von dem wir wissen das er den Fehler nicht enthält. Nun sagen wir git noch, in welchem Commit auf jeden Fall der Fehler existiert:

1
$ git bisect bad <commit>

Der aufmerksame Beobachter wird festgestellt haben, dass git bisect nun einen anderen Commit ausgecheckt hat. Nun sind wir an der Reihe. Wir müssen nun testen, ob der Commit auch den Fehler enthält oder nicht. Sollte der Fehler auch hier bestehen teilen wir das git mit:

1
$ git bisect bad

ansonsten, wenn alles funktioniert:

1
$ git bisect good

Dies wiederholt man nun solange bis git bisect irgendwann stolz verkündet, welcher Commit den Fehler erschaffen hat. Dann bleibt nur den Commit zu merken, anzuschauen was dort verändert wurde und den Fehler in den (hoffentlich wenigen) Zeilen zu finden.

Zum Schluss müssen wir noch git bisect beenden:

1
$ git bisect reset

(Nachzulesen auch im Git Book)

Die Erinnerung ist das, was uns immer begleitet.

Ich erwische mich oft dabei, wie ich alte Erlebnisse und Ereignisse wieder vor hole und einfach mal darüber nachdenke, was ich denn so schon alles in meinem kurzen Leben so erlebt habe.

Wie bei den meisten wahrscheinlich, sind so einige Erinnerungen ziemlich einfach zu erreichen und einige ehr nicht. Denn so wie ein altes Foto mit der Zeit an Farbe verliert, so verblassen auch Erinnerungen.

Doch wieso machen wir dann Fotos um diese Erinnerungen am Leben zu erhalten? Einfach: Fotos verblassen viel langsamer als Erinnerungen.

Rechts sehen wir zum Beispiel einen kleinen Jungen namens Christian zu seiner Einschulung. Kakao-Milch, Schulgarten… zwei Dinge aus meiner Grundschulzeit, die mir gerade nur beim spontanem draufschauen in den Sinn kommen.

Doch in diesem Artikel geht es heute nicht um meine alten Fotos, sondern um meine alten Blog Einträge. Einen Blog betreibe ich mal, mehr mal weniger aktiv, seit Mitte 2007 und auch schon davor hatte ich eine kleine aber feine Seite.

Nun habe ich mir mal die Zeit genommen die Einträge, die hier gerade in einem alten Backup verstauben, in meinen „neuen“ Blog einzupflegen und somit diesem Teil meiner Vergangenheit ein würdiges Zuhause zu geben.

Leider sind durch verschiedene virtuelle Umzüge so einige Dinge, wie die meisten Bilder, abhanden gekommen. Dies macht einige Artikel leider etwas trübe, aber besser so, als gar nicht.

Außerdem habe ich mich dazu entschieden, die Einträge nur im Layout dem aktuellen Aussehen anzupassen und die Rechtschreibung nicht zu korrigieren. Denn diese „Schlechtschreibung“ von damals gehörte, ich denke zumindest, dass es besser geworden ist, damals einfach zu meiner Persönlichkeit.

Um in den alten Beiträgen von mir mal ein wenig zu schnuppern, schaut einfach in der Archiv-Kategorie.

Ich persönlich fand es ziemlich witzig, meine alten Beiträge durchzulesen. Ich hoffe euch macht es auch ein wenig Spaß! =)

Wer das neue FileVault in Mac OS X Lion aktiviert hat, hat bestimmt folgendes Fenster zu Gesicht bekommen:

Screenshot

So wie ich den Text verstehe benötige ich den Schlüssel, wenn ich mein Anmeldekennwort vergessen habe. Da ich mir aber ziemlich sicher war das dies nie passieren wird, habe ich den Schlüssel mir nicht gemerkt.

Ein paar Wochen gingen ins Land und durch einen dummen Fehler hab ich mein Mac OS X kaputt gespielt aka er wollte nicht mehr starten. Also rein in die Wiederherstellungspartition und los geht’s. Diese wollte nun, das ich meine Hauptpartition öffne und verlangte mir mein Passwort ab. Doch mein Anmeldepassword akzeptierte er nicht.

Wie ich heute nun durch einen weiteren Test herrausfand, ist der obige Schlüssel das eigentliche Passwort für die Partition und nicht nur irgendein Recovery Key. Ergo: unbedingt aufbewahren auch wenn Ihr sicher seid, das ihr euer Anmeldepasswort nicht vergesst.

Lion: Lang lebe die Tastenwiederholunnnggg

Viele die auf Lion aktualisiert haben, werden bestimmt schon drüber gestolpert sein. Die geliebte “Ich halte die Taste gedrückt bis ich 200 o’s habe”-Funktion ist pasé. Stattdessen kommt ein iOS ähnliches Popover, welches einem verschiedene Varianten des Buchstaben anbietet.

Das lustigste daran: in den Systemeinstellungen kann man sehr wohl noch einstellen wie schnell den die Tasten beim gedrückten halten wiederholt werden sollen, Anwendung findet das nur leider nirgendsmehr.

Screenshot

Doch wie soll es so anders sein gibt es eine schicke Terminalzeile die einem das geliebte Verhalten zurück bringt:

1
$ defaults write -g ApplePressAndHoldEnabled -bool false

Dies scheint dann noch einem Neustart des Systemes zu bedürfen eh es in wirklich allen Anwendungen ankommt aber nun steht dem Schreibgenuss nichts mehr im Wege.

Ich liiiiieeeeeebe diese Einstellung =)

(Quelle: Medienmeisterei)

NSLogger: Entwickeln für Erwachsene

NSLogger ist ein Logviewer für Mac welcher auch iOS Apps unterstützt

Wer für iOS oder den Mac entwickelt wird es wahrscheinlich kennen. Hier ein NSLog einfügen, dort wieder löschen um nicht unter zu gehen. Aber genau wenn man gerade sich zu einem Fehler durchgehangelt hat, merkt man das einem ein Debugprint fehlt der alles lösen würde.

Dem zu entgegnen ist schwer. Meist schraubt man mit Macros so lange rum bis man bestimmte Log-Level hinbekommen hat. Für eine Software die später im Betrieb dem Benutzer was sagen soll ist das ganz ok (Serversoftware zum Bleistift) aber zum Entwickeln einfach nur anstrengend.

Durch einen schönen Zufall bin ich im IRC auf fpillet’s NSLogger gestoßen. Dies ist ein von Xcode losgelöster Logviewer der über eine hübsche Clientlibrary die zu loggenden Daten von der App erhält. Um nun der Flut von Meldungen Herr zu werden bietet NSLogger herrliche Filtermöglichkeiten nach Tags, Loglevel und dazu noch eigene Filterregeln.

NSLogger Fenster

So kann man nun ohne Probleme alles loggen, filtern was man braucht und nie mehr die App neukompilieren weil man ein Debugprint dummerweise noch auskommentiert hatte.

Herrliches Stück Software! =)

Für eines meiner iOS-Projekte wurde es nötig, dass sich die App mit einem Webservice verbindet und Daten speichert. Bei der Überlegung, wie ich nun die Daten im Webservice am sichersten einem Gerät zuordne, blieb ich bei einer Lösung mittels TLS Client Authentication hängen.

Der schwierige Teil ist nun einerseits die Client Zertifikate in dem Python Backend zu erstellen (Da der OpenSSL Wrapper M2Crypto ein paar Bugs hat) und andererseits das Zertifikat auf dem Gerät zu benutzen (Da Apple leider kaum etwas bietet, wodurch ich auf OpenSSL zurückgreifen musste). In dem ersten Teil dieses Zweiteilers möchte ich mich zunächst dem Python Backend zuwenden, da dieses mir die meisten Probleme bereitet hat.

Um das Zertifikat zu erstellen, habe ich mir eine kleine Helferfunktion gebastelt (da ich diese später nochmals weiterverwenden muss), welche man im folgenden bestaunen kann. Aber keine Angst ich werde darunter erklären was diese macht.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
def generate_certificate(type, ca_cert, ca_key, ca_stub, pubkey_pem,
                         common_name, expiration_date, comment=None):
    '''
        Generate a certificate of the given type and with the
        given parameters.
    '''

    assert issubclass(type, DeclarativeBase)

    cert = X509.X509()

    name = X509.X509_Name()
    name.CN = common_name
    cert.set_subject_name(name)

    # Set the public key of the certificate
    pubKey = EVP.PKey()
    pubKey.assign_rsa(RSA.load_pub_key_bio(BIO.MemoryBuffer(pubkey_pem)))
    cert.set_pubkey(pubKey)

    # Set the X509 extenstions
    cert.add_ext(X509.new_extension('nsCertType', 'client'))
    if comment is not None:
        cert.add_ext(X509.new_extension('nsComment', comment))
    cert.add_ext(X509.new_extension('extendedKeyUsage', 'clientAuth',
                 critical=1))
    cert.add_ext(X509.new_extension('keyUsage', 'digitalSignature',
                 critical=1))
    cert.add_ext(X509.new_extension('basicConstraints', 'CA:FALSE',
                 critical=1))

    # Create the subject key identifier
    modulus = cert.get_pubkey().get_modulus()
    sha_hash = hashlib.sha1(modulus).digest()
    sub_key_id = ":".join(["%02X"%ord(byte) for byte in sha_hash])

    cert.add_ext(X509.new_extension('subjectKeyIdentifier', sub_key_id))

    # Authority Identifier
    cert.add_ext(ca_stub.get_ext('authorityKeyIdentifier'))

    # Certificate is valid from now
    notBefore = ASN1_UTCTIME(m2.x509_get_not_before(cert.x509))
    notBefore.set_datetime(datetime.now())

    # Certificate is valid for expires_in seconds
    notAfter  = ASN1_UTCTIME(m2.x509_get_not_after(cert.x509))
    notAfter.set_datetime(expiration_date)

    # Interact with the db and get the serial number

    certificate_entry = type()

    certificate_entry.subject = str(name) # Will be /CN=common_name
    certificate_entry.expiration_date = expiration_date
    certificate_entry.revoked = False

    Session.add(certificate_entry)
    Session.commit()

    # Now we do have a valid serial number
    cert.set_serial_number(certificate_entry.serial_number)

    """
        Sign the certificate with our
        intermediate CA and make it valid
    """
    cert.set_issuer(ca_cert.get_subject())
    cert.sign(ca_key, 'sha1')

    # Return the certificate in PEM format
    return cert.as_pem()

Fangen wir mit den Parametern der Funktion an:

  • type …ist eine Klasse die von der DeclarativeBase von SQLAlchemy abgeleitet wurde. Diese wird benutzt um das Zertifikat in der Datenbank zu verewigen und gleichzeitig eine Serialnummer für es zu bekommen.
  • ca_cert, ca_key …sind ein X509 und ein EVP Objekt die einmal das Zertifikat bzw. den privaten Schlüssel der CA enthalten, die das Zertifikat erstellen soll.
  • pubkey_pem …ist ein String, welcher den öffentlichen Schlüssel im PEM Format enthält, der in das Zertifikat eingebettet werden soll.
  • common_name …ist ein String welcher den Allgemeinen Namen enthält für das Zertifikat. Man kann noch viel mehr in das Subject eines Zertifikates einbringen, aber für meine Zwecke reicht der Allgemeine Name.

Nach dem das geklärt wurde, sollte der Rest des Codes ziemlich einleuchtend sein. Kopfzerbechen haben mir allerdings der subjectKeyIdentifier und authorityKeyIdentifier bereitet. Im normlen OpenSSL Kommandozeilen Programm gibt es einfache Optionen wie “hash”, welche M2Crypto leider nicht unterstützt.

Daher muss man den subjectKeyIdentifer wie folgt berechnen:

1
2
3
4
5
6
# Create the subject key identifier
modulus = cert.get_pubkey().get_modulus()
sha_hash = hashlib.sha1(modulus).digest()
sub_key_id = ":".join(["%02X"%ord(byte) for byte in sha_hash])

cert.add_ext(X509.new_extension('subjectKeyIdentifier', sub_key_id))

Noch gemeiner war der authorityKeyIdentifier. Diesen kann man zwar auch selbst berechnen, aber durch einen Bug in M2Crypto stürzt das Programm immer ab, wenn man diesen dann setzt. Daher muss man sich hier einen Trick bedienen. Man nimmt ein bereits signirtes Zertifikat von der CA (Zum Beispiel mit dem openssl ca Kommando erstellt) und holt sich aus diesem einfach die authorityKeyIdentifier Extension und setzt diese:

1
2
# Authority Identifier
cert.add_ext(ca_stub.get_ext('authorityKeyIdentifier'))

Nun funktioniert’s!

(Update: das Setzen der Gültigkeitszeitspanne des Zertifikats funktioniert nun immer richtig.)

KeepAlive diese SSH-Verbindung

Eine ganze Weile lang hatte ich ein sehr nerviges Problem: meine SSH-Verbindung stirbt einfach andauernd. Der geübte Mensch kommt jetzt eventuell auf die Idee, das die Verbindung zu lange nicht benutzt wurde und deswegen aufgibt, was ich sogar verschmerzen könnte aber das einem die SSH-Verbindung quasi beim tippen unterm Hintern wegstirbt ist schon ein wenig zu viel des Guten.

Ein kleines besonders frustrierendes Beispiel: Man hat mal wieder ein kleines Konfigurationsproblem mit seinem Postfix und will deshalb die Log-File überwachen um den ganzen auf die Schliche zu kommen. Wenn hier nun die SSH-Verbindung stirbt und – wie in meinem Fall – der SSH-Client einfach hängt, kann man natürlich verdammt lange, ergebnislos auf eine solche Log schauen und nichts geschieht.

Durch einen Zufall bin ich neulich in einem Gespräch mit KriS auf dieses Problem bekommen und gestern ist ihm dann die Lösung über den Weg gelaufen. Man muss einfach ein KeepAlive in SSH einschalten. Ich war zu erst natürlich skeptisch, da es ja theoretisch nicht helfen sollte, wenn die Verbindung beim Tippen stirbt, aber siehe da… es funktioniert herrlich.

Hier das was ich in meine SSHD-Konfiguration auf dem Server hinterlegt hab:

1
2
KeepAlive yes
ClientAliveInterval 60

Mein unendlicher Dank geht dafür an meinen Hoster KriS =)

Xcode 4: Einfache Versionsnummern mit git

Immer wenn man sein Programm in ein Versions Control System einbetten, steht man vor dem Problem die “normalen” Versionsnummern mit den Revisionen zu verknüpfen.

Für dieses Problem gibt es na nun die Möglichkeit des Tags, aber was wenn man die mal wieder vergisst? Oder was wenn man sich zwischen den Releases befindet und seine Development-Build irgendwie einordnen will.

All dies kann man mit dem netten »git describe« Befehl lösen (sofern man Git benutzt). Hier eine Beispielausgabe:

1
2
$ ~/super-app$ git describe --dirty
1.5.0-14-g2414721-dirty

Hier die Auflösung:

  • 1.5.0 steht für den letzten annotated Tag. Diese benutze ich dreist für die einzelnen Versionsnummern. (Hinweis: die normalen “anderen” Tags werden defaultmäßig ignoriert.)
  • 14 steht für die Anzahl der Commits seit dem genannten Tag.
  • g2414721 steht einerseits für das verwendete VCS, das »g«, und andererseit beinhaltet es den Commithash »2414721«.
  • dirty steht dafür das noch Änderungen vorhanden sind, die nicht comitted wurden.

Die Teile dieser Ausgabe, die nicht notwendig sind, fallen weg. So würde ein Commit der gerade als »1.6.0« getagt wurde, nur folgendes liefern:

1
2
~/super-app$ git describe --dirty
1.6.0

Ziemlich schön, nicht?

Einbinden in Xcode

In Xcode, oder besser gesagt in Mac/iOS Apps, wird die Versionsnummer bekanntlicherweise in der Info.plist angegeben. Auch wenn ich keine Ahnung habe wer bei Apple auf die komische Idee gekommen ist, ist es möglich die PropertyList durch den Preprozessor zu jagen (Was sogar funktioniert!).

Als erstes ersetzen wir die Version in der Info.plist durch ein »GIT_VERSION«, welches dann durch den Preprozessor ersetzt wird.

1
2
3
4
5
6
7
8
<plist version="1.0">
    <dict><key>CFBundleVersion</key>
        <string>GIT_VERSION</string></dict>
</plist>

Nun erstellen wir ein neues »Aggregate« Target mit einer »Run-Script« Phase und gibt dort folgendes als Skript an:

1
2
3
4
VERSION=$(git describe --dirty)

echo "#define GIT_VERSION $VERSION" > Version.h
touch Info.plist

Dies erstellt uns nun bei jedem Build eine neue Version.h-Datei, welche die Aktuelle Version enthält. Zusätzlich berührt sie noch die Info.plist so das Xcode weiß, das er diese neu verarbeiten soll.

Damit das alles Hand in Hand funktioniert, müssen wir das Target noch als Abhängigkeit zu unserem eigentlichen App-Target hinzufügen.

Möge man es Magie nennen =)

Frühjahrsputz

Heute strahlt hier die Sonne und auch die Wärme kommt langsam an. Es wird Frühling =) Das war der perfekte Anlass, hier endlich mal das versprochene schöne Design einzuführen.

Die Idee für das Design habe ich mehr oder weniger von der Flask Dokumentation geklaut und in ein sehr einfaches Blogtheme verwandelt.

Wie man sieht sind dem auch viele Funktionen wie Tags, Kategorien oder die Suche zum Opfer gefallen. Aber ich denke das dies eine gute Entscheidung ist, da es das ganze sehr viel leichter macht.

Ich hoffe das Design gefällt euch auch =)