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