Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

newlib: add a scroll callback #682

Open
bog-dan-ro opened this issue Apr 7, 2018 · 6 comments
Open

newlib: add a scroll callback #682

bog-dan-ro opened this issue Apr 7, 2018 · 6 comments
Labels

Comments

@bog-dan-ro
Copy link

m4_zx_01_output_fzx is very powerful, it will be nice if the user can register a scroll callback.
This callback is useful when you want to stop the scrolling at some point or when you want to dynamically set the scroll lines count until pause.
e.g. int (*scrollCallback)() :

  • returns >= 0 scroll limit (number of scrolls until pause)
  • returns = no scroll limit
  • returns < 0 stops the scrolling
@aralbrec
Copy link
Member

aralbrec commented Apr 8, 2018

At the moment there are two ways to customize the terminal behaviour.

  1. Through ioctl() which is a means to send commands to the driver that is outside the stream.
    The list of ioctl()s understood by the spectrum's fzx driver is listed here. These ioctl()s manipulate flags within the driver's state and if you want to have a change made to the default, it's better to change the flag settings at compile time instead of running code to do that. This can be done with pragmas with a lot of the settings hidden in individual bits of CRT_OTERM_TERMINAL_FLAGS.

So, for example, you can stop pausing when the page fills by turning off pause which I think is what you mean by "no scroll limit": ioctl(1, IOCTL_OTERM_PAUSE, 0). Then the output will just continuously scroll as needed.

You can also reset the scroll limit so that the pause will not occur until another full page is printed: ioctl(1, IOCTL_OTERM_RESET_SCROLL).

It's also possible to get a pointer to the driver's state (called the FDSTRUCT). This is done with fdstruct = (unsigned char *)ioctl(1, IOCTL_OTERM_GET_OTERM). The pointer returned is actually 3 bytes behind the offsets shown in the link above because there is a lock in front of the structure. So to access the scroll limit member you'd have to use address fdstruct+20+3 instead of fdstruct+20. This isn't the best thing to do because these structures may change in time.

  1. By subclassing the driver. The drivers are all object oriented so you can change scroll behaviour by subclassing the fzx driver. This is done for the (wip) layer 2 example for the zx next target. The library's fzx driver is subclassed by tshr_01_fzx_smooth_scroll.asm which intercepts the OTERM_MSG_SCROLL scroll message and forwards all other messages to the library driver tshr_01_output_fzx. So this driver changes the behaviour of scroll so that it scrolls one pixel at a time instead of an entire row at a time. You can insert other scroll behaviour too.

Callbacks I don't think fit in very well because there's potentially a lot of them and adding them would just increase code size and slow things down since the library would have to supply do-nothing weak definitions. The subclassing, however, can let the user do exactly how much he wants. Or he can write his own driver, using library code where appropriate.

@bog-dan-ro
Copy link
Author

WOW so many information hidden from the user :), or there are docs but I didn't saw them ?
Anyway, thanks a lot for the ioctls and pragmas links, they are very useful!

Ok, maybe calling that callback too many times is an overhead.
But at least a simple callback which instructs the driver if it should continue scrolling or not (just ignore the rest of the text). The idea for this callback is to mimic the LIST basic command or unix cat some_big_text.txt | more and allow the users to cancel the scrolling.

@aralbrec
Copy link
Member

WOW so many information hidden from the user :), or there are docs but I didn't saw them ?
Anyway, thanks a lot for the ioctls and pragmas links, they are very useful!

There are no docs really. This is the part of the stdio model in the newlib which is "new" (well maybe closing in on two years old now) and this is not the final state of the driver model. The next version hopefully will be the last where non-blocking io will be added as well as better handling of tty emulation and conditioning the input stream which were found to be lacking recently. Then the disk model will go in and we can put in fatfs, etc, as available to all targets.

The list of io controls and pragmas are in the source code and I usually have to look at the source code to remember how the io controls are used. ioctl() takes a variable number of arguments and types depending on what the command is.

But at least a simple callback which instructs the driver if it should continue scrolling or not (just ignore
the rest of the text). The idea for this callback is to mimic the LIST basic command or unix cat
some_big_text.txt | more and allow the users to cancel the scrolling.

ok it sounds like you want to be able to signal the program when the scroll limit is met so it can choose to stop generating output or continue by scrolling another screenfull.

The way to do it now would be to subclass the driver and intercept the scroll message like the zxnext example in the previous message. You can add state to the driver as well, so you can add another two bytes to hold a callback address, so this new scroll message handler could read the callback address and if not zero call the function to notify it that the screen is full. The return value can indicate if the scrolling should terminate. This has to do two things :- it has to set a flag so that further characters are not printed (the driver is probably in the middle of printing a string since most things are delivered as whole strings rather than individual chars) and on exit from the driver (ie after a complete string is printed) it should clear this flag so that the next thing printed by the program actually is printed.

So I think there has to be two bytes for the callback function, another flag byte to disable printing, and insertion of some code that executes when the driver is about to exit so that the flag byte is cleared.

The only way to do all these things would be to subclass the driver as a simple callback alone will not do it.

@aralbrec
Copy link
Member

There may be a better way to do it by replacing the lowest level of the driver, in particular these two functions which are the first to receive a "write buffer" or "put many of the same char" message from stdio. So these could test for the flag being set and terminate early so that there is a quick exit there, clearing that flag at the same time.

Still thinking about the best way to do that but I think this is it.

@zx70 zx70 added the newlib label Apr 11, 2018
@zx70
Copy link
Member

zx70 commented Apr 11, 2018

It is quite out of topic, but you might be interested in the fact that f_ansi in the classic lib expects a buffer too (usually shortened to a single char buffer for simplicity).

@aralbrec
Copy link
Member

I think this is complicated enough that it should be made easier but it will probably be something that must be done in the next version of the driver model.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants