]> git.sur5r.net Git - i3/i3/blob - src/nc.c
handle destroynotify events
[i3/i3] / src / nc.c
1 /*
2  * vim:ts=4:sw=4:expandtab
3  */
4 #include <ev.h>
5 #include "all.h"
6
7 static int xkb_event_base;
8
9 int xkb_current_group;
10
11 extern Con *focused;
12
13 char **start_argv;
14
15 xcb_connection_t *conn;
16 xcb_event_handlers_t evenths;
17 xcb_property_handlers_t prophs;
18 xcb_atom_t atoms[NUM_ATOMS];
19
20 xcb_window_t root;
21 uint8_t root_depth;
22
23 xcb_key_symbols_t *keysyms;
24
25 /* The list of key bindings */
26 struct bindings_head *bindings;
27
28 /* The list of exec-lines */
29 struct autostarts_head autostarts = TAILQ_HEAD_INITIALIZER(autostarts);
30
31 /* The list of assignments */
32 struct assignments_head assignments = TAILQ_HEAD_INITIALIZER(assignments);
33
34 /*
35  * This callback is only a dummy, see xcb_prepare_cb and xcb_check_cb.
36  * See also man libev(3): "ev_prepare" and "ev_check" - customise your event loop
37  *
38  */
39 static void xcb_got_event(EV_P_ struct ev_io *w, int revents) {
40     /* empty, because xcb_prepare_cb and xcb_check_cb are used */
41 }
42
43 /*
44  * Flush before blocking (and waiting for new events)
45  *
46  */
47 static void xcb_prepare_cb(EV_P_ ev_prepare *w, int revents) {
48     xcb_flush(conn);
49 }
50
51 /*
52  * Instead of polling the X connection socket we leave this to
53  * xcb_poll_for_event() which knows better than we can ever know.
54  *
55  */
56 static void xcb_check_cb(EV_P_ ev_check *w, int revents) {
57     xcb_generic_event_t *event;
58
59     while ((event = xcb_poll_for_event(conn)) != NULL) {
60             xcb_event_handle(&evenths, event);
61             free(event);
62     }
63 }
64
65 void parse_command(const char *command) {
66     printf("received command: %s\n", command);
67
68     if (strcasecmp(command, "open") == 0)
69         tree_open_con(NULL);
70     else if (strcasecmp(command, "close") == 0)
71         tree_close_con();
72     else if (strcasecmp(command, "split h") == 0)
73         tree_split(focused, HORIZ);
74     else if (strcasecmp(command, "split v") == 0)
75         tree_split(focused, VERT);
76     else if (strcasecmp(command, "level up") == 0)
77         level_up();
78     else if (strcasecmp(command, "level down") == 0)
79         level_down();
80     else if (strcasecmp(command, "prev h") == 0)
81         tree_next('p', HORIZ);
82     else if (strcasecmp(command, "prev v") == 0)
83         tree_next('p', VERT);
84     else if (strcasecmp(command, "next h") == 0)
85         tree_next('n', HORIZ);
86     else if (strcasecmp(command, "next v") == 0)
87         tree_next('n', VERT);
88     else if (strncasecmp(command, "workspace ", strlen("workspace ")) == 0)
89         workspace_show(command + strlen("workspace "));
90     else if (strcasecmp(command, "stack") == 0) {
91         focused->layout = L_STACKED;
92     }
93     else if (strcasecmp(command, "fullscreen") == 0) {
94         if (focused->fullscreen_mode == CF_NONE)
95             focused->fullscreen_mode = CF_OUTPUT;
96         else focused->fullscreen_mode = CF_NONE;
97     }
98     else if (strcasecmp(command, "move before h") == 0)
99         tree_move('p', HORIZ);
100     else if (strcasecmp(command, "move before v") == 0)
101         tree_move('p', VERT);
102     else if (strcasecmp(command, "move after h") == 0)
103         tree_move('n', HORIZ);
104     else if (strcasecmp(command, "move after v") == 0)
105         tree_move('n', VERT);
106     else if (strncasecmp(command, "restore", strlen("restore")) == 0)
107         tree_append_json(command + strlen("restore "));
108     else if (strncasecmp(command, "exec", strlen("exec")) == 0)
109         start_application(command + strlen("exec "));
110     else if (strcasecmp(command, "restart") == 0)
111         i3_restart();
112     else if (strcasecmp(command, "floating") == 0) {
113         //toggle_floating_mode(focused, false);
114             parse_cmd("exit");
115     parse_cmd("exec /usr/bin/bleh");
116     parse_cmd("exec kill -9 33");
117     parse_cmd("kill");
118     parse_cmd("[ class=\"Xpdf\" ] kill");
119     parse_cmd("[ class=\"firefox\" ] kill");
120
121     }
122
123     tree_render();
124
125 #if 0
126     if (strcasecmp(command, "prev") == 0)
127         tree_prev(O_CURRENT);
128 #endif
129 }
130
131 int main(int argc, char *argv[]) {
132     //parse_cmd("[ foo ] attach, attach ; focus");
133     int screens;
134     char *override_configpath = NULL;
135     bool autostart = true;
136     bool only_check_config = false;
137     bool force_xinerama = false;
138     xcb_intern_atom_cookie_t atom_cookies[NUM_ATOMS];
139     static struct option long_options[] = {
140         {"no-autostart", no_argument, 0, 'a'},
141         {"config", required_argument, 0, 'c'},
142         {"version", no_argument, 0, 'v'},
143         {"help", no_argument, 0, 'h'},
144         {"force-xinerama", no_argument, 0, 0},
145         {0, 0, 0, 0}
146     };
147     int option_index = 0, opt;
148
149     setlocale(LC_ALL, "");
150
151     /* Disable output buffering to make redirects in .xsession actually useful for debugging */
152     if (!isatty(fileno(stdout)))
153         setbuf(stdout, NULL);
154
155     start_argv = argv;
156
157     while ((opt = getopt_long(argc, argv, "c:Cvahld:V", long_options, &option_index)) != -1) {
158         switch (opt) {
159             case 'a':
160                 LOG("Autostart disabled using -a\n");
161                 autostart = false;
162                 break;
163             case 'c':
164                 override_configpath = sstrdup(optarg);
165                 break;
166             case 'C':
167                 LOG("Checking configuration file only (-C)\n");
168                 only_check_config = true;
169                 break;
170             case 'v':
171                 printf("i3 version " I3_VERSION " © 2009 Michael Stapelberg and contributors\n");
172                 exit(EXIT_SUCCESS);
173             case 'V':
174                 set_verbosity(true);
175                 break;
176             case 'd':
177                 LOG("Enabling debug loglevel %s\n", optarg);
178                 add_loglevel(optarg);
179                 break;
180             case 'l':
181                 /* DEPRECATED, ignored for the next 3 versions (3.e, 3.f, 3.g) */
182                 break;
183             case 0:
184                 if (strcmp(long_options[option_index].name, "force-xinerama") == 0) {
185                     force_xinerama = true;
186                     ELOG("Using Xinerama instead of RandR. This option should be "
187                          "avoided at all cost because it does not refresh the list "
188                          "of screens, so you cannot configure displays at runtime. "
189                          "Please check if your driver really does not support RandR "
190                          "and disable this option as soon as you can.\n");
191                     break;
192                 }
193                 /* fall-through */
194             default:
195                 fprintf(stderr, "Usage: %s [-c configfile] [-d loglevel] [-a] [-v] [-V] [-C]\n", argv[0]);
196                 fprintf(stderr, "\n");
197                 fprintf(stderr, "-a: disable autostart\n");
198                 fprintf(stderr, "-v: display version and exit\n");
199                 fprintf(stderr, "-V: enable verbose mode\n");
200                 fprintf(stderr, "-d <loglevel>: enable debug loglevel <loglevel>\n");
201                 fprintf(stderr, "-c <configfile>: use the provided configfile instead\n");
202                 fprintf(stderr, "-C: check configuration file and exit\n");
203                 fprintf(stderr, "--force-xinerama: Use Xinerama instead of RandR. This "
204                                 "option should only be used if you are stuck with the "
205                                 "nvidia closed source driver which does not support RandR.\n");
206                 exit(EXIT_FAILURE);
207         }
208     }
209
210     LOG("i3 (tree) version " I3_VERSION " starting\n");
211
212     conn = xcb_connect(NULL, &screens);
213     if (xcb_connection_has_error(conn))
214         errx(EXIT_FAILURE, "Cannot open display\n");
215
216     load_configuration(conn, override_configpath, false);
217     if (only_check_config) {
218         LOG("Done checking configuration file. Exiting.\n");
219         exit(0);
220     }
221
222     xcb_screen_t *root_screen = xcb_aux_get_screen(conn, screens);
223     root = root_screen->root;
224     root_depth = root_screen->root_depth;
225
226     uint32_t mask = XCB_CW_EVENT_MASK;
227     uint32_t values[] = { XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT |
228                           XCB_EVENT_MASK_STRUCTURE_NOTIFY |         /* when the user adds a screen (e.g. video
229                                                                            projector), the root window gets a
230                                                                            ConfigureNotify */
231                           XCB_EVENT_MASK_POINTER_MOTION |
232                           XCB_EVENT_MASK_PROPERTY_CHANGE |
233                           XCB_EVENT_MASK_ENTER_WINDOW };
234     xcb_void_cookie_t cookie;
235     cookie = xcb_change_window_attributes_checked(conn, root, mask, values);
236     check_error(conn, cookie, "Another window manager seems to be running");
237
238     /* Place requests for the atoms we need as soon as possible */
239     #define REQUEST_ATOM(name) atom_cookies[name] = xcb_intern_atom(conn, 0, strlen(#name), #name);
240
241     REQUEST_ATOM(_NET_SUPPORTED);
242     REQUEST_ATOM(_NET_WM_STATE_FULLSCREEN);
243     REQUEST_ATOM(_NET_SUPPORTING_WM_CHECK);
244     REQUEST_ATOM(_NET_WM_NAME);
245     REQUEST_ATOM(_NET_WM_STATE);
246     REQUEST_ATOM(_NET_WM_WINDOW_TYPE);
247     REQUEST_ATOM(_NET_WM_DESKTOP);
248     REQUEST_ATOM(_NET_WM_WINDOW_TYPE_DOCK);
249     REQUEST_ATOM(_NET_WM_WINDOW_TYPE_DIALOG);
250     REQUEST_ATOM(_NET_WM_WINDOW_TYPE_UTILITY);
251     REQUEST_ATOM(_NET_WM_WINDOW_TYPE_TOOLBAR);
252     REQUEST_ATOM(_NET_WM_WINDOW_TYPE_SPLASH);
253     REQUEST_ATOM(_NET_WM_STRUT_PARTIAL);
254     REQUEST_ATOM(WM_PROTOCOLS);
255     REQUEST_ATOM(WM_DELETE_WINDOW);
256     REQUEST_ATOM(UTF8_STRING);
257     REQUEST_ATOM(WM_STATE);
258     REQUEST_ATOM(WM_CLIENT_LEADER);
259     REQUEST_ATOM(_NET_CURRENT_DESKTOP);
260     REQUEST_ATOM(_NET_ACTIVE_WINDOW);
261     REQUEST_ATOM(_NET_WORKAREA);
262
263     memset(&evenths, 0, sizeof(xcb_event_handlers_t));
264     memset(&prophs, 0, sizeof(xcb_property_handlers_t));
265
266     xcb_event_handlers_init(conn, &evenths);
267     xcb_property_handlers_init(&prophs, &evenths);
268     xcb_event_set_key_press_handler(&evenths, handle_key_press, NULL);
269
270     xcb_event_set_button_press_handler(&evenths, handle_button_press, NULL);
271
272     xcb_event_set_map_request_handler(&evenths, handle_map_request, NULL);
273
274     xcb_event_set_unmap_notify_handler(&evenths, handle_unmap_notify_event, NULL);
275     xcb_event_set_destroy_notify_handler(&evenths, handle_destroy_notify_event, NULL);
276
277     xcb_event_set_expose_handler(&evenths, handle_expose_event, NULL);
278
279     /* Enter window = user moved his mouse over the window */
280     xcb_event_set_enter_notify_handler(&evenths, handle_enter_notify, NULL);
281
282     /* Client message are sent to the root window. The only interesting client message
283        for us is _NET_WM_STATE, we honour _NET_WM_STATE_FULLSCREEN */
284     xcb_event_set_client_message_handler(&evenths, handle_client_message, NULL);
285
286     /* Setup NetWM atoms */
287     #define GET_ATOM(name) \
288         do { \
289             xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(conn, atom_cookies[name], NULL); \
290             if (!reply) { \
291                 ELOG("Could not get atom " #name "\n"); \
292                 exit(-1); \
293             } \
294             atoms[name] = reply->atom; \
295             free(reply); \
296         } while (0)
297
298     GET_ATOM(_NET_SUPPORTED);
299     GET_ATOM(_NET_WM_STATE_FULLSCREEN);
300     GET_ATOM(_NET_SUPPORTING_WM_CHECK);
301     GET_ATOM(_NET_WM_NAME);
302     GET_ATOM(_NET_WM_STATE);
303     GET_ATOM(_NET_WM_WINDOW_TYPE);
304     GET_ATOM(_NET_WM_DESKTOP);
305     GET_ATOM(_NET_WM_WINDOW_TYPE_DOCK);
306     GET_ATOM(_NET_WM_WINDOW_TYPE_DIALOG);
307     GET_ATOM(_NET_WM_WINDOW_TYPE_UTILITY);
308     GET_ATOM(_NET_WM_WINDOW_TYPE_TOOLBAR);
309     GET_ATOM(_NET_WM_WINDOW_TYPE_SPLASH);
310     GET_ATOM(_NET_WM_STRUT_PARTIAL);
311     GET_ATOM(WM_PROTOCOLS);
312     GET_ATOM(WM_DELETE_WINDOW);
313     GET_ATOM(UTF8_STRING);
314     GET_ATOM(WM_STATE);
315     GET_ATOM(WM_CLIENT_LEADER);
316     GET_ATOM(_NET_CURRENT_DESKTOP);
317     GET_ATOM(_NET_ACTIVE_WINDOW);
318     GET_ATOM(_NET_WORKAREA);
319
320     /* Watch _NET_WM_NAME (title of the window encoded in UTF-8) */
321     xcb_property_set_handler(&prophs, atoms[_NET_WM_NAME], 128, handle_windowname_change, NULL);
322
323     /* Watch WM_NAME (title of the window encoded in COMPOUND_TEXT) */
324     xcb_watch_wm_name(&prophs, 128, handle_windowname_change_legacy, NULL);
325
326
327     keysyms = xcb_key_symbols_alloc(conn);
328
329     xcb_get_numlock_mask(conn);
330
331     translate_keysyms();
332     grab_all_keys(conn, false);
333
334     int randr_base;
335     if (force_xinerama) {
336         xinerama_init();
337     } else {
338         DLOG("Checking for XRandR...\n");
339         randr_init(&randr_base);
340
341 #if 0
342         xcb_event_set_handler(&evenths,
343                               randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY,
344                               handle_screen_change,
345                               NULL);
346 #endif
347     }
348
349     if (!tree_restore())
350         tree_init();
351     tree_render();
352
353     /* proof-of-concept for assignments */
354     Con *ws = workspace_get("3");
355
356     Match *current_swallow = scalloc(sizeof(Match));
357     TAILQ_INSERT_TAIL(&(ws->swallow_head), current_swallow, matches);
358
359     current_swallow->insert_where = M_ACTIVE;
360     current_swallow->class = strdup("xterm");
361
362     struct ev_loop *loop = ev_loop_new(0);
363     if (loop == NULL)
364             die("Could not initialize libev. Bad LIBEV_FLAGS?\n");
365
366     /* Create the UNIX domain socket for IPC */
367     if (config.ipc_socket_path != NULL) {
368         int ipc_socket = ipc_create_socket(config.ipc_socket_path);
369         if (ipc_socket == -1) {
370             ELOG("Could not create the IPC socket, IPC disabled\n");
371         } else {
372             struct ev_io *ipc_io = scalloc(sizeof(struct ev_io));
373             ev_io_init(ipc_io, ipc_new_client, ipc_socket, EV_READ);
374             ev_io_start(loop, ipc_io);
375         }
376     }
377
378     struct ev_io *xcb_watcher = scalloc(sizeof(struct ev_io));
379     struct ev_check *xcb_check = scalloc(sizeof(struct ev_check));
380     struct ev_prepare *xcb_prepare = scalloc(sizeof(struct ev_prepare));
381
382     ev_io_init(xcb_watcher, xcb_got_event, xcb_get_file_descriptor(conn), EV_READ);
383     ev_io_start(loop, xcb_watcher);
384
385     ev_check_init(xcb_check, xcb_check_cb);
386     ev_check_start(loop, xcb_check);
387
388     ev_prepare_init(xcb_prepare, xcb_prepare_cb);
389     ev_prepare_start(loop, xcb_prepare);
390
391     xcb_flush(conn);
392
393     manage_existing_windows(root);
394
395     ev_loop(loop, 0);
396 }