]> git.sur5r.net Git - i3/i3/commitdiff
Merge branch 'master' into next
authorMichael Stapelberg <michael@stapelberg.de>
Tue, 28 May 2013 17:56:57 +0000 (19:56 +0200)
committerMichael Stapelberg <michael@stapelberg.de>
Tue, 28 May 2013 17:56:57 +0000 (19:56 +0200)
55 files changed:
contrib/dump-asy.pl
contrib/gtk-tree-watch.pl
debian/changelog
debian/rules
docs/hacking-howto
docs/i3bar-protocol
docs/ipc
docs/multi-monitor
docs/testsuite
docs/userguide
docs/wsbar
i3-config-wizard/main.c
i3-dmenu-desktop
i3bar/include/child.h
i3bar/include/common.h
i3bar/include/config.h
i3bar/src/child.c
i3bar/src/config.c
i3bar/src/ipc.c
i3bar/src/main.c
i3bar/src/parse_json_header.c
i3bar/src/xcb.c
include/commands.h
include/con.h
include/config.h
include/config_directives.h
include/data.h
include/i3/ipc.h
include/randr.h
include/x.h
man/i3-msg.man
parser-specs/commands.spec
parser-specs/config.spec
src/commands.c
src/commands_parser.c
src/con.c
src/config.c
src/config_directives.c
src/config_parser.c
src/floating.c
src/handlers.c
src/ipc.c
src/load_layout.c
src/log.c
src/manage.c
src/randr.c
src/render.c
src/scratchpad.c
src/workspace.c
src/x.c
testcases/t/187-commands-parser.t
testcases/t/201-config-parser.t
testcases/t/202-scratchpad-criteria.t
testcases/t/206-fullscreen-scratchpad.t [new file with mode: 0644]
testcases/t/514-ipc-workspace-multi-monitor.t [new file with mode: 0644]

index 47239f2d41e5ef274c014005c8a76fec373fe362..3ebdb858e9ef275d830765b1a54a75639790b6c4 100755 (executable)
@@ -32,13 +32,15 @@ sub dump_node {
     my $w = (defined($n->{window}) ? $n->{window} : "N");
     my $na = $n->{name};
     $na =~ s/#/\\#/g;
+    $na =~ s/\$/\\\$/g;
+    $na =~ s/&/\\&/g;
     $na =~ s/_/\\_/g;
     $na =~ s/~/\\textasciitilde{}/g;
     my $type = 'leaf';
     if (!defined($n->{window})) {
         $type = $n->{orientation} . '-split';
     }
-    my $name = qq|\\"$na\\" ($type)|;
+    my $name = qq|``$na'' ($type)|;
 
     print $tmp "TreeNode n" . $n->{id} . " = makeNode(";
 
index f15d0c1814eca7e73a13133b08f29538ca42c44f..30cc64fcf35abda5a3704edaec27ee115c5ca1d1 100755 (executable)
@@ -19,7 +19,7 @@ $window->signal_connect('delete_event' => sub { Gtk2->main_quit; });
 
 my $tree_store = Gtk2::TreeStore->new(qw/Glib::String/, qw/Glib::String/, qw/Glib::String/, qw/Glib::String/, qw/Glib::String/, qw/Glib::String/, qw/Glib::String/, qw/Glib::String/);
 
-my $i3 = i3("/tmp/nestedcons");
+my $i3 = i3();
 
 my $tree_view = Gtk2::TreeView->new($tree_store);
 
index ebdfbcef3ec7369c850fa23966f225ab4b92a40a..80a20e3c2d092e10066df87bb3a5db21d72d528e 100644 (file)
@@ -1,8 +1,20 @@
-i3-wm (4.4.1-0) unstable; urgency=low
+i3-wm (4.5.2-1) experimental; urgency=low
 
   * NOT YET RELEASED
 
- -- Michael Stapelberg <stapelberg@debian.org>  Wed, 12 Dec 2012 00:23:32 +0100
+ -- Michael Stapelberg <stapelberg@debian.org>  Mon, 18 Mar 2013 23:01:30 +0100
+
+i3-wm (4.5.1-1) experimental; urgency=low
+
+  * New upstream release
+
+ -- Michael Stapelberg <stapelberg@debian.org>  Mon, 18 Mar 2013 22:50:12 +0100
+
+i3-wm (4.5-1) experimental; urgency=low
+
+  * New upstream release
+
+ -- Michael Stapelberg <stapelberg@debian.org>  Tue, 12 Mar 2013 13:51:04 +0100
 
 i3-wm (4.4-1) experimental; urgency=low
 
index b119c47e84347a2c9a82230c4704cf151791e16d..3ae79dddf30ae59db5de256e4db3422b10e19f2d 100755 (executable)
@@ -38,7 +38,7 @@ override_dh_auto_build:
        $(MAKE) -C docs
 
 override_dh_installchangelogs:
-       dh_installchangelogs RELEASE-NOTES-4.4
+       dh_installchangelogs RELEASE-NOTES-4.5.1
 
 override_dh_install:
        $(MAKE) DESTDIR=$(CURDIR)/debian/i3-wm/ install
index 633c277165f40b3a4d22315573367f0b2641f31b..73f8e88ab4ff22e4a31511bc4bea56a843db2336 100644 (file)
@@ -57,7 +57,7 @@ all, most users sooner or later tend to lay out their windows in a way which
 corresponds to tiling or stacking mode in i3. Therefore, why not let i3 do this
 for you? Certainly, it’s faster than you could ever do it.
 
-The problem with most tiling window managers is that they are too unflexible.
+The problem with most tiling window managers is that they are too inflexible.
 In my opinion, a window manager is just another tool, and similar to vim which
 can edit all kinds of text files (like source code, HTML, …) and is not limited
 to a specific file type, a window manager should not limit itself to a certain
@@ -361,7 +361,7 @@ managed at all:
  * The override_redirect must not be set. Windows with override_redirect shall
    not be managed by a window manager
 
-Afterwards, i3 gets the intial geometry and reparents the window (see
+Afterwards, i3 gets the initial geometry and reparents the window (see
 `reparent_window()`) if it wasn’t already managed.
 
 Reparenting means that for each window which is reparented, a new window,
@@ -383,7 +383,7 @@ target workspace is not visible, the window will not be mapped.
 
 == What happens when an application is started?
 
-i3 does not care for applications. All it notices is when new windows are
+i3 does not care about applications. All it notices is when new windows are
 mapped (see `src/handlers.c`, `handle_map_request()`). The window is then
 reparented (see section "Manage windows").
 
@@ -534,7 +534,7 @@ position/size is different: They are placed next to each other on a single line
 
 ==== Dock area layout
 
-This is a special case. Users cannot chose the dock area layout, but it will be
+This is a special case. Users cannot choose the dock area layout, but it will be
 set for the dock area containers. In the dockarea layout (at the moment!),
 windows will be placed above each other.
 
@@ -944,9 +944,11 @@ Without much ado, here is the list of cases which need to be considered:
 
 == Using git / sending patches
 
+=== Introduction
+
 For a short introduction into using git, see
-http://www.spheredev.org/wiki/Git_for_the_lazy or, for more documentation, see
-http://git-scm.com/documentation
+http://web.archive.org/web/20121024222556/http://www.spheredev.org/wiki/Git_for_the_lazy
+or, for more documentation, see http://git-scm.com/documentation
 
 Please talk to us before working on new features to see whether they will be
 accepted. There are a few things which we don’t want to see in i3, e.g. a
@@ -963,6 +965,17 @@ them in the bugtracker, since all reviews should be done in public at
 http://cr.i3wm.org/. In order to make your review go as fast as possible, you
 could have a look at previous reviews and see what the common mistakes are.
 
+=== Which branch to use?
+
+Work on i3 generally happens in two branches: “master” and “next”. Since
+“master” is what people get when they check out the git repository, its
+contents are always stable. That is, it contains the source code of the latest
+release, plus any bugfixes that were applied since that release.
+
+New features are only found in the “next” branch. Therefore, if you are working
+on a new feature, use the “next” branch. If you are working on a bugfix, use
+the “next” branch, too, but make sure your code also works on “master”.
+
 == Thought experiments
 
 In this section, we collect thought experiments, so that we don’t forget our
index 9225d97eab134f90b9567095f2cb2eea622ef3e0..bd8ea5365a4a8883d023f9e4c6e2b88d8251626c 100644 (file)
@@ -51,7 +51,7 @@ consists of a single JSON hash:
 
 *All features example*:
 ------------------------------
-{ "version": 1, "stop_signal": 10, "cont_signal": 12 }
+{ "version": 1, "stop_signal": 10, "cont_signal": 12, "click_events": true }
 ------------------------------
 
 (Note that before i3 v4.3 the precise format had to be +{"version":1}+,
@@ -110,6 +110,9 @@ cont_signal::
        Specify to i3bar the signal (as an integer)to send to continue your
        processing.
        The default value (if none is specified) is SIGCONT.
+click_events::
+       If specified and true i3bar will write a infinite array (same as above)
+       to your stdin.
 
 === Blocks in detail
 
@@ -210,3 +213,28 @@ An example of a block which uses all possible entries follows:
  "separator_block_width": 9
 }
 ------------------------------------------
+
+=== Click events
+
+If enabled i3bar will send you notifications if the user clicks on a block and
+looks like this:
+
+name::
+       Name of the block, if set
+instance::
+       Instance of the block, if set
+x, y::
+       X11 root window coordinates where the click occured
+button:
+       X11 button ID (for example 1 to 3 for left/middle/right mouse button)
+
+*Example*:
+------------------------------------------
+{
+ "name": "ethernet",
+ "instance": "eth0",
+ "button": 1,
+ "x": 1320,
+ "y": 1400
+}
+------------------------------------------
index 8cfb21d035659d2089a4c59a0a3809b205192c7f..99bc5852c3e8fd2864b1fe6bb489554e3ffbc412 100644 (file)
--- a/docs/ipc
+++ b/docs/ipc
@@ -626,6 +626,9 @@ mode (2)::
 window (3)::
        Sent when a client's window is successfully reparented (that is when i3
        has finished fitting it into a container).
+barconfig_update (4)::
+    Sent when the hidden_state or mode field in the barconfig of any bar
+    instance was updated.
 
 *Example:*
 --------------------------------------------------------------------
@@ -723,6 +726,24 @@ window title as "urxvt").
 }
 ---------------------------
 
+=== barconfig_update event
+
+This event consists of a single serialized map reporting on options from the
+barconfig of the specified bar_id that were updated in i3. The map always
+consists of a property +id (string)+, which specifies to which bar instance the
+sent config update belongs, a property +hidden_state (string)+, which indicates
+the hidden_state of an i3bar instance, and a property +mode (string)+, which
+corresponds to the current mode.
+
+*Example:*
+---------------------------
+{
+    "id": "bar-0",
+    "hidden_state": "hide"
+    "mode": "hide"
+}
+---------------------------
+
 == See also (existing libraries)
 
 [[libraries]]
index a1fd6dc039273b454c2ddc9ddcd322c4726d754c..cafe569103b6c3f88a08615b697c4f176bb9c137 100644 (file)
@@ -1,15 +1,16 @@
 The multi-monitor situation
 ===========================
-Michael Stapelberg <michael+i3@stapelberg.de>
-September 2011
+Michael Stapelberg <michael@i3wm.org>
+April 2013
 
-…or: oh no, I have an nVidia graphics card!
+Please upgrade your nVidia driver to version 302.17 or newer and i3 will just
+work. This document is kept around for historic reasons only.
 
 == The quick fix
 
 If you are using the nVidia binary graphics driver (also known as 'blob')
-you need to use the +--force-xinerama+ flag (in your .xsession) when starting
-i3, like so:
+before version 302.17, you need to use the +--force-xinerama+ flag (in your
+.xsession) when starting i3, like so:
 
 .Example:
 ----------------------------------------------
index 9b7485bb82f39dd0f16d5f8cc953bd3d4e0942dc..6c3a36d9c78e9f253a8b0371eee4be55f0f29c31 100644 (file)
@@ -143,6 +143,16 @@ Result: PASS
 $ less latest/i3-log-for-04-floating.t
 ----------------------------------------
 
+If your attempt to run the tests with a bare call to ./complete-run.pl fails, try this:
+
+---------------------------------------------------
+$ ./complete-run.pl --parallel=1 --keep-xdummy-output
+---------------------------------------------------
+
+One common cause of failures is not having the X dummy server module
+installed.  Under Debian and Ubuntu this is the package
++xserver-xorg-video-dummy+.
+
 ==== IPC interface
 
 The testsuite makes extensive use of the IPC (Inter-Process Communication)
index a0f521c27b2613806742b35e494e15d351bea26f..09cd72bf3905d0f9a92e32bd9e410074052f66b5 100644 (file)
@@ -1,7 +1,7 @@
 i3 User’s Guide
 ===============
 Michael Stapelberg <michael@i3wm.org>
-February 2013
+March 2013
 
 This document contains all the information you need to configure and use the i3
 window manager. If it does not, please check http://faq.i3wm.org/ first, then
@@ -26,8 +26,8 @@ are your homerow.
 == Using i3
 
 Throughout this guide, the keyword +$mod+ will be used to refer to the
-configured modifier. This is the Alt key (Mod1) by default, with windows (Mod4)
-being a popular alternative.
+configured modifier. This is the Alt key (Mod1) by default, with the Windows
+key (Mod4) being a popular alternative.
 
 === Opening terminals and moving around
 
@@ -147,7 +147,7 @@ columns/rows with your keyboard.
 
 === Restarting i3 inplace
 
-To restart i3 inplace (and thus get into a clean state if there is a bug, or
+To restart i3 in place (and thus get into a clean state if there is a bug, or
 to upgrade to a newer version of i3) you can use +$mod+Shift+r+.
 
 === Exiting i3
@@ -156,11 +156,12 @@ To cleanly exit i3 without killing your X server, you can use +$mod+Shift+e+.
 
 === Floating
 
-Floating mode is the opposite of tiling mode. The position and size of a window
-are not managed by i3, but by you. Using this mode violates the tiling
-paradigm but can be useful for some corner cases like "Save as" dialog
-windows, or toolbar windows (GIMP or similar). Those windows usually set the
-appropriate hint and are opened in floating mode by default.
+Floating mode is the opposite of tiling mode. The position and size of
+a window are not managed automatically by i3, but manually by
+you. Using this mode violates the tiling paradigm but can be useful
+for some corner cases like "Save as" dialog windows, or toolbar
+windows (GIMP or similar). Those windows usually set the appropriate
+hint and are opened in floating mode by default.
 
 You can toggle floating mode for a window by pressing +$mod+Shift+Space+. By
 dragging the window’s titlebar with your mouse you can move the window
@@ -259,7 +260,7 @@ other one being the terminal window you moved down.
 [[configuring]]
 == Configuring i3
 
-This is where the real fun begins ;-). Most things are very dependant on your
+This is where the real fun begins ;-). Most things are very dependent on your
 ideal working environment so we can’t make reasonable defaults for them.
 
 While not using a programming language for the configuration, i3 stays
@@ -761,7 +762,7 @@ from single windows outside of a split container.
 
 === Interprocess communication
 
-i3 uses unix sockets to provide an IPC interface. This allows third-party
+i3 uses Unix sockets to provide an IPC interface. This allows third-party
 programs to get information from i3, such as the current workspaces
 (to display a workspace bar), and to control i3.
 
@@ -995,20 +996,39 @@ bar {
 
 === Display mode
 
-You can have i3bar either be visible permanently at one edge of the screen
-(+dock+ mode) or make it show up when you press your modifier key (+hide+
+You can either have i3bar be visible permanently at one edge of the screen
+(+dock+ mode) or make it show up when you press your modifier key (+hide+ mode).
+It is also possible to force i3bar to always stay hidden (+invisible+
 mode). The modifier key can be configured using the +modifier+ option.
 
+The mode option can be changed during runtime through the +bar mode+ command.
+On reload the mode will be reverted to its configured value.
+
 The hide mode maximizes screen space that can be used for actual windows. Also,
 i3bar sends the +SIGSTOP+ and +SIGCONT+ signals to the statusline process to
 save battery power.
 
-The default is dock mode; in hide mode, the default modifier is Mod4 (usually
-the windows key).
+Invisible mode allows to permanently maximize screen space, as the bar is never
+shown. Thus, you can configure i3bar to not disturb you by popping up because
+of an urgency hint or because the modifier key is pressed.
+
+In order to control whether i3bar is hidden or shown in hide mode, there exists
+the hidden_state option, which has no effect in dock mode or invisible mode. It
+indicates the current hidden_state of the bar: (1) The bar acts like in normal
+hide mode, it is hidden and is only unhidden in case of urgency hints or by
+pressing the modifier key (+hide+ state), or (2) it is drawn on top of the
+currently visible workspace (+show+ state).
+
+Like the mode, the hidden_state can also be controlled through i3, this can be
+done by using the +bar hidden_state+ command.
+
+The default mode is dock mode; in hide mode, the default modifier is Mod4 (usually
+the windows key). The default value for the hidden_state is hide.
 
 *Syntax*:
 ----------------
-mode <dock|hide>
+mode <dock|hide|invisible>
+hidden_state <hide|show>
 modifier <Modifier>
 ----------------
 
@@ -1016,12 +1036,31 @@ modifier <Modifier>
 ----------------
 bar {
     mode hide
+    hidden_state hide
     modifier Mod1
 }
 ----------------
 
 Available modifiers are Mod1-Mod5, Shift, Control (see +xmodmap(1)+).
 
+=== Bar ID
+
+Specifies the bar ID for the configured bar instance. If this option is missing,
+the ID is set to 'bar-x', where x corresponds to the position of the embedding
+bar block in the config file ('bar-0', 'bar-1', ...).
+
+*Syntax*:
+---------------------
+id <bar_id>
+---------------------
+
+*Example*:
+---------------------
+bar {
+    id bar-1
+}
+---------------------
+
 [[i3bar_position]]
 === Position
 
@@ -1223,7 +1262,7 @@ bindsym $mod+x move container to workspace 3; workspace 3
 
 [[command_criteria]]
 
-Furthermore, you can change the scope of a command, that is, which containers
+Furthermore, you can change the scope of a command - that is, which containers
 should be affected by that command, by using various criteria. These are
 prefixed in square brackets to every command. If you want to kill all windows
 which have the class Firefox, use:
@@ -1319,9 +1358,9 @@ bindsym $mod+h split horizontal
 
 === Manipulating layout
 
-Use +layout toggle split+, +layout stacking+ or +layout tabbed+ to change the
-current container layout to splith/splitv, stacking or tabbed layout,
-respectively.
+Use +layout toggle split+, +layout stacking+, +layout tabbed+, +layout splitv+
+or +layout splith+ to change the current container layout to splith/splitv,
+stacking, tabbed layout, splitv or splith, respectively.
 
 To make the current window (!) fullscreen, use +fullscreen+, to make
 it floating (or tiling again) use +floating enable+ respectively +floating disable+
@@ -1329,7 +1368,7 @@ it floating (or tiling again) use +floating enable+ respectively +floating disab
 
 *Syntax*:
 --------------
-layout <tabbed|stacking>
+layout <default|tabbed|stacking|splitv|splith>
 layout toggle [split|all]
 --------------
 
@@ -1774,6 +1813,38 @@ bindsym $mod+minus scratchpad show
 bindsym mod4+s [title="^Sup ::"] scratchpad show
 ------------------------------------------------
 
+=== i3bar control
+
+There are two options in the configuration of each i3bar instance that can be
+changed during runtime by invoking a command through i3. The commands +bar
+hidden_state+ and +bar mode+ allow setting the current hidden_state
+respectively mode option of each bar. It is also possible to toggle between
+hide state and show state as well as between dock mode and hide mode. Each
+i3bar instance can be controlled individually by specifying a bar_id, if none
+is given, the command is executed for all bar instances.
+
+*Syntax*:
+---------------
+bar hidden_state hide|show|toggle [<bar_id>]
+
+bar mode dock|hide|invisible|toggle [<bar_id>]
+---------------
+
+*Examples*:
+------------------------------------------------
+# Toggle between hide state and show state
+bindsym $mod+m bar hidden_state toggle
+
+# Toggle between dock mode and hide mode
+bindsym $mod+n bar mode toggle
+
+# Set the bar instance with id 'bar-1' to switch to hide mode
+bindsym $mod+b bar mode hide bar-1
+
+# Set the bar instance with id 'bar-1' to always stay hidden
+bindsym $mod+Shift+b bar mode invisible bar-1
+------------------------------------------------
+
 [[multi_monitor]]
 
 == Multiple monitors
index 9e379dd91f370e7d966e509020a35e049abd959a..6405880a580811c2520b191f7462212e51ad0804 100644 (file)
@@ -1,23 +1,18 @@
 External workspace bars
 =======================
-Michael Stapelberg <michael+i3@stapelberg.de>
-May 2010
+Michael Stapelberg <michael@i3wm.org>
+April 2013
 
-This document describes why the internal workspace bar is minimal and how an
-external workspace bar can be used. It explains the concepts using +i3-wsbar+
-as the reference implementation.
+i3 comes with i3bar by default, a simple bar that is sufficient for most users.
+In case you are unhappy with it, this document explains how to use a different,
+external workspace bar. Note that we do not provide support for external
+programs.
 
 == Internal and external bars
 
 The internal workspace bar of i3 is meant to be a reasonable default so that
 you can use i3 without having too much hassle when setting it up. It is quite
-simple and intended to stay this way. So, there is no way to display your own
-information in this bar (unlike dwm, wmii, awesome, …).
-
-We chose not to implement such a mechanism because that would be duplicating
-already existing functionality of tools such as dzen2, xmobar and similar.
-Instead, you should disable the internal bar and use an external workspace bar
-(which communicates with i3 through its IPC interface).
+simple and intended to stay this way.
 
 == dock mode
 
@@ -25,10 +20,10 @@ You typically want to see the same workspace bar on every workspace on a
 specific screen. Also, you don’t want to place the workspace bar somewhere
 in your layout by hand. This is where dock mode comes in: When a program sets
 the appropriate hint (_NET_WM_WINDOW_TYPE_DOCK), it will be managed in dock
-mode by i3. That means it will be placed at the bottom of the screen (while
-other edges of the screen are possible in the NetWM standard, this is not yet
-implemented in i3), it will not overlap any other window and it will be on
-every workspace for the specific screen it was placed on initially.
+mode by i3. That means it will be placed at the bottom or top of the screen
+(while other edges of the screen are possible in the NetWM standard, this is
+not yet implemented in i3), it will not overlap any other window and it will be
+on every workspace for the specific screen it was placed on initially.
 
 == The IPC interface
 
@@ -37,8 +32,8 @@ provide the bar program with the current workspaces and output (as in VGA-1,
 LVDS-1, …) configuration. In the other direction, the program has to be able
 to switch to specific workspaces.
 
-By default, the IPC interface is enabled and places its UNIX socket in
-+~/.i3/ipc.sock+.
+By default, the IPC interface is enabled and you can get the path to the socket
+by calling +i3 --get-socketpath+.
 
 To learn more about the protocol which is used for IPC, see +docs/ipc+.
 
@@ -49,17 +44,17 @@ external workspace bar implementation needs to make sure that when you change
 the resolution of any of your screens (or enable/disable an output), the bars
 will be adjusted properly.
 
-== i3-wsbar, the reference implementation
+== i3-wsbar, an example implementation
 
-Please keep in mind that +i3-wsbar+ is just a reference implementation. It is
-shipped with i3 to have a reasonable default. Thus, +i3-wsbar+ is designed to
-work well with dzen2 and there are no plans to make it more generic.
++i3-wsbar+ used to be the reference implementation before we had +i3bar+.
+Nowadays, it is not shipped with release tarballs, but you can still get it at
+http://code.stapelberg.de/git/i3/tree/contrib/i3-wsbar
 
 === The big picture
 
 The most common reason to use an external workspace bar is to integrate system
-information such as what +i3status+ provides into the workspace bar (to save
-screen space). So, we have +i3status+ or a similar program, which only provides
+information such as what +i3status+ or +conky+ provide into the workspace bar.
+So, we have +i3status+ or a similar program, which only provides
 text output (formatted in some way). To display this text nicely on the screen,
 there are programs such as dzen2, xmobar and similar. We will stick to dzen2
 from here on. So, we have the output of i3status, which needs to go into dzen2
@@ -89,6 +84,3 @@ To actually get a benefit, you want to give +i3-wsbar+ some input:
 ------------------------------------------
 i3status | i3-wsbar -c "dzen2 -x %x -dock"
 ------------------------------------------
-
-It is recommended to place the above command in your i3 configuration file
-to start it automatically with i3.
index 54c8e0245478d11b97677d8d445e4518d7ee8510..ffc3df93011869e88561e523445fa9e267aeb68e 100644 (file)
@@ -13,7 +13,7 @@
 #endif
 
 /* For systems without getline, fall back to fgetln */
-#if defined(__APPLE__) || (defined(__FreeBSD__) && __FreeBSD_version < 800000)
+#if defined(__APPLE__)
 #define USE_FGETLN
 #elif defined(__FreeBSD__)
 /* Defining this macro before including stdio.h is necessary in order to have
index 65e99ec09b13c663df6820dbcbf293c588bb463d..cf5b41e0f8f97d943aebc44e3ec6824d6d49de47 100755 (executable)
@@ -346,7 +346,13 @@ if (exists($choices{$choice})) {
         last;
     }
     if (!defined($app)) {
-        die "Invalid input: “$choice” does not match any application.";
+        warn "Invalid input: “$choice” does not match any application. Trying to execute nevertheless.";
+        $app->{Name} = '';
+        $app->{Exec} = $choice;
+        # We assume that the app is old and does not support startup
+        # notifications because it doesn’t ship a desktop file.
+        $app->{StartupNotify} = 0;
+        $app->{_Location} = '';
     }
 }
 
index d1c46890bc28e6534fdebf7987da54572c1e5bc0..dc244befe83080808246db148fb68ee8b0930ce8 100644 (file)
@@ -33,6 +33,12 @@ typedef struct {
      * The signal requested by the client to inform it of theun hidden state of i3bar
      */
     int cont_signal;
+
+    /**
+     * Enable click events
+     */
+    bool click_events;
+    bool click_events_init;
 } i3bar_child;
 
 /*
@@ -68,4 +74,10 @@ void stop_child(void);
  */
 void cont_child(void);
 
+/*
+ * Generates a click event, if enabled.
+ *
+ */
+void send_block_clicked(int button, const char *name, const char *instance, int x, int y);
+
 #endif
index 1365082f54591c0720712ce346ce7c37373ff36d..cb55e0d679d6f751ef5ed74d4a9bbc1c375ef829 100644 (file)
@@ -54,6 +54,10 @@ struct status_block {
     uint32_t x_offset;
     uint32_t x_append;
 
+    /* Optional */
+    char *name;
+    char *instance;
+
     TAILQ_ENTRY(status_block) blocks;
 };
 
index 4f6e8858f2f8798809fb17e3198d22e4f59cef56..4c01d68cec4d8aeb03be106081dd4f9576f034ab 100644 (file)
@@ -19,7 +19,6 @@ typedef enum {
 } position_t;
 
 typedef struct config_t {
-    int          hide_on_modifier;
     int          modifier;
     position_t   position;
     int          verbose;
@@ -31,6 +30,12 @@ typedef struct config_t {
     char         *tray_output;
     int          num_outputs;
     char         **outputs;
+
+    /* Bar display mode (hide unless modifier is pressed or show in dock mode or always hide in invisible mode) */
+    enum { M_DOCK = 0, M_HIDE = 1, M_INVISIBLE = 2 } hide_on_modifier;
+
+    /* The current hidden_state of the bar, which indicates whether it is hidden or shown */
+    enum { S_HIDE = 0, S_SHOW = 1 } hidden_state;
 } config_t;
 
 config_t config;
index e5f4ea213cd10a5f16d9a2eb0a21343f45fdae63..4e5e49c928edf37f1e3ff450c0cb5b5c0f87ddaa 100644 (file)
@@ -21,6 +21,7 @@
 #include <yajl/yajl_common.h>
 #include <yajl/yajl_parse.h>
 #include <yajl/yajl_version.h>
+#include <yajl/yajl_gen.h>
 
 #include "common.h"
 
@@ -35,6 +36,9 @@ ev_child *child_sig;
 yajl_callbacks callbacks;
 yajl_handle parser;
 
+/* JSON generator for stdout */
+yajl_gen gen;
+
 typedef struct parser_ctx {
     /* True if one of the parsed blocks was urgent */
     bool has_urgent;
@@ -53,6 +57,8 @@ parser_ctx parser_context;
 struct statusline_head statusline_head = TAILQ_HEAD_INITIALIZER(statusline_head);
 char *statusline_buffer = NULL;
 
+int child_stdin;
+
 /*
  * Stop and free() the stdin- and sigchild-watchers
  *
@@ -85,6 +91,8 @@ static int stdin_start_array(void *context) {
         first = TAILQ_FIRST(&statusline_head);
         I3STRING_FREE(first->full_text);
         FREE(first->color);
+        FREE(first->name);
+        FREE(first->instance);
         TAILQ_REMOVE(&statusline_head, first, blocks);
         free(first);
     }
@@ -152,6 +160,18 @@ static int stdin_string(void *context, const unsigned char *val, unsigned int le
         ctx->block.min_width = (uint32_t)predict_text_width(text);
         i3string_free(text);
     }
+    if (strcasecmp(ctx->last_map_key, "name") == 0) {
+        char *copy = (char*)malloc(len+1);
+        strncpy(copy, (const char *)val, len);
+        copy[len] = 0;
+        ctx->block.name = copy;
+    }
+    if (strcasecmp(ctx->last_map_key, "instance") == 0) {
+        char *copy = (char*)malloc(len+1);
+        strncpy(copy, (const char *)val, len);
+        copy[len] = 0;
+        ctx->block.instance = copy;
+    }
     return 1;
 }
 
@@ -336,6 +356,21 @@ void child_sig_cb(struct ev_loop *loop, ev_child *watcher, int revents) {
     cleanup();
 }
 
+void child_write_output(void) {
+    if (child.click_events) {
+        const unsigned char *output;
+#if YAJL_MAJOR < 2
+        unsigned int size;
+#else
+        size_t size;
+#endif
+        yajl_gen_get_buf(gen, &output, &size);
+        write(child_stdin, output, size);
+        write(child_stdin, "\n", 1);
+        yajl_gen_clear(gen);
+    }
+}
+
 /*
  * Start a child-process with the specified command and reroute stdin.
  * We actually start a $SHELL to execute the command so we don't have to care
@@ -357,14 +392,22 @@ void start_child(char *command) {
     yajl_parser_config parse_conf = { 0, 0 };
 
     parser = yajl_alloc(&callbacks, &parse_conf, NULL, (void*)&parser_context);
+
+    gen = yajl_gen_alloc(NULL, NULL);
 #else
     parser = yajl_alloc(&callbacks, NULL, &parser_context);
+
+    gen = yajl_gen_alloc(NULL);
 #endif
 
     if (command != NULL) {
-        int fd[2];
-        if (pipe(fd) == -1)
-            err(EXIT_FAILURE, "pipe(fd)");
+        int pipe_in[2]; /* pipe we read from */
+        int pipe_out[2]; /* pipe we write to */
+
+        if (pipe(pipe_in) == -1)
+            err(EXIT_FAILURE, "pipe(pipe_in)");
+        if (pipe(pipe_out) == -1)
+            err(EXIT_FAILURE, "pipe(pipe_out)");
 
         child.pid = fork();
         switch (child.pid) {
@@ -372,10 +415,13 @@ void start_child(char *command) {
                 ELOG("Couldn't fork(): %s\n", strerror(errno));
                 exit(EXIT_FAILURE);
             case 0:
-                /* Child-process. Reroute stdout and start shell */
-                close(fd[0]);
+                /* Child-process. Reroute streams and start shell */
+
+                close(pipe_in[0]);
+                close(pipe_out[1]);
 
-                dup2(fd[1], STDOUT_FILENO);
+                dup2(pipe_in[1], STDOUT_FILENO);
+                dup2(pipe_out[0], STDIN_FILENO);
 
                 static const char *shell = NULL;
 
@@ -385,10 +431,13 @@ void start_child(char *command) {
                 execl(shell, shell, "-c", command, (char*) NULL);
                 return;
             default:
-                /* Parent-process. Rerout stdin */
-                close(fd[1]);
+                /* Parent-process. Reroute streams */
 
-                dup2(fd[0], STDIN_FILENO);
+                close(pipe_in[1]);
+                close(pipe_out[0]);
+
+                dup2(pipe_in[0], STDIN_FILENO);
+                child_stdin = pipe_out[1];
 
                 break;
         }
@@ -409,6 +458,52 @@ void start_child(char *command) {
     atexit(kill_child_at_exit);
 }
 
+void child_click_events_initialize(void) {
+    if (!child.click_events_init) {
+        yajl_gen_array_open(gen);
+        child_write_output();
+        child.click_events_init = true;
+    }
+}
+
+void child_click_events_key(const char *key) {
+    yajl_gen_string(gen, (const unsigned char *)key, strlen(key));
+}
+
+/*
+ * Generates a click event, if enabled.
+ *
+ */
+void send_block_clicked(int button, const char *name, const char *instance, int x, int y) {
+    if (child.click_events) {
+        child_click_events_initialize();
+
+        yajl_gen_map_open(gen);
+
+        if (name) {
+            child_click_events_key("name");
+            yajl_gen_string(gen, (const unsigned char *)name, strlen(name));
+        }
+
+        if (instance) {
+            child_click_events_key("instance");
+            yajl_gen_string(gen, (const unsigned char *)instance, strlen(instance));
+        }
+
+        child_click_events_key("button");
+        yajl_gen_integer(gen, button);
+
+        child_click_events_key("x");
+        yajl_gen_integer(gen, x);
+
+        child_click_events_key("y");
+        yajl_gen_integer(gen, y);
+
+        yajl_gen_map_close(gen);
+        child_write_output();
+    }
+}
+
 /*
  * kill()s the child-process (if any). Called when exit()ing.
  *
index 6c7286c4afd62d5094de71b7c4e76bf56c3579e9..f5a2a342f48d756adca0ac79a238addac134fc7e 100644 (file)
@@ -73,7 +73,15 @@ static int config_string_cb(void *params_, const unsigned char *val, unsigned in
 
     if (!strcmp(cur_key, "mode")) {
         DLOG("mode = %.*s, len = %d\n", len, val, len);
-        config.hide_on_modifier = (len == 4 && !strncmp((const char*)val, "hide", strlen("hide")));
+        config.hide_on_modifier = (len == 4 && !strncmp((const char*)val, "dock", strlen("dock")) ? M_DOCK
+            : (len == 4 && !strncmp((const char*)val, "hide", strlen("hide")) ? M_HIDE
+                : M_INVISIBLE));
+        return 1;
+    }
+
+    if (!strcmp(cur_key, "hidden_state")) {
+        DLOG("hidden_state = %.*s, len = %d\n", len, val, len);
+        config.hidden_state = (len == 4 && !strncmp((const char*)val, "hide", strlen("hide")) ? S_HIDE : S_SHOW);
         return 1;
     }
 
index faab8e102890a84422b6fc8c5231d71c58c245b3..ca5b404a221d005501b614a746dd465bfa2d1e0d 100644 (file)
@@ -149,12 +149,37 @@ void got_mode_event(char *event) {
     draw_bars(false);
 }
 
+/*
+ * Called, when a barconfig_update event arrives (i.e. i3 changed the bar hidden_state or mode)
+ *
+ */
+void got_bar_config_update(char *event) {
+    /* check whether this affect this bar instance by checking the bar_id */
+    char *expected_id;
+    sasprintf(&expected_id, "\"id\":\"%s\"", config.bar_id);
+    char *found_id = strstr(event, expected_id);
+    FREE(expected_id);
+    if (found_id == NULL)
+       return;
+
+    /* update the configuration with the received settings */
+    DLOG("Received bar config update \"%s\"\n", event);
+    int old_mode = config.hide_on_modifier;
+    parse_config_json(event);
+    if (old_mode != config.hide_on_modifier) {
+        reconfig_windows();
+    }
 
-/* Data-structure to easily call the reply-handlers later */
+    draw_bars(false);
+}
+
+/* Data-structure to easily call the event-handlers later */
 handler_t event_handlers[] = {
     &got_workspace_event,
     &got_output_event,
-    &got_mode_event
+    &got_mode_event,
+    NULL,
+    &got_bar_config_update,
 };
 
 /*
@@ -310,8 +335,8 @@ void destroy_connection(void) {
  */
 void subscribe_events(void) {
     if (config.disable_ws) {
-        i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"output\", \"mode\" ]");
+        i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"output\", \"mode\", \"barconfig_update\" ]");
     } else {
-        i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"workspace\", \"output\", \"mode\" ]");
+        i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"workspace\", \"output\", \"mode\", \"barconfig_update\" ]");
     }
 }
index ea6056470c5f05381fc4c1e9efffddb1e3abbb7b..c62f7b3c96d74c8a1d0a802caf4ac9aeb455fcd5 100644 (file)
@@ -53,12 +53,12 @@ char *expand_path(char *path) {
 }
 
 void print_usage(char *elf_name) {
-    printf("Usage: %s [-b bar_id] [-s sock_path] [-h] [-v]\n", elf_name);
+    printf("Usage: %s -b bar_id [-s sock_path] [-h] [-v]\n", elf_name);
     printf("\n");
-    printf("--bar_id <bar_id>\tBar ID for which to get the configuration\n");
-    printf("-s <sock_path>\tConnect to i3 via <sock_path>\n");
-    printf("-h\t\tDisplay this help-message and exit\n");
-    printf("-v\t\tDisplay version number and exit\n");
+    printf("-b, --bar_id  <bar_id>\tBar ID for which to get the configuration\n");
+    printf("-s, --socket  <sock_path>\tConnect to i3 via <sock_path>\n");
+    printf("-h, --help    Display this help-message and exit\n");
+    printf("-v, --version Display version number and exit\n");
     printf("\n");
     printf(" PLEASE NOTE that i3bar will be automatically started by i3\n"
            " as soon as there is a 'bar' configuration block in your\n"
@@ -97,13 +97,13 @@ int main(int argc, char **argv) {
 
     static struct option long_opt[] = {
         { "socket",               required_argument, 0, 's' },
-        { "bar_id",               required_argument, 0, 0 },
+        { "bar_id",               required_argument, 0, 'b' },
         { "help",                 no_argument,       0, 'h' },
         { "version",              no_argument,       0, 'v' },
         { NULL,                   0,                 0, 0}
     };
 
-    while ((opt = getopt_long(argc, argv, "s:hv", long_opt, &option_index)) != -1) {
+    while ((opt = getopt_long(argc, argv, "b:s:hv", long_opt, &option_index)) != -1) {
         switch (opt) {
             case 's':
                 socket_path = expand_path(optarg);
@@ -112,11 +112,8 @@ int main(int argc, char **argv) {
                 printf("i3bar version " I3_VERSION " © 2010-2011 Axel Wagner and contributors\n");
                 exit(EXIT_SUCCESS);
                 break;
-            case 0:
-                if (!strcmp(long_opt[option_index].name, "bar_id")) {
-                    FREE(config.bar_id);
-                    config.bar_id = sstrdup(optarg);
-                }
+            case 'b':
+                config.bar_id = sstrdup(optarg);
                 break;
             default:
                 print_usage(argv[0]);
index 80ec5af8f78d4de61bda169e442b5552ac57631e..c09e0f499aaf107bedc0f531dd391483b0dd2622 100644 (file)
@@ -31,6 +31,7 @@ static enum {
     KEY_VERSION,
     KEY_STOP_SIGNAL,
     KEY_CONT_SIGNAL,
+    KEY_CLICK_EVENTS,
     NO_KEY
 } current_key;
 
@@ -54,6 +55,21 @@ static int header_integer(void *ctx, long val) {
         default:
             break;
     }
+
+    return 1;
+}
+
+static int header_boolean(void *ctx, int val) {
+    i3bar_child *child = ctx;
+
+    switch (current_key) {
+        case KEY_CLICK_EVENTS:
+            child->click_events = val;
+            break;
+        default:
+            break;
+    }
+
     return 1;
 }
 
@@ -71,13 +87,15 @@ static int header_map_key(void *ctx, const unsigned char *stringval, unsigned in
         current_key = KEY_STOP_SIGNAL;
     } else if (CHECK_KEY("cont_signal")) {
         current_key = KEY_CONT_SIGNAL;
+    } else if (CHECK_KEY("click_events")) {
+        current_key = KEY_CLICK_EVENTS;
     }
     return 1;
 }
 
 static yajl_callbacks version_callbacks = {
     NULL, /* null */
-    NULL, /* boolean */
+    &header_boolean, /* boolean */
     &header_integer,
     NULL, /* double */
     NULL, /* number */
index 3cdf6ed7bebc5b49d530a4c6ef8672203569b4a8..a4f75e624f20ca4fa8c47e05e7424ec28fe56c1e 100644 (file)
@@ -80,6 +80,9 @@ ev_io      *xkb_io;
 /* The name of current binding mode */
 static mode binding;
 
+/* Indicates whether a new binding mode was recently activated */
+bool activated_mode = false;
+
 /* The parsed colors */
 struct xcb_colors_t {
     uint32_t bar_fg;
@@ -162,7 +165,7 @@ void refresh_statusline(void) {
         realloc_sl_buffer();
 
     /* Clear the statusline pixmap. */
-    xcb_rectangle_t rect = { 0, 0, root_screen->width_in_pixels, font.height };
+    xcb_rectangle_t rect = { 0, 0, root_screen->width_in_pixels, font.height + 2 };
     xcb_poly_fill_rectangle(xcb_connection, statusline_pm, statusline_clear, 1, &rect);
 
     /* Draw the text of each block. */
@@ -195,7 +198,7 @@ void refresh_statusline(void) {
  *
  */
 void hide_bars(void) {
-    if (!config.hide_on_modifier) {
+    if ((config.hide_on_modifier == M_DOCK) || (config.hidden_state == S_SHOW)) {
         return;
     }
 
@@ -214,7 +217,7 @@ void hide_bars(void) {
  *
  */
 void unhide_bars(void) {
-    if (!config.hide_on_modifier) {
+    if (config.hide_on_modifier != M_HIDE) {
         return;
     }
 
@@ -320,24 +323,11 @@ void handle_button(xcb_button_press_event_t *event) {
     }
 
     int32_t x = event->event_x >= 0 ? event->event_x : 0;
+    int32_t original_x = x;
 
     DLOG("Got Button %d\n", event->detail);
 
     switch (event->detail) {
-        case 1:
-            /* Left Mousbutton. We determine, which button was clicked
-             * and set cur_ws accordingly */
-            TAILQ_FOREACH(cur_ws, walk->workspaces, tailq) {
-                DLOG("x = %d\n", x);
-                if (x >= 0 && x < cur_ws->name_width + 10) {
-                    break;
-                }
-                x -= cur_ws->name_width + 11;
-            }
-            if (cur_ws == NULL) {
-                return;
-            }
-            break;
         case 4:
             /* Mouse wheel up. We select the previous ws, if any.
              * If there is no more workspace, don’t even send the workspace
@@ -358,6 +348,52 @@ void handle_button(xcb_button_press_event_t *event) {
 
             cur_ws = TAILQ_NEXT(cur_ws, tailq);
             break;
+        default:
+            /* Check if this event regards a workspace button */
+            TAILQ_FOREACH(cur_ws, walk->workspaces, tailq) {
+                DLOG("x = %d\n", x);
+                if (x >= 0 && x < cur_ws->name_width + 10) {
+                    break;
+                }
+                x -= cur_ws->name_width + 11;
+            }
+            if (cur_ws == NULL) {
+                /* No workspace button was pressed.
+                 * Check if a status block has been clicked.
+                 * This of course only has an effect,
+                 * if the child reported bidirectional protocol usage. */
+
+                /* First calculate width of tray area */
+                trayclient *trayclient;
+                int tray_width = 0;
+                TAILQ_FOREACH_REVERSE(trayclient, walk->trayclients, tc_head, tailq) {
+                    if (!trayclient->mapped)
+                        continue;
+                    tray_width += (font.height + 2);
+                }
+
+                int block_x = 0, last_block_x;
+                int offset = (walk->rect.w - (statusline_width + tray_width)) - 10;
+
+                x = original_x - offset;
+                if (x < 0)
+                    return;
+
+                struct status_block *block;
+
+                TAILQ_FOREACH(block, &statusline_head, blocks) {
+                    last_block_x = block_x;
+                    block_x += block->width + block->x_offset + block->x_append;
+
+                    if (x <= block_x && x >= last_block_x) {
+                        send_block_clicked(event->detail, block->name, block->instance, event->root_x, event->root_y);
+                        return;
+                    }
+                }
+                return;
+            }
+            if (event->detail != 1)
+                return;
     }
 
     /* To properly handle workspace names with double quotes in them, we need
@@ -812,7 +848,7 @@ void xkb_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
         modstate = mods & config.modifier;
     }
 
-#define DLOGMOD(modmask, status, barfunc) \
+#define DLOGMOD(modmask, status) \
     do { \
         switch (modmask) { \
             case ShiftMask: \
@@ -837,14 +873,17 @@ void xkb_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
                 DLOG("Mod5Mask got " #status "!\n"); \
                 break; \
         } \
-        barfunc(); \
     } while (0)
 
     if (modstate != mod_pressed) {
         if (modstate == 0) {
-            DLOGMOD(config.modifier, released, hide_bars);
+            DLOGMOD(config.modifier, released);
+            if (!activated_mode)
+                hide_bars();
         } else {
-            DLOGMOD(config.modifier, pressed, unhide_bars);
+            DLOGMOD(config.modifier, pressed);
+            activated_mode = false;
+            unhide_bars();
         }
         mod_pressed = modstate;
     }
@@ -949,25 +988,13 @@ char *init_xcb_early() {
 }
 
 /*
- * Initialization which depends on 'config' being usable. Called after the
- * configuration has arrived.
+ * Register for xkb keyevents. To grab modifiers without blocking other applications from receiving key-events
+ * involving that modifier, we sadly have to use xkb which is not yet fully supported
+ * in xcb.
  *
  */
-void init_xcb_late(char *fontname) {
-    if (fontname == NULL)
-        fontname = "-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1";
-
-    /* Load the font */
-    font = load_font(fontname, true);
-    set_font(&font);
-    DLOG("Calculated Font-height: %d\n", font.height);
-
-    xcb_flush(xcb_connection);
-
-    /* To grab modifiers without blocking other applications from receiving key-events
-     * involving that modifier, we sadly have to use xkb which is not yet fully supported
-     * in xcb */
-    if (config.hide_on_modifier) {
+void register_xkb_keyevents() {
+    if (xkb_dpy == NULL) {
         int xkb_major, xkb_minor, xkb_errbase, xkb_err;
         xkb_major = XkbMajorVersion;
         xkb_minor = XkbMinorVersion;
@@ -1007,6 +1034,39 @@ void init_xcb_late(char *fontname) {
     }
 }
 
+/*
+ * Deregister from xkb keyevents.
+ *
+ */
+void deregister_xkb_keyevents() {
+    if (xkb_dpy != NULL) {
+        ev_io_stop (main_loop, xkb_io);
+        close(xkb_io->fd);
+        FREE(xkb_io);
+        FREE(xkb_dpy);
+    }
+}
+
+/*
+ * Initialization which depends on 'config' being usable. Called after the
+ * configuration has arrived.
+ *
+ */
+void init_xcb_late(char *fontname) {
+    if (fontname == NULL)
+        fontname = "-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1";
+
+    /* Load the font */
+    font = load_font(fontname, true);
+    set_font(&font);
+    DLOG("Calculated Font-height: %d\n", font.height);
+
+    xcb_flush(xcb_connection);
+
+    if (config.hide_on_modifier == M_HIDE)
+        register_xkb_keyevents();
+}
+
 /*
  * Inform clients waiting for a new _NET_SYSTEM_TRAY that we took the
  * selection.
@@ -1329,8 +1389,8 @@ void reconfig_windows(void) {
             mask = XCB_CW_BACK_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK;
             /* Black background */
             values[0] = colors.bar_bg;
-            /* If hide_on_modifier is set, i3 is not supposed to manage our bar-windows */
-            values[1] = config.hide_on_modifier;
+            /* If hide_on_modifier is set to hide or invisible mode, i3 is not supposed to manage our bar-windows */
+            values[1] = (config.hide_on_modifier == M_DOCK ? 0 : 1);
             /* We enable the following EventMask fields:
              * EXPOSURE, to get expose events (we have to re-draw then)
              * SUBSTRUCTURE_REDIRECT, to get ConfigureRequests when the tray
@@ -1451,7 +1511,7 @@ void reconfig_windows(void) {
 
             /* We finally map the bar (display it on screen), unless the modifier-switch is on */
             xcb_void_cookie_t map_cookie;
-            if (!config.hide_on_modifier) {
+            if (config.hide_on_modifier == M_DOCK) {
                 map_cookie = xcb_map_window_checked(xcb_connection, walk->bar);
             }
 
@@ -1462,7 +1522,7 @@ void reconfig_windows(void) {
                 xcb_request_failed(name_cookie,  "Could not set WM_NAME")   ||
                 xcb_request_failed(strut_cookie, "Could not set strut")     ||
                 xcb_request_failed(gc_cookie,    "Could not create graphical context") ||
-                (!config.hide_on_modifier && xcb_request_failed(map_cookie, "Could not map window"))) {
+                ((config.hide_on_modifier == M_DOCK) && xcb_request_failed(map_cookie, "Could not map window"))) {
                 exit(EXIT_FAILURE);
             }
 
@@ -1494,6 +1554,14 @@ void reconfig_windows(void) {
                                                                         mask,
                                                                         values);
 
+            mask = XCB_CW_OVERRIDE_REDIRECT;
+            values[0] = (config.hide_on_modifier == M_DOCK ? 0 : 1);
+            DLOG("Changing Window attribute override_redirect for output %s to %d\n", walk->name, values[0]);
+            xcb_void_cookie_t chg_cookie = xcb_change_window_attributes(xcb_connection,
+                                                                        walk->bar,
+                                                                        mask,
+                                                                        values);
+
             DLOG("Recreating buffer for output %s\n", walk->name);
             xcb_void_cookie_t pm_cookie = xcb_create_pixmap_checked(xcb_connection,
                                                                     root_screen->root_depth,
@@ -1502,10 +1570,27 @@ void reconfig_windows(void) {
                                                                     walk->rect.w,
                                                                     walk->rect.h);
 
-            if (xcb_request_failed(cfg_cookie, "Could not reconfigure window")) {
-                exit(EXIT_FAILURE);
+            /* Unmap the window, and draw it again when in dock mode */
+            xcb_void_cookie_t umap_cookie = xcb_unmap_window_checked(xcb_connection, walk->bar);
+            xcb_void_cookie_t map_cookie;
+            if (config.hide_on_modifier == M_DOCK) {
+                cont_child();
+                map_cookie = xcb_map_window_checked(xcb_connection, walk->bar);
+            }
+
+            if (config.hide_on_modifier == M_HIDE) {
+                /* Switching to hide mode, register for keyevents */
+                register_xkb_keyevents();
+            } else {
+                /* Switching to dock/invisible mode, deregister from keyevents */
+                deregister_xkb_keyevents();
             }
-            if (xcb_request_failed(pm_cookie,  "Could not create pixmap")) {
+
+            if (xcb_request_failed(cfg_cookie, "Could not reconfigure window") ||
+                xcb_request_failed(chg_cookie, "Could not change window") ||
+                xcb_request_failed(pm_cookie,  "Could not create pixmap") ||
+                xcb_request_failed(umap_cookie,  "Could not unmap window") ||
+                ((config.hide_on_modifier == M_DOCK) && xcb_request_failed(map_cookie, "Could not map window"))) {
                 exit(EXIT_FAILURE);
             }
         }
@@ -1573,7 +1658,7 @@ void draw_bars(bool unhide) {
                           outputs_walk->bargc,
                           MAX(0, (int16_t)(statusline_width - outputs_walk->rect.w + 4)), 0,
                           MAX(0, (int16_t)(outputs_walk->rect.w - statusline_width - traypx - 4)), 3,
-                          MIN(outputs_walk->rect.w - traypx - 4, statusline_width), font.height);
+                          MIN(outputs_walk->rect.w - traypx - 4, statusline_width), font.height + 2);
         }
 
         if (config.disable_ws) {
@@ -1672,16 +1757,21 @@ void draw_bars(bool unhide) {
 
             set_font_colors(outputs_walk->bargc, fg_color, bg_color);
             draw_text(binding.name, outputs_walk->buffer, outputs_walk->bargc, i + 5, 3, binding.width);
+
+            unhide = true;
         }
 
-        i = 0;
+        i = 1;
     }
 
+    /* Assure the bar is hidden/unhidden according to the specified hidden_state and mode */
+    bool should_unhide = (config.hidden_state == S_SHOW || (unhide && config.hidden_state == S_HIDE));
+    bool should_hide = (config.hide_on_modifier == M_INVISIBLE);
+
     if (!mod_pressed) {
-        if (unhide) {
-            /* The urgent-hint should get noticed, so we unhide the bars shortly */
+        if ((unhide || should_unhide) && !should_hide) {
             unhide_bars();
-        } else if (walks_away) {
+        } else if (walks_away || should_hide) {
             FREE(last_urgent_ws);
             hide_bars();
         }
@@ -1719,5 +1809,6 @@ void redraw_bars(void) {
 void set_current_mode(struct mode *current) {
     I3STRING_FREE(binding.name);
     binding = *current;
+    activated_mode = binding.name != NULL;
     return;
 }
index a517d83e09a04b71921993f3792c60325e0e0715..87f0ac1ac775f7bee549378b283c0d65d859072d 100644 (file)
@@ -265,4 +265,10 @@ void cmd_scratchpad_show(I3_CMD);
  */
 void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name);
 
+/**
+ * Implementation of 'bar (hidden_state hide|show|toggle)|(mode dock|hide|invisible|toggle) [<bar_id>]'
+ *
+ */
+void cmd_bar(I3_CMD, char *bar_type, char *bar_value, char *bar_id);
+
 #endif
index 62eb12d0d7584255c5a93aff2000fa07b4debffb..ec4ae3524d789f0546a7dc8dccc658dc7a0a0266 100644 (file)
 
 /**
  * Create a new container (and attach it to the given parent, if not NULL).
- * This function initializes the data structures and creates the appropriate
- * X11 IDs using x_con_init().
+ * This function only initializes the data structures.
+ *
+ */
+Con *con_new_skeleton(Con *parent, i3Window *window);
+
+
+/* A wrapper for con_new_skeleton, to retain the old con_new behaviour
  *
  */
 Con *con_new(Con *parent, i3Window *window);
@@ -270,7 +275,7 @@ void con_set_border_style(Con *con, int border_style, int border_width);
  * new split container before).
  *
  */
-void con_set_layout(Con *con, int layout);
+void con_set_layout(Con *con, layout_t layout);
 
 /**
  * This function toggles the layout of a given container. toggle_mode can be
index 7056af8587fe0ffba0cc2f3d69058157f8df2b07..c7479b3afeabec83ac8d3a32096f6a300c762e98 100644 (file)
@@ -95,7 +95,7 @@ struct Config {
     char *ipc_socket_path;
     const char *restart_state_path;
 
-    int default_layout;
+    layout_t default_layout;
     int container_stack_limit;
     int container_stack_limit_value;
     int default_border_width;
@@ -199,6 +199,9 @@ struct Config {
         /* just ignore the popup, that is, don’t map it */
         PDF_IGNORE = 2,
     } popup_during_fullscreen;
+
+    /* The number of currently parsed barconfigs */
+    int number_barconfigs;
 };
 
 /**
@@ -226,8 +229,11 @@ struct Barconfig {
      * root window! */
     char *socket_path;
 
-    /** Bar display mode (hide unless modifier is pressed or show in dock mode) */
-    enum { M_DOCK = 0, M_HIDE = 1 } mode;
+    /** Bar display mode (hide unless modifier is pressed or show in dock mode or always hide in invisible mode) */
+    enum { M_DOCK = 0, M_HIDE = 1, M_INVISIBLE = 2 } mode;
+
+    /* The current hidden_state of the bar, which indicates whether it is hidden or shown */
+    enum { S_HIDE = 0, S_SHOW = 1 } hidden_state;
 
     /** Bar modifier (to show bar when in hide mode). */
     enum {
@@ -323,6 +329,12 @@ void grab_all_keys(xcb_connection_t *conn, bool bind_mode_switch);
  */
 void switch_mode(const char *new_mode);
 
+/**
+ * Sends the current bar configuration as an event to all barconfig_update listeners.
+ * This update mechnism currently only includes the hidden_state and the mode in the config.
+ *
+ */void update_barconfig();
+
 /**
  * Returns a pointer to the Binding with the specified modifiers and keycode
  * or NULL if no such binding exists.
index 1faaa9734f135c7d06fff46c721f935f3f958549..f9b7a47fdf2493c5b2af6101750b35f772b05932 100644 (file)
@@ -62,6 +62,8 @@ CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *ke
 
 CFGFUN(bar_font, const char *font);
 CFGFUN(bar_mode, const char *mode);
+CFGFUN(bar_hidden_state, const char *hidden_state);
+CFGFUN(bar_id, const char *bar_id);
 CFGFUN(bar_output, const char *output);
 CFGFUN(bar_verbose, const char *verbose);
 CFGFUN(bar_modifier, const char *modifier);
index 1632efc76b8e7c6c21082fd09934930c05f544e7..31407f3c7bc4772f264c7e00e3b23c3916d5e368 100644 (file)
@@ -79,6 +79,19 @@ enum {
     BIND_MODE_SWITCH = (1 << 8)
 };
 
+/**
+ * Container layouts. See Con::layout.
+ */
+typedef enum {
+    L_DEFAULT = 0,
+    L_STACKED = 1,
+    L_TABBED = 2,
+    L_DOCKAREA = 3,
+    L_OUTPUT = 4,
+    L_SPLITV = 5,
+    L_SPLITH = 6
+} layout_t;
+
 /**
  * Stores a rectangle, for example the size of a window, the child window etc.
  * It needs to be packed so that the compiler will not add any padding bytes.
@@ -134,7 +147,7 @@ struct deco_render_params {
     Rect con_deco_rect;
     uint32_t background;
     bool con_is_leaf;
-    orientation_t parent_orientation;
+    layout_t parent_layout;
 };
 
 /**
@@ -531,15 +544,7 @@ struct Con {
      * parent and opening new containers). Instead, it stores the requested
      * layout in workspace_layout and creates a new split container with that
      * layout whenever a new container is attached to the workspace. */
-    enum {
-        L_DEFAULT = 0,
-        L_STACKED = 1,
-        L_TABBED = 2,
-        L_DOCKAREA = 3,
-        L_OUTPUT = 4,
-        L_SPLITV = 5,
-        L_SPLITH = 6
-    } layout, last_split_layout, workspace_layout;
+    layout_t layout, last_split_layout, workspace_layout;
     border_style_t border_style;
     /** floating? (= not in tiling layout) This cannot be simply a bool
      * because we want to keep track of whether the status was set by the
@@ -584,6 +589,9 @@ struct Con {
     /* The ID of this container before restarting. Necessary to correctly
      * interpret back-references in the JSON (such as the focus stack). */
     int old_id;
+
+    /* Depth of the container window */
+    uint16_t depth;
 };
 
 #endif
index 2a3321b50302727281a6838baf893a5c345669d0..6a50ccc8701fffe7fb68084de093929bed9f5553 100644 (file)
@@ -99,4 +99,7 @@ typedef struct i3_ipc_header {
 /* The window event will be triggered upon window changes */
 #define I3_IPC_EVENT_WINDOW                     (I3_IPC_EVENT_MASK | 3)
 
+/** Bar config update will be triggered to update the bar config */
+#define I3_IPC_EVENT_BARCONFIG_UPDATE           (I3_IPC_EVENT_MASK | 4)
+
 #endif
index 8222b99ac886520b8b1a5c7763a3e6ea0895d1f8..dadcfd64912bf55836d3486014582e4ef87da9e1 100644 (file)
@@ -87,6 +87,16 @@ Output *get_output_by_name(const char *name);
  */
 Output *get_output_containing(int x, int y);
 
+/*
+ * In contained_by_output, we check if any active output contains part of the container.
+ * We do this by checking if the output rect is intersected by the Rect.
+ * This is the 2-dimensional counterpart of get_output_containing.
+ * Since we don't actually need the outputs intersected by the given Rect (There could
+ * be many), we just return true or false for convenience.
+ *
+ */
+bool contained_by_output(Rect rect);
+
 /**
  * Gets the output which is the next one in the given direction.
  *
index c3d4ffc7cc29f5bac0194aa9ca401f469ee70e8e..b712c78692646ab961d52c420cbd21619f60f115 100644 (file)
@@ -93,8 +93,12 @@ void x_push_changes(Con *con);
  * Raises the specified container in the internal stack of X windows. The
  * next call to x_push_changes() will make the change visible in X11.
  *
+ * If above_all is true, the X11 window will be raised to the top
+ * of the stack. This should only be used for precisely one fullscreen
+ * window per output.
+ *
  */
-void x_raise_con(Con *con);
+void x_raise_con(Con *con, bool above_all);
 
 /**
  * Sets the WM_NAME property (so, no UTF8, but used only for debugging anyways)
index 2f6c2aab63967e8ad48e0718f37098c6c4f18d2b..911fc995c47bd1b7acaddecaa428fe1d7b895118 100644 (file)
@@ -9,7 +9,30 @@ i3-msg - send messages to i3 window manager
 
 == SYNOPSIS
 
-i3-msg [-t type] [message]
+i3-msg  [-q] [-v] [-h] [-s socket] [-t type] [message]
+
+== OPTIONS
+
+*-q, --quiet*::
+Only send ipc message and suppress the output of the response.
+
+*-v, --version*::
+Display version number and exit.
+
+*-h, --help*::
+Display a short help-message and exit.
+
+*-s, --socket* 'sock_path'::
+i3-msg will use the environment variable I3SOCK or the socket path
+given here. If both fail, it will try to get the socket information
+from the root window and then try /tmp/i3-ipc.sock before exiting
+with an error.
+
+*-t* 'type'::
+Send ipc message, see below.
+
+*message*::
+Send ipc message, see below.
 
 == IPC MESSAGE TYPES
 
index a4a01a88d0668385bb5c154d4c058b016771953a..12737a22838a010021a99cf67ffaae5b415ab352 100644 (file)
@@ -35,6 +35,7 @@ state INITIAL:
   'nop' -> NOP
   'scratchpad' -> SCRATCHPAD
   'mode' -> MODE
+  'bar' -> BAR
 
 state CRITERIA:
   ctype = 'class' -> CRITERION
@@ -319,3 +320,24 @@ state NOP:
 state SCRATCHPAD:
   'show'
       -> call cmd_scratchpad_show()
+
+# bar (hidden_state hide|show|toggle)|(mode dock|hide|invisible|toggle) [<bar_id>]
+state BAR:
+  bar_type = 'hidden_state'
+      -> BAR_HIDDEN_STATE
+  bar_type = 'mode'
+      -> BAR_MODE
+
+state BAR_HIDDEN_STATE:
+  bar_value = 'hide', 'show', 'toggle'
+      -> BAR_W_ID
+
+state BAR_MODE:
+  bar_value = 'dock', 'hide', 'invisible', 'toggle'
+      -> BAR_W_ID
+
+state BAR_W_ID:
+  bar_id = word
+      ->
+  end
+      -> call cmd_bar($bar_type, $bar_value, $bar_id)
index 6960510d4d386a731b92a3a997befeaf74f9c44c..fd13797b15d22710d12f8c5001b2463e6a6a0519 100644 (file)
@@ -49,7 +49,7 @@ state INITIAL:
 
 # We ignore comments and 'set' lines (variables).
 state IGNORE_LINE:
-  end, string
+  line
       -> INITIAL
 
 # floating_minimum_size <width> x <height>
@@ -311,7 +311,7 @@ state MODE:
 
 # We ignore comments and 'set' lines (variables).
 state MODE_IGNORE_LINE:
-  end, string
+  line
       -> MODE
 
 state MODE_BINDING:
@@ -349,6 +349,8 @@ state BAR:
   'status_command'    -> BAR_STATUS_COMMAND
   'socket_path'       -> BAR_SOCKET_PATH
   'mode'              -> BAR_MODE
+  'hidden_state'      -> BAR_HIDDEN_STATE
+  'id'                -> BAR_ID
   'modifier'          -> BAR_MODIFIER
   'position'          -> BAR_POSITION
   'output'            -> BAR_OUTPUT
@@ -362,7 +364,7 @@ state BAR:
 
 # We ignore comments and 'set' lines (variables).
 state BAR_IGNORE_LINE:
-  end, string
+  line
       -> BAR
 
 state BAR_BAR_COMMAND:
@@ -378,9 +380,17 @@ state BAR_SOCKET_PATH:
       -> call cfg_bar_socket_path($path); BAR
 
 state BAR_MODE:
-  mode = 'dock', 'hide'
+  mode = 'dock', 'hide', 'invisible'
       -> call cfg_bar_mode($mode); BAR
 
+state BAR_HIDDEN_STATE:
+  hidden_state = 'hide', 'show'
+      -> call cfg_bar_hidden_state($hidden_state); BAR
+
+state BAR_ID:
+  bar_id = word
+      -> call cfg_bar_id($bar_id); BAR
+
 state BAR_MODIFIER:
   modifier = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Control', 'Ctrl', 'Shift'
       -> call cfg_bar_modifier($modifier); BAR
@@ -428,7 +438,7 @@ state BAR_COLORS:
 
 # We ignore comments and 'set' lines (variables).
 state BAR_COLORS_IGNORE_LINE:
-  end, string
+  line
       -> BAR_COLORS
 
 state BAR_COLORS_SINGLE:
index 538e2dbc617fe87f8980fdbde66bb0bff1a4d3e7..f361f8670eae220889813dbadd234eedeba6162b 100644 (file)
@@ -1547,7 +1547,7 @@ void cmd_layout(I3_CMD, char *layout_str) {
     if (strcmp(layout_str, "stacking") == 0)
         layout_str = "stacked";
     owindow *current;
-    int layout;
+    layout_t layout;
     /* default is a special case which will be handled in con_set_layout(). */
     if (strcmp(layout_str, "default") == 0)
         layout = L_DEFAULT;
@@ -1632,6 +1632,8 @@ void cmd_reload(I3_CMD) {
     x_set_i3_atoms();
     /* Send an IPC event just in case the ws names have changed */
     ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"reload\"}");
+    /* Send an update event for the barconfig just in case it has changed */
+    update_barconfig();
 
     // XXX: default reply for now, make this a better reply
     ysuccess(true);
@@ -1915,3 +1917,113 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) {
 
     ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"rename\"}");
 }
+
+/*
+ * Implementation of 'bar mode dock|hide|invisible|toggle [<bar_id>]'
+ *
+ */
+bool cmd_bar_mode(char *bar_mode, char *bar_id) {
+    int mode;
+    bool toggle = false;
+    if (strcmp(bar_mode, "dock") == 0)
+        mode = M_DOCK;
+    else if (strcmp(bar_mode, "hide") == 0)
+        mode = M_HIDE;
+    else if (strcmp(bar_mode, "invisible") == 0)
+        mode = M_INVISIBLE;
+    else if (strcmp(bar_mode, "toggle") == 0)
+        toggle = true;
+    else {
+        ELOG("Unknown bar mode \"%s\", this is a mismatch between code and parser spec.\n", bar_mode);
+        return false;
+    }
+
+    bool changed_sth = false;
+    Barconfig *current = NULL;
+    TAILQ_FOREACH(current, &barconfigs, configs) {
+        if (bar_id && strcmp(current->id, bar_id) != 0)
+            continue;
+
+        if (toggle)
+            mode = (current->mode + 1) % 2;
+
+        DLOG("Changing bar mode of bar_id '%s' to '%s (%d)'\n", current->id, bar_mode, mode);
+        current->mode = mode;
+        changed_sth = true;
+
+        if (bar_id)
+             break;
+    }
+
+    if (bar_id && !changed_sth) {
+        DLOG("Changing bar mode of bar_id %s failed, bar_id not found.\n", bar_id);
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Implementation of 'bar hidden_state hide|show|toggle [<bar_id>]'
+ *
+ */
+bool cmd_bar_hidden_state(char *bar_hidden_state, char *bar_id) {
+    int hidden_state;
+    bool toggle = false;
+    if (strcmp(bar_hidden_state, "hide") == 0)
+        hidden_state = S_HIDE;
+    else if (strcmp(bar_hidden_state, "show") == 0)
+        hidden_state = S_SHOW;
+    else if (strcmp(bar_hidden_state, "toggle") == 0)
+        toggle = true;
+    else {
+        ELOG("Unknown bar state \"%s\", this is a mismatch between code and parser spec.\n", bar_hidden_state);
+        return false;
+    }
+
+    bool changed_sth = false;
+    Barconfig *current = NULL;
+    TAILQ_FOREACH(current, &barconfigs, configs) {
+        if (bar_id && strcmp(current->id, bar_id) != 0)
+            continue;
+
+        if (toggle)
+            hidden_state = (current->hidden_state + 1) % 2;
+
+        DLOG("Changing bar hidden_state of bar_id '%s' to '%s (%d)'\n", current->id, bar_hidden_state, hidden_state);
+        current->hidden_state = hidden_state;
+        changed_sth = true;
+
+        if (bar_id)
+             break;
+    }
+
+    if (bar_id && !changed_sth) {
+        DLOG("Changing bar hidden_state of bar_id %s failed, bar_id not found.\n", bar_id);
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Implementation of 'bar (hidden_state hide|show|toggle)|(mode dock|hide|invisible|toggle) [<bar_id>]'
+ *
+ */
+void cmd_bar(I3_CMD, char *bar_type, char *bar_value, char *bar_id) {
+    bool ret;
+    if (strcmp(bar_type, "mode") == 0)
+        ret = cmd_bar_mode(bar_value, bar_id);
+    else if (strcmp(bar_type, "hidden_state") == 0)
+        ret = cmd_bar_hidden_state(bar_value, bar_id);
+    else {
+        ELOG("Unknown bar option type \"%s\", this is a mismatch between code and parser spec.\n", bar_type);
+        ret = false;
+    }
+
+    ysuccess(ret);
+    if (!ret)
+        return;
+
+    update_barconfig();
+}
index 93ee38896eb96fb9f9cd63cb913875562583e904..4f04501c21408d02930e43c4285d9c20f03cc78e 100644 (file)
@@ -13,7 +13,7 @@
  * We use a hand-written parser instead of lex/yacc because our commands are
  * easy for humans, not for computers. Thus, it’s quite hard to specify a
  * context-free grammar for the commands. A PEG grammar would be easier, but
- * there’s downsides to every PEG parser generator I have come accross so far.
+ * there’s downsides to every PEG parser generator I have come across so far.
  *
  * This parser is basically a state machine which looks for literals or strings
  * and can push either on a stack. After identifying a literal or string, it
index 1050513ae593a76e8981e8d93055be0a07f550bf..fe26d6945ec2553f10b5217acb982836198d136f 100644 (file)
--- a/src/con.c
+++ b/src/con.c
@@ -44,11 +44,10 @@ static void con_force_split_parents_redraw(Con *con) {
 
 /*
  * Create a new container (and attach it to the given parent, if not NULL).
- * This function initializes the data structures and creates the appropriate
- * X11 IDs using x_con_init().
+ * This function only initializes the data structures.
  *
  */
-Con *con_new(Con *parent, i3Window *window) {
+Con *con_new_skeleton(Con *parent, i3Window *window) {
     Con *new = scalloc(sizeof(Con));
     new->on_remove_child = con_on_remove_child;
     TAILQ_INSERT_TAIL(&all_cons, new, all_cons);
@@ -56,6 +55,10 @@ Con *con_new(Con *parent, i3Window *window) {
     new->window = window;
     new->border_style = config.default_border;
     new->current_border_width = -1;
+    if (window)
+        new->depth = window->depth;
+    else
+        new->depth = XCB_COPY_FROM_PARENT;
     static int cnt = 0;
     DLOG("opening window %d\n", cnt);
 
@@ -66,10 +69,6 @@ Con *con_new(Con *parent, i3Window *window) {
     cnt++;
     if ((cnt % (sizeof(colors) / sizeof(char*))) == 0)
         cnt = 0;
-    if (window)
-        x_con_init(new, window->depth);
-    else
-        x_con_init(new, XCB_COPY_FROM_PARENT);
 
     TAILQ_INIT(&(new->floating_head));
     TAILQ_INIT(&(new->nodes_head));
@@ -82,6 +81,15 @@ Con *con_new(Con *parent, i3Window *window) {
     return new;
 }
 
+/* A wrapper for con_new_skeleton, to retain the old con_new behaviour
+ *
+ */
+Con *con_new(Con *parent, i3Window *window) {
+    Con *new = con_new_skeleton(parent, window);
+    x_con_init(new, new->depth);
+    return new;
+}
+
 /*
  * Attaches the given container to the given parent. This happens when moving
  * a container or when inserting a new container at a specific place in the
@@ -1201,7 +1209,7 @@ void con_set_border_style(Con *con, int border_style, int border_width) {
  * new split container before).
  *
  */
-void con_set_layout(Con *con, int layout) {
+void con_set_layout(Con *con, layout_t layout) {
     DLOG("con_set_layout(%p, %d), con->type = %d\n",
          con, layout, con->type);
 
index 595aa435da66805e4baa9ea1f7f2b51ff0eace9c..337b83a84886bbbef4df14a16b90a60be9da3915 100644 (file)
@@ -210,6 +210,49 @@ void switch_mode(const char *new_mode) {
     ELOG("ERROR: Mode not found\n");
 }
 
+/*
+ * Sends the current bar configuration as an event to all barconfig_update listeners.
+ * This update mechnism currently only includes the hidden_state and the mode in the config.
+ *
+ */
+void update_barconfig() {
+    Barconfig *current;
+    TAILQ_FOREACH(current, &barconfigs, configs) {
+        /* Build json message */
+        char *hidden_state;
+        switch (current->hidden_state) {
+            case S_SHOW:
+                hidden_state ="show";
+                break;
+            case S_HIDE:
+            default:
+                hidden_state = "hide";
+                break;
+        }
+
+        char *mode;
+        switch (current->mode) {
+            case M_HIDE:
+                mode ="hide";
+                break;
+            case M_INVISIBLE:
+                mode ="invisible";
+                break;
+            case M_DOCK:
+            default:
+                mode = "dock";
+                break;
+        }
+
+        /* Send an event to all barconfig listeners*/
+        char *event_msg;
+        sasprintf(&event_msg, "{ \"id\":\"%s\", \"hidden_state\":\"%s\", \"mode\":\"%s\" }", current->id, hidden_state, mode);
+
+        ipc_send_event("barconfig_update", I3_IPC_EVENT_BARCONFIG_UPDATE, event_msg);
+        FREE(event_msg);
+    }
+}
+
 /*
  * Get the path of the first configuration file found. If override_configpath
  * is specified, that path is returned and saved for further calls. Otherwise,
index a7fa3500526e356321edde1db5429658e1756a6d..0fac7006b807c4ae1b549e0fa0b2a4fe381cb276 100644 (file)
@@ -452,7 +452,15 @@ CFGFUN(bar_font, const char *font) {
 }
 
 CFGFUN(bar_mode, const char *mode) {
-    current_bar.mode = (strcmp(mode, "hide") == 0 ? M_HIDE : M_DOCK);
+    current_bar.mode = (strcmp(mode, "dock") == 0 ? M_DOCK : (strcmp(mode, "hide") == 0 ? M_HIDE : M_INVISIBLE));
+}
+
+CFGFUN(bar_hidden_state, const char *hidden_state) {
+    current_bar.hidden_state = (strcmp(hidden_state, "hide") == 0 ? S_HIDE : S_SHOW);
+}
+
+CFGFUN(bar_id, const char *bar_id) {
+    current_bar.id = sstrdup(bar_id);
 }
 
 CFGFUN(bar_output, const char *output) {
@@ -548,15 +556,11 @@ CFGFUN(bar_workspace_buttons, const char *value) {
 
 CFGFUN(bar_finish) {
     DLOG("\t new bar configuration finished, saving.\n");
-    /* Generate a unique ID for this bar */
-    current_bar.id = sstrdup("bar-XXXXXX");
-    /* This works similar to mktemp in that it replaces the last six X with
-     * random letters, but without the restriction that the given buffer
-     * has to contain a valid path name. */
-    char *x = current_bar.id + strlen("bar-");
-    while (*x != '\0') {
-        *(x++) = (rand() % 26) + 'a';
-    }
+    /* Generate a unique ID for this bar if not already configured */
+    if (!current_bar.id)
+        sasprintf(&current_bar.id, "bar-%d", config.number_barconfigs);
+
+    config.number_barconfigs++;
 
     /* If no font was explicitly set, we use the i3 font as default */
     if (!current_bar.font && font_pattern)
index 1cdc7acba8cd745fd1ba4803dbb41233996d5061..f197c39d8a62274b900104e72765833185e04d43 100644 (file)
@@ -446,6 +446,16 @@ struct ConfigResult *parse_config(const char *input, struct context *context) {
                 }
             }
 
+            if (strcmp(token->name, "line") == 0) {
+               while (*walk != '\0' && *walk != '\n' && *walk != '\r')
+                  walk++;
+               next_state(token);
+               token_handled = true;
+               linecnt++;
+               walk++;
+               break;
+            }
+
             if (strcmp(token->name, "end") == 0) {
                 //printf("checking for end: *%s*\n", walk);
                 if (*walk == '\0' || *walk == '\n' || *walk == '\r') {
index 49a4122ec2253ea67229e8b11c5efa92060395a8..b7bf3ff65c9537a25765ecb5047aa615595fa2a9 100644 (file)
@@ -659,11 +659,7 @@ void drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_window_t
 void floating_reposition(Con *con, Rect newrect) {
     /* Sanity check: Are the new coordinates on any output? If not, we
      * ignore that request. */
-    Output *output = get_output_containing(
-        newrect.x + (newrect.width / 2),
-        newrect.y + (newrect.height / 2));
-
-    if (!output) {
+    if (!contained_by_output(newrect)) {
         ELOG("No output found at destination coordinates. Not repositioning.\n");
         return;
     }
index f4782ca97dffece262160c27d956d43ba9ea0add..fdc75abe604098816d16b8e36a77815d908e50f1 100644 (file)
@@ -158,7 +158,7 @@ static void handle_enter_notify(xcb_enter_notify_event_t *event) {
     }
 
     /* see if the user entered the window on a certain window decoration */
-    int layout = (enter_child ? con->parent->layout : con->layout);
+    layout_t layout = (enter_child ? con->parent->layout : con->layout);
     if (layout == L_DEFAULT) {
         Con *child;
         TAILQ_FOREACH(child, &(con->nodes_head), nodes)
index cf2535733e46123fe8aa7a0540d6c18b052a4441..4c41465b7636aa0766429747ecf1cc41462c58af 100644 (file)
--- a/src/ipc.c
+++ b/src/ipc.c
@@ -354,6 +354,11 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
     }
     y(array_close);
 
+    if (inplace_restart && con->window != NULL) {
+        ystr("depth");
+        y(integer, con->depth);
+    }
+
     y(map_close);
 }
 
@@ -616,9 +621,29 @@ IPC_HANDLER(get_bar_config) {
         YSTR_IF_SET(socket_path);
 
         ystr("mode");
-        if (config->mode == M_HIDE)
-            ystr("hide");
-        else ystr("dock");
+        switch (config->mode) {
+            case M_HIDE:
+                ystr("hide");
+                break;
+            case M_INVISIBLE:
+                ystr("invisible");
+                break;
+            case M_DOCK:
+            default:
+                ystr("dock");
+                break;
+        }
+
+        ystr("hidden_state");
+        switch (config->hidden_state) {
+            case S_SHOW:
+                ystr("show");
+                break;
+            case S_HIDE:
+            default:
+                ystr("hide");
+                break;
+        }
 
         ystr("modifier");
         switch (config->modifier) {
index ca4c87ef827b1537735a1c46c407e488ef7362a9..1b08f8c17eae72821d8b69499a92f8039b8d7e88 100644 (file)
@@ -51,12 +51,12 @@ static int json_start_map(void *ctx) {
             if (last_key && strcasecmp(last_key, "floating_nodes") == 0) {
                 DLOG("New floating_node\n");
                 Con *ws = con_get_workspace(json_node);
-                json_node = con_new(NULL, NULL);
+                json_node = con_new_skeleton(NULL, NULL);
                 json_node->parent = ws;
                 DLOG("Parent is workspace = %p\n", ws);
             } else {
                 Con *parent = json_node;
-                json_node = con_new(NULL, NULL);
+                json_node = con_new_skeleton(NULL, NULL);
                 json_node->parent = parent;
             }
         }
@@ -69,6 +69,8 @@ static int json_end_map(void *ctx) {
     if (!parsing_swallows && !parsing_rect && !parsing_window_rect && !parsing_geometry) {
         LOG("attaching\n");
         con_attach(json_node, json_node->parent, true);
+        LOG("Creating window\n");
+        x_con_init(json_node, json_node->depth);
         json_node = json_node->parent;
     }
     if (parsing_rect)
@@ -277,6 +279,9 @@ static int json_int(void *ctx, long val) {
     if (strcasecmp(last_key, "current_border_width") == 0)
         json_node->current_border_width = val;
 
+    if (strcasecmp(last_key, "depth") == 0)
+        json_node->depth = val;
+
     if (!parsing_swallows && strcasecmp(last_key, "id") == 0)
         json_node->old_id = val;
 
index 42e714ef0bcee0f65445a557c15f7019d5453707..0269fb21ff9b7078a6f94ecd787c7e4a2dd4a1f6 100644 (file)
--- a/src/log.c
+++ b/src/log.c
@@ -81,11 +81,11 @@ static void store_log_markers(void) {
 void init_logging(void) {
     if (!errorfilename) {
         if (!(errorfilename = get_process_filename("errorlog")))
-            ELOG("Could not initialize errorlog\n");
+            fprintf(stderr, "Could not initialize errorlog\n");
         else {
             errorfile = fopen(errorfilename, "w");
             if (fcntl(fileno(errorfile), F_SETFD, FD_CLOEXEC)) {
-                ELOG("Could not set close-on-exec flag\n");
+                fprintf(stderr, "Could not set close-on-exec flag\n");
             }
         }
     }
@@ -107,25 +107,29 @@ void init_logging(void) {
                                         sysconf(_SC_PAGESIZE);
 #endif
         logbuffer_size = min(physical_mem_bytes * 0.01, shmlog_size);
+#if defined(__FreeBSD__)
+        sasprintf(&shmlogname, "/tmp/i3-log-%d", getpid());
+#else
         sasprintf(&shmlogname, "/i3-log-%d", getpid());
+#endif
         logbuffer_shm = shm_open(shmlogname, O_RDWR | O_CREAT, S_IREAD | S_IWRITE);
         if (logbuffer_shm == -1) {
-            ELOG("Could not shm_open SHM segment for the i3 log: %s\n", strerror(errno));
+            fprintf(stderr, "Could not shm_open SHM segment for the i3 log: %s\n", strerror(errno));
             return;
         }
 
         if (ftruncate(logbuffer_shm, logbuffer_size) == -1) {
             close(logbuffer_shm);
-            shm_unlink("/i3-log-");
-            ELOG("Could not ftruncate SHM segment for the i3 log: %s\n", strerror(errno));
+            shm_unlink(shmlogname);
+            fprintf(stderr, "Could not ftruncate SHM segment for the i3 log: %s\n", strerror(errno));
             return;
         }
 
         logbuffer = mmap(NULL, logbuffer_size, PROT_READ | PROT_WRITE, MAP_SHARED, logbuffer_shm, 0);
         if (logbuffer == MAP_FAILED) {
             close(logbuffer_shm);
-            shm_unlink("/i3-log-");
-            ELOG("Could not mmap SHM segment for the i3 log: %s\n", strerror(errno));
+            shm_unlink(shmlogname);
+            fprintf(stderr, "Could not mmap SHM segment for the i3 log: %s\n", strerror(errno));
             logbuffer = NULL;
             return;
         }
@@ -138,7 +142,7 @@ void init_logging(void) {
         pthread_condattr_t cond_attr;
         pthread_condattr_init(&cond_attr);
         if (pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED) != 0)
-            ELOG("pthread_condattr_setpshared() failed, i3-dump-log -f will not work!\n");
+            fprintf(stderr, "pthread_condattr_setpshared() failed, i3-dump-log -f will not work!\n");
         pthread_cond_init(&(header->condvar), &cond_attr);
 
         logwalk = logbuffer + sizeof(i3_shmlog_header);
index ae3cef0fc2babb68b52533d750c7aef563fb72b3..f5bd76ea39bc61595d8d4321b12a5f2c89144489 100644 (file)
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009-2013 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * manage.c: Initially managing new windows (or existing ones on restart).
  *
@@ -122,34 +122,30 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
 
 
     geomc = xcb_get_geometry(conn, d);
-#define FREE_GEOMETRY() do { \
-    if ((geom = xcb_get_geometry_reply(conn, geomc, 0)) != NULL) \
-        free(geom); \
-} while (0)
 
     /* Check if the window is mapped (it could be not mapped when intializing and
        calling manage_window() for every window) */
     if ((attr = xcb_get_window_attributes_reply(conn, cookie, 0)) == NULL) {
         DLOG("Could not get attributes\n");
-        FREE_GEOMETRY();
+        xcb_discard_reply(conn, geomc.sequence);
         return;
     }
 
     if (needs_to_be_mapped && attr->map_state != XCB_MAP_STATE_VIEWABLE) {
-        FREE_GEOMETRY();
+        xcb_discard_reply(conn, geomc.sequence);
         goto out;
     }
 
     /* Don’t manage clients with the override_redirect flag */
     if (attr->override_redirect) {
-        FREE_GEOMETRY();
+        xcb_discard_reply(conn, geomc.sequence);
         goto out;
     }
 
     /* Check if the window is already managed */
     if (con_by_window_id(window) != NULL) {
         DLOG("already managed (by con %p)\n", con_by_window_id(window));
-        FREE_GEOMETRY();
+        xcb_discard_reply(conn, geomc.sequence);
         goto out;
     }
 
index 10b085cbb9825006a58ad44bbefd3e7bc1c910c6..1aef9c9c620c59af800c4a1e972f6dd6cbb0aecb 100644 (file)
@@ -92,6 +92,31 @@ Output *get_output_containing(int x, int y) {
     return NULL;
 }
 
+/*
+ * In contained_by_output, we check if any active output contains part of the container.
+ * We do this by checking if the output rect is intersected by the Rect.
+ * This is the 2-dimensional counterpart of get_output_containing.
+ * Since we don't actually need the outputs intersected by the given Rect (There could
+ * be many), we just return true or false for convenience.
+ *
+ */
+bool contained_by_output(Rect rect){
+    Output *output;
+    int lx = rect.x, uy = rect.y;
+    int rx = rect.x + rect.width, by = rect.y + rect.height;
+    TAILQ_FOREACH(output, &outputs, outputs) {
+        if (!output->active)
+            continue;
+        DLOG("comparing x=%d y=%d with x=%d and y=%d width %d height %d\n",
+                        rect.x, rect.y, output->rect.x, output->rect.y, output->rect.width, output->rect.height);
+        if (rx >= (int)output->rect.x && lx <= (int)(output->rect.x + output->rect.width) &&
+            by >= (int)output->rect.y && uy <= (int)(output->rect.y + output->rect.height))
+            return true;
+    }
+    return false;
+
+}
+
 /*
  * Like get_output_next with close_far == CLOSEST_OUTPUT, but wraps.
  *
index c0a0fedfe4848b70c6b3c229fbe760115cd2b67b..0142ae9718f3fbd55771a222d9b08bab5d01ce42 100644 (file)
@@ -70,7 +70,7 @@ static void render_l_output(Con *con) {
     Con *fullscreen = con_get_fullscreen_con(ws, CF_OUTPUT);
     if (fullscreen) {
         fullscreen->rect = con->rect;
-        x_raise_con(fullscreen);
+        x_raise_con(fullscreen, true);
         render_con(fullscreen, true);
         return;
     }
@@ -110,7 +110,7 @@ static void render_l_output(Con *con) {
 
         DLOG("child at (%d, %d) with (%d x %d)\n",
                 child->rect.x, child->rect.y, child->rect.width, child->rect.height);
-        x_raise_con(child);
+        x_raise_con(child, false);
         render_con(child, false);
     }
 }
@@ -207,7 +207,7 @@ void render_con(Con *con, bool render_fullscreen) {
     }
     if (fullscreen) {
         fullscreen->rect = rect;
-        x_raise_con(fullscreen);
+        x_raise_con(fullscreen, false);
         render_con(fullscreen, true);
         return;
     }
@@ -298,7 +298,7 @@ void render_con(Con *con, bool render_fullscreen) {
                 }
                 DLOG("floating child at (%d,%d) with %d x %d\n",
                      child->rect.x, child->rect.y, child->rect.width, child->rect.height);
-                x_raise_con(child);
+                x_raise_con(child, false);
                 render_con(child, false);
             }
         }
@@ -407,7 +407,7 @@ void render_con(Con *con, bool render_fullscreen) {
 
         DLOG("child at (%d, %d) with (%d x %d)\n",
                 child->rect.x, child->rect.y, child->rect.width, child->rect.height);
-        x_raise_con(child);
+        x_raise_con(child, false);
         render_con(child, false);
         i++;
     }
@@ -415,7 +415,7 @@ void render_con(Con *con, bool render_fullscreen) {
     /* in a stacking or tabbed container, we ensure the focused client is raised */
     if (con->layout == L_STACKED || con->layout == L_TABBED) {
         TAILQ_FOREACH_REVERSE(child, &(con->focus_head), focus_head, focused)
-            x_raise_con(child);
+            x_raise_con(child, false);
         if ((child = TAILQ_FIRST(&(con->focus_head)))) {
             /* By rendering the stacked container again, we handle the case
              * that we have a non-leaf-container inside the stack. In that
@@ -429,7 +429,7 @@ void render_con(Con *con, bool render_fullscreen) {
              * top of every stack window. That way, when a new window is opened in
              * the stack, the old window will not obscure part of the decoration
              * (it’s unmapped afterwards). */
-            x_raise_con(con);
+            x_raise_con(con, false);
     }
     }
 }
index a4d2950519619bd563f804c36854c666f6cfcbad..d6c1a09e1d630e92eec364c125c9e4e2389a485e 100644 (file)
@@ -39,6 +39,12 @@ void scratchpad_move(Con *con) {
         return;
     }
 
+    /* If the current con is in fullscreen mode, we need to disable that,
+     *  as a scratchpad window should never be in fullscreen mode */
+    if (focused && focused->type != CT_WORKSPACE && focused->fullscreen_mode != CF_NONE) {
+        con_toggle_fullscreen(focused, CF_OUTPUT);
+    }
+
     /* 1: Ensure the window or any parent is floating. From now on, we deal
      * with the CT_FLOATING_CON. We use automatic == false because the user
      * made the choice that this window should be a scratchpad (and floating).
@@ -78,14 +84,25 @@ void scratchpad_show(Con *con) {
     Con *__i3_scratch = workspace_get("__i3_scratch", NULL);
     Con *floating;
 
+    /* If this was 'scratchpad show' without criteria, we check if the
+     * currently focused window is a scratchpad window and should be hidden
+     * again. */
+    if (!con &&
+        (floating = con_inside_floating(focused)) &&
+        floating->scratchpad_state != SCRATCHPAD_NONE) {
+        DLOG("Focused window is a scratchpad window, hiding it.\n");
+        scratchpad_move(focused);
+        return;
+    }
+
     /* If the current con or any of its parents are in fullscreen mode, we
      * first need to disable it before showing the scratchpad con. */
     Con *fs = focused;
     while (fs && fs->fullscreen_mode == CF_NONE)
         fs = fs->parent;
 
-    if (fs->type != CT_WORKSPACE) {
-        con_toggle_fullscreen(focused, CF_OUTPUT);
+    if (fs && fs->type != CT_WORKSPACE) {
+        con_toggle_fullscreen(fs, CF_OUTPUT);
     }
 
     /* If this was 'scratchpad show' without criteria, we check if there is a
@@ -93,7 +110,7 @@ void scratchpad_show(Con *con) {
     Con *walk_con;
     Con *focused_ws = con_get_workspace(focused);
     TAILQ_FOREACH(walk_con, &(focused_ws->floating_head), floating_windows) {
-        if ((floating = con_inside_floating(walk_con)) &&
+        if (!con && (floating = con_inside_floating(walk_con)) &&
             floating->scratchpad_state != SCRATCHPAD_NONE &&
             floating != con_inside_floating(focused)) {
                 DLOG("Found an unfocused scratchpad window on this workspace\n");
@@ -112,7 +129,7 @@ void scratchpad_show(Con *con) {
     focused_ws = con_get_workspace(focused);
     TAILQ_FOREACH(walk_con, &all_cons, all_cons) {
         Con *walk_ws = con_get_workspace(walk_con);
-        if (walk_ws &&
+        if (!con && walk_ws &&
             !con_is_internal(walk_ws) && focused_ws != walk_ws &&
             (floating = con_inside_floating(walk_con)) &&
             floating->scratchpad_state != SCRATCHPAD_NONE) {
@@ -123,14 +140,10 @@ void scratchpad_show(Con *con) {
         }
     }
 
-    /* If this was 'scratchpad show' without criteria, we check if the
-     * currently focused window is a scratchpad window and should be hidden
-     * again. */
-    if (!con &&
-        (floating = con_inside_floating(focused)) &&
-        floating->scratchpad_state != SCRATCHPAD_NONE) {
-        DLOG("Focused window is a scratchpad window, hiding it.\n");
-        scratchpad_move(focused);
+    /* If this was 'scratchpad show' with criteria, we check if the window
+     * is actually in the scratchpad */
+    if (con && con->parent->scratchpad_state == SCRATCHPAD_NONE) {
+        DLOG("Window is not in the scratchpad, doing nothing.\n");
         return;
     }
 
index 5a0913bfcc77852c1084fe2f2908097000714b8c..17f5ac33d27b3d4fe2ce2d7bf8404e1f920273c9 100644 (file)
@@ -442,7 +442,7 @@ static void _workspace_show(Con *workspace) {
     } else
         con_focus(next);
 
-    ipc_send_workspace_focus_event(workspace, old);
+    ipc_send_workspace_focus_event(workspace, current);
 
     DLOG("old = %p / %s\n", old, (old ? old->name : "(null)"));
     /* Close old workspace if necessary. This must be done *after* doing
diff --git a/src/x.c b/src/x.c
index fcb63c35bdef19765540800ecb610011ced1941a..f86559460ed213722fa70bbc1714d0e04636e13a 100644 (file)
--- a/src/x.c
+++ b/src/x.c
@@ -36,6 +36,7 @@ typedef struct con_state {
     bool mapped;
     bool unmap_now;
     bool child_mapped;
+    bool above_all;
 
     /** The con for which this state is. */
     Con *con;
@@ -350,7 +351,7 @@ void x_draw_decoration(Con *con) {
     p->con_deco_rect = con->deco_rect;
     p->background = config.client.background;
     p->con_is_leaf = con_is_leaf(con);
-    p->parent_orientation = con_orientation(parent);
+    p->parent_layout = con->parent->layout;
 
     if (con->deco_render_params != NULL &&
         (con->window == NULL || !con->window->name_x_changed) &&
@@ -445,10 +446,10 @@ void x_draw_decoration(Con *con) {
             TAILQ_PREV(con, nodes_head, nodes) == NULL &&
             con->parent->type != CT_FLOATING_CON) {
             xcb_change_gc(conn, con->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){ p->color->indicator });
-            if (p->parent_orientation == HORIZ)
+            if (p->parent_layout == L_SPLITH)
                 xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, (xcb_rectangle_t[]){
                         { r->width + br.width + br.x, br.y, r->width, r->height + br.height } });
-            else
+            else if (p->parent_layout == L_SPLITV)
                 xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, (xcb_rectangle_t[]){
                         { br.x, r->height + br.height + br.y, r->width - (2 * br.x), r->height } });
         }
@@ -899,6 +900,10 @@ void x_push_changes(Con *con) {
 
             xcb_configure_window(conn, prev->id, mask, values);
         }
+        if (state->above_all) {
+            DLOG("above all: 0x%08x\n", state->id);
+            xcb_configure_window(conn, state->id, XCB_CONFIG_WINDOW_STACK_MODE, (uint32_t[]){ XCB_STACK_MODE_ABOVE });
+        }
         state->initial = false;
     }
 
@@ -1024,12 +1029,18 @@ void x_push_changes(Con *con) {
  * Raises the specified container in the internal stack of X windows. The
  * next call to x_push_changes() will make the change visible in X11.
  *
+ * If above_all is true, the X11 window will be raised to the top
+ * of the stack. This should only be used for precisely one fullscreen
+ * window per output.
+ *
  */
-void x_raise_con(Con *con) {
+void x_raise_con(Con *con, bool above_all) {
     con_state *state;
     state = state_for_frame(con->frame);
     //DLOG("raising in new stack: %p / %s / %s / xid %08x\n", con, con->name, con->window ? con->window->name_json : "", state->id);
 
+    state->above_all = above_all;
+
     CIRCLEQ_REMOVE(&state_head, state, state);
     CIRCLEQ_INSERT_HEAD(&state_head, state, state);
 }
index 52070d56b1090b55b31e4d87b11dcf7e21767bab..65c72125861388a4ad80b0a39ad274320278ebe9 100644 (file)
@@ -144,7 +144,7 @@ is(parser_calls("\nworkspace test"),
 ################################################################################
 
 is(parser_calls('unknown_literal'),
-   "ERROR: Expected one of these tokens: <end>, '[', 'move', 'exec', 'exit', 'restart', 'reload', 'border', 'layout', 'append_layout', 'workspace', 'focus', 'kill', 'open', 'fullscreen', 'split', 'floating', 'mark', 'resize', 'rename', 'nop', 'scratchpad', 'mode'\n" .
+   "ERROR: Expected one of these tokens: <end>, '[', 'move', 'exec', 'exit', 'restart', 'reload', 'border', 'layout', 'append_layout', 'workspace', 'focus', 'kill', 'open', 'fullscreen', 'split', 'floating', 'mark', 'resize', 'rename', 'nop', 'scratchpad', 'mode', 'bar'\n" .
    "ERROR: Your command: unknown_literal\n" .
    "ERROR:               ^^^^^^^^^^^^^^^",
    'error for unknown literal ok');
index 6a49ff7844eddebfda36bec143ac450e8a83bec4..06588b111a617b10bdab7382eeadeca1bef692ac 100644 (file)
@@ -448,6 +448,21 @@ is(parser_calls($config),
    $expected,
    'errors dont harm subsequent statements');
 
+################################################################################
+# Regression: semicolons end comments, but shouldn’t
+################################################################################
+
+$config = <<'EOT';
+# "foo" client.focused          #4c7899 #285577 #ffffff #2e9ef4
+EOT
+
+$expected = <<'EOT';
+
+EOT
+
+is(parser_calls($config),
+   $expected,
+   'semicolon does not end a comment line');
 
 ################################################################################
 # Error message with 2+2 lines of context
@@ -612,7 +627,7 @@ EOT
 
 $expected = <<'EOT';
 cfg_bar_output(LVDS-1)
-ERROR: CONFIG: Expected one of these tokens: <end>, '#', 'set', 'i3bar_command', 'status_command', 'socket_path', 'mode', 'modifier', 'position', 'output', 'tray_output', 'font', 'workspace_buttons', 'verbose', 'colors', '}'
+ERROR: CONFIG: Expected one of these tokens: <end>, '#', 'set', 'i3bar_command', 'status_command', 'socket_path', 'mode', 'hidden_state', 'id', 'modifier', 'position', 'output', 'tray_output', 'font', 'workspace_buttons', 'verbose', 'colors', '}'
 ERROR: CONFIG: (in file <stdin>)
 ERROR: CONFIG: Line   1: bar {
 ERROR: CONFIG: Line   2:     output LVDS-1
index 2603f65af7e6328b7153e50914329d4e7e95563f..5ba8bfdddcdbd5d74067df85ab87a16fa4e9518d 100644 (file)
 # Verifies that using criteria to address scratchpad windows works.
 use i3test;
 
-################################################################################
+#####################################################################
 # Verify that using scratchpad show with criteria works as expected:
-# When matching a scratchpad window which is visible, it should hide it.
-# When matching a scratchpad window which is on __i3_scratch, it should show it.
-# When matching a non-scratchpad window, it should be a no-op.
-################################################################################
+# - When matching a scratchpad window which is visible,
+#   it should hide it.
+# - When matching a scratchpad window which is on __i3_scratch,
+#   it should show it.
+# - When matching a non-scratchpad window, it should be a no-op.
+# - When matching a scratchpad window,
+#   non-matching windows shouldn't appear
+######################################################################
 
 my $tmp = fresh_workspace;
 
 my $third_window = open_window(name => 'scratch-match');
 cmd 'move scratchpad';
 
-# Verify that using 'scratchpad show' without any matching windows is a no-op.
+#####################################################################
+# Verify that using 'scratchpad show' without any matching windows is
+# a no-op.
+#####################################################################
 my $old_focus = get_focused($tmp);
 
 cmd '[title="nomatch"] scratchpad show';
 
 is(get_focused($tmp), $old_focus, 'non-matching criteria have no effect');
 
+#####################################################################
 # Verify that we can use criteria to show a scratchpad window.
+#####################################################################
 cmd '[title="scratch-match"] scratchpad show';
 
 my $scratch_focus = get_focused($tmp);
@@ -47,12 +56,102 @@ cmd '[title="scratch-match"] scratchpad show';
 isnt(get_focused($tmp), $scratch_focus, 'matching criteria works');
 is(get_focused($tmp), $old_focus, 'focus restored');
 
+
+#####################################################################
 # Verify that we cannot use criteria to show a non-scratchpad window.
+#####################################################################
 my $tmp2 = fresh_workspace;
 my $non_scratch_window = open_window(name => 'non-scratch');
 cmd "workspace $tmp";
 is(get_focused($tmp), $old_focus, 'focus still ok');
-cmd '[title="non-match"] scratchpad show';
+cmd '[title="non-scratch"] scratchpad show';
 is(get_focused($tmp), $old_focus, 'focus unchanged');
 
+#####################################################################
+# Verify that non-matching windows doesn't appear
+#####################################################################
+# Subroutine to clear scratchpad
+sub clear_scratchpad {
+    while (scalar @{get_ws('__i3_scratch')->{floating_nodes}}) {
+        cmd 'scratchpad show';
+        cmd 'kill';
+    }
+}
+
+#Start from an empty fresh workspace
+my $empty_ws = fresh_workspace;
+cmd "workspace $empty_ws";
+
+my $no_focused = get_focused($empty_ws);
+cmd '[title="nothingmatchthistitle"] scratchpad show';
+#Check nothing match
+is(get_focused($empty_ws), $no_focused, "no window to focus on");
+
+clear_scratchpad;
+
+open_window(name => "my-scratch-window");
+my $w1_focus = get_focused($empty_ws);
+cmd 'move scratchpad';
+cmd '[title="my-scratch-window"] scratchpad show';
+#Check we created and shown a scratchpad window
+is(get_focused($empty_ws), $w1_focus, "focus on scratchpad window");
+
+#Switching workspace
+my $empty_ws2 = fresh_workspace;
+cmd "workspace $empty_ws2";
+open_window(name => "my-second-scratch-window");
+
+my $w2_focus = get_focused($empty_ws2);
+cmd 'move scratchpad';
+cmd '[title="my-second-scratch-window"] scratchpad show';
+
+#Check we got the correct window
+is(get_focused($empty_ws2), $w2_focus, "focus is on second window");
+
+#####################################################################
+# Verify that 'scratchpad show' correctly hide multiple scratchpad
+# windows
+#####################################################################
+clear_scratchpad;
+
+sub check_floating {
+    my($rws, $n) = @_;
+    my $ws = get_ws($rws);
+    is(scalar @{$ws->{nodes}}, 0, 'no windows on ws');
+    is(scalar @{$ws->{floating_nodes}}, $n, "$n floating windows on ws");
+}
+
+my $empty_ws3 = fresh_workspace;
+cmd "workspace $empty_ws3";
+
+check_floating($empty_ws3, 0);
+
+#Creating two scratchpad windows
+open_window(name => "toggle-1");
+cmd 'move scratchpad';
+open_window(name => "toggle-2");
+cmd 'move scratchpad';
+check_floating($empty_ws3, 0);
+#Showing both
+cmd '[title="toggle-"] scratchpad show';
+
+check_floating($empty_ws3, 2);
+
+#Hiding both
+cmd '[title="toggle-"] scratchpad show';
+check_floating($empty_ws3, 0);
+
+#Showing both again
+cmd '[title="toggle-"] scratchpad show';
+check_floating($empty_ws3, 2);
+
+
+#Hiding one
+cmd 'scratchpad show';
+check_floating($empty_ws3, 1);
+
+#Hiding the last
+cmd 'scratchpad show';
+check_floating($empty_ws3, 0);
+
 done_testing;
diff --git a/testcases/t/206-fullscreen-scratchpad.t b/testcases/t/206-fullscreen-scratchpad.t
new file mode 100644 (file)
index 0000000..53fca52
--- /dev/null
@@ -0,0 +1,94 @@
+#!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)
+#
+#   Assure that no window is in fullscreen mode after showing a scratchpad window
+# Bug still in: 4.5.1-54-g0f6b5fe
+
+use i3test;
+
+my $tmp = fresh_workspace;
+
+sub fullscreen_windows {
+    my $ws = $tmp;
+    $ws = shift if @_;
+
+    my $nodes = scalar grep { $_->{fullscreen_mode} != 0 } @{get_ws_content($ws)->[0]->{nodes}};
+    my $cons = scalar grep { $_->{fullscreen_mode} != 0 } @{get_ws_content($ws)};
+    return $nodes + $cons;
+}
+
+##########################################################################################
+# map two windows in one container, fullscreen one of them and then move it to scratchpad
+##########################################################################################
+
+my $first_win = open_window;
+my $second_win = open_window;
+
+# fullscreen the focused window
+cmd 'fullscreen';
+
+# see if the window really is in fullscreen mode
+is(fullscreen_windows(), 1, 'amount of fullscreen windows after enabling fullscreen');
+
+# move window to scratchpad
+cmd 'move scratchpad';
+
+###############################################################################
+# show the scratchpad window again; it should not be in fullscreen mode anymore
+###############################################################################
+
+# show window from scratchpad
+cmd 'scratchpad show';
+
+# switch window back to tiling mode
+cmd 'floating toggle';
+
+# see if no window is in fullscreen mode
+is(fullscreen_windows(), 0, 'amount of fullscreen windows after showing previously fullscreened scratchpad window');
+
+########################################################################################
+# move a window to scratchpad, focus parent container, make it fullscreen, focus a child
+########################################################################################
+
+# make layout tabbed
+cmd 'layout tabbed';
+
+# move one window to scratchpad
+cmd 'move scratchpad';
+
+# focus parent
+cmd 'focus parent';
+
+# fullscreen the container
+cmd 'fullscreen';
+
+# focus child
+cmd 'focus child';
+
+# see if the window really is in fullscreen mode
+is(fullscreen_windows(), 1, 'amount of fullscreen windows after enabling fullscreen on parent');
+
+##########################################################################
+# show a scratchpad window; no window should be in fullscreen mode anymore
+##########################################################################
+
+# show the scratchpad window
+cmd 'scratchpad show';
+
+# see if no window is in fullscreen mode
+is(fullscreen_windows(), 0, 'amount of fullscreen windows after showing a scratchpad window while a parent container was in fullscreen mode');
+
+done_testing;
diff --git a/testcases/t/514-ipc-workspace-multi-monitor.t b/testcases/t/514-ipc-workspace-multi-monitor.t
new file mode 100644 (file)
index 0000000..360bd42
--- /dev/null
@@ -0,0 +1,72 @@
+#!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)
+# 
+# Ticket: #990
+# Bug still in: 4.5.1-23-g82b5978
+
+use i3test i3_autostart => 0;
+
+my $config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+fake-outputs 1024x768+0+0,1024x768+1024+0
+EOT
+
+my $pid = launch_with_config($config);
+
+my $i3 = i3(get_socket_path());
+
+$i3->connect()->recv;
+
+################################
+# Workspaces requests and events
+################################
+
+my $focused = get_ws(focused_ws());
+
+# Events
+
+# We are switching to an empty workpspace on the output to the right from an empty workspace on the output on the left, so we expect
+# to receive "init", "focus", and "empty".
+my $focus = AnyEvent->condvar;
+$i3->subscribe({
+    workspace => sub {
+        my ($event) = @_;
+        if ($event->{change} eq 'focus') {
+            # Check that we have the old and new workspace
+            $focus->send(
+                $event->{current}->{name} == '2' &&
+                $event->{old}->{name} == $focused->{name}
+            );
+        }
+    }
+})->recv;
+
+cmd 'focus output right';
+
+my $t;
+$t = AnyEvent->timer(
+    after => 0.5,
+    cb => sub {
+        $focus->send(0);
+    }
+);
+
+ok($focus->recv, 'Workspace "focus" event received');
+
+exit_gracefully($pid);
+
+done_testing;