]> git.sur5r.net Git - i3/i3/commitdiff
Merge pull request #1698 from Airblader/feature-1696
authorMichael Stapelberg <stapelberg@users.noreply.github.com>
Sun, 17 May 2015 13:02:50 +0000 (15:02 +0200)
committerMichael Stapelberg <stapelberg@users.noreply.github.com>
Sun, 17 May 2015 13:02:50 +0000 (15:02 +0200)
Added 'move position mouse'

17 files changed:
docs/debugging
docs/userguide
i3bar/src/workspaces.c
i3bar/src/xcb.c
include/config.h
include/config_directives.h
include/libi3.h
libi3/resolve_tilde.c
libi3/safewrappers.c
parser-specs/config.spec
src/commands.c
src/config.c
src/config_directives.c
src/main.c
src/randr.c
src/window.c
testcases/t/201-config-parser.t

index 639dbdfe979d67d4469b17885c6c0fa9faec6813..cf61813d4789b52c1c4791f4b28f92698c73e4b2 100644 (file)
@@ -67,6 +67,30 @@ fly:
 i3-msg 'debuglog on; shmlog on; reload'
 ---------------------------------------
 
+== Reproducing the problem
+
+Before submitting an issue, please make sure to close down on the problem as
+much as you can yourself. Here are some steps you should consider:
+
+* Find a deterministic, reliable way to reproduce the problem and provide it
+  with your bug report.
+* Try using the default i3 config to reproduce the problem. If the issue does
+  not appear with the default config, gradually adapt it to track down what 
+  change(s) to the config introduce the problem.
+* Reproduce the problem with a minimal setup, i.e., only use as few applications,
+  windows and steps as necessary.
+* In addition, try to stick to applications that are common and, even more
+  importantly, free / open source.
+* Before obtaining the log file, restart i3 in-place, execute the steps to
+  reproduce the problem and then save the logs. This keeps the log file as
+  small as possible and necessary.
+
+Please be aware that we cannot support compatibility issues with closed-source
+software, as digging into compatibility problems without having access to the
+source code is too time-consuming. Additionally, experience has shown that
+often, the software in question is responsible for the issue. Please raise an
+issue with the software in question, not i3.
+
 == Obtaining the debug logfile
 
 Please note that log files may contain sensitive data such as window titles.
index d52e804583412ca077b8bee56a5e7ba8bbaed635..47bc6b78477989dc952fe948a2011a77443ce0be 100644 (file)
@@ -725,8 +725,8 @@ also when restarting i3 you should use the +exec_always+
 keyword. These commands will be run in order.
 
 See <<command_chaining>> for details on the special meaning of +;+ (semicolon)
-and +,+ (comma): they chain commands together in i3 and need to be escaped if
-you want to use them in your command.
+and +,+ (comma): they chain commands together in i3, so you need to use quoted
+strings if they appear in your command.
 
 *Syntax*:
 -------------------
@@ -1010,6 +1010,31 @@ force_display_urgency_hint <timeout> ms
 force_display_urgency_hint 500 ms
 ---------------------------------
 
+=== Delaying exiting on zero displays
+
+Outputs may disappear momentarily and come back later. For example,
+using a docking station that does not announce the undock (e.g. ACPI Undock 
+event triggered through manually pushing a button before actually ejecting 
+the notebook). During the removal of the notebook from the docking station,
+all outputs disappear momentarily.
+
+To prevent i3 from exiting when no output is available momentarily, you can 
+tell i3 to delay a certain time first and check available outputs again using 
+the +delay_exit_on_zero_displays+ directive. Setting the value to 0 disables 
+this feature.
+
+The default is 500ms.
+
+*Syntax*:
+----------------------------------------
+delay_exit_on_zero_displays <timeout> ms
+----------------------------------------
+
+*Example*:
+----------------------------------
+delay_exit_on_zero_displays 500 ms
+----------------------------------
+
 === Focus on window activation
 
 [[focus_on_window_activation]]
@@ -1549,8 +1574,8 @@ shell. This implies that you can use globbing (wildcards) and programs will be
 searched in your +$PATH+.
 
 See <<command_chaining>> for details on the special meaning of +;+ (semicolon)
-and +,+ (comma): they chain commands together in i3 and need to be escaped if
-you want to use them in your command.
+and +,+ (comma): they chain commands together in i3, so you need to use quoted
+strings if they appear in your command.
 
 *Syntax*:
 ------------------------------
index 773f8f546c6c07de688dd54734eb4dfd8a35877f..961a41f5da7913c4be7558204f546e1bcc91919f 100644 (file)
@@ -106,7 +106,7 @@ static int workspaces_string_cb(void *params_, const unsigned char *val, size_t
 
     if (!strcmp(params->cur_key, "name")) {
         const char *ws_name = (const char *)val;
-        params->workspaces_walk->canonical_name = strndup(ws_name, len);
+        params->workspaces_walk->canonical_name = sstrndup(ws_name, len);
 
         if (config.strip_ws_numbers && params->workspaces_walk->num >= 0) {
             /* Special case: strip off the workspace number */
index 11a017cf386224be3054596692a48ea1eb120b99..b9e5659f11a8b01264f5f82b2253e52f2ef52f03 100644 (file)
@@ -60,6 +60,9 @@ xcb_connection_t *conn;
 /* The font we'll use */
 static i3Font font;
 
+/* Icon size (based on font size) */
+int icon_size;
+
 /* Overall height of the bar (based on font size) */
 int bar_height;
 
@@ -122,6 +125,9 @@ static const int sb_hoff_px = 4;
 /* Additional offset between the tray and the statusline, if the tray is not empty */
 static const int tray_loff_px = 2;
 
+/* Padding around the tray icons */
+static const int tray_spacing_px = 2;
+
 /* Vertical offset between the bar and a separator */
 static const int sep_voff_px = 4;
 
@@ -148,7 +154,7 @@ int get_tray_width(struct tc_head *trayclients) {
     TAILQ_FOREACH_REVERSE(trayclient, trayclients, tc_head, tailq) {
         if (!trayclient->mapped)
             continue;
-        tray_width += font.height + logical_px(2);
+        tray_width += icon_size + logical_px(tray_spacing_px);
     }
     if (tray_width > 0)
         tray_width += logical_px(tray_loff_px);
@@ -591,8 +597,8 @@ static void configure_trayclients(void) {
             clients++;
 
             DLOG("Configuring tray window %08x to x=%d\n",
-                 trayclient->win, output->rect.w - (clients * (font.height + logical_px(2))));
-            uint32_t x = output->rect.w - (clients * (font.height + logical_px(2)));
+                 trayclient->win, output->rect.w - (clients * (icon_size + logical_px(tray_spacing_px))));
+            uint32_t x = output->rect.w - (clients * (icon_size + logical_px(tray_spacing_px)));
             xcb_configure_window(xcb_connection,
                                  trayclient->win,
                                  XCB_CONFIG_WINDOW_X,
@@ -702,16 +708,16 @@ static void handle_client_message(xcb_client_message_event_t *event) {
             xcb_reparent_window(xcb_connection,
                                 client,
                                 output->bar,
-                                output->rect.w - font.height - 2,
-                                2);
+                                output->rect.w - icon_size - logical_px(tray_spacing_px),
+                                logical_px(tray_spacing_px));
             /* We reconfigure the window to use a reasonable size. The systray
              * specification explicitly says:
              *   Tray icons may be assigned any size by the system tray, and
              *   should do their best to cope with any size effectively
              */
             mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
-            values[0] = font.height;
-            values[1] = font.height;
+            values[0] = icon_size;
+            values[1] = icon_size;
             xcb_configure_window(xcb_connection,
                                  client,
                                  mask,
@@ -941,10 +947,10 @@ static void handle_configure_request(xcb_configure_request_event_t *event) {
                 continue;
 
             xcb_rectangle_t rect;
-            rect.x = output->rect.w - (clients * (font.height + 2));
-            rect.y = 2;
-            rect.width = font.height;
-            rect.height = font.height;
+            rect.x = output->rect.w - (clients * (icon_size + logical_px(tray_spacing_px)));
+            rect.y = logical_px(tray_spacing_px);
+            rect.width = icon_size;
+            rect.height = icon_size;
 
             DLOG("This is a tray window. x = %d\n", rect.x);
             fake_configure_notify(xcb_connection, rect, event->window, 0);
@@ -1215,6 +1221,7 @@ void init_xcb_late(char *fontname) {
     set_font(&font);
     DLOG("Calculated font height: %d\n", font.height);
     bar_height = font.height + 2 * logical_px(ws_voff_px);
+    icon_size = bar_height - 2 * logical_px(tray_spacing_px);
 
     if (config.separator_symbol)
         separator_symbol_width = predict_text_width(config.separator_symbol);
index 4cc58a459c18f2a89b366be1d82f12df357f0423..75e0b127f96e084b9e566dad84b1db7e9ed0a3c7 100644 (file)
@@ -167,6 +167,10 @@ struct Config {
      * flag can be delayed using an urgency timer. */
     float workspace_urgency_timer;
 
+    /** Use a timer to delay exiting when no output is available.
+      * This can prevent i3 from exiting when all outputs disappear momentarily. */
+    float zero_disp_exit_timer_ms;
+
     /** Behavior when a window sends a NET_ACTIVE_WINDOW message. */
     enum {
         /* Focus if the target workspace is visible, set urgency hint otherwise. */
index 019b9bcdedfc9795523b4fcf1eb53d236636c316..1a7a39329e06c765e1de8ae3b637f36e94f64201 100644 (file)
@@ -51,6 +51,7 @@ CFGFUN(force_focus_wrapping, const char *value);
 CFGFUN(force_xinerama, const char *value);
 CFGFUN(fake_outputs, const char *outputs);
 CFGFUN(force_display_urgency_hint, const long duration_ms);
+CFGFUN(delay_exit_on_zero_displays, const long duration_ms);
 CFGFUN(focus_on_window_activation, const char *mode);
 CFGFUN(show_marks, const char *value);
 CFGFUN(hide_edge_borders, const char *borders);
index a64b3981e88df6bca1e8abc968f313c54bc37e63..3e6f8427d4f8e0f984e5d6c2964cd6413b1ff086 100644 (file)
@@ -127,6 +127,13 @@ void *srealloc(void *ptr, size_t size);
  */
 char *sstrdup(const char *str);
 
+/**
+ * Safe-wrapper around strndup which exits if strndup returns NULL (meaning that
+ * there is no more memory available)
+ *
+ */
+char *sstrndup(const char *str, size_t size);
+
 /**
  * Safe-wrapper around asprintf which exits if it returns -1 (meaning that
  * there is no more memory available)
index 3a56cbea853495a56a6646d3e13a6ecc52fcb167..26cbabe5f1679cd38ff4f7ac88f220cca2fb7c26 100644 (file)
@@ -23,7 +23,7 @@ char *resolve_tilde(const char *path) {
     char *head, *tail, *result;
 
     tail = strchr(path, '/');
-    head = strndup(path, tail ? (size_t)(tail - path) : strlen(path));
+    head = sstrndup(path, tail ? (size_t)(tail - path) : strlen(path));
 
     int res = glob(head, GLOB_TILDE, NULL, &globbuf);
     free(head);
index 74460f37662360f75153048c1c0b0298327b5c74..f5973cab982185f869d0ed6b39eaf0ea80b67235 100644 (file)
@@ -48,6 +48,13 @@ char *sstrdup(const char *str) {
     return result;
 }
 
+char *sstrndup(const char *str, size_t size) {
+    char *result = strndup(str, size);
+    if (result == NULL)
+        err(EXIT_FAILURE, "strndup()");
+    return result;
+}
+
 int sasprintf(char **strp, const char *fmt, ...) {
     va_list args;
     int result;
index 422efe485947c23fb30d20598fe8a1a53de80467..986086c7d27d90e06644d0d34610cc2f52ed0c58 100644 (file)
@@ -39,6 +39,7 @@ state INITIAL:
   'workspace_auto_back_and_forth'          -> WORKSPACE_BACK_AND_FORTH
   'fake_outputs', 'fake-outputs'           -> FAKE_OUTPUTS
   'force_display_urgency_hint'             -> FORCE_DISPLAY_URGENCY_HINT
+  'delay_exit_on_zero_displays'            -> DELAY_EXIT_ON_ZERO_DISPLAYS
   'focus_on_window_activation'             -> FOCUS_ON_WINDOW_ACTIVATION
   'show_marks'                             -> SHOW_MARKS
   'workspace'                              -> WORKSPACE
@@ -228,6 +229,17 @@ state FORCE_DISPLAY_URGENCY_HINT_MS:
   end
       -> call cfg_force_display_urgency_hint(&duration_ms)
 
+# delay_exit_on_zero_displays <delay> ms
+state DELAY_EXIT_ON_ZERO_DISPLAYS:
+  duration_ms = number
+      -> DELAY_EXIT_ON_ZERO_DISPLAYS_MS
+
+state DELAY_EXIT_ON_ZERO_DISPLAYS_MS:
+  'ms'
+      ->
+  end
+      -> call cfg_delay_exit_on_zero_displays(&duration_ms)
+
 # focus_on_window_activation <smart|urgent|focus|none>
 state FOCUS_ON_WINDOW_ACTIVATION:
   mode = word
index 1f5c8f44ebecfe074ac0e389081992bb1ab8a2e2..b5558182d567d6b549cc37126eb20076d22bfcfc 100644 (file)
@@ -335,7 +335,7 @@ void cmd_criteria_add(I3_CMD, char *ctype, char *cvalue) {
 
     if (strcmp(ctype, "con_id") == 0) {
         char *end;
-        long parsed = strtol(cvalue, &end, 10);
+        long parsed = strtol(cvalue, &end, 0);
         if (parsed == LONG_MIN ||
             parsed == LONG_MAX ||
             parsed < 0 ||
@@ -350,7 +350,7 @@ void cmd_criteria_add(I3_CMD, char *ctype, char *cvalue) {
 
     if (strcmp(ctype, "id") == 0) {
         char *end;
-        long parsed = strtol(cvalue, &end, 10);
+        long parsed = strtol(cvalue, &end, 0);
         if (parsed == LONG_MIN ||
             parsed == LONG_MAX ||
             parsed < 0 ||
index 6eb67a121fe6f377469674ed0db3ed58340fbf40..bac9d7f369b168c6f9ed345d66c7c1281b606140 100644 (file)
@@ -203,6 +203,10 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
     if (config.workspace_urgency_timer == 0)
         config.workspace_urgency_timer = 0.5;
 
+    /* Set default zero displays exit delay to 500ms */
+    if (config.zero_disp_exit_timer_ms == 0)
+        config.zero_disp_exit_timer_ms = 500;
+
     parse_configuration(override_configpath, true);
 
     if (reload) {
index ff1c280e4f6d7fac4adb51c70510b6d5722694a8..ae78e0c0e87b255ad7e3c3a1ac63c24f27dc43f4 100644 (file)
@@ -354,6 +354,10 @@ CFGFUN(force_display_urgency_hint, const long duration_ms) {
     config.workspace_urgency_timer = duration_ms / 1000.0;
 }
 
+CFGFUN(delay_exit_on_zero_displays, const long duration_ms) {
+    config.zero_disp_exit_timer_ms = duration_ms;
+}
+
 CFGFUN(focus_on_window_activation, const char *mode) {
     if (strcmp(mode, "smart") == 0)
         config.focus_on_window_activation = FOWA_SMART;
index 86e40831902eef2051eb940d7fdd8b33cb91f01e..e8e0daa8c84a4bbfa783ef5afb8f691cda19de5e 100644 (file)
@@ -621,6 +621,8 @@ int main(int argc, char *argv[]) {
             ELOG("ERROR: No screen at (%d, %d), starting on the first screen\n",
                  pointerreply->root_x, pointerreply->root_y);
             output = get_first_output();
+            if (!output)
+                die("No usable outputs available.\n");
         }
 
         con_focus(con_descend_focused(output_get_content(output->con)));
index 9e236dcb517e8bab4214dba72821f4d5a3e10a12..1bd9993164d946b455db95ba3a5b0fd31f2ef503 100644 (file)
@@ -69,7 +69,7 @@ Output *get_first_output(void) {
     if (output->active)
         return output;
 
-    die("No usable outputs available.\n");
+    return NULL;
 }
 
 /*
@@ -570,6 +570,8 @@ static void handle_output(xcb_connection_t *conn, xcb_randr_output_t id,
     if (!new->active) {
         DLOG("width/height 0/0, disabling output\n");
         return;
+    } else {
+        new->to_be_disabled = false;
     }
 
     DLOG("mode: %dx%d+%d+%d\n", new->rect.width, new->rect.height,
@@ -591,11 +593,7 @@ static void handle_output(xcb_connection_t *conn, xcb_randr_output_t id,
     new->changed = true;
 }
 
-/*
- * (Re-)queries the outputs via RandR and stores them in the list of outputs.
- *
- */
-void randr_query_outputs(void) {
+static bool __randr_query_outputs(void) {
     Output *output, *other, *first;
     xcb_randr_get_output_primary_cookie_t pcookie;
     xcb_randr_get_screen_resources_current_cookie_t rcookie;
@@ -609,7 +607,7 @@ void randr_query_outputs(void) {
     xcb_randr_output_t *randr_outputs;
 
     if (randr_disabled)
-        return;
+        return true;
 
     /* Get screen resources (primary output, crtcs, outputs, modes) */
     rcookie = xcb_randr_get_screen_resources_current(conn, root);
@@ -621,7 +619,7 @@ void randr_query_outputs(void) {
         DLOG("primary output is %08x\n", primary->output);
     if ((res = xcb_randr_get_screen_resources_current_reply(conn, rcookie, NULL)) == NULL) {
         disable_randr(conn);
-        return;
+        return true;
     }
     cts = res->config_timestamp;
 
@@ -703,6 +701,11 @@ void randr_query_outputs(void) {
             DLOG("Output %s disabled, re-assigning workspaces/docks\n", output->name);
 
             first = get_first_output();
+            if (!first) {
+                FREE(res);
+                FREE(primary);
+                return false;
+            }
 
             /* TODO: refactor the following code into a nice function. maybe
              * use an on_destroy callback which is implement differently for
@@ -818,6 +821,32 @@ void randr_query_outputs(void) {
 
     FREE(res);
     FREE(primary);
+
+    return true;
+}
+
+/*
+ * (Re-)queries the outputs via RandR and stores them in the list of outputs.
+ *
+ */
+void randr_query_outputs(void) {
+    static bool first_query = true;
+
+    if (first_query) {
+        /* find monitors at least once via RandR */
+        if (!__randr_query_outputs())
+            die("No usable outputs available.\n");
+        first_query = false;
+    } else {
+        /* requery */
+        if (!__randr_query_outputs()) {
+            DLOG("sleep %f ms due to zero displays\n", config.zero_disp_exit_timer_ms);
+            usleep(config.zero_disp_exit_timer_ms * 1000);
+
+            if (!__randr_query_outputs())
+                die("No usable outputs available.\n");
+        }
+    }
 }
 
 /*
index 29cd11eb599ddd68abce6b1932d839bb95ed9319..7b0f397d5d05fee52efdd2f32ae60e23ed20825e 100644 (file)
@@ -27,30 +27,27 @@ void window_update_class(i3Window *win, xcb_get_property_reply_t *prop, bool bef
      * null-terminated strings (for compatibility reasons). Instead, we
      * use strdup() on both strings */
     const size_t prop_length = xcb_get_property_value_length(prop);
-    char *new_class = smalloc(prop_length + 1);
-    memcpy(new_class, xcb_get_property_value(prop), prop_length);
-    new_class[prop_length] = '\0';
+    char *new_class = xcb_get_property_value(prop);
+    const size_t class_class_index = strnlen(new_class, prop_length) + 1;
 
     FREE(win->class_instance);
     FREE(win->class_class);
 
-    win->class_instance = sstrdup(new_class);
-    if ((strlen(new_class) + 1) < prop_length)
-        win->class_class = sstrdup(new_class + strlen(new_class) + 1);
+    win->class_instance = sstrndup(new_class, prop_length);
+    if (class_class_index < prop_length)
+        win->class_class = sstrndup(new_class + class_class_index, prop_length - class_class_index);
     else
         win->class_class = NULL;
     LOG("WM_CLASS changed to %s (instance), %s (class)\n",
         win->class_instance, win->class_class);
 
     if (before_mgmt) {
-        free(new_class);
         free(prop);
         return;
     }
 
     run_assignments(win);
 
-    free(new_class);
     free(prop);
 }
 
index 7568de48394e4ba4c09edcce2af289fca191ee32..de25e7ff70f52c3e6b02e763fba2e4fb71d58c86 100644 (file)
@@ -369,6 +369,40 @@ is(parser_calls($config),
    $expected,
    'force_display_urgency_hint ok');
 
+################################################################################
+# delay_exit_on_zero_displays
+################################################################################
+
+is(parser_calls('delay_exit_on_zero_displays 300'),
+   "cfg_delay_exit_on_zero_displays(300)\n",
+   'delay_exit_on_zero_displays ok');
+
+is(parser_calls('delay_exit_on_zero_displays 500 ms'),
+   "cfg_delay_exit_on_zero_displays(500)\n",
+   'delay_exit_on_zero_displays ok');
+
+is(parser_calls('delay_exit_on_zero_displays 700ms'),
+   "cfg_delay_exit_on_zero_displays(700)\n",
+   'delay_exit_on_zero_displays ok');
+
+$config = <<'EOT';
+delay_exit_on_zero_displays 300
+delay_exit_on_zero_displays 500 ms
+delay_exit_on_zero_displays 700ms
+delay_exit_on_zero_displays 700
+EOT
+
+$expected = <<'EOT';
+cfg_delay_exit_on_zero_displays(300)
+cfg_delay_exit_on_zero_displays(500)
+cfg_delay_exit_on_zero_displays(700)
+cfg_delay_exit_on_zero_displays(700)
+EOT
+
+is(parser_calls($config),
+   $expected,
+   'delay_exit_on_zero_displays ok');
+
 ################################################################################
 # workspace
 ################################################################################
@@ -441,7 +475,7 @@ client.focused          #4c7899 #285577 #ffffff #2e9ef4
 EOT
 
 my $expected_all_tokens = <<'EOT';
-ERROR: CONFIG: Expected one of these tokens: <end>, '#', 'set', 'bindsym', 'bindcode', 'bind', 'bar', 'font', 'mode', 'floating_minimum_size', 'floating_maximum_size', 'floating_modifier', 'default_orientation', 'workspace_layout', 'new_window', 'new_float', 'hide_edge_borders', 'for_window', 'assign', 'no_focus', 'focus_follows_mouse', 'mouse_warping', 'force_focus_wrapping', 'force_xinerama', 'force-xinerama', 'workspace_auto_back_and_forth', 'fake_outputs', 'fake-outputs', 'force_display_urgency_hint', 'focus_on_window_activation', 'show_marks', 'workspace', 'ipc_socket', 'ipc-socket', 'restart_state', 'popup_during_fullscreen', 'exec_always', 'exec', 'client.background', 'client.focused_inactive', 'client.focused', 'client.unfocused', 'client.urgent', 'client.placeholder'
+ERROR: CONFIG: Expected one of these tokens: <end>, '#', 'set', 'bindsym', 'bindcode', 'bind', 'bar', 'font', 'mode', 'floating_minimum_size', 'floating_maximum_size', 'floating_modifier', 'default_orientation', 'workspace_layout', 'new_window', 'new_float', 'hide_edge_borders', 'for_window', 'assign', 'no_focus', 'focus_follows_mouse', 'mouse_warping', 'force_focus_wrapping', 'force_xinerama', 'force-xinerama', 'workspace_auto_back_and_forth', 'fake_outputs', 'fake-outputs', 'force_display_urgency_hint', 'delay_exit_on_zero_displays', 'focus_on_window_activation', 'show_marks', 'workspace', 'ipc_socket', 'ipc-socket', 'restart_state', 'popup_during_fullscreen', 'exec_always', 'exec', 'client.background', 'client.focused_inactive', 'client.focused', 'client.unfocused', 'client.urgent', 'client.placeholder'
 EOT
 
 my $expected_end = <<'EOT';