Sehr Umfangreiche Webseite zum Programmieren in C Perl CGI, Skripting, Linux, Systemprogrammierung C C++ C/C++ ANSI C Linux Linuxsystemprogrammierung Semigrafik curses.h ncurses curses gpm.h gpm Linuxsystemprogrammieren Semigrafik curses.h ncurses curses gpm.h gpm 11. Mausprogrammierung mit gpm.h

11.5. gpm und curses.h           zurück Ein Kapitel tiefer zum Inhaltsverzeichnis

Was liegt näher, als gpm in Verbindung mit curses zu verwenden. Nur wie können wir auf die einzelnen oder das einzelne Fenster zugreifen? Zum Einlesen von Ereignissen hatten wir doch nur Gpm_Getchar() und Gpm_Getc(). Wenn Sie sich noch an curses erinnern (Ist Voraussetzung für dieses und dem nächsten Kapitel) wissen Sie doch, dass wir für jede Funktion Beispiel mvprintw() ein Fensterorientiertes Gegenstück hatten das sich nicht auf stdscr bezieht. In diesem Fall wäre das mvwprintw. Und bei der Mausfunktion mit gpm ist dies ebenso der Fall mit ...

int Gpm_Wgetch(WINDOW *win);

Wenn sich die Mausabfrage auf den stdscr bezieht, können Sie sicherlich auch weiterhin Gpm_Getch() verwenden oder aber auch ...

Gpm_Wgetch(stdscr);

Da wir dies nun wissen sollten wir uns das ganze mal in der Praxis ansehen ...

#include <stdio.h>
#include <unistd.h>
#include <gpm.h>
#include <linux/keyboard.h>
#include <ncurses.h>

#define WIDTH 30
#define HEIGHT 10
#define MOVE_DONE 99

int startx = 0;
int starty = 0;

char *choices[] = { "Wahl 1",
                    "Wahl 2",
                    "Wahl 3",
                    "Wahl 4",
                    "Ende",
                  };

/*Anzahl der Menüs zum Auswählen*/
int n_choices = sizeof(choices) / sizeof(char *);

void print_menu(WINDOW *menu_win, int highlight);

int my_handler(Gpm_Event *event, void *data)
{
  int choice = -1;
  /*Eine Art Mouseover-Effekt*/
  if(event->type & GPM_MOVE) /*Maus wurde bewegt?*/
    {
      choice = report_choice(event->x, event->y); /*Wenn ja wo?*/
/*Liefert die Funktion report_choice -1 zurück dann wars außerhalb*/
      if(choice == -1)
          return 0; /*Kein Auswahl*/
      else
          return MOVE_DONE + choice;
    }
  if(event->type & GPM_DOWN)
    {
       choice = report_choice(event->x, event->y);
       if(choice == -1)
           return 0; /*Bedeutet für Gpm_Wgetch() weitermachen*/
       else
           return choice;
    }
  return 0;
}

int main()
{
  Gpm_Connect conn;
  int c, choice = -1;
  WINDOW *menu_win;
/*Standartausgabe auf richtigem Terminal eingestellt?*/
  if(!isatty(STDOUT_FILENO))
    {
       printf("Filedeskriptor stdout ist nicht für das Terminal eingestellt");
       exit(1);
    }

  conn.eventMask = ~0;   /*Alle Mausereignisse*/
  conn.defaultMask = 0;   /*nix davon an gpm*/
  conn.minMod = 0;         /*Keine Taste muss gedrückt werden*/
  conn.maxMod = ~0;      /*....aber es dürfen dennoch alle gedrückt werden*/

  if(Gpm_Open(&conn, 0) == -1){
        printf("Kann keine Verbindung zum Maus-Server herstellen\n");
        exit(0);
       }

  initscr();      /*curses initialisieren*/
  clear();        /*Bildschirm löschen*/
  noecho();     /*keine Zeichen ausgeben*/
  cbreak();      /*Zeilenpufferung ausschalten*/
  startx = (80 - WIDTH) / 2;
  starty = (24 - HEIGHT) / 2;

  menu_win = newwin(HEIGHT, WIDTH, starty, startx);
/*Zeichne neues Fenster und heben Auswahl 1 "Wahl1" hervor*/
  print_menu(menu_win, 1);

  gpm_handler = my_handler; /*Maushandler einrichten*/
  gpm_visiblepointer = 1;   /*Mauszeiger immer sichtbar*/

  while((c = Gpm_Wgetch(menu_win)) != EOF) /*EOF = -1*/
    {
      if(c != -1 && gpm_hflag)
        {
           if(c > MOVE_DONE) /*Wurde ein Menü Ausgewählt?*/
              {
                choice = c - MOVE_DONE; /*Bsp. "Wahl 2" -> 101-99=2*/
 /*Menü neu ausgeben mit neuer  Hervorhebung*/
                print_menu(menu_win, choice);
                continue; /*Und weiter...........*/
              }
           else
              { /*Was haben wir Ausgewählt?*/
                mvprintw(23, 1,
                  "Ihre Wahl ist : %d. Auswahl \"%10s\"", c, choices[c - 1]);
                refresh();
                if(c == n_choices) /*Haben wir ENDE angeklickt?*/
                    break; /*Und tschüss........*/
                print_menu(menu_win, c); /*Fenster neu zeichnen*/
              }
        }
    }
  Gpm_Close();
  endwin(); /*Ende curses.......*/

  return 0;
}

/* "Zeichne" Menü*/
void print_menu(WINDOW *menu_win, int highlight)
{
  int x, y, i;

  x = 2;
  y = 2;
/*Ein Rahmen um das Fenster zeichnen*/
  box(menu_win, 0, 0);
/*Für die Hervorhebung des Mouseover-Effekt*/
  for(i = 0; i < n_choices; ++i)
   {
      if(highlight == i + 1)
        {
          wattron(menu_win, A_REVERSE|A_BOLD); /*Hervorhebung für Auswahl an*/
          mvwprintw(menu_win, y, x, "%s", choices[i]); /*Auswahl mit Hervorhebungausgeben*/
          wattroff(menu_win, A_REVERSE|A_BOLD); /*Hervorhebung für Auswahlwieder aus*/
        }
      else/*Auswahl ausgeben ohne Hervorhebung*/
         mvwprintw(menu_win, y, x, "%s", choices[i]);
      ++y; /*Nächste Zeile*/
    }
  wrefresh(menu_win); /*Zum Schluss Fenster neu Zeichnen*/
}

/*Gibt Ihre Auswahl zurück, wird in der Funktion my_handler aufgerufen*/
int report_choice(int mouse_x, int mouse_y)
{
  int i,j, choice;

  i = startx + 2; /*+2 da String "Wahl n" 2Spalten vom Rand entfernt beginnt*/
  j = starty + 3; /*+3 da String "Wahl n" 3 Zeilen unter Top beginnt*/

  for(choice = 0; choice < n_choices; ++choice)
/*Wir testen alle Möglichkeiten durch*/
    if(mouse_y == j + choice && mouse_x >= i &&
        mouse_x <= i + strlen(choices[choice]))
            break;

  if(choice == n_choices)
     return -1; /*Nix passiert*/
  else
     return (choice + 1); /*...oder neue Position der Auswahl zurückgeben*/
}

Diese Programm stellt normalerweise kein Neuland mehr da wenn Sie die Kapitel zuvor gelesen haben. Vorraussetzung ist natürlich auch, dass Sie über curses bescheid wissen. Dies können Sie ebenso in diesem Kurs nachlesen. Die einzige "Schwierigkeit" in einem Programm dieser Art, dürfte sein, den aktuellen Status und Position der Maus zurückzugeben. Dies geschieht in unserem Fall bei der Funktion report_choice ganz einfach. Passen Sie das Programm einfach Ihren Bedürfnissen an. Rufen Sie z.B. anstatt der Ausgabe ...

mvprintw(23, 1, "Ihre Wahl ist : %d. Auswahl \"%10s\"", c, choices[c - 1]);

... einfach eine Funktion oder ein anderes Programm auf, welches das tun soll, was Sie eben benötigen.

11.6. Regions of Interest           zurück Ein Kapitel tiefer zum Inhaltsverzeichnis

Die Meisten dürften mit dem Beispiel im Kapitel zuvor für Ihr Programm zufrieden sein. Für Wissbedürftige gibt es noch ein weiteres Kapitel, Regions of Interest.

Die "Region of Interest" ist ein rechteckiger Bereich des Bildschirms, der von Ihnen festgelegt wird und mit bestimmte Maushandler aufgerufen werden kann. Wozu soll das wieder gut sein? Wenn Sie z.B. mit curses mehrere Fenster gleichzeitig offen haben und für jedes Fenster einen eigenen Maushandler einrichten wollen.

Dabei legen Sie jede neue Region of Interest auf einem Stack. gpm kümmert sich dann um denn Rest, damit jedes Fenster den passenden Maushandler erhält. Die Funktion die uns das ganze abnimmt lautet ...

Gpm_Roi *Gpm_PushRoi(int xmin, int xmax, int ymax, int ymin,
         int mask, Gpm_Handler *gpm, void *xtradata);

Zugegeben die vielen Parameter erscheinen einem nicht gerade Anwenderfreundlich, aber es ist halb so schlimm wie es aussieht. Zuerst müssen wir die Struktur Gpm_Roi etwas genauer betrachten ...

typedef struct Gpm_Roi
  {
    short xMin, xMax; /*Obere linke Ecke der Region of Interest*/
    short yMin, yMax; /*Untere rechte Ecke der Region of Interest*/
    unsigned short minMod, maxMod; /*Modifiers und eventMask*/
    unsigned short eventMask; /*das selbe wie schon bei Gpm_Event*/
    unsigned short owned; /*für uns nicht von Interesse*/
    Gpm_Handler *handler; /*Handler für die Region of Interest*/
    void *clientdata; /*Informationen von einer Region of Interest an*/
                                   /*anderen Handler übergeben*/
    struct Gpm_Roi *prev; /*voriges Element auf dem Stack*/
    struct Gpm_Roi *next; /*nextes Element auf dem Stack*/
  }Gpm_Roi;

Anhand dieser Struktur lässt sich schon erkennen, dass es sich um eine doppelte Verkettete Liste in Form eines Stacks handelt. Den Region of Interest - Handler dürfen Sie nicht wie den normalen Handler mit gpm_handler aktivieren, da dieser Vorrang vor allen Maushandlern der Region of Interest besitzt und wir somit keine Region of Interset haben. Anstatt nun den gpm_handler zu benutzen, gibt es für Region of Interest, den gpm_roi_handler den Sie dafür verwenden können. Ansonsten ist der komplette Ablauf nicht viel anders als schon bei Gpm_Event. Die Konstanten sind die selben. Wollen wir uns ein einfaches Beispiel dazu ansehen ...

#include <stdio.h>
#include <gpm.h>

int Roi_fuer_Berreich(Gpm_Event *event, char *data)
{
  printf("Ereignis in der \"Region of Interest\" auf Planeten Erde ");
  printf(" an Position ->%d<- ->%d<- \n", event->x, event->y);
  if(data != NULL)
       printf("%s\n",data);
  return 0;
}

int Ein_weiterer_Roi_Berreich(Gpm_Event *event, char *data)
{
  printf("Ereignis in der \"Region of Interest\" auf Pluto");
  printf(" an Position ->%d<- ->%d<- \n", event->x, event->y);
    if(data != NULL)
  printf("%s\n",data);
  return 0;
}

int Roi_ausserhalb_Berreich(Gpm_Event *event, void *data)
{
  printf("Diese Ereignis trat NICHT in der \"Region of Interest\" auf ");
  printf(" an Position ->%d<- ->%d<- \n", event->x, event->y);
  return 0;
}

int main()
{
  Gpm_Connect connect;
  Gpm_Roi *roi, *roi2;
  char zeichen=0;
  char erde[] = "Hallo Pluto hier Erde";
  char pluto[] = "Hallo Erde take it easy";

  connect.eventMask =~0;
  connect.defaultMask=0;
  connect.minMod=0;
  connect.maxMod=~0;

  if(Gpm_Open(&connect, 0) == -1) /*Verbinden mit Mausserver*/
    {
       fprintf(stderr, "Kann keine Verbindung zum Mausserver herstellen!!\n");
       exit(0);
    }

  gpm_visiblepointer=1; /*Mauszeiger immer sichtbar*/

/*Handler für 10 Zeilen und 30 Spalten an der linken oberen Ecke anlegen,
      ~0 = alle Mausereignisse, Handler ist die Funktion Roi_im_Berreich*/
  roi=Gpm_PushRoi(1,1,31,10,~0, Roi_fuer_Berreich, (void *)&erde);
/*Handler für einen weiteren Bereich ab Spalte 31 Zeile 10 bis Spalte 80 Zeile 25*/
  roi2=Gpm_PushRoi(31,10,80,25,~0,Ein_weiterer_Roi_Berreich , (void *)&pluto);
/*Jetzt noch einen Region of Interest - Handler einrichten der für den*/
/*   restlichen Bereich zuständig ist*/
  gpm_roi_handler=Roi_ausserhalb_Berreich;
/*Ereignisse lesen..........*/
  while(zeichen != 'e')
      zeichen = Gpm_Getc(stdin);

  Gpm_PopRoi(roi); /*Region of Interest wieder vom Stack entfernen*/
  Gpm_PopRoi(roi2);
  Gpm_Close();
  return 0;
}
Wir legen in diesem Beispiel zwei "Regions of Interest" auf dem Stack. Der eine stellt die Erde da und der andere den Pluto. Bewegen Sie die Maus nicht in einen der beiden Regionen, legen wir noch mit dem gpm_roi_handler eine Region für unbekannten Bereich an. Also: Move the Mouse from Earth to Pluto and return, but take care in Regions out of intrest ;) Neu dürfte in diesem Beispiel die Funktion ...
Gpm_Roi *GpmPopRoi(Gpm_Roi *roi);

...womit wir unsere Region of Interest wieder vom Stack entfernen.

Dumm an dem Programm ist nur, dass wir die Region of Interest nicht sehen können. Und was könnte sich da wieder besser eignen als curses? Dies folgende Programm habe ich in ähnlicher Form im Internet gefunden etwas gekürzt und an den Bedürfnissen des Kurses angepasst ...

#include <stdio.h>
#include <gpm.h>
#include <linux/keyboard.h>
#include <ncurses.h>

typedef struct _WIN {
    int nlines;
    int ncols;
    int y;
    int x;
    WINDOW *p_win;
   }WIN;

void create_windows(WIN my_windows[]);
void set_win_params(WIN *win, int nlines, int ncols, int y, int x);

/*Handler gilt für alle 4 Fenster, Wir benutzen die Variable data als übergabe um
welches Fenster es sich handelt*/
int my_handler(Gpm_Event *p_event, void *data)
{
  WINDOW *current_win;
  WIN *my_win;

  my_win = (WIN *)data;
  current_win = my_win -> p_win;

  if(p_event->type & GPM_ENTER)
      mvwprintw(current_win, 1, 1, "Willkommen......");
  if(p_event->type & GPM_LEAVE)
      mvwprintw(current_win, 1, 1, "Und Tschuess......");
  if(p_event->type & GPM_DOWN)
       {
         mvwprintw(current_win, my_win->nlines - 2, 1, "Mausbutton gedrückt");
         if(p_event->buttons & GPM_B_LEFT)
             mvwprintw(current_win, my_win->nlines/2, 1,"Linke Maustaste gedrückt ");
         if(p_event->buttons & GPM_B_RIGHT)
             mvwprintw(current_win, my_win->nlines/2, 1,"Rechte Maustaste gedrückt");
         if(p_event->buttons & GPM_B_MIDDLE)
             mvwprintw(current_win, my_win->nlines/2, 1,"Mittlere Maustaste gedrückt");
       }
  if(p_event->type & GPM_UP)
       {
         mvwprintw(current_win, my_win->nlines - 2, 1, "Mausbutton losgelassen ");
         if(p_event->buttons & GPM_B_LEFT)
             mvwprintw(current_win, my_win->nlines/2, 1,"Linke Maustaste gedrückt");
         if(p_event->buttons & GPM_B_RIGHT)
             mvwprintw(current_win, my_win->nlines/2, 1,"Rechte Maustaste gedrückt ");
         if(p_event->buttons & GPM_B_MIDDLE)
             mvwprintw(current_win, my_win->nlines/2, 1,"Mittlere Maustaste gedrückt");
       }
  wrefresh(current_win);
  return 1;
}

int main()
{
  Gpm_Connect conn;
  WIN my_windows[4];
  int i, mask, c;

  conn.eventMask = ~0; /*Alle Mausereignisse*/
  conn.defaultMask = 0; /*nix geht an gpm*/
  conn.minMod = 0;        /*keine Taste muss gedrückt werden*/
  conn.maxMod = ~0;      /*Tasten dürfen aber gedrückt werden*/


  if(Gpm_Open(&conn, 0) == -1) /*Verbindung zum Mausserver*/
    {
      fprintf(stderr, "Kann keine Verbindung zum Mausserver herstellen.....\n");
      exit(0);
    }

  initscr();    /*Curses initialisieren*/
  clear();      /*Bildschirm löschen*/
  refresh();
  noecho();   /*Keine Ausgabe auf dem Bildschirm*/
  cbreak();    /*Zeilenpufferung ausschalten*/

  create_windows(my_windows);
/*Unser Interessieren nur die Ereignisse Maus drücken und loslassen und*/
/*   Eintritt bzw. Austritt aus dem Region of Interest - Bereich*/
  mask = GPM_UP | GPM_DOWN | GPM_ENTER | GPM_LEAVE;
  for(i = 0;i < 4; ++i)
    {
       box(my_windows[i].p_win, 0, 0); /*Rahmen um die Fenster*/
       wrefresh(my_windows[i].p_win); /*Zeichenen*/
/*Handler für die einzelnen Regionen einrichten*/
       Gpm_PushRoi(my_windows[i].x, my_windows[i].y, /*obere linke Ecke bis......*/
       my_windows[i].x + my_windows[i].ncols - 1,
       my_windows[i].y + my_windows[i].nlines - 1, /*....untere rechte Ecke*/
       mask, my_handler, &my_windows[i]); /*Ereignisshandler und die Daten*/
    }
  gpm_visiblepointer = 1; /*Zeiger immer sichtbar*/
  gpm_zerobased = 1; /*linke obere ist 1/1*/
  gpm_roi_handler = my_handler; /*Handler einrichten für restlichen Bereich*/
  gpm_roi_data = stdscr;
  while((c = Gpm_Wgetch()) != EOF) /*Und Daten lesen*/
    {
      if(c == 4) /*CTRL & D*/
         break;
    }
  Gpm_Close(); /*Verbindung wieder beenden*/
  endwin(); /*curses beenden*/

  return 0;
}

/*Wir erzeugen 4 neue Fenster, alle Daten werden an die Struktur WIN übergeben*/
void create_windows(WIN my_windows[])
{
  int nlines = (LINES - 3) / 2;
  int ncols = (COLS - 3) / 2;
/*Fenster links oben*/
  my_windows[0].p_win = newwin(nlines, ncols, 1, 1);
  set_win_params(&my_windows[0], nlines, ncols, 1, 1);
/*Fenster rechts oben*/
  my_windows[1].p_win = newwin(nlines, ncols, 2 + nlines, 1);
  set_win_params(&my_windows[1], nlines, ncols, 2 + nlines, 1);
/*Fenster links unten*/
  my_windows[2].p_win = newwin(nlines, ncols, 1, 2 + ncols);
  set_win_params(&my_windows[2], nlines, ncols, 1, 2 + ncols);
/*Fenster rechts unten*/
  my_windows[3].p_win = newwin(nlines, ncols, 2 + nlines, 2 + ncols);
  set_win_params(&my_windows[3], nlines, ncols, 2 + nlines, 2 + ncols);
}

/*Die Restlichen Parameter an unsere selbstdefinierte Struktur WIN*/
void set_win_params(WIN *win, int nlines, int ncols, int y, int x)
{
  win->nlines = nlines;
  win->ncols = ncols;
  win->y = y;
  win->x = x;
}

Diese Simple Beispiel demonstriert eindeutig, was man sich unter Region of Interest vorstellen kann.

Als Ergänzung zu diesem Kapitel sind noch die Funktionen ...

Gpm_Roi *Gpm_RaiseRoi(Gpm_Roi *which, Gpm_Roi *before);
Gpm_Roi *Gpm_LowerRoi(Gpm_Roi *which, Gpm_Roi *after);

...zu erklären, womit wir die einzelnen Regions of Interests auf dem Stapel verschieben können. Mit Gpm_RaiseRoi() können Sie eine Roi an den Anfang des Stapels schieben ...

Gpm_RaiseRoi(roi, NULL); //Region of Interest roi kommt an den Anfang des Stapels

...oder...

Gpm_RaiseRoi(roi , roi2) //roi kommt im Stapel vor der Region of Interests roi2

Mit Gpm_LowerRoi() machen wir genau das Gegenteil ...

Gpm_LowerRoi(roi, NULL); //roi kommt ans Ende des Stapels
Gpm_LowerRoi(roi, roi2); //roi kommt genau hinter roi2

Weiter mit Empfehlenswerter Literatur und Links