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