Home > Enterprise >  c get users cursor position in console "cells"
c get users cursor position in console "cells"

Time:10-05

System: Windows; Version c 23 Want to create an interactive button in a console application. For this to work, I need to get where a user clicked 1) relative to console window 2) NOT IN PIXELS, but in CONSOLE CELL UNITS. Things I tried:

using namespace std;
void GetMouseCursorPos(POINT *mC) {
    *mC = POINT{0,0};
    ScreenToClient(GetConsoleWindow(),mC);
    mC->x = mC->x / font.dwFontSize.X;
    mC->y = mC->y / font.dwFontSize.Y;
    system("cls");
    cout << mC->x << " " << mC->y;
}

int main (){
    POINT mCoord;
    while (true){
        SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ENABLE_MOUSE_INPUT | 
 ENABLE_EXTENDED_FLAGS);
        GetMouseCursorPos(&mCoord);
    }
}

In fullscreen ALWAYS outputs 0 -4
Second try:

using namespace std;
void GetMouseCursorPos(POINT *mC) {
    *mC = POINT{0,0};
    ScreenToClient(GetConsoleWindow(),mC);
    mC->x = mC->x / font.dwFontSize.X;
    mC->y = LONG(mC->y - 22.5) / font.dwFontSize.Y;
    system("cls");
    cout << mC->x << " " << mC->y;
}

int main (){
    POINT mCoord;
    while (true){
        SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ENABLE_MOUSE_INPUT | 
 ENABLE_EXTENDED_FLAGS);
        GetMouseCursorPos(&mCoord);
    }
}

Although 2nd try is better (but still not perfect) Why the 2nd try is not perfect(Picture)
When you click on the red crosses you select the slot leftside to it (not the slot itself)

Button class:

using namespace std;

class Button {
public:
    int x;
    int y;
    int height;
    int width;
    string text;
    string text1;
    string text2;
    int color = 7;
    int itemColor = 7;

    string getText() { return text; };

    void setText(string text) { this->text = text; };

    bool isPressed(int mX, int mY) {
        if (mX >= this->x && mX <= this->x   width && mY >= this->y && mY <= this->y   height) {
            return true;
        }
        return false;
    };

    void print() {
        SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), {static_cast<SHORT>(x   1), static_cast<SHORT>(y)});
        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color);
        for (int i = 0; i < width - 1; i  ) {
            cout << "-";
        }
        for (int i = 0; i < (height - 2); i  ) {
            SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),
                                 {static_cast<SHORT>(x), static_cast<SHORT>(y   i   1)});
            cout << "|";
            SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),
                                 {static_cast<SHORT>(x   width), static_cast<SHORT>(y   i   1)});
            cout << "|";
        }
        SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),
                             {static_cast<SHORT>(x   1), static_cast<SHORT>(y   height - 
1)});
        for (int i = 0; i < width - 1; i  ) {
            cout << "-";
        }
        SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),
                                 {static_cast<SHORT>(x   1),
                                  static_cast<SHORT>(y   height / 2)});
        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7);
        for (int i = 0; i < width / 2 - text.length() / 2 - 1; i  ) {
            cout << " ";
        }
        cout << text;
        for (int i = 0; i < width / 2 - text.length() / 2 - 1; i  ) {
            cout << " ";
        }
        if (!text1.empty()) {
            SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),
                                     {static_cast<SHORT>(x   1),
                                      static_cast<SHORT>(y   height / 2   1)});
            SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7);
            for (int i = 0; i < width / 2 - text1.length() / 2 - 1; i  ) {
                cout << " ";
            }
            SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), itemColor);
            cout << text1[0];
            SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7);
            cout << text1.substr(1);
            for (int i = 0; i < width / 2 - text1.length() / 2 - 1; i  ) {
                cout << " ";
            }
        }
        if (!text2.empty()) {
            SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),
                                      {static_cast<SHORT>(x   1),
                                      static_cast<SHORT>(y   height / 2 - 1)});
            SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7);
            for (int i = 0; i < width / 2 - text2.length() / 2 - 1; i  ) {
                cout << " ";
            }
            cout << text2;
            for (int i = 0; i < width / 2 - text2.length() / 2 - 1; i  ) {
                cout << " ";
            }
        }
    }
};

CodePudding user response:

By calling the function SetConsoleMode with the argument ENABLE_MOUSE_INPUT, you can set the console to report mouse events. These events can then be read using the function ReadConsoleInput.

When reading a mouse event, you will get a MOUSE_EVENT_RECORD structure. The dwMousePosition member of this structure is in character-cell coordinates, not pixels.

Here is an example program:

#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
    HANDLE hInput = GetStdHandle(STD_INPUT_HANDLE);
    INPUT_RECORD ir;
    DWORD dwRead;

    //set console to report mouse input events (but nothing else, including keyboard events)
    if ( !SetConsoleMode( hInput, ENABLE_MOUSE_INPUT )  )
    {
        fprintf( stderr, "Error setting console mode!\n" );
        exit( EXIT_FAILURE );
    }

    while ( ReadConsoleInput( hInput, &ir, 1, &dwRead ) && dwRead == 1 )
    {
        //ignore all non-mouse events, except for the key "q", which is a request to end the program
        if ( ir.EventType != MOUSE_EVENT )
        {
            if (
                ir.EventType == KEY_EVENT &&
                ir.Event.KeyEvent.bKeyDown &&
                ir.Event.KeyEvent.uChar.AsciiChar == 'q'
            )
            {
                return 0;
            }
                
            continue;
        }

        //ignore all mouse events which don't involve mouse button presses
        if ( ir.Event.MouseEvent.dwEventFlags != 0 && ir.Event.MouseEvent.dwEventFlags != DOUBLE_CLICK )
            continue;

        //determine whether mouse button was clicked or released
        if ( ir.Event.MouseEvent.dwButtonState != 0 )
            printf( "Mouse button clicked" );
        else
            printf( "Mouse button released" );

        //print coordinates of mouse click/release
        printf(
            " at the following cell coordinates: [%u,%u]\n",
            ir.Event.MouseEvent.dwMousePosition.X,
            ir.Event.MouseEvent.dwMousePosition.Y
        );
        fflush( stdout );
    }

    fprintf( stderr, "Input error!\n" );
}

This program has the following output:

Mouse button clicked at the following cell coordinates: [24,9]
Mouse button released at the following cell coordinates: [24,9]
Mouse button clicked at the following cell coordinates: [46,10]
Mouse button released at the following cell coordinates: [60,12]
Mouse button clicked at the following cell coordinates: [45,17]
Mouse button released at the following cell coordinates: [31,10]

In order to quit the program, you must press the Q key.

Note that this program is only for demonstration purposes and is not optimized for performance. In a proper program in which performance may be an issue, it would probably be better to read more than one event at once using ReadConsoleInput.

  •  Tags:  
  • c
  • Related