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 xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){ get_colorpixel("#FFFFFF") }); 00051 00052 for (int i = 0; i < sizeof(crash_text) / sizeof(char*); i++) { 00053 int text_len = strlen(crash_text[i]); 00054 char *full_text = convert_utf8_to_ucs2(crash_text[i], &text_len); 00055 xcb_image_text_16(conn, text_len, pixmap, pixmap_gc, 8 /* X */, 00056 3 + (i + 1) * font_height /* Y = baseline of font */, 00057 (xcb_char2b_t*)full_text); 00058 free(full_text); 00059 } 00060 00061 /* Copy the contents of the pixmap to the real window */ 00062 xcb_copy_area(conn, pixmap, win, pixmap_gc, 0, 0, 0, 0, width, height); 00063 xcb_flush(conn); 00064 00065 return 1; 00066 } 00067 00068 /* 00069 * Handles keypresses of 'e' or 'r' to exit or restart i3 00070 * 00071 */ 00072 static int sig_handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press_event_t *event) { 00073 uint16_t state = event->state; 00074 00075 /* Apparantly, after activating numlock once, the numlock modifier 00076 * stays turned on (use xev(1) to verify). So, to resolve useful 00077 * keysyms, we remove the numlock flag from the event state */ 00078 state &= ~xcb_numlock_mask; 00079 00080 xcb_keysym_t sym = xcb_key_press_lookup_keysym(keysyms, event, state); 00081 00082 if (sym == 'e') { 00083 DLOG("User issued exit-command, raising error again.\n"); 00084 raise(raised_signal); 00085 exit(1); 00086 } 00087 00088 if (sym == 'r') 00089 i3_restart(false); 00090 00091 if (sym == 'f') 00092 i3_restart(true); 00093 00094 return 1; 00095 } 00096 00097 /* 00098 * Opens the window we use for input/output and maps it 00099 * 00100 */ 00101 static xcb_window_t open_input_window(xcb_connection_t *conn, Rect screen_rect, uint32_t width, uint32_t height) { 00102 xcb_window_t win = xcb_generate_id(conn); 00103 00104 uint32_t mask = 0; 00105 uint32_t values[2]; 00106 00107 mask |= XCB_CW_BACK_PIXEL; 00108 values[0] = 0; 00109 00110 mask |= XCB_CW_OVERRIDE_REDIRECT; 00111 values[1] = 1; 00112 00113 /* center each popup on the specified screen */ 00114 uint32_t x = screen_rect.x + ((screen_rect.width / 2) - (width / 2)), 00115 y = screen_rect.y + ((screen_rect.height / 2) - (height / 2)); 00116 00117 xcb_create_window(conn, 00118 XCB_COPY_FROM_PARENT, 00119 win, /* the window id */ 00120 root, /* parent == root */ 00121 x, y, width, height, /* dimensions */ 00122 0, /* border = 0, we draw our own */ 00123 XCB_WINDOW_CLASS_INPUT_OUTPUT, 00124 XCB_WINDOW_CLASS_COPY_FROM_PARENT, /* copy visual from parent */ 00125 mask, 00126 values); 00127 00128 /* Map the window (= make it visible) */ 00129 xcb_map_window(conn, win); 00130 00131 return win; 00132 } 00133 00134 /* 00135 * Handle signals 00136 * It creates a window asking the user to restart in-place 00137 * or exit to generate a core dump 00138 * 00139 */ 00140 void handle_signal(int sig, siginfo_t *info, void *data) { 00141 DLOG("i3 crashed. SIG: %d\n", sig); 00142 00143 struct sigaction action; 00144 action.sa_handler = SIG_DFL; 00145 sigaction(sig, &action, NULL); 00146 raised_signal = sig; 00147 00148 /* width and height of the popup window, so that the text fits in */ 00149 int crash_text_num = sizeof(crash_text) / sizeof(char*); 00150 int height = 13 + (crash_text_num * config.font.height); 00151 00152 /* calculate width for longest text */ 00153 int text_len = strlen(crash_text[crash_text_longest]); 00154 char *longest_text = convert_utf8_to_ucs2(crash_text[crash_text_longest], &text_len); 00155 int font_width = predict_text_width(longest_text, text_len); 00156 int width = font_width + 20; 00157 00158 /* Open a popup window on each virtual screen */ 00159 Output *screen; 00160 xcb_window_t win; 00161 TAILQ_FOREACH(screen, &outputs, outputs) { 00162 if (!screen->active) 00163 continue; 00164 win = open_input_window(conn, screen->rect, width, height); 00165 00166 /* Create pixmap */ 00167 pixmap = xcb_generate_id(conn); 00168 pixmap_gc = xcb_generate_id(conn); 00169 xcb_create_pixmap(conn, root_depth, pixmap, win, width, height); 00170 xcb_create_gc(conn, pixmap_gc, pixmap, 0, 0); 00171 00172 /* Create graphics context */ 00173 xcb_change_gc(conn, pixmap_gc, XCB_GC_FONT, (uint32_t[]){ config.font.id }); 00174 00175 /* Grab the keyboard to get all input */ 00176 xcb_grab_keyboard(conn, false, win, XCB_CURRENT_TIME, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC); 00177 00178 /* Grab the cursor inside the popup */ 00179 xcb_grab_pointer(conn, false, win, XCB_NONE, XCB_GRAB_MODE_ASYNC, 00180 XCB_GRAB_MODE_ASYNC, win, XCB_NONE, XCB_CURRENT_TIME); 00181 00182 sig_draw_window(win, width, height, config.font.height); 00183 xcb_flush(conn); 00184 } 00185 00186 xcb_generic_event_t *event; 00187 /* Yay, more own eventhandlers… */ 00188 while ((event = xcb_wait_for_event(conn))) { 00189 /* Strip off the highest bit (set if the event is generated) */ 00190 int type = (event->response_type & 0x7F); 00191 if (type == XCB_KEY_PRESS) { 00192 sig_handle_key_press(NULL, conn, (xcb_key_press_event_t*)event); 00193 } 00194 free(event); 00195 } 00196 } 00197 00198 /* 00199 * Setup signal handlers to safely handle SIGSEGV and SIGFPE 00200 * 00201 */ 00202 void setup_signal_handler() { 00203 struct sigaction action; 00204 00205 action.sa_sigaction = handle_signal; 00206 action.sa_flags = SA_NODEFER | SA_RESETHAND | SA_SIGINFO; 00207 sigemptyset(&action.sa_mask); 00208 00209 if (sigaction(SIGSEGV, &action, NULL) == -1 || 00210 sigaction(SIGABRT, &action, NULL) == -1 || 00211 sigaction(SIGFPE, &action, NULL) == -1) 00212 ELOG("Could not setup signal handler"); 00213 }