]> git.sur5r.net Git - i3/i3/blobdiff - docs/hacking-howto
Merge branch 'master' into next
[i3/i3] / docs / hacking-howto
index 1377d87c8c4f50086df323e7775b7d715458738b..bc59eaeb7a3384afab05025f510f992e195f81c6 100644 (file)
@@ -1,7 +1,7 @@
 Hacking i3: How To
 ==================
-Michael Stapelberg <michael+i3@stapelberg.de>
-July 2011
+Michael Stapelberg <michael@i3wm.org>
+February 2013
 
 This document is intended to be the first thing you read before looking and/or
 touching i3’s source code. It should contain all important information to help
@@ -28,7 +28,8 @@ In the case of i3, the tasks (and order of them) are the following:
   the first client of X) and manage them (reparent them, create window
   decorations, etc.)
 . When new windows are created, manage them
-. Handle the client’s `_WM_STATE` property, but only the `_WM_STATE_FULLSCREEN`
+. Handle the client’s `_WM_STATE` property, but only `_WM_STATE_FULLSCREEN` and
+  `_NET_WM_STATE_DEMANDS_ATTENTION`
 . Handle the client’s `WM_NAME` property
 . Handle the client’s size hints to display them proportionally
 . Handle the client’s urgency hint
@@ -56,64 +57,29 @@ 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
 layout (like dwm, awesome, …) but provide mechanisms for you to easily create
 the layout you need at the moment.
 
-=== The layout table
+=== The layout tree
 
-*********************************************************************************
-This section has not been updated for v4.0 yet, sorry! We wanted to release on
-time, but we will update this soon. Please talk to us on IRC if you need to
-know stuff *NOW* :).
-*********************************************************************************
-
-/////////////////////////////////////////////////////////////////////////////////
-To accomplish flexible layouts, we decided to simply use a table. The table
-grows and shrinks as you need it. Each cell holds a container which then holds
-windows (see picture below). You can use different layouts for each container
-(default layout and stacking layout).
+The data structure which i3 uses to keep track of your windows is a tree. Every
+node in the tree is a container (type +Con+). Some containers represent actual
+windows (every container with a +window != NULL+), some represent split
+containers and a few have special purposes: they represent workspaces, outputs
+(like VGA1, LVDS1, …) or the X11 root window.
 
 So, when you open a terminal and immediately open another one, they reside in
-the same container, in default layout. The layout table has exactly one column,
-one row and therefore one cell. When you move one of the terminals to the
-right, the table needs to grow. It will be expanded to two columns and one row.
-This enables you to have different layouts for each container. The table then
-looks like this:
-
-[width="15%",cols="^,^"]
-|========
-| T1 | T2
-|========
-
-When moving terminal 2 to the bottom, the table will be expanded again.
-
-[width="15%",cols="^,^"]
-|========
-| T1 |
-|    | T2
-|========
-
-You can really think of the layout table like a traditional HTML table, if
-you’ve ever designed one. Especially col- and rowspan work similarly. Below,
-you see an example of colspan=2 for the first container (which has T1 as
-window).
+the same split container, which uses the default layout. In case of an empty
+workspace, the split container we are talking about is the workspace.
 
-[width="15%",cols="^asciidoc"]
-|========
-| T1
-|
-[cols="^,^",frame="none"]
-!========
-! T2 ! T3
-!========
-|========
-
-Furthermore, you can freely resize table cells.
-/////////////////////////////////////////////////////////////////////////////////
+To get an impression of how different layouts are represented, just play around
+and look at the data structures -- they are exposed as a JSON hash. See
+http://i3wm.org/docs/ipc.html#_tree_reply for documentation on that and an
+example.
 
 == Files
 
@@ -131,21 +97,18 @@ Contains forward definitions for all public functions, as well as
 doxygen-compatible comments (so if you want to get a bit more of the big
 picture, either browse all header files or use doxygen if you prefer that).
 
-src/cfgparse.l::
-Contains the lexer for i3’s configuration file, written for +flex(1)+.
-
-src/cfgparse.y::
-Contains the parser for i3’s configuration file, written for +bison(1)+.
+src/config_parser.c::
+Contains a custom configuration parser. See src/command_parser.c for rationale
+ on why we use a custom parser.
 
 src/click.c::
 Contains all functions which handle mouse button clicks (right mouse button
 clicks initiate resizing and thus are relatively complex).
 
-src/cmdparse.l::
-Contains the lexer for i3 commands, written for +flex(1)+.
-
-src/cmdparse.y::
-Contains the parser for i3 commands, written for +bison(1)+.
+src/command_parser.c::
+Contains a hand-written parser to parse commands (commands are what
+you bind on keys and what you can send to i3 using the IPC interface, like
+'move left' or 'workspace 4').
 
 src/con.c::
 Contains all functions which deal with containers directly (creating
@@ -176,7 +139,7 @@ src/load_layout.c::
 Contains code for loading layouts from JSON files.
 
 src/log.c::
-Handles the setting of loglevels, contains the logging functions.
+Contains the logging functions.
 
 src/main.c::
 Initializes the window manager.
@@ -395,7 +358,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,
@@ -417,7 +380,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").
 
@@ -568,7 +531,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.
 
@@ -707,41 +670,123 @@ floating windows:
 
 /////////////////////////////////////////////////////////////////////////////////
 
-== User commands / commandmode (src/cmdparse.{l,y})
-
-*********************************************************************************
-This section has not been updated for v4.0 yet, sorry! We wanted to release on
-time, but we will update this soon. Please talk to us on IRC if you need to
-know stuff *NOW* :).
-*********************************************************************************
-
-/////////////////////////////////////////////////////////////////////////////////
-
-
-Like in vim, you can control i3 using commands. They are intended to be a
-powerful alternative to lots of shortcuts, because they can be combined. There
-are a few special commands, which are the following:
-
-exec <command>::
-Starts the given command by passing it to `/bin/sh`.
-
-restart::
-Restarts i3 by executing `argv[0]` (the path with which you started i3) without
-forking.
-
-w::
-"With". This is used to select a bunch of windows. Currently, only selecting
-the whole container in which the window is in, is supported by specifying "w".
-
-f, s, d::
-Toggle fullscreen, stacking, default mode for the current window/container.
-
-The other commands are to be combined with a direction. The directions are h,
-j, k and l, like in vim (h = left, j = down, k = up, l = right). When you just
-specify the direction keys, i3 will move the focus in that direction. You can
-provide "m" or "s" before the direction to move a window respectively or snap.
-
-/////////////////////////////////////////////////////////////////////////////////
+== User commands (parser-specs/commands.spec)
+
+In the configuration file and when using i3 interactively (with +i3-msg+, for
+example), you use commands to make i3 do things, like focus a different window,
+set a window to fullscreen, and so on. An example command is +floating enable+,
+which enables floating mode for the currently focused window. See the
+appropriate section in the link:userguide.html[User’s Guide] for a reference of
+all commands.
+
+In earlier versions of i3, interpreting these commands was done using lex and
+yacc, but experience has shown that lex and yacc are not well suited for our
+command language. Therefore, starting from version 4.2, we use a custom parser
+for user commands (not yet for the configuration file).
+The input specification for this parser can be found in the file
++parser-specs/commands.spec+. Should you happen to use Vim as an editor, use
+:source parser-specs/highlighting.vim to get syntax highlighting for this file
+(highlighting files for other editors are welcome).
+
+.Excerpt from commands.spec
+-----------------------------------------------------------------------
+state INITIAL:
+  '[' -> call cmd_criteria_init(); CRITERIA
+  'move' -> MOVE
+  'exec' -> EXEC
+  'workspace' -> WORKSPACE
+  'exit' -> call cmd_exit()
+  'restart' -> call cmd_restart()
+  'reload' -> call cmd_reload()
+-----------------------------------------------------------------------
+
+The input specification is written in an extremely simple format. The
+specification is then converted into C code by the Perl script
+generate-commands-parser.pl (the output file names begin with GENERATED and the
+files are stored in the +include+ directory). The parser implementation
++src/commands_parser.c+ includes the generated C code at compile-time.
+
+The above excerpt from commands.spec illustrates nearly all features of our
+specification format: You describe different states and what can happen within
+each state. State names are all-caps; the state in the above excerpt is called
+INITIAL. A list of tokens and their actions (separated by an ASCII arrow)
+follows. In the excerpt, all tokens are literals, that is, simple text strings
+which will be compared with the input. An action is either the name of a state
+in which the parser will transition into, or the keyword 'call', followed by
+the name of a function (and optionally a state).
+
+=== Example: The WORKSPACE state
+
+Let’s have a look at the WORKSPACE state, which is a good example of all
+features. This is its definition:
+
+.WORKSPACE state (commands.spec)
+----------------------------------------------------------------
+# workspace next|prev|next_on_output|prev_on_output
+# workspace back_and_forth
+# workspace <name>
+state WORKSPACE:
+  direction = 'next_on_output', 'prev_on_output', 'next', 'prev'
+      -> call cmd_workspace($direction)
+  'back_and_forth'
+      -> call cmd_workspace_back_and_forth()
+  workspace = string
+      -> call cmd_workspace_name($workspace)
+----------------------------------------------------------------
+
+As you can see from the commands, there are multiple different valid variants
+of the workspace command:
+
+workspace <direction>::
+       The word 'workspace' can be followed by any of the tokens 'next',
+       'prev', 'next_on_output' or 'prev_on_output'. This command will
+       switch to the next or previous workspace (optionally on the same
+       output). +
+       There is one function called +cmd_workspace+, which is defined
+       in +src/commands.c+. It will handle this kind of command. To know which
+       direction was specified, the direction token is stored on the stack
+       with the name "direction", which is what the "direction = " means in
+       the beginning. +
+
+NOTE: Note that you can specify multiple literals in the same line. This has
+       exactly the same effect as if you specified `direction =
+       'next_on_output' -> call cmd_workspace($direction)` and so forth. +
+
+NOTE: Also note that the order of literals is important here: If 'next' were
+       ordered before 'next_on_output', then 'next_on_output' would never
+       match.
+
+workspace back_and_forth::
+       This is a very simple case: When the literal 'back_and_forth' is found
+       in the input, the function +cmd_workspace_back_and_forth+ will be
+       called without parameters and the parser will return to the INITIAL
+       state (since no other state was specified).
+workspace <name>::
+       In this case, the workspace command is followed by an arbitrary string,
+       possibly in quotes, for example "workspace 3" or "workspace bleh". +
+       This is the first time that the token is actually not a literal (not in
+       single quotes), but just called string. Other possible tokens are word
+       (the same as string, but stops matching at a whitespace) and end
+       (matches the end of the input).
+
+=== Introducing a new command
+
+The following steps have to be taken in order to properly introduce a new
+command (or possibly extend an existing command):
+
+1. Define a function beginning with +cmd_+ in the file +src/commands.c+. Copy
+   the prototype of an existing function.
+2. After adding a comment on what the function does, copy the comment and
+   function definition to +include/commands.h+. Make the comment in the header
+   file use double asterisks to make doxygen pick it up.
+3. Write a test case (or extend an existing test case) for your feature, see
+   link:testsuite.html[i3 testsuite]. For now, it is sufficient to simply call
+   your command in all the various possible ways.
+4. Extend the parser specification in +parser-specs/commands.spec+. Run the
+   testsuite and see if your new function gets called with the appropriate
+   arguments for the appropriate input.
+5. Actually implement the feature.
+6. Document the feature in the link:userguide.html[User’s Guide].
 
 == Moving containers
 
@@ -889,37 +934,44 @@ Without much ado, here is the list of cases which need to be considered:
   leads to code which looks like it works fine but which does not work under
   certain conditions.
 
+* Forgetting to call `floating_fix_coordinates(con, old_rect, new_rect)` after
+  moving workspaces across outputs. Coordinates for floating containers are
+  not relative to workspace boundaries, so you must correct their coordinates
+  or those containers will show up in the wrong workspace or not at all.
+
 == 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
 
-When you want to send a patch because you fixed a bug or implemented a cool
-feature (please talk to us before working on features to see whether they are
-maybe already implemented, not possible for some some reason, or don’t fit
-into the concept), please use git to create a patchfile.
+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
+command which will focus windows in an alt+tab like way.
 
-First of all, update your working copy to the latest version of the master
-branch:
+When working on bugfixes, please make sure you mention that you are working on
+it in the corresponding bugreport at http://bugs.i3wm.org/. In case there is no
+bugreport yet, please create one.
 
---------
-git pull
---------
+After you are done, please submit your work for review at http://cr.i3wm.org/
 
-Afterwards, make the necessary changes for your bugfix/feature. Then, review
-the changes using +git diff+ (you might want to enable colors in the diff using
-+git config diff.color auto+).  When you are definitely done, use +git commit
--a+ to commit all changes you’ve made.
+Do not send emails to the mailing list or any author directly, and don’t submit
+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.
 
-Then, use the following command to generate a patchfile which we can directly
-apply to the branch, preserving your commit message and name:
+=== Which branch to use?
 
------------------------
-git format-patch origin
------------------------
+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.
 
-Just send us the generated file via email.
+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