]> git.sur5r.net Git - i3/i3/blob - src/nc.c
44a434616d9af432464f753324bcd0d60ef601e2
[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         x_push_changes(croot);
93
94     }
95     else if (strcasecmp(command, "move before h") == 0)
96         tree_move('p', HORIZ);
97     else if (strcasecmp(command, "move before v") == 0)
98         tree_move('p', VERT);
99     else if (strcasecmp(command, "move after h") == 0)
100         tree_move('n', HORIZ);
101     else if (strcasecmp(command, "move after v") == 0)
102         tree_move('n', VERT);
103     else if (strncasecmp(command, "restore", strlen("restore")) == 0)
104         tree_append_json(command + strlen("restore "));
105     else if (strncasecmp(command, "exec", strlen("exec")) == 0)
106         start_application(command + strlen("exec "));
107     else if (strcasecmp(command, "restart") == 0)
108         i3_restart();
109     else if (strcasecmp(command, "floating") == 0)
110         toggle_floating_mode(focused, false);
111
112     tree_render();
113
114 #if 0
115     if (strcasecmp(command, "prev") == 0)
116         tree_prev(O_CURRENT);
117 #endif
118 }
119
120 int main(int argc, char *argv[]) {
121     int screens;
122     char *override_configpath = NULL;
123     bool autostart = true;
124     bool only_check_config = false;
125     bool force_xinerama = false;
126     xcb_intern_atom_cookie_t atom_cookies[NUM_ATOMS];
127     static struct option long_options[] = {
128         {"no-autostart", no_argument, 0, 'a'},
129         {"config", required_argument, 0, 'c'},
130         {"version", no_argument, 0, 'v'},
131         {"help", no_argument, 0, 'h'},
132         {"force-xinerama", no_argument, 0, 0},
133         {0, 0, 0, 0}
134     };
135     int option_index = 0, opt;
136
137     setlocale(LC_ALL, "");
138
139     /* Disable output buffering to make redirects in .xsession actually useful for debugging */
140     if (!isatty(fileno(stdout)))
141         setbuf(stdout, NULL);
142
143     start_argv = argv;
144
145     while ((opt = getopt_long(argc, argv, "c:Cvahld:V", long_options, &option_index)) != -1) {
146         switch (opt) {
147             case 'a':
148                 LOG("Autostart disabled using -a\n");
149                 autostart = false;
150                 break;
151             case 'c':
152                 override_configpath = sstrdup(optarg);
153                 break;
154             case 'C':
155                 LOG("Checking configuration file only (-C)\n");
156                 only_check_config = true;
157                 break;
158             case 'v':
159                 printf("i3 version " I3_VERSION " © 2009 Michael Stapelberg and contributors\n");
160                 exit(EXIT_SUCCESS);
161             case 'V':
162                 set_verbosity(true);
163                 break;
164             case 'd':
165                 LOG("Enabling debug loglevel %s\n", optarg);
166                 add_loglevel(optarg);
167                 break;
168             case 'l':
169                 /* DEPRECATED, ignored for the next 3 versions (3.e, 3.f, 3.g) */
170                 break;
171             case 0:
172                 if (strcmp(long_options[option_index].name, "force-xinerama") == 0) {
173                     force_xinerama = true;
174                     ELOG("Using Xinerama instead of RandR. This option should be "
175                          "avoided at all cost because it does not refresh the list "
176                          "of screens, so you cannot configure displays at runtime. "
177                          "Please check if your driver really does not support RandR "
178                          "and disable this option as soon as you can.\n");
179                     break;
180                 }
181                 /* fall-through */
182             default:
183                 fprintf(stderr, "Usage: %s [-c configfile] [-d loglevel] [-a] [-v] [-V] [-C]\n", argv[0]);
184                 fprintf(stderr, "\n");
185                 fprintf(stderr, "-a: disable autostart\n");
186                 fprintf(stderr, "-v: display version and exit\n");
187                 fprintf(stderr, "-V: enable verbose mode\n");
188                 fprintf(stderr, "-d <loglevel>: enable debug loglevel <loglevel>\n");
189                 fprintf(stderr, "-c <configfile>: use the provided configfile instead\n");
190                 fprintf(stderr, "-C: check configuration file and exit\n");
191                 fprintf(stderr, "--force-xinerama: Use Xinerama instead of RandR. This "
192                                 "option should only be used if you are stuck with the "
193                                 "nvidia closed source driver which does not support RandR.\n");
194                 exit(EXIT_FAILURE);
195         }
196     }
197
198     LOG("i3 (tree) version " I3_VERSION " starting\n");
199
200     conn = xcb_connect(NULL, &screens);
201     if (xcb_connection_has_error(conn))
202         errx(EXIT_FAILURE, "Cannot open display\n");
203
204     load_configuration(conn, override_configpath, false);
205     if (only_check_config) {
206         LOG("Done checking configuration file. Exiting.\n");
207         exit(0);
208     }
209
210     xcb_screen_t *root_screen = xcb_aux_get_screen(conn, screens);
211     root = root_screen->root;
212     root_depth = root_screen->root_depth;
213
214     uint32_t mask = XCB_CW_EVENT_MASK;
215     uint32_t values[] = { XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT |
216                           XCB_EVENT_MASK_STRUCTURE_NOTIFY |         /* when the user adds a screen (e.g. video
217                                                                            projector), the root window gets a
218                                                                            ConfigureNotify */
219                           XCB_EVENT_MASK_POINTER_MOTION |
220                           XCB_EVENT_MASK_PROPERTY_CHANGE |
221                           XCB_EVENT_MASK_ENTER_WINDOW };
222     xcb_void_cookie_t cookie;
223     cookie = xcb_change_window_attributes_checked(conn, root, mask, values);
224     check_error(conn, cookie, "Another window manager seems to be running");
225
226     /* Place requests for the atoms we need as soon as possible */
227     #define REQUEST_ATOM(name) atom_cookies[name] = xcb_intern_atom(conn, 0, strlen(#name), #name);
228
229     REQUEST_ATOM(_NET_SUPPORTED);
230     REQUEST_ATOM(_NET_WM_STATE_FULLSCREEN);
231     REQUEST_ATOM(_NET_SUPPORTING_WM_CHECK);
232     REQUEST_ATOM(_NET_WM_NAME);
233     REQUEST_ATOM(_NET_WM_STATE);
234     REQUEST_ATOM(_NET_WM_WINDOW_TYPE);
235     REQUEST_ATOM(_NET_WM_DESKTOP);
236     REQUEST_ATOM(_NET_WM_WINDOW_TYPE_DOCK);
237     REQUEST_ATOM(_NET_WM_WINDOW_TYPE_DIALOG);
238     REQUEST_ATOM(_NET_WM_WINDOW_TYPE_UTILITY);
239     REQUEST_ATOM(_NET_WM_WINDOW_TYPE_TOOLBAR);
240     REQUEST_ATOM(_NET_WM_WINDOW_TYPE_SPLASH);
241     REQUEST_ATOM(_NET_WM_STRUT_PARTIAL);
242     REQUEST_ATOM(WM_PROTOCOLS);
243     REQUEST_ATOM(WM_DELETE_WINDOW);
244     REQUEST_ATOM(UTF8_STRING);
245     REQUEST_ATOM(WM_STATE);
246     REQUEST_ATOM(WM_CLIENT_LEADER);
247     REQUEST_ATOM(_NET_CURRENT_DESKTOP);
248     REQUEST_ATOM(_NET_ACTIVE_WINDOW);
249     REQUEST_ATOM(_NET_WORKAREA);
250
251     memset(&evenths, 0, sizeof(xcb_event_handlers_t));
252     memset(&prophs, 0, sizeof(xcb_property_handlers_t));
253
254     xcb_event_handlers_init(conn, &evenths);
255     xcb_property_handlers_init(&prophs, &evenths);
256     xcb_event_set_key_press_handler(&evenths, handle_key_press, NULL);
257
258     xcb_event_set_button_press_handler(&evenths, handle_button_press, NULL);
259
260     xcb_event_set_map_request_handler(&evenths, handle_map_request, NULL);
261
262     xcb_event_set_unmap_notify_handler(&evenths, handle_unmap_notify_event, NULL);
263     //xcb_event_set_destroy_notify_handler(&evenths, handle_destroy_notify_event, NULL);
264
265     xcb_event_set_expose_handler(&evenths, handle_expose_event, NULL);
266
267     /* Enter window = user moved his mouse over the window */
268     xcb_event_set_enter_notify_handler(&evenths, handle_enter_notify, NULL);
269
270
271     /* Setup NetWM atoms */
272     #define GET_ATOM(name) \
273         do { \
274             xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(conn, atom_cookies[name], NULL); \
275             if (!reply) { \
276                 ELOG("Could not get atom " #name "\n"); \
277                 exit(-1); \
278             } \
279             atoms[name] = reply->atom; \
280             free(reply); \
281         } while (0)
282
283     GET_ATOM(_NET_SUPPORTED);
284     GET_ATOM(_NET_WM_STATE_FULLSCREEN);
285     GET_ATOM(_NET_SUPPORTING_WM_CHECK);
286     GET_ATOM(_NET_WM_NAME);
287     GET_ATOM(_NET_WM_STATE);
288     GET_ATOM(_NET_WM_WINDOW_TYPE);
289     GET_ATOM(_NET_WM_DESKTOP);
290     GET_ATOM(_NET_WM_WINDOW_TYPE_DOCK);
291     GET_ATOM(_NET_WM_WINDOW_TYPE_DIALOG);
292     GET_ATOM(_NET_WM_WINDOW_TYPE_UTILITY);
293     GET_ATOM(_NET_WM_WINDOW_TYPE_TOOLBAR);
294     GET_ATOM(_NET_WM_WINDOW_TYPE_SPLASH);
295     GET_ATOM(_NET_WM_STRUT_PARTIAL);
296     GET_ATOM(WM_PROTOCOLS);
297     GET_ATOM(WM_DELETE_WINDOW);
298     GET_ATOM(UTF8_STRING);
299     GET_ATOM(WM_STATE);
300     GET_ATOM(WM_CLIENT_LEADER);
301     GET_ATOM(_NET_CURRENT_DESKTOP);
302     GET_ATOM(_NET_ACTIVE_WINDOW);
303     GET_ATOM(_NET_WORKAREA);
304
305     /* Watch _NET_WM_NAME (title of the window encoded in UTF-8) */
306     xcb_property_set_handler(&prophs, atoms[_NET_WM_NAME], 128, handle_windowname_change, NULL);
307
308     /* Watch WM_NAME (title of the window encoded in COMPOUND_TEXT) */
309     xcb_watch_wm_name(&prophs, 128, handle_windowname_change_legacy, NULL);
310
311
312     keysyms = xcb_key_symbols_alloc(conn);
313
314     xcb_get_numlock_mask(conn);
315
316     translate_keysyms();
317     grab_all_keys(conn, false);
318
319     int randr_base;
320     if (force_xinerama) {
321         xinerama_init();
322     } else {
323         DLOG("Checking for XRandR...\n");
324         randr_init(&randr_base);
325
326 #if 0
327         xcb_event_set_handler(&evenths,
328                               randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY,
329                               handle_screen_change,
330                               NULL);
331 #endif
332     }
333
334     if (!tree_restore())
335         tree_init();
336     tree_render();
337
338     /* proof-of-concept for assignments */
339     Con *ws = workspace_get("3");
340
341     Match *current_swallow = scalloc(sizeof(Match));
342     TAILQ_INSERT_TAIL(&(ws->swallow_head), current_swallow, matches);
343
344     current_swallow->insert_where = M_ACTIVE;
345     current_swallow->class = strdup("xterm");
346
347     struct ev_loop *loop = ev_loop_new(0);
348     if (loop == NULL)
349             die("Could not initialize libev. Bad LIBEV_FLAGS?\n");
350
351     /* Create the UNIX domain socket for IPC */
352     if (config.ipc_socket_path != NULL) {
353         int ipc_socket = ipc_create_socket(config.ipc_socket_path);
354         if (ipc_socket == -1) {
355             ELOG("Could not create the IPC socket, IPC disabled\n");
356         } else {
357             struct ev_io *ipc_io = scalloc(sizeof(struct ev_io));
358             ev_io_init(ipc_io, ipc_new_client, ipc_socket, EV_READ);
359             ev_io_start(loop, ipc_io);
360         }
361     }
362
363     struct ev_io *xcb_watcher = scalloc(sizeof(struct ev_io));
364     struct ev_check *xcb_check = scalloc(sizeof(struct ev_check));
365     struct ev_prepare *xcb_prepare = scalloc(sizeof(struct ev_prepare));
366
367     ev_io_init(xcb_watcher, xcb_got_event, xcb_get_file_descriptor(conn), EV_READ);
368     ev_io_start(loop, xcb_watcher);
369
370     ev_check_init(xcb_check, xcb_check_cb);
371     ev_check_start(loop, xcb_check);
372
373     ev_prepare_init(xcb_prepare, xcb_prepare_cb);
374     ev_prepare_start(loop, xcb_prepare);
375
376     xcb_flush(conn);
377
378     manage_existing_windows(root);
379
380     ev_loop(loop, 0);
381 }