This, like many things, is a case where many different problems get complected, because no one is able to step back and tweak every level of the stack to cleanly separate out the relevant models/abstractions. Ideally, a computer keyboard would be able to directly send both an arbitrary number of named control functions, and arbitrary unicode text (either as full strings or as code units one by one). Instead though, k…
You're right about how the input stack is tangled up, but wrong about the ideal state. The keyboard is absolutely not the right place for this sort of intelligence.
No keyboard has 100k+ keys, so Unicode input is fundamentally a UI problem. Look at the enormous number of Chinese input methods, all of which need to cooperate closely with the GUI to work. Or heck, how do you type é? On OS X, you can either press option-e followed by e, or press and hold e, and select from a popup menu. These both require integration with the OS and GUI, and cannot be handled by the keyboard itself.
Even setting aside Unicode input, we still often need to know which keys were pressed. I'm programming a FPS game - what happens when the user presses the 2 key? If it's on the number row, it should select weapon #2; but if it's on the numpad, it should move the character backwards. So it's not enough to know the key's character; I need to know which physical key was pressed!
The layered approach you describe is confusing and error-prone, but it's necessary, because software needs to act at different levels. Some software wants very high-level Unicode text input, while others need to know very fine-grained keyboard layout details. All of the data must be bubbled up through all layers.