]> git.sur5r.net Git - i3/i3/blob - mainx.c
Implement more moving
[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 static void move_current_window(xcb_connection_t *connection, direction_t direction) {
642         printf("moving window to direction %d\n", direction);
643         /* Get current window */
644         Container *container = CUR_CELL;
645
646         /* There has to be a container, see focus_window() */
647         assert(container != NULL);
648
649         /* If there is no window, we’re done */
650         if (container->currently_focused == NULL)
651                 return;
652
653         Client *current_client = container->currently_focused;
654
655         Container *new;
656         int new_current_col = current_col,
657             new_current_row = current_row;
658         /* As soon as the client is moved away, the next client in the old
659          * container needs to get focus, if any. Therefore, we save it here. */
660         Client *to_focus = CIRCLEQ_NEXT(current_client, clients);
661         if (to_focus == CIRCLEQ_END(&(container->clients)))
662                 to_focus = NULL;
663
664         switch (direction) {
665                 case D_LEFT:
666                         printf("moving left\n");
667                         if (current_col == 0)
668                                 return;
669
670                         new = table[--new_current_col][current_row];
671                         break;
672                 case D_RIGHT:
673                         printf("ok, moving right\n");
674                         if (current_col == (table_dims.x-1)) {
675                                 printf("need to expand\n");
676                                 expand_table_cols();
677                         }
678
679                         new = table[++new_current_col][current_row];
680                         break;
681                 case D_UP:
682                         printf("moving up\n");
683                         Client *prev = CIRCLEQ_PREV(current_client, clients);
684                         if (prev != CIRCLEQ_END(&(container->clients))) {
685                                 printf("i can do that\n");
686                                 /* We can move the client inside its current container */
687                                 CIRCLEQ_REMOVE(&(container->clients), current_client, clients);
688                                 CIRCLEQ_INSERT_BEFORE(&(container->clients), prev, current_client, clients);
689                                 render_layout(connection);
690                                 return;
691                         }
692
693                         if (current_row == 0)
694                                 return;
695
696                         new = table[current_col][--new_current_row];
697                         break;
698                 case D_DOWN:
699                         printf("moving down\n");
700                         Client *next = CIRCLEQ_NEXT(current_client, clients);
701                         if (next != CIRCLEQ_END(&(container->clients))) {
702                                 printf("i can do that\n");
703                                 /* We can move the client inside its current container */
704                                 CIRCLEQ_REMOVE(&(container->clients), current_client, clients);
705                                 CIRCLEQ_INSERT_AFTER(&(container->clients), next, current_client, clients);
706                                 render_layout(connection);
707                                 return;
708                         }
709
710                         /* We need to create a new container or push the client
711                            into the container below */
712                         if (current_row == (table_dims.y-1)) {
713                                 printf("creating a new container\n");
714                                 expand_table_rows();
715                         }
716
717                         new = table[current_col][++new_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         current_col = new_current_col;
733         current_row = new_current_row;
734
735         render_layout(connection);
736 }
737
738 int format_event(xcb_generic_event_t *e)
739 {           
740     uint8_t sendEvent;
741     uint16_t seqnum;
742
743     sendEvent = (e->response_type & 0x80) ? 1 : 0;
744     e->response_type &= ~0x80;
745     seqnum = *((uint16_t *) e + 1);
746
747     switch(e->response_type) 
748     {   
749     case 0:
750         printf("Error %s on seqnum %d (%s).\n",
751             labelError[*((uint8_t *) e + 1)],
752             seqnum,
753             labelRequest[*((uint8_t *) e + 10)]);
754         break;
755     default:
756         printf("Event %s following seqnum %d%s.\n",
757             labelEvent[e->response_type],
758             seqnum,
759             labelSendEvent[sendEvent]);
760         break;  
761     case XCB_KEYMAP_NOTIFY:
762         printf("Event %s%s.\n",
763             labelEvent[e->response_type],
764             labelSendEvent[sendEvent]);
765         break;
766     }
767
768     fflush(stdout);
769     return 1;
770 }
771
772
773 static int handleEvent(void *ignored, xcb_connection_t *c, xcb_generic_event_t *e)
774 {
775         return format_event(e);
776 }
777
778 /*
779  * Starts the given application with the given args.
780  *
781  */
782 static void start_application(char *path, char *args) {
783         pid_t pid;
784         if ((pid = vfork()) == 0) {
785                 /* This is the child */
786                 char *argv[2];
787                 /* TODO: For now, we ignore args. Later on, they should be parsed
788                    correctly (like in the shell?) */
789                 argv[0] = path;
790                 argv[1] = NULL;
791                 execve(path, argv, environment);
792                 /* not reached */
793         }
794 }
795
796 /*
797  * There was a key press. We lookup the key symbol and see if there are any bindings
798  * on that. This allows to do things like binding special characters (think of ä) to
799  * functions to get one more modifier while not losing AltGr :-)
800  *
801  */
802 static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press_event_t *event) {
803
804         /* FIXME: We need to translate the keypress + state into a string (like, ä)
805            because they do not generate keysyms (use xev and see for yourself) */
806
807         printf("oh yay!\n");
808         printf("gots press %d\n", event->detail);
809         printf("i'm in state %d\n", event->state);
810
811         /* 30 = u
812          * 57 = n
813          * 27 = r
814          * 28 = t
815          * 40 = d
816          *
817          * …uhm, I should probably say that I’ve remapped my keys in hardware :)
818          */
819         direction_t direction;
820         if (event->detail == 30) {
821                 /* 'u' */
822                 start_application(TERMINAL, NULL);
823                 return 1;
824         } else if (event->detail == 57) {
825                 direction = D_LEFT;
826         } else if (event->detail == 27) {
827                 direction = D_DOWN;
828         } else if (event->detail == 28) {
829                 direction = D_UP;
830         } else if (event->detail == 40) {
831                 direction = D_RIGHT;
832         } else {
833                 printf("don't want this.\n");
834                 return 1;
835         }
836
837         /* TODO: ctrl -> focus_container(conn, direction) */
838         /* FIXME: actually wrong but i'm too lazy to grab my keys all the time */
839         if (event->state & XCB_MOD_MASK_CONTROL) {
840                 move_current_window(conn, direction);
841         } else if (event->state & XCB_MOD_MASK_1)
842                 focus_window(conn, direction);
843         /* TODO: shift -> move_current_window(conn, direction) */
844         /* TODO: shift + ctrl -> move_current_container(conn, direction) */
845
846         return 1;
847 }
848
849 /*
850  * When the user moves the mouse pointer onto a window, this callback gets called.
851  *
852  */
853 static int handle_enter_notify(void *ignored, xcb_connection_t *conn, xcb_enter_notify_event_t *event) {
854         /* This was either a focus for a client’s parent (= titlebar)… */
855         Client *client = table_get(byParent, event->event),
856                *old_client;
857         /* …or the client itself */
858         if (client == NULL)
859                 client = table_get(byChild, event->event);
860
861         /* If not, then this event is not interesting. This should not happen */
862         if (client == NULL) {
863                 printf("DEBUG: Uninteresting enter_notify-event?\n");
864                 return 1;
865         }
866
867         /* Update container */
868         old_client = client->container->currently_focused;
869         client->container->currently_focused = client;
870
871         /* Set focus to the entered window, and flush xcb buffer immediately */
872         xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, client->child, XCB_CURRENT_TIME);
873         /* Update last/current client’s titlebar */
874         if (old_client != NULL)
875                 decorate_window(conn, old_client);
876         decorate_window(conn, client);
877         xcb_flush(conn);
878
879         return 1;
880 }
881
882 int handle_map_notify_event(void *prophs, xcb_connection_t *c, xcb_map_notify_event_t *e)
883 {
884         window_attributes_t wa = { TAG_VALUE };
885         wa.u.override_redirect = e->override_redirect;
886         printf("MapNotify for 0x%08x.\n", e->window);
887         manage_window(prophs, c, e->window, wa);
888         return 1;
889 }
890
891 /*
892  * Our window decorations were unmapped. That means, the window will be killed now,
893  * so we better clean up before.
894  *
895  */
896 int handle_unmap_notify_event(void *data, xcb_connection_t *c, xcb_unmap_notify_event_t *e) {
897         Client *client = table_remove(byChild, e->event);
898         xcb_window_t root;
899         printf("UnmapNotify for 0x%08x (received from 0x%08x): ", e->window, e->event);
900         if(!client)
901         {
902                 printf("not a managed window. Ignoring.\n");
903                 return 0;
904         }
905
906         int rows, cols;
907         Client *con_client;
908         for (cols = 0; cols < table_dims.x; cols++)
909                 for (rows = 0; rows < table_dims.y; rows++)
910                         CIRCLEQ_FOREACH(con_client, &(table[cols][rows]->clients), clients)
911                                 if (con_client == client) {
912                                         printf("removing from container\n");
913                                         if (client->container->currently_focused == client)
914                                                 client->container->currently_focused = NULL;
915                                         CIRCLEQ_REMOVE(&(table[cols][rows]->clients), con_client, clients);
916                                         break;
917                                 }
918
919
920
921         root = xcb_setup_roots_iterator(xcb_get_setup(c)).data->root;
922         printf("child of 0x%08x.\n", client->frame);
923         xcb_reparent_window(c, client->child, root, 0, 0);
924         xcb_destroy_window(c, client->frame);
925         xcb_flush(c);
926         table_remove(byParent, client->frame);
927         free(client);
928
929         render_layout(c);
930
931         return 1;
932 }
933
934
935
936 static int handleExposeEvent(void *data, xcb_connection_t *c, xcb_expose_event_t *e) {
937 printf("exposeevent\n");
938         Client *client = table_get(byParent, e->window);
939         if(!client || e->count != 0)
940                 return 1;
941         decorate_window(c, client);
942         return 1;
943 }
944 void manage_existing_windows(xcb_connection_t *c, xcb_property_handlers_t *prophs, xcb_window_t root) {
945         xcb_query_tree_cookie_t wintree;
946         xcb_query_tree_reply_t *rep;
947         int i, len;
948         xcb_window_t *children;
949         xcb_get_window_attributes_cookie_t *cookies;
950
951         wintree = xcb_query_tree(c, root);
952         rep = xcb_query_tree_reply(c, wintree, 0);
953         if(!rep)
954                 return;
955         len = xcb_query_tree_children_length(rep);
956         cookies = malloc(len * sizeof(*cookies));
957         if(!cookies)
958         {
959                 free(rep);
960                 return;
961         }
962         children = xcb_query_tree_children(rep);
963         for(i = 0; i < len; ++i)
964                 cookies[i] = xcb_get_window_attributes(c, children[i]);
965         for(i = 0; i < len; ++i)
966         {
967                 window_attributes_t wa = { TAG_COOKIE, { cookies[i] } };
968                 manage_window(prophs, c, children[i], wa);
969         }
970         free(rep);
971 }
972
973 int main(int argc, char *argv[], char *env[]) {
974         int i, e = 0;
975
976         for (i = 0; (env[i] != NULL); i++)
977                 if (strncmp(env[i], "LC_", strlen("LC_")) == 0 ||
978                         strncmp(env[i], "LANG=", strlen("LANG=")) == 0 ||
979                         strncmp(env[i], "DISPLAY=", strlen("DISPLAY=")) == 0) {
980                         printf("Passing environment \"%s\"\n", env[i]);
981                         environment = realloc(environment, sizeof(char*) * ++e);
982                         environment[e-1] = env[i];
983                 }
984
985         /* environment has to be NULL-terminated */
986         environment = realloc(environment, sizeof(char*) * ++e);
987         environment[e-1] = NULL;
988
989         init_table();
990
991         xcb_connection_t *c;
992         xcb_event_handlers_t evenths;
993         xcb_property_handlers_t prophs;
994         xcb_window_t root;
995
996         int screens;
997
998         memset(&evenths, 0, sizeof(xcb_event_handlers_t));
999         memset(&prophs, 0, sizeof(xcb_property_handlers_t));
1000
1001         byChild = alloc_table();
1002         byParent = alloc_table();
1003
1004         c = xcb_connect(NULL, &screens);
1005
1006         printf("x screen is %d\n", screens);
1007
1008         /* Font loading */
1009         myfont = load_font(c, pattern);
1010
1011         xcb_event_handlers_init(c, &evenths);
1012         for(i = 2; i < 128; ++i)
1013                 xcb_event_set_handler(&evenths, i, handleEvent, 0);
1014
1015         for(i = 0; i < 256; ++i)
1016                 xcb_event_set_error_handler(&evenths, i, (xcb_generic_error_handler_t) handleEvent, 0);
1017
1018         /* Expose = an Application should redraw itself. That is, we have to redraw our
1019          * contents (= top/bottom bar, titlebars for each window) */
1020         xcb_event_set_expose_handler(&evenths, handleExposeEvent, 0);
1021
1022         /* Key presses are pretty obvious, I think */
1023         xcb_event_set_key_press_handler(&evenths, handle_key_press, 0);
1024
1025         /* Enter window = user moved his mouse over the window */
1026         xcb_event_set_enter_notify_handler(&evenths, handle_enter_notify, 0);
1027
1028         xcb_event_set_unmap_notify_handler(&evenths, handle_unmap_notify_event, 0);
1029
1030         xcb_property_handlers_init(&prophs, &evenths);
1031         xcb_event_set_map_notify_handler(&evenths, handle_map_notify_event, &prophs);
1032
1033         root = xcb_aux_get_screen(c, screens)->root;
1034         root_win = root;
1035
1036         uint32_t mask = XCB_CW_EVENT_MASK;
1037         uint32_t values[] = { XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE };
1038         xcb_change_window_attributes(c, root, mask, values);
1039
1040         /* Grab 'a' */
1041         //xcb_grab_key(c, 0, root, 0, 38, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
1042
1043         xcb_grab_key(c, 0, root, 0, 30, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
1044         xcb_grab_key(c, 0, root, XCB_MOD_MASK_1, 57, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
1045         xcb_grab_key(c, 0, root, XCB_MOD_MASK_1, 28, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
1046         xcb_grab_key(c, 0, root, XCB_MOD_MASK_1, 27, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
1047         xcb_grab_key(c, 0, root, XCB_MOD_MASK_1, 40, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
1048
1049         xcb_grab_key(c, 0, root, XCB_MOD_MASK_CONTROL, 57, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
1050         xcb_grab_key(c, 0, root, XCB_MOD_MASK_CONTROL, 28, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
1051         xcb_grab_key(c, 0, root, XCB_MOD_MASK_CONTROL, 27, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
1052         xcb_grab_key(c, 0, root, XCB_MOD_MASK_CONTROL, 40, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
1053
1054
1055         //xcb_grab_key(c, 0, root, XCB_BUTTON_MASK_ANY, 40, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
1056         start_application(TERMINAL, NULL);
1057
1058         xcb_flush(c);
1059
1060         manage_existing_windows(c, &prophs, root);
1061
1062         xcb_event_wait_for_event_loop(&evenths);
1063
1064         /* not reached */
1065         return 0;
1066 }