]> git.sur5r.net Git - i3/i3/blob - mainx.c
20ed2f5252a63769d94da9a81f9e750586b93122
[i3/i3] / mainx.c
1 #define _GNU_SOURCE
2 #include <stdio.h>
3 #include <assert.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <sys/types.h>
7 #include <unistd.h>
8 #include <stdbool.h>
9 #include <assert.h>
10
11 #include <xcb/xcb.h>
12
13 #include "xcb_wm.h"
14 #include "xcb_aux.h"
15 #include "xcb_event.h"
16 #include "xcb_property.h"
17 #include "xcb_keysyms.h"
18 #include "data.h"
19
20 #include "queue.h"
21
22 Font myfont;
23
24 static const int TOP = 20;
25 static const int LEFT = 5;
26 static const int BOTTOM = 5;
27 static const int RIGHT = 5;
28
29 /* hm, xcb_wm wants us to implement this. */
30 table_t *byChild = 0;
31 table_t *byParent = 0;
32 xcb_window_t root_win;
33
34 /* _the_ table. Stores all clients. */
35 Container *table[10][10];
36
37 int current_col = 0;
38 int current_row = 0;
39
40
41 int globalc = 0;
42
43
44 static const char *labelError[] = {
45     "Success",
46     "BadRequest",
47     "BadValue",
48     "BadWindow",
49     "BadPixmap",
50     "BadAtom",
51     "BadCursor",
52     "BadFont",
53     "BadMatch",
54     "BadDrawable",
55     "BadAccess",
56     "BadAlloc",
57     "BadColor",
58     "BadGC",
59     "BadIDChoice",
60     "BadName",
61     "BadLength",
62     "BadImplementation",
63 };
64
65 static const char *labelRequest[] = {
66     "no request",
67     "CreateWindow",
68     "ChangeWindowAttributes",
69     "GetWindowAttributes",
70     "DestroyWindow",
71     "DestroySubwindows",
72     "ChangeSaveSet",
73     "ReparentWindow",
74     "MapWindow",
75     "MapSubwindows",
76     "UnmapWindow",
77     "UnmapSubwindows",
78     "ConfigureWindow",
79     "CirculateWindow",
80     "GetGeometry",
81     "QueryTree",
82     "InternAtom",
83     "GetAtomName",
84     "ChangeProperty",
85     "DeleteProperty",
86     "GetProperty",
87     "ListProperties",
88     "SetSelectionOwner",
89     "GetSelectionOwner",
90     "ConvertSelection",
91     "SendEvent",
92     "GrabPointer",
93     "UngrabPointer",
94     "GrabButton",
95     "UngrabButton",
96     "ChangeActivePointerGrab",
97     "GrabKeyboard",
98     "UngrabKeyboard",
99     "GrabKey",
100     "UngrabKey",
101     "AllowEvents",
102     "GrabServer",
103     "UngrabServer",
104     "QueryPointer",
105     "GetMotionEvents",
106     "TranslateCoords",
107     "WarpPointer",
108     "SetInputFocus",
109     "GetInputFocus",
110     "QueryKeymap",
111     "OpenFont",
112     "CloseFont",
113     "QueryFont",
114     "QueryTextExtents",
115     "ListFonts",
116     "ListFontsWithInfo",
117     "SetFontPath",
118     "GetFontPath",
119     "CreatePixmap",
120     "FreePixmap",
121     "CreateGC",
122     "ChangeGC",
123     "CopyGC",
124     "SetDashes",
125     "SetClipRectangles",
126     "FreeGC",
127     "ClearArea",
128     "CopyArea",
129     "CopyPlane",
130     "PolyPoint",
131     "PolyLine",
132     "PolySegment",
133     "PolyRectangle",
134     "PolyArc",
135     "FillPoly",
136     "PolyFillRectangle",
137     "PolyFillArc",
138     "PutImage",
139     "GetImage",
140     "PolyText",
141     "PolyText",
142     "ImageText",
143     "ImageText",
144     "CreateColormap",
145     "FreeColormap",
146     "CopyColormapAndFree",
147     "InstallColormap",
148     "UninstallColormap",
149     "ListInstalledColormaps",
150     "AllocColor",
151     "AllocNamedColor",
152     "AllocColorCells",
153     "AllocColorPlanes",
154     "FreeColors",
155     "StoreColors",
156     "StoreNamedColor",
157     "QueryColors",
158     "LookupColor",
159     "CreateCursor",
160     "CreateGlyphCursor",
161     "FreeCursor",
162     "RecolorCursor",
163     "QueryBestSize",
164     "QueryExtension",
165     "ListExtensions",
166     "ChangeKeyboardMapping",
167     "GetKeyboardMapping",
168     "ChangeKeyboardControl",
169     "GetKeyboardControl",
170     "Bell",
171     "ChangePointerControl",
172     "GetPointerControl",
173     "SetScreenSaver",
174     "GetScreenSaver",
175     "ChangeHosts",
176     "ListHosts",
177     "SetAccessControl",
178     "SetCloseDownMode",
179     "KillClient",
180     "RotateProperties",
181     "ForceScreenSaver",
182     "SetPointerMapping",
183     "GetPointerMapping",
184     "SetModifierMapping",
185     "GetModifierMapping",
186     "major 120",
187     "major 121",
188     "major 122",
189     "major 123",
190     "major 124",
191     "major 125",
192     "major 126",
193     "NoOperation",
194 };
195
196 static const char *labelEvent[] = {
197     "error",
198     "reply",
199     "KeyPress",
200     "KeyRelease",
201     "ButtonPress",
202     "ButtonRelease",
203     "MotionNotify",
204     "EnterNotify",
205     "LeaveNotify",
206     "FocusIn",
207     "FocusOut",
208     "KeymapNotify",
209     "Expose",
210     "GraphicsExpose",
211     "NoExpose",
212     "VisibilityNotify",
213     "CreateNotify",
214     "DestroyNotify",
215     "UnmapNotify",
216     "MapNotify",
217     "MapRequest",
218     "ReparentNotify",
219     "ConfigureNotify",
220     "ConfigureRequest",
221     "GravityNotify",
222     "ResizeRequest",
223     "CirculateNotify",
224     "CirculateRequest",
225     "PropertyNotify",
226     "SelectionClear",
227     "SelectionRequest",
228     "SelectionNotify",
229     "ColormapNotify",
230     "ClientMessage",
231     "MappingNotify",
232 };
233
234 static const char *labelSendEvent[] = {
235     "",
236     " (from SendEvent)",
237 };
238
239 /*
240  *
241  * TODO: what exactly does this, what happens if we leave stuff out?
242  *
243  */
244 void manage_window(xcb_property_handlers_t *prophs, xcb_connection_t *c, xcb_window_t window, window_attributes_t wa)
245 {
246         printf("managing window.\n");
247         xcb_drawable_t d = { window };
248         xcb_get_geometry_cookie_t geomc;
249         xcb_get_geometry_reply_t *geom;
250         xcb_get_window_attributes_reply_t *attr = 0;
251         if(wa.tag == TAG_COOKIE)
252         {
253                 attr = xcb_get_window_attributes_reply(c, wa.u.cookie, 0);
254                 if(!attr)
255                         return;
256                 if(attr->map_state != XCB_MAP_STATE_VIEWABLE)
257                 {
258                         printf("Window 0x%08x is not mapped. Ignoring.\n", window);
259                         free(attr);
260                         return;
261                 }
262                 wa.tag = TAG_VALUE;
263                 wa.u.override_redirect = attr->override_redirect;
264         }
265         if(!wa.u.override_redirect && table_get(byChild, window))
266         {
267                 printf("Window 0x%08x already managed. Ignoring.\n", window);
268                 free(attr);
269                 return;
270         }
271         if(wa.u.override_redirect)
272         {
273                 printf("Window 0x%08x has override-redirect set. Ignoring.\n", window);
274                 free(attr);
275                 return;
276         }
277         geomc = xcb_get_geometry(c, d);
278         if(!attr)
279         {
280                 wa.tag = TAG_COOKIE;
281                 wa.u.cookie = xcb_get_window_attributes(c, window);
282                 attr = xcb_get_window_attributes_reply(c, wa.u.cookie, 0);
283         }
284         geom = xcb_get_geometry_reply(c, geomc, 0);
285         if(attr && geom)
286         {
287                 reparent_window(c, window, attr->visual, geom->root, geom->depth, geom->x, geom->y, geom->width, geom->height);
288                 xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_NAME);
289         }
290         free(attr);
291         free(geom);
292 }
293
294
295 /*
296  * Returns the colorpixel to use for the given RGB color code
297  *
298  */
299 uint32_t get_colorpixel(xcb_connection_t *conn, xcb_window_t window, int r, int g, int b) {
300         xcb_screen_t *root_screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data;
301
302         xcb_colormap_t colormapId = xcb_generate_id(conn);
303         xcb_create_colormap(conn, XCB_COLORMAP_ALLOC_NONE, colormapId, window, root_screen->root_visual);
304         xcb_alloc_color_reply_t *reply = xcb_alloc_color_reply(conn, xcb_alloc_color(conn, colormapId, r, g, b), NULL);
305
306         if (!reply) {
307                 printf("color fail\n");
308                 exit(1);
309         }
310
311         uint32_t pixel = reply->pixel;
312         free(reply);
313         return pixel;
314 }
315
316 /*
317  * (Re-)draws window decorations for a given Client
318  *
319  */
320 void decorate_window(xcb_connection_t *conn, Client *client) {
321         uint32_t mask = 0;
322         uint32_t values[3];
323         xcb_screen_t *root_screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data;
324
325         mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT;
326
327         xcb_font_t font = xcb_generate_id (conn);
328         char *font_name = "-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso8859-1";
329         xcb_void_cookie_t fontCookie = xcb_open_font_checked (conn, font, strlen (font_name), font_name ); 
330
331         values[0] = root_screen->black_pixel;
332         if (globalc++ > 1)
333                 values[1] = get_colorpixel(conn, client->window, 65535, 0, 0);
334         else values[1] = get_colorpixel(conn, client->window, 0, 0, 65535);
335         values[2] = font;
336
337         xcb_change_gc(conn, client->titlegc, mask, values);
338
339
340         /* TODO: utf8? */
341         //char *label = "i3 rocks :>";
342         char *label;
343         asprintf(&label, "gots win %08x", client->window);
344         xcb_void_cookie_t textCookie = xcb_image_text_8_checked (conn, strlen (label), client->window, client->titlegc, 2, 15, label );
345 }
346
347 void render_container(xcb_connection_t *connection, Container *container) {
348         Client *client;
349         uint32_t values[4];
350         uint32_t mask = XCB_CONFIG_WINDOW_X |
351                         XCB_CONFIG_WINDOW_Y |
352                         XCB_CONFIG_WINDOW_WIDTH |
353                         XCB_CONFIG_WINDOW_HEIGHT;
354
355
356         if (container->mode == MODE_DEFAULT) {
357                 int num_clients = 0;
358                 CIRCLEQ_FOREACH(client, &(container->clients), clients)
359                         num_clients++;
360                 printf("got %d clients in this default container.\n", num_clients);
361
362                 int current_client = 0;
363                 CIRCLEQ_FOREACH(client, &(container->clients), clients) {
364                         /* TODO: at the moment, every column/row is 200px. This
365                          * needs to be changed to "percentage of the screen" by
366                          * default and adjustable by the user if necessary.
367                          */
368                         values[0] = container->col * container->width; /* x */
369                         values[1] = container->row * container->height +
370                                 (container->height / num_clients) * current_client; /* y */
371                         /* TODO: vertical default layout */
372                         values[2] = container->width; /* width */
373                         values[3] = container->height / num_clients; /* height */
374                         printf("frame will be at %dx%d with size %dx%d\n",
375                                         values[0], values[1], values[2], values[3]);
376
377                         /* TODO: update only if necessary */
378                         xcb_configure_window(connection, client->window, mask, values);
379
380                         /* The coordinates of the child are relative to its frame */
381                         values[0] = 2;
382                         values[1] = 20;
383                         values[2] -= 2;
384                         values[3] -= 20;
385                         printf("child itself will be at %dx%d with size %dx%d\n",
386                                         values[0], values[1], values[2], values[3]);
387
388                         xcb_configure_window(connection, client->child, mask, values);
389
390                         decorate_window(connection, client);
391                         current_client++;
392                 }
393         } else {
394                 /* TODO: Implement stacking */
395         }
396 }
397
398 void render_layout(xcb_connection_t *conn) {
399         int cols, rows;
400         xcb_screen_t *root_screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data;
401         int width = root_screen->width_in_pixels;
402         int height = root_screen->height_in_pixels;
403
404         int num_cols = 0, num_rows = 0;
405         bool row_has_content;
406         for (rows = 0; rows < 10; rows++) {
407                 row_has_content = false;
408                 for (cols = 0; cols < 10; cols++)
409                         if (table[cols][rows] != NULL) {
410                                 num_cols++;
411                                 row_has_content = true;
412                         }
413                 if (row_has_content)
414                         num_rows++;
415         }
416
417         printf("got %d rows and %d cols\n", num_rows, num_cols);
418         printf("each of them therefore is %d px width and %d px height\n",
419                         width / num_cols, height / num_rows);
420
421         /* Go through the whole table and render what’s necessary */
422         for (rows = 0; rows < 10; rows++)
423                 for (cols = 0; cols < 10; cols++)
424                         if (table[cols][rows] != NULL) {
425                                 /* Update position of the container */
426                                 table[cols][rows]->row = rows;
427                                 table[cols][rows]->col = cols;
428                                 table[cols][rows]->width = width / num_cols;
429                                 table[cols][rows]->height = height / num_rows;
430
431                                 /* Render it */
432                                 render_container(conn, table[cols][rows]);
433                         }
434 }
435
436 /*
437  * Let’s own this window…
438  *
439  */
440 void reparent_window(xcb_connection_t *conn, xcb_window_t child,
441                 xcb_visualid_t visual, xcb_window_t root, uint8_t depth,
442                 int16_t x, int16_t y, uint16_t width, uint16_t height)
443 {
444
445         Client *new = table_get(byChild, child);
446         if (new == NULL) {
447                 printf("oh, it's new\n");
448                 new = calloc(sizeof(Client), 1);
449         }
450         uint32_t mask = 0;
451         uint32_t values[3];
452         xcb_screen_t *root_screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data;
453
454         /* Insert into the currently active container */
455         CIRCLEQ_INSERT_TAIL(&(table[current_col][current_row]->clients), new, clients);
456
457         printf("currently_focused = %p\n", new);
458         table[current_col][current_row]->currently_focused = new;
459         new->container = table[current_col][current_row];
460
461         new->window = xcb_generate_id(conn);
462         new->child = child;
463
464         /* TODO: what do these mean? */
465         mask |= XCB_CW_BACK_PIXEL;
466         values[0] = root_screen->white_pixel;
467
468         mask |= XCB_CW_OVERRIDE_REDIRECT;
469         values[1] = 1;
470
471         mask |= XCB_CW_EVENT_MASK;
472         values[2] = XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE
473                 | XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_ENTER_WINDOW;
474
475         printf("Reparenting 0x%08x under 0x%08x.\n", child, new->window);
476
477         /* Yo dawg, I heard you like windows, so I create a window around your window… */
478         xcb_create_window(conn,
479                         depth,
480                         new->window,
481                         root,
482                         x,
483                         y,
484                         width + LEFT + RIGHT,
485                         height + TOP + BOTTOM,
486                         /* border_width */ 0,
487                         XCB_WINDOW_CLASS_INPUT_OUTPUT,
488                         visual,
489                         mask,
490                         values);
491         xcb_change_save_set(conn, XCB_SET_MODE_INSERT, child);
492
493         /* Map the window on the screen (= make it visible) */
494         xcb_map_window(conn, new->window);
495
496         /* Generate a graphics context for the titlebar */
497         new->titlegc = xcb_generate_id(conn);
498         xcb_create_gc(conn, new->titlegc, new->window, 0, 0);
499
500         /* Draw decorations */
501         decorate_window(conn, new);
502
503         /* Put our data structure (Client) into the table */
504         table_put(byParent, new->window, new);
505         table_put(byChild, child, new);
506
507         /* Moves the original window into the new frame we've created for it */
508         /* TODO: hmm, LEFT/TOP needs to go */
509         xcb_reparent_window(conn, child, new->window, LEFT - 1, TOP - 1);
510
511         /* We are interested in property changes */
512         mask = XCB_CW_EVENT_MASK;
513         values[0] =     XCB_EVENT_MASK_PROPERTY_CHANGE |
514                         XCB_EVENT_MASK_STRUCTURE_NOTIFY |
515                         XCB_EVENT_MASK_ENTER_WINDOW;
516         xcb_change_window_attributes(conn, child, mask, values);
517
518         /* TODO: At the moment, new windows just get focus */
519         xcb_set_input_focus(conn, XCB_INPUT_FOCUS_NONE, new->window, XCB_CURRENT_TIME);
520
521         render_layout(conn);
522
523         xcb_flush(conn);
524 }
525
526 static bool focus_window_in_container(xcb_connection_t *connection, Container *container,
527                 direction_t direction) {
528         /* If this container is empty, we’re done */
529         if (container->currently_focused == NULL)
530                 return false;
531
532         Client *candidad;
533         if (direction == D_UP)
534                 candidad = CIRCLEQ_PREV(container->currently_focused, clients);
535         else if (direction == D_DOWN)
536                 candidad = CIRCLEQ_NEXT(container->currently_focused, clients);
537
538         /* If we don’t have anything to select, we’re done */
539         if (candidad == CIRCLEQ_END(&(container->clients)))
540                 return false;
541
542         /* Set focus if we could successfully move */
543         container->currently_focused = candidad;
544         xcb_set_input_focus(connection, XCB_INPUT_FOCUS_NONE, candidad->child, XCB_CURRENT_TIME);
545         xcb_flush(connection);
546
547         return true;
548 }
549
550 static void focus_window(xcb_connection_t *connection, direction_t direction) {
551         printf("focusing direction %d\n", direction);
552         /* TODO: for horizontal default layout, this has to be expanded to LEFT/RIGHT */
553         if (direction == D_UP || direction == D_DOWN) {
554                 /* Let’s see if we can perform up/down focus in the current container */
555                 Container *container = table[current_col][current_row];
556
557                 /* There always is a container. If not, current_col or current_row is wrong */
558                 assert(container != NULL);
559
560                 if (focus_window_in_container(connection, container, direction))
561                         return;
562         } else {
563                 printf("direction unhandled\n");
564         }
565 }
566
567
568 int format_event(xcb_generic_event_t *e)
569 {           
570     uint8_t sendEvent;
571     uint16_t seqnum;
572
573     sendEvent = (e->response_type & 0x80) ? 1 : 0;
574     e->response_type &= ~0x80;
575     seqnum = *((uint16_t *) e + 1);
576
577     switch(e->response_type) 
578     {   
579     case 0:
580         printf("Error %s on seqnum %d (%s).\n",
581             labelError[*((uint8_t *) e + 1)],
582             seqnum,
583             labelRequest[*((uint8_t *) e + 10)]);
584         break;
585     default:
586         printf("Event %s following seqnum %d%s.\n",
587             labelEvent[e->response_type],
588             seqnum,
589             labelSendEvent[sendEvent]);
590         break;  
591     case XCB_KEYMAP_NOTIFY:
592         printf("Event %s%s.\n",
593             labelEvent[e->response_type],
594             labelSendEvent[sendEvent]);
595         break;
596     }
597
598     fflush(stdout);
599     return 1;
600 }
601
602
603 static int handleEvent(void *ignored, xcb_connection_t *c, xcb_generic_event_t *e)
604 {
605         return format_event(e);
606 }
607
608 /*
609  * There was a key press. We lookup the key symbol and see if there are any bindings
610  * on that. This allows to do things like binding special characters (think of ä) to
611  * functions to get one more modifier while not losing AltGr :-)
612  *
613  */
614 static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press_event_t *event) {
615
616         /* FIXME: We need to translate the keypress + state into a string (like, ä)
617            because they do not generate keysyms (use xev and see for yourself) */
618
619         printf("oh yay!\n");
620         printf("gots press %d\n", event->detail);
621         printf("i'm in state %d\n", event->state);
622
623         /* 30 = u
624          * 57 = n
625          * 27 = r
626          * 28 = t
627          * 40 = d
628          *
629          * …uhm, I should probably say that I’ve remapped my keys in hardware :)
630          */
631         direction_t direction;
632         if (event->detail == 30) {
633                 /* 'u' */
634                 pid_t pid;
635                 if ((pid = vfork()) == 0) {
636                         /* Child */
637                         /* TODO: what environment do we need to pass? */
638                         char *env[2];
639                         env[0] = "DISPLAY=:1";
640                         env[1] = NULL;
641                         char *argv[2];
642                         argv[0] = "/usr/bin/xterm";
643                         argv[1] = NULL;
644                         execve("/usr/bin/xterm", argv, env);
645                         /* not reached */
646                 }
647                 return 1;
648         } else if (event->detail == 57) {
649                 direction = D_LEFT;
650         } else if (event->detail == 27) {
651                 direction = D_DOWN;
652         } else if (event->detail == 28) {
653                 direction = D_UP;
654         } else if (event->detail == 40) {
655                 direction = D_RIGHT;
656         }
657
658         /* TODO: ctrl -> focus_container(conn, direction) */
659         focus_window(conn, direction);
660         /* TODO: shift -> move_current_window(conn, direction) */
661         /* TODO: shift + ctrl -> move_current_container(conn, direction) */
662
663         return 1;
664 }
665
666 /*
667  * When the user moves the mouse pointer onto a window, this callback gets called.
668  *
669  */
670 static int handle_enter_notify(void *ignored, xcb_connection_t *conn, xcb_enter_notify_event_t *event) {
671         /* This was either a focus for a client’s parent (= titlebar)… */
672         Client *client = table_get(byParent, event->event);
673         /* …or the client itself */
674         if (client == NULL)
675                 client = table_get(byChild, event->event);
676
677         /* If not, then this event is not interesting. This should not happen */
678         if (client == NULL) {
679                 printf("DEBUG: Uninteresting enter_notify-event?\n");
680                 return 1;
681         }
682
683         /* Update container */
684         client->container->currently_focused = client;
685
686         /* Set focus to the entered window, and flush xcb buffer immediately */
687         xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, client->child, XCB_CURRENT_TIME);
688         xcb_flush(conn);
689
690         return 1;
691 }
692
693 int handle_map_notify_event(void *prophs, xcb_connection_t *c, xcb_map_notify_event_t *e)
694 {
695         window_attributes_t wa = { TAG_VALUE };
696         wa.u.override_redirect = e->override_redirect;
697         printf("MapNotify for 0x%08x.\n", e->window);
698         manage_window(prophs, c, e->window, wa);
699         return 1;
700 }
701
702 /*
703  * Our window decorations were unmapped. That means, the window will be killed now,
704  * so we better clean up before.
705  *
706  */
707 int handle_unmap_notify_event(void *data, xcb_connection_t *c, xcb_unmap_notify_event_t *e) {
708         Client *client = table_remove(byChild, e->event);
709         xcb_window_t root;
710         printf("UnmapNotify for 0x%08x (received from 0x%08x): ", e->window, e->event);
711         if(!client)
712         {
713                 printf("not a managed window. Ignoring.\n");
714                 return 0;
715         }
716
717         /* TODO: make a list of containers to run through */
718         int rows, cols;
719         Client *con_client;
720         for (rows = 0; rows < 10; rows++)
721                 for (cols = 0; cols < 10; cols++)
722                         if (table[cols][rows] != NULL)
723                                 CIRCLEQ_FOREACH(con_client, &(table[cols][rows]->clients), clients)
724                                         if (con_client == client) {
725                                                 printf("removing from container\n");
726                                                 CIRCLEQ_REMOVE(&(table[cols][rows]->clients), con_client, clients);
727                                                 break;
728                                         }
729
730
731
732         root = xcb_setup_roots_iterator(xcb_get_setup(c)).data->root;
733         printf("child of 0x%08x.\n", client->window);
734         xcb_reparent_window(c, client->child, root, 0, 0);
735         xcb_destroy_window(c, client->window);
736         xcb_flush(c);
737         table_remove(byParent, client->window);
738         free(client);
739
740         render_layout(c);
741
742         return 1;
743 }
744
745
746
747 static int handleExposeEvent(void *data, xcb_connection_t *c, xcb_expose_event_t *e)
748 {
749 printf("exposeevent\n");
750         Client *client = table_get(byParent, e->window);
751         if(!client || e->count != 0)
752                 return 1;
753         decorate_window(c, client);
754         return 1;
755 }
756 void manage_existing_windows(xcb_connection_t *c, xcb_property_handlers_t *prophs, xcb_window_t root)
757 {
758         xcb_query_tree_cookie_t wintree;
759         xcb_query_tree_reply_t *rep;
760         int i, len;
761         xcb_window_t *children;
762         xcb_get_window_attributes_cookie_t *cookies;
763
764         wintree = xcb_query_tree(c, root);
765         rep = xcb_query_tree_reply(c, wintree, 0);
766         if(!rep)
767                 return;
768         len = xcb_query_tree_children_length(rep);
769         cookies = malloc(len * sizeof(*cookies));
770         if(!cookies)
771         {
772                 free(rep);
773                 return;
774         }
775         children = xcb_query_tree_children(rep);
776         for(i = 0; i < len; ++i)
777                 cookies[i] = xcb_get_window_attributes(c, children[i]);
778         for(i = 0; i < len; ++i)
779         {
780                 window_attributes_t wa = { TAG_COOKIE, { cookies[i] } };
781                 manage_window(prophs, c, children[i], wa);
782         }
783         free(rep);
784 }
785
786 int main() {
787         int i, j;
788         for (i = 0; i < 10; i++)
789                 for (j = 0; j < 10; j++)
790                         table[i][j] = NULL;
791
792         /*
793          * By default, the table is one row and one column big. It contains
794          * one container in default mode in it.
795          *
796          */
797         table[0][0] = calloc(sizeof(Container), 1);
798         CIRCLEQ_INIT(&(table[0][0]->clients));
799
800         xcb_connection_t *c;
801         xcb_event_handlers_t evenths;
802         xcb_property_handlers_t prophs;
803         xcb_window_t root;
804
805         int screens;
806
807         memset(&evenths, 0, sizeof(xcb_event_handlers_t));
808         memset(&prophs, 0, sizeof(xcb_property_handlers_t));
809
810         byChild = alloc_table();
811         byParent = alloc_table();
812
813         c = xcb_connect(NULL, &screens);
814
815         printf("x screen is %d\n", screens);
816
817         /* Font loading */
818
819 char *pattern = "-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso8859-1";
820
821 xcb_list_fonts_with_info_cookie_t cookie = xcb_list_fonts_with_info(c, 1, strlen(pattern), pattern);
822 xcb_list_fonts_with_info_reply_t *reply = xcb_list_fonts_with_info_reply(c, cookie, NULL);
823 if (!reply) {
824         printf("Could not load font\n");
825         return 1;
826 }
827
828 myfont.name = strdup(xcb_list_fonts_with_info_name(reply));
829 myfont.height = reply->font_ascent + reply->font_descent;
830
831
832         xcb_event_handlers_init(c, &evenths);
833         for(i = 2; i < 128; ++i)
834                 xcb_event_set_handler(&evenths, i, handleEvent, 0);
835
836
837         for(i = 0; i < 256; ++i)
838                 xcb_event_set_error_handler(&evenths, i, (xcb_generic_error_handler_t) handleEvent, 0);
839
840         /* Expose = an Application should redraw itself. That is, we have to redraw our
841          * contents (= top/bottom bar, titlebars for each window) */
842         xcb_event_set_expose_handler(&evenths, handleExposeEvent, 0);
843
844         /* Key presses are pretty obvious, I think */
845         xcb_event_set_key_press_handler(&evenths, handle_key_press, 0);
846
847         /* Enter window = user moved his mouse over the window */
848         xcb_event_set_enter_notify_handler(&evenths, handle_enter_notify, 0);
849
850         xcb_event_set_unmap_notify_handler(&evenths, handle_unmap_notify_event, 0);
851
852         xcb_property_handlers_init(&prophs, &evenths);
853         xcb_event_set_map_notify_handler(&evenths, handle_map_notify_event, &prophs);
854
855
856         root = xcb_aux_get_screen(c, screens)->root;
857         root_win = root;
858
859         uint32_t mask = XCB_CW_EVENT_MASK;
860         uint32_t values[] = { XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE };
861         xcb_change_window_attributes(c, root, mask, values);
862
863         /* Grab 'a' */
864         //xcb_grab_key(c, 0, root, 0, 38, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
865
866         xcb_grab_key(c, 0, root, 0, 30, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
867         xcb_grab_key(c, 0, root, 0, 57, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
868         xcb_grab_key(c, 0, root, 0, 28, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
869         xcb_grab_key(c, 0, root, 0, 27, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
870         xcb_grab_key(c, 0, root, 0, 40, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
871
872         //xcb_grab_key(c, 0, root, XCB_BUTTON_MASK_ANY, 40, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
873                 pid_t pid;
874                 if ((pid = vfork()) == 0) {
875                         /* Child */
876                         /* TODO: what environment do we need to pass? */
877                         char *env[2];
878                         env[0] = "DISPLAY=:1";
879                         env[1] = NULL;
880                         char *argv[3];
881                         argv[0] = "/usr/bin/xterm";
882                         argv[1] = NULL;
883                         execve("/usr/bin/xterm", argv, env);
884                 }
885
886
887         xcb_flush(c);
888
889         manage_existing_windows(c, &prophs, root);
890
891         xcb_event_wait_for_event_loop(&evenths);
892
893         /* not reached */
894         return 0;
895 }