]> git.sur5r.net Git - i3/i3/blobdiff - docs/hacking-howto
Merge pull request #3452 from orestisf1993/title_align
[i3/i3] / docs / hacking-howto
index d0f5ac3a5b1b5e96f0b325d59c41fbe653d04d8f..2ca44a5ffadff9acec79766f4a1a4e8a94ad76fa 100644 (file)
@@ -1,13 +1,91 @@
 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
 you understand why things are like they are. If it does not mention something
 you find necessary, please do not hesitate to contact me.
 
+== Building i3
+
+You can build i3 like you build any other software package which uses autotools.
+Here’s a memory refresher:
+
+    $ autoreconf -fi
+    $ mkdir -p build && cd build
+    $ ../configure
+    $ make -j8
+
+(The autoreconf -fi step is unnecessary if you are building from a release tarball,
+ but shouldn’t hurt either.)
+
+=== Build system features
+
+* We use the AX_ENABLE_BUILDDIR macro to enforce builds happening in a separate
+  directory. This is a prerequisite for the AX_EXTEND_SRCDIR macro and building
+  in a separate directory is common practice anyway. In case this causes any
+  trouble when packaging i3 for your distribution, please open an issue.
+
+* “make check” runs the i3 testsuite. See docs/testsuite for details.
+
+* “make distcheck” (runs testsuite on “make dist” result, tiny bit quicker
+  feedback cycle than waiting for the travis build to catch the issue).
+
+* “make uninstall” (occasionally requested by users who compile from source)
+
+* “make” will build manpages/docs by default if the tools are installed.
+  Conversely, manpages/docs are not tried to be built for users who don’t want
+  to install all these dependencies to get started hacking on i3.
+
+* non-release builds will enable address sanitizer by default. Use the
+  --disable-sanitizers configure option to turn off all sanitizers, and see
+  --help for available sanitizers.
+
+* Support for pre-compiled headers (PCH) has been dropped for now in the
+  interest of simplicity. If you need support for PCH, please open an issue.
+
+* Coverage reports are now generated using “make check-code-coverage”, which
+  requires specifying --enable-code-coverage when calling configure.
+
+== Using git / sending patches
+
+For a short introduction into using git, see
+https://web.archive.org/web/20121024222556/http://www.spheredev.org/wiki/Git_for_the_lazy
+or, for more documentation, see https://git-scm.com/documentation
+
+Please talk to us before working on new features to see whether they will be
+accepted. A good way for this is to open an issue and asking for opinions on it.
+Even for accepted features, this can be a good way to refine an idea upfront. However,
+we don't want to see certain features in i3, e.g., switching window focus in an
+Alt+Tab like way.
+
+When working on bugfixes, please make sure you mention that you are working on
+it in the corresponding bug report at https://github.com/i3/i3/issues. In case
+there is no bug report yet, please create one.
+
+After you are done, please submit your work for review as a pull request at
+https://github.com/i3/i3.
+
+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
+https://github.com/i3/i3. 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” (the latter
+being the default branch, the one that people get when they check out the git
+repository).
+
+The contents of “master” 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”.
+
 == Window Managers
 
 A window manager is not necessarily needed to run X, but it is usually used in
@@ -28,7 +106,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,7 +135,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
@@ -77,7 +156,7 @@ workspace, the split container we are talking about is the workspace.
 
 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#_get_tree_reply for documentation on that and an
+https://i3wm.org/docs/ipc.html#_tree_reply for documentation on that and an
 example.
 
 == Files
@@ -96,21 +175,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
@@ -119,10 +195,7 @@ containers, searching containers, getting specific properties from containers,
 
 src/config.c::
 Contains all functions handling the configuration file (calling the parser
-(src/cfgparse.y) with the correct path, switching key bindings mode).
-
-src/debug.c::
-Contains debugging functions to print unhandled X events.
+src/config_parser.c) with the correct path, switching key bindings mode).
 
 src/ewmh.c::
 Functions to get/set certain EWMH properties easily.
@@ -141,7 +214,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.
@@ -174,6 +247,10 @@ values will later be pushed to X11 in +src/x.c+.
 src/resize.c::
 Contains the functions to resize containers.
 
+src/restore_layout.c::
+Everything for restored containers that is not pure state parsing (which can be
+found in load_layout.c).
+
 src/sighandler.c::
 Handles +SIGSEGV+, +SIGABRT+ and +SIGFPE+ by showing a dialog that i3 crashed.
 You can chose to let it dump core, to restart it in-place or to restart it
@@ -185,7 +262,7 @@ cleanup ("flatten") the tree. See also +src/move.c+ for another similar
 function, which was moved into its own file because it is so long.
 
 src/util.c::
-Contains useful functions which are not really dependant on anything.
+Contains useful functions which are not really dependent on anything.
 
 src/window.c::
 Handlers to update X11 window properties like +WM_CLASS+, +_NET_WM_NAME+,
@@ -360,7 +437,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,
@@ -382,7 +459,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").
 
@@ -402,10 +479,14 @@ can reconfigure themselves).
 
 == _NET_WM_STATE
 
-Only the _NET_WM_STATE_FULLSCREEN atom is handled. It calls
-``toggle_fullscreen()'' for the specific client which just configures the
-client to use the whole screen on which it currently is. Also, it is set as
-fullscreen_client for the i3Screen.
+Only the _NET_WM_STATE_FULLSCREEN and _NET_WM_STATE_DEMANDS_ATTENTION atoms
+are handled.
+
+The former calls ``toggle_fullscreen()'' for the specific client which just
+configures the client to use the whole screen on which it currently is.
+Also, it is set as fullscreen_client for the i3Screen.
+
+The latter is used to set, read and display urgency hints.
 
 == WM_NAME
 
@@ -533,7 +614,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.
 
@@ -587,7 +668,7 @@ optimize this and call +x_push_node+ on the appropriate con directly.
    itself for the container’s children. This function actually pushes the
    state, see the next paragraph.
 4. If the pointer needs to be warped to a different position (for example when
-   changing focus to a differnt output), it will be warped now.
+   changing focus to a different output), it will be warped now.
 5. The eventmask is restored for all mapped windows.
 6. Window decorations will be rendered by calling +x_deco_recurse+ on the root
    container, which then recursively calls itself for the children.
@@ -627,8 +708,8 @@ unmapped if it should not be visible anymore. +WM_STATE+ will be set to
 +x_draw_decoration+ draws window decorations. It is run for every leaf
 container (representing an actual X11 window) and for every non-leaf container
 which is in a stacked/tabbed container (because stacked/tabbed containers
-display a window decoration for split containers, which at the moment just says
-"another container").
+display a window decoration for split containers, which consists of a representation
+of the child container's names.
 
 Then, parameters are collected to be able to determine whether this decoration
 drawing is actually necessary or was already done. This saves a substantial
@@ -672,41 +753,130 @@ 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 and the configuration file.
+The input specification for this parser can be found in the file
++parser-specs/*.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>
+# workspace number <number>
+state WORKSPACE:
+  direction = 'next_on_output', 'prev_on_output', 'next', 'prev'
+      -> call cmd_workspace($direction)
+  'back_and_forth'
+      -> call cmd_workspace_back_and_forth()
+  'number'
+      -> WORKSPACE_NUMBER
+  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).
+workspace number <number>::
+        The workspace command has to be followed by the keyword +number+. It
+        then transitions into the state +WORKSPACE_NUMBER+, where the actual
+        parameter will be read.
+
+=== 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
 
@@ -755,7 +925,7 @@ empty.
 Afterwards, +con_focus+ will be called to fix the focus stack and the tree will
 be flattened.
 
-=== Case 3: Moving to non-existant top/bottom
+=== Case 3: Moving to non-existent top/bottom
 
 Like in case 1, the reference layout for this case is a single workspace in
 horizontal orientation with two containers on it. Focus is on the left
@@ -782,7 +952,7 @@ container and the container above/below the current one (on the level of
 Now, +con_focus+ will be called to fix the focus stack and the tree will be
 flattened.
 
-=== Case 4: Moving to existant top/bottom
+=== Case 4: Moving to existent top/bottom
 
 The reference layout for this case is a vertical workspace with two containers.
 The bottom one is a h-split containing two containers (1 and 2). Focus is on
@@ -854,37 +1024,10 @@ 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.
 
-== Using git / sending patches
-
-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
-
-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.
-
-First of all, update your working copy to the latest version of the master
-branch:
-
---------
-git pull
---------
-
-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.
-
-Then, use the following command to generate a patchfile which we can directly
-apply to the branch, preserving your commit message and name:
-
------------------------
-git format-patch origin
------------------------
-
-Just send us the generated file via email.
+* 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.
 
 == Thought experiments
 
@@ -921,7 +1064,7 @@ gets started in any way) and the window(s) which appear.
 
 Imagine for example using dmenu: The user starts dmenu by pressing Mod+d, dmenu
 gets started with PID 3390. The user then decides to launch Firefox, which
-takes a long time. So he enters firefox into dmenu and presses enter. Firefox
+takes a long time. So they enter firefox into dmenu and press enter. Firefox
 gets started with PID 4001. When it finally finishes loading, it creates an X11
 window and uses MapWindow to make it visible. This is the first time i3
 actually gets in touch with Firefox. It decides to map the window, but it has