]> git.sur5r.net Git - i3/i3/commitdiff
Merge pull request #2649 from s3rb31/next
authorIngo Bürk <admin@airblader.de>
Thu, 23 Feb 2017 23:58:19 +0000 (00:58 +0100)
committerGitHub <noreply@github.com>
Thu, 23 Feb 2017 23:58:19 +0000 (00:58 +0100)
layout toggle: take any combination of layouts as arguments (continuation of #2476)

20 files changed:
.github/GOVERNANCE.md [new file with mode: 0644]
docs/ipc
docs/layout-saving
docs/userguide
i3-config-wizard/main.c
i3-input/main.c
i3-nagbar/main.c
i3bar/src/ipc.c
i3bar/src/xcb.c
include/i3/ipc.h
include/ipc.h
include/libi3.h
src/click.c
src/commands.c
src/handlers.c
src/ipc.c
src/restore_layout.c
src/util.c
testcases/complete-run.pl.in
testcases/t/264-ipc-shutdown-event.t [new file with mode: 0644]

diff --git a/.github/GOVERNANCE.md b/.github/GOVERNANCE.md
new file mode 100644 (file)
index 0000000..44e1334
--- /dev/null
@@ -0,0 +1,30 @@
+# i3 project governance
+
+## Overview
+
+The i3 project uses a governance model commonly described as Benevolent
+Dictator For Life (BDFL). This document outlines our understanding of what this
+means.
+
+## Roles
+
+* user: anyone who interacts with the i3 project
+* core contributor: a handful of people who have contributed significantly to
+  the project by any means (issue triage, support, documentation, code, etc.).
+  Core contributors are recognizable via GitHub’s “Member” badge.
+* BDFL: a single individual who makes decisions when consensus cannot be
+  reached. i3’s current BDFL is [@stapelberg](https://github.com/stapelberg).
+
+## Decision making process
+
+In general, we try to reach consensus in discussions. In case consensus cannot
+be reached, the BDFL makes a decision.
+
+For feature requests and code contributions specifically, the values with which
+we consider them can be found on the bottom of https://i3wm.org/. These values
+are not set in stone and are to be treated as guiding principles, not absolute
+rules that must be followed in every case.
+
+## Contribution process
+
+Please see [CONTRIBUTING](CONTRIBUTING.md).
index fda289a0a21c2776d30c29a7c4207c7b6484b3bb..466b6596d77c0d5d9e668c821340b20e095d2986 100644 (file)
--- a/docs/ipc
+++ b/docs/ipc
@@ -671,6 +671,8 @@ barconfig_update (4)::
 binding (5)::
        Sent when a configured command binding is triggered with the keyboard or
        mouse
+shutdown (6)::
+       Sent when the ipc shuts down because of a restart or exit by user command
 
 *Example:*
 --------------------------------------------------------------------
@@ -694,9 +696,9 @@ if ($is_event) {
 
 This event consists of a single serialized map containing a property
 +change (string)+ which indicates the type of the change ("focus", "init",
-"empty", "urgent"). A +current (object)+ property will be present with the
-affected workspace whenever the type of event affects a workspace (otherwise,
-it will be +null).
+"empty", "urgent", "reload", "rename", "restored"). A +current (object)+
+property will be present with the affected workspace whenever the type of event
+affects a workspace (otherwise, it will be +null).
 
 When the change is "focus", an +old (object)+ property will be present with the
 previous workspace.  When the first switch occurs (when i3 focuses the
@@ -829,6 +831,20 @@ input_type (string)::
 }
 ---------------------------
 
+=== shutdown event
+
+This event is triggered when the connection to the ipc is about to shutdown
+because of a user action such as a +restart+ or +exit+ command. The +change
+(string)+ field indicates why the ipc is shutting down. It can be either
++"restart"+ or +"exit"+.
+
+*Example:*
+---------------------------
+{
+ "change": "restart"
+}
+---------------------------
+
 == See also (existing libraries)
 
 [[libraries]]
index 5897036e5143f310e2b665e68db6174f7318198b..6ca08fa239dbe7bd38ac2221e987c153cf3162c0 100644 (file)
@@ -259,3 +259,27 @@ container:
     ]
 }
 --------------------------------------------------------------------------------
+
+=== Placeholders using window title matches don't swallow the window
+
+If you use the +title+ attribute to match a window and find that it doesn't
+work or only works sometimes, the reason might be that the application sets the
+title only after making the window visible. This will be especially true for
+programs running inside terminal emulators, e.g., +urxvt -e irssi+ when
+matching on +title: "irssi"+.
+
+One way to deal with this is to not rely on the title, but instead use, e.g.,
+the +instance+ attribute and running the program to set this window instance to
+that value:
+
+--------------------------------------------------------------------------------
+# Run irssi via
+# urxvt -name "irssi-container" -e irssi
+
+"swallows": [
+    {
+        "class": "URxvt",
+        "instance": "irssi-container"
+    }
+]
+--------------------------------------------------------------------------------
index 21d0862521edb5e698172a9f13773b57a0a4b84a..40e9e3b9ea24f63328643fe317bc23641a291b61 100644 (file)
@@ -479,7 +479,7 @@ mode <name>
 
 *Example*:
 ------------------------------------------------------------------------
-# Press $mod+o followed by either f, t, Esc or Return to launch firefox,
+# Press $mod+o followed by either f, t, Escape or Return to launch firefox,
 # thunderbird or return to the default mode, respectively.
 set $mode_launcher Launch: [f]irefox [t]hunderbird
 bindsym $mod+o mode "$mode_launcher"
@@ -488,7 +488,7 @@ mode "$mode_launcher" {
     bindsym f exec firefox
     bindsym t exec thunderbird
 
-    bindsym Esc mode "default"
+    bindsym Escape mode "default"
     bindsym Return mode "default"
 }
 ------------------------------------------------------------------------
@@ -946,12 +946,12 @@ the next section.
 
 === Focus follows mouse
 
-By default, window focus follows your mouse movements. However, if you have a
-setup where your mouse usually is in your way (like a touchpad on your laptop
-which you do not want to disable completely), you might want to disable 'focus
-follows mouse' and control focus only by using your keyboard.  The mouse will
-still be useful inside the currently active window (for example to click on
-links in your browser window).
+By default, window focus follows your mouse movements as the mouse crosses
+window borders. However, if you have a setup where your mouse usually is in your
+way (like a touchpad on your laptop which you do not want to disable
+completely), you might want to disable 'focus follows mouse' and control focus
+only by using your keyboard.  The mouse will still be useful inside the
+currently active window (for example to click on links in your browser window).
 
 *Syntax*:
 --------------------------
@@ -2106,6 +2106,23 @@ i3-msg 'rename workspace to "2: mail"'
 bindsym $mod+r exec i3-input -F 'rename workspace to "%s"' -P 'New name: '
 --------------------------------------------------------------------------
 
+If you want to rename workspaces on demand while keeping the navigation stable,
+you can use a setup like this:
+
+*Example*:
+-------------------------
+bindsym $mod+1 workspace number "1: www"
+bindsym $mod+2 workspace number "2: mail"
+...
+-------------------------
+
+If a workspace does not exist, the command +workspace number "1: mail"+ will
+create workspace "1: mail".
+
+If a workspace with number 1 does already exist, the command will switch to this
+workspace and ignore the text part. So even when the workspace has been renamed
+to "1: web", the above command will still switch to it.
+
 === Moving workspaces to a different screen
 
 See <<move_to_outputs>> for how to move a container/workspace to a different
index 8eec941c758144f41e216d7ecdbf498a34677c63..dd58fd124547df7dcee0ce40785784eb1e43be4d 100644 (file)
 #include "xcb.h"
 #include "libi3.h"
 
+#define TEXT_PADDING logical_px(4)
+#define WIN_POS_X logical_px(490)
+#define WIN_POS_Y logical_px(297)
+#define WIN_WIDTH logical_px(300)
+#define WIN_HEIGHT (15 * font.height + TEXT_PADDING)
+
+#define col_x(col) \
+    (((col)-1) * char_width + TEXT_PADDING)
 #define row_y(row) \
-    (((row)-1) * font.height + logical_px(4))
-#define window_height() \
-    (row_y(15) + font.height)
+    (((row)-1) * font.height + TEXT_PADDING)
 
 enum { STEP_WELCOME,
        STEP_GENERATE } current_step = STEP_WELCOME;
@@ -90,8 +96,7 @@ static i3Font bold_font;
 static int char_width;
 static char *socket_path;
 static xcb_window_t win;
-static xcb_pixmap_t pixmap;
-static xcb_gcontext_t pixmap_gc;
+static surface_t surface;
 static xcb_key_symbols_t *symbols;
 xcb_window_t root;
 static struct xkb_keymap *xkb_keymap;
@@ -463,82 +468,73 @@ void errorlog(char *fmt, ...) {
 void debuglog(char *fmt, ...) {
 }
 
+static void txt(int col, int row, char *text, color_t fg, color_t bg) {
+    int x = col_x(col);
+    int y = row_y(row);
+    i3String *string = i3string_from_utf8(text);
+    draw_util_text(string, &surface, fg, bg, x, y, WIN_WIDTH - x - TEXT_PADDING);
+    i3string_free(string);
+}
+
 /*
  * Handles expose events, that is, draws the window contents.
  *
  */
 static int handle_expose() {
-    /* re-draw the background */
-    xcb_rectangle_t border = {0, 0, logical_px(300), window_height()};
-    xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){get_colorpixel("#000000")});
-    xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &border);
+    const color_t black = draw_util_hex_to_color("#000000");
+    const color_t white = draw_util_hex_to_color("#FFFFFF");
+    const color_t green = draw_util_hex_to_color("#00FF00");
+    const color_t red = draw_util_hex_to_color("#FF0000");
 
-    set_font(&font);
+    /* draw background */
+    draw_util_clear_surface(&surface, black);
 
-#define txt(x, row, text)                    \
-    draw_text_ascii(text, pixmap, pixmap_gc, \
-                    x, row_y(row), logical_px(500) - x * 2)
+    set_font(&font);
 
     if (current_step == STEP_WELCOME) {
-        /* restore font color */
-        set_font_colors(pixmap_gc, draw_util_hex_to_color("#FFFFFF"), draw_util_hex_to_color("#000000"));
-
-        txt(logical_px(10), 2, "You have not configured i3 yet.");
-        txt(logical_px(10), 3, "Do you want me to generate a config at");
+        txt(2, 2, "You have not configured i3 yet.", white, black);
+        txt(2, 3, "Do you want me to generate a config at", white, black);
 
         char *msg;
         sasprintf(&msg, "%s?", config_path);
-        txt(logical_px(10), 4, msg);
+        txt(2, 4, msg, white, black);
         free(msg);
 
-        txt(logical_px(85), 6, "Yes, generate the config");
-        txt(logical_px(85), 8, "No, I will use the defaults");
+        txt(13, 6, "Yes, generate the config", white, black);
+        txt(13, 8, "No, I will use the defaults", white, black);
 
-        /* green */
-        set_font_colors(pixmap_gc, draw_util_hex_to_color("#00FF00"), draw_util_hex_to_color("#000000"));
-        txt(logical_px(25), 6, "<Enter>");
+        txt(4, 6, "<Enter>", green, black);
 
-        /* red */
-        set_font_colors(pixmap_gc, draw_util_hex_to_color("#FF0000"), draw_util_hex_to_color("#000000"));
-        txt(logical_px(31), 8, "<ESC>");
+        txt(5, 8, "<ESC>", red, black);
     }
 
     if (current_step == STEP_GENERATE) {
-        set_font_colors(pixmap_gc, draw_util_hex_to_color("#FFFFFF"), draw_util_hex_to_color("#000000"));
-
-        txt(logical_px(10), 2, "Please choose either:");
-        txt(logical_px(85), 4, "Win as default modifier");
-        txt(logical_px(85), 5, "Alt as default modifier");
-        txt(logical_px(10), 7, "Afterwards, press");
-        txt(logical_px(85), 9, "to write the config");
-        txt(logical_px(85), 10, "to abort");
+        txt(2, 2, "Please choose either:", white, black);
+        txt(13, 4, "Win as default modifier", white, black);
+        txt(13, 5, "Alt as default modifier", white, black);
+        txt(2, 7, "Afterwards, press", white, black);
+        txt(13, 9, "to write the config", white, black);
+        txt(13, 10, "to abort", white, black);
 
         /* the not-selected modifier */
         if (modifier == MOD_Mod4)
-            txt(logical_px(31), 5, "<Alt>");
+            txt(5, 5, "<Alt>", white, black);
         else
-            txt(logical_px(31), 4, "<Win>");
+            txt(5, 4, "<Win>", white, black);
 
         /* the selected modifier */
         set_font(&bold_font);
-        set_font_colors(pixmap_gc, draw_util_hex_to_color("#FFFFFF"), draw_util_hex_to_color("#000000"));
         if (modifier == MOD_Mod4)
-            txt(logical_px(10), 4, "-> <Win>");
+            txt(2, 4, "-> <Win>", white, black);
         else
-            txt(logical_px(10), 5, "-> <Alt>");
+            txt(2, 5, "-> <Alt>", white, black);
 
-        /* green */
         set_font(&font);
-        set_font_colors(pixmap_gc, draw_util_hex_to_color("#00FF00"), draw_util_hex_to_color("#000000"));
-        txt(logical_px(25), 9, "<Enter>");
+        txt(4, 9, "<Enter>", green, black);
 
-        /* red */
-        set_font_colors(pixmap_gc, draw_util_hex_to_color("#FF0000"), draw_util_hex_to_color("#000000"));
-        txt(logical_px(31), 10, "<ESC>");
+        txt(5, 10, "<ESC>", red, black);
     }
 
-    /* Copy the contents of the pixmap to the real window */
-    xcb_copy_area(conn, pixmap, win, pixmap_gc, 0, 0, 0, 0, logical_px(500), logical_px(500));
     xcb_flush(conn);
 
     return 1;
@@ -625,8 +621,7 @@ static void handle_button_press(xcb_button_press_event_t *event) {
     if (current_step != STEP_GENERATE)
         return;
 
-    if (event->event_x < logical_px(32) ||
-        event->event_x > (logical_px(32) + char_width * 5))
+    if (event->event_x < col_x(5) || event->event_x > col_x(10))
         return;
 
     if (event->event_y >= row_y(4) && event->event_y <= (row_y(4) + font.height)) {
@@ -867,10 +862,10 @@ int main(int argc, char *argv[]) {
     xcb_create_window(
         conn,
         XCB_COPY_FROM_PARENT,
-        win,                                                                /* the window id */
-        root,                                                               /* parent == root */
-        logical_px(490), logical_px(297), logical_px(300), window_height(), /* dimensions */
-        0,                                                                  /* X11 border = 0, we draw our own */
+        win,                                         /* the window id */
+        root,                                        /* parent == root */
+        WIN_POS_X, WIN_POS_Y, WIN_WIDTH, WIN_HEIGHT, /* dimensions */
+        0,                                           /* X11 border = 0, we draw our own */
         XCB_WINDOW_CLASS_INPUT_OUTPUT,
         XCB_WINDOW_CLASS_COPY_FROM_PARENT, /* copy visual from parent */
         XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK,
@@ -915,11 +910,8 @@ int main(int argc, char *argv[]) {
                         strlen("i3: first configuration"),
                         "i3: first configuration");
 
-    /* Create pixmap */
-    pixmap = xcb_generate_id(conn);
-    pixmap_gc = xcb_generate_id(conn);
-    xcb_create_pixmap(conn, root_screen->root_depth, pixmap, win, logical_px(500), logical_px(500));
-    xcb_create_gc(conn, pixmap_gc, pixmap, 0, 0);
+    /* Initialize drawable surface */
+    draw_util_surface_init(conn, &surface, win, get_visualtype(root_screen), WIN_WIDTH, WIN_HEIGHT);
 
     /* Grab the keyboard to get all input */
     xcb_flush(conn);
@@ -976,5 +968,8 @@ int main(int argc, char *argv[]) {
         free(event);
     }
 
+    /* Dismiss drawable surface */
+    draw_util_surface_free(conn, &surface);
+
     return 0;
 }
index 6d1e337811102b041376e0a997172f20c34e8778..785a133fc10e7de57b307821b1c3302ee0c2069b 100644 (file)
 
 #include "i3-input.h"
 
+#define MAX_WIDTH logical_px(500)
+#define BORDER logical_px(2)
+#define PADDING logical_px(2)
+
 /* IPC format string. %s will be replaced with what the user entered, then
  * the command will be sent to i3 */
 static char *format;
@@ -42,8 +46,7 @@ static int sockfd;
 static xcb_key_symbols_t *symbols;
 static bool modeswitch_active = false;
 static xcb_window_t win;
-static xcb_pixmap_t pixmap;
-static xcb_gcontext_t pixmap_gc;
+static surface_t surface;
 static xcb_char2b_t glyphs_ucs[512];
 static char *glyphs_utf8[512];
 static int input_position;
@@ -109,30 +112,30 @@ static uint8_t *concat_strings(char **glyphs, int max) {
 static int handle_expose(void *data, xcb_connection_t *conn, xcb_expose_event_t *event) {
     printf("expose!\n");
 
-    /* re-draw the background */
-    xcb_rectangle_t border = {0, 0, logical_px(500), font.height + logical_px(8)},
-                    inner = {logical_px(2), logical_px(2), logical_px(496), font.height + logical_px(8) - logical_px(4)};
-    xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){get_colorpixel("#FF0000")});
-    xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &border);
-    xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){get_colorpixel("#000000")});
-    xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &inner);
+    color_t border_color = draw_util_hex_to_color("#FF0000");
+    color_t fg_color = draw_util_hex_to_color("#FFFFFF");
+    color_t bg_color = draw_util_hex_to_color("#000000");
+
+    int text_offset = BORDER + PADDING;
 
-    /* restore font color */
-    set_font_colors(pixmap_gc, draw_util_hex_to_color("#FFFFFF"), draw_util_hex_to_color("#000000"));
+    /* draw border */
+    draw_util_rectangle(&surface, border_color, 0, 0, surface.width, surface.height);
+
+    /* draw background */
+    draw_util_rectangle(&surface, bg_color, BORDER, BORDER, surface.width - 2 * BORDER, surface.height - 2 * BORDER);
 
     /* draw the prompt … */
     if (prompt != NULL) {
-        draw_text(prompt, pixmap, pixmap_gc, NULL, logical_px(4), logical_px(4), logical_px(492));
+        draw_util_text(prompt, &surface, fg_color, bg_color, text_offset, text_offset, MAX_WIDTH - text_offset);
     }
+
     /* … and the text */
     if (input_position > 0) {
         i3String *input = i3string_from_ucs2(glyphs_ucs, input_position);
-        draw_text(input, pixmap, pixmap_gc, NULL, prompt_offset + logical_px(4), logical_px(4), logical_px(492));
+        draw_util_text(input, &surface, fg_color, bg_color, text_offset + prompt_offset, text_offset, MAX_WIDTH - text_offset);
         i3string_free(input);
     }
 
-    /* Copy the contents of the pixmap to the real window */
-    xcb_copy_area(conn, pixmap, win, pixmap_gc, 0, 0, 0, 0, logical_px(500), font.height + logical_px(8));
     xcb_flush(conn);
 
     return 1;
@@ -287,7 +290,7 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press
 }
 
 static xcb_rectangle_t get_window_position(void) {
-    xcb_rectangle_t result = (xcb_rectangle_t){logical_px(50), logical_px(50), logical_px(500), font.height + logical_px(8)};
+    xcb_rectangle_t result = (xcb_rectangle_t){logical_px(50), logical_px(50), MAX_WIDTH, font.height + 2 * BORDER + 2 * PADDING};
 
     xcb_get_property_reply_t *supporting_wm_reply = NULL;
     xcb_get_input_focus_reply_t *input_focus = NULL;
@@ -448,6 +451,7 @@ int main(int argc, char *argv[]) {
 
     symbols = xcb_key_symbols_alloc(conn);
 
+    init_dpi();
     font = load_font(pattern, true);
     set_font(&font);
 
@@ -476,11 +480,8 @@ int main(int argc, char *argv[]) {
     /* Map the window (make it visible) */
     xcb_map_window(conn, win);
 
-    /* Create pixmap */
-    pixmap = xcb_generate_id(conn);
-    pixmap_gc = xcb_generate_id(conn);
-    xcb_create_pixmap(conn, root_screen->root_depth, pixmap, win, logical_px(500), font.height + logical_px(8));
-    xcb_create_gc(conn, pixmap_gc, pixmap, 0, 0);
+    /* Initialize the drawable surface */
+    draw_util_surface_init(conn, &surface, win, get_visualtype(root_screen), win_pos.width, win_pos.height);
 
     /* Grab the keyboard to get all input */
     xcb_flush(conn);
@@ -535,5 +536,6 @@ int main(int argc, char *argv[]) {
         free(event);
     }
 
+    draw_util_surface_free(conn, &surface);
     return 0;
 }
index eb25e9cb0bd3c77b726621ba180b3fd2aa771bd4..7d38f73140d741c635aa0de25bd0e5809d94df75 100644 (file)
  * constant for that. */
 #define XCB_CURSOR_LEFT_PTR 68
 
+#define MSG_PADDING logical_px(8)
+#define BTN_PADDING logical_px(3)
+#define BTN_BORDER logical_px(3)
+#define BTN_GAP logical_px(20)
+#define CLOSE_BTN_GAP logical_px(15)
+#define BAR_BORDER logical_px(2)
+
 static char *argv0 = NULL;
 
 typedef struct {
@@ -48,11 +55,12 @@ typedef struct {
 } button_t;
 
 static xcb_window_t win;
-static xcb_pixmap_t pixmap;
-static xcb_gcontext_t pixmap_gc;
-static xcb_rectangle_t rect = {0, 0, 600, 20};
+static surface_t bar;
+
 static i3Font font;
 static i3String *prompt;
+
+static button_t btn_close;
 static button_t *buttons;
 static int buttoncnt;
 
@@ -138,7 +146,7 @@ static void handle_button_release(xcb_connection_t *conn, xcb_button_release_eve
     printf("button released on x = %d, y = %d\n",
            event->event_x, event->event_y);
     /* If the user hits the close button, we exit(0) */
-    if (event->event_x >= (rect.width - logical_px(32)))
+    if (event->event_x >= btn_close.x && event->event_x < btn_close.x + btn_close.width)
         exit(0);
     button_t *button = get_button_at(event->event_x, event->event_y);
     if (!button)
@@ -190,108 +198,64 @@ static void handle_button_release(xcb_connection_t *conn, xcb_button_release_eve
     /* TODO: unset flag, re-render */
 }
 
+/*
+ * Draws a button and returns its width
+ *
+ */
+static int button_draw(button_t *button, int position) {
+    int text_width = predict_text_width(button->label);
+    button->width = text_width + 2 * BTN_PADDING + 2 * BTN_BORDER;
+    button->x = position - button->width;
+
+    /* draw border */
+    draw_util_rectangle(&bar, color_border,
+                        position - button->width,
+                        MSG_PADDING - BTN_PADDING - BTN_BORDER,
+                        button->width,
+                        font.height + 2 * BTN_PADDING + 2 * BTN_BORDER);
+    /* draw background */
+    draw_util_rectangle(&bar, color_button_background,
+                        position - button->width + BTN_BORDER,
+                        MSG_PADDING - BTN_PADDING,
+                        text_width + 2 * BTN_PADDING,
+                        font.height + 2 * BTN_PADDING);
+    /* draw label */
+    draw_util_text(button->label, &bar, color_text, color_button_background,
+                   position - button->width + BTN_BORDER + BTN_PADDING,
+                   MSG_PADDING,
+                   200);
+    return button->width;
+}
+
 /*
  * Handles expose events (redraws of the window) and rendering in general. Will
  * be called from the code with event == NULL or from X with event != NULL.
  *
  */
 static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) {
-    /* re-draw the background */
-    xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){color_background.colorpixel});
-    xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &rect);
+    /* draw background */
+    draw_util_clear_surface(&bar, color_background);
+    /* draw message */
+    draw_util_text(prompt, &bar, color_text, color_background,
+                   MSG_PADDING, MSG_PADDING,
+                   bar.width - 2 * MSG_PADDING);
 
-    /* restore font color */
-    set_font_colors(pixmap_gc, color_text, color_background);
-    draw_text(prompt, pixmap, pixmap_gc, NULL,
-              logical_px(4) + logical_px(4),
-              logical_px(4) + logical_px(4),
-              rect.width - logical_px(4) - logical_px(4));
+    int position = bar.width - (MSG_PADDING - BTN_BORDER - BTN_PADDING);
 
     /* render close button */
-    const char *close_button_label = "X";
-    int line_width = logical_px(4);
-    /* set width to the width of the label */
-    int w = predict_text_width(i3string_from_utf8(close_button_label));
-    /* account for left/right padding, which seems to be set to 8px (total) below */
-    w += logical_px(8);
-    int y = rect.width;
-    uint32_t values[3];
-    values[0] = color_button_background.colorpixel;
-    values[1] = line_width;
-    xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND | XCB_GC_LINE_WIDTH, values);
-
-    xcb_rectangle_t close = {y - w - (2 * line_width), 0, w + (2 * line_width), rect.height};
-    xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &close);
-
-    xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){color_border.colorpixel});
-    xcb_point_t points[] = {
-        {y - w - (2 * line_width), line_width / 2},
-        {y - (line_width / 2), line_width / 2},
-        {y - (line_width / 2), (rect.height - (line_width / 2)) - logical_px(2)},
-        {y - w - (2 * line_width), (rect.height - (line_width / 2)) - logical_px(2)},
-        {y - w - (2 * line_width), line_width / 2}};
-    xcb_poly_line(conn, XCB_COORD_MODE_ORIGIN, pixmap, pixmap_gc, 5, points);
-
-    values[0] = 1;
-    set_font_colors(pixmap_gc, color_text, color_button_background);
-    /* the x term here seems to set left/right padding */
-    draw_text_ascii(close_button_label, pixmap, pixmap_gc,
-                    y - w - line_width + w / 2 - logical_px(4),
-                    logical_px(4) + logical_px(3),
-                    rect.width - y + w + line_width - w / 2 + logical_px(4));
-    y -= w;
-
-    y -= logical_px(20);
+    position -= button_draw(&btn_close, position);
+    position -= CLOSE_BTN_GAP;
 
     /* render custom buttons */
-    line_width = 1;
-    for (int c = 0; c < buttoncnt; c++) {
-        /* set w to the width of the label */
-        w = predict_text_width(buttons[c].label);
-        /* account for left/right padding, which seems to be set to 12px (total) below */
-        w += logical_px(12);
-        y -= logical_px(30);
-        xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){color_button_background.colorpixel});
-        close = (xcb_rectangle_t){y - w - (2 * line_width), logical_px(2), w + (2 * line_width), rect.height - logical_px(6)};
-        xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &close);
-
-        xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){color_border.colorpixel});
-        buttons[c].x = y - w - (2 * line_width);
-        buttons[c].width = w;
-        xcb_point_t points2[] = {
-            {y - w - (2 * line_width), (line_width / 2) + logical_px(2)},
-            {y - (line_width / 2), (line_width / 2) + logical_px(2)},
-            {y - (line_width / 2), (rect.height - logical_px(4) - (line_width / 2))},
-            {y - w - (2 * line_width), (rect.height - logical_px(4) - (line_width / 2))},
-            {y - w - (2 * line_width), (line_width / 2) + logical_px(2)}};
-        xcb_poly_line(conn, XCB_COORD_MODE_ORIGIN, pixmap, pixmap_gc, 5, points2);
-
-        values[0] = color_text.colorpixel;
-        values[1] = color_button_background.colorpixel;
-        set_font_colors(pixmap_gc, color_text, color_button_background);
-        /* the x term seems to set left/right padding */
-        draw_text(buttons[c].label, pixmap, pixmap_gc, NULL,
-                  y - w - line_width + logical_px(6),
-                  logical_px(4) + logical_px(3),
-                  rect.width - y + w + line_width - logical_px(6));
-
-        y -= w;
+    for (int i = 0; i < buttoncnt; i++) {
+        position -= BTN_GAP;
+        position -= button_draw(&buttons[i], position);
     }
 
     /* border line at the bottom */
-    line_width = logical_px(2);
-    values[0] = color_border_bottom.colorpixel;
-    values[1] = line_width;
-    xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND | XCB_GC_LINE_WIDTH, values);
-    xcb_point_t bottom[] = {
-        {0, rect.height - 0},
-        {rect.width, rect.height - 0}};
-    xcb_poly_line(conn, XCB_COORD_MODE_ORIGIN, pixmap, pixmap_gc, 2, bottom);
-
-    /* Copy the contents of the pixmap to the real window */
-    xcb_copy_area(conn, pixmap, win, pixmap_gc, 0, 0, 0, 0, rect.width, rect.height);
-    xcb_flush(conn);
+    draw_util_rectangle(&bar, color_border_bottom, 0, bar.height - BAR_BORDER, bar.width, BAR_BORDER);
 
+    xcb_flush(conn);
     return 1;
 }
 
@@ -301,7 +265,7 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) {
  */
 static xcb_rectangle_t get_window_position(void) {
     /* Default values if we cannot determine the primary output or its CRTC info. */
-    xcb_rectangle_t result = (xcb_rectangle_t){50, 50, 500, font.height + logical_px(8) + logical_px(8)};
+    xcb_rectangle_t result = (xcb_rectangle_t){50, 50, 500, font.height + 2 * MSG_PADDING + BAR_BORDER};
 
     xcb_randr_get_screen_resources_current_cookie_t rcookie = xcb_randr_get_screen_resources_current(conn, root);
     xcb_randr_get_output_primary_cookie_t pcookie = xcb_randr_get_output_primary(conn, root);
@@ -438,6 +402,8 @@ int main(int argc, char *argv[]) {
         }
     }
 
+    btn_close.label = i3string_from_utf8("X");
+
     int screens;
     if ((conn = xcb_connect(NULL, &screens)) == NULL ||
         xcb_connection_has_error(conn))
@@ -575,11 +541,8 @@ int main(int argc, char *argv[]) {
                         12,
                         &strut_partial);
 
-    /* Create pixmap */
-    pixmap = xcb_generate_id(conn);
-    pixmap_gc = xcb_generate_id(conn);
-    xcb_create_pixmap(conn, root_screen->root_depth, pixmap, win, 500, font.height + logical_px(8));
-    xcb_create_gc(conn, pixmap_gc, pixmap, 0, 0);
+    /* Initialize the drawable bar */
+    draw_util_surface_init(conn, &bar, win, get_visualtype(root_screen), win_pos.width, win_pos.height);
 
     /* Grab the keyboard to get all input */
     xcb_flush(conn);
@@ -612,18 +575,7 @@ int main(int argc, char *argv[]) {
 
             case XCB_CONFIGURE_NOTIFY: {
                 xcb_configure_notify_event_t *configure_notify = (xcb_configure_notify_event_t *)event;
-                rect = (xcb_rectangle_t){
-                    configure_notify->x,
-                    configure_notify->y,
-                    configure_notify->width,
-                    configure_notify->height};
-
-                /* Recreate the pixmap / gc */
-                xcb_free_pixmap(conn, pixmap);
-                xcb_free_gc(conn, pixmap_gc);
-
-                xcb_create_pixmap(conn, root_screen->root_depth, pixmap, win, rect.width, rect.height);
-                xcb_create_gc(conn, pixmap_gc, pixmap, 0, 0);
+                draw_util_surface_set_size(&bar, configure_notify->width, configure_notify->height);
                 break;
             }
         }
@@ -632,6 +584,7 @@ int main(int argc, char *argv[]) {
     }
 
     FREE(pattern);
+    draw_util_surface_free(conn, &bar);
 
     return 0;
 }
index 4a090ad794f16fac5339ff03a4401936496753b1..cc5074e5b27306e01bdff16d2f6e2f116753b3de 100644 (file)
@@ -178,6 +178,11 @@ void got_bar_config_update(char *event) {
     init_xcb_late(config.fontname);
     init_colors(&(config.colors));
 
+    /* restart status command process */
+    kill_child();
+    start_child(config.command);
+    FREE(config.command);
+
     draw_bars(false);
 }
 
index edef9b7e2dd1edbae31b4376c896f4c2edeedc8f..24f91642d3945f16ef3fde27cd81fa05eb69d93f 100644 (file)
@@ -531,7 +531,8 @@ void handle_button(xcb_button_press_event_t *event) {
         return;
     }
     switch (event->detail) {
-        case 4:
+        case XCB_BUTTON_SCROLL_UP:
+        case XCB_BUTTON_SCROLL_LEFT:
             /* Mouse wheel up. We select the previous ws, if any.
              * If there is no more workspace, don’t even send the workspace
              * command, otherwise (with workspace auto_back_and_forth) we’d end
@@ -541,7 +542,8 @@ void handle_button(xcb_button_press_event_t *event) {
 
             cur_ws = TAILQ_PREV(cur_ws, ws_head, tailq);
             break;
-        case 5:
+        case XCB_BUTTON_SCROLL_DOWN:
+        case XCB_BUTTON_SCROLL_RIGHT:
             /* Mouse wheel down. We select the next ws, if any.
              * If there is no more workspace, don’t even send the workspace
              * command, otherwise (with workspace auto_back_and_forth) we’d end
index 98ac35b0eb8b6fba24250e04ba86378234d02c04..249cc32e3d6e1e85c977db546c63b00dcce51b47 100644 (file)
@@ -91,3 +91,6 @@ typedef struct i3_ipc_header {
 
 /** The binding event will be triggered when bindings run */
 #define I3_IPC_EVENT_BINDING (I3_IPC_EVENT_MASK | 5)
+
+/** The shutdown event will be triggered when the ipc shuts down */
+#define I3_IPC_EVENT_SHUTDOWN (I3_IPC_EVENT_MASK | 6)
index 7ff4704c626195517b0a4efde49a91031f92842d..7ffbf7a831f9de3418f9d4ddad304beee5cbff22 100644 (file)
@@ -77,11 +77,18 @@ int ipc_create_socket(const char *filename);
 void ipc_send_event(const char *event, uint32_t message_type, const char *payload);
 
 /**
- * Calls shutdown() on each socket and closes it. This function to be called
- * when exiting or restarting only!
+ * Calls to ipc_shutdown() should provide a reason for the shutdown.
+ */
+typedef enum {
+    SHUTDOWN_REASON_RESTART,
+    SHUTDOWN_REASON_EXIT
+} shutdown_reason_t;
+
+/**
+ * Calls shutdown() on each socket and closes it.
  *
  */
-void ipc_shutdown(void);
+void ipc_shutdown(shutdown_reason_t reason);
 
 void dump_node(yajl_gen gen, Con *con, bool inplace_restart);
 
index d33d6c71afc35fb82b2ed237be1d0996b2d9e3cf..dbb29e1f79b587ba7395db436e3f8b8b48cfdcd6 100644 (file)
 
 #define DEFAULT_DIR_MODE (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
 
+/** Mouse buttons */
+#define XCB_BUTTON_CLICK_LEFT XCB_BUTTON_INDEX_1
+#define XCB_BUTTON_CLICK_MIDDLE XCB_BUTTON_INDEX_2
+#define XCB_BUTTON_CLICK_RIGHT XCB_BUTTON_INDEX_3
+#define XCB_BUTTON_SCROLL_UP XCB_BUTTON_INDEX_4
+#define XCB_BUTTON_SCROLL_DOWN XCB_BUTTON_INDEX_5
+/* xcb doesn't define constants for these. */
+#define XCB_BUTTON_SCROLL_LEFT 6
+#define XCB_BUTTON_SCROLL_RIGHT 7
+
 /**
  * XCB connection and root screen
  *
index 913741b4fd96cdfd3484fd3d55f9c8c9a738297e..e989b88d987aa15877e565e9e743e991b0367cca 100644 (file)
@@ -178,8 +178,8 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod
     if (con->parent->type == CT_DOCKAREA)
         goto done;
 
-    const bool is_left_or_right_click = (event->detail == XCB_BUTTON_INDEX_1 ||
-                                         event->detail == XCB_BUTTON_INDEX_3);
+    const bool is_left_or_right_click = (event->detail == XCB_BUTTON_CLICK_LEFT ||
+                                         event->detail == XCB_BUTTON_CLICK_RIGHT);
 
     /* if the user has bound an action to this click, it should override the
      * default behavior. */
@@ -228,8 +228,10 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod
     /* 1: see if the user scrolled on the decoration of a stacked/tabbed con */
     if (in_stacked &&
         dest == CLICK_DECORATION &&
-        (event->detail == XCB_BUTTON_INDEX_4 ||
-         event->detail == XCB_BUTTON_INDEX_5)) {
+        (event->detail == XCB_BUTTON_SCROLL_UP ||
+         event->detail == XCB_BUTTON_SCROLL_DOWN ||
+         event->detail == XCB_BUTTON_SCROLL_LEFT ||
+         event->detail == XCB_BUTTON_SCROLL_RIGHT)) {
         DLOG("Scrolling on a window decoration\n");
         orientation_t orientation = (con->parent->layout == L_STACKED ? VERT : HORIZ);
         /* Focus the currently focused container on the same level that the
@@ -244,10 +246,12 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod
          * #557), we first check if scrolling is possible at all. */
         bool scroll_prev_possible = (TAILQ_PREV(focused, nodes_head, nodes) != NULL);
         bool scroll_next_possible = (TAILQ_NEXT(focused, nodes) != NULL);
-        if (event->detail == XCB_BUTTON_INDEX_4 && scroll_prev_possible)
+        if ((event->detail == XCB_BUTTON_SCROLL_UP || event->detail == XCB_BUTTON_SCROLL_LEFT) && scroll_prev_possible) {
             tree_next('p', orientation);
-        else if (event->detail == XCB_BUTTON_INDEX_5 && scroll_next_possible)
+        } else if ((event->detail == XCB_BUTTON_SCROLL_DOWN || event->detail == XCB_BUTTON_SCROLL_RIGHT) && scroll_next_possible) {
             tree_next('n', orientation);
+        }
+
         goto done;
     }
 
@@ -261,7 +265,7 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod
         floating_raise_con(floatingcon);
 
         /* 4: floating_modifier plus left mouse button drags */
-        if (mod_pressed && event->detail == XCB_BUTTON_INDEX_1) {
+        if (mod_pressed && event->detail == XCB_BUTTON_CLICK_LEFT) {
             floating_drag_window(floatingcon, event);
             return 1;
         }
@@ -269,7 +273,7 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod
         /*  5: resize (floating) if this was a (left or right) click on the
          * left/right/bottom border, or a right click on the decoration.
          * also try resizing (tiling) if it was a click on the top */
-        if (mod_pressed && event->detail == XCB_BUTTON_INDEX_3) {
+        if (mod_pressed && event->detail == XCB_BUTTON_CLICK_RIGHT) {
             DLOG("floating resize due to floatingmodifier\n");
             floating_resize_window(floatingcon, proportional, event);
             return 1;
@@ -283,7 +287,7 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod
                 goto done;
         }
 
-        if (dest == CLICK_DECORATION && event->detail == XCB_BUTTON_INDEX_3) {
+        if (dest == CLICK_DECORATION && event->detail == XCB_BUTTON_CLICK_RIGHT) {
             DLOG("floating resize due to decoration right click\n");
             floating_resize_window(floatingcon, proportional, event);
             return 1;
@@ -298,7 +302,7 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod
         /* 6: dragging, if this was a click on a decoration (which did not lead
          * to a resize) */
         if (!in_stacked && dest == CLICK_DECORATION &&
-            (event->detail == XCB_BUTTON_INDEX_1)) {
+            (event->detail == XCB_BUTTON_CLICK_LEFT)) {
             floating_drag_window(floatingcon, event);
             return 1;
         }
@@ -313,7 +317,7 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod
     }
 
     /* 7: floating modifier pressed, initiate a resize */
-    if (dest == CLICK_INSIDE && mod_pressed && event->detail == XCB_BUTTON_INDEX_3) {
+    if (dest == CLICK_INSIDE && mod_pressed && event->detail == XCB_BUTTON_CLICK_RIGHT) {
         if (floating_mod_on_tiled_client(con, event))
             return 1;
     }
index 44a5d8a441732b76cb7318abceaf6cd47630e457..33737f71187647609228d77d9cb24924d8816de0 100644 (file)
@@ -1549,7 +1549,7 @@ void cmd_exit(I3_CMD) {
 #ifdef I3_ASAN_ENABLED
     __lsan_do_leak_check();
 #endif
-    ipc_shutdown();
+    ipc_shutdown(SHUTDOWN_REASON_EXIT);
     unlink(config.ipc_socket_path);
     xcb_disconnect(conn);
     exit(0);
@@ -1582,7 +1582,7 @@ void cmd_reload(I3_CMD) {
  */
 void cmd_restart(I3_CMD) {
     LOG("restarting i3\n");
-    ipc_shutdown();
+    ipc_shutdown(SHUTDOWN_REASON_RESTART);
     unlink(config.ipc_socket_path);
     /* We need to call this manually since atexit handlers don’t get called
      * when exec()ing */
index 315688c440768cae2c05caa3ff46c45c3bb2c993..9fb9040e3067f6170805f51a4060a6f087b1d76c 100644 (file)
@@ -614,12 +614,9 @@ static void handle_expose_event(xcb_expose_event_t *event) {
     }
 
     /* Since we render to our surface on every change anyways, expose events
-     * only tell us that the X server lost (parts of) the window contents. We
-     * can handle that by copying the appropriate part from our surface to the
-     * window. */
+     * only tell us that the X server lost (parts of) the window contents. */
     draw_util_copy_surface(&(parent->frame_buffer), &(parent->frame),
-                           event->x, event->y, event->x, event->y,
-                           event->width, event->height);
+                           0, 0, 0, 0, parent->rect.width, parent->rect.height);
     xcb_flush(conn);
     return;
 }
index db2fa362e286f8cf995c98a7d9d25dd9af87269f..bb20b340cb8323990b6048faa7773c5d161f26dd 100644 (file)
--- a/src/ipc.c
+++ b/src/ipc.c
@@ -62,11 +62,39 @@ void ipc_send_event(const char *event, uint32_t message_type, const char *payloa
 }
 
 /*
- * Calls shutdown() on each socket and closes it. This function to be called
+ * For shutdown events, we send the reason for the shutdown.
+ */
+static void ipc_send_shutdown_event(shutdown_reason_t reason) {
+    yajl_gen gen = ygenalloc();
+    y(map_open);
+
+    ystr("change");
+
+    if (reason == SHUTDOWN_REASON_RESTART) {
+        ystr("restart");
+    } else if (reason == SHUTDOWN_REASON_EXIT) {
+        ystr("exit");
+    }
+
+    y(map_close);
+
+    const unsigned char *payload;
+    ylength length;
+
+    y(get_buf, &payload, &length);
+    ipc_send_event("shutdown", I3_IPC_EVENT_SHUTDOWN, (const char *)payload);
+
+    y(free);
+}
+
+/*
+ * Calls shutdown() on each socket and closes it. This function is to be called
  * when exiting or restarting only!
  *
  */
-void ipc_shutdown(void) {
+void ipc_shutdown(shutdown_reason_t reason) {
+    ipc_send_shutdown_event(reason);
+
     ipc_client *current;
     while (!TAILQ_EMPTY(&all_clients)) {
         current = TAILQ_FIRST(&all_clients);
index 9edf4b11e1480a8bd762af57f103d3869e07615b..d567e9717139d51eb996abe15565341a4a82376e 100644 (file)
@@ -15,6 +15,8 @@
 #include <sanitizer/lsan_interface.h>
 #endif
 
+#define TEXT_PADDING logical_px(2)
+
 typedef struct placeholder_state {
     /** The X11 placeholder window. */
     xcb_window_t window;
@@ -24,10 +26,8 @@ typedef struct placeholder_state {
     /** Current size of the placeholder window (to detect size changes). */
     Rect rect;
 
-    /** The pixmap to render on (back buffer). */
-    xcb_pixmap_t pixmap;
-    /** The graphics context for “pixmap”. */
-    xcb_gcontext_t gc;
+    /** The drawable surface */
+    surface_t surface;
 
     TAILQ_ENTRY(placeholder_state)
     state;
@@ -138,17 +138,15 @@ void restore_connect(void) {
 }
 
 static void update_placeholder_contents(placeholder_state *state) {
-    xcb_change_gc(restore_conn, state->gc, XCB_GC_FOREGROUND,
-                  (uint32_t[]){config.client.placeholder.background.colorpixel});
-    xcb_poly_fill_rectangle(restore_conn, state->pixmap, state->gc, 1,
-                            (xcb_rectangle_t[]){{0, 0, state->rect.width, state->rect.height}});
+    const color_t foreground = config.client.placeholder.text;
+    const color_t background = config.client.placeholder.background;
+
+    draw_util_clear_surface(&(state->surface), background);
 
     // TODO: make i3font functions per-connection, at least these two for now…?
     xcb_flush(restore_conn);
     xcb_aux_sync(restore_conn);
 
-    set_font_colors(state->gc, config.client.placeholder.text, config.client.placeholder.background);
-
     Match *swallows;
     int n = 0;
     TAILQ_FOREACH(swallows, &(state->con->swallow_head), matches) {
@@ -175,7 +173,10 @@ static void update_placeholder_contents(placeholder_state *state) {
         DLOG("con %p (placeholder 0x%08x) line %d: %s\n", state->con, state->window, n, serialized);
 
         i3String *str = i3string_from_utf8(serialized);
-        draw_text(str, state->pixmap, state->gc, NULL, 2, (n * (config.font.height + 2)) + 2, state->rect.width - 2);
+        draw_util_text(str, &(state->surface), foreground, background,
+                       TEXT_PADDING,
+                       (n * (config.font.height + TEXT_PADDING)) + TEXT_PADDING,
+                       state->rect.width - 2 * TEXT_PADDING);
         i3string_free(str);
         n++;
         free(serialized);
@@ -186,7 +187,7 @@ static void update_placeholder_contents(placeholder_state *state) {
     int text_width = predict_text_width(line);
     int x = (state->rect.width / 2) - (text_width / 2);
     int y = (state->rect.height / 2) - (config.font.height / 2);
-    draw_text(line, state->pixmap, state->gc, NULL, x, y, text_width);
+    draw_util_text(line, &(state->surface), foreground, background, x, y, text_width);
     i3string_free(line);
     xcb_flush(conn);
     xcb_aux_sync(conn);
@@ -228,11 +229,8 @@ static void open_placeholder_window(Con *con) {
         state->window = placeholder;
         state->con = con;
         state->rect = con->rect;
-        state->pixmap = xcb_generate_id(restore_conn);
-        xcb_create_pixmap(restore_conn, root_depth, state->pixmap,
-                          state->window, state->rect.width, state->rect.height);
-        state->gc = xcb_generate_id(restore_conn);
-        xcb_create_gc(restore_conn, state->gc, state->pixmap, XCB_GC_GRAPHICS_EXPOSURES, (uint32_t[]){0});
+
+        draw_util_surface_init(conn, &(state->surface), placeholder, get_visualtype(root_screen), state->rect.width, state->rect.height);
         update_placeholder_contents(state);
         TAILQ_INSERT_TAIL(&state_head, state, state);
 
@@ -286,8 +284,7 @@ bool restore_kill_placeholder(xcb_window_t placeholder) {
             continue;
 
         xcb_destroy_window(restore_conn, state->window);
-        xcb_free_pixmap(restore_conn, state->pixmap);
-        xcb_free_gc(restore_conn, state->gc);
+        draw_util_surface_free(restore_conn, &(state->surface));
         TAILQ_REMOVE(&state_head, state, state);
         free(state);
         DLOG("placeholder window 0x%08x destroyed.\n", placeholder);
@@ -306,14 +303,8 @@ static void expose_event(xcb_expose_event_t *event) {
 
         DLOG("refreshing window 0x%08x contents (con %p)\n", state->window, state->con);
 
-        /* Since we render to our pixmap on every change anyways, expose events
-         * only tell us that the X server lost (parts of) the window contents. We
-         * can handle that by copying the appropriate part from our pixmap to the
-         * window. */
-        xcb_copy_area(restore_conn, state->pixmap, state->window, state->gc,
-                      event->x, event->y, event->x, event->y,
-                      event->width, event->height);
-        xcb_flush(restore_conn);
+        update_placeholder_contents(state);
+
         return;
     }
 
@@ -338,19 +329,10 @@ static void configure_notify(xcb_configure_notify_event_t *event) {
         state->rect.width = event->width;
         state->rect.height = event->height;
 
-        xcb_free_pixmap(restore_conn, state->pixmap);
-        xcb_free_gc(restore_conn, state->gc);
-
-        state->pixmap = xcb_generate_id(restore_conn);
-        xcb_create_pixmap(restore_conn, root_depth, state->pixmap,
-                          state->window, state->rect.width, state->rect.height);
-        state->gc = xcb_generate_id(restore_conn);
-        xcb_create_gc(restore_conn, state->gc, state->pixmap, XCB_GC_GRAPHICS_EXPOSURES, (uint32_t[]){0});
+        draw_util_surface_set_size(&(state->surface), state->rect.width, state->rect.height);
 
         update_placeholder_contents(state);
-        xcb_copy_area(restore_conn, state->pixmap, state->window, state->gc,
-                      0, 0, 0, 0, state->rect.width, state->rect.height);
-        xcb_flush(restore_conn);
+
         return;
     }
 
index 0289ded91f6a5a8a4a8dc1b6dbd9ae14f3de1465..06fbea2a7ef0856aa6a2ac0d71c07bce5ea85dd5 100644 (file)
@@ -287,7 +287,7 @@ void i3_restart(bool forget_layout) {
 
     restore_geometry();
 
-    ipc_shutdown();
+    ipc_shutdown(SHUTDOWN_REASON_RESTART);
 
     LOG("restarting \"%s\"...\n", start_argv[0]);
     /* make sure -a is in the argument list or add it */
index d872bda11d870cd1b66596bcffeb089d9d4eecc6..2019253c49ca99965d9ac00941f73d2f03d1acef 100755 (executable)
@@ -87,6 +87,17 @@ foreach my $binary (@binaries) {
     die "$binary is not an executable" unless -x $binary;
 }
 
+my @test_binaries = qw(
+                        @abs_top_builddir@/test.commands_parser
+                        @abs_top_builddir@/test.config_parser
+                        @abs_top_builddir@/test.inject_randr15
+                    );
+
+foreach my $binary (@test_binaries) {
+    die "$binary executable not found, did you run “make check”?" unless -e $binary;
+    die "$binary is not an executable" unless -x $binary;
+}
+
 $ENV{PATH} = join(':',
     '@abs_top_builddir@/i3-nagbar',
     '@abs_top_builddir@/i3-msg',
diff --git a/testcases/t/264-ipc-shutdown-event.t b/testcases/t/264-ipc-shutdown-event.t
new file mode 100644 (file)
index 0000000..379b9bf
--- /dev/null
@@ -0,0 +1,71 @@
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • http://build.i3wm.org/docs/testsuite.html
+#   (or docs/testsuite)
+#
+# • http://build.i3wm.org/docs/lib-i3test.html
+#   (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • http://build.i3wm.org/docs/ipc.html
+#   (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+#   (unless you are already familiar with Perl)
+#
+# Test the ipc shutdown event. This event is triggered when the connection to
+# the ipc is about to shutdown because of a user action such as with a
+# `restart` or `exit` command. The `change` field indicates why the ipc is
+# shutting down. It can be either "restart" or "exit".
+#
+# Ticket: #2318
+# Bug still in: 4.12-46-g2123888
+use i3test;
+
+SKIP: {
+    skip "AnyEvent::I3 too old (need >= 0.17)", 1 if $AnyEvent::I3::VERSION < 0.17;
+
+my $i3 = i3(get_socket_path());
+$i3->connect->recv;
+
+my $cv = AE::cv;
+my $timer = AE::timer 0.5, 0, sub { $cv->send(0); };
+
+$i3->subscribe({
+        shutdown => sub {
+            $cv->send(shift);
+        }
+    })->recv;
+
+cmd 'restart';
+
+my $e = $cv->recv;
+
+diag "Event:\n", Dumper($e);
+ok($e, 'the shutdown event should emit when the ipc is restarted by command');
+is($e->{change}, 'restart', 'the `change` field should tell the reason for the shutdown');
+
+# restarting kills the ipc client so we have to make a new one
+$i3 = i3(get_socket_path());
+$i3->connect->recv;
+
+$cv = AE::cv;
+$timer = AE::timer 0.5, 0, sub { $cv->send(0); };
+
+$i3->subscribe({
+        shutdown => sub {
+            $cv->send(shift);
+        }
+    })->recv;
+
+cmd 'exit';
+
+$e = $cv->recv;
+
+diag "Event:\n", Dumper($e);
+ok($e, 'the shutdown event should emit when the ipc is exited by command');
+is($e->{change}, 'exit', 'the `change` field should tell the reason for the shutdown');
+}
+
+done_testing;