benblazak / ergodox-firmware

firmware for the ergoDOX keyboard
Other
596 stars 294 forks source link

Any way to give Ergodox the full control over layout? #71

Open cheery opened 8 years ago

cheery commented 8 years ago

I'm using Ubuntu linux here and I like the idea that my keyboard would be smart and retain my favorite layout wherever I go.

Unfortunately the USB keyboard protocol was not designed with smart keyboards in mind. Ergodox goes through the localization layer just like an ordinary keyboard. This more or less forces me to use Ergodox like an ordinary keyboard, even if it could be anything I want it to be.

Any ideas or plans to fix this or minimize the problems in your average linux system?

I could probably hack up something. After all I seem to have access to everything to do so. I'm glad that I do. But has anyone else thought up something on this?

benblazak commented 8 years ago

So, for linux specifically, it should be relatively safe to assume that the escape sequence for unicode character input is consistent between systems -- though it's been a while since I researched that. I've never tried, but it might be possible to design an ergodox layout that always sends these escape sequences rather than plain scan codes. Of course... having the OS keyboard mode set to a different local might mess up the scan code to escape sequence mapping, I forget... But that's about the only thing I can think to try if you're talking about dealing with non-english characters working consistently between systems set to different locales. Otherwise, I'm pretty sure you'd have to write your own driver... and then install that on every system you planned to use the keyboard with. /sigh/. The USB keyboard protocol seems to have not been designed with a lot of things in mind.

Hmm... one other idea does occur to me, if we're talking just about linux: I bet there's a way, via keyboard, to change the system locale -- though I have no idea whether that would be consistent across systems. But if it was, or close enough, you could write a macro that did that, and assign it to some key, or even have it execute some amount of time after the keyboard powered on.

Not particularly good ideas, I suppose, but that's what I have. If you have any others (or, if I misunderstood what you were saying) I'd be glad to hear them :-) . I haven't had much time at all to spend on this lately, and probably won't for at least a while still, but, I'm not always the only one around here.

cheery commented 8 years ago

The escape sequences idea sounds like it would completely bust gaming applications. And the loadkeys/xkbdset hacks would require user intervetion to setup the keyboard.

The driver would have some challenge, since Linux&Xorg has duplicated keymappings for no sane reason. But then that sort of makes it more attracting too.

I suppose there's no other options that'd provide me with completely customizable keyboard layout.

There's some problems in coming up with sensible keyboard protocol that is not raw scancodes. For example, say you consider SDL as a reference. The first API is hacky and the newer API seems to be flawed in practice for anything else but gaming. The way X11 does it can be expected to be not rosy either.

Also if you look into http://wayland.freedesktop.org/protocol.html#wayland-interface-wl_keyboard they seem to be pulling into exact opposite direction by proposing the client app parses the keyboard map. Though in other hand they represent keys by unsigned integers.

cheery commented 8 years ago

Since I am using my ergodox keyboard regularly now, I'd like to keep the firmware running while writing the alternative driver. I suppose this is possible and I could activate raw HID interface that sends the ergodox scancodes over to the desktop for starters.

benblazak commented 8 years ago

Hmm -- that's interesting. I suppose, if you're going to be writing a driver anyway, there's no practical reason (and only a tenuous philosophical one) not to leave the keyboard as is and do everything in software. I suppose I haven't thought about it as thoroughly as I thought, lol. If you're really gonna jump into working on this, you might also check out the work of Jacob Alexander (haata on github) -- I believe he did most of the code and hardware for the ergodox infinity, among other awesome things, and he may have gone through this same thought process at some point.

cheery commented 8 years ago

There is a good reason to locate something into the keyboard. That would be the layout keyboard represents to the user. But should the keyboard do anything else than send the scancodes? Hmm.

I just checked into https://www.pjrc.com/teensy/rawhid.html and thought usb_rawhid.c is suspiciously similar to the https://github.com/benblazak/ergodox-firmware/blob/master/src/lib-other/pjrc/usb_keyboard/usb_keyboard.c

Some of this seems very familiar.

I'm quite sure that I could write a raw HID device for it, which completely replaces the keyboard protocol. But I am not sure yet how to retain the USB keyboard, at least when the machine doesn't treat it as a different device.

I checked into haata's repository too. He seems to have multi-endpoint setting there: https://github.com/kiibohd/controller/blob/master/Output/pjrcUSB/avr/usb_keyboard_serial.c

Now I suppose I can do it too by merging those files. It'll take a while to peek into manuals and learn everything though.

cheery commented 8 years ago

Now I have inserted an additional interface. When I update the firmware, it should give me a rawhid device I can open...

cheery@ruttunen:~/ergodox-firmware/rawhid$ sudo ./rawhid_test 
found rawhid device

Yep. Next I just have to send the scancodes over and the both stuff keeps working! hopefully.

So I made it to work like this. Inside the keyboard loop I put:

                usb_rawhid_buffer[usb_rawhid_fill++] = is_pressed ? 1 : 2;
                usb_rawhid_buffer[usb_rawhid_fill++] = col*KB_COLUMNS + row;
                if (usb_rawhid_fill >= 64) {
                    usb_rawhid_send(usb_rawhid_buffer, 0);
                    usb_rawhid_fill = 0;
                }

And into the end of the loop I put:

    // This addition sends rawhid packet if it is filled up.
    if (usb_rawhid_fill > 0) {
        while (usb_rawhid_fill < 64) usb_rawhid_buffer[usb_rawhid_fill++] = 0;
        usb_rawhid_send(usb_rawhid_buffer, 0);
        usb_rawhid_fill = 0;
    }

At the beginning I forgot to zero usb_rawhid_fill, and the thing failed after few packets was sent. But this seems to work now. I am getting a satisfying stream of messages from the device.

Here's a repository that contains the changes I made so far: https://github.com/cheery/ergodox-firmware

dragon788 commented 8 years ago

@cheery This sounds pretty sweet. I'd wondered some of the same things you were but I don't have the programming chops to tackle a project like this yet. It would be really interesting to see how your raw HID code performs vs some of the interpreted layers like TMK and QMK or kiibohd across various platforms.

cheery commented 8 years ago

@dragon788 It is only a small part to letting the ergodox control the layout. Not even that, it's a development tool to get that happen.

At the same time desktop systems are pulling it to the opposite direction, where the keys are interpreted just before they are passed to the app that needs them.