LinuxQuestions.org
Visit the LQ Articles and Editorials section
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices



Reply
 
Search this Thread
Old 05-31-2012, 04:04 PM   #1
esmn
LQ Newbie
 
Registered: Apr 2012
Posts: 15

Rep: Reputation: Disabled
get cursor position in c


Is there any way to get cursor position in c?(no ncurses please)
this code report the cursor position:
Code:
echo -e "\033[6n"
how can I use it in c?
 
Old 05-31-2012, 05:59 PM   #2
BeaverusIV
Member
 
Registered: Oct 2004
Location: New Zealand
Distribution: Arch Linux 2010.05
Posts: 129

Rep: Reputation: 18
I would have a look at how curses does it. You might be able to pick a few commands out.
 
Old 06-01-2012, 02:21 AM   #3
hydraMax
Member
 
Registered: Jul 2010
Location: Skynet
Distribution: Debian + Emacs
Posts: 467
Blog Entries: 60

Rep: Reputation: 51
Quote:
Originally Posted by esmn View Post
Is there any way to get cursor position in c?(no ncurses please)
this code report the cursor position:
Code:
echo -e "\033[6n"
how can I use it in c?
The ideal approach is to use the nice API from a toolkit, such as GTK+ or one of the many others available. However, you can do this more directly with the XLIB functions. One approach, at least, is to grab the pointer, and then catch the next button press event. From some code I wrote a while back:


Code:
	    Window rootw = XDefaultRootWindow(disp);
	    XEvent evt;
	    int err = XGrabPointer(disp,
				   rootw,
				   False,
				   ButtonPressMask,
				   GrabModeAsync,
				   GrabModeAsync,
				   None,
				   csr,
				   CurrentTime);
	    switch(err) {
            // ...
	    }
	    XNextEvent(disp, &evt);
            // ...
	    printf("Absolute coordinates: %d,%d\n", evt.xbutton.x_root, evt.xbutton.y_root);
All the commands used above have manual pages: XGrabPointer(3), XNextEvent(3), etc.
 
Old 06-01-2012, 02:24 AM   #4
hydraMax
Member
 
Registered: Jul 2010
Location: Skynet
Distribution: Debian + Emacs
Posts: 467
Blog Entries: 60

Rep: Reputation: 51
ERm... You probably meant the "terminal" cursor, didn't you?
 
Old 06-01-2012, 03:55 AM   #5
esmn
LQ Newbie
 
Registered: Apr 2012
Posts: 15

Original Poster
Rep: Reputation: Disabled
Quote:
ERm... You probably meant the "terminal" cursor, didn't you?
yes. It's a terminal program.
but thanks for your reply.
 
Old 06-01-2012, 04:47 AM   #6
NevemTeve
Senior Member
 
Registered: Oct 2011
Location: Budapest
Distribution: Debian/GNU/Linux, AIX
Posts: 1,948

Rep: Reputation: 524Reputation: 524Reputation: 524Reputation: 524Reputation: 524Reputation: 524
Use write(1,buf,len) for writing, read(0,buf,len) for reading... (but first use isatty to check you aren't redirected)

Edit: plus you may want to use termios, if you haven't done it yet (example: http://dtelnet.sourceforge.net/shkeys.c)

Last edited by NevemTeve; 06-01-2012 at 05:08 AM.
 
Old 06-01-2012, 12:43 PM   #7
esmn
LQ Newbie
 
Registered: Apr 2012
Posts: 15

Original Poster
Rep: Reputation: Disabled
I use write() read() function:
Code:
/* get_cur_pos.c */
#include <stdio.h>
#include <unistd.h>
#include <termios.h>
int main()
{

	char buf[8];
	int curline;
	char cmd[]="echo -e '\033[6n'";
	struct termios save,raw;
	tcgetattr(0,&save);
	cfmakeraw(&raw); tcsetattr(0,TCSANOW,&raw);
	if (isatty(fileno(stdin))) 
	{
		write(1,cmd,sizeof(cmd));
		read (0 ,buf ,sizeof(buf));

		/* It doesn't work!!?
		sscanf(buf,"%d",curline);
		printf("\n\rCurrent Line: %d\n\r" , curline);
		*/

		printf("\n\rCurrent Line: %c%c\n\r" , buf[2] ,buf[3] );
	}
	tcsetattr(0,TCSANOW,&save);
	return 0;
}
but there are some problems:
-write() function print out "echo -e ' '" in the output:
Code:
esmn@deblin:$ ./get_cur_pos 
echo -e ''
Current Line: 2;
esmn@deblin:$
-sscanf doesn't work properly. so how can I use the result in the program?

-Is there any termios function to get cursor position?
 
Old 06-01-2012, 02:16 PM   #8
NevemTeve
Senior Member
 
Registered: Oct 2011
Location: Budapest
Distribution: Debian/GNU/Linux, AIX
Posts: 1,948

Rep: Reputation: 524Reputation: 524Reputation: 524Reputation: 524Reputation: 524Reputation: 524
You don't write 'echo' to terminal, 'echo' means write in shell... Correction:

Code:
char cmd[]="\033[6n";
Also it's not 'scanf', but 'read'

Last edited by NevemTeve; 06-01-2012 at 02:17 PM.
 
1 members found this post helpful.
Old 06-01-2012, 02:56 PM   #9
Nominal Animal
Senior Member
 
Registered: Dec 2010
Location: Finland
Distribution: Xubuntu, CentOS, LFS
Posts: 1,723
Blog Entries: 3

Rep: Reputation: 943Reputation: 943Reputation: 943Reputation: 943Reputation: 943Reputation: 943Reputation: 943Reputation: 943
Quote:
Originally Posted by esmn View Post
there are some problems:
-write() function print out "echo -e ' '" in the output
-sscanf doesn't work properly
-Is there any termios function to get cursor position?
No, there is no termios function to get cursor position.

What you need to do is to use tcsetattr to disable echo (ECHO, fixing your write() problem), set noncanonical input mode (disabling ICANON, fixing your sscanf() problem), and I believe temporarily disable the receiver (disabling CREAD) while conversing with the terminal so that you do not mix actual standard input and console responses.

It is best to first use tcgetattr to get the current settings, save them so that you can restore them later (easiest is just to call it twice, saving the results to different structures), and edit only the relevant bits in the results.

You are going to have very little success with stdio.h, because of the internal caching (especially of input) it does, though. It works fine with unistd.h low-level I/O, though; you just have to check for the exit statuses carefully.

Here is a proof-of-concept shell script:
Code:
#!/bin/bash

# Get current settings.
if ! termios="$(stty -g 2>/dev/null)" ; then
    echo "Not running in a terminal." >&2
    exit 1
fi

# Restore terminal settings when the script exits.
trap "stty '$termios'" EXIT

# Disable ICANON ECHO. Should probably also disable CREAD.
stty -icanon -echo

# Request cursor coordinates
printf '\033[6n'

# Read response from standard input; note, it ends at R, not at newline
read -d "R" rowscols

# Clean up the rowscols (from \033[rows;cols -- the R at end was eaten)
rowscols="${rowscols//[^0-9;]/}"
rowscols=("${rowscols//;/ }")
printf '(row %d, column %d) ' ${rowscols[0]} ${rowscols[1]}

# Reset original terminal settings.
stty "$termios"
I threw together a proof-of-concept C code, too:
Code:
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>

#define   RD_EOF   -1
#define   RD_EIO   -2

static inline int rd(const int fd)
{
    unsigned char   buffer[4];
    ssize_t         n;

    while (1) {

        n = read(fd, buffer, 1);
        if (n > (ssize_t)0)
            return buffer[0];

        else
        if (n == (ssize_t)0)
            return RD_EOF;

        else
        if (n != (ssize_t)-1)
            return RD_EIO;

        else
        if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK)
            return RD_EIO;
    }
}

static inline int wr(const int fd, const char *const data, const size_t bytes)
{
    const char       *head = data;
    const char *const tail = data + bytes;
    ssize_t           n;

    while (head < tail) {

        n = write(fd, head, (size_t)(tail - head));
        if (n > (ssize_t)0)
            head += n;

        else
        if (n != (ssize_t)-1)
            return EIO;

        else
        if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK)
            return errno;
    }

    return 0;
}

/* Return a new file descriptor to the current TTY.
*/
int current_tty(void)
{
    const char *dev;
    int         fd;

    dev = ttyname(STDIN_FILENO);
    if (!dev)
        dev = ttyname(STDOUT_FILENO);
    if (!dev)
        dev = ttyname(STDERR_FILENO);
    if (!dev) {
        errno = ENOTTY;
        return -1;
    }

    do {
        fd = open(dev, O_RDWR | O_NOCTTY);
    } while (fd == -1 && errno == EINTR);
    if (fd == -1)
        return -1;

    return fd;
}

/* As the tty for current cursor position.
 * This function returns 0 if success, errno code otherwise.
 * Actual errno will be unchanged.
*/
int cursor_position(const int tty, int *const rowptr, int *const colptr)
{
    struct termios  saved, temporary;
    int             retval, result, rows, cols, saved_errno;

    /* Bad tty? */
    if (tty == -1)
        return ENOTTY;

    saved_errno = errno;

    /* Save current terminal settings. */
    do {
        result = tcgetattr(tty, &saved);
    } while (result == -1 && errno == EINTR);
    if (result == -1) {
        retval = errno;
        errno = saved_errno;
        return retval;
    }

    /* Get current terminal settings for basis, too. */
    do {
        result = tcgetattr(tty, &temporary);
    } while (result == -1 && errno == EINTR);
    if (result == -1) {
        retval = errno;
        errno = saved_errno;
        return retval;
    }

    /* Disable ICANON, ECHO, and CREAD. */
    temporary.c_lflag &= ~ICANON;
    temporary.c_lflag &= ~ECHO;
    temporary.c_cflag &= ~CREAD;

    /* This loop is only executed once. When broken out,
     * the terminal settings will be restored, and the function
     * will return retval to caller. It's better than goto.
    */
    do {

        /* Set modified settings. */
        do {
            result = tcsetattr(tty, TCSANOW, &temporary);
        } while (result == -1 && errno == EINTR);
        if (result == -1) {
            retval = errno;
            break;
        }

        /* Request cursor coordinates from the terminal. */
        retval = wr(tty, "\033[6n", 4);
        if (retval)
            break;

        /* Assume coordinate reponse parsing fails. */
        retval = EIO;

        /* Expect an ESC. */
        result = rd(tty);
        if (result != 27)
            break;

        /* Expect [ after the ESC. */
        result = rd(tty);
        if (result != '[')
            break;

        /* Parse rows. */
        rows = 0;
        result = rd(tty);
        while (result >= '0' && result <= '9') {
            rows = 10 * rows + result - '0';
            result = rd(tty);
        }

        if (result != ';')
            break;

        /* Parse cols. */
        cols = 0;
        result = rd(tty);
        while (result >= '0' && result <= '9') {
            cols = 10 * cols + result - '0';
            result = rd(tty);
        }

        if (result != 'R')
            break;

        /* Success! */

        if (rowptr)
            *rowptr = rows;

        if (colptr)
            *colptr = cols;

        retval = 0;

    } while (0);

    /* Restore saved terminal settings. */
    do {
        result = tcsetattr(tty, TCSANOW, &saved);
    } while (result == -1 && errno == EINTR);
    if (result == -1 && !retval)
        retval = errno;

    /* Done. */
    return retval;
}

int main(void)
{
    int         fd, row, col;
    char        buffer[64];
    char *const tail = buffer + sizeof(buffer);
    char       *head = buffer + sizeof(buffer);

    fd = current_tty();
    if (fd == -1)
        return 1;

    row = 0;
    col = 0;
    if (cursor_position(fd, &row, &col))
        return 2;

    if (row < 1 || col < 1)
        return 3;

    /* Construct response "(row, col) " from right to left,
     * then output it to standard error, and exit.
    */

    *(--head) = ' ';
    *(--head) = ')';

    {   unsigned int    u = col;
        do {
            *(--head) = '0' + (u % 10U);
            u /= 10U;
        } while (u);
    }

    *(--head) = ' ';
    *(--head) = ',';

    {   unsigned int    u = row;
        do {
            *(--head) = '0' + (u % 10U);
            u /= 10U;
        } while (u);
    }

    *(--head) = '(';

    if (wr(STDERR_FILENO, head, (size_t)(tail - head)))
        return 4;

    return 0;
}
I am not at all sure if disabling CREAD is sufficient to avoid mixing input with terminal output. The code seems to work even when there is pending input in standard input, but I don't know if that behaviour is Linux-specific or standard.

This is why a terminal library like ncurses is so useful: it handles both the input processing, and the conversation with the terminal. If you only need a subset of the functionality, I'm sure you could quite easily write your own, but you'd have to use low-level I/O like I did above. In that case, I'd keep the original terminal settings somewhere, and just flip the current settings when necessary; that saves a lot of unnecessary tcsetattr()/tcgetattr() calls. Also, when reading the terminal response, I'd use longer reads, and everything not a response moved to an input cache; it would be much faster, and would not discard proper input even if interspersed with the terminal responses.

Finally, in noncanonical mode, you can read individual keypresses, not just completed lines.
 
1 members found this post helpful.
Old 06-02-2012, 04:34 AM   #10
esmn
LQ Newbie
 
Registered: Apr 2012
Posts: 15

Original Poster
Rep: Reputation: Disabled
Thanks for complete guidance.

Of course ncurses is very useful but I like to handle terminal with
escape sequences and linux low level function.
 
Old 09-02-2013, 09:12 AM   #11
irancplusplus
LQ Newbie
 
Registered: Sep 2013
Distribution: Ubuntu
Posts: 3

Rep: Reputation: Disabled
to be deleted

Last edited by irancplusplus; 09-03-2013 at 08:16 PM.
 
Old 09-03-2013, 01:19 AM   #12
irancplusplus
LQ Newbie
 
Registered: Sep 2013
Distribution: Ubuntu
Posts: 3

Rep: Reputation: Disabled
to be deletedd

Last edited by irancplusplus; 09-14-2013 at 08:11 AM.
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
related to cursor position ravidangaych Linux - General 1 06-18-2009 05:31 AM
Cursor Global Position boogy Programming 10 04-27-2009 05:59 AM
deletion of line above the cursor position. dina3e Linux - Newbie 1 04-07-2008 10:41 PM
Vim cursor position Tischbein Linux - Newbie 4 03-31-2008 02:32 AM
vim: startup at last cursor position chrism01 Solaris / OpenSolaris 3 02-20-2007 12:25 PM


All times are GMT -5. The time now is 02:25 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration