System 6 extension/control panel to modify keyboard input?

Frobozz

Well-known member
I'd like to make my Mac Plus more useful/pleasant-to-use. I use an original mac 128 keyboard on it, not the bigger Plus keyboard. So: no cursor keys! I have used a mac (original) keyboard on my modern macs with an adapter. I can program layers/etc and basically set up cursor keys that fire when caps lock is held down (for example). But if I want to do that same on compact mac hardware, I don't have a hardware based thing in between with firmware I can modify. 

I was thinking about ForwardDelete68K, which lets you do shift-backspace to get a DEL key. Some programs that modify key input: 




I've done a little System 6 app programming in the past, and thought this would make a fun project to try with retro68. Thing is... I don't have the first clue what I'd be doing here. Does anyone have a pointer for the general theory I'd be trying to execute? Or whether this is even practical? I assume it will have some limits, based on how ForwardDelete works (I think only apps that rely entirely on toolkit calls respect it). 

I'd like to try caps lock turning +/= into up, [ into left, ] into down, and \ into right. On the 128 keyboard, those are very close to an inverted T, and near the backspace key, which I tend to need a lot. 

 

Crutch

Well-known member
You would need to write an INIT to patch a trap, which is doable but sort of an advanced topic in System 6-7 coding that requires some care.

I would suggest patching GetNextEvent and WaitNextEvent, which most apps will call to get a keypress.  I would simply patch them to do their normal thing then, if they are about to return a keyDown or autoKey event that looks like Caps Lock and + or whatever, to instead return the character code for the arrow key you want.  This is a “tail patch” which can theoretically introduce incompatibilities for technical reasons, but you are probably fine.

The whole thing is probably 80 lines of code.  I can offer some more specific details if you’d like.

 

Frobozz

Well-known member
Thanks Crutch, that was just what I needed.

I haven't used it enough to see if there are stability issues, but it seems to be ok so far. I did notice some apps (e.g, MacWrite pre-II versions) don't do anything with the cursor actions, but I'm not 100% sure yet that they would react to real cursor keys. Wouldn't surprise me, as I was surprised by having keyCode = charCode in the EventRecord. The key codes listed in Think C reference for Mac Plus/etc aren't returned at all (at least for the keys I tested). 

Some links that I found helpful in case anyone stumbles across this in the future: 



 

Frobozz

Well-known member
I can't believe how much time I spent today and yesterday basically trying to do one thing: move a 2K system extension from a Mac Plus over to my modern Mac so I can upload it here. ;)  Ah, well. I now have a SCSI2D and "Serial" app with a null modem connection to the Plus, so it hasn't been a total loss of time. 

What this system extension (INIT) does:

  • When caps lock is down, turns [{ key into left cursor, \| key into right cursor, =+ key into up cursor, ]} key into down cursor. 

Why you might want this:

  • You have an original 128/512 Mac keyboard and wish it had cursor keys
  • You have a Mac Plus keyboard, Apple Desktop Bus Keyboard ("IIGS"), M0116 (II/SE "standard keyboard"), or Apple Keyboard II and wished you had cursor keys in an inverted T arrangement

How to install this:

  • Get it onto your Mac somehow
  • Expand the .SIT file
  • Drop the INIT/extension on your system folder. For System 7+, the Mac will further drop it into your System Extensions folder

How to use it:

  • Engage caps lock key, and use [, ], \, and = as cursor keys
  • No other keys are affected

Does it have a Control Panel?

  • No. Nothing is configurable, in the interest of keeping size to a minimum.

Which systems is it compatible with?

  • Good question. I have tested so far with: 


    Mac Plus with 128/512 keyboard running 6.0.8 (US)
  • Mac Classic with Apple Extended Keyboard running 7.1 (US)
  • Sheepshaver (OS X) running 9.0 (Japanese)

[*]If you test it on another system, please let me know if it works or not. I would expect it to not work perfectly for any language/country that has a different arrangement in that part of the keyboard. 



Will it work in all apps?

  • No, but it should work in all apps that are well-behaved, and which actually have some kind of cursor-based behavior. I noticed in testing, for example, that MacWrite (pre-II versions) didn't do anything with the cursor input. I tried MacWrite with a Mac that had native cursor keys, they didn't do anything either! Your mileage may vary. 

Why didn't you map IJKL?

  • IJKL does make a better inverted T for cursors, the but problem with that is that you basically can't use the keyboard for any typing at all when you are in that mode. 

Can it do my homework?

  • Not currently. All it does is map 4 keys. If you have any idea for other keys, I'm not adverse to mapping more. The only concern I have is that the more keys that get remapped, the more the hit on typing performance might be noticeable. I didn't do any testing though, so it's possible it's not a concern. I'm sure it wouldn't be on anything faster than a bare 68000. I am thinking it might be bad to unmap all the caps lock capitalization, so you could basically run all the time with the caps lock engaged, and only undo it when you need [, ], =, or | (which, unfortunately, as a programmer, is pretty much all the time!)

Version 0.1.1 attached

View attachment CapsLock Cursors.sit

 

Crutch

Well-known member
This is super cool, I am excited to check it out later.  What a useful idea.  I dislike the Plus keyboard (always found them funny looking) but use them because I feel like I can’t live without cursor keys.  You have solved my problem for me in a way that never occurred to me.

What development platform did you use to implement it?  I think you could implement most of your ideas for enhancements with no performance hit.

You linked some very nice resources there.  The “Writing INITs from THINK C” from MacTutor is a classic, but I’d never seen the Apple doc you linked on the Trap Manager before.  Well researched.

 

Frobozz

Well-known member
Ok, found a dumb bug that explained my confusion about the keycodes=charcodes. 

This version de-capitalizes alpha keys too, so the idea is the user can just leave caps lock on until they need =, [, ], \. 

I'll get a GitHub up soon, but I threw the source into it for now. it's a Think C 5.0 project. (I was having so many other futsy old computer issues that Retro68 went by the wayside in the name of getting something done). 

View attachment CapsLockCursors_v0.2.0.sit

 

Crutch

Well-known member
Agreed, code looks like a textbook trap patching INIT, nicely done.

You said it works in 7.1 which is interesting because you didn’t patch WaitNextEvent.  I wonder if WNE calls GetNextEvent, so you get a WNE patch for “free” under System 7 orMultiFinder?  It would be easy to check I suppose ....

 

Frobozz

Well-known member
That's a good question. I guess my assumption is that WaitNextEvent has GetNextEvent in its own call chain. But another possibility is that the very few apps I tested with hadn't been written with WaitNextEvent, but were still doing GetNextEvent. Seems unlikely, but I suppose it's possible. 

 

Trash80toHP_Mini

NIGHT STALKER
Did a quick skim of the topic, so please forgive me if this blast from the past is irrelevant to your System 6 project:





63523658.jpg.071f396bcd83aa28ea5f1aeb6e883f5f.jpg


Does any part of this resemble anything like what you're trying to achieve?

 

Frobozz

Well-known member
Did a quick skim of the topic, so please forgive me if this blast from the past is irrelevant to your System 6 project:







Does any part of this resemble anything like what you're trying to achieve?


Yes, it's the same concept. I did look at the resedit path, but I wanted something like Forward Delete, an extension I (or others) could simply remove if they didn't want it, and add it back if they did. Also wanted to learn how to make an INIT. The same exact code, with a few more conditions, could do what you have above. It would need to be a lot bigger to be able to read preferred keymap from disk, and even more to have a GUI for switching keys, and this is for Mac Pluses and 512s, so every byte is bit precious. 

That photo makes me want to drag out one of my PBs. but I have very little confidence either of them would start up. <sigh>

 

Frobozz

Well-known member
I revisited this idea this week, after getting tired of not being able to easily type [, ], and =when programming. I made a new version that activates when you hold down Option (instead of CapsLock). Idea being on a Mac 128 keyboard, you can hold option with left thumb, and do cursors with your fingers, then let go altogether when you want to type normal stuff.
Works fine, as far as I can tell with mini vMac: my plus has what I think are solder joint cracks.
But the totally pointless thing I can't figure out is: how do I get it to show a badge on the startup screen when loading? I tried asking a couple bots, but they both missed, or perhaps I just got what they said wrong.
Anybody know? Using THINK C 5.0 btw.
 

Frobozz

Well-known member
Thanks for that. I am struggling with it. I started from there, got the main idea, but haven't been able to get anything working. The closest I got was that at one point, I could write into the "Welcome to Macintosh" window. Neat, but not what I was trying to do. Mostly the problems seem to come down to failing to draw to the quickdraw screen.
My current approach is to break it up into 2 parts:
1. on startup, it patches the event handler with the new code for ersatz cursor keys. this works fine. It also installs a hook so that the first time (??) _InitWindows is called, we get a chance to call the badge display.
2. the function for displaying the badge, which, hopefully, can now safely use global quickdraw stuff.

Reality, however, is that as soon as the patched _InitWindows is called, it crashes. I set up a beep before and after, and only the one before is hit.

I feel like if I knew what I was doing, this would be a 5 minute job :)



C:
// Patch the system's call to init windows to also call our badge display
pascal void App_PatchedInitWindows(void)
{
    // Establish context for this INIT's globals
    SetUpA4();

SysBeep(1);    // this will fire, so it's at least getting to hear before crashing mac.

    // Now it's safe to access gOrigInitWindows and call Badge_Show
    // First call the original InitWindows trap
    CallPascal(gOrigInitWindows);

SysBeep(1); // this will NOT fire, so it's dying before this presumably

    // Then call your badge code (safe now!)
    Badge_Show(ICON_ID);

    // Restore the previous context
    RestoreA4();
}
This is set up by:

C:
// show badge, install patch, etc.
void main(void)
{
    Handle    myHandle;
    Ptr        myPtr;

    asm
    {
        move.l A0, myPtr
    }
    
    RememberA0();
    SetUpA4();

    if (Button() == false)
    {
        myHandle = RecoverHandle(myPtr);         
        DetachResource(myHandle);

        // Patch GetNextEvent
        gOrigGetNextEvent = NGetTrapAddress((int)GetNextEventTrap, ToolTrap);
        NSetTrapAddress((long)NewGetNextEvent, (int)GetNextEventTrap, ToolTrap);

        // Patch InitWindows so we know QuickDraw is ready
        gOrigInitWindows = NGetTrapAddress(_InitWindows, OSTrap);
        NSetTrapAddress((long)&App_PatchedInitWindows, _InitWindows, OSTrap);

        // We don’t call Badge_Show here anymore — it’s too early
        // we just wait until system gets further along, then it will show our badge
    }

    RestoreA4();
}
 

cheesestraws

Well-known member
By "badge", do you mean an icon in the march of icons under Welcome to Macintosh? If so, you're overthinking this - no need to patch anything at all.

As @joevt says, use ShowInitIcon - this can just be dropped in and used as a library. As an example, look at how my Force32 does it: ShowInitIcon is just imported as part of the project (https://github.com/cheesestraws/Force32/blob/main/ShowInitIcon.c). Then no need to patch anything - at the time your main() is called, you already have QD ready to go to draw your icon, so you can just draw an icon out of your resources, like this: https://github.com/cheesestraws/Force32/blob/main/INIT.c#L53. The second boolean parameter to ShowInitIcon just says whether to advance to the position where the next icon is drawn or not; you can see that Force32 uses this to initially draw its own icon and then draw an overlay YES or NO icon to say whether it's done its job or not.
 

Frobozz

Well-known member
I must be doing something wrong because THINK C (5.0) gave me grief about this:

Code:
typedef struct {
    QDGlobals            qd;                                    // Storage for the QuickDraw globals
    long                qdGlobalsPtr;                            // A5 points to this place; it will contain a pointer to qd
} QDStorage;

And that led me down various rabbit holes.
 

cheesestraws

Well-known member
I don't know a huge amount about THINK C, I've only ever really used CodeWarrior, but i believe it uses older names for some header files? What grief is it giving you?
 
Top