简体   繁体   中英

How can I make mouse support work for ncurses on OSX?

I have currently compiled and run the example from this tutorial on OSX, and found that it does not work to capture the mouse event.

I modified it slightly to make it easier to debug. It looks like wgetch is never returning KEY_MOUSE and instead is just returning some arbitrary character when I click. I'm also fairly sure it is not a terminal setting issue because Vim has no problem capturing mouse clicks in the exact same terminal.

I suspect the example is simply out of date.

Does anyone know the modern way to use ncurses to capture the mouse?

Below is my modified version of the code, for context.

Also, I compiled with g++ -std=c++17 -o curses_play curses_play.cc -lncurses .

#include <ncurses.h>
#include <string.h>
#include <stdio.h>

#define WIDTH 30
#define HEIGHT 10

int startx = 0;
int starty = 0;

char *choices[] = {   "Choice 1",
  "Choice 2",
  "Choice 3",
  "Choice 4",
  "Exit",
};

int n_choices = sizeof(choices) / sizeof(char *);

void print_menu(WINDOW *menu_win, int highlight);
void report_choice(int mouse_x, int mouse_y, int *p_choice);

int main()
{ int c, choice = 0;
  WINDOW *menu_win;
  MEVENT event;

  /* Initialize curses */
  initscr();
  clear();
  noecho();
  cbreak(); //Line buffering disabled. pass on everything

  /* Try to put the window in the middle of screen */
  startx = (80 - WIDTH) / 2;
  starty = (24 - HEIGHT) / 2;

  attron(A_REVERSE);
  mvprintw(23, 1, "Click on Exit to quit (Works best in a virtual console)");
  refresh();
  attroff(A_REVERSE);

  /* Print the menu for the first time */
  menu_win = newwin(HEIGHT, WIDTH, starty, startx);
  print_menu(menu_win, 1);
  /* Get all the mouse events */
  mousemask(ALL_MOUSE_EVENTS  | REPORT_MOUSE_POSITION, NULL);

  while(1)
  { c = wgetch(menu_win);
    switch(c)
    { case KEY_MOUSE:
      mvprintw(24, 0, "Found mouse!");
      if(getmouse(&event) == OK)
      { /* When the user clicks left mouse button */
        if(event.bstate & BUTTON1_PRESSED)
        { report_choice(event.x + 1, event.y + 1, &choice);
          if(choice == -1) //Exit chosen
            goto end;
          mvprintw(22, 1, "Choice made is : %d String Chosen is \"%10s\"", choice, choices[choice - 1]);
          refresh();
        }
      }
      print_menu(menu_win, choice);
      break;
      default:
      mvprintw(24, 0, "Charcter pressed is = %3d Hopefully it can be printed as '%c'", c, c);
      refresh();
      break;
    }
  }
end:
  endwin();
  return 0;
}


void print_menu(WINDOW *menu_win, int highlight)
{
  int x, y, i;

  x = 2;
  y = 2;
  box(menu_win, 0, 0);
  for(i = 0; i < n_choices; ++i)
  { if(highlight == i + 1)
    { wattron(menu_win, A_REVERSE);
      mvwprintw(menu_win, y, x, "%s", choices[i]);
      wattroff(menu_win, A_REVERSE);
    }
    else
      mvwprintw(menu_win, y, x, "%s", choices[i]);
    ++y;
  }
  wrefresh(menu_win);
}

/* Report the choice according to mouse position */
void report_choice(int mouse_x, int mouse_y, int *p_choice)
{ int i,j, choice;

  i = startx + 2;
  j = starty + 3;

  for(choice = 0; choice < n_choices; ++choice)
    if(mouse_y == j + choice && mouse_x >= i && mouse_x <= i + strlen(choices[choice]))
    { if(choice == n_choices - 1)
      *p_choice = -1;
      else
        *p_choice = choice + 1;
      break;
    }
}

I got it working, I'm not sure which particular tweak was the important tweak (there's about a dozen-or-so things I changed). and I also have a bunch of instrumentation which I did not strip out

Hopefully this is sufficient to get you going.

/*
brew install ncurses
clang++ -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded -Wno-c99-compat -Wno-poison-system-directories -pedantic -fsanitize=undefined,null -std=c++17 -cxx-isystem /usr/local/include -o Merlin2011 Merlin2011.cpp -lncurses
./Merlin2011
*/
#include <ncurses.h>
#include <clocale>
#include <cstring>

#define WIDTH 30
#define HEIGHT 10

static int startx = 0;
static int starty = 0;

static char const* choices[] = {
    "Choice 1", "Choice 2", "Choice 3", "Choice 4", "Exit",
};

static int n_choices = sizeof(choices) / sizeof(char*);

static void print_menu(WINDOW* menu_win, int highlight);
static void report_choice(int mouse_x, int mouse_y, int* p_choice);

static
char sane(int c) {
    if (c > ' ' && c <= '~')
        return static_cast<char>(c);
    return ' ';
}

int main() {
    int choice = 0;
    WINDOW* menu_win;

    setlocale(LC_ALL, "");

    /* Initialize curses */
    initscr();
    clear();
    noecho();
    cbreak();  // Line buffering disabled. pass on everything
    keypad(stdscr, TRUE);
    halfdelay(1);

    /* Try to put the window in the middle of screen */
    startx = (80 - WIDTH) / 2;
    starty = (24 - HEIGHT) / 2;

    attron(A_REVERSE);
    mvprintw(23, 1, "Click on Exit to quit (Works best in a virtual console)");
    refresh();
    attroff(A_REVERSE);

    /* Print the menu for the first time */
    menu_win = newwin(HEIGHT, WIDTH, starty, startx);
    scrollok(menu_win, TRUE);
    keypad(menu_win, TRUE);
    print_menu(menu_win, 1);
    /* Get all the mouse events */
    //auto mm_success = mousemask(ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION, nullptr);
    /* Get just just the click event. */
    mmask_t mousemask_setting = NCURSES_BUTTON_CLICKED;
    auto mm_success = mousemask(mousemask_setting, nullptr);

    if (!mm_success) {
        mvprintw(22, 1, "FAIL: mousemask");
    } else if (mm_success == mousemask_setting) {
        mvprintw(22, 1, "SUCCESS: mousemask");
    } else {
        mvprintw(22, 1, "Partial success: mousemask %x of %x", mm_success, mousemask_setting);
    }
    refresh();

    bool loop{ true };

    while (loop) {
        auto c = wgetch(menu_win);

        switch (c) {
            case KEY_MOUSE: {
                MEVENT event;
                mvprintw(20, 1, "Found mouse!");
                if (getmouse(&event) == OK) { /* When the user clicks left mouse button */
                    mvprintw(20, 1, "Found mouse! %3d %3d %d", event.x, event.y, event.bstate);
                    //if (event.bstate & BUTTON1_PRESSED) { ... }
                    if (event.bstate & NCURSES_BUTTON_CLICKED) {
                        mvprintw(20, 1, "Found mouse! %3d %3d MOUSE_CLICK", event.x, event.y);
                        report_choice(event.x + 1, event.y + 1, &choice);
                        if (choice == -1) { // Exit chosen
                            loop = false;
                            break;
                        }
                        mvprintw(22, 1, "Choice made is : %d String Chosen is \"%10s\"", choice, choices[choice - 1]);
                        refresh();
                    }
                }
                print_menu(menu_win, choice);
            } break;

            case ERR: {
                static int count = 0;
                ++count;
                mvprintw(21, 0, "Nothing happened (%d).", count);
                refresh();
            } break;

            default: {
                static int i = 0;
                mvprintw(24+i, 0, "Key pressed is = %3d '%c'", c, sane(c));
                if (++i == 30)
                    i = 0;
                mvprintw(24+i, 0, "-------------------------");
                refresh();
                if (c == 'q')
                    loop = false;
            } break;
        }
    }

    endwin();
}

void print_menu(WINDOW* menu_win, int highlight) {
    int x, y, i;

    x = 2;
    y = 2;
    box(menu_win, 0, 0);
    for (i = 0; i < n_choices; ++i) {
        if (highlight == i + 1) {
            wattron(menu_win, A_REVERSE);
            mvwprintw(menu_win, y, x, "%s", choices[i]);
            wattroff(menu_win, A_REVERSE);
        } else
            mvwprintw(menu_win, y, x, "%s", choices[i]);
        ++y;
    }
    wrefresh(menu_win);
}

/* Report the choice according to mouse position */
void report_choice(int mouse_x, int mouse_y, int* p_choice) {
    int i, j, choice;

    i = startx + 2;
    j = starty + 3;

    for (choice = 0; choice < n_choices; ++choice)
        if (mouse_y == j + choice && mouse_x >= i && mouse_x <= i + strlen(choices[choice])) {
            if (choice == n_choices - 1)
                *p_choice = -1;
            else
                *p_choice = choice + 1;
            break;
        }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM