summaryrefslogtreecommitdiff
path: root/tutorial/basicwindowsanddrawing.mdwn
blob: 21361a33a2ebd10407975ce77f5e7e981aa95daf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
# Creating a basic window - the "hello world" program

After we got some basic information about our screen, we can create our first window. In the X Window System, a window is characterized by an Id. So, in XCB, a window is of type:

		typedef uint32_t xcb_window_t;

We first ask for a new Id for our window, with this function:

		xcb_window_t xcb_generate_id (xcb_connection_t *connection);

Then, XCB supplies the following function to create new windows:

		xcb_void_cookie_t xcb_create_window (xcb_connection_t *connection,    /* Pointer to the xcb_connection_t structure */
											 uint8_t           depth,         /* Depth of the screen */
											 xcb_window_t      wid,           /* Id of the window */
											 xcb_window_t      parent,        /* Id of an existing window that should be the parent of the new window */
											 int16_t           x,             /* X position of the top-left corner of the window (in pixels) */
											 int16_t           y,             /* Y position of the top-left corner of the window (in pixels) */
											 uint16_t          width,         /* Width of the window (in pixels) */
											 uint16_t          height,        /* Height of the window (in pixels) */
											 uint16_t          border_width,  /* Width of the window's border (in pixels) */
											 uint16_t          _class,
											 xcb_visualid_t    visual,
											 uint32_t          value_mask,
											 const uint32_t   *value_list );

The fact that we created the window does not mean that it will be drawn on screen. By default, newly created windows are not mapped on the screen (they are invisible). In order to make our window visible, we use the function xcb\_map\_window(), whose prototype is

		xcb_void_cookie_t xcb_map_window (xcb_connection_t  *connection,
										  xcb_window_t       window );

Finally, here is a small program to create a window of size 150x150 pixels, positioned at the top-left corner of the screen:

		#include <unistd.h>      /* pause() */

		#include <xcb/xcb.h>

		int
		main ()
		{
			/* Open the connection to the X server */
			xcb_connection_t *connection = xcb_connect (NULL, NULL);


			/* Get the first screen */
			const xcb_setup_t      *setup  = xcb_get_setup (connection);
			xcb_screen_iterator_t   iter   = xcb_setup_roots_iterator (setup);
			xcb_screen_t           *screen = iter.data;


			/* Create the window */
			xcb_window_t window = xcb_generate_id (connection);
			xcb_create_window (connection,                    /* Connection          */
							   XCB_COPY_FROM_PARENT,          /* depth (same as root)*/
							   window,                        /* window Id           */
							   screen->root,                  /* parent window       */
							   0, 0,                          /* x, y                */
							   150, 150,                      /* width, height       */
							   10,                            /* border_width        */
							   XCB_WINDOW_CLASS_INPUT_OUTPUT, /* class               */
							   screen->root_visual,           /* visual              */
							   0, NULL );                     /* masks, not used yet */


			/* Map the window on the screen */
			xcb_map_window (connection, window);


			/* Make sure commands are sent before we pause so that the window gets shown */
			xcb_flush (connection);


			pause ();    /* hold client until Ctrl-C */

			xcb_disconnect (connection);
			
			return 0;
		}

In this code, you see one more function - xcb\_flush(), not explained yet. It is used to flush all the pending requests. More precisely, there are 2 functions that do such things. The first one is xcb\_flush():

		int xcb_flush (xcb_connection_t *c);

This function flushes all pending requests to the X server (much like the fflush() function is used to flush standard output). The second function is xcb\_aux\_sync():

		int xcb_aux_sync (xcb_connection_t *c);

This functions also flushes all pending requests to the X server, and then waits until the X server finishing processing these requests. In a normal program, this will not be necessary (we'll see why when we get to write a normal X program), but for now, we put it there.

The window that is created by the above code has a non defined background. This one can be set to a specific color, thanks to the two last parameters of xcb\_create\_window(), which are not described yet. See the subsections Configuring a window or Registering for event types using event masks for examples on how to use these parameters. In addition, as no events are handled, you have to make a Ctrl-C to interrupt the program.

TODO: one should tell what these functions return and about the generic error

Comparison Xlib/XCB:

* XCreateWindow ()  =>
 
        xcb_generate_id ()
        xcb_create_window ()

# Drawing in a window

Drawing in a window can be done using various graphical functions (drawing pixels, lines, rectangles, etc). In order to draw in a window, we first need to define various general drawing parameters (what line width to use, which color to draw with, etc). This is done using a graphical context.

### 1. Allocating a Graphics Context

As we said, a graphical context defines several attributes to be used with the various drawing functions. For this, we define a graphical context. We can use more than one graphical context with a single window, in order to draw in multiple styles (different colors, different line widths, etc). In XCB, a Graphics Context is, as a window, characterized by an Id:

		typedef uint32_t xcb_gcontext_t;

We first ask the X server to attribute an Id to our graphic context with this function:

		xcb_gcontext_t xcb_generate_id (xcb_connection_t *c);

Then, we set the attributes of the graphic context with this function:

		xcb_void_cookie_t xcb_create_gc (xcb_connection_t *c,
										 xcb_gcontext_t    cid,
										 xcb_drawable_t    drawable,
										 uint32_t          value_mask,
										 const uint32_t   *value_list );

We give now an example on how to allocate a graphic context that specifies that each drawing function that uses it will draw in foreground with a black color.

		#include <xcb/xcb.h>

		int
		main ()
		{
			/* Open the connection to the X server and get the first screen */
			xcb_connection_t *connection = xcb_connect (NULL, NULL);
			xcb_screen_t     *screen     = xcb_setup_roots_iterator (xcb_get_setup (connection)).data;

			/* Create a black graphic context for drawing in the foreground */
			xcb_drawable_t  window   = screen->root;
			xcb_gcontext_t  black    = xcb_generate_id (connection);
			uint32_t        mask     = XCB_GC_FOREGROUND;
			uint32_t        value[]  = { screen->black_pixel };
			
			xcb_create_gc (connection, black, window, mask, value);

			return 0;
		}

Note should be taken regarding the role of "valuemask" and "valuelist" in the prototype of xcb\_create\_gc(). Since a graphic context has many attributes, and since we often just want to define a few of them, we need to be able to tell the xcb\_create\_gc() which attributes we want to set. This is what the "valuemask" parameter is for. We then use the "valuelist" parameter to specify actual values for the attribute we defined in "valuemask". Thus, for each constant used in "valuelist", we will use the matching constant in "value_mask". In this case, we define a graphic context with one attribute: when drawing (a point, a line, etc), the foreground color will be black. The rest of the attributes of this graphic context will be set to their default values.

See the next Subsection for more details.

Comparison Xlib/XCB: 

* XCreateGC () =>
		
		xcb_generate_id ()
		xcb_create_gc ()

### 2. Changing the attributes of a Graphics Context

Once we have allocated a Graphic Context, we may need to change its attributes (for example, changing the foreground color we use to draw a line, or changing the attributes of the font we use to display strings. See Subsections Drawing with a color and Assigning a Font to a Graphic Context). This is done by using this function:

		xcb_void_cookie_t xcb_change_gc (xcb_connection_t *c,            /* The XCB Connection */
										 xcb_gcontext_t    gc,           /* The Graphic Context */
										 uint32_t          value_mask,   /* Components of the Graphic Context that have to be set */
										 const uint32_t   *value_list ); /* Value as specified by value_mask */

The valuemask parameter could take any combination of these masks from the xcb_gc_t enumeration:

		XCB_GC_FUNCTION
		XCB_GC_PLANE_MASK
		XCB_GC_FOREGROUND
		XCB_GC_BACKGROUND
		XCB_GC_LINE_WIDTH
		XCB_GC_LINE_STYLE
		XCB_GC_CAP_STYLE
		XCB_GC_JOIN_STYLE
		XCB_GC_FILL_STYLE
		XCB_GC_FILL_RULE
		XCB_GC_TILE
		XCB_GC_STIPPLE
		XCB_GC_TILE_STIPPLE_ORIGIN_X
		XCB_GC_TILE_STIPPLE_ORIGIN_Y
		XCB_GC_FONT
		XCB_GC_SUBWINDOW_MODE
		XCB_GC_GRAPHICS_EXPOSURES
		XCB_GC_CLIP_ORIGIN_X
		XCB_GC_CLIP_ORIGIN_Y
		XCB_GC_CLIP_MASK
		XCB_GC_DASH_OFFSET
		XCB_GC_DASH_LIST
		XCB_GC_ARC_MODE

It is possible to set several attributes at the same time (for example setting the attributes of a font and the color which will be used to display a string), by OR'ing these values in valuemask. Then valuelist has to be an array which lists the value for the respective attributes. These values must be in the same order as masks listed above. See Subsection Drawing with a color to have an example.

TODO: set the links of the 3 subsections, once they will be written :)

TODO: give an example which sets several attributes.


### 3. Drawing primitives: point, line, box, circle,...
After we have created a Graphic Context, we can draw on a window using this Graphic Context, with a set of XCB functions, collectively called "drawing primitives". Let see how they are used.

To draw a point, or several points, we use:

		xcb_void_cookie_t xcb_poly_point (xcb_connection_t  *c,               /* The connection to the X server */
										  uint8_t            coordinate_mode, /* Coordinate mode, usually set to XCB_COORD_MODE_ORIGIN */
										  xcb_drawable_t     drawable,        /* The drawable on which we want to draw the point(s) */
										  xcb_gcontext_t     gc,              /* The Graphic Context we use to draw the point(s) */
										  uint32_t           points_len,      /* The number of points */
										  const xcb_point_t *points );         /* An array of points */

The coordinate_mode parameter specifies the coordinate mode. Available values are:

		XCB_COORD_MODE_ORIGIN
		XCB_COORD_MODE_PREVIOUS

If XCB\_COORD\_MODE\_PREVIOUS is used, then all points but the first one are relative to the immediately previous point.

The xcb\_point\_t type is just a structure with two fields (the coordinates of the point):

		typedef struct {
			int16_t x;
			int16_t y;
		} xcb_point_t;

You could see an example in xpoints.c. TODO Set the link.

To draw a line, or a polygonal line, we use:

		xcb_void_cookie_t xcb_poly_line (xcb_connection_t  *c,               /* The connection to the X server */
										 uint8_t            coordinate_mode, /* Coordinate mode, usually set to XCB_COORD_MODE_ORIGIN */
										 xcb_drawable_t     drawable,        /* The drawable on which we want to draw the line(s) */
										 xcb_gcontext_t     gc,              /* The Graphic Context we use to draw the line(s) */
										 uint32_t           points_len,      /* The number of points in the polygonal line */
										 const xcb_point_t *points );        /* An array of points */

This function will draw the line between the first and the second points, then the line between the second and the third points, and so on.

To draw a segment, or several segments, we use:

		xcb_void_cookie_t xcb_poly_segment (xcb_connection_t    *c,              /* The connection to the X server */
											xcb_drawable_t       drawable,       /* The drawable on which we want to draw the segment(s) */
											xcb_gcontext_t       gc,             /* The Graphic Context we use to draw the segment(s) */
											uint32_t             segments_len,   /* The number of segments */
											const xcb_segment_t *segments );     /* An array of segments */

The xcb\_segment\_t type is just a structure with four fields (the coordinates of the two points that define the segment):

		typedef struct {
			int16_t x1;
			int16_t y1;
			int16_t x2;
			int16_t y2;
		} xcb_segment_t;

To draw a rectangle, or several rectangles, we use:

		xcb_void_cookie_t xcb_poly_rectangle (xcb_connection_t      *c,              /* The connection to the X server */
											  xcb_drawable_t         drawable,       /* The drawable on which we want to draw the rectangle(s) */
											  xcb_gcontext_t         gc,             /* The Graphic Context we use to draw the rectangle(s) */
											  uint32_t               rectangles_len, /* The number of rectangles */
											  const xcb_rectangle_t *rectangles );   /* An array of rectangles */

The xcb\_rectangle\_t type is just a structure with four fields (the coordinates of the top-left corner of the rectangle, and its width and height):

		typedef struct {
			int16_t  x;
			int16_t  y;
			uint16_t width;
			uint16_t height;
		} xcb_rectangle_t;

To draw an elliptical arc, or several elliptical arcs, we use:

        xcb_void_cookie_t xcb_poly_arc (xcb_connection_t *c,          /* The connection to the X server */
                                        xcb_drawable_t    drawable,   /* The drawable on which we want to draw the arc(s) */
                                        xcb_gcontext_t    gc,         /* The Graphic Context we use to draw the arc(s) */
                                        uint32_t          arcs_len,   /* The number of arcs */
                                        const xcb_arc_t  *arcs );     /* An array of arcs */

The xcb\_arc\_t type is a structure with six fields:

        typedef struct {
            int16_t  x;       /* Top left x coordinate of the rectangle surrounding the ellipse */
            int16_t  y;       /* Top left y coordinate of the rectangle surrounding the ellipse */
            uint16_t width;   /* Width of the rectangle surrounding the ellipse */
            uint16_t height;  /* Height of the rectangle surrounding the ellipse */
            int16_t  angle1;  /* Angle at which the arc begins */
            int16_t  angle2;  /* Angle at which the arc ends */
        } xcb_arc_t;

Note: the angles are expressed in units of 1/64 of a degree, so to have an angle of 90 degrees, starting at 0, angle1 = 0 and angle2 = 90 << 6. Positive angles indicate counterclockwise motion, while negative angles indicate clockwise motion.

The corresponding function which fill inside the geometrical object are listed below, without further explanation, as they are used as the above functions.

To Fill a polygon defined by the points given as arguments , we use

        xcb_void_cookie_t xcb_fill_poly (xcb_connection_t  *c,
                                         xcb_drawable_t     drawable,
                                         xcb_gcontext_t     gc,
                                         uint8_t            shape,
                                         uint8_t            coordinate_mode,
                                         uint32_t           points_len,
                                         const xcb_point_t *points );

The shape parameter specifies a shape that helps the server to improve performance. Available values are:

		XCB_POLY_SHAPE_COMPLEX
		XCB_POLY_SHAPE_NONCONVEX
		XCB_POLY_SHAPE_CONVEX

To fill one or several rectangles, we use:

		xcb_void_cookie_t xcb_poly_fill_rectangle (xcb_connection_t      *c,
												   xcb_drawable_t         drawable,
												   xcb_gcontext_t         gc,
												   uint32_t               rectangles_len,
												   const xcb_rectangle_t *rectangles );

To fill one or several arcs, we use:

		xcb_void_cookie_t xcb_poly_fill_arc (xcb_connection_t *c,
											 xcb_drawable_t    drawable,
											 xcb_gcontext_t    gc,
											 uint32_t          arcs_len,
											 const xcb_arc_t  *arcs );

To illustrate these functions, here is an example that draws four points, a polygonal line, two segments, two rectangles and two arcs. Remark that we use events for the first time, as an introduction to the next section.

TODO: Use screen->root_depth for depth parameter.

		#include <stdlib.h>
		#include <stdio.h>

		#include <xcb/xcb.h>

		int
		main ()
		{
			/* geometric objects */
			xcb_point_t          points[] = {
				{10, 10},
				{10, 20},
				{20, 10},
				{20, 20}};

			xcb_point_t          polyline[] = {
				{50, 10},
				{ 5, 20},     /* rest of points are relative */
				{25,-20},
				{10, 10}};

			xcb_segment_t        segments[] = {
				{100, 10, 140, 30},
				{110, 25, 130, 60}};

			xcb_rectangle_t      rectangles[] = {
				{ 10, 50, 40, 20},
				{ 80, 50, 10, 40}};

			xcb_arc_t            arcs[] = {
				{10, 100, 60, 40, 0, 90 << 6},
				{90, 100, 55, 40, 0, 270 << 6}};


			/* Open the connection to the X server */
			xcb_connection_t *connection = xcb_connect (NULL, NULL);

			/* Get the first screen */
			xcb_screen_t *screen = xcb_setup_roots_iterator (xcb_get_setup (connection)).data;

			/* Create black (foreground) graphic context */
			xcb_drawable_t  window     = screen->root;
			xcb_gcontext_t  foreground = xcb_generate_id (connection);
			uint32_t        mask       = XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES;
			uint32_t        values[2]  = {screen->black_pixel, 0};

			xcb_create_gc (connection, foreground, window, mask, values);


			/* Create a window */

			window = xcb_generate_id (connection);

			mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
			values[0] = screen->white_pixel;
			values[1] = XCB_EVENT_MASK_EXPOSURE;

			xcb_create_window (connection,                    /* connection          */
							   XCB_COPY_FROM_PARENT,          /* depth               */
							   window,                        /* window Id           */
							   screen->root,                  /* parent window       */
							   0, 0,                          /* x, y                */
							   150, 150,                      /* width, height       */
							   10,                            /* border_width        */
							   XCB_WINDOW_CLASS_INPUT_OUTPUT, /* class               */
							   screen->root_visual,           /* visual              */
							   mask, values );                /* masks */


			/* Map the window on the screen and flush*/
			xcb_map_window (connection, window);
			xcb_flush (connection);
			
			
			/* draw primitives */

			xcb_generic_event_t *event;
			while (event = xcb_wait_for_event (connection)) {
				switch (event->response_type & ~0x80) {
				case XCB_EXPOSE:
					/* We draw the points */
					xcb_poly_point (connection, XCB_COORD_MODE_ORIGIN, window, foreground, 4, points);

					/* We draw the polygonal line */
					xcb_poly_line (connection, XCB_COORD_MODE_PREVIOUS, window, foreground, 4, polyline);

					/* We draw the segements */
					xcb_poly_segment (connection, window, foreground, 2, segments);

					/* draw the rectangles */
					xcb_poly_rectangle (connection, window, foreground, 2, rectangles);

					/* draw the arcs */
					xcb_poly_arc (connection, window, foreground, 2, arcs);

					/* flush the request */
					xcb_flush (connection);

					break;
				default: 
					/* Unknown event type, ignore it */
					break;
				}

				free (event);
			}

			return 0;
		}