Cleanups, documentation

This commit is contained in:
Michael Stapelberg 2009-02-24 01:24:28 +01:00
parent 0e3a378c39
commit 36aec13860
4 changed files with 122 additions and 85 deletions

View File

@ -18,12 +18,14 @@
#ifndef _I3_H #ifndef _I3_H
#define _I3_H #define _I3_H
#define NUM_ATOMS 9
extern Display *xkbdpy; extern Display *xkbdpy;
extern TAILQ_HEAD(bindings_head, Binding) bindings; extern TAILQ_HEAD(bindings_head, Binding) bindings;
extern SLIST_HEAD(stack_wins_head, Stack_Window) stack_wins; extern SLIST_HEAD(stack_wins_head, Stack_Window) stack_wins;
extern xcb_event_handlers_t evenths; extern xcb_event_handlers_t evenths;
extern char *pattern; extern char *pattern;
extern int num_screens; extern int num_screens;
extern xcb_atom_t atoms[9]; extern xcb_atom_t atoms[NUM_ATOMS];
#endif #endif

View File

@ -292,6 +292,10 @@ int handle_button_press(void *ignored, xcb_connection_t *conn, xcb_button_press_
return 1; return 1;
} }
/*
* A new window appeared on the screen (=was mapped), so lets manage it.
*
*/
int handle_map_notify_event(void *prophs, xcb_connection_t *conn, xcb_map_notify_event_t *event) { int handle_map_notify_event(void *prophs, xcb_connection_t *conn, xcb_map_notify_event_t *event) {
window_attributes_t wa = { TAG_VALUE }; window_attributes_t wa = { TAG_VALUE };
wa.u.override_redirect = event->override_redirect; wa.u.override_redirect = event->override_redirect;

View File

@ -154,7 +154,7 @@ static void reposition_client(xcb_connection_t *connection, Client *client) {
static void resize_client(xcb_connection_t *connection, Client *client) { static void resize_client(xcb_connection_t *connection, Client *client) {
i3Font *font = load_font(connection, pattern); i3Font *font = load_font(connection, pattern);
printf("resizing client \"%s\" to %d x %d\n", client->name, client->rect.width, client->rect.height); printf("resizing client to %d x %d\n", client->rect.width, client->rect.height);
xcb_configure_window(connection, client->frame, xcb_configure_window(connection, client->frame,
XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
&(client->rect.width)); &(client->rect.width));

View File

@ -41,74 +41,77 @@
#include "util.h" #include "util.h"
#include "xcb.h" #include "xcb.h"
#include "xinerama.h" #include "xinerama.h"
#include "i3.h"
#define TERMINAL "/usr/pkg/bin/urxvt" #define TERMINAL "/usr/pkg/bin/urxvt"
/* This is our connection to X11 for use with XKB */
Display *xkbdpy; Display *xkbdpy;
TAILQ_HEAD(bindings_head, Binding) bindings = TAILQ_HEAD_INITIALIZER(bindings); /* The list of key bindings */
SLIST_HEAD(stack_wins_head, Stack_Window) stack_wins = SLIST_HEAD_INITIALIZER(stack_wins); struct bindings_head bindings = TAILQ_HEAD_INITIALIZER(bindings);
xcb_event_handlers_t evenths;
xcb_window_t root_win; /* This is a list of Stack_Windows, global, for easier/faster access on expose events */
xcb_atom_t atoms[9]; struct stack_wins_head stack_wins = SLIST_HEAD_INITIALIZER(stack_wins);
/* The event handlers need to be global because they are accessed by our custom event handler
in handle_button_press(), needed for graphical resizing */
xcb_event_handlers_t evenths;
xcb_atom_t atoms[NUM_ATOMS];
char *pattern = "-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso8859-1"; char *pattern = "-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso8859-1";
int num_screens = 0; int num_screens = 0;
/* /*
* * Do some sanity checks and then reparent the window.
* TODO: what exactly does this, what happens if we leave stuff out?
* *
*/ */
void manage_window(xcb_property_handlers_t *prophs, xcb_connection_t *c, xcb_window_t window, window_attributes_t wa) void manage_window(xcb_property_handlers_t *prophs, xcb_connection_t *c, xcb_window_t window, window_attributes_t wa) {
{
printf("managing window.\n"); printf("managing window.\n");
xcb_drawable_t d = { window }; xcb_drawable_t d = { window };
xcb_get_geometry_cookie_t geomc; xcb_get_geometry_cookie_t geomc;
xcb_get_geometry_reply_t *geom; xcb_get_geometry_reply_t *geom;
xcb_get_window_attributes_reply_t *attr = 0; xcb_get_window_attributes_reply_t *attr = 0;
if(wa.tag == TAG_COOKIE)
{ if (wa.tag == TAG_COOKIE) {
attr = xcb_get_window_attributes_reply(c, wa.u.cookie, 0); /* Check if the window is mapped (it could be not mapped when intializing and
if(!attr) calling manage_window() for every window) */
if ((attr = xcb_get_window_attributes_reply(c, wa.u.cookie, 0)) == NULL)
return; return;
if(attr->map_state != XCB_MAP_STATE_VIEWABLE)
{ if (attr->map_state != XCB_MAP_STATE_VIEWABLE)
printf("Window 0x%08x is not mapped. Ignoring.\n", window); goto out;
free(attr);
return;
}
wa.tag = TAG_VALUE; wa.tag = TAG_VALUE;
wa.u.override_redirect = attr->override_redirect; wa.u.override_redirect = attr->override_redirect;
} }
if(!wa.u.override_redirect && table_get(byChild, window))
{ /* Check if the window is already managed */
printf("Window 0x%08x already managed. Ignoring.\n", window); if (!wa.u.override_redirect && table_get(byChild, window))
free(attr); goto out;
return;
} /* Dont manage clients with the override_redirect flag */
if(wa.u.override_redirect) if (wa.u.override_redirect)
{ goto out;
printf("Window 0x%08x has override-redirect set. Ignoring.\n", window);
free(attr); /* Get the initial geometry (position, size, …) */
return;
}
geomc = xcb_get_geometry(c, d); geomc = xcb_get_geometry(c, d);
if(!attr) if (!attr) {
{
wa.tag = TAG_COOKIE; wa.tag = TAG_COOKIE;
wa.u.cookie = xcb_get_window_attributes(c, window); wa.u.cookie = xcb_get_window_attributes(c, window);
attr = xcb_get_window_attributes_reply(c, wa.u.cookie, 0); attr = xcb_get_window_attributes_reply(c, wa.u.cookie, 0);
} }
geom = xcb_get_geometry_reply(c, geomc, 0); geom = xcb_get_geometry_reply(c, geomc, 0);
if(attr && geom) if (attr && geom) {
{ reparent_window(c, window, attr->visual, geom->root, geom->depth,
reparent_window(c, window, attr->visual, geom->root, geom->depth, geom->x, geom->y, geom->width, geom->height); geom->x, geom->y, geom->width, geom->height);
xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_NAME); xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_NAME);
} }
free(attr);
free(geom); free(geom);
out:
free(attr);
return;
} }
/* /*
@ -124,7 +127,7 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
xcb_get_property_cookie_t wm_type_cookie, strut_cookie; xcb_get_property_cookie_t wm_type_cookie, strut_cookie;
/* Place requests for propertys ASAP */ /* Place requests for properties ASAP */
wm_type_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_WINDOW_TYPE], UINT32_MAX); wm_type_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_WINDOW_TYPE], UINT32_MAX);
strut_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_STRUT_PARTIAL], UINT32_MAX); strut_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_STRUT_PARTIAL], UINT32_MAX);
@ -133,10 +136,7 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
/* TODO: When does this happen for existing clients? Is that a bug? */ /* TODO: When does this happen for existing clients? Is that a bug? */
printf("oh, it's new\n"); printf("oh, it's new\n");
new = calloc(sizeof(Client), 1); new = calloc(sizeof(Client), 1);
/* We initialize x and y with the invalid coordinates -1 so that they will new->force_reconfigure = true;
get updated at the next render_layout() at any case */
new->rect.x = -1;
new->rect.y = -1;
} }
uint32_t mask = 0; uint32_t mask = 0;
uint32_t values[3]; uint32_t values[3];
@ -243,33 +243,35 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
render_layout(conn); render_layout(conn);
} }
/*
* Go through all existing windows (if the window manager is restarted) and manage them
*
*/
void manage_existing_windows(xcb_connection_t *c, xcb_property_handlers_t *prophs, xcb_window_t root) { void manage_existing_windows(xcb_connection_t *c, xcb_property_handlers_t *prophs, xcb_window_t root) {
xcb_query_tree_cookie_t wintree; xcb_query_tree_reply_t *reply;
xcb_query_tree_reply_t *rep;
int i, len; int i, len;
xcb_window_t *children; xcb_window_t *children;
xcb_get_window_attributes_cookie_t *cookies; xcb_get_window_attributes_cookie_t *cookies;
wintree = xcb_query_tree(c, root); /* Get the tree of windows whose parent is the root window (= all) */
rep = xcb_query_tree_reply(c, wintree, 0); if ((reply = xcb_query_tree_reply(c, xcb_query_tree(c, root), 0)) == NULL)
if(!rep)
return; return;
len = xcb_query_tree_children_length(rep);
cookies = malloc(len * sizeof(*cookies)); len = xcb_query_tree_children_length(reply);
if(!cookies) cookies = smalloc(len * sizeof(*cookies));
{
free(rep); /* Request the window attributes for every window */
return; children = xcb_query_tree_children(reply);
}
children = xcb_query_tree_children(rep);
for(i = 0; i < len; ++i) for(i = 0; i < len; ++i)
cookies[i] = xcb_get_window_attributes(c, children[i]); cookies[i] = xcb_get_window_attributes(c, children[i]);
for(i = 0; i < len; ++i)
{ /* Call manage_window with the attributes for every window */
for(i = 0; i < len; ++i) {
window_attributes_t wa = { TAG_COOKIE, { cookies[i] } }; window_attributes_t wa = { TAG_COOKIE, { cookies[i] } };
manage_window(prophs, c, children[i], wa); manage_window(prophs, c, children[i], wa);
} }
free(rep);
free(reply);
} }
int main(int argc, char *argv[], char *env[]) { int main(int argc, char *argv[], char *env[]) {
@ -277,6 +279,7 @@ int main(int argc, char *argv[], char *env[]) {
xcb_connection_t *c; xcb_connection_t *c;
xcb_property_handlers_t prophs; xcb_property_handlers_t prophs;
xcb_window_t root; xcb_window_t root;
xcb_intern_atom_cookie_t atom_cookies[NUM_ATOMS];
/* Initialize the table data structures for each workspace */ /* Initialize the table data structures for each workspace */
init_table(); init_table();
@ -289,6 +292,20 @@ int main(int argc, char *argv[], char *env[]) {
c = xcb_connect(NULL, &screens); c = xcb_connect(NULL, &screens);
/* Place requests for the atoms we need as soon as possible */
#define REQUEST_ATOM(name) atom_cookies[name] = xcb_intern_atom(c, 0, strlen(#name), #name);
REQUEST_ATOM(_NET_SUPPORTED);
REQUEST_ATOM(_NET_WM_STATE_FULLSCREEN);
REQUEST_ATOM(_NET_SUPPORTING_WM_CHECK);
REQUEST_ATOM(_NET_WM_NAME);
REQUEST_ATOM(_NET_WM_STATE);
REQUEST_ATOM(_NET_WM_WINDOW_TYPE);
REQUEST_ATOM(_NET_WM_DESKTOP);
REQUEST_ATOM(_NET_WM_WINDOW_TYPE_DOCK);
REQUEST_ATOM(_NET_WM_STRUT_PARTIAL);
REQUEST_ATOM(UTF8_STRING);
/* TODO: this has to be more beautiful somewhen */ /* TODO: this has to be more beautiful somewhen */
int major, minor, error; int major, minor, error;
@ -310,14 +327,15 @@ int main(int argc, char *argv[], char *env[]) {
/* end of ugliness */ /* end of ugliness */
xcb_event_handlers_init(c, &evenths); xcb_event_handlers_init(c, &evenths);
for(i = 2; i < 128; ++i)
/* DEBUG: Trap all events and print them */
for (i = 2; i < 128; ++i)
xcb_event_set_handler(&evenths, i, handle_event, 0); xcb_event_set_handler(&evenths, i, handle_event, 0);
for(i = 0; i < 256; ++i) for (i = 0; i < 256; ++i)
xcb_event_set_error_handler(&evenths, i, (xcb_generic_error_handler_t)handle_event, 0); xcb_event_set_error_handler(&evenths, i, (xcb_generic_error_handler_t)handle_event, 0);
/* Expose = an Application should redraw itself. That is, we have to redraw our /* Expose = an Application should redraw itself, in this case its our titlebars. */
* contents (= top/bottom bar, titlebars for each window) */
xcb_event_set_expose_handler(&evenths, handle_expose_event, 0); xcb_event_set_expose_handler(&evenths, handle_expose_event, 0);
/* Key presses/releases are pretty obvious, I think */ /* Key presses/releases are pretty obvious, I think */
@ -330,33 +348,42 @@ int main(int argc, char *argv[], char *env[]) {
/* Button press = user pushed a mouse button over one of our windows */ /* Button press = user pushed a mouse button over one of our windows */
xcb_event_set_button_press_handler(&evenths, handle_button_press, 0); xcb_event_set_button_press_handler(&evenths, handle_button_press, 0);
xcb_event_set_unmap_notify_handler(&evenths, handle_unmap_notify_event, 0); /* Map notify = there is a new window */
xcb_property_handlers_init(&prophs, &evenths);
xcb_event_set_map_notify_handler(&evenths, handle_map_notify_event, &prophs); xcb_event_set_map_notify_handler(&evenths, handle_map_notify_event, &prophs);
/* Unmap notify = window disappeared. When sent from a client, we dont manage
it any longer. Usually, the client destroys the window shortly afterwards. */
xcb_event_set_unmap_notify_handler(&evenths, handle_unmap_notify_event, 0);
/* Client message = client changed its properties (EWMH) */
/* TODO: cant we do this via property handlers? */
xcb_event_set_client_message_handler(&evenths, handle_client_message, 0); xcb_event_set_client_message_handler(&evenths, handle_client_message, 0);
/* Initialize the property handlers */
xcb_property_handlers_init(&prophs, &evenths);
/* Watch the WM_NAME (= title of the window) property */
xcb_watch_wm_name(&prophs, 128, handle_windowname_change, 0); xcb_watch_wm_name(&prophs, 128, handle_windowname_change, 0);
/* Get the root window and set the event mask */
root = xcb_aux_get_screen(c, screens)->root; root = xcb_aux_get_screen(c, screens)->root;
root_win = root;
uint32_t mask = XCB_CW_EVENT_MASK; uint32_t mask = XCB_CW_EVENT_MASK;
uint32_t values[] = { XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_ENTER_WINDOW}; uint32_t values[] = { XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
XCB_EVENT_MASK_PROPERTY_CHANGE |
XCB_EVENT_MASK_ENTER_WINDOW };
xcb_change_window_attributes(c, root, mask, values); xcb_change_window_attributes(c, root, mask, values);
/* Setup NetWM atoms */ /* Setup NetWM atoms */
/* TODO: needs cleanup, needs more xcb (asynchronous), needs more error checking */ #define GET_ATOM(name) { \
#define GET_ATOM(name) { \ xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(c, atom_cookies[name], NULL); \
xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(c, xcb_intern_atom(c, 0, strlen(#name), #name), NULL); \
if (!reply) { \ if (!reply) { \
printf("Could not get atom " #name "\n"); \ printf("Could not get atom " #name "\n"); \
exit(-1); \ exit(-1); \
} \ } \
atoms[name] = reply->atom; \ atoms[name] = reply->atom; \
free(reply); \ free(reply); \
} }
GET_ATOM(_NET_SUPPORTED); GET_ATOM(_NET_SUPPORTED);
GET_ATOM(_NET_WM_STATE_FULLSCREEN); GET_ATOM(_NET_WM_STATE_FULLSCREEN);
@ -372,11 +399,13 @@ int main(int argc, char *argv[], char *env[]) {
xcb_property_set_handler(&prophs, atoms[_NET_WM_WINDOW_TYPE], UINT_MAX, window_type_handler, NULL); xcb_property_set_handler(&prophs, atoms[_NET_WM_WINDOW_TYPE], UINT_MAX, window_type_handler, NULL);
/* TODO: In order to comply with EWMH, we have to watch _NET_WM_STRUT_PARTIAL */ /* TODO: In order to comply with EWMH, we have to watch _NET_WM_STRUT_PARTIAL */
check_error(c, xcb_change_property_checked(c, XCB_PROP_MODE_REPLACE, root, atoms[_NET_SUPPORTED], ATOM, 32, 7, atoms), "Could not set _NET_SUPPORTED"); /* Set up the atoms we support */
check_error(c, xcb_change_property_checked(c, XCB_PROP_MODE_REPLACE, root, atoms[_NET_SUPPORTED],
ATOM, 32, 7, atoms), "Could not set _NET_SUPPORTED");
/* Set up the window managers name */
xcb_change_property(c, XCB_PROP_MODE_REPLACE, root, atoms[_NET_SUPPORTING_WM_CHECK], WINDOW, 32, 1, &root); xcb_change_property(c, XCB_PROP_MODE_REPLACE, root, atoms[_NET_SUPPORTING_WM_CHECK], WINDOW, 32, 1, &root);
xcb_change_property(c, XCB_PROP_MODE_REPLACE, root, atoms[_NET_WM_NAME], atoms[UTF8_STRING], 8, strlen("i3"), "i3");
xcb_change_property(c, XCB_PROP_MODE_REPLACE, root, atoms[_NET_WM_NAME] , atoms[UTF8_STRING], 8, strlen("i3"), "i3");
#define BIND(key, modifier, cmd) { \ #define BIND(key, modifier, cmd) { \
Binding *new = malloc(sizeof(Binding)); \ Binding *new = malloc(sizeof(Binding)); \
@ -422,6 +451,7 @@ int main(int argc, char *argv[], char *env[]) {
BIND(18, BIND_MOD_1 , "9"); BIND(18, BIND_MOD_1 , "9");
BIND(19, BIND_MOD_1 , "0"); BIND(19, BIND_MOD_1 , "0");
/* Grab the bound keys */
Binding *bind; Binding *bind;
TAILQ_FOREACH(bind, &bindings, bindings) { TAILQ_FOREACH(bind, &bindings, bindings) {
printf("Grabbing %d\n", bind->keycode); printf("Grabbing %d\n", bind->keycode);
@ -434,6 +464,7 @@ int main(int argc, char *argv[], char *env[]) {
printf("Checking for Xinerama...\n"); printf("Checking for Xinerama...\n");
initialize_xinerama(c); initialize_xinerama(c);
/* DEBUG: Start a terminal */
start_application(TERMINAL); start_application(TERMINAL);
xcb_flush(c); xcb_flush(c);
@ -441,9 +472,8 @@ int main(int argc, char *argv[], char *env[]) {
manage_existing_windows(c, &prophs, root); manage_existing_windows(c, &prophs, root);
/* Get pointer position to see on which screen were starting */ /* Get pointer position to see on which screen were starting */
xcb_query_pointer_cookie_t pointer_cookie = xcb_query_pointer(c, root); xcb_query_pointer_reply_t *reply;
xcb_query_pointer_reply_t *reply = xcb_query_pointer_reply(c, pointer_cookie, NULL); if ((reply = xcb_query_pointer(c, xcb_query_pointer(c, root), NULL)) == NULL) {
if (reply == NULL) {
printf("Could not get pointer position\n"); printf("Could not get pointer position\n");
return 1; return 1;
} }
@ -458,6 +488,7 @@ int main(int argc, char *argv[], char *env[]) {
c_ws = &workspaces[screen->current_workspace]; c_ws = &workspaces[screen->current_workspace];
} }
/* Enter xcbs event handler */
xcb_event_wait_for_event_loop(&evenths); xcb_event_wait_for_event_loop(&evenths);
/* not reached */ /* not reached */