6
La librairie Xlib propose des fonctions de dessin élémentaires. Quoique assez variées (contours de figures géométriques, remplissages de figures et dessin de textes), ces fonctions prennent toutes en argument un contexte graphique (type GC) qui permet de spécifier les paramètres de la requête. Un contexte graphique est en quelque sorte un paramètre multiple qui contient tous les paramètres utilisés par les requêtes de dessin.
Cependant, les paramètres du contexte graphique utilisés par une requête varient d'une requête à l'autre. Ainsi, un contexte graphique comporte un attribut indiquant une police de caractères (font). Il contient aussi un attribut permettant de définir un style de tracé de ligne (line_style). Les fonctions qui dessinent des contours de polygones utilisent l'attribut line_style alors que celles qui impriment des textes utilisent l'attribut font.
Une requête graphique utilise fréquemment plusieurs paramètres du contexte graphique. Un principe général sous-tend cependant l’usage du contexte graphique pour toutes les primitives de dessin.
Une requête graphique engendre un bitmap représentant les points du dessin (par exemple, un segment de droite avec XDrawLine). Appelons ce dessin initial la source. La requête graphique indique en outre toujours où doit être reporté ce dessin — la destination — et dans quel contexte graphique il doit être effectué. La requête graphique vient alors modifier la destination (en général une fenêtre) en fonction du dessin source et des valeurs des différents attributs du contexte graphique.
Pour toutes les requêtes, la destination est un argument de type Drawable, c’est-à-dire quelque chose sur lequel on peut dessiner :
• soit une fenêtre de type InputOutput
• soit un Pixmap
Les paramètres du contexte graphique sont d'abord consultés pour déterminer la couleur des points du dessin source, puis d’autres paramètres sont consultés pour déterminer les points effectivement modifiés sur la destination. Il existe par exemple des paramètres permettant de limiter le cadre du dessin et une fonction de transfert du dessin permettant de tenir compte également des points de la destination. Le Pixmap qui résulte de ce calcul est finalement placé sur les points de la destination.
Un contexte graphique est en général global au programme et n'est associé à aucune fenêtre particulière. Pour faire des dessins différents, on peut disposer de plusieurs contextes graphiques ou bien faire varier un même contexte graphique. Dans le premier cas, on minimise les échanges avec le serveur, dans le deuxième, on économise la mémoire.
La documentation sur la librairie recommande de limiter si possible le nombre de contextes graphiques. En effet, passé un certain nombre, les contextes graphiques ne sont plus gardés en mémoire et les performances du programme s’en trouvent particulièrement diminuées.
La structure de données permettant de définir les paramètres d'un contexte graphique (type GC) est de type XGCValues (cf. figure 6.1.).
typedef struct {
int
function; /* opération
logique entre la source et la
destination */
unsigned
long plane_mask;
/* masque des plans
*/
unsigned
long foreground;
/*
couleur du dessin */
unsigned long background;
/*
couleur du fond */ int
line_width;
/*
largeur de ligne */
intline_style;
/*
LineSolid, LineOnOffDash, LineDoubleDash */ int cap_style;
/* CapNotLast, CapButt, CapRound, CapProjecting */
int
join_style;
/* JoinMiter, JoinRound, JoinBevel
*/
int
fill_style;
/* FillSolid, FillTiled */
/*
FillStippled, FillOpaqueStippled
*/
int
fill_rule;
/*
EvenOddRule, WindingRule */
int arc_mode; /* ArcChord, ArcPieSlice */
Pixmap
tile;
/* tuile pour les opérations de
pavage */
Pixmap
stipple;
/*
pochoir (pixmap plan)
*/ int ts_x_origin;
/* origine du premier pavé dans le
dessin */
int ts_y_origin;
Font
font;
/*
fonte utilisée pour les textes */
int
subwindow_mode;
/*
ClipByChildren, IncludeInferiors */
Bool
graphics_exposures; /*
pour sélectionner des evenements
*/
int
clip_x_origin;
/*
origine de la zone limitant le dessin */
int clip_y_origin;
Pixmap
clip_mask;
/*
masque de la zone atteinte */
int
dash_offset;
/* attributs du mode
pointillé */
char dashes;
} XGCValues;
fig. 6.1. La structure de données XGCValues
qui
permet de spécifier un contexte
graphique
Les trois premiers champs de cette structure spécifient des attributs qui sont utilisés par toutes les requêtes graphiques.
function La fonction de transfert du dessin indique une opération à effectuer bit à bit entre les pixels du Pixmap source et les pixels de la destination. On peut modifier le champ function d’un contexte graphique grâce à la requête XSetFunction (dpy, gc, function).
Il existe 16 fonctions de transfert correspondant aux 16 connecteurs binaires de la logique. Pour chacune d'entre elles, un symbole a été défini en C (cf. fig. 6.2. ). La fonction de transfert est effectuée bit à bit entre chaque pixel du dessin source et de la destination. Ainsi la fonction de transfert GXCopy permet d'ignorer la couleur des points de la destination. Avec GXCopy le dessin est directement reporté sur la destination (en général dans la couleur de foreground), sans tenir compte de son contenu antérieur. Inversement, GXnoop et GXinvert ne tiennent compte que des couleurs de la destination. L'effet de la fonction GXxor est illustré figure 6.3.
GXclear
/*
0 */
GXand
/*
src ET dst */
GXandReverse
/*
src ET NON dst */
GXcopy
/*
src */
GXandInverted
/*
NON src ET dst */
GXnoop /* dst */
GXxor
/*
src OU (exclusif) dst */
GXor
/*
src OU dst */
GXnor
/*
NON src ET NON dst */
GXequiv
/*
NON src OU (exclusif) dst */
GXinvert
/*
NON dst */
GXorReverse
/*
src OU NON dst */
GXcopyInverted
/*
NON src */
GXorInverted
/*
NON src OU dst */
GXnand
/*
NON src OU NON dst */
GXset /* 1 */
fig.
6.2. Les fonctions de transfert
fig.
6.3. Effet de la fonction GXxor
sur un pixel de la
destination
On utilisera GXxor[1] ou GXInvert pour faire des affichages en inverse vidéo. GXxor permet de faire de l'inverse vidéo en couleur. Avec GXinvert, l'attribut foreground du contexte graphique est ignoré et seule la couleur de la destination est prise en compte (et inversée).
plane_mask Le masque des plans indique les plans de couleurs effectivement atteints sur la destination. On pourra modifier cet attribut par la requête XSetPlaneMask (dpy, gc, plane_mask). Pour affecter tous les plans, on utilise la macro AllPlanes.
On ne reporte le dessin source sur la destination que pour les plans indiqués par le masque des plans. Les autres plans restent inchangés, c'est-à-dire que les bits des pixels concernés par le dessin gardent les valeurs qu'ils avaient précédemment sur la destination dans les plans non masqués (cf. figure 6.4.).
fig.
6.4. Effet du masque des plans sur un pixel
(avec
la fonction GXcopy)
L’intérêt
du masque des plans sera illustré dans le chapitre sur la couleur. Sur
les écrans noir et blanc, le masque des plans est en
général positionné à AllPlanes
et on ne s'en préoccupera pas, à moins que l'on ne souhaite
réaliser un programme portable sur une machine couleur (cf. section 9.4.
Le noir et blanc).
foreground background |
L'attribut foreground est très important : il permet d'indiquer la couleur du dessin source. L'attribut background indique la couleur du fond utilisé par certaines requêtes (comme XDrawImageString, XFillRectangles, etc.). |
On peut modifier les attributs foreground et background d’un contexte graphique grâce aux requêtes
XSetForeground(dpy,gc,pixel)
XSetBackground(dpy,gc,pixel)
Quatre attributs précisent la forme des lignes dans les dessins de segments ou de polygones.
fig.
6.5. L'épaisseur de ligne (line_width)
line_width indique l'épaisseur des lignes (cf. figure 6.5.).
line_style indique un style de ligne. Cet attribut peut prendre les valeurs LineSolid, LineOffOnDash ou LineDoubleDash (cf. figure 6.6.).
fig.
6.6. Le style de ligne (line_style)
cap_style caractérise la manière dont se terminent les lignes. Les bouts de lignes peuvent être arrêtés nets avec CapButt. CapNotLast permet de ne pas inclure le dernier pixel. CapRound et CapProjecting indiquent des bouts de lignes respectivement arrondis et rectangulaires, pour des lignes possédant déjà un certain relief (une demi-épaisseur de ligne d'avancée).
join_style caractérise la manière dont se joignent les segments dans le tracé de polygones (cf. figure 6.7.). Il s’agit d’approximation du fait de la nature discrète des points d’écran.
fig.
6.7. Les styles de raccords de lignes (join_style)
La fonction XSetLineAttributes permet de modifier simultanément les quatre attributs du dessin des lignes :
XSetLineAttributes(dpy, gc, line_width, line_style, cap_style, join_style)
Pour les lignes pointillées, deux autres attributs du contexte graphique seront utilisés : dash_offset, et dashes (de type char). On pourra ainsi préciser l'origine de la reproduction du tiret et sa longueur en pixel. Cependant, on évitera d'utiliser les modes pointillés dans la Release 4 car ils sont très lents.
Les attributs dash_offset et dashes du contexte graphique permettent de définir un style de pointillé standard mais la fonction XSetDashes permet d'affecter au contexte graphique un style de pointillé plus complexe :
XSetDashes (dpy, gc, dash_offset, dash_list, nb_dash)
Display * dpy ;
GC gc ;
int dash_offset ;
char dash_list [];
int nb_dash ;
Le motif du pointillé est alors défini par un tableau de caractères (argument dash_list) indiquant, par une liste d'entier, le nombre de pixels devant successivement être alternés dans les couleurs foreground et background. La figure 6.8. en donne quelques exemples.
fig. 6.8. Exemples de lignes pointillées
L’argument nb_dash indique le nombre d'entiers figurant dans le tableau, et dash_offset précise l'origine en pixel de la reproduction du motif. Les pointillés peuvent ainsi être plus complexes que ceux que l'on peut définir à partir des seuls attributs du contexte graphique.
Les attributs fill_style, fill_rule, arc_mode, tile, stipple, ts_x_origin et ts_y_origin concernent les requêtes de remplissage comme XFillRectangles ou XFillArcs. Ces requêtes dessinent en général dans la couleur foreground mais elles peuvent aussi utiliser un motif pour paver régulièrement le dessin.
tile stipple |
Des motifs peuvent être indiqués dans ces attributs. Le premier est un Pixmap quelconque appelé tile (tuile) pour évoquer la disposition des tuiles sur un toit. Le second, stipple (pochoir), est appelé ainsi car c'est un Pixmap plan qui permet de laisser des points de la destination inchangés. L'utilisation de ces attributs est gouvernée par l'attribut fill_style. |
fill_style Cet attribut détermine le style de remplissage. Il peut prendre les valeurs FillSolid, FillTiled, FillStippled et FillOpaqueStipple. Ces différents styles sont illustrés figure 6.9.
ts_x_origin ts_y_origin |
Ces attributs indiquent l'origine à partir de laquelle est reporté le motif quand le style de remplissage utilise la tuile ou le pochoir (cf. figure 6.10). |
Les fonctions suivantes permettent de modifier ces attributs :
XSetFillStyle (dpy, gc,fill_style)
XSetTile (dpy, gc, tile)
XSetStipple (dpy, gc, stipple)
XSetTSOrigin (dpy, gc, ts_x_origin, ts_y_origin)
fig. 6.9. Le style de remplissage (fill_style)
fig. 6.10. L'origine du pavage d’un motif
Avec FillSolid, la figure est remplie avec la couleur de foreground. Le style FillTiled permet d'effectuer un pavage de la tuile et le style FillStippled effectue un remplissage du pochoir dans la couleur foreground. FillOpaqueStippled permet de disposer d'un deuxième motif dans les couleurs du foreground et du background.
Il est recommandé d'utiliser des tailles de motif spécifiques pour obtenir une vitesse de réaffichage optimale. Ces tailles dépendent du serveur. Les fonctions suivantes permettent de connaître les dimensions préférées du serveur :
Status XQueryBestTile (dpy, drawable, width, height, &ret_width,
&ret_height)
Status XQueryBestStipple (dpy, drawable, width, height, &ret_width,
&ret_height)
Status XQueryBestSize (dpy, class, drawable, width, height, &ret_width,
&ret_height)
Dans ces trois fonctions l’argument drawable sert à indiquer la destination. Les arguments width et height indiquent les dimensions souhaitées par le client et ret_width et ret_height permettent de récupérer les tailles préférées du serveur. Les deux premières fonctions permettent de récupérer des dimensions pour une tuile et pour un pochoir. La fonction XQueryBestSize est plus générale car l’entier class permet d’indiquer l’usage qui sera fait du motif : CursorShape pour un curseur, TileShape pour une tuile et StippleShape pour un pochoir.
fill_rule L'attribut fill_rule (règle de remplissage) concerne le remplissage des polygones fermés. Il peut prendre deux valeurs : EvenOddRule et WindingRule (cf. figure 6.11. page suivante).
La fonction XSetFillRule (dpy, gc, fill_rule) permet de modifier cet attribut[2]. Avec EvenOddRule, littéralement règle paire/impaire, la requête ne remplira les surfaces internes que si elles se recouvrent un nombre impair de fois alors qu’avec la règle WindingRule elle remplira l’intérieur du polygone quel que soit le nombre de superpositions.
fig.
6.11. Les valeurs de l'attribut fill_rule
pour
remplir l’intérieur des polygones
arc_mode L'attribut arc_mode définit le mode de remplissage des arcs. Cet attribut peut prendre deux valeurs, ArcChord (à la corde) et ArcPieSlice (en part de gâteau, cf. figure 6.12.) La fonction XSetArcMode permet de changer cet attribut.
fig. 6.12. Modes de remplissage des arcs
font L'attribut font indique la police de caractères utilisée par les requêtes XDrawString, XDrawImageString et XDrawText.
La fonction XSetFont (dpy, gc, font) permet de changer la fonte d’un contexte graphique. Nous reviendrons en détail sur les fontes dans les deux chapitres suivants.
subwindow_mode L'attribut subwindow_mode indique si l'opération effectuée dans une fenêtre parent doit également porter sur les fenêtres filles (IncludeInferiors) ou les ignorer (ClipByChildren). On peut modifier cet attribut avec la fonction XSetSubwindowMode (dpy, gc, mode).
L’attribut subwindow_mode est utilisé aussi bien par les requêtes de dessin que par les fonctions de copie. Par exemple, si l’on souhaite copier tout l’écran (i.e. la fenêtre racine et le contenu de toutes ses filles), il faut positionner l’attribut subwindow_mode à IncludeInferiors pour que la requête copie également le contenu des filles de la racine.
Les fonctions de copies (XCopyArea et XCopyPlane) permettent de transférer le contenu d'une zone rectangulaire d’un objet source sur un autre objet. Ces procédures peuvent échouer plus ou moins car la zone source peut ne pas être entièrement disponible, du fait des dimensions indiquées ou parce qu'elle peut être en partie cachée. On peut être informé en cas d’échec (dû à une indisponibilité de la source) des zones non atteintes par la requête de copie, à condition d'avoir positionné l’attribut graphics_exposures du contexte graphique à True dans la requête.
graphics_exposures Cet attribut permet de sélectionner les événements de type GraphicsExpose et NoExpose résultant d'un appel à une fonction de copie comme XCopyArea ou XCopyPlane. On pourra modifier cet attribut avec la fonction XSetGraphicsExposures (dpy, gc, bool).
Si l'attribut graphics_exposures est positionné à True lors de l'appel à la requête de copie, le serveur émettra :
• un ou plusieurs événements de type GraphicsExpose si la source n’était que partiellement disponible pour indiquer les zones qui n'ont pas été atteintes et qui doivent être réexposées à la requête. Ces régions sont analogues à celles envoyées par un événement de type Expose, car elles nécessitent également un réaffichage.
• un seul événement de type NoExpose si la source était entièrement disponible (succès total) ; cependant, le serveur émet également un événement de type NoExpose si la destination n'était pas du tout disponible sur la région concernée[3].
Viennent ensuite trois attributs permettant de restreindre la zone des points de la destination affectés par une requête graphique. Cette zone s'appelle en anglais la zone de clipping.
clip_x_origin clip_y_origin clip_mask |
clip_x_origin et clip_y_origin indiquent l'origine de la zone affectée par la requête et l'attribut clip_mask délimite la zone proprement dite (cf. figure 6.13.). On appellera cette zone par abus de langage la zone de clipping . Les attributs |
fig.
6.13. Effet du masque de cadrage sur le tracé d'un
segment
Les fonctions qui permettent de modifier les attributs de la zone de clipping du contexte graphique sont les suivantes :
XSetClipMask (dpy, gc, clip_mask)
XSetClipOrigin (dpy, gc, clip_x_origin, clip_y_origin)
XSetClipMask permet de définir la zone atteinte par le Pixmap passé en argument. On peut passer la constante None si l’on ne souhaite plus utiliser de zone de clipping. Un appel à XSetClipMask clipping (dpy, gc, None) permet donc d'annuler la zone de .
La zone de clipping est modifiée pour le réaffichage de dessins. En effet, cette optimisation est nécessaire pour éviter de réafficher une zone où le dessin est encore présent. On fait appel à des requêtes dessinant le dessin dans toute la fenêtre, mais on restreint la zone atteinte par ces requêtes à la zone qui a perdu les dessins (celle correspondant à l'événement Expose qui a été reçu) dans le contexte graphique.
Pour faciliter la programmation, la librairie offre plusieurs fonctions permettant de modifier la zone de clipping. La fonction XSetClipRectangles permet de limiter la zone de clipping du contexte graphique à une réunion de rectangles (passée en argument sous forme d’un tableau). On peut ainsi limiter la zone de clipping aux rectangles rapportés par les événements de type Expose ou GraphicsExpose. Nous reviendrons sur cette fonction dans la section 6.5.
La fonction XSetRegion permet également d'affecter la zone de clipping à une zone délimitée par une région (cf. section 6.6.).
Pour créer un contexte graphique on utilise la fonction
GC XCreateGC (display, drawable, valuemask, &xgc_values)
unsigned long valuemask ;
XGCValues xgc_values ;
Cette fonction utilise le même principe que XCreateWindow. Le contexte graphique ainsi créé bénéficie des valeurs d'attributs de la structure xgc_values qui sont spécifiés par la combinaison de masques valuemask. Les masques correspondants aux différents champs de la structure XGCValues (cf. figure 6.1.) sont énumérés figure 6.14. L'argument de type Drawable sert ici à indiquer les caractéristiques de l'écran contenant la fenêtre ou le Pixmap sur lequel seront réalisés les dessins. On pourra donc souvent passer en argument la fenêtre racine.
#define GCFunction (1L<<0)
#define GCPlaneMask (1L<<1)
#define GCForeground (1L<<2)
#define GCBackground (1L<<3)
#define GCLineWidth (1L<<4)
#define GCLineStyle (1L<<5)
#define GCCapStyle (1L<<6)
#define GCJoinStyle (1L<<7)
#define GCFillStyle (1L<<8)
#define GCFillRule (1L<<9) #define GCTile (1L<<10) #define GCStipple (1L<<11) #define GCTileStipXOrigin (1L<<12)
#define GCTileStipYOrigin (1L<<13) #define GCFont (1L<<14) #define GCSubwindowMode (1L<<15) #define GCGraphicsExposures (1L<<16)
#define GCClipXOrigin (1L<<17)
#define GCClipYOrigin (1L<<18)
#define GCClipMask (1L<<19)
#define GCDashOffset (1L<<20)
#define GCDashList (1L<<21)
#define GCArcMode (1L<<22)
fig. 6.14. Masques pour la
définition
des
champs du contexte graphique
De manière analogue, on peut modifier un contexte graphique existant grâce à la fonction
XChangeGC (dpy, gc, valuemask, &xgc_values)
Cependant cette procédure est un peu lourde puisqu'il faut d'abord initialiser une structure de type XGCValues. Si l'on souhaite modifier un contexte graphique sur quelques attributs seulement, on utilisera volontiers une procédure ad hoc. Comme nous l'avons vu dans la section précédente, il existe quasiment une fonction par attribut :
XSetState (dpy, gc, fg, bg, function, plane_mask)
XSetForeground (dpy, gc, fg)
XSetBackground (dpy, gc, bg)
XSetFunction (dpy, gc, function)
XSetPlaneMask (dpy, gc, planemask)
XSetLineAttributes (dpy, gc, lin_width, line_style, cap_style, join_style)
XSetDashes (dpy,gc,dash_offset, dash_list, n)
XSetFillStyle (dpy, gc, fill_style)
XSetFillRule (dpy, gc, fill_rule)
XSetTile ( dpy, gc, tile)
XSetStipple (dpy, gc, stipple)
XSetTSOrigin (dpy, gc, ts_x, ts_y)
XSetFont (dpy, gc, font)
XSetClipOrigin (dpy, gc, x, y)
XSetClipMask (dpy, gc, pixmap)
XSetClipRectangles (dpy, gc, cx, xy, rectangles, nrect, ordre)
XSetArcMode (dpy, gc, arc_mode)
XSetSubwindowMode (dpy, gc, subwindow_mode)
On peut également copier les attributs d'un contexte graphique dans un autre avec la fonction XCopyGC qui spécifie dans valuemask les valeurs devant être copiées.
XCopyGC (dpy, gc_source, valuemask, gc_dest)
La macro DefaultGC (dpy,screen) permet d'éviter la création d'un contexte graphique en retournant un contexte graphique par défaut. Les valeurs des attributs du contexte graphique par défaut sont énumérées figure 6.15. page suivante. On prendra garde cependant à bien contrôler que les valeurs des attributs importants, comme par exemple celle de l'attribut foreground, soient convenables. Si le programme est en noir et blanc, on a tout de même une chance sur deux d’écrire en blanc sur blanc ou en noir sur noir dans les fenêtres ! La figure 6.15. indique les valeurs par défaut de ce contexte graphique.
int function GCCopy
unsigned long plane_mask Tous à 1
unsigned long foreground 0
unsigned long background 1
int line_width 0
int line_style LineSolid
int cap_style CapButt
int join_style JoinMiter
int fill_style FillSolid
int fill_rule EvenOddRule
int arc_mode ArcPieSlice
Pixmap tile de la couleur de foreground
Pixmap stipple rempli de 1
int ts_x_origin 0
int ts_y_origin 0
Font font dépend de l'implantation
int subwindow_mode ClipByChildren
Bool graphics_exposures True
int clip_x_origin 0
int clip_y_origin 0
Pixmap clip_mask None
int dash_offset 0
char dashes 4
fig. 6.15. Les valeurs des attributs du contexte graphique par défaut
On dispose de fonctions graphiques élémentaires pour dessiner sur un objet. Toutes ces requêtes portent sur un objet de type Drawable, c'est-à-dire une fenêtre de classe InputOutput ou un Pixmap. Elles consultent toutes le contexte graphique sur les attributs foreground, function, subwindow_mode, plane_mask, clip_x_origin, clip_y_origin et clip_mask. Il existe également des requêtes de dessin permettant de dessiner des morceaux de textes, mais ces fonctions seront détaillées dans les chapitres suivants.
Pour éviter des déceptions, on prendra garde avec toutes les fonctions de dessin qui suivent :
• A bien contrôler les valeurs des paramètres du contexte graphique intervenant dans le calcul des couleurs du dessin final. Ainsi, pour dessiner dans une fenêtre de fond blanc, on pourra utiliser un contexte graphique de foreground noir et de fonction de transfert Gxcopy, ou bien utiliser la fonction de transfert GXinvert. L'avantage de la fonction GXinvert sur écran noir et blanc est qu'elle permet de dessiner sur fond blanc comme sur fond noir. L'inconvénient est que deux dessins successifs effacent le dessin.
• A s'assurer que la fenêtre dans laquelle on dessine est bien visible à l'écran au moment où se produit la requête de dessin (voir plus loin section 6.5.). Sinon, le dessin n'apparaitra pas, mais aucun message d'erreur ne sera émis.
On peut dessiner un ou plusieurs points dans la couleur de foreground grâce aux requêtes XDrawPoint et XDrawPoints :
XDrawPoint (dpy, draw, gc, x, y)
XDrawPoints (dpy, draw, gc, points, npoints, mode)
Drawable draw ;
GC gc ;
XPoint points [] ;
int npoints ;
int mode ;
typedef struct {
short x, y ;
} XPoint ;
Les requêtes dessinant des lignes, des segments ou des contours de polygones consulteront les attributs foreground, line_width, line_style, cap_style, join_style, dash_offset et dash_list. Pour certains styles de pointillés (cf. la description de l'attribut line_style), les attributs background, dashes et dash_offset seront également consultés.
Pour dessiner une ou plusieurs lignes passant par les points indiqués en argument, on utilisera les requêtes XDrawLine et XDrawLines :
XDrawLine (dpy, draw, gc, x1, y1, x2, y2)
int x1, y1, x2, y2 ;
XDrawLines (dpy, draw, gc, points, npoints, mode)
Drawable draw ;
GC gc ;
XPoint points [] ;
int npoints ;
int mode ; /* CoordModeOrigin ou CoordModePrevious */
Le mode permet de spécifier dans la requête si les points sont donnés relativement à l'origine, ou à partir de l'origine pour le premier point et relativement au point précédent pour les autres. XDrawLines trace toutes les lignes entre deux points successifs dans le tableau et permet donc de tracer des polygones[4]. Par contre XDrawSegments est utilisée pour tracer un groupe de segment disjoints :
XDrawSegments (dpy, draw, gc, segments, nsegments)
Drawable draw ;
GC gc ;
XSegment segments [] ;
int nsegments ;
typedef struct {
short x1, y1, x2, y2 ;
} XSegment ;
Pour tracer un ou plusieurs rectangles, on utilisera les fonctions
XDrawRectangle (dpy, draw, gc, x, y, width, height)
Drawable draw ;
GC gc ;
int x, y ;
unsigned int width, height ;
XDrawRectangles (dpy, draw, gc, rectangles, nrects)
Drawable draw ;
GC gc ;
XRectangle rectangles [] ;
int nrects ;
typedef struct {
short x, y ;
unsigned short width, height ;
} XRectangle ;
XDrawArc et XDrawArcs utilisent des angles exprimés en 64e de degré ; on utilise donc 64*90 pour désigner p/2 et 64*180 pour désigner un angle plat.
XDrawArc (dpy, draw, gc, x, y, width, height, angle1, angle2)
Drawable draw ;
GC gc ;
int x, y, angle1, angle2 ;
unsigned int width, height ;
XDrawArcs (dpy, draw, gc, x, y, arcs, narcs)
Drawable draw ;
GC gc ;
XArc arcs [] ;
int narcs ;
typedef struct {
short x,y;
unsigned short width, height;
short angle1, angle2;
} XArc ;
Les angles peuvent être positifs (sens inverse des aiguilles d’une montre) ou négatifs (sens des aiguilles d’une montre). Ainsi par exemple, la requête
XDrawArc (dpy,win,gc,x,y,width,height, 90x64, -(45x64))
dessine l'arc correspondant au secteur angulaire représenté figure 6.16. page suivante.
fig.
6.16. Le dessin des arcs
Des fonctions analogues aux précédentes permettent de remplir des figures :
XFillRectangle (dpy, draw, gc, x, y, width, height)
int x, y ;
unsigned int width, height ;
XFillRectangles (dpy, draw, gc, rectangles, nrects)
XRectangle rectangles [] ;
int nrects ;
XFillArc (dpy, draw, gc, x, y, width, height, angle1, angle2)
int x, y, angle1, angle2;
unsigned int width, height ;
XFillArcs (dpy, draw, gc, x, y, arcs, narcs)
XArc arcs [] ;
int narcs ;
XFillPolygon (dpy, draw, gc, points, npoints, shape, mode)
XPoint points [] ;
int npoints, shape, /* Complex, Convex ou Nonconvex */
mode ; /* CoordModeOrigin ou CoordModPrevious */
Toutes ces fonctions utilisent les attributs fill_style, background, tile, stipple, ts_x_origin et ts_y_origin pour spécifier le mode de remplissage. Les fonctions sur les arcs utilisent en outre l'attribut arc_mode et la fonction XFillPolygon utilise l'attribut fill_rule.
1. Les requêtes qui dessinent plusieurs objets simultanément ont une limite. Le nombre maximal d'objets dépend du serveur et des requêtes[5].
2. Les requêtes XFillRectangle et XDrawRectangle ne spécifient pas le rectangle de la même façon. Employées avec les mêmes arguments, elles ne dessinent pas le contour et l'intérieur d'un même objet. En fait XFillRectangle respecte bien les dimensions indiquées alors que XDrawRectangle dessine un rectangle dont les dimensions ont été augmentées d'un pixel.
Il existe également des fonctions permettant de copier le contenu d'une zone rectangulaire d’un objet source vers un autre objet :
XCopyArea (dpy, src, dest, gc, src_x, src_y, width, height, dest_x, dest_y)
XCopyPlane (dpy, src, dest, gc, src_x, src_y, width, height, dest_x, dest_y,
plane)
Drawable src, dest ;
int src_x, src_y, dest_x, dest_y :
unsigned int width, height ;
unsigned long plane ;
La zone rectangulaire de la source src est délimitée par les quatre arguments src_x, src_y, width, et height, et le point d'origine du report est spécifié sur la destination dest par les coordonnées dest_x et dest_y (cf. figure 6.17. page suivante).
fig. 6.17. Copier de la source vers la destination
Ces procédures sont susceptibles d’échouer si la source n'est pas entièrement disponible. En cas d’échec, on peut recevoir sous forme d'événements les zones de la destination qui n'ont pu être atteintes par la requête du fait d'une indisponibilité de la source. Pour recevoir ces événements, il faut avoir positionné l’attribut graphics_exposures du contexte graphique à True dans la requête de copie. Le serveur émet alors après la copie : soit un ou plusieurs événements de type GraphicsExpose, pour indiquer les zones de la destination qui devront être à nouveau soumises à la requête (du fait d'une indisponibilité de la source), soit un événement de type NoExpose si la copie s'est bien passée[6].
La fonction XClearArea permet d'effacer les dessins d'une zone en réaffichant le fond de la fenêtre[7]. Elle ne prend pas de contexte graphique en argument mais le booléen exposures_bool permet d'indiquer la sélection des événements GraphicsExpose et NoExpose qui résultent de la requête.
XClearArea (dpy, win, x, y, width, height, exposures_bool)
Bool exposures_bool;
XClearWindow (dpy,win)
La fonction XClearWindow permet de réafficher entièrement le fond de la fenêtre.
Un événement de type Expose est envoyé à l’application quand une fenêtre (ou une partie) précédemment invisible devient visible (sous réserve, bien entendu, que la fenêtre concernée ait bien sélectionné ce type d'événement). C'est l'indication pour l'application qu'elle a perdu le contenu de la fenêtre, et elle doit normalement le redessiner.
La librairie Xlib n’est pas une librairie d’objets graphiques. Elle permet de dessiner dans les fenêtres quand celles-ci se trouvent à l'écran, mais les dessins qui sont produits ne sont mémorisés nulle part. Les primitives graphiques modifient directement l’écran. Tout se passe donc comme sur un tableau noir : on dessine dans une fenêtre, mais si une autre fenêtre vient à recouvrir la première, le dessin précédent se trouve effacé par l'affichage de la nouvelle fenêtre. Quand l’ancienne fenêtre est remise en avant, le serveur lui envoie un événement de type Expose mais c'est à l’application d’en redessiner le contenu. Le serveur est chargé d’informer les applications de la mise en avant des fenêtres (via les événements de type Expose) mais le contenu des fenêtres est en général à la charge des applications (seul le fond est repeint par le serveur).
En réalité, quand le serveur le permet, on peut mémoriser le contenu des fenêtres (cf. l’attribut backing_store des fenêtres). Cependant, les applications devant être prêtes à tourner sur n’importe quelle machine, les programmes doivent être préparés à réafficher le contenu des fenêtres sur réception des événements de type Expose.
Selon les programmes, ces informations pourront être stockées dans différents objets caractérisant le dessin. Dans d'autres cas, on pourra procéder à la sauvegarde systématique de tous les dessins effectués dans la fenêtre (ces dessins étant alors tout simplement effectués deux fois, une fois dans la fenêtre et une seconde fois dans un Pixmap ou une Image permettant de posséder un "double" de la fenêtre).
Quoi qu'il en soit, l'application effectuera le réaffichage des dessins — ainsi que les premiers dessins eux-mêmes — sur réception des événements de type Expose. En effet, si la requête de dessin est effectuée avant que la fenêtre soit effectivement visible à l’écran, le dessin n’est tout simplement pas reporté sur le fond de la fenêtre.
Les événements de type Expose sont sélectionnés avec le masque ExposureMask et ne concernent que les fenêtres InputOutput car ce sont les seules fenêtres dans lesquelles on peut dessiner. La structure associée à un événement de type Expose contient la zone rectangulaire mise en avant. On peut donc optimiser l'affichage en affectant la zone de clipping du contexte graphique à ce rectangle juste avant de procéder au réaffichage des dessins.
Le champ count d'un événement de type Expose indique en outre son rang dans la série des événements provenant de la même mise en avant. En effet, un même mouvement de fenêtre peut provoquer la mise en avant de plusieurs zones disjointes de la fenêtre. Il en résulte l’envoi de plusieurs événements de type Expose, comme l’illustre l'exemple de la figure 6.18. ci-contre. Ces événements sont numérotés de manière décroissante, le dernier événement envoyé ayant un champ count à zéro.
Pour optimiser les échanges avec le serveur, on peut affecter la zone de clipping du contexte graphique à toute la zone exposée lors de la mise en avant. Pour cela, au fur et à mesure que l'on reçoit des événements de type Expose et tant que le champ count de l’événement n’est pas nul, on accumule les petits rectangles exposés dans un tableau de XRectangle. Une fois le champ count à zéro, on réaffiche le contenu de la fenêtre après avoir modifié la zone de clipping du contexte graphique par l’union des rectangles accumulés. On remet ensuite à jour la zone de clipping du contexte graphique[8] (à moins qu'on ne procède toujours de cette manière pour dessiner).
La librairie a prévu spécialement une fonction permettant de réaliser l'optimisation proposée dans le paragraphe précédent. Il s’agit de la fonction XSetClipRectangles. Cette fonction modifie la zone de clipping (attributs clip_mask, clip_x_origin et clip_y_origin du contexte graphique) à partir d'un tableau de XRectangles :
fig. 6.18. Trois zones pouvant être
exposées simultanément
par la mise en avant de la fenêtre
située en arrière-plan
XSetClipRectangles (dpy, gc, clip_x_origin, clip_y_origin, rectangles,
nb_r, ordering)
GC gc ;
int clip_x_origin, clip_y_origin ;
XRectangle rectangles [] ;
int nb_r, ordering ;
Avec XSetClipRectangles, le champ clip_mask du contexte graphique gc est affecté par la réunion des rectangles du tableau rectangles. L’argument ordering indique l’ordre dans lesquels sont fournis les nb_r rectangles du tableau de rectangles. L’ordre dans lequel les rectangles exposés sont envoyés par le serveur dépend des implantations[9] mais peut être connu. (Le serveur d’un Sun4 effectue un découpage par bandes et les envoie selon l’ordre YXBanded illustré figure 6.19. page suivante). Les valeurs possibles sont Unsorted, YSorted, YXSorted, or YXBanded. En l'absence d'information, on utilisera la constante Unsorted.
fig.
6.19. Quatre zones exposées envoyées par le serveur dans
l’ordre YXBanded
Les événements de type GraphicsExpose pourront être traités de manière analogue, car ils sont envoyés pour indiquer les zones qui doivent être réexposées à une requête de copie (une fois que l'utilisateur aura été prévenu et aura modifié la disposition des fenêtres pour rendre la copie possible).
Les régions présentent l’intérêt de définir des zones non connexes et permettent d’implanter des objets graphiques. Les fonctions sur les régions sont optimisées et on utilisera volontiers les régions pour définir des zones sensibles non rectangulaires ou non connexes, comme des réunions de fenêtres disjointes.
Il existe parmi les fonctions sur les régions, des opérateurs ensemblistes intéressants (réunion, intersection, etc.), et de nombreux opérateurs booléens permettant de déterminer si un point ou un rectangle se trouve dans une région.
Les fonctions disponibles sur les Region sont les suivantes :
Region XCreateRegion ()
Region XPolygonRegion (points, npoints, fill_rule)
XPoint points [] ;
int npoints, fill_rule ;
XDestroyRegion (region)
Region region ;
XSetRegion (dpy, gc, region)
Region region ;
GC gc ;
XOffsetRegion (region,dx,dy)
XShrinkRegion (region, dx, dy)
Region region ;
int dx, dy ;
XClipBox (region, &rect_return)
Region region ;
XRectangle rect_return ;
XIntersectRegion (region1,region2, region_dest)
XUnionRegion (region1,region2, region_dest)
XXorRegion (region1,region2, region_dest)
XSubstractRegion (region1,region2, region_dest)
XUnionRectWithRegion (&rect, region_src, region_dest)
XRectangle rect ;
Region region1, region2,
region_dest, region_src ;
Bool XEmptyRegion (region)
Bool XEqualRegion (region1, region2)
Bool XPointInRegion (region, x, y)
int XRectInRegion (region, x, y, width, height)
Region region, region1, region2 ;
int x, y, width, height ;
On notera en particulier XSetRegion qui positionne l’attribut clip_mask d’un contexte graphique à la zone définie par une région.
Les régions peuvent être déplacées par rapport à une origine avec la requête XOffsetRegion. La zone de clipping définie ensuite par un appel à XSetRegion se trouve alors doublement décalée, d’une part à cause du décalage de la région, et d'autre part, à cause des attributs clip_x_origin et clip_y_origin du contexte graphique. Ces deux décalages s’ajoutent pour définir la position finale de la zone.
On ne peut clore le chapitre sur les dessins sans indiquer l'existence d'autres structures que les Pixmap pour stocker des dessins : les images (type XImage). Les images permettent de transférer des informations graphiques entre un client et le serveur. L'intérêt des images est que ce sont des structures locales au client (par opposition aux Pixmap qui nécessitent pour leurs manipulations des communications avec le serveur). Le traitement d'images étant exécuté localement, il sera plus rapide. Les fonctions de base sur les images sont XGetImage et XPutImage. XGetImage crée une image à partir du contenu d'un Drawable et XPutImage permet de transférer le contenu d'une image dans une fenêtre ou un Pixmap.
Malheureusement, il n’y pas de format unique sur les images[10] et c’est pourquoi les fonctions sur les images sont en général très lentes. Mais ce n'est pas toujours le cas car certains constructeurs ont implanté des fonctions rapides correspondant aux formats les plus fréquemment utilisés sur leurs machines[11].
La structure XImage est la structure utilisée pour représenter l'image dans la mémoire du client. Cette structure (cf. figure 6.20) contient toutes les données nécessaires à la compréhension du format des données. Certaines caractéristiques peuvent être modifiées par l'utilisateur (height, width, xoffset). D'autres paramètres (byte_order, bitmap_unit, etc.) sont caractéristiques à la fois de l'image et du serveur. Si ces paramètres diffèrent entre ceux de l'image et du serveur, XPutImage effectuera les conversions appropriées. Le champ de fonction f a été prévu pour permettre d'optimiser les procédures de manipulation d'images quand les caractéristiques sont connues.
typedef struct _XImage {
int width, height;
/*
taille de l’image */
int xoffset; /* nombre de pixels de décalage dans la direction x */
int format;
/*
XYBitmap, XYPixmap, ZPixmap */
char
*data;
/* pointeur sur les données image
*/
int byte_order;
/*
ordre des octets:LSBFirst,MSBFirst */
int bitmap_unit;
/*
quant. de bits parcourus 8, 16, 32 */
int bitmap_bit_order;
/*
LSBFirst, MSBFirst */
int bitmap_pad;
/*
8, 16, 32 ou bien XY ou ZPixmap */
int depth; /* profondeur de l’image */
int bytes_per_line; /* nb d’octets par ligne */
int bits_per_pixel;
/* bits par pixel (ZPixmap) */
unsigned
long red_mask;
/*
bits en arrangement selon
l’axe z*/
unsigned long green_mask;
unsigned long blue_mask;
char
*obdata;
struct
funcs {
/* procédures de manipulation
d’image */
struct _XImage *(*create_image)();
int (*destroy_image)();
unsigned long (*get_pixel)();
int (*put_pixel)();
struct _XImage *(*sub_image)();
int (*add_pixel)();
} f;
} XImage;
fig. 6.20. La structure XImage
On notera l'existence des fonctions suivantes pour créer et manipuler les images :
XImage* XCreateImage (dpy, visual, depth, format, offset, data, width, height,
bitmap_pad, bytes_per_line)
visual ;
Visual*unsigned int depth ;
int format, offset ;
char* data ;
unsigned int width, height ;
int bitmap_pad, bytes_per_line ;
XImage* XGetImage (dpy, draw, x, y, width, height, plane_mask, format)
XPutImage (dpy, draw, gc, image, sx, sy, dx, dy, width, height)
XImage* image ;
Ximage* XSubImage (image, x, y, width, height)
XImage* XGetSubImage (dpy, draw, x, y, width, height, plane_mask, format,
dest_image, dest_x, dest_y)
unsigned long XGetPixel (image, x, y)
unsigned long XPutPixel (image, x, y, pixel)
XAddPixel (image, value)
XDestroyImage (image)
On utilisera bien entendu les images pour faire du traitement d’images, mais aussi pour tester des pixels. On notera que la fonction XGetPixel permet de récupérer la valeur des pixels point par point — ce qui n’est pas possible avec les fonctions opérant sur des Pixmap. Ainsi, dans un logiciel de dessin on pourra utiliser une structure XImage pour représenter le voisinage d'un point désigné par l’utilisateur (par exemple, pour sélectionner un objet) uniquement dans le but de tester les valeurs des pixels dans ce voisinage[12].
On appelle curseur la forme qui représente la position de la souris à l'écran. Pour créer un curseur, on utilise le plus souvent la fonction XCreateFontCursor qui permet de charger un curseur à partir d'un nom de forme (correspondant en réalité à un caractère d'une fonte spéciale appelée fonte cursor).
Cursor XCreateFontCursor (dpy, forme)
Display* dpy ;
unsigned int forme ;
Les noms de formes utilisables se trouvent définis dans le fichier <X11/cursorfont.h> dont un extrait est présenté figure 6.21. Il faudra bien entendu inclure ce fichier dans le programme avant d'utiliser ces noms. Pour choisir un curseur, on pourra visualiser la fonte cursor à l'écran à l’aide de la commande shell xfd (pour X font displayer).
Un curseur est en réalité défini par la donnée d'un motif rectangulaire bicolore appelé Pixmap source, d'un Pixmap plan de même dimension servant de masque, pour indiquer quels points doivent être pris en considération dans la source, et d'un point de référence, appelé en anglais hot spot, servant à indiquer la position de la souris (cf. figure 6.22 page suivante).
L'intérêt du masque est de permettre un découpage plus fin du dessin. On peut ainsi avoir des curseurs non rectangulaires. Le masque sera en général un peu plus grand que le motif central. De cette manière, le motif est augmenté d'un liseré qui lui permet de se détacher sur n'importe quel fond.
#define XC_num_glyphs 154
#define XC_X_cursor 0
#define XC_arrow 2
#define XC_based_arrow_down 4
#define XC_based_arrow_up 6
#define XC_boat 8
#define XC_bogosity 10
#define XC_bottom_left_corner 12
#define XC_bottom_right_corner 14
#define XC_bottom_side 16
#define XC_bottom_tee 18
#define XC_box_spiral 20
#define XC_center_ptr 22
#define XC_circle 24
#define XC_clock 26
fig.
6.21. Un extrait du fichier cursorfont.h
On peut soi-même définir un curseur à partir de deux Pixmap, l'un servant de dessin et l'autre de masque (bitmap), avec à la fonction
Cursor XCreatePixmapCursor (dpy, source, mask, &fg, &bg, x_hot, y_hot)
Pixmap source, mask ;
XColor fg, bg ;
unsigned int x_hot, y_hot ;
Les couleurs d'un curseur pourront être modifiées grâce à la requête
XRecolorCursor (dpy, cursor, &fg, &bg)
Les arguments fg et bg utilisés par XRecolorCursor et XCreatePixmapCursor ne sont pas des pixels, mais des pointeurs sur des structures XColor qui devront être correctement initialisées (cf. chapitre 9 sur la couleur). Cette initialisation peut se faire par exemple avec la fonction XQueryColor[13] (pour traduire les pixels en niveaux d'intensité) ou avec une fonction commençant par XAllocColor.
fig. 6.22. La définition des
curseurs
La souris se déplaçant fréquemment à l'écran, le serveur gère lui-même le réaffichage des dessins situés sous le curseur. La taille du curseur influe sur la vitesse du réaffichage et la taille optimale varie selon les machines. On pourra obtenir cette information à l'aide de la fonction XQueryBestSize ou XQueryBestCursor.
Status XQueryBestCursor (dpy, draw, width, height, &width_return,
&height_return)
unsigned int width, height, width_return, height_return ;
Signalons aussi XCreateGlyphCursor qui est similaire à XCreatePixmapCursor à la différence près que les Pixmap source et masque sont spécifiés par les index de caractères dans des fontes. XCreateGlyphCursor permet ainsi d'utiliser un caractère comme curseur.
Pour associer un curseur à une fenêtre, on utilisera la fonction XDefineCursor. La fonction XUndefinedCursor permet de revenir au curseur précédent[14] (celui de la fenêtre parent).
XDefineCursor (dpy, win, cursor)
XUndefineCursor (dpy, win)
Cursor cursor ;
Pour libérer les ressources utilisées par un curseur, on appellera la fonction
XFreeCursor (dpy,cursor).
Les
fonctions importantes
GC XCreateGC (display, drawable, valuemask, &xgc_values)
GC DefaultGC (dpy,screen)
XSetState (dpy, gc, fg, bg, function, plane_mask)
XSetLineAttributes (dpy, gc, line_width, line_style, cap_style,
join_style)
XSetForeground (dpy, gc, fg)
XSetBackground (dpy, gc, bg)
XSetFunction (dpy, gc, function)
XSetPlaneMask (dpy, gc, planemask)
XSetLineAttributes (dpy, gc, lin_width, line_style, cap_style,
join_style)
XSetDashes (dpy,gc,dash_offset, dash_list, n)
XSetFillStyle (dpy, gc, fill_style)
XSetFillRule (dpy, gc, fill_rule)
XSetTile ( dpy, gc, tile)
XSetStipple (dpy, gc, stipple)
XSetTSOrigin (dpy, gc, ts_x, ts_y)
XSetFont (dpy, gc, font)
XSetClipOrigin (dpy, gc, x, y)
XSetClipMask (dpy, gc, pixmap)
XSetArcMode (dpy, gc, arc_mode)
XSetSubwindowMode (dpy, gc, subwindow_mode)
XCopyGC (dpy, gc_source, valuemask, gc_dest)
XChangeGC (dpy, gc, valuemask, &xgc_values)
XSetClipRectangles (dpy, gc, cx, xy, rectangles, nrect, ordre)
XDrawPoint (dpy, draw, gc, x, y)
XDrawPoints (dpy, draw, gc, points, npoints, mode)
XDrawLine (dpy, draw, gc, x1, y1, x2, y2)
XDrawLines (dpy, draw, gc, points, npoints, mode)
XDrawSegments (dpy, draw, gc, segments, nsegments)
XDrawRectangle (dpy, draw, gc, x, y, width, height)
XDrawRectangles (dpy, draw, gc, rectangles, nrects)
XDrawArc (dpy, draw, gc, x, y, width, height, angle1, angle2)
XDrawArcs (dpy, draw, gc, x, y, arcs, narcs)
XDrawArc (dpy,win,gc,x,y,width,height, 90x64, -(45x64))
XFillRectangle (dpy, draw, gc, x, y, width, height)
XFillRectangles (dpy, draw, gc, rectangles, nrects)
XFillArc (dpy, draw, gc, x, y, width, height, angle1, angle2)
XFillArcs (dpy, draw, gc, x, y, arcs, narcs)
XFillPolygon (dpy, draw, gc, points, npoints, shape, mode)
XCopyArea (dpy, src, dest, gc, src_x, src_y, width, height, dest_x, dest_y)
XCopyPlane (dpy, src, dest, gc, src_x, src_y, width, height, dest_x, dest_y,
plane)
XClearWindow (dpy,win)
XClearArea (dpy, win, x, y, width, height, exposures_bool)
Cursor XCreateFontCursor (dpy, forme)
XDefineCursor (dpy, win, cursor)
Toute primitive graphique utilise un contexte graphique permettant de spécifier les paramètres gouvernant le report du dessin sur la destination. Il est important de contrôler les attributs foreground, function et plane_mask du contexte graphique, car ils interviennent dans le calcul des couleurs et du dessin.
Les dessins sont effectués dans les fenêtres sur réception des événements de type Expose. Il est donc primordial de sélectionner ce type d'événement si l'on souhaite dessiner dans des fenêtres.
Les corrigés de ces exercices sont regroupés pages 297 et suivantes.
Exercice 10 : (événements, contexte graphique)
Faire une copie de tout l'écran dans une structure Pixmap. En afficher une partie dans une fenêtre et en faire défiler le contenu sur déplacement de la souris avec le bouton 2 enfoncé.
Exercice 11 : (contexte graphique)
Petite farce : copier toute la racine de l'écran d'un collègue dans une fenêtre (de même taille que la racine) et l'afficher ensuite sur son écran. Prévoir tout de même un cas de sortie. On peut aussi adapter l'exercice précédent de sorte que toute la racine de l'écran se déplace quand on tire la souris en maintenant le bouton 2 enfoncé.
Exercice 12 : (Etude des événements de type Expose)
Créer une fenêtre comportant un certain nombre de sous-fenêtres. On sélectionnera sur toutes les fenêtres les événements de type Expose et on imprimera sur réception d'un événement, les composantes de la zone exposée et le champ count de l'événement. (Pour créer des événements de type Expose, créer des recouvrements suivis de mises en avant-plan à l'aide du window manager.)
Exercice 13 : (Etude des événements de type GraphicsExpose)
Créer deux fenêtres dont l'une suffisamment grande. Quand on clique dans une fenêtre, le contenu de la racine située en (0,0) de largeur 200 et hauteur 200 est copié en (150, 200) dans la grande fenêtre avec la fonction XCopyArea. Tester ainsi les événements de type GraphicsExpose envoyés selon la configuration des deux fenêtres (disponibilité de la source, de la destination, des deux).
Exercice 14 : (Un rectangle qui suit la souris)
Dans une fenêtre, quand on clique avec les boutons 1 ou 2 et que l'on maintient le bouton enfoncé, on trace le rectangle dont le premier point correspond au point où le bouton a été enfoncé et dont le sommet opposé suit la souris. Quand on relâche, le rectangle reste affiché jusqu'au prochain enfoncement de bouton. On quitte sur enfoncement du bouton 3.
[1] Attention, GXxor prend deux x et est différent de GXor. Pour l'utilisation de GXxor et GXinvert, voir la section 9.4. dans le chapitre sur la couleur.
[2] Egalement utilisé comme argument des fonctions XFillPolygon et XPolygonRegion.
[3] On a envie dans ce cas de parler d'échec total puisque le transfert du dessin n'a pas eu lieu ; cependant, du point de vue du serveur, la copie s'est de toute façon nécessairement bien passée, car le résultat est le même que si la source était entièrement disponible. Le fait que le dessin n'ait pas atteint la destination n'est en réalité jamais pris en compte par le serveur.
[4] Cependant, elle ne consulte pas les attributs de jointure de lignes.
[5] Il s'agit des requêtes XDrawArcs, XDrawLines, XDrawPoints, XDrawRectangles, XDrawSegments, XDrawText, XDrawText16, XFillArcs et XFillRectangles. D'après [NYE 90], pour savoir quel est le nombre maximum de requêtes graphiques que le serveur peut appréhender simultanément, on peut utiliser la requête XMaxRequestSize et appliquer la recette suivante : retrancher d'abord trois pour avoir le nombre de points dessinables avec XDrawPoints, diviser ensuite par deux pour obtenir le nombre d'objets dessinables avec XDrawRectangles, et diviser par trois pour les arcs. Pour les requêtes XDrawText et XDrawText16, ce nombre dépendra de la taille des chaînes de caractères utilisées.
[6] La zone source était entièrement disponible ou la destination n'est pas du tout disponible (indépendamment de l'état de la source), ce qui revient au même du point de vue du serveur. En fait un événement de type NoExpose ne fait qu'indiquer qu'il n'y a pas d'événement de type GraphicsExpose.
[7] XClearArea appelée avec (dpy, win, 0, 0, 0, 0, False) a le même effet que XClearWindow (dpy, win) qui réaffiche simplement le fond dans toute la partie visible de la fenêtre.
[8] On utilisera pour cela la fonction XSetClipMask en lui passant la constante None.
[9] On pourra observer l’ordre dans lequel les événements sont envoyés par le serveur en imprimant les composantes et le champ count de chaque événement Expose reçu (cf. exercice n° 3).
[10] On notera, pour les mêmes raisons, l’absence de fonctions permettant de sauvegarder des images dans des fichiers, ou de les lire sur disque. Il faudra passer ici par l’intermédiaire des Pixmap et de bitmap pour les images bicolores, et écrire ses propres fonctions (ou utiliser les différents convertisseurs du marché) pour faire des sauvegardes sur disque.
[11] Si votre machine n'est pas suffisamment rapide, il vaut mieux réécrire les fonctions d'accès et utiliser directement les données si le format est connu, car les fonctions standards, prévues pour un format quelconque, ne sont pas très performantes.
[12] On crée une petite image représentant le voisinage du point. On peut alors repeindre dans le voisinage tous les objets présents à l'écran en testant après chaque requête de dessin les pixels de ce voisinage. S’il en est d’affectés, c’est que le voisinage a rencontré l’objet.
[13] Même pour créer des curseurs noir et blanc, on doit utiliser XQueryColor pour initialiser les XColor à des structures codant le noir et le blanc (cf. section 9.4.).
[14] Et non pas, comme on pourrait le croire à partir du nom, d'éliminer le curseur. Pour définir un curseur transparent, il faut créer un curseur (avec XCreatePixmapCursor) à partir d'un masque entièrement nul. L'idée n'est pas absurde car c'est la méthode utilisée pour définir un curseur comme intersection de deux droites (la grande croix utilisée dans certains logiciels de dessin). On définit d'abord un curseur transparent et on affiche en permanence deux droites pour marquer par leur intersection l'endroit où se trouve la souris.