I am using ncurses to write a text-based client in C. The main loop of the program simply blocks until a keypress is detected and then handles it and continues to wait for another keypress.
I have a single thread I launch (posted below) that blocks (using select) waiting for input from the server and when it receives it adds it to the chat log buffer and prints the buffer to the screen. It works perfectly.
I know ncurses is not thread safe but my understanding of threads is that as long as I make 100% sure that only one thread is making calls to ncurses at a time, it will work fine.
My issue is with the cursor position.
It is modified with the line move(height+1, curx);
and no matter what values I pass to it, ncurses seems to ignore the call entirely and places my cursor at a different position. I cannot seem to influence it.
To further explain the problem, in my main thread (the keypress loop), I use the same mutex blocking. When the cursor is updated in those sections of code, it works as planned. When it is updated from the receive thread below, the cursor call is ignored.
Any ideas?
receive thread
char buf[512];
fd_set read_fds;
FD_ZERO(&read_fds);
int nbytes;
for (;;) {
read_fds = master;
select(sockfd+1, &read_fds, NULL, NULL, NULL);
pthread_mutex_lock(&mutexdisplay);
memset(&buf, 0, sizeof buf);
nbytes = recv(sockfd, buf, 512, 0);
buf[nbytes] = 0;
add_chatmsg(chatlog, &numchatlog, buf);
// erase window
werase(chat_window);
// redraw border
wborder(chat_window, '|', '|', '-', '-', '+', '+', '+', '+');
// scroll completely into the future
chatlogstart = numchatlog-1;
// print the chat log
print_chatlog(chatlog, &numchatlog, &chatlogstart, &height);
move(height+1, curx);
// refresh window
wrefresh(chat_box);
wrefresh(chat_window);
pthread_mutex_unlock(&mutexdisplay);
}
May be its too late for an answer to this question.
You didnt specify on which window you want to control the cursor. I am assuming that you wanted the cursor at the (height+1, curx) position of the chat_box or chat_window.
One can control the cursor with move funtion in the stdout widow which is the original terminal but not on the windows(chat_box and chat_window) you created. The function for for controlling the cursor in user created windows is wmove.
int move(int y, int x);
int wmove(WINDOW *win, int y, int x);
Hope this helps others facing similar problem. Please do point if this is not the correct solution.
Related
I'm writing a Z80 simulator with a GUI running on GTK (https://github.com/clancyj4/z80sim/tree/dev for those interested).
My problem is that I need to trap the IN opcode and allow a window to be edited to provide the IO codes. Let me show you what I've got so far:
/* get a byte (char?) from the input part of the Port window */
/* What we really want to do is have a queue */
BYTE IOPort_IN(int port)
{
char whole_buffer[IOINBUFLEN * 4]; /* 4x in case of Hex */
BYTE c;
int i;
if (IOPort[port] == NULL) /* struct exists? */
Create_IOPort_Struct(port);
printf("IOPort_IN: port=%d in_len=%d in_ptr=%d\n",
port, IOPort[port]->in_len, IOPort[port]->in_ptr);
if (IOPort[port]->in_len == 0)
{
sprintf(tstr, "Port %d requires input", port);
Add_to_Log(tstr);
show_log(TRUE);
gtk_text_buffer_set_text(inportprompt_textbuffer, "Input Required", -1);
/* somehow keep the gtk main loop running so the interface updates and allows the editing of the ioportin_textbuffer,
but wait for the input submission button the be pressed */
i = gtk_text_buffer_get_char_count(ioportin_textbuffer);
printf("in port buff len is %d.\n", i);
}
return(c);
}
The trapping of the opcode is OK and this function is called. The trouble is that I have no idea how to implement the comment in the middle.
The aim is to have the rest of the simulation on hold, but with the GTK main loop still running, so the Log window etc get updated and I can enter a string in the IO window. But it waits until a submit button is pressed. I can freeze the Z80 simulation by setting a flag, so that's no problem.
I have a nasty feeling that I haven't expressed the problem very well, so please bear with me and ask any questions that would clarify the situation.
Cheers,
Justin.
I sidestepped the issue and forced a return of 00 if there wasn't an input queue already in place. With plenty of warnings, of course.
I would still like to know how to get GTK to hold execution on the main thread until a sub-window has been activated. When I know, I'll put the answer in here.
This question is a bit unclear. This will setup the callback for a function, it will call whatever function you pass through it.
void button_connect_callback(GtkWidget *button, void *button_callback)
{
g_signal_connect(button, "clicked", G_CALLBACK(button_callback), NULL);
}
I have some tutorials that might be of help here
I've started to learn about ANSI escape sequences online through the magic of Google. It is neat being able to position the cursor \e[row;colH on the screen and set the colors of the outputs (ie: \e[31m).
Next I would like try and see how the mouse able to be captured in a virtual terminal. I realize this code is not portable, and I know I can use ncurses or some other curses library, but the goal here is to learn how it works, not write production code with it.
I have tried \e[?1003h and it starts to fill the screen with mouse events. (Pretty cool!) However, how do I capture these in a C or C++ program?
I saw an example of what I would like to do in PHP: https://stackoverflow.com/a/58390575/1770034
However, when I try to port the code over to something in C it just locks up in the while loop. (Tested with GDB to find that out.)
#include <stdio.h> //bring in printf and fread
int main()
{
system("stty -echo"); //make the terminal not output mouse events
system("stty -icanon"); //put stdin in raw mode
printf("\e[?1003h\e[?1015h\e[?1006h"); //start getting mouse events
char* buffer[255];
while(fread(buffer, 16, 1, stdin)) // <-- suppose to read in the mouse events
{
printf("here"); //Did you actually work?!
}
printf("\e[?1000l"); //Turn off mouse events
system("stty echo"); //Turn echoing of the display back on
return 0; //end the program in a successful state
}
I have also tried scanf and it just locks up until I hit enter, and I'm not convinced it is seeing the mouse events.
Edit
I now have some working code that spits out the mouse events.
Here is the updated code from applying the accepted answer to this question:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
int main()
{
system("stty -echo"); //don't show mouse events on screen
system("stty -icanon");
fprintf(stderr, "\e[?1003h\e[?1015h\e[?1006h"); //use stderr since not buffered turn on mouse event capture
char buffer[16] = " ";
char previousBuffer[16] = " ";
//Make standard in not be blocking
int flags = fcntl(stdin->_fileno, F_GETFL, 0);
fcntl(stdin->_fileno, F_SETFL, flags | O_NONBLOCK);
for (int hunSeconds = 0; hunSeconds < 500; hunSeconds++) //Run for 50 seconds
{
read(fileno(stdin), buffer, 16); //read mouse input
if (strcmp(buffer, previousBuffer) != 0) //only show event if it is different
{
fprintf(stderr, "%s", buffer);
strncpy(previousBuffer, buffer, 16);
}
usleep(100); // sleep for .1 seconds
}
printf("\e[?1000l"); //turn off mouse events
system("stty echo"); //turn on screen echo again
return 0;
}
Two problems:
printf is (using stdout) buffered, so there's no guarantee that the escape sequences got to the terminal before attempting to read.
stdin isn't necessarily the terminal (though it might be). Again, fread is buffered (and you may not get the result as promptly as you wish).
Since stderr is not buffered, it would help to send the escape sequences with that stream. Rather than using fread, it can help to use read, e.g.,
read(fileno(stdin), buffer, 16)
I am writing a simple instant messaging client in c. It's currently working well, however if a user is typing and receives a message while typing, the message displays AFTER the text, then the user continues on the line below. It would look like this:
USER: I am trying to ty...FRIEND: Hello
pe a message. <--- (the end of the users message)
My idea is:
Somehow force the current data from stdin and load it into a buffer, then use a \r before printing FRIEND: to erase what is on the line, then print from the buffer. Does anyone have any concrete examples of how to accomplish this task?
The final result should be
FRIEND: Hello
USER: I am trying to type a message
The user started typing the message, received a message, the stdin line was shifted downwards, then the user completed their message.
Note: I am running GNOME Terminal 3.6.2 on the newest version of Linux Mint
The usual way to do this is using ncurses (any flavor of curses would work), accepting the input in one window and writing the result to another. Here is a short example:
#include <curses.h>
int
main(void)
{
bool done = FALSE;
WINDOW *input, *output;
char buffer[1024];
initscr();
cbreak();
echo();
input = newwin(1, COLS, LINES - 1, 0);
output = newwin(LINES - 1, COLS, 0, 0);
wmove(output, LINES - 2, 0); /* start at the bottom */
scrollok(output, TRUE);
while (!done) {
mvwprintw(input, 0, 0, "> ");
if (wgetnstr(input, buffer, COLS - 4) != OK) {
break;
}
werase(input);
waddch(output, '\n'); /* result from wgetnstr has no newline */
waddstr(output, buffer);
wrefresh(output);
done = (*buffer == 4); /* quit on control-D */
}
endwin();
return 0;
}
If you want to learn about VT100 control codes (as distinct from ECMA-48), vt100.net has manuals for some terminals.
Regarding the link VT100 control codes: that is a source of misinformation, as noted in the ncurses FAQ How do I get color with VT100?
I'm working on a class project in which I must write a command line shell with the following requirements:
The shell must able to read buffered input
Buffer should be 64 characters
Error conditions should be handled
Exceeded buffer size
Interruptions (when a signal arrives) – see the man page for read()
Invalid input (unparsable characters, blank lines, etc)
Any other error that may be encountered.
Shell must have a history of at least 20 items, and the history must not be of a static size. When the history buffer is full, the oldest item is removed and the newest item added.
Programs should be able to run in the foreground or background. (using &)
Ctrl-D will exit the shell
Ctrl-C will print the complete history
The Command ‘history’ will also print the complete history. Newest items will be at the bottom of the list.
All other signals will be trapped and displayed to the user in the shell
Program will use the read() command to read in input, unless the arrow keys are supported
I have opted to implement arrow keys for history cycling, so I'm using ncurses for input, rather than read(). I think I'm doing all right using strtok() to parse input, and fork() and execvp() to run the processes, but I'm not doing all right implementing ncurses correctly. All I've gotten it to do so far is init a new screen, display the prompt, then segfault upon any key press. Not good.
I reckon the problem must be in my design. I'm not wrapping my head around ncurses too well. What sort of data structures should I be using for this project? How should I handle the ncurses setup, teardown, and everything in between? What's the deal with windows and screens, and should I have a single globally accessible window/screen that I work with? Also, I've been trying to use a char* for the input buffer, and a char** for the command history, but I have no experience in C, so despite reading up on malloc, calloc, and realloc, I'm not sure of the best way to store commands in the buffer and the history. Any tips on managing these char arrays?
tl;dr: How do I use ncurses correctly to make a command line shell, and how do I handle the command memory management with C?
I realize this is a pretty hefty question. :(
edit: I have already seen http://www.gnu.org/software/libc/manual/html_node/Implementing-a-Shell.html and http://www.linuxinfor.com/english/NCURSES-Programming/ but the ncurses documentation has actually too much overhead. I just want to use its ability to recognize arrow keys.
Here's some sample code which:
Performs dynamic memory allocation.
Reads from the console in non-blocking mode.
Uses VT100 codes to print a frame buffer to the console.
It compiles on Linux using GCC without warnings or errors. It's far from bug free, but it should give you some ideas of what's possible. Compile and run it, pressing [up] and [down] will print messages, typing characters and hitting [enter] will "execute" the command.
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
/** VT100 command to clear the screen. Use puts(VT100_CLEAR_SCREEN) to clear
* the screen. */
#define VT100_CLEAR_SCREEN "\033[2J"
/** VT100 command to reset the cursor to the top left hand corner of the
* screen. */
#define VT100_CURSOR_TO_ORIGIN "\033[H"
struct frame_s
{
int x;
int y;
char *data;
};
static int draw_frame(struct frame_s *frame)
{
int row;
char *data;
int attrib;
puts(VT100_CLEAR_SCREEN);
puts(VT100_CURSOR_TO_ORIGIN);
for (row = 0, data = frame->data; row < frame->y; row++, data += frame->x)
{
/* 0 for normal, 1 for bold, 7 for reverse. */
attrib = 0;
/* The VT100 commands to move the cursor, set the attribute, and the
* actual frame line. */
fprintf(stdout, "\033[%d;%dH\033[0m\033[%dm%.*s", row + 1, 0, attrib, frame->x, data);
fflush(stdout);
}
return (0);
}
int main(void)
{
const struct timespec timeout = { .tv_sec = 1, .tv_nsec = 0 };
struct frame_s frame;
struct termios tty_old;
struct termios tty_new;
unsigned char line[128];
unsigned int count = 0;
int ret;
struct pollfd fds[1];
sigset_t sigmask;
struct tm *tp;
time_t current_time;
/* Set up a little frame. */
frame.x = 80;
frame.y = 5;
frame.data = malloc(frame.x * frame.y);
if (frame.data == NULL)
{
fprintf(stderr, "No memory\n");
exit (1);
}
memset(frame.data, ' ', frame.x * frame.y);
/* Get the terminal state. */
tcgetattr(STDIN_FILENO, &tty_old);
tty_new = tty_old;
/* Turn off "cooked" mode (line buffering) and set minimum characters
* to zero (i.e. non-blocking). */
tty_new.c_lflag &= ~ICANON;
tty_new.c_cc[VMIN] = 0;
/* Set the terminal attributes. */
tcsetattr(STDIN_FILENO, TCSANOW, &tty_new);
/* Un-mask all signals while in ppoll() so any signal will cause
* ppoll() to return prematurely. */
sigemptyset(&sigmask);
fds[0].events = POLLIN;
fds[0].fd = STDIN_FILENO;
/* Loop forever waiting for key presses. Update the output on every key
* press and every 1.0s (when ppoll() times out). */
do
{
fds[0].revents = 0;
ret = ppoll(fds, sizeof(fds) / sizeof(struct pollfd), &timeout, &sigmask);
if (fds[0].revents & POLLIN)
{
ret = read(STDIN_FILENO, &line[count], sizeof(line) - count);
if (ret > 0)
{
line[count + ret] = '\0';
if (strcmp(&line[count], "\033[A") == 0)
{
snprintf(frame.data, frame.x, "up");
count = 0;
}
else if (strcmp(&line[count], "\033[B") == 0)
{
snprintf(frame.data, frame.x, "down");
count = 0;
}
else if (line[count] == 127) // backspace
{
if (count != 0) { count -= ret;}
}
else if (line[count] == '\n')
{
snprintf(frame.data, frame.x, "entered: %s", line);
count = 0;
}
else
{
count += ret;
}
}
}
/* Print the current time to the output buffer. */
current_time = time(NULL);
tp = localtime(¤t_time);
strftime(&frame.data[1 * frame.x], frame.x, "%Y/%m/%d %H:%M:%S", tp);
/* Print the command line. */
line[count] = '\0';
snprintf(&frame.data[(frame.y - 1) * frame.x], frame.x, "$ %s", line);
draw_frame(&frame);
}
while (1);
/* Restore terminal and free resources. */
tcsetattr(STDIN_FILENO, TCSANOW, &tty_old);
free(frame.data);
return (0);
}
If your input buffer is defined to be 64 characters, then I would recommend using a char array instead of a char*. Something like char input_buffer[65]; should serve your purposes (add an extra character for the trailing '\0').
As far as command history goes, you can use a two-dimensional array for that. Something like char command_history[20][65]; should let you store 20 old commands of 64 characters each.
Allocating these buffers statically should make things a bit easier for you, as you won't have to worry about malloc and friends.
It's hard to give you too much specific advice without seeing your code. I have a feeling that you are making the same type of mistakes that are typical to people first learning C. Can you post the part of your code that is giving you problems so that we can learn more about what you are doing?
Update after posted provided code:
One problem I'm seeing is that the function takeInput doesn't have a return statement. When you use input = takeInput(); inside your main function, the value of input isn't being set to what you think it is. It's probably not a valid pointer, which is causing your line that says input[j] to segfault.
Your usage of cmdHistory also needs revisiting. You allocate it with cmdHistory = (char**)calloc(21,sizeof(int));, which gives you enough space to store 21 integers. In the function printHistory, you pass elements of cmdHistory to printw as if they were strings (they're only integers). This is most definitely not doing what you want it to do. Instead, your allocation logic for cmdHistory needs to look more like your de-allocation logic (except backwards). Allocate an array of char**, then iterate through the array, assigning each pointer to a newly-allocated buffer. Just like you have one free statement for each element in the array plus a free for the array as a whole, you should have one malloc for each element plus one malloc for the array as a whole.
Even if you can't use a statically-allocated stack, try writing your program using one anyway. This will let you work the kinks out of your key detection logic, etc without having to worry about the dynamic memory part of the program. Once the rest of it is working, go back in and swap out the static memory for dynamic memory allocation. That way, you're only having to debug a little bit at a time.
Have you looked at the Readline library? It's ideal for use in your project.
http://cnswww.cns.cwru.edu/php/chet/readline/rltop.html
I'm using select() call to detect input presence in the main cycle of my program. This makes me use raw file descriptor (0) instead of stdin.
While working in this mode I've noticed that my software occasionally loses a chunk of input at the beginning. I suspect that stdin consumes some of it on the program start. Is there a way to prevent this behavior of stdin or otherwise get the whole input data?
The effect described can be reproduced only with some data on standard input at the very moment of program start. My executable should be used as xinetd service in a way that it always has some input on the start.
Standard input is read in the following way:
Error processInput() {
struct timeval ktimeout;
int fd=fileno(stdin);
int maxFd=fd+1;
FD_ZERO(&fdset);
FD_SET(fd, &fdset);
ktimeout.tv_sec = 0;
ktimeout.tv_usec = 1;
int selectRv=-1;
while ((selectRv=select(maxFd, &fdset, NULL, NULL, &ktimeout)) > 0) {
int left=MAX_BUFFER_SIZE-position-1;
assert(left>0);
int bytesCount=read(fd, buffer+position, left);
//Input processing goes here
}
}
Don't mix cooked and raw meat together. Try replacing the read() call with the equivalent fread() call.
It is very likely that fileno(stdin) is initializing the stdin object, causing it to read and buffer some input. Or perhaps you are already calling something that causes it to initialize (scanf(), getchar(), etc...).