i3
|
00001 /* 00002 * vim:ts=4:sw=4:expandtab 00003 * 00004 * i3 - an improved dynamic tiling window manager 00005 * 00006 * © 2009-2010 Michael Stapelberg and contributors 00007 * © 2009-2010 Jan-Erik Rediger 00008 * 00009 * See file LICENSE for license information. 00010 * 00011 * sighandler.c: contains all functions for signal handling 00012 * 00013 */ 00014 #include <ev.h> 00015 #include <stdio.h> 00016 #include <stdlib.h> 00017 #include <string.h> 00018 #include <unistd.h> 00019 #include <iconv.h> 00020 #include <signal.h> 00021 00022 #include <xcb/xcb.h> 00023 #include <xcb/xcb_aux.h> 00024 #include <xcb/xcb_event.h> 00025 #include <xcb/xcb_keysyms.h> 00026 00027 #include <X11/keysym.h> 00028 00029 #include "all.h" 00030 00031 static xcb_gcontext_t pixmap_gc; 00032 static xcb_pixmap_t pixmap; 00033 static int raised_signal; 00034 00035 static char *crash_text[] = { 00036 "i3 just crashed.", 00037 "To debug this problem, either attach gdb now", 00038 "or press", 00039 "- 'e' to exit and get a core-dump,", 00040 "- 'r' to restart i3 in-place or", 00041 "- 'f' to forget the current layout and restart" 00042 }; 00043 static int crash_text_longest = 5; 00044 00045 /* 00046 * Draw the window containing the info text 00047 * 00048 */ 00049 static int sig_draw_window(xcb_window_t win, int width, int height, int font_height) { 00050 /* re-draw the background */ 00051 xcb_rectangle_t border = { 0, 0, width, height}, 00052 inner = { 2, 2, width - 4, height - 4}; 00053 xcb_change_gc_single(conn, pixmap_gc, XCB_GC_FOREGROUND, get_colorpixel("#FF0000")); 00054 xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &border); 00055 xcb_change_gc_single(conn, pixmap_gc, XCB_GC_FOREGROUND, get_colorpixel("#000000")); 00056 xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &inner); 00057 00058 /* restore font color */ 00059 xcb_change_gc_single(conn, pixmap_gc, XCB_GC_FOREGROUND, get_colorpixel("#FFFFFF")); 00060 00061 for (int i = 0; i < sizeof(crash_text) / sizeof(char*); i++) { 00062 int text_len = strlen(crash_text[i]); 00063 char *full_text = convert_utf8_to_ucs2(crash_text[i], &text_len); 00064 xcb_image_text_16(conn, text_len, pixmap, pixmap_gc, 8 /* X */, 00065 3 + (i + 1) * font_height /* Y = baseline of font */, 00066 (xcb_char2b_t*)full_text); 00067 free(full_text); 00068 } 00069 00070 /* Copy the contents of the pixmap to the real window */ 00071 xcb_copy_area(conn, pixmap, win, pixmap_gc, 0, 0, 0, 0, width, height); 00072 xcb_flush(conn); 00073 00074 return 1; 00075 } 00076 00077 /* 00078 * Handles keypresses of 'e' or 'r' to exit or restart i3 00079 * 00080 */ 00081 static int sig_handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press_event_t *event) { 00082 uint16_t state = event->state; 00083 00084 /* Apparantly, after activating numlock once, the numlock modifier 00085 * stays turned on (use xev(1) to verify). So, to resolve useful 00086 * keysyms, we remove the numlock flag from the event state */ 00087 state &= ~xcb_numlock_mask; 00088 00089 xcb_keysym_t sym = xcb_key_press_lookup_keysym(keysyms, event, state); 00090 00091 if (sym == 'e') { 00092 DLOG("User issued exit-command, raising error again.\n"); 00093 raise(raised_signal); 00094 exit(1); 00095 } 00096 00097 if (sym == 'r') 00098 i3_restart(false); 00099 00100 if (sym == 'f') 00101 i3_restart(true); 00102 00103 return 1; 00104 } 00105 00106 /* 00107 * Opens the window we use for input/output and maps it 00108 * 00109 */ 00110 static xcb_window_t open_input_window(xcb_connection_t *conn, Rect screen_rect, uint32_t width, uint32_t height) { 00111 xcb_window_t win = xcb_generate_id(conn); 00112 00113 uint32_t mask = 0; 00114 uint32_t values[2]; 00115 00116 mask |= XCB_CW_BACK_PIXEL; 00117 values[0] = 0; 00118 00119 mask |= XCB_CW_OVERRIDE_REDIRECT; 00120 values[1] = 1; 00121 00122 /* center each popup on the specified screen */ 00123 uint32_t x = screen_rect.x + ((screen_rect.width / 2) - (width / 2)), 00124 y = screen_rect.y + ((screen_rect.height / 2) - (height / 2)); 00125 00126 xcb_create_window(conn, 00127 XCB_COPY_FROM_PARENT, 00128 win, /* the window id */ 00129 root, /* parent == root */ 00130 x, y, width, height, /* dimensions */ 00131 0, /* border = 0, we draw our own */ 00132 XCB_WINDOW_CLASS_INPUT_OUTPUT, 00133 XCB_WINDOW_CLASS_COPY_FROM_PARENT, /* copy visual from parent */ 00134 mask, 00135 values); 00136 00137 /* Map the window (= make it visible) */ 00138 xcb_map_window(conn, win); 00139 00140 return win; 00141 } 00142 00143 /* 00144 * Handle signals 00145 * It creates a window asking the user to restart in-place 00146 * or exit to generate a core dump 00147 * 00148 */ 00149 void handle_signal(int sig, siginfo_t *info, void *data) { 00150 DLOG("i3 crashed. SIG: %d\n", sig); 00151 00152 struct sigaction action; 00153 action.sa_handler = SIG_DFL; 00154 sigaction(sig, &action, NULL); 00155 raised_signal = sig; 00156 00157 /* width and height of the popup window, so that the text fits in */ 00158 int crash_text_num = sizeof(crash_text) / sizeof(char*); 00159 int height = 13 + (crash_text_num * config.font.height); 00160 00161 /* calculate width for longest text */ 00162 int text_len = strlen(crash_text[crash_text_longest]); 00163 char *longest_text = convert_utf8_to_ucs2(crash_text[crash_text_longest], &text_len); 00164 int font_width = predict_text_width(longest_text, text_len); 00165 int width = font_width + 20; 00166 00167 /* Open a popup window on each virtual screen */ 00168 Output *screen; 00169 xcb_window_t win; 00170 TAILQ_FOREACH(screen, &outputs, outputs) { 00171 if (!screen->active) 00172 continue; 00173 win = open_input_window(conn, screen->rect, width, height); 00174 00175 /* Create pixmap */ 00176 pixmap = xcb_generate_id(conn); 00177 pixmap_gc = xcb_generate_id(conn); 00178 xcb_create_pixmap(conn, root_depth, pixmap, win, width, height); 00179 xcb_create_gc(conn, pixmap_gc, pixmap, 0, 0); 00180 00181 /* Create graphics context */ 00182 xcb_change_gc_single(conn, pixmap_gc, XCB_GC_FONT, config.font.id); 00183 00184 /* Grab the keyboard to get all input */ 00185 xcb_grab_keyboard(conn, false, win, XCB_CURRENT_TIME, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC); 00186 00187 /* Grab the cursor inside the popup */ 00188 xcb_grab_pointer(conn, false, win, XCB_NONE, XCB_GRAB_MODE_ASYNC, 00189 XCB_GRAB_MODE_ASYNC, win, XCB_NONE, XCB_CURRENT_TIME); 00190 00191 sig_draw_window(win, width, height, config.font.height); 00192 xcb_flush(conn); 00193 } 00194 00195 xcb_generic_event_t *event; 00196 /* Yay, more own eventhandlers… */ 00197 while ((event = xcb_wait_for_event(conn))) { 00198 /* Strip off the highest bit (set if the event is generated) */ 00199 int type = (event->response_type & 0x7F); 00200 if (type == XCB_KEY_PRESS) { 00201 sig_handle_key_press(NULL, conn, (xcb_key_press_event_t*)event); 00202 } 00203 free(event); 00204 } 00205 } 00206 00207 /* 00208 * Setup signal handlers to safely handle SIGSEGV and SIGFPE 00209 * 00210 */ 00211 void setup_signal_handler() { 00212 struct sigaction action; 00213 00214 action.sa_sigaction = handle_signal; 00215 action.sa_flags = SA_NODEFER | SA_RESETHAND | SA_SIGINFO; 00216 sigemptyset(&action.sa_mask); 00217 00218 if (sigaction(SIGSEGV, &action, NULL) == -1 || 00219 sigaction(SIGABRT, &action, NULL) == -1 || 00220 sigaction(SIGFPE, &action, NULL) == -1) 00221 ELOG("Could not setup signal handler"); 00222 }