]> git.sur5r.net Git - i3/i3/commitdiff
Merge branch 'master' into next
authorMichael Stapelberg <michael@stapelberg.de>
Sat, 29 Jun 2013 18:28:07 +0000 (20:28 +0200)
committerMichael Stapelberg <michael@stapelberg.de>
Sat, 29 Jun 2013 18:28:07 +0000 (20:28 +0200)
68 files changed:
.gitignore
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
i3-nagbar/main.c
i3.applications.desktop
i3bar/include/child.h
i3bar/include/common.h
i3bar/include/config.h
i3bar/include/xcb.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/commands_parser.h
include/con.h
include/config.h
include/config_directives.h
include/data.h
include/i3/ipc.h
include/libi3.h
include/log.h
include/randr.h
include/shmlog.h
include/x.h
libi3/get_exe_path.c [new file with mode: 0644]
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/main.c
src/manage.c
src/randr.c
src/render.c
src/scratchpad.c
src/workspace.c
src/x.c
testcases/lib/i3test.pm
testcases/t/113-urgent.t
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/207-shmlog.t [new file with mode: 0644]
testcases/t/514-ipc-workspace-multi-monitor.t [new file with mode: 0644]

index efeb48936443475a822c82f59f0e5eb117369437..0160a2467615241286a85a85e66195864de33c8e 100644 (file)
@@ -2,11 +2,16 @@
 tags
 include/GENERATED_*.h
 include/all.h.pch
+*~
 *.swp
 *.gcda
 *.gcno
 test.commands_parser
 test.config_parser
+testcases/MYMETA.json
+testcases/MYMETA.yml
+testcases/blib/
+testcases/pm_to_blib
 *.output
 *.tab.*
 *.yy.c
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..60345bec286d2427c1679e9c1966a93e197135e3 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]
 --------------
 
@@ -1723,6 +1762,31 @@ stack-limit rows 5
 image:stacklimit.png[Container limited to two columns]
 ///////////////////////////////////////////////////////////////////////////////
 
+=== Enabling shared memory logging
+
+As described in http://i3wm.org/docs/debugging.html, i3 can log to a shared
+memory buffer, which you can dump using +i3-dump-log+. The +shmlog+ command
+allows you to enable or disable the shared memory logging at runtime.
+
+Note that when using +shmlog <size_in_bytes>+, the current log will be
+discarded and a new one will be started.
+
+*Syntax*:
+------------------------------
+shmlog <size_in_bytes>
+shmlog <on|off|toggle>
+------------------------------
+
+*Examples*:
+---------------
+# Enable/disable logging
+bindsym $mod+x shmlog toggle
+
+# or, from a terminal:
+# increase the shared memory log buffer to 50 MiB
+i3-msg shmlog $((50*1024*1024))
+---------------
+
 === Reloading/Restarting/Exiting
 
 You can make i3 reload its configuration file with +reload+. You can also
@@ -1774,6 +1838,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 6ca80ba5babfaf6f80bde744c954caca2a765bdf..a9619f9620913c9bcc0920343adb9946199358ef 100644 (file)
@@ -164,15 +164,18 @@ static void handle_button_release(xcb_connection_t *conn, xcb_button_release_eve
     /* Also closes fd */
     fclose(script);
 
+    char *link_path;
+    sasprintf(&link_path, "%s.nagbar_cmd", script_path);
+    symlink(get_exe_path(argv0), link_path);
+
     char *terminal_cmd;
-    sasprintf(&terminal_cmd, "i3-sensible-terminal -e %s", argv0);
+    sasprintf(&terminal_cmd, "i3-sensible-terminal -e %s", link_path);
     printf("argv0 = %s\n", argv0);
     printf("terminal_cmd = %s\n", terminal_cmd);
 
-    setenv("_I3_NAGBAR_CMD", script_path, 1);
     start_application(terminal_cmd);
-    unsetenv("_I3_NAGBAR_CMD");
 
+    free(link_path);
     free(terminal_cmd);
     free(script_path);
 
@@ -275,23 +278,35 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) {
 }
 
 int main(int argc, char *argv[]) {
-    /* The following lines are a horrible kludge. Because terminal emulators
-     * have different ways of interpreting the -e command line argument (some
-     * need -e "less /etc/fstab", others need -e less /etc/fstab), we need to
-     * write commands to a script and then just run that script. However, since
-     * on some machines, $XDG_RUNTIME_DIR and $TMPDIR are mounted with noexec,
-     * we cannot directly execute the script either.
+    /* The following lines are a terribly horrible kludge. Because terminal
+     * emulators have different ways of interpreting the -e command line
+     * argument (some need -e "less /etc/fstab", others need -e less
+     * /etc/fstab), we need to write commands to a script and then just run
+     * that script. However, since on some machines, $XDG_RUNTIME_DIR and
+     * $TMPDIR are mounted with noexec, we cannot directly execute the script
+     * either.
+     *
+     * Initially, we tried to pass the command via the environment variable
+     * _I3_NAGBAR_CMD. But turns out that some terminal emulators such as
+     * xfce4-terminal run all windows from a single master process and only
+     * pass on the command (not the environment) to that master process.
      *
-     * Therefore, we run i3-nagbar instead and pass the path to the script in
-     * the environment variable $_I3_NAGBAR_CMD. i3-nagbar then execs /bin/sh
-     * with that path in order to run that script.
+     * Therefore, we symlink i3-nagbar (which MUST reside on an executable
+     * filesystem) with a special name and run that symlink. When i3-nagbar
+     * recognizes it’s started as a binary ending in .nagbar_cmd, it strips off
+     * the .nagbar_cmd suffix and runs /bin/sh on argv[0]. That way, we can run
+     * a shell script on a noexec filesystem.
      *
      * From a security point of view, i3-nagbar is just an alias to /bin/sh in
      * certain circumstances. This should not open any new security issues, I
      * hope. */
     char *cmd = NULL;
-    if ((cmd = getenv("_I3_NAGBAR_CMD")) != NULL) {
-        unsetenv("_I3_NAGBAR_CMD");
+    const size_t argv0_len = strlen(argv[0]);
+    if (argv0_len > strlen(".nagbar_cmd") &&
+        strcmp(argv[0] + argv0_len - strlen(".nagbar_cmd"), ".nagbar_cmd") == 0) {
+        unlink(argv[0]);
+        cmd = strdup(argv[0]);
+        *(cmd + argv0_len - strlen(".nagbar_cmd")) = '\0';
         execl("/bin/sh", "/bin/sh", cmd, NULL);
         err(EXIT_FAILURE, "execv(/bin/sh, /bin/sh, %s)", cmd);
     }
index 31d67045a3bed5068f9c6f2ca62aa7e60e9f6fe8..e40689dd26884312066d6d14d51768d085e05ba0 100644 (file)
@@ -1,6 +1,7 @@
 [Desktop Entry]
 Type=Application
 Name=i3
+NoDisplay=true
 Comment=improved dynamic tiling window manager
 Exec=i3
 X-GNOME-WMName=i3
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 d8d0c09049287cf0cc4768836e6325680a3d223d..e1654a3481dee9ae10a9322641405bed25872ec9 100644 (file)
@@ -114,7 +114,7 @@ void realloc_sl_buffer(void);
  * Reconfigure all bars and create new for newly activated outputs
  *
  */
-void reconfig_windows(void);
+void reconfig_windows(bool redraw_bars);
 
 /*
  * Render the bars, with buttons and statusline
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..3536b7dcb27f7b9a57fc45eed4edcb5091fd9fcf 100644 (file)
@@ -64,7 +64,7 @@ void got_output_reply(char *reply) {
     parse_outputs_json(reply);
     DLOG("Reconfiguring Windows...\n");
     realloc_sl_buffer();
-    reconfig_windows();
+    reconfig_windows(false);
 
     i3_output *o_walk;
     SLIST_FOREACH(o_walk, outputs, slist) {
@@ -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(true);
+    }
 
-/* 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..79d55e5cd339fe749f03ef3fd3efc159d9141bad 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 && config.hide_on_modifier == M_HIDE)) {
         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,40 @@ 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);
+        XCloseDisplay(xkb_dpy);
+        close(xkb_io->fd);
+        FREE(xkb_io);
+        xkb_dpy = NULL;
+    }
+}
+
+/*
+ * 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.
@@ -1307,7 +1368,7 @@ void realloc_sl_buffer(void) {
  * Reconfigure all bars and create new bars for recently activated outputs
  *
  */
-void reconfig_windows(void) {
+void reconfig_windows(bool redraw_bars) {
     uint32_t mask;
     uint32_t values[5];
     static bool tray_configured = false;
@@ -1329,8 +1390,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 +1512,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 +1523,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 +1555,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 +1571,31 @@ void reconfig_windows(void) {
                                                                     walk->rect.w,
                                                                     walk->rect.h);
 
-            if (xcb_request_failed(cfg_cookie, "Could not reconfigure window")) {
-                exit(EXIT_FAILURE);
+            xcb_void_cookie_t map_cookie, umap_cookie;
+            if (redraw_bars) {
+                /* Unmap the window, and draw it again when in dock mode */
+                umap_cookie = xcb_unmap_window_checked(xcb_connection, walk->bar);
+                if (config.hide_on_modifier == M_DOCK) {
+                    cont_child();
+                    map_cookie = xcb_map_window_checked(xcb_connection, walk->bar);
+                } else {
+                    stop_child();
+                }
+
+                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") ||
+                (redraw_bars && (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);
             }
         }
@@ -1533,7 +1623,7 @@ void draw_bars(bool unhide) {
         }
         if (outputs_walk->bar == XCB_NONE) {
             /* Oh shit, an active output without an own bar. Create it now! */
-            reconfig_windows();
+            reconfig_windows(false);
         }
         /* First things first: clear the backbuffer */
         uint32_t color = colors.bar_bg;
@@ -1573,7 +1663,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,19 +1762,22 @@ 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;
     }
 
-    if (!mod_pressed) {
-        if (unhide) {
-            /* The urgent-hint should get noticed, so we unhide the bars shortly */
-            unhide_bars();
-        } else if (walks_away) {
-            FREE(last_urgent_ws);
-            hide_bars();
-        }
+    /* 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 || (should_unhide && !should_hide)) {
+        unhide_bars();
+    } else if (!mod_pressed && (walks_away || should_hide)) {
+        FREE(last_urgent_ws);
+        hide_bars();
     }
 
     redraw_bars();
@@ -1719,5 +1812,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..a09e7466aa05a26013bb9913b188938399faeb85 100644 (file)
@@ -265,4 +265,16 @@ 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);
+
+/*
+ * Implementation of 'shmlog <size>|toggle|on|off'
+ *
+ */
+void cmd_shmlog(I3_CMD, char *argument);
+
 #endif
index fcc14ff58881f3ffbfb1096d9ff7b02afa673061..37c4d4b1705459da0894d8ef2f0c4e0390dd9d2e 100644 (file)
@@ -25,13 +25,13 @@ struct CommandResult {
     /* The JSON generator to append a reply to. */
     yajl_gen json_gen;
 
-    /* Whether the command requires calling tree_render. */
-    bool needs_tree_render;
-
     /* The next state to transition to. Passed to the function so that we can
      * determine the next state as a result of a function call, like
      * cfg_criteria_pop_state() does. */
     int next_state;
+
+    /* Whether the command requires calling tree_render. */
+    bool needs_tree_render;
 };
 
 struct CommandResult *parse_command(const char *input);
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..b53e277d331aee1a9e2a7e17ac34b2d0c70b5c64 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.
@@ -133,8 +146,8 @@ struct deco_render_params {
     struct width_height con_window_rect;
     Rect con_deco_rect;
     uint32_t background;
+    layout_t parent_layout;
     bool con_is_leaf;
-    orientation_t parent_orientation;
 };
 
 /**
@@ -215,6 +228,14 @@ struct Binding {
         B_UPON_KEYRELEASE_IGNORE_MODS = 2,
     } release;
 
+    uint32_t number_keycodes;
+
+    /** Keycode to bind */
+    uint32_t keycode;
+
+    /** Bitmask consisting of BIND_MOD_1, BIND_MODE_SWITCH, … */
+    uint32_t mods;
+
     /** Symbol the user specified in configfile, if any. This needs to be
      * stored with the binding to be able to re-convert it into a keycode
      * if the keyboard mapping changes (using Xmodmap for example) */
@@ -227,13 +248,6 @@ struct Binding {
      * This is an array of number_keycodes size. */
     xcb_keycode_t *translated_to;
 
-    uint32_t number_keycodes;
-
-    /** Keycode to bind */
-    uint32_t keycode;
-
-    /** Bitmask consisting of BIND_MOD_1, BIND_MODE_SWITCH, … */
-    uint32_t mods;
 
     /** Command, like in command mode */
     char *command;
@@ -268,11 +282,6 @@ struct Autostart {
 struct xoutput {
     /** Output id, so that we can requery the output directly later */
     xcb_randr_output_t id;
-    /** Name of the output */
-    char *name;
-
-    /** Pointer to the Con which represents this output */
-    Con *con;
 
     /** Whether the output is currently active (has a CRTC attached with a
      * valid mode) */
@@ -284,6 +293,12 @@ struct xoutput {
     bool to_be_disabled;
     bool primary;
 
+    /** Name of the output */
+    char *name;
+
+    /** Pointer to the Con which represents this output */
+    Con *con;
+
     /** x, y, width, height */
     Rect rect;
 
@@ -303,6 +318,11 @@ struct Window {
     xcb_window_t leader;
     xcb_window_t transient_for;
 
+    /** Pointers to the Assignments which were already ran for this Window
+     * (assignments run only once) */
+    uint32_t nr_assignments;
+    Assignment **ran_assignments;
+
     char *class_class;
     char *class_instance;
 
@@ -323,9 +343,6 @@ struct Window {
     /** Whether the application needs to receive WM_TAKE_FOCUS */
     bool needs_take_focus;
 
-    /** When this window was marked urgent. 0 means not urgent */
-    struct timeval urgent;
-
     /** Whether this window accepts focus. We store this inverted so that the
      * default will be 'accepts focus'. */
     bool doesnt_accept_focus;
@@ -333,14 +350,12 @@ struct Window {
     /** Whether the window says it is a dock window */
     enum { W_NODOCK = 0, W_DOCK_TOP = 1, W_DOCK_BOTTOM = 2 } dock;
 
+    /** When this window was marked urgent. 0 means not urgent */
+    struct timeval urgent;
+
     /** Pixels the window reserves. left/right/top/bottom */
     struct reservedpx reserved;
 
-    /** Pointers to the Assignments which were already ran for this Window
-     * (assignments run only once) */
-    uint32_t nr_assignments;
-    Assignment **ran_assignments;
-
     /** Depth of the window */
     uint16_t depth;
 };
@@ -373,8 +388,8 @@ struct Match {
         M_DOCK_BOTTOM = 3
     } dock;
     xcb_window_t id;
-    Con *con_id;
     enum { M_ANY = 0, M_TILING, M_FLOATING } floating;
+    Con *con_id;
 
     /* Where the window looking for a match should be inserted:
      *
@@ -387,12 +402,12 @@ struct Match {
      */
     enum { M_HERE = 0, M_ASSIGN_WS, M_BELOW } insert_where;
 
+    TAILQ_ENTRY(Match) matches;
+
     /* Whether this match was generated when restarting i3 inplace.
      * Leads to not setting focus when managing a new window, because the old
      * focus stack should be restored. */
     bool restart_mode;
-
-    TAILQ_ENTRY(Match) matches;
 };
 
 /**
@@ -441,6 +456,24 @@ struct Assignment {
  */
 struct Con {
     bool mapped;
+
+    /* Should this container be marked urgent? This gets set when the window
+     * inside this container (if any) sets the urgency hint, for example. */
+    bool urgent;
+
+    /** This counter contains the number of UnmapNotify events for this
+     * container (or, more precisely, for its ->frame) which should be ignored.
+     * UnmapNotify events need to be ignored when they are caused by i3 itself,
+     * for example when reparenting or when unmapping the window on a workspace
+     * change. */
+    uint8_t ignore_unmap;
+
+    /* ids/pixmap/graphics context for the frame window */
+    bool pixmap_recreated;
+    xcb_window_t frame;
+    xcb_pixmap_t pixmap;
+    xcb_gcontext_t pm_gc;
+
     enum {
         CT_ROOT = 0,
         CT_OUTPUT = 1,
@@ -449,6 +482,11 @@ struct Con {
         CT_WORKSPACE = 4,
         CT_DOCKAREA = 5
     } type;
+
+    /** the workspace number, if this Con is of type CT_WORKSPACE and the
+     * workspace is not a named workspace (for named workspaces, num == -1) */
+    int num;
+
     struct Con *parent;
 
     struct Rect rect;
@@ -459,10 +497,6 @@ struct Con {
 
     char *name;
 
-    /** the workspace number, if this Con is of type CT_WORKSPACE and the
-     * workspace is not a named workspace (for named workspaces, num == -1) */
-    int num;
-
     /* a sticky-group is an identifier which bundles several containers to a
      * group. The contents are shared between all of them, that is they are
      * displayed on whichever of the containers is currently visible */
@@ -492,19 +526,9 @@ struct Con {
 
     struct Window *window;
 
-    /* Should this container be marked urgent? This gets set when the window
-     * inside this container (if any) sets the urgency hint, for example. */
-    bool urgent;
-
     /* timer used for disabling urgency */
     struct ev_timer *urgency_timer;
 
-    /* ids/pixmap/graphics context for the frame window */
-    xcb_window_t frame;
-    xcb_pixmap_t pixmap;
-    xcb_gcontext_t pm_gc;
-    bool pixmap_recreated;
-
     /** Cache for the decoration rendering */
     struct deco_render_params *deco_render_params;
 
@@ -531,15 +555,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
@@ -554,13 +570,6 @@ struct Con {
         FLOATING_USER_ON = 3
     } floating;
 
-    /** This counter contains the number of UnmapNotify events for this
-     * container (or, more precisely, for its ->frame) which should be ignored.
-     * UnmapNotify events need to be ignored when they are caused by i3 itself,
-     * for example when reparenting or when unmapping the window on a workspace
-     * change. */
-    uint8_t ignore_unmap;
-
     TAILQ_ENTRY(Con) nodes;
     TAILQ_ENTRY(Con) focused;
     TAILQ_ENTRY(Con) all_cons;
@@ -584,6 +593,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 53f3383d35390671e0f2621e23c57150e1d1fc0a..b0141f1df9cc22c8eae3e6abf4b2961967ce0330 100644 (file)
@@ -364,4 +364,12 @@ bool is_debug_build() __attribute__((const));
  */
 char *get_process_filename(const char *prefix);
 
+/**
+ * This function returns the absolute path to the executable it is running in.
+ *
+ * The implementation follows http://stackoverflow.com/a/933996/712014
+ *
+ */
+const char *get_exe_path(const char *argv0);
+
 #endif
index 6fabeca339fdd3283a35e039a6fd2c7922f1e697..c9d1d0cd67a15e4f7652fb7128b327022e7fc543 100644 (file)
@@ -38,6 +38,18 @@ extern int shmlog_size;
  */
 void init_logging(void);
 
+/**
+ * Opens the logbuffer.
+ *
+ */
+void open_logbuffer(void);
+
+/**
+ * Closes the logbuffer.
+ *
+ */
+void close_logbuffer(void);
+
 /**
  * Set debug logging.
  *
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 fd3f53ebfac73a0ef60ea9f688c9126498626140..94da2bdb5065fc3e47c4d5d2ecf4d7f14872812f 100644 (file)
@@ -14,6 +14,9 @@
 #include <stdint.h>
 #include <pthread.h>
 
+/* Default shmlog size if not set by user. */
+extern const int default_shmlog_size;
+
 /*
  * Header of the shmlog file. Used by i3/src/log.c and i3/i3-dump-log/main.c.
  *
index c3d4ffc7cc29f5bac0194aa9ca401f469ee70e8e..b6bb4a5283702dd2f0110f2122b2d28574f77710 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)
@@ -104,6 +108,12 @@ void x_raise_con(Con *con);
  */
 void x_set_name(Con *con, const char *name);
 
+/**
+ * Set up the SHMLOG_PATH atom.
+ *
+ */
+void update_shmlog_atom(void);
+
 /**
  * Sets up i3 specific atoms (I3_SOCKET_PATH and I3_CONFIG_PATH)
  *
diff --git a/libi3/get_exe_path.c b/libi3/get_exe_path.c
new file mode 100644 (file)
index 0000000..fca7ba0
--- /dev/null
@@ -0,0 +1,74 @@
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#include "libi3.h"
+
+/*
+ * This function returns the absolute path to the executable it is running in.
+ *
+ * The implementation follows http://stackoverflow.com/a/933996/712014
+ *
+ */
+const char *get_exe_path(const char *argv0) {
+       static char destpath[PATH_MAX];
+       char tmp[PATH_MAX];
+
+#if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+       /* Linux and Debian/kFreeBSD provide /proc/self/exe */
+#if defined(__linux__) || defined(__FreeBSD_kernel__)
+       const char *exepath = "/proc/self/exe";
+#elif defined(__FreeBSD__)
+       const char *exepath = "/proc/curproc/file";
+#endif
+       ssize_t linksize;
+
+       if ((linksize = readlink(exepath, destpath, sizeof(destpath) - 1)) != -1) {
+               /* readlink() does not NULL-terminate strings, so we have to. */
+               destpath[linksize] = '\0';
+
+               return destpath;
+       }
+#endif
+
+       /* argv[0] is most likely a full path if it starts with a slash. */
+       if (argv0[0] == '/')
+               return argv0;
+
+       /* if argv[0] contains a /, prepend the working directory */
+       if (strchr(argv0, '/') != NULL &&
+               getcwd(tmp, sizeof(tmp)) != NULL) {
+               snprintf(destpath, sizeof(destpath), "%s/%s", tmp, argv0);
+               return destpath;
+       }
+
+       /* Fall back to searching $PATH (or _CS_PATH in absence of $PATH). */
+       char *path = getenv("PATH");
+       if (path == NULL) {
+               /* _CS_PATH is typically something like "/bin:/usr/bin" */
+               confstr(_CS_PATH, tmp, sizeof(tmp));
+               sasprintf(&path, ":%s", tmp);
+       } else {
+               path = strdup(path);
+       }
+       const char *component;
+       char *str = path;
+       while (1) {
+               if ((component = strtok(str, ":")) == NULL)
+                       break;
+               str = NULL;
+               snprintf(destpath, sizeof(destpath), "%s/%s", component, argv0);
+               /* Of course this is not 100% equivalent to actually exec()ing the
+                * binary, but meh. */
+               if (access(destpath, X_OK) == 0) {
+                       free(path);
+                       return destpath;
+               }
+       }
+       free(path);
+
+       /* Last resort: maybe it’s in /usr/bin? */
+       return "/usr/bin/i3-nagbar";
+}
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..4407158cb6398c49fb358f48158c484190a80c64 100644 (file)
@@ -19,6 +19,7 @@ state INITIAL:
   'exit' -> call cmd_exit()
   'restart' -> call cmd_restart()
   'reload' -> call cmd_reload()
+  'shmlog' -> SHMLOG
   'border' -> BORDER
   'layout' -> LAYOUT
   'append_layout' -> APPEND_LAYOUT
@@ -35,6 +36,7 @@ state INITIAL:
   'nop' -> NOP
   'scratchpad' -> SCRATCHPAD
   'mode' -> MODE
+  'bar' -> BAR
 
 state CRITERIA:
   ctype = 'class' -> CRITERION
@@ -61,6 +63,12 @@ state EXEC:
   command = string
       -> call cmd_exec($nosn, $command)
 
+# shmlog <size>|toggle|on|off
+state SHMLOG:
+  # argument may be a number
+  argument = string
+    -> call cmd_shmlog($argument)
+
 # border normal|none|1pixel|toggle|1pixel
 state BORDER:
   border_style = 'normal', 'pixel'
@@ -319,3 +327,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..fbe4b11723e0c3567e7769d177c1f53466925cd3 100644 (file)
@@ -13,6 +13,7 @@
 #include <stdarg.h>
 
 #include "all.h"
+#include "shmlog.h"
 
 // Macros to make the YAJL API a bit easier to use.
 #define y(x, ...) yajl_gen_ ## x (cmd_output->json_gen, ##__VA_ARGS__)
@@ -1547,7 +1548,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 +1633,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 +1918,144 @@ 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();
+}
+
+/*
+ * Implementation of 'shmlog <size>|toggle|on|off'
+ *
+ */
+void cmd_shmlog(I3_CMD, char *argument) {
+    if (!strcmp(argument,"toggle"))
+        /* Toggle shm log, if size is not 0. If it is 0, set it to default. */
+        shmlog_size = shmlog_size ? -shmlog_size : default_shmlog_size;
+    else if (!strcmp(argument, "on"))
+        shmlog_size = default_shmlog_size;
+    else if (!strcmp(argument, "off"))
+        shmlog_size = 0;
+    else {
+        /* If shm logging now, restart logging with the new size. */
+        if (shmlog_size > 0) {
+            shmlog_size = 0;
+            LOG("Restarting shm logging...\n");
+            init_logging();
+        }
+        shmlog_size = atoi(argument);
+        /* Make a weakly attempt at ensuring the argument is valid. */
+        if (shmlog_size <= 0)
+            shmlog_size = default_shmlog_size;
+    }
+    LOG("%s shm logging\n", shmlog_size > 0 ? "Enabling" : "Disabling");
+    init_logging();
+    update_shmlog_atom();
+    // XXX: default reply for now, make this a better reply
+    ysuccess(true);
+}
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..7ef19477d1e59f822e18c2f71a9caf6025f6a410 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);
 
@@ -1361,6 +1369,8 @@ static void con_on_remove_child(Con *con) {
     }
 
     con_force_split_parents_redraw(con);
+    con->urgent = con_has_urgent_child(con);
+    con_update_parents_urgency(con);
 
     /* TODO: check if this container would swallow any other client and
      * don’t close it automatically. */
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 ea9374725af4871a7f24fc9531467e836e249c17..643f204b64110a12aede45c4bf4b6ee9e4851f86 100644 (file)
@@ -662,11 +662,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..68131af88daae542cd9f2c792bf1e2a3331c8768 100644 (file)
--- a/src/log.c
+++ b/src/log.c
@@ -81,18 +81,29 @@ 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");
             }
         }
     }
+    /* Start SHM logging if shmlog_size is > 0. shmlog_size is SHMLOG_SIZE by
+     * default on development versions, and 0 on release versions. If it is
+     * not > 0, the user has turned it off, so let's close the logbuffer. */
+     if (shmlog_size > 0 && logbuffer == NULL)
+        open_logbuffer();
+     else if (shmlog_size <= 0 && logbuffer)
+        close_logbuffer();
+     atexit(purge_zerobyte_logfile);
+}
 
-    /* If this is a debug build (not a release version), we will enable SHM
-     * logging by default, unless the user turned it off explicitly. */
-    if (logbuffer == NULL && shmlog_size > 0) {
+/*
+ * Opens the logbuffer.
+ *
+ */
+void open_logbuffer(void) {
         /* Reserve 1% of the RAM for the logfile, but at max 25 MiB.
          * For 512 MiB of RAM this will lead to a 5 MiB log buffer.
          * At the moment (2011-12-10), no testcase leads to an i3 log
@@ -107,26 +118,28 @@ 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));
-            logbuffer = NULL;
+            close_logbuffer();
+            fprintf(stderr, "Could not mmap SHM segment for the i3 log: %s\n", strerror(errno));
             return;
         }
 
@@ -138,14 +151,23 @@ 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);
         loglastwrap = logbuffer + logbuffer_size;
         store_log_markers();
-    }
-    atexit(purge_zerobyte_logfile);
+}
+
+/*
+ * Closes the logbuffer.
+ *
+ */
+void close_logbuffer(void) {
+    close(logbuffer_shm);
+    shm_unlink(shmlogname);
+    logbuffer = NULL;
+    shmlogname = "";
 }
 
 /*
index db3aca49f4c0f7909200f7afa6d6eac6a462c665..bc3bb6341a8bafd8367622c42e52f953f22dab0b 100644 (file)
@@ -19,6 +19,7 @@
 #include <sys/mman.h>
 #include <sys/stat.h>
 #include "all.h"
+#include "shmlog.h"
 
 #include "sd-daemon.h"
 
@@ -67,6 +68,9 @@ xcb_key_symbols_t *keysyms;
 /* Those are our connections to X11 for use with libXcursor and XKB */
 Display *xlibdpy, *xkbdpy;
 
+/* Default shmlog size if not set by user. */
+const int default_shmlog_size = 25 * 1024 * 1024;
+
 /* The list of key bindings */
 struct bindings_head *bindings;
 
@@ -290,8 +294,8 @@ int main(int argc, char *argv[]) {
      * (file) logging. */
     init_logging();
 
-    /* On non-release builds, disable SHM logging by default. */
-    shmlog_size = (is_debug_build() ? 25 * 1024 * 1024 : 0);
+    /* On release builds, disable SHM logging by default. */
+    shmlog_size = (is_debug_build() ? default_shmlog_size : 0);
 
     start_argv = argv;
 
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..27d92acad13687499c207414ac0b15f7c2fb99d3 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);
 }
@@ -1052,6 +1063,16 @@ void x_set_name(Con *con, const char *name) {
     state->name = sstrdup(name);
 }
 
+/*
+ * Set up the I3_SHMLOG_PATH atom.
+ *
+ */
+void update_shmlog_atom() {
+    xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root,
+            A_I3_SHMLOG_PATH, A_UTF8_STRING, 8,
+            strlen(shmlogname), shmlogname);
+}
+
 /*
  * Sets up i3 specific atoms (I3_SOCKET_PATH and I3_CONFIG_PATH)
  *
@@ -1064,8 +1085,7 @@ void x_set_i3_atoms(void) {
     xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A_I3_PID, XCB_ATOM_CARDINAL, 32, 1, &pid);
     xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A_I3_CONFIG_PATH, A_UTF8_STRING, 8,
                         strlen(current_configpath), current_configpath);
-    xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A_I3_SHMLOG_PATH, A_UTF8_STRING, 8,
-                        strlen(shmlogname), shmlogname);
+    update_shmlog_atom();
 }
 
 /*
index d6d71b23b7f49be099d60b2249c2ee53da8779af..9f3a6ea2d0be346be4363dd074c8cac69a19a8cd 100644 (file)
@@ -315,6 +315,11 @@ Usually, though, calls are simpler:
 
   my $top_window = open_window;
 
+To identify the resulting window object in i3 commands, use the id property:
+
+  my $top_window = open_window;
+  cmd '[id="' . $top_window->id . '"] kill';
+
 =cut
 sub open_window {
     my %args = @_ == 1 ? %{$_[0]} : @_;
index 02f98af5829dd64d568dd2ae9ed728ce6adad3c2..2eb853de1cd958c4880ac934aee76ba7698b7a9a 100644 (file)
@@ -278,6 +278,32 @@ for ($type = 1; $type <= 2; $type++) {
     is($w->{urgent}, 0, 'Urgent flag no longer set after killing the window ' .
        'from another workspace');
 
+##############################################################################
+# Check if urgent flag can be unset if we move the window out of the container
+##############################################################################
+    my $tmp = fresh_workspace;
+    cmd 'layout tabbed';
+    my $w1 = open_window;
+    my $w2 = open_window;
+    sync_with_i3;
+    cmd '[id="' . $w2->id . '"] focus';
+    sync_with_i3;
+    cmd 'split v';
+    cmd 'layout stacked';
+    my $w3 = open_window;
+    sync_with_i3;
+    cmd '[id="' . $w2->id . '"] focus';
+    sync_with_i3;
+    set_urgency($w3, 1, $type);
+    sync_with_i3;
+    cmd 'focus parent';
+    sync_with_i3;
+    cmd 'move right';
+    cmd '[id="' . $w3->id . '"] focus';
+    sync_with_i3;
+    my $ws = get_ws($tmp);
+    ok(!$ws->{urgent}, 'urgent flag not set on workspace');
+
     exit_gracefully($pid);
 }
 
index 52070d56b1090b55b31e4d87b11dcf7e21767bab..a6bb1c5a0fd6410659327e6bc1a1fd06180bfe71 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', 'shmlog', '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/207-shmlog.t b/testcases/t/207-shmlog.t
new file mode 100644 (file)
index 0000000..b63a749
--- /dev/null
@@ -0,0 +1,87 @@
+#!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)
+#
+use i3test i3_autostart => 0;
+use IPC::Run qw(run);
+use File::Temp;
+
+################################################################################
+# 1: test that shared memory logging does not work yet
+################################################################################
+
+my $config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+EOT
+
+# NB: launch_with_config sets --shmlog-size=0 because the logfile gets
+# redirected via stdout redirection anyways.
+my $pid = launch_with_config($config);
+
+my $stdout;
+my $stderr;
+run [ '../i3-dump-log/i3-dump-log' ],
+    '>', \$stdout,
+    '2>', \$stderr;
+
+like($stderr, qr#^i3-dump-log: ERROR: i3 is running, but SHM logging is not enabled\.#,
+    'shm logging not enabled');
+
+################################################################################
+# 2: enable shared memory logging and verify new content shows up
+################################################################################
+
+cmd 'shmlog on';
+
+my $random_nop = mktemp('nop.XXXXXX');
+cmd "nop $random_nop";
+
+run [ '../i3-dump-log/i3-dump-log' ],
+    '>', \$stdout,
+    '2>', \$stderr;
+
+like($stdout, qr#$random_nop#, 'random nop found in shm log');
+like($stderr, qr#^$#, 'stderr empty');
+
+################################################################################
+# 3: change size of the shared memory log buffer and verify old content is gone
+################################################################################
+
+cmd 'shmlog ' . (23 * 1024 * 1024);
+
+run [ '../i3-dump-log/i3-dump-log' ],
+    '>', \$stdout,
+    '2>', \$stderr;
+
+unlike($stdout, qr#$random_nop#, 'random nop not found in shm log');
+like($stderr, qr#^$#, 'stderr empty');
+
+################################################################################
+# 4: disable logging and verify it no longer works
+################################################################################
+
+cmd 'shmlog off';
+
+run [ '../i3-dump-log/i3-dump-log' ],
+    '>', \$stdout,
+    '2>', \$stderr;
+
+like($stderr, qr#^i3-dump-log: ERROR: i3 is running, but SHM logging is not enabled\.#,
+    'shm logging not enabled');
+
+exit_gracefully($pid);
+
+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;