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