rust-embedded-community / ssd1306

SSD1306 OLED driver
Apache License 2.0
301 stars 68 forks source link

Newline characters are rendered as spaces in TerminalMode #78

Closed mjadczak closed 4 years ago

mjadczak commented 5 years ago

TerminalMode does not treat newline characters in any special way. Instead, they hit this case which just renders an empty bitmap for that character. I think that the TerminalMode would be much more useful if it recognised \n and skipped to the beginning of the next line.

therealprof commented 5 years ago

@mjadczak that observation is absolutely correct. The reason for that is that this is the only way to operate completely stateless since the display is essentially write-only. It might be possible to keep track of the cursor position and advance accordingly to the next line on a newline "character".

Would you like to give it a try?

mjadczak commented 5 years ago

I was making the assumption that the terminal mode had to keep track of the position anyway in order to know when to go to the next line, but it seems that this is done in the display chip itself.

In any case, I'll try to get a bit more familiar with the display's commands and give this a go (it would also be nice to support \r, and I also see some references to "scrolling" in the commands - when put together it would make for a more usable mode that better emulates a "terminal").

therealprof commented 5 years ago

@mjadczak The interesting bit about this mode is that it does progressive rendering so it only needs to write the data which is supposed to be displayed next (vs refreshing the whole frame with every update). Unfortunately the SSD1306 is pretty dumb which means this also significantly limits the options what we can do. Newlines should be pretty easy to implement if we know the vertical position (simply update the position with every printed character and if a newline is encountered, fill up with the required amount of blanks to reach the first position in the next line).

Anything beyond that will likely require some voodoo.

Scrolling would be cool, maybe that is indeed possible with some command foo; haven't looked into it. Carriage return would also be nice to have but that would not only require the column position but also the ability to reposition the memory pointer in the display. Might be possible, but maybe not.

mjadczak commented 5 years ago

Yeah, my mental model (before reading the actual datasheet) was that it functioned as some sort of addressable character display in this mode, but I realise that's incorrect. I'll have a read through the datasheet and see what's possible here (though I agree that newline implemented with writing spaces is a simple solution which should definitely be possible and I'll attempt that first).

mjadczak commented 5 years ago

I think there is a hierarchy of what we could do to TerminalMode in order to improve it in this regard, with each step increasing both the usefulness but also the complexity of the mode. My feeling is that at some point it becomes a different mode (CharacterMode ?) but I'm not sure at which point.

  1. We can support \n by maintaining a simple u8 counter which is incremented every time we write a character. When we encounter a \n character, we mod the counter with the line width to obtain "the number of characters on the current line", from which we can easily get the number of spaces we need to insert to go to the next line. We can manually wrap this counter to 0 when we wrap around to the top left corner, or just let it wrap around naturally if the line width divides into 256.
  2. The only way to change the "address" we are writing to in horizontal mode (I think this driver calls it column mode?) is to re-set the text area itself (and our address is reset into the top-left corner of this new area). This doesn't seem like a sane way of handling things like \r (though could, in theory, be done). Instead, at this level I would propose moving into page-addressing mode, and keeping track of both the horizontal and vertical position (in terms of characters, not pixels) on the screen. This would mean we would have to handle line-wrapping logic, but would easily be able to handle both \r and \n. As a bonus, we could offer an explicit set_cursor(x, y), again in character coordinates, to offer a more powerful but still bufferless interface for users (e.g. outputting a few fixed-width pieces of information on the screen in different places).
  3. Even in page addressing mode, the display is write-only. Therefore, to implement any sort of terminal-like scrolling, we need an internal buffer (the "scrolling" I saw mentioned in the datasheet is actually intended for smaller displays, so that the whole 128x64 buffer can be hardware-scrolled on the smaller panel in order to display all of it). However, if we know we are only writing characters, it may be beneficial to cut the buffer size in 8 compared to the GraphicsMode, and buffer the contents of the screen as characters rather than as pixels (I imagine this would be a circular buffer of lines). Then, on detecting a line wrap at the bottom of the screen, we can shift the circular buffer and redraw the whole screen, effectively implementing terminal scrolling.

Any thoughts on the above? I'm happy to give all of these a go, but as I mentioned I'm just not sure they all belong in TerminalMode.

therealprof commented 5 years ago

I'd start with the 1) and then take it from there. 2) and 3) might be implementable using some clever trickery but introducing a buffer is a big no-no.

mjadczak commented 5 years ago

I've implemented 1 in #79. 2 would require some changes to the DisplayProperties to allow an API to use the paging mode for arbitrary addressing, but I also think it's reasonably straightforward to implement.

therealprof commented 5 years ago

@mjadczak Arbitrary addressing would also enable partial screen updates which is would be a great thing to have IMHO.

mjadczak commented 5 years ago

@therealprof I've made another PR #80 (which supersedes the earlier one) which implements 2) and therefore has arbitrary addressing and \r handling.