Programmation dirigée par événement (X Windows System
Transcription
Programmation dirigée par événement (X Windows System
2014-2015 Programmation dirigée par événement (X Windows System - Xlib) 1. X Window System (Généralités) 2. Quelques clients 3. Connexion 4. Fenêtre 5. Événements 6. Gestionnaire de fenêtres * Cette note de cours est inspirée du cours du Professeur Jean Berstel http://www-igm.univ-mlv.fr/~berstel/Cours/liste.html 2I007 & LI357 / Université Paris VI 1 Choun Tong LIEU 2014-2015 1. X Windows System (Généralités) • Le système X (X Window System, X ou X11) a été développé au MIT, dans le cadre du projet Athena (1984). • X Window System (1984) • • • Le site officiel : www.x.org Le système X est un système de création et de gestion d’interfaces graphiques. Il permet l’affichage – exploitant les possibilités des cartes graphiques récentes (OpenGL), – sur un ou plusieurs écrans physiques, – de plusieurs applications en exécution sur des machines différentes, – dans des fenêtres séparées ou communes. Il coordonne les activités des différentes applications – qui n’ont pas besoin de se connaître, – mais qui peuvent quand même échanger des données (par exemple, le copier/coller). Nous utilisons uniquement la bibliothèque Xlib pour ce cours. Il existe une autre bibliothèque : xcb. • • 2I007 & LI357 / Université Paris VI → XFree86 (1991) → X.Org (2004). 2 Choun Tong LIEU 2014-2015 • • • • • Les programmes construits sur les principes du système X sont appelés clients X ou applications X. (par exemple, le gestionnaire de fenêtre qui gère/décore les fenêtres principales d'applications avec les bordures et barres réseau de titre). Client Programme java, tk, etc … Utilitaires : gnome, kde, ... X Window System OS (Linux, ...) Le dispositif graphique de sortie (écran), et le dispositif de saisie (souris, clavier ou autres) constituent le “display”. Le processus (programme) chargé de gérer le display est appelé un serveur. Il est possible qu’une machine adaptée prenne en charge plusieurs serveurs. Il est possible qu’un serveur adapté prenne en charge plusieurs écrans physiques (à ne pas confondre avec des écrans virtuels gérés par le gestionnaire de fenêtres). Chaque application X – demande une connexion à un ou plusieurs serveurs (par exemple, le jeu d’échecs) via un réseau; – et dispose sur son ou ses serveurs de zéro, d’une ou de plusieurs fenêtres. 2I007 & LI357 / Université Paris VI 3 Choun Tong LIEU 2014-2015 Application A Application B Réseau A B A A Serveur (PC ou Terminal X) • Serveur (PC ou Terminal X) L’usage d’un gestionnaire de fenêtres (GF) est recommandé, mais n’est pas indispensable. Il permet le déplacement, l'icônification des fenêtres, etc, … Titre Titre + Une fenêtre principale et ses filles 2I007 & LI357 / Université Paris VI = Fenêtres de décoration fournies par le GF 4 Choun Tong LIEU 2014-2015 • • Arbre des fenêtres : – au sommet de l’arbre, la fenêtre racine dont l’affichage couvre (normalement) tout l’écran, – ensuite les fenêtres filles de la racine, appelées aussi fenêtres principales des applications, – ensuite les fenêtres filles de ces filles, etc, … Un gestionnaire de fenêtres (GF) – coordonne la position relative des fenêtres principales par rapport à la fenêtre racine (écran), – les décore avec une barre de titre (munie de boutons et menus déroulants), des bordures et des coins, – permet de modifier manuellement leurs tailles avec ces décorations. • • • – ne s'occupe jamais du contenu (l’intérieur) de ces fenêtres principales. Metacity(fenêtres)/GNOME(bureau), Kwin/KDE, Xfwm4/Xfce, etc, ... Le système X constitue le fondement d’un système de fenêtrage sur lequel sont construits des interfaces graphiques utilisateurs : les plus répandus des interfaces graphiques sont Gtk+ (GNOME), Qt (KDE), ... et Motif (MWM). Avec leurs gestionnaires de fenêtres (et/ou d’environnement de travail respectifs gnome, kde, ... et mwm) qui sont eux aussi des applications X. 2I007 & LI357 / Université Paris VI 5 Choun Tong LIEU 2014-2015 • X Window System n’impose ni un "look", ni un style : – un grand choix de gestionnaires de fenêtre, d’environnement de bureau, etc, … – et si aucun d'eux ne nous satisfait, nous pouvons toujours en écrire un nouveau (rappel : ce n'est qu'une application X comme une autre). • X Window System utilise par défaut le port 6000 et en clair : – ce port est en général fermé par sécurité pour les requêtes en provenance de l'extérieur de la machine, – par sécurité, on utilise en général les canaux (tunnels) cryptés (voir par exemple la commande ssh -X ... ) . • Portage de X Window System sur d'autres systèmes d'exploitation : – Cygwin/X : sous Cygwin, un émulateur du système Unix sous Windows. – Xming : portage sous Windows du système de fenêtrage X Window System, fondé sur le serveur X. – Apple X11 : sous Mac OS X. – etc, ... 2I007 & LI357 / Université Paris VI 6 Choun Tong LIEU 2014-2015 2. Quelques clients • Le système est fourni avec un certain nombre de clients; de plus, de nombreuses et nouvelles contributions offrent gratuitement des applications X performantes . • Quelques clients : – xdpyinfo affiche les informations sur le serveur – xwininfo affiche les informations sur une fenêtre – xsetroot configure le fond d’écran (dessin) – xset configure le display (pointeur, sonnerie) – xrdb gère la base de ressources du serveur – xhost autorise ou non la connexion des clients X d'autres machines – xterm émulateur de terminal alphanumérique avec exécution d’un shell – xedit un éditeur de texte simple – bitmap un éditeur de bitmap – xfig un logiciel de dessin – xcalc une calculette – xclock une horloge – xload donne la charge du système – xkill comme son nom l'indique : c'est dangereux. 2I007 & LI357 / Université Paris VI 7 Choun Tong LIEU 2014-2015 • Avec quelques options des commandes : -display <adresse physique de la machine> : <serveur> . <ecran> avec serveur et ecran comptés à partir de 0 (le premier). Si on omet l’écran (resp. machine), ce sera alors le 0 (resp. machine locale). Sans l'option, le contenu de la variable shell $DISPLAY est utilisé. Elle est initialisée par la commande ssh pour utiliser son canal crypté. Par exemple, pour afficher une horloge dans la fenêtre de la machine ippc13 à partir de n’importe quelle autre machine autorisée, on exécute : xclock –display ippc13:0.0 ou xclock –display ippc13:0 -bg <nom ou indices de couleur RGB> Par exemple, pour afficher une horloge avec un fond rouge, on exécute : xclock –bg red ou xclock –bg "#FF0000“ -geometry <largeur> x <hauteur> [ ± <en x> ± <en y> ] + : à partir du haut ou de la gauche de l’écran (occupé par la fenêtre racine), - : à partir du bas ou de la droite. Par exemple : xclock –geometry 300x300 2I007 & LI357 / Université Paris VI 8 Choun Tong LIEU 2014-2015 3. Connexion • Tout commence par une demande de connexion à un (ou des) serveur(s) : Display *XOpenDisplay (char *serveur); • Si serveur == NULL, alors la variable shell DISPLAY est utilisée. En général, DISPLAY=:0 ou DISPLAY=:0.0, c'est-à-dire la machine locale (voir l'option –display ci-dessus concernant les clients). • Si la valeur retournée est NULL, la connexion est refusée par le serveur : ● la connexion est physiquement impossible (serveur éteint ou débranché), ● ou on a exécuté sur ce serveur : ● xhost + pour autoriser toutes les machines, ● xhost pour interdire toutes les machines, ● xhost ± <machine> pour une machine donnée, • Si la valeur retournée n’est pas NULL, elle pointe alors sur une (énorme) structure contenant de précieuses informations pour le client (voir le paquet de réponse du serveur en cas de connexion réussie) : ● le nombre d’écrans physiques, 2I007 & LI357 / Université Paris VI 9 Choun Tong LIEU 2014-2015 le nombre de plans (bits) pour traiter les couleurs, ● le numéro d’identification (appelé généralement l’ID) de la fenêtre racine (qui occupe tout l’écran), ● l’ID de la table de couleurs (colormap) par défaut, ● le pixel pour la couleur blanche et celui pour la couleur noire, ● la hauteur et la largeur de l’écran en pixel et en millimètre, ● la sauvegarde ou non en mémoire de la partie cachée d’une fenêtre, etc, … On retrouve ces informations affichées par la commande xdpyinfo. Il existe des macros permettant d’obtenir une information particulière : ● int DefaultScreen (Display *dpy); ● window DefaultRootWindow (Display *dpy); ● unsigned long BlackPixel (Display *dpy, int ecran); ● unsigned long WhitePixel (Display *dpy, int ecran); ● GC DefaultGC (Display *dpy, int ecran); ● int DisplayWidth (Display *dpy, int ecran); ● int DisplayHeight (Display *dpy, int ecran); etc, … Et on ferme la connexion avec void XCloseDisplay (Display *dpy); Un exemple de connexion : ● • • • • 2I007 & LI357 / Université Paris VI 10 Choun Tong LIEU 2014-2015 Display *dpy; Window racine, principale; int ecran; GC ctx; XEvent evmt; void PourExpose (XExposeEvent *evmt) { XDrawString(dpy, evmt->window, ctx, 50, 100, "Bonjour Monde !", 15); } int main (int argc, char *argv[]) { /* la procedue main() Installer(NULL); for (;;) { /* la boucle d'evenements XNextEvent(dpy, &evmt); switch (evmt.type) { case Expose : PourExpose((XExposeEvent *)&evmt); break; default :; } } } 2I007 & LI357 / Université Paris VI 11 */ */ Choun Tong LIEU 2014-2015 void Installer (char *serveur) { dpy = XOpenDisplay(serveur); /* demande une connexion au serveur */ if (dpy == NULL) { printf("Connection impossoble avec le serveur %s.\n", serveur); exit(0); } racine ecran ctx = DefaultRootWindow(dpy); = DefaultScreen(dpy); = DefaultGC(dpy, ecran); /* on récupère la racine /* écran par défaut qui doit être 0 /* le contexte graphique par défaut principale = XCreateSimpleWindow(dpy, /* demande de création d'une fenêtre racine, /* principale avec comme mère racine 0, 0, 200, 200, /* la géométrie 0, /* épaisseur pour la bordure BlackPixel(dpy, ecran), /* pixel (couleur pour la bordure) WhitePixel(dpy, ecran));/* pixel (pour le fond) */ */ */ */ */ */ */ */ */ XSelectInput(dpy, principale, ExposureMask); /* sélectionne les évènements*/ XStoreName(dpy, principale, "Exemple 1"); XMapWindow(dpy, principale); } 2I007 & LI357 / Université Paris VI 12 Choun Tong LIEU 2014-2015 4. Fenêtre • • • • • • • Une fenêtre est en général une zone rectangulaire. Il est possible de créer des fenêtres de forme quelconque en utilisant la bibliothèque d’extension pour les fenêtres. Toute fenêtre a une mère, sauf la fenêtre racine (root window). La relation mère-fille des fenêtres est gérée comme un arbre avec au sommet la fenêtre racine. Pour connaître l’arbre à partir d’une fenêtre donnée avec le client (application) xwininfo –tree et en cliquant dans la fenêtre ou en donnant son ID. Attention : lorsque l’on est pris en charge par un gestionnaire d’environnement (GNOME, KDE ou autres), ce dernier couvre la fenêtre racine avec une fenêtre intermédiaire. Ce qui leur permet de simuler plusieurs écrans virtuels. L’affichage des fenêtres suit un parcours préfixe en profondeur de l’arbre en commençant par la racine (récursivement, le sommet, puis ses branches filles de la gauche vers la droite) en utilisant le modèle du peintre. Une fenêtre est affichée à l’intérieur de sa mère. Les parties qui dépassent sont rognées (par clipping). La création d’une fenêtre par XCreateSimpleWindow() ou XCreateWindow() avec les attributs la place à droite de ses soeurs. 2I007 & LI357 / Université Paris VI 13 Choun Tong LIEU 2014-2015 Window XCreateSimpleWindow (Display *display, Window parent, int x, int y, unsigned int width, unsigned int height, unsigned int border_width, unsigned int border, unsigned int background); Window XCreateWindow (Display *display, Window parent, int x, int y, unsigned int width, unsigned int height, unsigned int border_width, int depth, /* CopyFromParent en général unsigned int class, /* InputOnly, InputOutput ou CopyFromParent Visual *visual, /* CopyFromParent en général unsigned long valuemask, XSetWindowAttributes *attributes); */ */ */ InputOnly : sans attribut d'affichage (invisible donc), mais sensible à la souris et au clavier. 2I007 & LI357 / Université Paris VI 14 Choun Tong LIEU 2014-2015 typedef struct { Pixmap background_pixmap; /* background or None or ParentRelative */ unsigned long background_pixel; /* background pixel */ Pixmap border_pixmap; /* border of the window */ unsigned long border_pixel; /* border pixel value */ int bit_gravity; /* one of bit gravity values */ int win_gravity; /* one of the window gravity values */ int backing_store; /* NotUseful, WhenMapped, Always */ unsigned long backing_planes; /* planes to be preseved if possible */ unsigned long backing_pixel; /* value to use in restoring planes */ Bool save_under; /* should bits under be saved? (popups) */ long event_mask; /* set of events that should be saved */ long do_not_propagate_mask; /* set of events that should not propagate */ Bool override_redirect; /* boolean value for override-redirect */ Colormap colormap; /* color map to be associated with window */ Cursor cursor; /* cursor to be displayed (or None) */ } XSetWindowAttributes; #define #define #define #define #define #define #define CWBackPixmap CWBackPixel CWBorderPixmap CWBorderPixel CWBitGravity CWWinGravity CWBackingStore 2I007 & LI357 / Université Paris VI (1L<<0) (1L<<1) (1L<<2) (1L<<3) (1L<<4) (1L<<5) (1L<<6) #define #define #define #define #define #define #define #define 15 CWBackingPlanes CWBackingPixel CWOverrideRedirect CWSaveUnder CWEventMask CWDontPropagate CWColormap CWCursor (1L<<7) (1L<<8) (1L<<9) (1L<<10) (1L<<11) (1L<<12) (1L<<13) (1L<<14) Choun Tong LIEU 2014-2015 ● On remplit les champs de attributes et on positionne les masques avec l'opérateur « | » (ou logique) pour indiquer les champs concernés. Par exemple : valuemask = CWBorderPixel | CWEventMask | CWDontPropagate ● Pour changer les attributs : void XChangeWindowAttributes (Display *display, Window w, unsigned long valuemask, XSetWindowAttributes *attributes) 2I007 & LI357 / Université Paris VI 16 Choun Tong LIEU 2014-2015 • Pour changer de place entre sœurs dans l'arbre de fenêtres, de position et de taille : void XConfigureWindow (Display *display, Window w, unsigned int value_mask, XWindowChanges *values) typedef struct { int x, y; int width, height; int border_width; Window sibling; int stack_mode; } XWindowChanges; /* Window stacking method (in configureWindow) */ #define #define #define #define #define Above Below TopIf BottomIf Opposite 2I007 & LI357 / Université Paris VI 0 1 2 3 4 /* ConfigureWindow structure */ #define #define #define #define #define #define #define 17 CWX CWY CWWidth CWHeight CWBorderWidth CWSibling CWStackMode (1<<0) (1<<1) (1<<2) (1<<3) (1<<4) (1<<5) (1<<6) Choun Tong LIEU 2014-2015 void XReparentWindow (Display *display, Window w, Window parent, int x, int y); • Pour changer de mère : • On peut consulter les attributs (ceux qui peuvent être consultés) d’une fenêtre par : Status XGetWindowAttributes (Display *display, Window w, XWindowAttributes *window_attributes_return); 2I007 & LI357 / Université Paris VI 18 Choun Tong LIEU 2014-2015 typedef struct { int x, y; /* location of window */ int width, height; /* width and height of window */ int border_width; /* border width of window */ int depth; /* depth of window */ Visual *visual; /* the associated visual structure */ Window root; /* root of screen containing window */ #if defined(__cplusplus) || defined(c_plusplus) int c_class; /* C++ InputOutput, InputOnly*/ #else int class; /* InputOutput, InputOnly*/ #endif int bit_gravity; /* one of bit gravity values */ int win_gravity; /* one of the window gravity values */ int backing_store; /* NotUseful, WhenMapped, Always */ unsigned long backing_planes;/* planes to be preserved if possible */ unsigned long backing_pixel; /* value to be used when restoring planes */ Bool save_under; /* boolean, should bits under be saved? */ Colormap colormap; /* color map to be associated with window */ Bool map_installed; /* boolean, is color map currently installed*/ int map_state; /* IsUnmapped, IsUnviewable, IsViewable */ long all_event_masks; /* set of events all people have interest in*/ long your_event_mask; /* my event mask */ long do_not_propagate_mask; /* set of events that should not propagate */ Bool override_redirect; /* boolean value for override-redirect */ Screen *screen; /* back pointer to correct screen */ } XWindowAttributes; 2I007 & LI357 / Université Paris VI 19 Choun Tong LIEU • La géométrie d’une fenêtre : position par rapport à l’origine de la mère origine hauteur 2014-2015 largeur épaisseur de bordure • On affiche une fenêtre ou on retire une fenêtre de l’affichage par void XMapWindow (Display *display, Window w); void XMapRaised (Display *display, Window w); /* en passant devant */ void XMapSubwindows (Display *display, Window w); /* les filles de w */ void XUnmapWindow (Display *display, Window w); void XUnmapSubwindows (Display *display, Window w); 2I007 & LI357 / Université Paris VI 20 Choun Tong LIEU 2014-2015 • Quelques utilitaires tirés des procédures générales ci-dessus et autres : /* pour placer w devant toutes ses sœurs. */ void XRaiseWindow (Display *display, Window w); /* pour placer w derrière toutes ses sœurs. */ void XLowerWindow (Display *display, Window w); /* pour réorganiser circulairement l'ordre des filles avec direction = RaiseLowest ou LowerHighest. */ void XCirculateSubwindows (Display *display, Window w, int direction); void XCirculateSubwindowsUp (Display *display, Window w); void XCirculateSubwindowsDown (display, Display *w); /* pour réorganiser complètement l'ordre des filles */ void XRestackWindows (Display *display, Window *windows, int nwindows); /* pour connaître l'arbre des fenêtres */ Status XQueryTree (Display *display, Window w, Window *root_return, Window *parent_return, Window **children_return, unsigned int *nchildren_return); void XSetWindowBackground (Display *display, Window w, unsigned long background_pixel); 2I007 & LI357 / Université Paris VI 21 Choun Tong LIEU 2014-2015 /* pour choisir un pixmap (image) comme fond de la fenêtre */ void XSetWindowBackgroundPixmap (Display *display, Window w, Pixmap background_pixmap); /* pour effacer le contenu d'une fenêtre * void XClearWindow (Display *display, Windows w); /* ou pour affacer partiellement le contenu d'une fenêtre * void XClearArea (Display *display, Windows w, int x, int y, unsigned int width, unsigned int height, Bool exposures); /* en générant ou pas l'événement Expose */ void XSetWindowBorder (Display *display, Window w, unsigned long border_pixel); /* pour choisir un pixmap (image) comme bordure de la fenêtre */ void XSetWindowBorderPixmap (Display *display, Window w, Pixmap border_pixmap); /* pour changer la palette de couleur pour la fenêtre w */ void XSetWindowColormap (Display *display, Window w, Colormap colormap); void XMoveWindow (Display *display, Window w, int x, int y); 2I007 & LI357 / Université Paris VI 22 Choun Tong LIEU 2014-2015 void XResizeWindow (Display *display, Window w, unsigned int width, unsigned int height); void XMoveResizeWindow (Display *display, Window w, int x, int y, unsigned int width, unsigned int height); void XSetWindowBorderWidth (Display *display, Window w, unsigned int width) Status XGetGeometry (Display *display, Drawable d, Window *root_return, int *x_return, int *y_return, unsigned int *width_return, unsigned int *height_return, unsigned int *border_width_return, unsigned int *depth_return); 2I007 & LI357 / Université Paris VI 23 Choun Tong LIEU 2014-2015 5. Événements • • • Une fenêtre est source d'évènements. Les évènements sont envoyés par le serveur aux clients concernés. Pour cela, le client sélectionne sur chacune des fenêtres concernées les évènements en utilisant leurs masques : void XSelectInput (Display *display, Windows w, long event_mask); • Où event_mask est un masque ou la combinaison (union logique "|") de plusieurs masques d'évènements : ButtonPressMask, ButtonPressMask|Button1MotionMask|ButtonReleaseMask, SubStructureNotifyMask|KeyPressMask, … • Certains masques concernent plusieurs évènements (StructureNotifyMask), d'autres des évènements plus précis (Button1MotionMask pour le mouvement de la souris avec le bouton 1 enfoncé). Il y a des évènements sans masque (SelectionClear) et un masque sans évènements (OwnerGrabButtonMask). 2I007 & LI357 / Université Paris VI 24 Choun Tong LIEU 2014-2015 • Un client peut envoyer un évènement quelconque aux autres clients à travers une fenêtre où ces derniers ont sélectionné cet évènement : Status XSendEvent (Display *display, Windows w, Bool propagate, long event_mask, XEvent *event); • • Le serveur jouera alors simplement le rôle de facteur en mettant le champ send_event de la structure de l'évènement à True. C'est un champ commun à toutes les structures d'évènement. On remarque qu'il n'y a aucune indication concernant le ou les clients receveurs et pour cause : tous clients s'ignorent. 25 évènements sélectionnables par un client sur une fenêtre donnée en utilisant les masques correspondants, + 7 évènements sans masques que le serveur envoie d'office aux clients concernés, + l'évènement XErrorEvent sans masque pour le traitement d'erreur. Tout client peut sélectionner les masques dans une fenêtre créée par un autre client qu'il ignore. Il a juste besoin de l'ID de la fenêtre (voir paramètre w) : void XSelectInput (..., Windows w, ...); 2I007 & LI357 / Université Paris VI 25 Choun Tong LIEU serveur • • File d'évènements void XNextEvent (Display * dpy XEvent *evmt); File de requêtes En général, le client récupère un évènement dans sa file d'évènement par File d'évènements • File de requêtes 2014-2015 client Si la file d'évènements du client est vide, cette instruction permettra de vider la file de requêtes du client vers celle du serveur et elle se mettra en attente d'un évènement (elle est dite bloquante). La file de requêtes du client pourrait être vidée (sous certaines conditions) par void XFlush (Display * display); • Un client ne peut jamais aller consulter les files du serveurs, ni les files d'un autre client. 2I007 & LI357 / Université Paris VI 26 Choun Tong LIEU 2014-2015 • Le fonctionnement (interaction entre clients et serveur) est asynchrone : – Le serveur traite dans l'ordre les requêtes reçues, • – Un client n'attend pas nécessairement une réponse du serveur. Autour de la file d'évènements du côté client : – int XNextEvent(Display *display, XEvent *event_return); si la file n'est pas vide, elle enlève un évènement en tête de la file ; sinon elle vide la file de requêtes vers le serveur et attend un évènement. Elle est donc bloquante. – int XPeekEvent(Display *display, XEvent *event_return); même chose que XNextEvent(), mais en copiant l'évènement sans le retirer de la file. Elle est donc bloquante. – Bool XCheckWindowEvent(Display *display, Window w, long event_mask, XEvent *event_return); – Bool XCheckMaskEvent(Display *display, long event_mask, XEvent *event_return); même chose que XNextEvent() sans être bloquant. – etc, ... 2I007 & LI357 / Université Paris VI 27 Choun Tong LIEU 2014-2015 • Structure de tout événement : XEvent typedef union _XEvent { int XAnyEvent XKeyEvent XButtonEvent XMotionEvent XCrossingEvent XFocusChangeEvent XExposeEvent XGraphicsExposeEvent XNoExposeEvent XVisibilityEvent XCreateWindowEvent XDestroyWindowEvent XUnmapEvent XMapEvent XMapRequestEvent XReparentEvent XConfigureEvent XGravityEvent XResizeRequestEvent 2I007 & LI357 / Université Paris VI 28 type; xany; xkey; xbutton; xmotion; xcrossing; xfocus; xexpose; xgraphicsexpose; xnoexpose; xvisibility; xcreatewindow; xdestroywindow; xunmap; xmap; xmaprequest; xreparent; xconfigure; xgravity; xresizerequest; Choun Tong LIEU 2014-2015 XConfigureRequestEvent XCirculateEvent XCirculateRequestEvent XPropertyEvent XSelectionClearEvent XSelectionRequestEvent XSelectionEvent XColormapEvent XClientMessageEvent XMappingEvent XErrorEvent XKeymapEvent XGenericEvent XGenericEventCookie long } XEvent; • xconfigurerequest; xcirculate; xcirculaterequest; xproperty; xselectionclear; xselectionrequest; xselection; xcolormap; xclient; xmapping; xerror; xkeymap; xgeneric; xcookie; pad[24]; Et les masques correspondants : 2I007 & LI357 / Université Paris VI 29 Choun Tong LIEU 2014-2015 /* Input Event Masks. Used as event-mask window attribute ... */ #define NoEventMask 0L #define KeyPressMask (1L<<0) #define KeyReleaseMask (1L<<1) #define ButtonPressMask (1L<<2) #define ButtonReleaseMask (1L<<3) #define EnterWindowMask (1L<<4) #define LeaveWindowMask (1L<<5) #define PointerMotionMask (1L<<6) #define PointerMotionHintMask (1L<<7) #define Button1MotionMask (1L<<8) #define Button2MotionMask (1L<<9) #define Button3MotionMask (1L<<10) #define Button4MotionMask (1L<<11) #define Button5MotionMask (1L<<12) #define ButtonMotionMask (1L<<13) #define KeymapStateMask (1L<<14) #define ExposureMask (1L<<15) #define VisibilityChangeMask (1L<<16) #define StructureNotifyMask (1L<<17) #define ResizeRedirectMask (1L<<18) #define SubstructureNotifyMask (1L<<19) #define SubstructureRedirectMask (1L<<20) #define FocusChangeMask (1L<<21) #define PropertyChangeMask (1L<<22) #define ColormapChangeMask (1L<<23) #define OwnerGrabButtonMask (1L<<24) 2I007 & LI357 / Université Paris VI 30 Choun Tong LIEU 2014-2015 • Structure minimal pour un événement : typedef struct { int type; unsigned long serial; Bool send_event; Display *display; Window window; } XAnyEvent; 2I007 & LI357 / Université Paris VI 31 Choun Tong LIEU 2014-2015 typedef struct { int type; /* of event */ unsigned long serial; /* # of last request processed by server */ Bool send_event; /* true if this came from a SendEvent request */ Display *display; /* Display the event was read from */ Window window; /* "event" window it is reported relative to */ Window root; /* root window that the event occurred on */ Window subwindow; /* child window */ Time time; /* milliseconds */ int x, y; /* pointer x, y coordinates in event window */ int x_root, y_root; /* coordinates relative to root */ unsigned int state; /* key or button mask */ unsigned int keycode; /* detail */ Bool same_screen; /* same screen flag */ } XKeyEvent; typedef XKeyEvent XKeyPressedEvent; typedef XKeyEvent XKeyReleasedEvent; 2I007 & LI357 / Université Paris VI 32 Choun Tong LIEU 2014-2015 typedef struct { int type; /* of event */ unsigned long serial; /* # of last request processed by server */ Bool send_event; /* true if this came from a SendEvent request */ Display *display; /* Display the event was read from */ Window window; /* "event" window it is reported relative to */ Window root; /* root window that the event occurred on */ Window subwindow; /* child window */ Time time; /* milliseconds */ int x, y; /* pointer x, y coordinates in event window */ int x_root, y_root; /* coordinates relative to root */ unsigned int state; /* key or button mask */ unsigned int button; /* detail */ Bool same_screen; /* same screen flag */ } XButtonEvent; typedef XButtonEvent XButtonPressedEvent; typedef XButtonEvent XButtonReleasedEvent; 2I007 & LI357 / Université Paris VI 33 Choun Tong LIEU 2014-2015 typedef struct { int type; /* of event */ unsigned long serial; /* # of last request processed by server */ Bool send_event; /* true if this came from a SendEvent request */ Display *display; /* Display the event was read from */ Window window; /* "event" window reported relative to */ Window root; /* root window that the event occurred on */ Window subwindow; /* child window */ Time time; /* milliseconds */ int x, y; /* pointer x, y coordinates in event window */ int x_root, y_root; /* coordinates relative to root */ unsigned int state; /* key or button mask */ char is_hint; /* detail */ Bool same_screen; /* same screen flag */ } XMotionEvent; typedef XMotionEvent XPointerMovedEvent; typedef struct { int type; unsigned long serial; Bool send_event; Display *display; Window window; int x, y; int width, height; int count; } XExposeEvent; 2I007 & LI357 / Université Paris VI 34 /* # of last request processed by server */ /* true if this came from a SendEvent request */ /* Display the event was read from */ /* if non-zero, at least this many more */ Choun Tong LIEU 2014-2015 • On sélectionne un ensemble d'événements sur une fenêtre avec void XSelectInput (Display *display, Window w, long event_mask); en utilisant l'opérateur "|" (ou logique) avec ces évènements : XSelectInput(dpy, fen, ButtonPressMask|ButtonReleaseMask|ExposureMask); • Attention : XSelectInput() supprimera l'ancienne sélection (masques) et mettra en place la nouvelle pour le client qui l'exécute. • Sur une fenêtre donnée, un seul client peut sélectionner les évènements ButtonPressMask et SubstructureRedirectMask. Une fois sélectionné, le serveur refuse ces sélections aux autres clients en leur envoyant un évènement de type XErrorEvent. 2I007 & LI357 / Université Paris VI 35 Choun Tong LIEU 2014-2015 • Les évènements pourraient être regroupés par thème : – Évènements concernant la souris : ButtonPress ButtonRelease MotionNotify EnterNotify LeaveNotify bouton enfoncé bouton relâché mouvement de la souris, avec bouton enfoncé ou non entrée de la souris dans une fenêtre sortie de la souris d'une fenêtre – Évènements concernant les touches et clavier : touche enfoncée touche relâchée fenêtre recevant le foyer clavier fenêtre perdant le foyer clavier généré quand la souris ou le foyer clavier entre dans une fenêtre, juste après EnterNotify ou FocusIn. MappingNotify généré quand il y a un changement par exemple entre les touches physiques et les touches symboliques (par exemple, un client a changé le clavier azerty en qwerty). KeyPress KeyRelease FocusIn FocusOut KeymapNotify 2I007 & LI357 / Université Paris VI 36 Choun Tong LIEU 2014-2015 – Évènements concernant les expositions : Expose GraphicsExpose NoExpose une partie de la fenêtre est découverte en copiant le contenu d'une fenêtre avec un contexte graphique ayant l'attribut graphics_exposures mis à True et ce contenu est couvert par quelques choses. Idem, mais avec un contenu entier. – Évènements concernant les changements d'état d'une fenêtre : ConfigureNotify CirculateNotify CreateNotify DestroyNotify GravityNotify MapNotify UnmapNotify ReparentNotify VisibilityNotify ColormapNotify 2I007 & LI357 / Université Paris VI changement de géométrie ou de place entre sœur généré par XCirculateSubwindows, Up et Down création d'une fenêtre destruction d'une fenêtre quand la fenêtre bouge lorsque la mère change de taille affichage de la fenêtre le contraire changement de mère changement de visibilité d'une fenêtre changement de palette, son installation et désinstallation 37 Choun Tong LIEU 2014-2015 – Évènements intéressés par le GF : CirculateRequest ConfigureRequest CreateNotify MapRequest ResizeRequest demande de changement de place entre les filles demande par un autre client de changement de géométrie ou de place entre les sœurs création d'une fenêtre demande d'affichage demande de changement de taille – Évènements concernant les communications : (voir communication) ClientMessage PropertyNotify SelectionClear SelectionNotify SelectionRequest message d'un client changement d'une propriété d'une fenêtre changement de propriétaire de sélection envoyé par un client qui offre les données générer par une demande de données – Parmi ces évènements, les sans masques : MappingNotify ClientMessage 2I007 & LI357 / Université Paris VI GraphicsExpose NoExpose 38 SelectionClear SelectionNotify SelectionRequest Choun Tong LIEU 2014-2015 • L'évènement XErrorEvent : (sans masque) typedef struct { int type; Display *display; XID resourceid; unsigned long serial; unsigned char error_code; unsigned char request_code; unsigned char minor_code; } XErrorEvent; /* Display the event was read from */ /* resource id */ /* serial number of failed request */ /* error code of failed request */ /* Major op-code of failed request */ /* Minor op-code of failed request */ – Un évènement sans masque. – Par exemple, un accès à un mauvais identificateur de fenêtre ou autre. – En général, lorsque le client reçoit cet évènement (voir XNextEvent()), c'est déjà trop tard : le programme se termine par une erreur (non "fatales"). – Pour intercepter ces erreurs non "fatales", la couche Xlib propose la procédure XSetErrorHandler() . Par exemple, dans le programme, on peut écrire : 2I007 & LI357 / Université Paris VI 39 Choun Tong LIEU 2014-2015 int TraitementErreurNonFatale (Display *dpy, XErrorEvent *e) { printf("\n\nUn GF est déjà en fonction.\n\n"); exit(0); } ... XSetErrorHandler(TraitementErreurNonFatale); • • Un seul client peut sélectionner sur une fenêtre donnée le masque ButtonPressMask et/ou SubstructureRedirectMask et/ou ResizeRedirectMask. Tout autre client qui tente de le ou les sélectionner recevra un évènement d'erreur qui termine son exécution. En général, c'est le GF qui sélectionne sur la fenêtre racine ces évènements : le premier pour dérouler un menu dans la fenêtre racine et le second pour recevoir un MapRequest du serveur concernant une fenêtre qui a comme mère la fenêtre racine et qui veut s'afficher. C'est comme cela que le GF est informé par le serveur que la fenêtre (dont la mère est la fenêtre racine) veut s'afficher. 2I007 & LI357 / Université Paris VI 40 Choun Tong LIEU 2014-2015 • • • • En général, on sélectionne des masques dans une fenêtre donnée pour recevoir les évènements dont elle est la source. Pour les masques Subxxx (par exemple SubstructureNotify), on sélectionne sur une fenêtre pour recevoir les évènements dont les filles sont les sources. Le masque PointerMotionHintMask permet de demander au serveur de ne pas envoyer tous les évènements concernant les mouvements de la souris, mais les évènements intermédiaires, donc moins d'évènements. Le masque OwnerGrabButtonMask permet lors d'une capture (voir cidessous) aux autres fenêtres (où le client a sélectionné les évènements concernant les touches, souris et boutons) d'être aussi sources des ces évènements. Sinon, seule la fenêtre qui fait la capture est la source. 2I007 & LI357 / Université Paris VI 41 Choun Tong LIEU 2014-2015 • Quelques détails sur KeyPress : typedef struct { ... Window window; ... Window subwindow; /* évènement propagé de la fille immédiate ou 0 */ ... unsigned int state; /* les altérateurs : Shift, Control, CapsLock, Meta */ unsigned int keycode; /* numéro de la touche de clavier */ Bool same_screen; #define ShiftMask (1«0) } XKeyEvent; #define LockMask (1«1) typedef XKeyEvent XKeyPressedEvent; #define ControlMask (1«2) typedef XKeyEvent XKeyReleasedEvent; #define Mod1Mask (1«3) #define Mod2Mask (1«4) #define Mod3Mask (1«5) Avec state est égale à #define Mod4Mask (1«6) #define Mod5Mask (1«7) #define Button1Mask (1«8) #define Button2Mask (1«9) #define Button3Mask (1«10) #define Button4Mask (1«11) #define Button5Mask (1«12) 2I007 & LI357 / Université Paris VI 42 Choun Tong LIEU 2014-2015 • Avec state égale à Quelques détails sur ButtonPress : Avec button égal à #define Button1 #define Button2 #define Button3 #define Button4 #define Button5 #define #define #define #define #define #define #define #define #define #define #define #define #define 1 2 3 4 5 ShiftMask LockMask ControlMask Mod1Mask Mod2Mask Mod3Mask Mod4Mask Mod5Mask Button1Mask Button2Mask Button3Mask Button4Mask Button5Mask (1«0) (1«1) (1«2) (1«3) (1«4) (1«5) (1«6) (1«7) (1«8) (1«9) (1«10) (1«11) (1«12) typedef struct { ... Window window; ... Window subwindow; /* évènement propagé de la fille immédiate ou 0 */ ... unsigned int state; /* les altérateurs : Shift, Control, CapsLock, Meta */ unsigned int button; /* numéro du bouton */ Bool same_screen; } XButtonEvent; typedef XButtonEvent XButtonPressedEvent; typedef XButtonEvent XButtonReleasedEvent; 2I007 & LI357 / Université Paris VI 43 Choun Tong LIEU 2014-2015 • • • • Lorsqu'un client reçoit un ButtonPress (du serveur) sur une fenêtre fen donnée, il y a capture ("grab") jusqu'au relâchement de tous les boutons : tous les évènements concernant les souris, boutons et clavier sont envoyés par le serveur à ce client unique et ces évènements ont comme seule fenêtre source fen. Si le masque OwnerGrabButtonMask a été sélectionné par le client sur la fenêtre fen, alors tous les autres fenêtres que le client a sélectionné ces évènements (souris et boutons) peuvent aussi être sources de ces évènements. Mais dans tous les cas, ce client est le seul destinataire de ces évènements. On peut aussi provoquer une capture passive des touches par void XGrabKey (Display * display, int keycode, unsigned int modifiers, Window grab_window, Bool owner_events, int pointer_mode, int keyboard_mode); keycode = numéro de la touche, par exemple XKeysymToKeycode(dpy, XK_a) grab_window = la fenêtre accaparente modifiers = altérateurs (voir les valeurs de state ci-dessus) owner_events= True (voir OwnerGrabButtonMask ci-dessus), false pour que la fenêtre grab_window seule est la source de ces évènements. pointer_mode, keyboard_mode = GrabModeSync (le serveur gèle tous ces évènements et ne l'envoie que si le client exécute XAllowEvent()et les gèle à nouveau) ou GrabModeAsync (le serveur envoie normalement ces évènements). 2I007 & LI357 / Université Paris VI 44 Choun Tong LIEU 2014-2015 • Qui devient active en appuyant sur des touches indiquées. Et pour quitter la capture : void XUngrabKey (Display * display, int keycode, unsigned int modifiers, Window grab_window); • Une autre capture passive, mais avec les boutons : void XGrabButton (Display * display, unsigned int button, unsigned int modifiers, Window grab_window, Bool owner_events, unsigned int event_mask, int pointer_mode, int keyboard_mode, Window confine_to, Cursor cursor); void XUngrabButton (Display * display, unsigned int button, unsigned int modifiers, Window grab_window); button event_mask confine_to cursor = Button1, Button2, … = les évènements concernant les touches, souris et boutons, = en gardant la souris dans cette fenêtre, sinon None, = si on veut un autre curseur pendant la capture. 2I007 & LI357 / Université Paris VI 45 Choun Tong LIEU 2014-2015 • Une capture active du clavier : int XGrabKeyboard (Display * display, Window grab_window, Bool owner_events, int pointer_mode, int keyboard_mode, Time time); void XUngrabKeyboard (Display * display, Time time); time = CurrentTime ou temps machine du serveur. • Une autre capture active des touches, souris et boutons : int XGrabPointer (Display * display, Window grab_window, Bool owner_events, unsigned int event_mask, int pointer_mode, int keyboard_mode, Window confine_to, Cursor cursor, Time time); void XUngrabPointer (Display * display, Time time); event_mask = les évènements touches, souris et boutons à traiter. 2I007 & LI357 / Université Paris VI 46 Choun Tong LIEU 2014-2015 6. Gestionnaire de fenêtres (GF) • • C'est un client comme un autre. Normalement, il a sélectionné sur la fenêtre racine les événements exclusifs : ● SubstructureRedirectMask pour être informé par le serveur de la création, de l'affichage et du changement de taille/position des fenêtres déclarées filles de la racine.. Le client veut afficher fen ayant comme mère la racine (1)requête MapWindow serveur (4)Evénement MapNotify GF prépare une fenêtre (2)Evénement principale et des MapRequest décorations, enlève fen de la racine et déclare une de (3)requête ses fenêtres comme MapWindow nouvelle mère de fen ButtonPressMask pour les menus déroulants et autres. Résultat : un autre gestionnaire de fenêtre ne peut pas s'installer. Pour chaque fenêtre, le premier travail du GF est de créer des fenêtres de décoration et change la parenté de la fenêtre (ayant au départ la racine comme mère) en lui fournissant comme nouvelle mère une des fenêtres de ses décorations. ● • • 2I007 & LI357 / Université Paris VI 47 Choun Tong LIEU 2014-2015 • Comme ce sont des clients (GF et les autres) qui s'ignorent, ils communiquent entre eux avec les propriétés. Une propriété est une donnée que l'on peut ajouter à une fenêtre quelconque. Elle est gérée par le serveur. Pour distinguer les propriétés entre elles, chacune d'elles a un nom (appelé atome, un entier unique représentant une chaîne de caractères). Pour lire la valeur de ces données stockées, chacune d'elle a aussi un type (aussi un atome). Par exemple : • • • ● XStoreName(dpy, fen, "blablabla"); ● Nom de la propriété concernée XA_WM_NAME ● Type de la propriété concernée XA_STRING ● Valeur "blablabla". ● XSetWMNormalHints(dpy, fen, &geom); avec XSizeHints geom; ● #include <X11/Xutil.h> ● Nom de la propriété concernée XA_WM_NORMAL_HINTS ● Type de la propriété concernée XA_WM_SIZE_HINTS ● Valeur = le contenu de la structure geom. 2I007 & LI357 / Université Paris VI 48 Choun Tong LIEU 2014-2015 Références • • • • • • • • Les sites officiels : www.x.org. VOL 0: X PROTOCOL REFERENCE MANUAL - Adrian Nye / O'REILLY VOL 1: XLIB PROGRAMMING MANUAL - Adrian Nye / O'REILLY VOL 2: XLIB REFERENCE MANUAL FOR VERSION 11 - Adrian Nye / O'REILLY The Joy of X – Niall Mansfield / ADDISON-WESLEY Notes de cours sur X Window System du Professeur Jean Berstel : http://www-igm.univ-mlv.fr/~berstel/Cours/liste.html X WINDOW SYSTEM: CORE LIBRARIES AND STANDARDS - Robert W. SCHEIFLER / DIGITAL PRESS Les aides sur les macros, structures, fonctions et procédures de la bibliothèque Xlib avec la commande man. 2I007 & LI357 / Université Paris VI 49 Choun Tong LIEU