]> git.sur5r.net Git - i3/i3/commitdiff
Merge pull request #1706 from Airblader/feature-docs-5
authorMichael Stapelberg <stapelberg@users.noreply.github.com>
Mon, 18 May 2015 19:46:47 +0000 (21:46 +0200)
committerMichael Stapelberg <stapelberg@users.noreply.github.com>
Mon, 18 May 2015 19:46:47 +0000 (21:46 +0200)
Made syntax of syntax descriptions consistent:

22 files changed:
docs/debugging
docs/userguide
i3bar/src/workspaces.c
i3bar/src/xcb.c
include/commands.h
include/config.h
include/config_directives.h
include/floating.h
include/libi3.h
libi3/resolve_tilde.c
libi3/safewrappers.c
parser-specs/commands.spec
parser-specs/config.spec
src/commands.c
src/config.c
src/config_directives.c
src/floating.c
src/main.c
src/randr.c
src/window.c
testcases/t/201-config-parser.t
testcases/t/245-move-position-mouse.t [new file with mode: 0644]

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 7eea85423b335f336d7feb806b86b4ec37e68b8c..977e1358c3632460d737ff36176bf04ab093a04b 100644 (file)
@@ -727,8 +727,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*:
 ---------------------------------------
@@ -1012,6 +1012,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]]
@@ -1551,8 +1576,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*:
 --------------------------------
@@ -1639,15 +1664,17 @@ bindsym $mod+f fullscreen toggle
 bindsym $mod+t floating toggle
 --------------
 
-=== Focusing/Moving containers
+[[_focusing_moving_containers]]
 
-To change the focus, use the focus command: +focus left+, +focus right+, +focus
-down+ and +focus up+.
+=== Focusing containers
 
-There are a few special parameters you can use for the focus command:
+To change focus, you can use the +focus+ command. The following options are
+available:
 
+left|right|up|down::
+        Sets focus to the nearest container in the given direction.
 parent::
-       Sets focus to the +Parent Container+ of the current +Container+.
+       Sets focus to the parent container of the current container.
 child::
        The opposite of +focus parent+, sets the focus to the last focused
        child container.
@@ -1661,23 +1688,16 @@ output::
        Followed by a direction or an output name, this will focus the
        corresponding output.
 
-For moving, use +move left+, +move right+, +move down+ and +move up+.
-
 *Syntax*:
------------------------------------
-focus <left|right|down|up>
-focus <parent|child|floating|tiling|mode_toggle>
-focus output <<left|right|down|up>|output>
-move <left|right|down|up> [<px> px]
-move [absolute] position [[<px> px] [<px> px]|center]
------------------------------------
-
-Note that the amount of pixels you can specify for the +move+ command is only
-relevant for floating containers. The default amount is 10 pixels.
+----------------------------------------------
+focus left|right|down|up
+focus parent|child|floating|tiling|mode_toggle
+focus output left|right|up|down|<output>
+----------------------------------------------
 
 *Examples*:
-----------------------
-# Focus container on the left, bottom, top, right:
+-------------------------------------------------
+# Focus container on the left, bottom, top, right
 bindsym $mod+j focus left
 bindsym $mod+k focus down
 bindsym $mod+l focus up
@@ -1694,8 +1714,33 @@ bindsym $mod+x focus output right
 
 # Focus the big output
 bindsym $mod+x focus output HDMI-2
+-------------------------------------------------
+
+=== Moving containers
+
+Use the +move+ command to move a container.
+
+*Syntax*:
+-----------------------------------------------------
+# Moves the container into the given direction.
+# The optional pixel argument specifies how far the
+# container should be moved if it is floating and
+# defaults to 10 pixels.
+move <left|right|down|up> [<px> px]
 
-# Move container to the left, bottom, top, right:
+# Moves the container either to a specific location
+# or to the center of the screen. If 'absolute' is
+# used, it is moved to the center of all outputs.
+move [absolute] position [[<px> px] [<px> px]|center]
+
+# Moves the container to the current position of the
+# mouse cursor. Only affects floating containers.
+move position mouse
+-----------------------------------------------------
+
+*Examples*:
+-------------------------------------------------------
+# Move container to the left, bottom, top, right
 bindsym $mod+j move left
 bindsym $mod+k move down
 bindsym $mod+l move up
@@ -1705,10 +1750,12 @@ bindsym $mod+semicolon move right
 # move more than the default
 bindsym $mod+j move left 20 px
 
-# Move floating container to the center
-# of all outputs
+# Move floating container to the center of all outputs
 bindsym $mod+c move absolute position center
-----------------------
+
+# Move container to the current position of the cursor
+bindsym $mod+m move position mouse
+-------------------------------------------------------
 
 === Changing (named) workspaces/moving to workspaces
 
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 bbcd7f6f5bb91dcbda16494c92b8aa9ae7880f65..afb3c32abb605fd04a4c95ece8f18def5f04f273 100644 (file)
@@ -258,6 +258,12 @@ void cmd_move_window_to_position(I3_CMD, char *method, char *x, char *y);
  */
 void cmd_move_window_to_center(I3_CMD, char *method);
 
+/**
+ * Implementation of 'move [window|container] [to] position mouse'
+ *
+ */
+void cmd_move_window_to_mouse(I3_CMD);
+
 /**
  * Implementation of 'move scratchpad'.
  *
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 5e7b8e31361f5873347ed98a1109ecbb960a305d..78e75be8fd35d9589dea7ac873131a8fdaf4cac9 100644 (file)
@@ -70,6 +70,12 @@ bool floating_maybe_reassign_ws(Con *con);
  */
 void floating_center(Con *con, Rect rect);
 
+/**
+ * Moves the given floating con to the current pointer position.
+ *
+ */
+void floating_move_to_pointer(Con *con);
+
 #if 0
 /**
  * Removes the floating client from its workspace and attaches it to the new
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 1cd8d2f6789686dda558edb5f102e4ff3140bffd..8d497cd140d4eeb3a335b66c36bb4c0e7208dcc4 100644 (file)
@@ -271,6 +271,7 @@ state RENAME_WORKSPACE_NEW_NAME:
 # move workspace to [output] <str>
 # move scratchpad
 # move [window|container] [to] [absolute] position [ [<pixels> [px] <pixels> [px]] | center ]
+# move [window|container] [to] position mouse|cursor|pointer
 state MOVE:
   'window'
       ->
@@ -342,6 +343,8 @@ state MOVE_TO_ABSOLUTE_POSITION:
 state MOVE_TO_POSITION:
   'center'
       -> call cmd_move_window_to_center($method)
+  'mouse', 'cursor', 'pointer'
+      -> call cmd_move_window_to_mouse()
   coord_x = word
       -> MOVE_TO_POSITION_X
 
index 992e190f7e3d9e74b4d82dbcd558cc47d944b121..a0218ba6e72ec38f549ac2f667812181b54e26fe 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
@@ -226,6 +227,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 3263dd0394b0d73216151f454e31bbb214f6bb22..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 ||
@@ -1833,6 +1833,30 @@ void cmd_move_window_to_center(I3_CMD, char *method) {
     ysuccess(true);
 }
 
+/*
+ * Implementation of 'move [window|container] [to] position mouse'
+ *
+ */
+void cmd_move_window_to_mouse(I3_CMD) {
+    HANDLE_EMPTY_MATCH;
+
+    owindow *current;
+    TAILQ_FOREACH(current, &owindows, owindows) {
+        Con *floating_con = con_inside_floating(current->con);
+        if (floating_con == NULL) {
+            DLOG("con %p / %s is not floating, cannot move it to the mouse position.\n",
+                 current->con, current->con->name);
+            continue;
+        }
+
+        DLOG("moving floating container %p / %s to cursor position\n", floating_con, floating_con->name);
+        floating_move_to_pointer(floating_con);
+    }
+
+    cmd_output->needs_tree_render = true;
+    ysuccess(true);
+}
+
 /*
  * Implementation of 'move scratchpad'.
  *
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 f5641fffa3b81941e2da4ce5e043f8529ba535e8..a2843535e3d5c2396231c13caf5f349db0bedbbf 100644 (file)
@@ -426,6 +426,42 @@ void floating_center(Con *con, Rect rect) {
     con->rect.y = rect.y + (rect.height / 2) - (con->rect.height / 2);
 }
 
+/*
+ * Moves the given floating con to the current pointer position.
+ *
+ */
+void floating_move_to_pointer(Con *con) {
+    assert(con->type == CT_FLOATING_CON);
+
+    xcb_query_pointer_reply_t *reply = xcb_query_pointer_reply(conn, xcb_query_pointer(conn, root), NULL);
+    if (reply == NULL) {
+        ELOG("could not query pointer position, not moving this container\n");
+        return;
+    }
+
+    Output *output = get_output_containing(reply->root_x, reply->root_y);
+    if (output == NULL) {
+        ELOG("The pointer is not on any output, cannot move the container here.");
+        return;
+    }
+
+    /* Determine where to put the window. */
+    int32_t x = reply->root_x - con->rect.width / 2;
+    int32_t y = reply->root_y - con->rect.height / 2;
+    FREE(reply);
+
+    /* Correct target coordinates to be in-bounds. */
+    x = MAX(x, (int32_t)output->rect.x);
+    y = MAX(y, (int32_t)output->rect.y);
+    if (x + con->rect.width > output->rect.x + output->rect.width)
+        x = output->rect.x + output->rect.width - con->rect.width;
+    if (y + con->rect.height > output->rect.y + output->rect.height)
+        y = output->rect.y + output->rect.height - con->rect.height;
+
+    /* Update container's coordinates to position it correctly. */
+    floating_reposition(con, (Rect){x, y, con->rect.width, con->rect.height});
+}
+
 DRAGGING_CB(drag_window_callback) {
     const struct xcb_button_press_event_t *event = extra;
 
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';
diff --git a/testcases/t/245-move-position-mouse.t b/testcases/t/245-move-position-mouse.t
new file mode 100644 (file)
index 0000000..ae04927
--- /dev/null
@@ -0,0 +1,142 @@
+#!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)
+#
+# Tests the 'move [window|container] [to] position mouse|cursor|pointer command.
+# Ticket: #1696
+use i3test i3_autostart => 0;
+use POSIX qw(floor);
+
+sub warp_pointer {
+    my ($pos_x, $pos_y) = @_;
+    $x->root->warp_pointer($pos_x, $pos_y);
+    sync_with_i3;
+}
+
+my ($pid, $config, $ws, $rect, @cons);
+my $root_rect = $x->root->rect;
+
+##########################################################################
+
+##########################################################################
+# Given a floating container and the cursor far from any edges, when
+# moving the container to the mouse, then the container is moved such
+# that the cursor is centered inside it.
+##########################################################################
+
+$config = <<EOT;
+# i3 config file (v4)
+font pango:monospace 8
+EOT
+$pid = launch_with_config($config);
+
+$ws = fresh_workspace;
+open_floating_window;
+warp_pointer(100, 100);
+
+cmd 'move position mouse';
+
+@cons = @{get_ws($ws)->{floating_nodes}};
+$rect = $cons[0]->{rect};
+
+is(floor($rect->{x} + $rect->{width} / 2), 100, "con is centered around cursor horizontally");
+is(floor($rect->{y} + $rect->{height} / 2), 100, "con is centered around cursor vertically");
+
+exit_gracefully($pid);
+
+##########################################################################
+# Given a floating container and the cursor is in the left upper edge
+# of the output, when moving the container to the mouse, then the 
+# container is moved but adjusted to stay in-bounds.
+##########################################################################
+
+$config = <<EOT;
+# i3 config file (v4)
+font pango:monospace 8
+EOT
+$pid = launch_with_config($config);
+
+$ws = fresh_workspace;
+open_floating_window;
+warp_pointer(5, 5);
+
+cmd 'move position mouse';
+
+@cons = @{get_ws($ws)->{floating_nodes}};
+$rect = $cons[0]->{rect};
+
+is($rect->{x}, 0, "con is on the left edge");
+is($rect->{y}, 0, "con is on the upper edge");
+
+exit_gracefully($pid);
+
+##########################################################################
+# Given a floating container and the cursor is in the left right lower
+# edge of the output, when moving the container to the mouse, then the 
+# container is moved but adjusted to stay in-bounds.
+##########################################################################
+
+$config = <<EOT;
+# i3 config file (v4)
+font pango:monospace 8
+EOT
+$pid = launch_with_config($config);
+
+$ws = fresh_workspace;
+open_floating_window;
+warp_pointer($root_rect->width - 5, $root_rect->height - 5);
+
+cmd 'move position mouse';
+
+@cons = @{get_ws($ws)->{floating_nodes}};
+$rect = $cons[0]->{rect};
+
+is($rect->{x} + $rect->{width}, $root_rect->width, "con is on the right edge");
+is($rect->{y} + $rect->{height}, $root_rect->height, "con is on the lower edge");
+
+exit_gracefully($pid);
+
+##########################################################################
+# Given a floating container and the cursor being close to the corner of
+# an output, when the container is moved to the mouse, then the container
+# is moved but adjusted to the output boundaries.
+# This test verifies that boundaries of individual outputs are respected
+# and not just boundaries of the entire X root screen.
+##########################################################################
+
+$config = <<EOT;
+# i3 config file (v4)
+font pango:monospace 8
+fake-outputs 500x500+0+0,500x500+500+0,500x500+0+500,500x500+500+500
+EOT
+$pid = launch_with_config($config);
+
+$ws = fresh_workspace;
+open_floating_window;
+warp_pointer(495, 495);
+
+cmd 'move position mouse';
+
+@cons = @{get_ws($ws)->{floating_nodes}};
+$rect = $cons[0]->{rect};
+
+is($rect->{x} + $rect->{width}, 500, "con is on the right edge");
+is($rect->{y} + $rect->{height}, 500, "con is on the lower edge");
+
+exit_gracefully($pid);
+
+##########################################################################
+
+done_testing;