TSE: Linux: Mouse: Method: X10: Create a C program that uses the X10 method to retrieve and then show the mouse X Y coordinates

10 views
Skip to first unread message

knud van eeden

unread,
Jun 27, 2025, 9:04:33 AM6/27/25
to SemWare TSE Pro Text Editor, S.E. Mitchell
Hello,

The mouse x y coordinates are shown until column 126 here.

Tested compiling first, 
then opening a cmd.exe window, 
running wsl, 
then running ./mouse_viewer

with friendly greetings
Knud van Eeden

===

Google Gemini:

> Create a C program that uses the X10 method to retrieve and then show the mouse X Y coordinates

Here is a C program that demonstrates how to capture and display mouse
X,Y coordinates using the X10 mouse reporting mode. This is the protocol
your TSE macro is currently relying on.

This program will highlight the limitation you are experiencing with
column 94.

--- cut here: begin --------------------------------------------------

#include <stdio.h>    // For printf, getchar, puts, fputs, stdout, stderr
#include <stdlib.h>   // For exit
#include <unistd.h>   // For STDIN_FILENO, read
#include <termios.h>  // For termios functions (raw mode)
#include <string.h>   // For strlen

// Function to set terminal to raw mode
// In raw mode, input is read character by character without buffering
// and special characters like Ctrl+C are not processed by the terminal.
void set_raw_mode(struct termios *old_termios) {
    struct termios new_termios;

    // Get current terminal attributes
    if (tcgetattr(STDIN_FILENO, old_termios) == -1) {
        perror("tcgetattr");
        exit(EXIT_FAILURE);
    }

    new_termios = *old_termios; // Copy old settings

    // Disable canonical mode (ICANON), echo input (ECHO),
    // and signal characters (ISIG).
    new_termios.c_lflag &= ~(ICANON | ECHO | ISIG);
    // Disable IXON (Ctrl+S/Ctrl+Q flow control), ICRNL (map CR to NL)
    new_termios.c_iflag &= ~(IXON | ICRNL);
    // Disable output post-processing (OPOST)
    new_termios.c_oflag &= ~(OPOST);

    // Set minimum number of characters for non-canonical read (VMIN) to 1
    // and timeout (VTIME) to 0. This means read will return after 1 character.
    new_termios.c_cc[VMIN] = 1;
    new_termios.c_cc[VTIME] = 0;

    // Set new terminal attributes immediately
    if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &new_termios) == -1) {
        perror("tcsetattr");
        exit(EXIT_FAILURE);
    }
}

// Function to restore original terminal mode
void restore_termios(struct termios *old_termios) {
    if (tcsetattr(STDIN_FILENO, TCSAFLUSH, old_termios) == -1) {
        perror("tcsetattr restore");
    }
}

int main() {
    struct termios old_termios;
    int c;
    char buffer[5]; // Buffer to hold ESC[M<btn><x><y> sequence

    // Set terminal to raw mode
    set_raw_mode(&old_termios);

    // Enable X10 (Normal) mouse tracking
    // ESC[?1000h: Enable X10 mouse protocol
    // In this mode, mouse events are reported as ESC[M<button><x><y>
    // where <button>, <x>, <y> are ASCII characters with their value + 32.
    // X10 mode has a coordinate limit (typically 0-223 for X/Y, due to byte values).
    fputs("\x1b[?1000h", stdout);
    fflush(stdout); // Ensure the escape sequence is sent immediately

    printf("X10 Mouse Coordinate Viewer\n");
    printf("Click mouse anywhere in the terminal. Press 'q' to quit.\n");
    printf("----------------------------------\n");

    while (1) {
        c = getchar(); // Read one character at a time

        // Check for Escape character (often 27) - could be part of mouse sequence or user exit
        if (c == '\x1b') {
            // Read next character for mouse sequence detection
            // Most escape sequences start with '['
            c = getchar();
            if (c == '[') {
                c = getchar();
                // Check for 'M' which indicates an X10 mouse report
                if (c == 'M') {
                    // Read the 3 bytes for button, x, y
                    // These are raw ASCII values which represent (coordinate + 32)
                    int btn = getchar();
                    int x_char = getchar();
                    int y_char = getchar();

                    // Convert raw ASCII values back to coordinates
                    // Coordinates are 1-indexed
                    int mouse_x = x_char - 32;
                    int mouse_y = y_char - 32;

                    // Print raw ASCII values and calculated coordinates
                    printf("Mouse Event Detected:\n");
                    printf("  Raw Button Char: %d (0x%02x), Decoded Button: %d\n", btn, btn, btn);
                    printf("  Raw X Char: %d (0x%02x), Decoded X: %d\n", x_char, x_char, mouse_x);
                    printf("  Raw Y Char: %d (0x%02x), Decoded Y: %d\n", y_char, y_char, mouse_y);
                    printf("----------------------------------\n");

                    // IMPORTANT NOTE ON LIMITATION:
                    // If x_char or y_char values exceed 127 (e.g., for coordinates > 95),
                    // they might be interpreted differently or cause issues.
                    // Some terminals/fonts might interpret values > 127 as multi-byte
                    // characters, leading to corrupted output or behavior.
                    // This is why you likely see the behavior around column 93/94.
                    // (93 + 32 = 125, 94 + 32 = 126, 95 + 32 = 127).
                    // Characters 128-255 often behave unpredictably depending on locale.
                } else {
                    // Not an X10 mouse report, could be another escape sequence.
                    // You'd need more robust parsing for other sequences.
                    // For simplicity, just consume remaining chars of this sequence.
                    while (c != EOF && c != 'M' && c != '\x1b' && c != '\n') {
                        c = getchar();
                    }
                }
            } else if (c == '\x1b') { // If user presses ESC twice in quick succession
                break;
            } else {
                // If the ESC was followed by something else,
                // and it's not a mouse sequence, consume characters until a known break.
                // This is a simplified escape sequence parser.
                char seq_buf[10]; // Small buffer for simple sequences
                seq_buf[0] = '\x1b';
                seq_buf[1] = c;
                int i = 2;
                while (i < 9 && (c = getchar()) != EOF && c != '\x1b' && c != '\n' && (c < 'A' || c > 'Z')) {
                    seq_buf[i++] = c;
                }
                seq_buf[i] = '\0';
                // printf("Unknown ESC sequence: %s\n", seq_buf);
                if (c == '\x1b') { // If we read another ESC while parsing, break
                    break;
                }
            }
        } else if (c == 'q' || c == 'Q') {
            // User pressed 'q' or 'Q' to quit
            break;
        }
        // Other characters will just be ignored in this loop
    }

    printf("\nExiting. Restoring terminal settings.\n");

    // Disable mouse tracking before restoring terminal settings
    fputs("\x1b[?1000l", stdout);
    fflush(stdout);

    // Restore original terminal mode
    restore_termios(&old_termios);

    return 0;
}

--- cut here: end ----------------------------------------------------

How to Compile and Run:

Save: Save the code above as mouse_viewer.c (or any other .c filename).

Compile: Open your terminal (WSL/Ubuntu) and compile it using gcc:

--- cut here: begin --------------------------------------------------

gcc mouse_viewer.c -o mouse_viewer

--- cut here: end ----------------------------------------------------

Run: Execute the compiled program:

--- cut here: begin --------------------------------------------------

./mouse_viewer

--- cut here: end ----------------------------------------------------

What to Observe:

Click within columns 1-93: You should see Raw X Char values
corresponding to (column_number + 32), and Decoded X matching your
column_number. These should be relatively small ASCII values.

Click at or beyond column 94:

Observe what the Raw X Char value becomes. You will likely see it jump
to a much higher value, or the program might print unexpected
characters, or fail to parse correctly.

The Decoded X might then become 4072 - 32 = 4040 (if the Query(Key)
behavior from TSE is mimicked by the C getchar() reading the first part
of a multi-byte sequence).

This program might even produce Raw X Char values beyond 127 if your
terminal is sending them, which could further demonstrate the protocol's
fragility in that range.

This C program directly illustrates the X10 protocol's limitation and
how character encoding beyond a certain point (often ASCII 127 or
similar thresholds for extended ASCII/UTF-8 interpretation) can lead to
the "broken" behavior you're seeing in your TSE macro. The 4072 you see
in TSE's Query(Key) is almost certainly TSE trying to interpret part of
a non-X10 mouse report (like an SGR sequence) as if it were a single X10
coordinate byte.

Inline image



mouse_viewer.c

knud van eeden

unread,
Jun 27, 2025, 5:06:48 PM6/27/25
to SemWare TSE Pro Text Editor, S.E. Mitchell
 Q. TSE: Linux: Mouse: Method: SGR: Create a C program that uses the SGR method to retrieve and then show the mouse X Y coordinates and runs in Linux WSL Ubuntu

Hello,

The mouse x y coordinates are received using this 'SGR' method shown on the whole screen of my 75 inch monitor.

No restrictions regarding a maximum value MouseX anymore thus as is current the case with the currenly used X10 method inside TSE Linux

It runs successfully only on the following terminals:

 1. cmd.exe

 2. powershell.exe

 3. wt.exe (that is 'Windows Terminal', you will have to install it from the Microsoft Store.

To run it, e.g. from cmd.exe (the other two similarly)

1. start cmd.exe

2. type

    wsl

3. navigate to the attached C file

    sgr_mouse_tcc.c

4. compile it using

    gcc sgr_mouse_tcc.c -o sgr_mouse_tcc

5. run it

    ./sgr_mouse_tcc

6. It will show the mouse output when clicking anywhere on the screen.

Mouse event: type=0, X=92, Y=15
Mouse event: type=0, X=183, Y=17
Mouse event: type=0, X=183, Y=17
Mouse event: type=0, X=176, Y=16
Mouse event: type=0, X=176, Y=16
Mouse event: type=0, X=176, Y=16
Mouse event: type=0, X=176, Y=16
Mouse event: type=0, X=176, Y=16
Mouse event: type=0, X=176, Y=16
Mouse event: type=0, X=179, Y=16
Mouse event: type=0, X=179, Y=16
Unknown input: d
Mouse event: type=0, X=1, Y=4
Mouse event: type=0, X=1, Y=10
Mouse event: type=0, X=23, Y=16
Unknown input: dfad
Mouse event: type=0, X=32, Y=25
Mouse event: type=0, X=32, Y=24
Mouse event: type=0, X=12, Y=15

Typing 'q' to exit

Exiting...

--- cut here: end ----------------------------------------------------

7. The advantage of this SGR method is that it detects mouse clicks on the
   FULL screen, even on e.g. my large 75 inch monitor.

8. -See screenshot.

9. -Here the (also attached) C program:

Asked ChatGPT:

> Create a C program that uses the SGR method to retrieve and then show the mouse X Y coordinates and runs in Linux WSL Ubuntu

--- cut here: begin --------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>

struct termios orig_termios;

void disable_raw_mode() {
    tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios);
    printf("\x1b[?1006l"); // disable SGR mouse mode
    printf("\x1b[?1000l"); // disable basic mouse tracking
    fflush(stdout);
}

void enable_raw_mode() {
    tcgetattr(STDIN_FILENO, &orig_termios);
    atexit(disable_raw_mode);

    struct termios raw = orig_termios;
    raw.c_lflag &= ~(ECHO | ICANON);
    raw.c_iflag &= ~(IXON | ICRNL);
    tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);

    // Set stdin to non-blocking
    int flags = fcntl(STDIN_FILENO, F_GETFL, 0);
    fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);

    printf("\x1b[?1000h"); // enable basic mouse tracking
    printf("\x1b[?1006h"); // enable SGR mouse mode
    fflush(stdout);
}

void read_mouse_event() {
    char buffer[64];
    int idx = 0;
    char ch;

    // Try reading input non-blocking
    while (1) {
        ssize_t n = read(STDIN_FILENO, &ch, 1);
        if (n <= 0) {
            usleep(10000); // wait 10 ms
            continue;
        }

        if (ch == 'q') {
            printf("Exiting...\n");
            exit(0);
        }

        buffer[idx++] = ch;
        buffer[idx] = '\0';

        if (ch == 'M' || ch == 'm')
            break;

        if (idx >= sizeof(buffer) - 1)
            break;
    }

    int event_type = 0, x = 0, y = 0;
    if (sscanf(buffer, "\x1b[<%d;%d;%d", &event_type, &x, &y) == 3) {
        printf("Mouse event: type=%d, X=%d, Y=%d\n", event_type, x, y);
    } else if (idx > 0) {
        printf("Unknown input: %s\n", buffer);
    }
}

int main() {
    enable_raw_mode();
    printf("Click inside terminal. Press 'q' to quit.\n");

    while (1) {
        read_mouse_event();
    }

    return 0;
}

--- cut here: end ----------------------------------------------------

with friendly greetings
Knud van Eeden

Inline image:

Clicking anywhere on the screen of a maximized full screen window on my 75 inch monitor

Inline image

knud van eeden

unread,
Jun 27, 2025, 5:12:50 PM6/27/25
to SemWare TSE Pro Text Editor, S.E. Mitchell
Additionally the C program runs also full screen everywhere in wsl.exe itself

 E.g.

  type in a cmd.exe box

   start wsl.exe 

and run

 ./sgr_mouse_tcc

Inline image

Reply all
Reply to author
Forward
0 new messages