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.1. Was ist und kann gpm?           zurück Ein Kapitel tiefer zum Inhaltsverzeichnis

gpm stellt eine Möglichkeit für die Mausprogrammierung im Textmodus da (gpm = General Purpose Mousehandler). Damit ist es möglich, z.B. Copy&Paste im Textmodus zu realisieren. Es soll sogar möglich sein Touchscreens auf Notebooks zu Programmieren. Da ich leider kein Notebook mit Touchscreen habe, kann ich nicht sagen ob und wie dies genau funktioniert. Wer Erfahrungen damit gemacht hat kann mir ja eine Mail schicken.

Der Vorteil von gpm ist sicherlich, dass Sie mit dieser API, sowohl im Textmodus als auch mit einer XTerm arbeiten können.

Die gpm-Programmierung besteht aus einem Server, der die Maus und die Kommunikation zwischen der Maus und Ihrem Rechner verwaltet. Der zweite Teil stellt den Client da, der die Kommunikation zwischen dem Server und dem Programm sicherstellt.

Hierzu ein schneller Überblick, wie man sich das Bildlich vorstellen kann ...

GMP-Server-Client-Prinzip

11.2. Gpm_Open und Gpm_Close - Verbinden mit dem gpm-Server           zurück Ein Kapitel tiefer Ein Kapitel höher zum Inhaltsverzeichnis

Damit gpm überhaupt läuft, müssen Sie es natürlich erst installierst haben. Vor allem die libgpm. Falls nicht holen Sie dies nach. Um eine Verbindung zum gpm-Dämon zu erstellen, haben wir die Funktion ...

int Gpm_Open(Gpm_Connect *connect, int flag);

Mit der Variable Gpm_Connect bestimmen wir, welche Ereignisse wir bearbeiten wollen. Gpm_Connect ist eine Struktur und hat folgendes Aussehen ...

typedef struct Gpm_Connect
      {
        unsigned short eventMask, defaultMask;
        unsigned short minMod, maxMod;
        int pid;                          /* wird von gpm gefüllt*/
        int vc;                           /* "     "   "       " */
      }Gpm_Connect;

Den Parameter flag können Sie auf 0 setzen. Es sei denn Sie wollen eine Verbindung mit einer bestimmten Konsole herstellen (Beispielsweise /dev/ttyn). vc bestimmt dabei die Nummer der Konsole.

Im Feld eventMask in der Struktur Gpm_Connect, können Sie angeben, welche Ereignisse gpm an das Programm schicken soll. Folgende Flags können Sie hierfür für die Ereignismaske angeben ...

Mit der Variable defaultMask in der Struktur Gpm_Connect, bestimmen Sie welche Ereignisse von gpm weiterverarbeitet werden sollen. Folgende Werte können Sie hierzu z.B. verwenden ...

Mit den beiden Variablen minMod und maxMod sind wir fähig, Ereignisse aus Kombinationen von Mausknöpfen und Tastendrücke zu generieren. In minMod und maxMod stellen wir die die Tasten ein, die beachtet werden sollen ...

Folgende Möglichkeiten haben Sie für TASTE ...

KG_SHIFT, KG_CTRL, KG_ALT, KG_ALTGR, KG_SHIFTL, KG_SHIFTR, KG_CTRLL, KG_CTRLR, KG_CAPSSHIFT

Sie finden mehr zu diesen Konstanten in der Headerdatei <linux/keyboard.h>

Jetzt wollen wir das ganze mal in der Praxis testen (linken Sie zur Übersetzung die Library gpm hinzu ...

gcc -o connect_gpm connect_gpm.c -lgpm

Hier nun das Beispiel ...

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

int main()
{
  int status;
  Gpm_Connect connect;

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

  status=Gpm_Open(&connect, 0); /*Wir stellen ein Verbindung zu gpm her*/
  if(status==-1)   /*-1 bedeutet Verbindung kann nicht hergestellt werden*/
    {
      fprintf(stderr, "Fehler bei Gpm_Open..........\n");
      exit(0);
    } /*-2 bedeutet das das Programm im einem Xterm ausgeführt wird*/
  else if(status==-2)
     printf("Das Programm läuft in einem Xterm-Fenster\n");
  else
     printf("Das Programm läuft in einer Textkonsole\n");

  Gpm_Close(); /*Wir beenden die Verbindung zu gpm-Dämon wieder*/
  return 0;
}

Sollte das Programm nicht mal im Textmodus funktionieren, liegt es meistens daran, dass der gpm Dämon noch nicht läuft. Starten Sie diesen mit einem einfachen Aufruf gpm. Mehr dazu unter man gpm.

Das Programm macht jetzt noch nichts. Wir definieren hier nur kurz die Ereignisse. Anschließend wird versucht eine Verbindung mit dem gpm-Dämon herzustellen (Gpm_Open). Zum Schluss testen wir, ob die Verbindung zu gpm geklappt hat (-1) oder ob die Verbindung unter einem Xterm (-2) läuft. Mit Gpm_Close beenden wir die Verbindung zum gpm-Dämon wieder.

11.3. Gpm_Handler - Der Ereignishandler           zurück Ein Kapitel tiefer Ein Kapitel höher zum Inhaltsverzeichnis

Als nächstes benötigen wir einen Ereignishandler, der aufgerufen wird, wenn ein Mausereignis an das Programm geschickt wird. Diese Funktion ist eine von uns geschriebene, die auf das Ereignis beliebig reagieren kann. Hier der Syntax dazu ...

typedef int Gpm_Handler(Gpm_Event *EVENT, void *DATA);
Gpm_Handler *gpm_handler;

Diese Funktion sorgt dafür, dass ein Mausereignis in eine Tastaturangabe umgewandelt wird, die dann der Funktion Gpm_Getc oder Gpm_Getchar übergeben wird. Diese beiden Funktionen dienen zum Einlesen der Daten von Tastatur und Maus. Diese beiden Routinen sollten Sie also anstelle von getc und getchar verwenden. Hier der Syntax dazu ...

int Gpm_Getc(FILE *f);
int Gpm_Getchar(void);

Wollen wir uns schnell ein kurzes Beispiel ansehen ...

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

int Maushandler(Gpm_Event *event, void *data)
{
  if(event->buttons & GPM_B_LEFT)
    {
      Gpm_Close();
      exit (1);
    }
  return 0;
}

int main()
{
  int status;
  Gpm_Connect connect;
  char zeichen=0;
  Gpm_Event *event;

  connect.eventMask=~0;  /*alle Mausereignisse*/
  connect.defaultMask=0; /*nichts an gpm zurückgeben*/
  connect.maxMod=~0;     /*keine Taste muss gedrückt sein*/
  connect.minMod=0;      /*Es dürfen aber alle gedrückt werden*/

  status=Gpm_Open(&connect, 0); /*Wir stellen ein Verbindung zu gpm her*/
  if(status==-1) /*-1 bedeutet Verbindung kann nicht hergestellt werden*/
    {
      fprintf(stderr, "Fehler bei Gpm_Open..........\n");
      exit(0);
    } /*-2 bedeutet das das Programm im einem Xterm ausgeführt wird*/
  else if(status==-2)
      printf("Das Programm läuft in einem Xterm-Fenster\n");
  else
      printf("Das Programm läuft in einer Textkonsole\n");

  printf("Programm mit der linken Maustaste beenden!!\n");

  gpm_handler=Maushandler;
  while(zeichen != 'e') /*Mit 'e' kann man auch Abbrechen*/
    {
       zeichen=Gpm_Getc(stdin);
    }
  fflush(stdout);

  Gpm_Close(); /*Wir beenden die Verbindung zu gpm-Dämon wieder*/
  return 0;
}

Die Funktion Maushandler stellt den Ereignishandler da. Einrichten tun wir diesen mittels ...

gpm_handler=Maushandler;

Nach jedem Tasten-oder-Mausdruck wird jetzt überprüft, ob es sich dabei um die linke Maustaste handelt. Falls ja, brechen wir das Programm ab. Auch abgebrochen wird unser Programm durch die Eingabe des Buchstaben 'e' und Enter.

Da ja nun durch das Einlesen mittels Gpm_Getc oder Gpm_Getchar nicht mehr zwischen der Tastatur und der Maus unterschieden wird, Sie das aber nicht unbedingt wollen, gibt es dafür eine globale Variable Namens gmp_hflag. Diese setzen Sie auf dem Wert 1, damit wieder zwischen Tastatur und Maus unterschieden wird.

Jetzt haben Sie zwar gesehen wie Sie Ereignishandler einsetzen können aber nicht welche Ereignisse es alles gibt. Die Variable Gpm_Event ist eine Struktur mit folgendem Aussehen ...

typedef struct Gpm_Event
      {
        unsigned char buttons, modifiers;
        /*Nicht von Interesse. Für Mausereignisse mehrer Konsolen gedacht*/
        unsigned short vc;
        short dx, dy, x, y;
        /*x,y Position des Mauszeigers als Ereigniss auftrat...*/
        /*....dx, dy Deltax Deltay Veränderung Pos. gegebenüber...*/
        /*....des letzten Ereignises*/
        enum Gpm_Etype type;
        int clicks;
        enum Gpm_Margin margin;
      }Gpm_Event;

Wie schon im Programmbeispiel oben gesehen, können Sie den Button mit '&' und einer Konstante überprüfen. Folgende drei Möglichkeiten gibt es ...

if(event->buttons & GPM_B_LEFT)
{ /*linke Maustaste wurde gedrückt*/ }
if(event->buttons &GPM_B_RIGHT)
{ /*rechte Maustaste wurde gedrückt*/ }
if(event->buttons &GPM_B_MIDDLE)
{ /*Mittlere Maustaste wurde gedrückt*/ }

Im Feld modifiers sind die gedrückten Metatasten eingetragen. Beispielsweise ...

if(event->modifiers &KG_CTRLL)
{/*linke Controll-Taste wurde gedrückt*/}
if(event->modifiers &KG_SHIFTR)
{/* rechte Shift-Taste wurde gedrückt*/}
...........
..........

Im Feld type wird ebenfalls kodiert, welche Ereignisse aufgetreten sind. Beispielsweise ...

if(event->type &GPM_DOUBLE)
{ /*Doppelklick erkannt*/ }
if(event->type &GPM_TRIPPLE)
{/*Dreifachklick wurde festgestellt (wer macht sowas?) */ }

Folgende Bitmasken stehen außerdem noch zu Ihrer Verfügung ...

Im Feld click befindet sich die Anzahl der Klicks die Aufgetreten sind, bei GPM_DRAG|GPM_UP und nur GPM_DRAG. Der mögliche Wert beträgt 0-3. Beispielsweise ...

if(event->type &GPM_DRAG)
    if(event->clicks ==2)
       printf("Heissa geschafft\n");

In diesem Falls brauchen Sie nur wie ein verrückter die Maus bewegen und Doppelklicks machen ;)

In margin befinden sich die Bits wenn der Mauscursor den Bildschirm verlässt. Eins der folgenden 4 Bits ist dann gesetzt ...

Auch hier ist der Einsatz wie schon in den anderen Beispielen zuvor ...

if(event->margin &GPM_TOP)
   printf("Der Cursor ist noch oben weg\n");

Sie können gerne diese Ereignisse in das Programm oben reinschreiben und testen.

11.4. GPM_DRAWPOINTER, gpm_visiblepoint u. Programmbeispiele           zurück Ein Kapitel tiefer zum Inhaltsverzeichnis

Ein häufiges Problem in der Textkonsole ist, dass der Pointer, wenn ich den so nennen darf, der beim Neuzeichnen des Bildschirmes verschwindet. Um dies zu vermeiden empfehle ich Ihnen folgendes Makro zu verwenden ...

int GPM_DRAWPOINTER(Gpm_Event *event);

Diese Funktion setzen Sie so ein ...

int Maushandler(Gpm_Event *event, void *data)
{
  if(event->margin &GPM_TOP)
     printf("Der Cursor ist noch oben weg\n");
  GPM_DRAWPOINTER(event);
  return 0;
}

Das Ganze lässt sich aber noch einfacher realisieren, in dem Sie die globale Variable gpm_visiblepointer auf 1 setzen. Dann wird der Mauszeiger immer angezeigt. So da wir jetzt mehrer Möglichkeiten von Ereignisse kennen, die es gilt abzufangen, wollen wir auch noch ein Beispiel dazu schreiben ...

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

int my_handler(Gpm_Event *event, void *data)
{
  gpm_visiblepointer=1; /*Mauscursor immer vorhanden*/
  if(event->buttons & GPM_B_LEFT)
      if(event->type & GPM_DOWN)
         printf("Linker Button gedrückt: ");
      else if(event->type & GPM_UP)
         printf("Linker Button losgelassen: ");

  if(event->buttons & GPM_B_RIGHT)
      if(event->type & GPM_DOWN)
            printf("Rechter Button gedrückt: ");
      else if(event->type & GPM_UP)
            printf("Rechter Button losgelassen: ");

  if(event->buttons & GPM_B_MIDDLE)
      if(event->type & GPM_DOWN)
            printf("Mittlerer Button gedrückt");
      else if(event->type & GPM_UP)
            printf("Mittlerer Button losgelassen");

  printf(" an Position %d %d ",event->x, event->y);
  if(event->type &GPM_SINGLE)
      printf(" -> Einfacher Klick");
  if(event->type &GPM_DOUBLE)
      printf(" -> Doppelter Klick");
  if(event->type &GPM_TRIPLE)
      printf(" -> Dreifacher Mausklick");

  if(event->buttons & GPM_B_LEFT || event->buttons & GPM_B_RIGHT ||
     event->buttons & GPM_B_MIDDLE)
         printf("\n");
  return 0;
}

int main()
{
  Gpm_Connect connect;
  int c;

  connect.eventMask = ~0;  /*Alle Mausereignisse*/
  connect.defaultMask = 0;  /*Nichts geht an gpm*/
  connect.minMod = 0;        /*Es müssen keine Tasten gedrückt werden*/
  connect.maxMod = ~0;     /*Es dürfen aber alle Tasten gedrückt werden*/

  if(Gpm_Open(&connect, 0) == -1){ /*Verbindung zu gpm öffnen*/
        printf("Kann keine Verbindung zum Mouseserver herstellen\n");
        exit(0);
       }
  gpm_handler = my_handler; /*Handler initialisieren*/
  while((c = Gpm_Getc(stdin)) != EOF)
       printf("%d\n", c);
  Gpm_Close();
  return 0;
}

Ein weiteres Beispiel mit Abfangen von Tastatur und Mausdrucks also event->button und event->modifiers ...

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

int my_handler(Gpm_Event *event, void *data)
{
  gpm_visiblepointer=1;
  if(event->type & GPM_DOWN) /*wurde eine Moustaste gedrückt*/
    {
      if(event->modifiers & (1 << KG_SHIFT))
         printf("Beide Shift + ");
      if(event->modifiers & (1 << KG_CTRL))
         printf("Beide Ctrl + ");
      if(event->modifiers & (1 << KG_ALT))
         printf("Linke Alt + ");
      if(event->modifiers & (1 << KG_ALTGR))
         printf("Rechte Alt + ");
      if(event->modifiers & (1 << KG_SHIFTL))
         printf("Linke Shift + ");
      if(event->modifiers & (1 << KG_SHIFTR))
         printf("Rechte Shift + ");
      if(event->modifiers & (1 << KG_CTRLL))
         printf("Linke Ctrl + ");
      if(event->modifiers & (1 << KG_SHIFTR))
         printf("Rechte Ctrl + ");
      if(event->modifiers & (1 << KG_CAPSSHIFT))
         printf("Capslock + ");
      if(event->buttons & GPM_B_LEFT)
         printf(" Linker Mousebutton\n");
      if(event->buttons & GPM_B_RIGHT)
         printf(" Rechter Mousebutton\n");
      if(event->buttons & GPM_B_MIDDLE)
         printf(" Mittlerer Mousebutton\n ");
     }
  return 0;
}

int main()
{
  Gpm_Connect connect;
  int c;
/*Wir interessieren uns für eine gedrückte Maustaste*/
  connect.eventMask = GPM_DOWN;
/*Alle Ergebnisse weitergeben die unser Programm nicht verarbeitet*/
  connect.defaultMask = GPM_MOVE;
/*Alle Ereignisse bei denen mindestens eine  Shift-Taste gedrückt wurde*/
  connect.minMod = 1 << KG_SHIFT;
  connect.maxMod = ~0;

  if(Gpm_Open(&connect, 0) == -1) {
      printf("Kann keine Verbindung zum Mouseserver herstellen\n");
      exit(0);
     }
  gpm_handler = my_handler;
  while((c = Gpm_Getc(stdin)) != EOF)
       printf("%d\n", c);
  Gpm_Close();
  return 0;
}

Weiter mit 11.5. gpm und curses.h