Wednesday, November 29, 2006

Hacking the Microsoft Natural Ergonomic Keyboard 4000, redux

Earlier I wrote about hacking IntelliType's commands.xml file to enable the use of the "Zoom slider" as a "Scroll slider". Having recently repaved my computer, I found myself installing the latest version of Microsoft IntelliType Pro (version 6.02, a.k.a. 6.02.303.0) and trying to apply the patch I posted earlier:


C:\Program Files\Microsoft IntelliType Pro>patch -p0 < command.patch
patching file `commands.xml'
Hunk #1 FAILED at 1606.
Hunk #2 FAILED at 1694.
Hunk #3 FAILED at 2122.
Hunk #4 FAILED at 2134.
Hunk #5 FAILED at 2152.
Hunk #6 FAILED at 2182.
patch unexpectedly ends in middle of line
Hunk #7 FAILED at 2224.
7 out of 7 hunks FAILED -- saving rejects to commands.xml.rej


B'oh! Err... I mean: D'oh! Ok, time for plan B: Zoom2Scroll.xsl


<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

<xsl:output method="xml" encoding="UTF-8" indent="yes" />

<!-- Pass-through (identity transform) template -->
<xsl:template match="* | @* | node()">
<xsl:copy>
<xsl:apply-templates select="* | @* | node()" />
</xsl:copy>
</xsl:template>

<xsl:template match="C319">
<C319 Type="6" Activator="ScrollUp" />
</xsl:template>

<xsl:template match="C320">
<C320 Type="6" Activator="ScrollDown" />
</xsl:template>

</xsl:stylesheet>


There. A quick trip to the command-line with my trusty xsl.exe:


C:\Program Files\Microsoft IntelliType Pro>xsl commands.old Zoom2Scroll.xsl commands.xml

C:\Program Files\Microsoft IntelliType Pro>


...and killing the IType.exe process, then re-launching that EXE again... it works! In your face, Larry Wall!

Oh, right. This post wouldn't be complete without the source code to xsl.exe. It's a JScript.NET program:


import System.Xml;
import System.Xml.Schema;

var src : String = System.Environment.GetCommandLineArgs()[1];

var transformer : System.Xml.Xsl.XslTransform = new System.Xml.Xsl.XslTransform ( );
var stylesheet : String = System.Environment.GetCommandLineArgs()[2];
transformer.Load ( stylesheet );

transformer.Transform ( src, System.Environment.GetCommandLineArgs()[3], null );


...which you can compile as follows:


C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322>jsc /nologo xsl.jsn


Thanks go out to the folks at Cute Overload for providing me with a [semi-]random web page to test the [new] scrolling capabilities of my "slider" in Internet Explorer.

23 comments:

Anonymous said...

Thanks man, good work.

Sean Chambers said...

awesome! very useful. thanks !!

Jeff Handley said...

This is absolutely awesome, thanks!!

Anonymous said...

useful! thanks for the hack

angel333 said...

You don't have to make an XSLT processor in .NET, I think the better way is to use the online one - http://xslt.sitesfree.com . There is also something from W3C but it didn't work for me. And thank you! It works well!

elliot said...

Adds a much-needed function to an otherwise solid product, thanks for posting!

Tobbe said...

Thanks for a great hack!

I can add that it works with the 6.01.250.0 version as well :)

And maybe it's worth mentioning that there needs to exist a file called commands.old, otherwise it will give you a "File not found" error

TwinSubs said...

Here's a powershell script version of the transform code. Run it from a powershell prompt. You might need to adjust your execution policy using set-executionpolicy before it will run. Run get-help set-executionpolicy if you need help.


#Transform-Xml.ps1 by Steve Hiner
param (
[string]$inputfile = $(Read-Host "What XML file should I transform?")
, [string]$stylesheet = $(Read-Host "What XSL file should I use for the transform?")
, [string]$outputfile = $(Read-Host "Where should I put the transformed XML file?")
)
[System.Xml.Xsl.XslTransform]$transformer = New-Object System.Xml.Xsl.XslTransform
$transformer.Load($stylesheet)
$transformer.Transform($inputfile, $outputfile, $Null)

Troy Stauffer said...

Awesome, exactly what I was looking for!

It should be noted that when compiling xsl.jsn using the .NET framework v2.0, I had to add "import System.Data.SqlXml;" for it to compile.

Waldemar said...

I have a problem that I can not solve.

I want to replace "Ctrl Enter" with "Enter" and "Enter" with "Ctrl Enter" in Windows Live Messenger.

Now I've added:
<C28 Type="5" KeySeq="b" />

under :
<Application UniqueName="StandardSupport">

just to see if I can replace "Enter" with "b", saved, killed iType and started again. And nothing. :(

Can anyone help me?

Olivier Dagenais said...

@waldemar:

Try upgrading your IntelliType software: my commands.xml file contains these sections that might work better:

<Application UniqueName="MSBLClass" AppName="Windows/MSN Messenger">

and

<Application UniqueName="IMWindowClass" AppName="Windows/MSN Messenger">

...but then again, I don't know that this mechanism would allow you to trap Ctrl Enter (or even simply Enter), so you may be better off asking Microsoft for that feature/option or switching to a different instant messaging client, such as Trillian or Pidgin.

Waldemar said...

Thanks for the info.

mos said...

Great hack, thanks. Do you know of any way to disable the FLock key (so that it's always on?)

Olivier Dagenais said...

@mos:

Unfortunately, I don't know how to make that setting permanent, as I also occasionally hit and wonder why all of the F-keys suddenly don't work.

In my opinion, that "F Lock" should have been a slide switch accessible from underneath the keyboard, just like the XT <-> AT switch under old keyboards.

If you do find a solution (a quick web search indicates people are simply working around the problem by remapping the keys, similar to how I remapped the "zoom scroller"), please post it here!

mos said...

Well, I've *almost* got it working. Here's the xsl I wrote:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
<!-- Pass-through (identity transform) template -->
<xsl:template match="* | @* | node()">
<xsl:copy>
<xsl:apply-templates select="* | @* | node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="C302">
<C302 Type="5" KeySeq="F1" />
</xsl:template>
<xsl:template match="C203">
<C203 Type="5" KeySeq="F2" />
</xsl:template>
<xsl:template match="C204">
<C204 Type="5" KeySeq="F3" />
</xsl:template>
<xsl:template match="C307">
<C307 Type="5" KeySeq="F4" />
</xsl:template>
<xsl:template match="C308">
<C308 Type="5" KeySeq="F5" />
</xsl:template>
<xsl:template match="C309">
<C309 Type="5" KeySeq="F6" />
</xsl:template>
<xsl:template match="C900">
<C900 Type="5" KeySeq="F7" />
</xsl:template>
<xsl:template match="C901">
<C901 Type="5" KeySeq="F8" />
</xsl:template>
<xsl:template match="C902">
<C902 Type="5" KeySeq="F9" />
</xsl:template>
<xsl:template match="C401">
<C401 Type="5" KeySeq="F10" />
</xsl:template>
<xsl:template match="C311">
<C311 Type="5" KeySeq="F11" />
</xsl:template>
<xsl:template match="C310">
<C310 Type="5" KeySeq="F12" />
</xsl:template>
</xsl:stylesheet>

However, it doesn't work for F7, F8, or F9 (and only those three). Do you see anything wrong with what I did? (I hope the HTML encoding worked out, there.)

Olivier Dagenais said...

@mos:

I'm not sure XSL is the best way to go here, since if there are no instances of C900, C901 or C902 elements in the original XML, the template will not add them for you.

Take a look at the Reprogramming Intellitype Pro Method; the only spot they have <C900> and "F7" is in the section they added. You might be able to do so with XSL like the following (warning: untested!):

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="xml" encoding="UTF-8" indent="yes" />
    <!-- Pass-through (identity transform) template -->
    <xsl:template match="* | @* | node()">
        <xsl:copy>
            <xsl:apply-templates select="* | @* | node()" />
        </xsl:copy>
    </xsl:template>

    <xsl:template match="Application[@UniqueName='StandardSupport']">
        <C302 Type="5" KeySeq="F1" />
        <C203 Type="5" KeySeq="F2" />
        <C204 Type="5" KeySeq="F3" />
        <C307 Type="5" KeySeq="F4" />
        <C308 Type="5" KeySeq="F5" />
        <C309 Type="5" KeySeq="F6" />
        <C900 Type="5" KeySeq="F7" />
        <C901 Type="5" KeySeq="F8" />
        <C902 Type="5" KeySeq="F9" />
        <C401 Type="5" KeySeq="F10" />
        <C311 Type="5" KeySeq="F11" />
        <C310 Type="5" KeySeq="F12" />
    </xsl:template>
</xsl:stylesheet>

...as always, make a backup of the file before starting!

mos said...

Oh, of course. My "All" section didn't contain entries for C900-C902. Duh.

For whatever reason, I couldn't get the Reprogramming Intellitype Pro method to work for me, which is why I went with the xsl route. Any time the F Lock key was off, pressing a function key would pop up an error dialog.

Anyhow, just adding the lines to the All section worked just fine.

Loch Ness said...

KTHXBYE

Guido said...

Still works like a charm with Vista, thx!

PHenry said...

Thanks for the heads up, I even wrote up my own blog entry about this with some additions to your information. Thanks.

http://www.pchenry.com/Home/tabid/36/EntryID/77/Default.aspx

Lamonte said...

is it possible to make the Zoom become the (Previous & Next) buttons for playing music. this keyboard doesn't have it but i love the board.

Anonymous said...

Btw, you CAN assign any command to that so called "unassignable" "My Favorites" button....

You need to add this:

to the section MY_FAVORITES_LEGEND_EVENT of "ITypeDevices.xml" file...so it becomes like this:


MY_FAVORITES_LEGEND_EVENT
IDS_KN_MY_FAVORITES_LEGEND
IDI_FAVORITES_SETTINGS
MY_FAVORITES_LEGEND_COMMAND





Then you can add 83 (which is the code to My Favorites key) to Event Mapping in your registry and make it work however you like :)

To find Event Mapping section in your registry simply Google it...and it's a good idea to make backup of the ITypeDevices.xml

Greetings from Istanbul Turkey

Anonymous said...

hmm...blog commenting deleted section name...

here is what you need to add to ITypeDevices.xml file "Capability Name='CanRemap'/" put this sentence in "<" and ">"