]> git.sur5r.net Git - i3/i3/blob - mainx.c
Add correct environment-handling (pass LC_*, LANG, DISPLAY), add -rpath for NetBSD
[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 RGB color code
305  *
306  */
307 uint32_t get_colorpixel(xcb_connection_t *conn, xcb_window_t window, int r, int g, int b) {
308         xcb_screen_t *root_screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data;
309
310         xcb_colormap_t colormapId = xcb_generate_id(conn);
311         xcb_create_colormap(conn, XCB_COLORMAP_ALLOC_NONE, colormapId, window, root_screen->root_visual);
312         xcb_alloc_color_reply_t *reply = xcb_alloc_color_reply(conn, xcb_alloc_color(conn, colormapId, r, g, b), NULL);
313
314         if (!reply) {
315                 printf("color fail\n");
316                 exit(1);
317         }
318
319         uint32_t pixel = reply->pixel;
320         free(reply);
321         return pixel;
322 }
323
324 /*
325  * (Re-)draws window decorations for a given Client
326  *
327  */
328 void decorate_window(xcb_connection_t *conn, Client *client) {
329         uint32_t mask = 0;
330         uint32_t values[3];
331         xcb_screen_t *root_screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data;
332
333         mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT;
334
335         Font *font = load_font(conn, pattern);
336
337         values[0] = root_screen->black_pixel;
338         if (client->container->currently_focused == client) {
339                 printf("oh, currently active = %p\n", client);
340                 values[1] = get_colorpixel(conn, client->window, 65535, 0, 0);
341         }else values[1] = get_colorpixel(conn, client->window, 0, 0, 65535);
342         values[2] = font->id;
343
344         xcb_change_gc(conn, client->titlegc, mask, values);
345
346
347         /* TODO: utf8? */
348         //char *label = "i3 rocks :>";
349         char *label;
350         asprintf(&label, "gots win %08x", client->window);
351         xcb_void_cookie_t textCookie = xcb_image_text_8_checked (conn, strlen (label), client->window, client->titlegc, 2, 2 + font->height, label );
352 }
353
354 void render_container(xcb_connection_t *connection, Container *container) {
355         Client *client;
356         uint32_t values[4];
357         uint32_t mask = XCB_CONFIG_WINDOW_X |
358                         XCB_CONFIG_WINDOW_Y |
359                         XCB_CONFIG_WINDOW_WIDTH |
360                         XCB_CONFIG_WINDOW_HEIGHT;
361
362
363         if (container->mode == MODE_DEFAULT) {
364                 int num_clients = 0;
365                 CIRCLEQ_FOREACH(client, &(container->clients), clients)
366                         num_clients++;
367                 printf("got %d clients in this default container.\n", num_clients);
368
369                 int current_client = 0;
370                 CIRCLEQ_FOREACH(client, &(container->clients), clients) {
371                         /* TODO: at the moment, every column/row is 200px. This
372                          * needs to be changed to "percentage of the screen" by
373                          * default and adjustable by the user if necessary.
374                          */
375                         values[0] = container->col * container->width; /* x */
376                         values[1] = container->row * container->height +
377                                 (container->height / num_clients) * current_client; /* y */
378                         /* TODO: vertical default layout */
379                         values[2] = container->width; /* width */
380                         values[3] = container->height / num_clients; /* height */
381                         printf("frame will be at %dx%d with size %dx%d\n",
382                                         values[0], values[1], values[2], values[3]);
383
384                         /* TODO: update only if necessary */
385                         xcb_configure_window(connection, client->window, mask, values);
386
387                         /* The coordinates of the child are relative to its frame */
388                         values[0] = 2;
389                         values[1] = 20;
390                         values[2] -= 2;
391                         values[3] -= 20;
392                         printf("child itself will be at %dx%d with size %dx%d\n",
393                                         values[0], values[1], values[2], values[3]);
394
395                         xcb_configure_window(connection, client->child, mask, values);
396
397                         decorate_window(connection, client);
398                         current_client++;
399                 }
400         } else {
401                 /* TODO: Implement stacking */
402         }
403 }
404
405 void render_layout(xcb_connection_t *conn) {
406         int cols, rows;
407         xcb_screen_t *root_screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data;
408         int width = root_screen->width_in_pixels;
409         int height = root_screen->height_in_pixels;
410
411         int num_cols = table_dims.x, num_rows = table_dims.y;
412
413         printf("got %d rows and %d cols\n", num_rows, num_cols);
414         printf("each of them therefore is %d px width and %d px height\n",
415                         width / num_cols, height / num_rows);
416
417         /* Go through the whole table and render what’s necessary */
418         for (cols = 0; cols < table_dims.x; cols++)
419                 for (rows = 0; rows < table_dims.y; rows++)
420                         if (table[cols][rows] != NULL) {
421                                 /* Update position of the container */
422                                 table[cols][rows]->row = rows;
423                                 table[cols][rows]->col = cols;
424                                 table[cols][rows]->width = width / num_cols;
425                                 table[cols][rows]->height = height / num_rows;
426
427                                 /* Render it */
428                                 render_container(conn, table[cols][rows]);
429                         }
430
431         xcb_flush(conn);
432 }
433
434 /*
435  * Let’s own this window…
436  *
437  */
438 void reparent_window(xcb_connection_t *conn, xcb_window_t child,
439                 xcb_visualid_t visual, xcb_window_t root, uint8_t depth,
440                 int16_t x, int16_t y, uint16_t width, uint16_t height) {
441
442         Client *new = table_get(byChild, child);
443         if (new == NULL) {
444                 printf("oh, it's new\n");
445                 new = calloc(sizeof(Client), 1);
446         }
447         uint32_t mask = 0;
448         uint32_t values[3];
449         xcb_screen_t *root_screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data;
450
451         /* Insert into the currently active container */
452         CIRCLEQ_INSERT_TAIL(&(CUR_CELL->clients), new, clients);
453
454         printf("currently_focused = %p\n", new);
455         CUR_CELL->currently_focused = new;
456         new->container = CUR_CELL;
457
458         new->window = xcb_generate_id(conn);
459         new->child = child;
460
461         /* TODO: what do these mean? */
462         mask |= XCB_CW_BACK_PIXEL;
463         values[0] = root_screen->white_pixel;
464
465         mask |= XCB_CW_OVERRIDE_REDIRECT;
466         values[1] = 1;
467
468         mask |= XCB_CW_EVENT_MASK;
469         values[2] = XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE
470                 | XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_ENTER_WINDOW;
471
472         printf("Reparenting 0x%08x under 0x%08x.\n", child, new->window);
473
474         /* Yo dawg, I heard you like windows, so I create a window around your window… */
475         xcb_create_window(conn,
476                         depth,
477                         new->window,
478                         root,
479                         x,
480                         y,
481                         width + LEFT + RIGHT,
482                         height + TOP + BOTTOM,
483                         /* border_width */ 0,
484                         XCB_WINDOW_CLASS_INPUT_OUTPUT,
485                         visual,
486                         mask,
487                         values);
488         xcb_change_save_set(conn, XCB_SET_MODE_INSERT, child);
489
490         /* Map the window on the screen (= make it visible) */
491         xcb_map_window(conn, new->window);
492
493         /* Generate a graphics context for the titlebar */
494         new->titlegc = xcb_generate_id(conn);
495         xcb_create_gc(conn, new->titlegc, new->window, 0, 0);
496
497         /* Draw decorations */
498         decorate_window(conn, new);
499
500         /* Put our data structure (Client) into the table */
501         table_put(byParent, new->window, new);
502         table_put(byChild, child, new);
503
504         /* Moves the original window into the new frame we've created for it */
505         /* TODO: hmm, LEFT/TOP needs to go */
506         xcb_reparent_window(conn, child, new->window, LEFT - 1, TOP - 1);
507
508         /* We are interested in property changes */
509         mask = XCB_CW_EVENT_MASK;
510         values[0] =     XCB_EVENT_MASK_PROPERTY_CHANGE |
511                         XCB_EVENT_MASK_STRUCTURE_NOTIFY |
512                         XCB_EVENT_MASK_ENTER_WINDOW;
513         xcb_change_window_attributes(conn, child, mask, values);
514
515         /* TODO: At the moment, new windows just get focus */
516         xcb_set_input_focus(conn, XCB_INPUT_FOCUS_NONE, new->window, XCB_CURRENT_TIME);
517
518         render_layout(conn);
519
520         xcb_flush(conn);
521 }
522
523 static bool focus_window_in_container(xcb_connection_t *connection, Container *container,
524                 direction_t direction) {
525         /* If this container is empty, we’re done */
526         if (container->currently_focused == NULL)
527                 return false;
528
529         Client *candidad;
530         if (direction == D_UP)
531                 candidad = CIRCLEQ_PREV(container->currently_focused, clients);
532         else if (direction == D_DOWN)
533                 candidad = CIRCLEQ_NEXT(container->currently_focused, clients);
534
535         /* If we don’t have anything to select, we’re done */
536         if (candidad == CIRCLEQ_END(&(container->clients)))
537                 return false;
538
539         /* Set focus if we could successfully move */
540         container->currently_focused = candidad;
541         xcb_set_input_focus(connection, XCB_INPUT_FOCUS_NONE, candidad->child, XCB_CURRENT_TIME);
542         render_layout(connection);
543         xcb_flush(connection);
544
545         return true;
546 }
547
548 static void focus_window(xcb_connection_t *connection, direction_t direction) {
549         printf("focusing direction %d\n", direction);
550         /* TODO: for horizontal default layout, this has to be expanded to LEFT/RIGHT */
551         if (direction == D_UP || direction == D_DOWN) {
552                 /* Let’s see if we can perform up/down focus in the current container */
553                 Container *container = CUR_CELL;
554
555                 /* There always is a container. If not, current_col or current_row is wrong */
556                 assert(container != NULL);
557
558                 if (focus_window_in_container(connection, container, direction))
559                         return;
560         } else if (direction == D_LEFT || direction == D_RIGHT) {
561                 if (direction == D_RIGHT && cell_exists(current_col+1, current_row))
562                         current_col++;
563                 else if (direction == D_LEFT && cell_exists(current_col-1, current_row))
564                         current_col--;
565                 else {
566                         printf("nah, not possible\n");
567                         return;
568                 }
569                 if (CUR_CELL->currently_focused != NULL) {
570                         xcb_set_input_focus(connection, XCB_INPUT_FOCUS_NONE,
571                                         CUR_CELL->currently_focused->child, XCB_CURRENT_TIME);
572                         render_layout(connection);
573                         xcb_flush(connection);
574                 }
575
576         } else {
577                 printf("direction unhandled\n");
578         }
579 }
580
581 static void move_current_window(xcb_connection_t *connection, direction_t direction) {
582         printf("moving window to direction %d\n", direction);
583         /* Get current window */
584         Container *container = CUR_CELL;
585
586         /* There has to be a container, see focus_window() */
587         assert(container != NULL);
588
589         /* If there is no window, we’re done */
590         if (container->currently_focused == NULL)
591                 return;
592
593         Client *current_client = container->currently_focused;
594
595         if (direction == D_RIGHT) {
596                 printf("ok, moving right\n");
597                 expand_table_cols();
598
599                 Container *new = table[current_col+1][current_row];
600
601                 /* As soon as the client is moved away, the next client in the old
602                  * container needs to get focus, if any. Therefore, we save it here. */
603                 Client *to_focus = CIRCLEQ_NEXT(current_client, clients);
604                 if (to_focus == CIRCLEQ_END(&(container->clients)))
605                         to_focus = NULL;
606
607                 /* Remove it from the old container and put it into the new one */
608                 CIRCLEQ_REMOVE(&(container->clients), current_client, clients);
609                 CIRCLEQ_INSERT_TAIL(&(new->clients), current_client, clients);
610
611                 /* Update data structures */
612                 current_client->container = new;
613                 container->currently_focused = to_focus;
614                 new->currently_focused = current_client;
615
616                 current_col++;
617
618                 printf("done\n");
619         }
620
621         render_layout(connection);
622 }
623
624 int format_event(xcb_generic_event_t *e)
625 {           
626     uint8_t sendEvent;
627     uint16_t seqnum;
628
629     sendEvent = (e->response_type & 0x80) ? 1 : 0;
630     e->response_type &= ~0x80;
631     seqnum = *((uint16_t *) e + 1);
632
633     switch(e->response_type) 
634     {   
635     case 0:
636         printf("Error %s on seqnum %d (%s).\n",
637             labelError[*((uint8_t *) e + 1)],
638             seqnum,
639             labelRequest[*((uint8_t *) e + 10)]);
640         break;
641     default:
642         printf("Event %s following seqnum %d%s.\n",
643             labelEvent[e->response_type],
644             seqnum,
645             labelSendEvent[sendEvent]);
646         break;  
647     case XCB_KEYMAP_NOTIFY:
648         printf("Event %s%s.\n",
649             labelEvent[e->response_type],
650             labelSendEvent[sendEvent]);
651         break;
652     }
653
654     fflush(stdout);
655     return 1;
656 }
657
658
659 static int handleEvent(void *ignored, xcb_connection_t *c, xcb_generic_event_t *e)
660 {
661         return format_event(e);
662 }
663
664 /*
665  * Starts the given application with the given args.
666  *
667  */
668 static void start_application(char *path, char *args) {
669         pid_t pid;
670         if ((pid = vfork()) == 0) {
671                 /* This is the child */
672                 char *argv[2];
673                 /* TODO: For now, we ignore args. Later on, they should be parsed
674                    correctly (like in the shell?) */
675                 argv[0] = path;
676                 argv[1] = NULL;
677                 execve(path, argv, environment);
678                 /* not reached */
679         }
680 }
681
682 /*
683  * There was a key press. We lookup the key symbol and see if there are any bindings
684  * on that. This allows to do things like binding special characters (think of ä) to
685  * functions to get one more modifier while not losing AltGr :-)
686  *
687  */
688 static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press_event_t *event) {
689
690         /* FIXME: We need to translate the keypress + state into a string (like, ä)
691            because they do not generate keysyms (use xev and see for yourself) */
692
693         printf("oh yay!\n");
694         printf("gots press %d\n", event->detail);
695         printf("i'm in state %d\n", event->state);
696
697         /* 30 = u
698          * 57 = n
699          * 27 = r
700          * 28 = t
701          * 40 = d
702          *
703          * …uhm, I should probably say that I’ve remapped my keys in hardware :)
704          */
705         direction_t direction;
706         if (event->detail == 30) {
707                 /* 'u' */
708                 start_application(TERMINAL, NULL);
709                 return 1;
710         } else if (event->detail == 57) {
711                 direction = D_LEFT;
712         } else if (event->detail == 27) {
713                 direction = D_DOWN;
714         } else if (event->detail == 28) {
715                 direction = D_UP;
716         } else if (event->detail == 40) {
717                 direction = D_RIGHT;
718         } else {
719                 printf("don't want this.\n");
720                 return 1;
721         }
722
723         /* TODO: ctrl -> focus_container(conn, direction) */
724         /* FIXME: actually wrong but i'm too lazy to grab my keys all the time */
725         if (event->state & XCB_MOD_MASK_CONTROL) {
726                 move_current_window(conn, direction);
727         } else
728
729         focus_window(conn, direction);
730         /* TODO: shift -> move_current_window(conn, direction) */
731         /* TODO: shift + ctrl -> move_current_container(conn, direction) */
732
733         return 1;
734 }
735
736 /*
737  * When the user moves the mouse pointer onto a window, this callback gets called.
738  *
739  */
740 static int handle_enter_notify(void *ignored, xcb_connection_t *conn, xcb_enter_notify_event_t *event) {
741         /* This was either a focus for a client’s parent (= titlebar)… */
742         Client *client = table_get(byParent, event->event);
743         /* …or the client itself */
744         if (client == NULL)
745                 client = table_get(byChild, event->event);
746
747         /* If not, then this event is not interesting. This should not happen */
748         if (client == NULL) {
749                 printf("DEBUG: Uninteresting enter_notify-event?\n");
750                 return 1;
751         }
752
753         /* Update container */
754         client->container->currently_focused = client;
755
756         /* Set focus to the entered window, and flush xcb buffer immediately */
757         xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, client->child, XCB_CURRENT_TIME);
758         xcb_flush(conn);
759
760         return 1;
761 }
762
763 int handle_map_notify_event(void *prophs, xcb_connection_t *c, xcb_map_notify_event_t *e)
764 {
765         window_attributes_t wa = { TAG_VALUE };
766         wa.u.override_redirect = e->override_redirect;
767         printf("MapNotify for 0x%08x.\n", e->window);
768         manage_window(prophs, c, e->window, wa);
769         return 1;
770 }
771
772 /*
773  * Our window decorations were unmapped. That means, the window will be killed now,
774  * so we better clean up before.
775  *
776  */
777 int handle_unmap_notify_event(void *data, xcb_connection_t *c, xcb_unmap_notify_event_t *e) {
778         Client *client = table_remove(byChild, e->event);
779         xcb_window_t root;
780         printf("UnmapNotify for 0x%08x (received from 0x%08x): ", e->window, e->event);
781         if(!client)
782         {
783                 printf("not a managed window. Ignoring.\n");
784                 return 0;
785         }
786
787         int rows, cols;
788         Client *con_client;
789         for (cols = 0; cols < table_dims.x; cols++)
790                 for (rows = 0; rows < table_dims.y; rows++)
791                         CIRCLEQ_FOREACH(con_client, &(table[cols][rows]->clients), clients)
792                                 if (con_client == client) {
793                                         printf("removing from container\n");
794                                         CIRCLEQ_REMOVE(&(table[cols][rows]->clients), con_client, clients);
795                                         break;
796                                 }
797
798
799
800         root = xcb_setup_roots_iterator(xcb_get_setup(c)).data->root;
801         printf("child of 0x%08x.\n", client->window);
802         xcb_reparent_window(c, client->child, root, 0, 0);
803         xcb_destroy_window(c, client->window);
804         xcb_flush(c);
805         table_remove(byParent, client->window);
806         free(client);
807
808         render_layout(c);
809
810         return 1;
811 }
812
813
814
815 static int handleExposeEvent(void *data, xcb_connection_t *c, xcb_expose_event_t *e) {
816 printf("exposeevent\n");
817         Client *client = table_get(byParent, e->window);
818         if(!client || e->count != 0)
819                 return 1;
820         decorate_window(c, client);
821         return 1;
822 }
823 void manage_existing_windows(xcb_connection_t *c, xcb_property_handlers_t *prophs, xcb_window_t root) {
824         xcb_query_tree_cookie_t wintree;
825         xcb_query_tree_reply_t *rep;
826         int i, len;
827         xcb_window_t *children;
828         xcb_get_window_attributes_cookie_t *cookies;
829
830         wintree = xcb_query_tree(c, root);
831         rep = xcb_query_tree_reply(c, wintree, 0);
832         if(!rep)
833                 return;
834         len = xcb_query_tree_children_length(rep);
835         cookies = malloc(len * sizeof(*cookies));
836         if(!cookies)
837         {
838                 free(rep);
839                 return;
840         }
841         children = xcb_query_tree_children(rep);
842         for(i = 0; i < len; ++i)
843                 cookies[i] = xcb_get_window_attributes(c, children[i]);
844         for(i = 0; i < len; ++i)
845         {
846                 window_attributes_t wa = { TAG_COOKIE, { cookies[i] } };
847                 manage_window(prophs, c, children[i], wa);
848         }
849         free(rep);
850 }
851
852 int main(int argc, char *argv[], char *env[]) {
853         int i, e = 0;
854
855         for (i = 0; (env[i] != NULL); i++)
856                 if (strncmp(env[i], "LC_", strlen("LC_")) == 0 ||
857                         strncmp(env[i], "LANG=", strlen("LANG=")) == 0 ||
858                         strncmp(env[i], "DISPLAY=", strlen("DISPLAY=")) == 0) {
859                         printf("Passing environment \"%s\"\n", env[i]);
860                         environment = realloc(environment, sizeof(char*) * ++e);
861                         environment[e-1] = env[i];
862                 }
863
864         /* environment has to be NULL-terminated */
865         environment = realloc(environment, sizeof(char*) * ++e);
866         environment[e-1] = NULL;
867
868         init_table();
869
870         xcb_connection_t *c;
871         xcb_event_handlers_t evenths;
872         xcb_property_handlers_t prophs;
873         xcb_window_t root;
874
875         int screens;
876
877         memset(&evenths, 0, sizeof(xcb_event_handlers_t));
878         memset(&prophs, 0, sizeof(xcb_property_handlers_t));
879
880         byChild = alloc_table();
881         byParent = alloc_table();
882
883         c = xcb_connect(NULL, &screens);
884
885         printf("x screen is %d\n", screens);
886
887         /* Font loading */
888
889         myfont = load_font(c, pattern);
890
891         xcb_event_handlers_init(c, &evenths);
892         for(i = 2; i < 128; ++i)
893                 xcb_event_set_handler(&evenths, i, handleEvent, 0);
894
895
896         for(i = 0; i < 256; ++i)
897                 xcb_event_set_error_handler(&evenths, i, (xcb_generic_error_handler_t) handleEvent, 0);
898
899         /* Expose = an Application should redraw itself. That is, we have to redraw our
900          * contents (= top/bottom bar, titlebars for each window) */
901         xcb_event_set_expose_handler(&evenths, handleExposeEvent, 0);
902
903         /* Key presses are pretty obvious, I think */
904         xcb_event_set_key_press_handler(&evenths, handle_key_press, 0);
905
906         /* Enter window = user moved his mouse over the window */
907         xcb_event_set_enter_notify_handler(&evenths, handle_enter_notify, 0);
908
909         xcb_event_set_unmap_notify_handler(&evenths, handle_unmap_notify_event, 0);
910
911         xcb_property_handlers_init(&prophs, &evenths);
912         xcb_event_set_map_notify_handler(&evenths, handle_map_notify_event, &prophs);
913
914
915         root = xcb_aux_get_screen(c, screens)->root;
916         root_win = root;
917
918         uint32_t mask = XCB_CW_EVENT_MASK;
919         uint32_t values[] = { XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE };
920         xcb_change_window_attributes(c, root, mask, values);
921
922         /* Grab 'a' */
923         //xcb_grab_key(c, 0, root, 0, 38, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
924
925         xcb_grab_key(c, 0, root, 0, 30, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
926         xcb_grab_key(c, 0, root, XCB_BUTTON_MASK_ANY, 57, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
927         xcb_grab_key(c, 0, root, XCB_BUTTON_MASK_ANY, 28, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
928         xcb_grab_key(c, 0, root, XCB_BUTTON_MASK_ANY, 27, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
929         xcb_grab_key(c, 0, root, XCB_BUTTON_MASK_ANY, 40, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
930
931         //xcb_grab_key(c, 0, root, XCB_BUTTON_MASK_ANY, 40, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
932         start_application(TERMINAL, NULL);
933
934         xcb_flush(c);
935
936         manage_existing_windows(c, &prophs, root);
937
938         xcb_event_wait_for_event_loop(&evenths);
939
940         /* not reached */
941         return 0;
942 }