Getting the pen's position by Brian Parker

Editor's note: Brian, who's working on a Palm emulator for the Newton, describes here a method to get the position of the pen using PenPos NewtonScript function. It has only been tested on a MP2x00 US but it should work on any 2.1 device and it might work on 2.0 devices as well.

The idea of click scripts works well for most apps, but completely fails when other processing is needed while the pen is down. Actions like animation do not work because your screen updates will not occur while the click script is running. This doc describes a new way to find the position of the stylus on the screen. It can be called at any time, not just inside click scripts. It can also be used within C++. This information is for use with OS 2.1 devices and will probably crash horribly on earlier Newtons. I assume the x and y are swapped when the screen is rotated, but I have only worked with landscape mode.

PenPos() is a lower level undocumented function that returns the absolute x,y coordinates of the stylus. NTK does not know about this function so you have to tell it to avoid a warning. To do so, add the following line somewhere in your project (like in the Install & Remove.f file or in a beforeScript slot) before calling PenPos:

	knownGlobalFunctions.PenPos := 0;
Then simply call PenPos:

  position := PenPos();

  if (position <> NIL) then
  begin
    Print(position.x);
    Print(position.y);
  end;

There are no arguments to send. A frame with x and y values is returned. If the pen is not down, nil will be returned. If the pen very recently went down, the x,y may be 0,0 before the actual values arrive.

This code can also be used in C++ using CallGlobalFn (if you only call PenPos in C++, there is no need to declare that function to NTK with knownGlobalFunction trick). The result is a pointer to a frame. The x,y coordinates are encoded. The low bits are multiplied by 64, then added to the shifted high bits. Here is a decoding example:


  Ref penposition;
  unsigned long pen_vert;
  unsigned long pen_horiz;
  
  penposition = NSCallGlobalFn(SYM(PenPos));
  
  if (penposition != 2)  //make sure pen is down
  {
    unsigned long* ppos = (unsigned long*) penposition;
              
    if(ppos[3] != 0)  //make sure it isnt at 0,0
    {
      pen_horiz = (ppos[3] / 0x4000000) + (ppos[3] << 6);
      pen_vert = (ppos[4] / 0x4000000) + (ppos[4] << 6);
    }
  }

I'm sure this can be further optimized using more shifts.

If PenPos could be called from C++ without going into NewtonScript, a program could run it significantly faster. Using PenPos and the direct screen access, some programs will be able to completely stay in C++. Most likely this function is just doing minimal processing on some hardware registers. Disassembling PenPos could give valuable information about the hardware layout.

Editor's note: calling PenPos like this, even from C++, means accessing the NewtonScript heap. It shouldn't be done outside the NewtonScript task (a.k.a. the Application task) because the NewtonScript world (especially the garbage collector) isn't thread safe.