]> git.sur5r.net Git - i3/i3/commitdiff
added popup for handling SIGSEGV or SIGFPE
authorJan-Erik Rediger <badboy@archlinux.us>
Sun, 3 Jan 2010 13:52:38 +0000 (14:52 +0100)
committerMichael Stapelberg <michael@stapelberg.de>
Sun, 3 Jan 2010 20:32:50 +0000 (21:32 +0100)
the popup is placed on each of the virtual screens
the user can decide to restart or quit i3
in case of an exit a core-dump is generated

include/sighandler.h [new file with mode: 0644]
include/util.h
src/commands.c
src/mainx.c
src/sighandler.c [new file with mode: 0644]
src/util.c

diff --git a/include/sighandler.h b/include/sighandler.h
new file mode 100644 (file)
index 0000000..cbb72cd
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * vim:ts=8:expandtab
+ *
+ * i3 - an improved dynamic tiling window manager
+ *
+ * © 2009 Michael Stapelberg and contributors
+ * © 2009 Jan-Erik Rediger
+ *
+ * See file LICENSE for license information.
+ *
+ */
+#ifndef _SIGHANDLER_H
+#define _SIGHANDLER_H
+
+/*
+ * Setup signal handlers to safely handle SIGSEGV and SIGFPE
+ *
+ */
+void setup_signal_handler();
+
+#endif
index f4e956043072906b201458c9763652b82fe7fdbf..e45fc75a9f85690341b8565f41e00cee990f63f4 100644 (file)
@@ -150,6 +150,13 @@ void switch_layout_mode(xcb_connection_t *conn, Container *container, int mode);
 Client *get_matching_client(xcb_connection_t *conn,
                             const char *window_classtitle, Client *specific);
 
+/*
+ * Restart i3 in-place
+ * appends -a to argument list to disable autostart
+ *
+ */
+void i3_restart();
+
 #if defined(__OpenBSD__)
 /* OpenBSD does not provide memmem(), so we provide FreeBSD’s implementation */
 void *memmem(const void *l, size_t l_len, const void *s, size_t s_len);
index 31564bb90fcf8436e194aaa31f2780b386547ba7..92b4aa8c98ddaa777402f17d6ed2c07d46474a1c 100644 (file)
@@ -31,6 +31,7 @@
 #include "commands.h"
 #include "resize.h"
 #include "log.h"
+#include "sighandler.h"
 
 bool focus_window_in_container(xcb_connection_t *conn, Container *container, direction_t direction) {
         /* If this container is empty, we’re done */
@@ -759,29 +760,6 @@ static void travel_focus_stack(xcb_connection_t *conn, const char *arguments) {
                 }
 }
 
-/*
- * Goes through the list of arguments (for exec()) and checks if the given argument
- * is present. If not, it copies the arguments (because we cannot realloc it) and
- * appends the given argument.
- *
- */
-static char **append_argument(char **original, char *argument) {
-        int num_args;
-        for (num_args = 0; original[num_args] != NULL; num_args++) {
-                DLOG("original argument: \"%s\"\n", original[num_args]);
-                /* If the argument is already present we return the original pointer */
-                if (strcmp(original[num_args], argument) == 0)
-                        return original;
-        }
-        /* Copy the original array */
-        char **result = smalloc((num_args+2) * sizeof(char*));
-        memcpy(result, original, num_args * sizeof(char*));
-        result[num_args] = argument;
-        result[num_args+1] = NULL;
-
-        return result;
-}
-
 /* 
  * Switch to next or previous existing workspace
  *
@@ -965,12 +943,7 @@ void parse_command(xcb_connection_t *conn, const char *command) {
 
         /* Is it <restart>? Then restart in place. */
         if (STARTS_WITH(command, "restart")) {
-                LOG("restarting \"%s\"...\n", start_argv[0]);
-                /* make sure -a is in the argument list or append it */
-                start_argv = append_argument(start_argv, "-a");
-
-                execvp(start_argv[0], start_argv);
-                /* not reached */
+                i3_restart();
         }
 
         if (STARTS_WITH(command, "kill")) {
index 3ff8c02246958ac13e7999029fc8ec5cd0e0b199..61e71613e9bc630799d3cb61630b2901d23a09a2 100644 (file)
@@ -49,6 +49,7 @@
 #include "manage.h"
 #include "ipc.h"
 #include "log.h"
+#include "sighandler.h"
 
 xcb_connection_t *global_conn;
 
@@ -499,6 +500,7 @@ int main(int argc, char *argv[], char *env[]) {
         /* Handle the events which arrived until now */
         xcb_check_cb(NULL, NULL, 0);
 
+        setup_signal_handler();
         /* Ungrab the server to receive events and enter libev’s eventloop */
         xcb_ungrab_server(conn);
         ev_loop(loop, 0);
diff --git a/src/sighandler.c b/src/sighandler.c
new file mode 100644 (file)
index 0000000..9eeb239
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+ * vim:ts=8:expandtab
+ *
+ * i3 - an improved dynamic tiling window manager
+ *
+ * © 2009 Michael Stapelberg and contributors
+ * © 2009 Jan-Erik Rediger
+ *
+ * See file LICENSE for license information.
+ *
+ * sighandler.c: contains all functions for signal handling
+ *
+ */
+#include <ev.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <iconv.h>
+#include <signal.h>
+
+#include <xcb/xcb.h>
+#include <xcb/xcb_aux.h>
+#include <xcb/xcb_event.h>
+#include <xcb/xcb_keysyms.h>
+
+#include <X11/keysym.h>
+
+#include "i3.h"
+#include "util.h"
+#include "xcb.h"
+#include "log.h"
+#include "config.h"
+#include "xinerama.h"
+
+static xcb_gcontext_t pixmap_gc;
+static xcb_pixmap_t pixmap;
+static int raised_signal;
+
+static char *crash_text[] = {
+        "i3 just crashed.",
+        "To debug this problem, either attach gdb now",
+        "or press 'e' to exit and get a core-dump.",
+        "If you want to keep your session,",
+        "press 'r' to restart i3 in-place."
+};
+static int crash_text_longest = 1;
+
+/*
+ * Draw the window containing the info text
+ *
+ */
+static int sig_draw_window(xcb_connection_t *conn, xcb_window_t win, int width, int height, int font_height) {
+        /* re-draw the background */
+        xcb_rectangle_t border = {0, 0, width, height}, inner = {2, 2, width-4, height-4};
+        xcb_change_gc_single(conn, pixmap_gc, XCB_GC_FOREGROUND, get_colorpixel(conn, "#FF0000"));
+        xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &border);
+        xcb_change_gc_single(conn, pixmap_gc, XCB_GC_FOREGROUND, get_colorpixel(conn, "#000000"));
+        xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &inner);
+
+        /* restore font color */
+        xcb_change_gc_single(conn, pixmap_gc, XCB_GC_FOREGROUND, get_colorpixel(conn, "#FFFFFF"));
+
+        char *full_text;
+        int text_len;
+        int i;
+        int crash_text_num = sizeof(crash_text) / sizeof(char*);
+        for (i = 0; i < crash_text_num; i++) {
+                text_len = strlen(crash_text[i]);
+                full_text = convert_utf8_to_ucs2(crash_text[i], &text_len);
+                xcb_image_text_16(conn, text_len, pixmap, pixmap_gc, 8 /* X */,
+                                3 + (i+1)*font_height /* Y = baseline of font */, (xcb_char2b_t*)full_text);
+                free(full_text);
+        }
+
+        /* Copy the contents of the pixmap to the real window */
+        xcb_copy_area(conn, pixmap, win, pixmap_gc, 0, 0, 0, 0, width, height);
+        xcb_flush(conn);
+
+        return 1;
+}
+
+
+/*
+ * Handles keypresses of 'e' or 'r' to exit or restart i3
+ *
+ */
+static int sig_handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press_event_t *event) {
+        xcb_keysym_t sym = xcb_key_press_lookup_keysym(keysyms, event, event->state);
+
+        if (sym == 'e') {
+                LOG("User issued exit-command, raising error again.\n");
+                raise(raised_signal);
+                exit(1);
+        }
+        if (sym == 'r')
+                i3_restart();
+
+        return 1;
+}
+
+/*
+ * Opens the window we use for input/output and maps it
+ *
+ */
+static xcb_window_t open_input_window(xcb_connection_t *conn, Rect screen_rect, uint32_t width, uint32_t height) {
+        xcb_window_t root = xcb_setup_roots_iterator(xcb_get_setup(conn)).data->root;
+        xcb_window_t win = xcb_generate_id(conn);
+
+        uint32_t mask = 0;
+        uint32_t values[3];
+
+        mask |= XCB_CW_BACK_PIXEL;
+        values[0] = 0;
+
+        mask |= XCB_CW_OVERRIDE_REDIRECT;
+        values[1] = 1;
+
+        mask |= XCB_CW_EVENT_MASK;
+        values[2] = XCB_EVENT_MASK_EXPOSURE;
+
+        /* center each popup on the specified screen */
+        uint32_t x = screen_rect.x + ((screen_rect.width / 2) - (width/2)),
+                 y = screen_rect.y + ((screen_rect.height / 2) - (height/2));
+
+        xcb_create_window(conn,
+                        XCB_COPY_FROM_PARENT,
+                        win, /* the window id */
+                        root, /* parent == root */
+                        x, y, width, height, /* dimensions */
+                        0, /* border = 0, we draw our own */
+                        XCB_WINDOW_CLASS_INPUT_OUTPUT,
+                        XCB_WINDOW_CLASS_COPY_FROM_PARENT, /* copy visual from parent */
+                        mask,
+                        values);
+
+        /* Map the window (= make it visible) */
+        xcb_map_window(conn, win);
+
+        return win;
+}
+
+/*
+ * Handle signals
+ * It creates a window asking the user to restart in-place
+ * or exit to generate a core dump
+ *
+ */
+void handle_signal(int sig, siginfo_t *info, void *data) {
+        LOG("i3 crashed. SIG: %d\n", sig);
+
+        struct sigaction action;
+        action.sa_handler = SIG_DFL;
+        sigaction(sig, &action, NULL);
+        raised_signal = sig;
+
+        xcb_connection_t *conn = global_conn;
+
+        /* Set up event handlers for key press and key release */
+        xcb_event_handlers_t sig_evenths;
+        memset(&sig_evenths, 0, sizeof(xcb_event_handlers_t));
+        xcb_event_handlers_init(conn, &sig_evenths);
+        xcb_event_set_key_press_handler(&sig_evenths, sig_handle_key_press, NULL);
+
+        i3Font *font = load_font(conn, config.font);
+
+        /* width and height of the popup window, so that the text fits in */
+        int crash_text_num = sizeof(crash_text) / sizeof(char*);
+        int height = 13 + (crash_text_num * font->height);
+
+        /* calculate width for longest text */
+        int text_len = strlen(crash_text[crash_text_longest]);
+        char *longest_text = convert_utf8_to_ucs2(crash_text[crash_text_longest], &text_len);
+        int font_width = predict_text_width(conn, config.font, longest_text, text_len);
+        int width = font_width + 20;
+
+        /* Open an popup window on each virtual screen */
+        i3Screen *screen;
+        xcb_window_t win;
+        TAILQ_FOREACH(screen, virtual_screens, screens) {
+                LOG("%d, %d, %d, %d\n", screen->rect.width, screen->rect.height, screen->rect.x, screen->rect.y);
+
+                win = open_input_window(conn, screen->rect, width, height);
+
+                /* Create pixmap */
+                pixmap = xcb_generate_id(conn);
+                pixmap_gc = xcb_generate_id(conn);
+                xcb_create_pixmap(conn, root_depth, pixmap, win, width, height);
+                xcb_create_gc(conn, pixmap_gc, pixmap, 0, 0);
+
+                /* Create graphics context */
+                xcb_change_gc_single(conn, pixmap_gc, XCB_GC_FONT, font->id);
+
+                /* Grab the keyboard to get all input */
+                xcb_grab_keyboard(conn, false, win, XCB_CURRENT_TIME, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
+
+                sig_draw_window(conn, win, width, height, font->height);
+                xcb_flush(conn);
+        }
+
+        xcb_event_wait_for_event_loop(&sig_evenths);
+}
+
+/*
+ * Setup signal handlers to safely handle SIGSEGV and SIGFPE
+ *
+ */
+void setup_signal_handler() {
+        struct sigaction action;
+        action.sa_sigaction = handle_signal;
+        action.sa_flags = SA_NODEFER | SA_RESETHAND | SA_SIGINFO;
+        sigemptyset(&action.sa_mask);
+        sigaction(SIGSEGV, &action, NULL);
+        sigaction(SIGFPE, &action, NULL);
+}
index bc54caa553614d11ef1c48388d8fb5dbb15b4986..ba4bd6ddb889524123aec528ef70a2f4b5627e26 100644 (file)
@@ -159,16 +159,16 @@ void check_error(xcb_connection_t *conn, xcb_void_cookie_t cookie, char *err_mes
  *
  */
 char *convert_utf8_to_ucs2(char *input, int *real_strlen) {
-       size_t input_size = strlen(input) + 1;
-       /* UCS-2 consumes exactly two bytes for each glyph */
-       int buffer_size = input_size * 2;
+        size_t input_size = strlen(input) + 1;
+        /* UCS-2 consumes exactly two bytes for each glyph */
+        int buffer_size = input_size * 2;
 
-       char *buffer = smalloc(buffer_size);
-       size_t output_size = buffer_size;
-       /* We need to use an additional pointer, because iconv() modifies it */
-       char *output = buffer;
+        char *buffer = smalloc(buffer_size);
+        size_t output_size = buffer_size;
+        /* We need to use an additional pointer, because iconv() modifies it */
+        char *output = buffer;
 
-       /* We convert the input into UCS-2 big endian */
+        /* We convert the input into UCS-2 big endian */
         if (conversion_descriptor == 0) {
                 conversion_descriptor = iconv_open("UCS-2BE", "UTF-8");
                 if (conversion_descriptor == 0) {
@@ -177,22 +177,22 @@ char *convert_utf8_to_ucs2(char *input, int *real_strlen) {
                 }
         }
 
-       /* Get the conversion descriptor back to original state */
-       iconv(conversion_descriptor, NULL, NULL, NULL, NULL);
+        /* Get the conversion descriptor back to original state */
+        iconv(conversion_descriptor, NULL, NULL, NULL, NULL);
 
-       /* Convert our text */
-       int rc = iconv(conversion_descriptor, (void*)&input, &input_size, &output, &output_size);
+        /* Convert our text */
+        int rc = iconv(conversion_descriptor, (void*)&input, &input_size, &output, &output_size);
         if (rc == (size_t)-1) {
                 perror("Converting to UCS-2 failed");
                 if (real_strlen != NULL)
-                       *real_strlen = 0;
+                        *real_strlen = 0;
                 return NULL;
-       }
+        }
 
         if (real_strlen != NULL)
-               *real_strlen = ((buffer_size - output_size) / 2) - 1;
+                *real_strlen = ((buffer_size - output_size) / 2) - 1;
 
-       return buffer;
+        return buffer;
 }
 
 /*
@@ -463,6 +463,43 @@ done:
         return matching;
 }
 
+/*
+ * Goes through the list of arguments (for exec()) and checks if the given argument
+ * is present. If not, it copies the arguments (because we cannot realloc it) and
+ * appends the given argument.
+ *
+ */
+static char **append_argument(char **original, char *argument) {
+        int num_args;
+        for (num_args = 0; original[num_args] != NULL; num_args++) {
+                DLOG("original argument: \"%s\"\n", original[num_args]);
+                /* If the argument is already present we return the original pointer */
+                if (strcmp(original[num_args], argument) == 0)
+                        return original;
+        }
+        /* Copy the original array */
+        char **result = smalloc((num_args+2) * sizeof(char*));
+        memcpy(result, original, num_args * sizeof(char*));
+        result[num_args] = argument;
+        result[num_args+1] = NULL;
+
+        return result;
+}
+
+/*
+ * Restart i3 in-place
+ * appends -a to argument list to disable autostart
+ *
+ */
+void i3_restart() {
+        LOG("restarting \"%s\"...\n", start_argv[0]);
+        /* make sure -a is in the argument list or append it */
+        start_argv = append_argument(start_argv, "-a");
+
+        execvp(start_argv[0], start_argv);
+        /* not reached */
+}
+
 #if defined(__OpenBSD__)
 
 /*