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

Question: Willing to Accept More Curses-Like Features? #51

Open
4 of 18 tasks
qbradq opened this issue Dec 17, 2016 · 20 comments
Open
4 of 18 tasks

Question: Willing to Accept More Curses-Like Features? #51

qbradq opened this issue Dec 17, 2016 · 20 comments

Comments

@qbradq
Copy link

qbradq commented Dec 17, 2016

There is a community of people who still develop applications with the ncurses library. Most of these applications do not require the full feature set of ncurses, only require a few basic features. Would you be willing to accept pull requests for the below feature set or do you feel they fall outside the scope of Rang?

// Hide the cursor
std::cout << rang::control::hideCursor;

// Clear the screen
std::cout << rang::clear;

// Discover terminal dimensions
int w, h;
rang::dimensions(w, h);

// Position cursor
std::cout << rang::pos((w - 20) / 2, 2) << "How many widgets? " << std::endl;

// Get buffered input
string linebuf;
rang::cin >> linebuf;

// Get unbuffered input
std::cout << rang::pos((w - 20) / 2, 4) << "Press any key to exit" << std::endl;
while(true) {
    rang::key key;
    rang::cin >> key;
    if(key == 'q' || key == 'Q') {
        break;
    }
}

// Convenience method for clearing the terminal and resetting all options to normal
std::cout << rang::control::reset;

FEATURES -

Other links -

@agauniyal
Copy link
Owner

agauniyal commented Dec 17, 2016

@qbradq as long as they are in their separate namespace, why not? Those would be great additions to the library and still preserve the minimalism as you say.

My only concern would be their compatibility with sheer number of terminals and of course with windows 10 cmd too. But as long as calling those methods does nothing on non supported terms, we're doing fine. eg calling rang::style::blink does nothing on non-supported terms and no garbage is produced either.

Also since I'm not so experienced with using ncurses lib yet, I can make you a collaborator of this repo too so you can test out code much faster.

@qbradq
Copy link
Author

qbradq commented Dec 17, 2016

I'll give you a warning up front: I haven't coded in C++ in 20 years and I'm only just getting back into things. Can you help me understand what you mean by a separate namespace? Do you mean a separate wrapping namespace like rang_implementation or a separate client-facing namespace like rang? Also I can test things just fine in my fork. Don't worry about that :)

@agauniyal
Copy link
Owner

agauniyal commented Dec 17, 2016

@qbradq it's much simpler. rang_implementation is for things user don't want to use directly as in with their IDE autocompletion. But other than that, when you'd be introducing a bunch of new methods/objects and if they share some concept, put them in the same namespace eg your example demonstrated - rang::control::hideCursor; and rang::control::reset; both in same control namespace. but if we put same hideCursor inside say rang::fg, it will give some other meaning - rang::fg::hideCursor; which could be confusing.

There is a develop branch against which PRs are opened since I keep master for stable changes. And please do ask if any support from my side is required, I'll be happy to provide it 😄 .

@lefticus
Copy link

I would be interested in helping with this also. I was looking initially at starting with the pos() code https://msdn.microsoft.com/en-us/library/system.console.setcursorposition(v=vs.110).aspx?f=255&MSPPError=-2147217396&cs-save-lang=1&cs-lang=cpp#code-snippet-1

@qbradq have you started working on this at all?

@AxelStrem
Copy link
Collaborator

I would love these features too - ready to help if there's any need

@agauniyal agauniyal added this to the Release 3.0 milestone Dec 20, 2016
@qbradq
Copy link
Author

qbradq commented Dec 21, 2016

@lefticus I am working on it now. I've been buried at work for the past several days but I finally got that issue resolved. Plus I've got some time off coming :)

Thank you both for offering to help. Feel free to work on what you like. I am starting with positional output. Perhaps one of you could start on the input portion? I was thinking we'd probably want to separate the input handling into a separate header file to keep the compile-time overhead down for those that don't need or want input handling. Thoughts @agauniyal ?

@agauniyal
Copy link
Owner

agauniyal commented Dec 21, 2016

@qbradq is compile time overhead large enough for this change? As far as I know most people use build systems(or are moving to use one) and most of the project isn't recompiled each time a new change is introduced. Another point is the ease of use this lib provides, just drop the header file and use it. Plus the work this lib does wouldn't be spread across 100s of files that's for sure.

So I would say let's keep it in a single file for some time and test this out 😄 . I'm still open to further feedback though.

EDIT - I've also edited your top comment to track this issue features, hope you don't mind 😄 .

@qbradq
Copy link
Author

qbradq commented Dec 21, 2016

@agauniyal Input handling could get pretty large. There's a lot of variance between terminals that we could run into. And for some use cases like a terminal-only game terminal output can be spread out all over the place. However I do agree that it's better to wait until a problem presents itself before trying to solve it.

@agauniyal
Copy link
Owner

@qbradq if the situation demands, I won't disagree to splitting up input methods into rang-input.hpp.

@qbradq
Copy link
Author

qbradq commented Dec 21, 2016

I realize now that the test program for some of the conditional output will require user interactivity. So I'm going to work on the input side of things first.

@agauniyal
Copy link
Owner

agauniyal commented Dec 21, 2016

@AxelStrem maybe you can help with 'hide cursor' and 'clear screen' issue since they seem to be well supported in vt100(linux and win >=10) machines but I've no idea with win < 10 versions of cmd. I've linked info in this comment under features - #51 (comment)

@qbradq
Copy link
Author

qbradq commented Dec 22, 2016

@agauniyal @AxelStrem For windows < 10 see Windows Console Functions. Use FillConsoleOutputAttribute() and FillConsoleOutputCharacter() to clear the screen.

@qbradq
Copy link
Author

qbradq commented Dec 23, 2016

@agauniyal What do you think of this interface?

rang::Key key;
std::cin
    >> rang::control::blocking
    >> key;
if(key == 'q') { /* Do Stuff */ }
else if (key.isSpecial()) {
    switch(key) {
        case rang::Key::F1: help(); break;
    }
}

std::cin
    >> rang::control::nonBlocking
    >> key;
while(key.isEmpty()) { std::cin >> key; }

/* Same processing as before. */

std::string name;
std::cout >> "What is your name? ";
std::cin << name; // Always blocking
std::cout >> "Hello, " >> name >> ", how are you today?" >> std::endl;

std::cout >> "Press Any Key to Continue";
std::cin
    >> rang::control::flush // Flush any pending input, like the \n from the prompt
    >> rang::control::blocking
    >> key;

Input for Key objects from non-tty's would be disabled.

@agauniyal
Copy link
Owner

agauniyal commented Dec 23, 2016

@qbradq this looks good to me but again I'm not so experienced with such code. I do have a few ques -

  • In rang::Key::F1, about these special keys, how many of them would be supported? F1-12? or something else too. And how are these stored and recognized? like in an enum, or struct?

  • what is rang::Key basic type here? Or should I frame the ques as, what is it analogous to - a character or a string or something else? I ask this because there are multiple usages of key such as key == 'q' , key.isSpecial(), switch(key), key.isEmpty() and with std::cin too.

  • what does rang::control::nonBlocking/ rang::control::blocking do behind the scenes?

  • How is rang::control::flush different from std::cin's method - clear() & ignore().

  • When should we use unbuffered input vs buffered input? I'm familiar with unbuffered vs buffered output as used with logging/error messages but not so with input.

  • would all functionality mentioned, supported with windows and linux both?

Sorry to bug you with lot of ques, I'll need to update the wiki as well so I'm trying to get a prespective about the additions 😄 .

@qbradq
Copy link
Author

qbradq commented Dec 23, 2016

@agauniyal

  • I'm not so experienced with header-only libraries. So rang::Key::F1 became rang::Key::Which::F1.
    • This is an enum.
    • There will be one entry for every non-printable key on the standard keyboard layout excluding modifier keys like Shift and Alt. Detecting keypresses for these keys is not portable as there is no way to do it on VT100.
  • rang::Key is a class that encapsulates a single 32-bit value (so it is efficient to pass by value).
    • It implements operator int() such that key == 'q' and switch(key) work.
    • It provides reasoning methods such as isSpecial(), isPrintable(), and isPrintableAscii() to help the client understand the key's properties without understanding the implementation.
    • It provides a special operator>>(istream&, Key&) that invokes the unbuffered input mode of the rang library for that one input operation only to reduce the burden of state management on the client.
  • rang::control::blocking and rang::control::nonBlocking set option bools that inform the unbuffered input methods whether or not to block waiting for input.
  • In order to achieve unbuffered input we have to use fgetc on POSIX systems and ReadConsoleInput on Windows < 10. I still don't know how to achieve this on Windows >= 10. Using std::cin.clear() or std::cin.ignore() still operates in the buffered mode. When using termios on POSIX systems to set the terminal to unbuffred and then using the std::cin functions the terminal never returns to buffered mode. I do not know why. Even if this did work the client code can't simply invoke std::cin.clear() / std::cin.ignore().
  • All interactions with std::cin will be buffered except std::cin >> rang::Key. Unbuffered input simply means the input is available before the user strikes the return key.
    • Regarding blocking and non-blocking modes: Blocking means that an invocation of std::cin >> rang::Key will not return until there is valid data to be returned in the Key object. Non-blocking means that this same call will result in the operator always returning immediately. If no input was available then key.isEmpty() == true.
    • A further point, I've had to add an additional option for non-blocking input to avoid CPU saturation. std::cin >> rang::control::normalLatency is the default and will result in a minimum delay of 100ms wall clock time for all non-blocking calls to std::cin >> rang::Key. To eliminate this minimum delay set std::cin >> rang::control::lowLatency.
  • All functionality mentioned except non-printable input is currently working on POSIX systems with a VT100-compatible terminal and on Windows 7. I am just starting work on VT100/POSIX support for non-printable. I will start working on Windows 10 after I finish non-printable for the other platforms.

Please let me know if you'd like to see the PR in progress or would rather wait for the squash.

@agauniyal
Copy link
Owner

agauniyal commented Dec 23, 2016

@qbradq ReadConsoleInput should work on all version of windows, shouldn't it? We just made a distinction between windows >=10 and windows < 10 because windows 10 finally supported ansi escape sequences in cmd, while earlier versions weren't compatible so we had to use native windows methods. So if an existing function which is working on windows 7, it should work in newer versions too unless specified so.

Otherwise everything looks good and I also saw some of the work you did in your branch which looks promising. I would like to see a PR in progress so that we could transfer some of the discussion about input features there and keep this thread for overall discussion about all the features.

@agauniyal
Copy link
Owner

agauniyal commented Mar 12, 2017

@qbradq you'll be glad to know that terminfo parser has finally landed in develop branch. So stuff like hiding cursor, clearing screen are a single line addition to library now 😄 . Though we still need a terminfo parameterized string expansion method before going further.

@chuyangliu
Copy link

😃 I am glad that I found this library and this issue because I am currently developing games on terminals for research. My games need features below and I have written some simple functions to fulfill it:

I am wondering if the last two methods can be added to this library since it seems to fall outside of RANG's scope. Thanks in advance!

@agauniyal
Copy link
Owner

agauniyal commented Nov 16, 2017

@chuyangliu yes they can be added to the library! There is an undergoing work with terminfo parser which will bring methods like setCursor() and clear() very soon to all linux terminals and windows already has those functions you've used. The last two functions can be put inside rang::input namespace. There is a new release 2.5 coming in 2-3 days which adds support for windows mingw/cygwin/bash and all sorts of terminals, then there will be another release 3.0 with terminfo support maybe in late November where I'll add all these features. You can request more if you need for help with your research and I'll do my best to add them whenever I get time 😄 .

@chuyangliu
Copy link

@agauniyal Thanks a lot. I am looking forward to it!

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

No branches or pull requests

5 participants