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