A Dvorak layout that works when layout is switched to Dvorak in the operating system (Model 01 layout conflicts with software-defined keyboard layout)

plugin
question

(tyler) #1

Model 01 layout conflicts with software-defined keyboard layout

It seems that in order to use the default Dvorak layout*, you must have QWERTY (“English (US)”) selected as your keyboard layout in your operating system*. Does this seem odd or contradictory to anyone else?

(* default Dvorak layout meaning https://github.com/keyboardio/Kaleidoscope/wiki/Common-Alternate-Layouts#dvorak, which I see is now even included in Model01-Firmware.ino in master using PRIMARY_KEYMAP_DVORAK so I will have fewer merge conflicts when I pull down master now—nice!)

(*I’m using Ubuntu Linux, but I’m pretty sure it would be the same on any other operating system.)

If I try to use the Model 01 with my OS set to Dvorak then typing aoeui actually comes through as ar.gc instead! Arg, indeed!

I’m sure there’s a logical explanation for it… Something like… When you press the key corresponding to Key_O in the firmware (the one with the S keyboard on mine since I still haven’t switched from the default QWERTY keycaps), the Model 01 emits a key scan code (?) for a letter O. The OS then interprets the scan code according to the software-defined keyboard layout, sees that in Dvorak, O is remapped to R, and sends an R to the system instead of an O. But that’s not what I want it to do!

Is there no way to make the Model 01 emit an “absolute” letter O that will always be interpreted as an O rather than relative to any host OS setting?

I’m guessing not… I guess the OS-level keyboard layout setting, by definition, defines how incoming [key codes] get interpreted. It seems like OS-level keyboard layouts are really kind of a crude workaround for the fact that most physical keyboards don’t themselves provide any way to switch layouts. They’re really just faking it, “tricking” the system into thinking you’d typed an O when you actually typed an S on your QWERTY-only keyboard.

This illusion works great … until you throw a Model 01 into the mix. :wink: In fact, software-defined keyboard layouts are the only way I can type Dvorak on my laptop keyboard on my cheap, generic QWERTY USB keyboard — but it prevents the actual character emitted by the physical keyboard from coming through as-is, which becomes a problem …

When is this a problem?

It seems that it’s primarily a problem when you have multiple keyboards — which everyone who uses their Model 01 on a laptop is going to have! :slight_smile: You have your built-in laptop keyboard, and then you have your Model 01 plugged into it via USB. And some of us may have another “normal” external keyboard plugged in as well (at least I do)…

It’s no problem if you’re just using QWERTY on all of your keyboards. Or if you only want to use Dvorak on your Model 01 (and for some reason want to use a different layout, QWERTY, on your QWERTY-only keyboard(s)). Or if you only want to use Dvorak on your “dumb” keyboards (one that don’t let you switch layouts in hardware, thus requiring you to use software-based layout trickery) and aren’t using a Model 01.

It’s only a problem if you want to continue touch typing Dvorak on your “dumb” keyboards and also on your Model 01.

Currently, whenever I switch from my Model 01 back to a dumb keyboard, I have to either hunt and peck for the keys since I don’t touch type QWERTY anymore, or I have to remember to switch back to Dvorak with the OS layout switcher. Both of those options are annoying, especially when you switch between keyboards frequently. (Then when I switch back to the Model 01 again, if I’ve switched the OS layout, I have to remember to switch it back to QWERTY again.)

How I’d like it to work…

In case it’s not obvious from my rambling above, what I’d like is to be able to leave my OS layout switched to Dvorak (so that I can continue to type Dvorak on my dumb keyboards, like I’m used to) and still be able to type Dvorak on my Model 01.

Has anyone else wished for that ability?? Surely I’m not alone out there, with the number of people using alternate layouts as high as it is in the Keyboardio community. (The same problems would apply to any alternate layout—this isn’t specific to Dvorak.)

And if you have wished for it, has anyone taken the time to come up with a Model 01 layout/plugin/function/converter/tool that allows it to work that way?

I know it’s possible. You’d just have to account for the extra QWERTY-to-Dvorak translation that the OS is going to do and reverse it. So in order to make the Key_O key come across as an O, you have to replace it with a Key_S like in the Model 01 QWERTY layout. In fact, all of the letters will go back where they are in the QWERTY layout. “So why don’t you just leave your Model 01 on the factory QWERTY layout then?”, you ask. Well, because:

  1. That only gets you partway there. That gets all the letters to come through correctly and some punctuation (like ;), yes, but it’s doesn’t get all punctuation characters in their correct/expected locations. For example, = key in QWERTY should be a / in the Model 01 Dvorak, but instead gives me neither: it shows a ] character instead — which is indeed what I would expect on a “regular” keyboard, but I want it to match the Model 01 Dvorak and give me a / ( since Model 01 variant of Dvorak is a bit more logically organized than a naive QWERTY-to-Dvorak character translation would give you).

For reference, here is what software-mapped-Dvorak gives me on factory-default Model 01 QWERTY (looking only at the printable characters on the 3 main layers for now) vs. (every other line) how I want it to show up (to match Model 01 Dvorak):

                # Row 1
`',.py  fgcrl]  # Primary (Model 01 QWERTY on Dvorak)
`',.py  fgcrl/  # Primary (Model 01 Dvorak on QWERTY)

~"<>PY  FGCRL}  # Shift (Model 01 QWERTY on Dvorak)
~"<>PY  FGCRL?  # Shift (Model 01 Dvorak on QWERTY)

 ' .     ?+/=   # Function (Model 01 QWERTY on Dvorak)
 ' .     {}[]   # Function (Model 01 Dvorak on QWERTY)

                # Row 2
 aoeui  dhtns-  # Primary (Model 01 QWERTY on Dvorak)
 aoeui  dhtns-  # Primary (Model 01 Dvorak on QWERTY)
                                                       
 AOEUI  DHTNS_  # Shift (Model 01 QWERTY on Dvorak)
 AOEUI  DHTNS_  # Shift (Model 01 Dvorak on QWERTY)
                                                       
            s-  # Function (Model 01 QWERTY on Dvorak)
            s-  # Function (Model 01 Dvorak on QWERTY)

                # Row 3
 ;qjkx  bmwvz[  # Primary (Model 01 QWERTY on Dvorak)
 ;qjkx  bmwvz=  # Primary (Model 01 Dvorak on QWERTY)
                                                       
 :QJKX  BMWVZ{  # Shift (Model 01 QWERTY on Dvorak)
 :QJKX  BMWVZ+  # Shift (Model 01 Dvorak on QWERTY)
                                                       
           v\|  # Function (Model 01 QWERTY on Dvorak)
   j       v\|  # Function (Model 01 Dvorak on QWERTY)
  1. I want to be able to customize my layout. Which means having a human-readable layout that I can edit. What’s shown in the layout source code should be what actually gets output.

I shouldn’t have to write LSHIFT(Key_Minus) where I actually want it to output Key_LeftCurlyBracket. Look at this crazy change I had to make to the Function layer in order to actually keep curly brackets in their standard (and perfectly reasonable) locations (in all standard Model 01 layouts, which normally only change the Primary layer):

-   Consumer_PlaySlashPause,    Consumer_ScanNextTrack, Key_LeftCurlyBracket,     Key_RightCurlyBracket,    Key_LeftBracket, Key_RightBracket, Key_F12,
+   Consumer_PlaySlashPause,    Consumer_ScanNextTrack, LSHIFT(Key_Minus),        LSHIFT(Key_Equals),       Key_Minus,       Key_Equals,       Key_F12,

What I’d like, in other words, is a Kaleidoscope plugin that lets me tell it “I want to use this with the layout set to Dvorak in the operating system” and have it do all the work of translating what key codes we actually need to output to get the desired character as defined in the layout.

In other words, I should be able to just say something like let host_layout = "dvorak" and be able to just use the PRIMARY_KEYMAP_DVORAK layout option as-is. That should be a pretty easy plugin to write, right?


(tyler) #2

Here’s what I came up with, in case it’s helpful to anyone else. This layout lets you type Dvorak on your Model 01 layout with layout switched to Dvorak in your operating system .

  [PRIMARY] = KEYMAP_STACKED
  (___,          Key_1, Key_2, Key_3, Key_4, Key_5, Key_LEDEffectNext,
   Key_Backtick, Key_Q, Key_W, Key_E, Key_R, Key_T, Key_Tab,
   Key_Tab,      Key_A, Key_S, Key_D, Key_F, Key_G,
   ___,          Key_Z, Key_X, Key_C, Key_V, Key_B, Key_Escape,
   Key_LeftControl, Key_Backspace, Key_LeftShift, Key_LeftGui,
   ShiftToLayer(FUNCTION),

   Key_PageUp,    Key_6, Key_7, Key_8,     Key_9,         Key_0,         LockLayer(NUMPAD),
   Key_PageDown,  Key_Y, Key_U, Key_I,     Key_O,         Key_P,         Key_LeftBracket,
                  Key_H, Key_J, Key_K,     Key_L,         Key_Semicolon, Key_Quote,
   Key_Enter,     Key_N, Key_M, Key_Comma, Key_Period,    Key_Slash,     Key_RightBracket,
   Key_LeftAlt, Key_RightShift, Key_Spacebar, Key_RightControl,
   ShiftToLayer(FUNCTION)),
  [FUNCTION] =  KEYMAP_STACKED
  (___,      Key_F1,           Key_F2,      Key_F3,     Key_F4,        Key_F5,           Key_CapsLock,
   ___,      ___,              Key_mouseUp, ___,        Key_mouseBtnR, Key_mouseWarpEnd, Key_mouseWarpNE,
   Key_Home, Key_mouseL,       Key_mouseDn, Key_mouseR, Key_mouseBtnL, Key_mouseWarpNW,
   Key_End,  Key_PrintScreen,  Key_Insert,  ___,        Key_mouseBtnM, Key_mouseWarpSW,  Key_mouseWarpSE,
   ___, Key_Delete, ___, ___,
   ___,

   Consumer_ScanPreviousTrack, Key_F6,                 Key_F7,                   Key_F8,                   Key_F9,          Key_F10,          Key_F11,
   Consumer_PlaySlashPause,    Consumer_ScanNextTrack, LSHIFT(Key_Minus),        LSHIFT(Key_Equals),       Key_Minus,       Key_Equals,       Key_F12,

                               Key_LeftArrow,          Key_DownArrow,            Key_UpArrow,              Key_RightArrow,  ___,              ___,
   Key_PcApplication,          Consumer_Mute,          Consumer_VolumeDecrement, Consumer_VolumeIncrement, ___,             Key_Backslash,    Key_Pipe,
   ___, ___, Key_Enter, ___,
   ___)

Note that this layout includes a few unrelated changes as well, but they should be easy to ignore.


(tyler) #3

Now to try writing a Kaleidoscope plugin to make the layout what-you-see-is-what-you-get again… :slight_smile:


(tyler) #4

Or can anyone think of a better way to solve this problem…?


(Gergely Nagy) #5

This can certainly be surprising, indeed. Before I explain why this happens, I’d like to clarify why the Model01’s Dvorak layout requires the host to have US QWERTY selected as the keyboard layout: because we wanted to allow people to switch between the default QWERTY layout and Dvorak without having to switch the layout on the host side too. If the firmware-Dvorak assumes a QWERTY layout, you only have to change one thing.

This is more or less what is happening, indeed.

Sadly, there is no such way.

I’m using my Model01 with a laptop, but I don’t have this problem, because I don’t type on the laptop keyboard. O:)

But indeed, anyone who has multiple keyboards, and at least one of them is not programmable enough to make it type Dvorak, will have this problem, until they remap their Model01.

I’d suggest a pretty simple plugin: one that does no translation, but provides new constants to use. Stuff like this:

#define DV_A Key_A
#define DV_O Key_S
#define DV_E Key_D

And so on and so forth. Then you can use DV_<foo> in your keymap, and you’ll get what you see, without having to do any run-time translation!

Granted, this will not be the same as the Dvorak keymap in the factory firmware… we could do something like have two headers: Kaleidoscope-Layout-Dvorak/firmware.h and Kaleidoscope-Layout-Dvorak/host.h. Both would define the same constants, but firmware.h would define them with the assumption that the host is in US QWERTY, while host.h would define them with the assumption that the host is in Dvorak. This way, you could have the same layout, and switch between firmware-Dvorak and host-Dvorak by changing an include.

(We should, at some point, figure out a way to better support both alternative layouts, and localized layouts.)


(Tim Holt) #6

I consider it an advantage that two keyboards plugged in (one being the Model 01) can have different layouts. If someone else needs to type on my machine, they can just use my legacy keyboard. They’d probably want to anyway.

Incidentally, I have a Surface Pro, which is just about the perfect laptop-like PC to use with this keyboard.


(Andrew Gallagher) #7

It is possible to apply a firmware layout that behaves reasonably under a large number of OS keymap settings. I have developed five variants that cover all major Latin layouts.

Try it out here: https://github.com/andrewgdotcom/Model01-Firmware - you probably want either the ngetal2 or merlin2 layout for qwerty/dvorak switching.


(Not Available) #8

Make a qwerty layer, so when you are on Dvorak on the software level, you can just toggle to qwerty and then turn the toggle off to quickly switch to Dvorak when you use a qwerty pc without having to dig through the settings.


(tyler) #9

Make a qwerty layer, so when you are on Dvorak on the software level, you can just toggle to qwerty…

If only it were that simple. As noted in OP, switching to firmware Qwerty only gets you partway there (some punctuation are not right, etc.).

Being able to switch between 2 layers/variants would still be useful. My “Qwerty” layout would just have to be a slightly modified one, that’s all (since I would never use it to actually type in Qwerty, but rather to type Dvorak when OS layout is switched to Dvorak).


(tyler) #10

Interesting idea, though I’m not sure I understand how you could make it work under a large number of OS keymap settings. And I couldn’t understand from the Readme how it applied to my situation or how I would know to choose ngetal2 or merlin2. I guess I’ll just have to try it out! (Sorry that I haven’t yet.)


(tyler) #11

Thanks for the comments and ideas!

Great idea. Definitely a step up from what I have now at least.

Right! That’s kind of along the lines of what I was thinking… except I’d like to be able to switch at run-time rather than compile time…

I would love to read about or be a part of that discussion! :slight_smile:


(Andrew Gallagher) #12

Well, a standard pc104/105 keyboard works with all(*) IBM-compatible OS keymaps without remapping (obviously!), and the only keys that have moved from their IBM positions in the Model01 are in the columns to the left of QWERTY q and right of QWERTY p, which hold QWERTY symbols (I call these “orphans”), and modifier keys (Shift, Return etc.). So in principle all you need to do is find new locations for the orphans and modifiers that break as few expectations as possible. Given that under the thumbs is the natural and intended location for modifiers, we’re left with the task of redistributing eight orphan keys around the edges of the layout. In practice, since most keyboard layouts make logical assumptions about relative locations (e.g. square brackets beside each other) we only ever need to move about five keys or so. Which particular five keys to move depends on the baked-in assumptions of each layout, and there are only a few of them. So there only really need to be five alternative layouts, three of which are only
required for one or two languages each.

The alternative layouts are controlled by commenting in and out particular lines in the layouts.h file. If the README.abg file is confusing then please ask; feedback is welcome.

(*) except japanese and brazilian standard, because they have EVEN MORE extra keys.