Merge branch 'next'
This commit is contained in:
commit
fe482cf193
|
@ -7,5 +7,4 @@ AlwaysBreakBeforeMultilineStrings: false
|
|||
IndentWidth: 4
|
||||
PointerBindsToType: false
|
||||
ColumnLimit: 0
|
||||
ForEachMacros: [ TAILQ_FOREACH, TAILQ_FOREACH_REVERSE, SLIST_FOREACH, CIRCLEQ_FOREACH, CIRCLEQ_FOREACH_REVERSE, NODES_FOREACH, NODES_FOREACH_REVERSE ]
|
||||
SpaceBeforeParens: ControlStatements
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
# i3status/i3lock bugreports/feature requests
|
||||
|
||||
Note that i3status and i3lock related bugreports and feature requests should be
|
||||
filed in the corresponding repositories, i.e. https://github.com/i3/i3status
|
||||
and https://github.com/i3/i3lock
|
||||
|
||||
# i3 bugreports/feature requests
|
||||
|
||||
1. Read http://i3wm.org/docs/debugging.html
|
||||
2. Make sure you include a link to your logfile in your report (section 3).
|
||||
3. Make sure you include the i3 version number in your report (section 1).
|
||||
|
||||
# Pull requests
|
||||
|
||||
* Before sending a pull request for new features, please check with us that the
|
||||
feature is something we want to see in i3 by opening an issue which has
|
||||
“feature request” or “enhancement” in its title.
|
||||
* Use the `next` branch for developing and sending your pull request.
|
||||
* Use `clang-format` to format your code.
|
||||
* Run the testsuite, see http://i3wm.org/docs/testsuite.html
|
18
DEPENDS
18
DEPENDS
|
@ -4,12 +4,14 @@
|
|||
"min" means minimum required version
|
||||
"lkgv" means last known good version
|
||||
|
||||
┌─────────────┬────────┬────────┬────────────────────────────────────────┐
|
||||
┌──────────────┬────────┬────────┬────────────────────────────────────────┐
|
||||
│ dependency │ min. │ lkgv │ URL │
|
||||
├─────────────┼────────┼────────┼────────────────────────────────────────┤
|
||||
├──────────────┼────────┼────────┼────────────────────────────────────────┤
|
||||
│ pkg-config │ 0.25 │ 0.26 │ http://pkgconfig.freedesktop.org/ │
|
||||
│ libxcb │ 1.1.93 │ 1.7 │ http://xcb.freedesktop.org/dist/ │
|
||||
│ xcb-util │ 0.3.3 │ 0.3.8 │ http://xcb.freedesktop.org/dist/ │
|
||||
│ libxcb │ 1.1.93 │ 1.10 │ http://xcb.freedesktop.org/dist/ │
|
||||
│ xcb-util │ 0.3.3 │ 0.4.1 │ http://xcb.freedesktop.org/dist/ │
|
||||
│ xkbcommon │ 0.4.0 │ 0.4.0 │ http://xkbcommon.org/ │
|
||||
│ xkbcommon-x11│ 0.4.0 │ 0.4.0 │ http://xkbcommon.org/ │
|
||||
│ util-cursor³ │ 0.0.99 │ 0.0.99 │ http://xcb.freedesktop.org/dist/ │
|
||||
│ libev │ 4.0 │ 4.11 │ http://libev.schmorp.de/ │
|
||||
│ yajl │ 2.0.1 │ 2.0.4 │ http://lloyd.github.com/yajl/ │
|
||||
|
@ -22,7 +24,7 @@
|
|||
│ libsn¹ │ 0.10 │ 0.12 │ http://freedesktop.org/wiki/Software/startup-notification
|
||||
│ pango │ 1.30.0 | 1.30.0 │ http://www.pango.org/ │
|
||||
│ cairo │ 1.12.2 │ 1.12.2 │ http://cairographics.org/ │
|
||||
└─────────────┴────────┴────────┴────────────────────────────────────────┘
|
||||
└──────────────┴────────┴────────┴────────────────────────────────────────┘
|
||||
¹ libsn = libstartup-notification
|
||||
² Pod::Simple is a Perl module required for converting the testsuite
|
||||
documentation to HTML. See http://michael.stapelberg.de/cpan/#Pod::Simple
|
||||
|
@ -35,6 +37,6 @@
|
|||
i3-migrate-config-to-v4 and i3-dmenu-desktop are implemented in Perl, but have
|
||||
no dependencies besides Perl 5.10.
|
||||
|
||||
i3-save-tree is also implemented in Perl and needs AnyEvent::I3 and JSON::XS.
|
||||
While i3-save-tree is not required for running i3 itself, it is strongly
|
||||
recommended to provide it in distribution packages.
|
||||
i3-save-tree is also implemented in Perl and needs AnyEvent::I3 (>= 0.12) and
|
||||
JSON::XS. While i3-save-tree is not required for running i3 itself, it is
|
||||
strongly recommended to provide it in distribution packages.
|
||||
|
|
|
@ -1,138 +0,0 @@
|
|||
|
||||
┌──────────────────────────────┐
|
||||
│ Release notes for i3 v4.8 │
|
||||
└──────────────────────────────┘
|
||||
|
||||
This is i3 v4.8. This version is considered stable. All users of i3 are
|
||||
strongly encouraged to upgrade.
|
||||
|
||||
The biggest new feature certainly is layout saving/restoring. See
|
||||
http://i3wm.org/docs/layout-saving.html for more details. tl;dr: export your
|
||||
current layout as JSON file, load it into new i3 sessions, get placeholder
|
||||
windows that will be replaced by the actual apps once you start them.
|
||||
|
||||
Also very important for owners of HiDPI/“retina” displays is that i3 will now
|
||||
respect your configured DPI and scale up its UI elements accordingly. Use
|
||||
“xrandr --dpi 184” to set your dpi to 184, in case your setup does not figure
|
||||
it out automatically. To get properly scaling fonts, we also changed the
|
||||
default font from a bitmap font to a pango font (“DejaVu Sans Mono 8”).
|
||||
|
||||
Multiple changes improve the compatibility of i3 with other software, e.g.
|
||||
java-based software (focus handling, once again) or external pagers (we now
|
||||
provide _NET_CLIENT_LIST and let pager applications change workspaces).
|
||||
|
||||
For packagers, another change is that yajl ≥ 2.0 is now required for compiling
|
||||
i3. This should not be a problem for anyone, as that version is pretty old by
|
||||
now.
|
||||
|
||||
For contributors, note that we have starting formatting the source code with
|
||||
clang-format-3.5. This means that there will no longer be a need to argue about
|
||||
coding style when discussing patches :).
|
||||
|
||||
┌────────────────────────────┐
|
||||
│ Changes in v4.8 │
|
||||
└────────────────────────────┘
|
||||
|
||||
• docs/ipc: reformat/update list of ipc libraries
|
||||
• docs/ipc: fix current_workspace outputs reply member
|
||||
• docs/ipc: update ipc COMMAND reply docs
|
||||
• docs/userguide: fix multiple typos
|
||||
• docs/debugging: use bzip2
|
||||
• docs/debugging: explain how to enable logging on the fly
|
||||
• docs/debugging: merge the debug symbols/backtrace section
|
||||
• docs/debugging: recommend i3 --moreversion
|
||||
• man/i3-nagbar.man: update manpage to document all options
|
||||
• i3bar: Amend status line error 127 message
|
||||
• i3bar: don’t kill watcher on EOF, leads to better error messages
|
||||
• i3bar: send mouse wheel events to child too
|
||||
• i3bar: do click handling and tray padding retina-correctly
|
||||
• i3bar: render separators render-correctly
|
||||
• i3bar: reinit colors on barconfig update
|
||||
• i3bar: Don't start child unless status_command
|
||||
• i3bar: implement custom workspace numbers config
|
||||
• resize floating windows when right-clicking the decoration
|
||||
• enable shmlog when invoked as i3-with-shmlog
|
||||
• Disable pointer warps when focus_follows_mouse is disabled
|
||||
• Movement into a branch considers movement direction
|
||||
• set ewmh desktop properties on startup
|
||||
• handle ButtonPress events with child != XCB_NONE
|
||||
• implement layout restoring
|
||||
• only LOG() the DPI when it changes, DLOG() it otherwise
|
||||
• send IPC window events for focus and title changes
|
||||
• these types of windows are now floating by default:
|
||||
dialog, utility, toolbar and splash windows, modal windows, windows with an
|
||||
equal minimum and maximum size
|
||||
• send last event timestamp with WM_TAKE_FOCUS message
|
||||
• maintain the _NET_CLIENT_LIST property
|
||||
• don’t set input focus _and_ send WM_TAKE_FOCUS
|
||||
• respect CFLAGS in linking command
|
||||
• fix parallel make
|
||||
• reset SIGPIPE handler before executing a command
|
||||
• render default window border width retina-correctly
|
||||
• draw workspace buttons and padded text blocks retina-correctly
|
||||
• render resize windows retina-correctly
|
||||
• delegate click handling to dock clients
|
||||
• send complete config on barconfig_update
|
||||
• implement the window::fullscreen_mode ipc event
|
||||
• make all workspaces starting with "__" internal
|
||||
• improve error messages for i3-internal workspace names
|
||||
• allow _NET_ACTIVE_WINDOW requests to switch workspaces if they indicate
|
||||
that they are a pager (following the spec)
|
||||
• workspace assignments by number
|
||||
• add configuration option for disabling mouse warping
|
||||
• set _NET_ACTIVE_WINDOW to None when none has focus
|
||||
• set X-LightDM-DesktopName in i3.xsession.desktop to fix autostart on Ubuntu
|
||||
• don’t ELOG ipc EOF
|
||||
• replace all printf()s with D?LOG
|
||||
• delete ipc socket when exiting, cleanup tmpdir
|
||||
• default config: switch to DejaVu Sans Mono 8 as default font
|
||||
• cleanup tmpdir when restarting and not using XDG_RUNTIME_DIR
|
||||
• Snap pointer to resize bar on drag resize
|
||||
• Size resizebar according to container size
|
||||
• Fix clang -Wextra except -Wunused-parameter
|
||||
• Respect Motif hint for window decorations
|
||||
|
||||
┌────────────────────────────┐
|
||||
│ Bugfixes │
|
||||
└────────────────────────────┘
|
||||
|
||||
• create con pixmaps when not needed
|
||||
• i3bar: fix resource leak: statusline_ctx needs to be freed first
|
||||
• tree_split should not split floating cons
|
||||
• fix memory leak with ipc_receive_message
|
||||
• fix invalid reads by setting con->window to NULL in tree_close
|
||||
• fix memory leak when closing windows
|
||||
• fix memory leak when matching window by criteria
|
||||
• fix memory leak when matching window by con_id
|
||||
• ignore dock clients in the resize command
|
||||
• clear wm_size_hints if they are not set
|
||||
• resize window check should check for NULL
|
||||
• fix window event crash with no window
|
||||
• i3-dmenu-desktop: also quote the %c field code
|
||||
• new_window and new_float can now be used simultaneously with different
|
||||
border widths
|
||||
• fix crash when using multiple for_window statements that move windows
|
||||
• Set input focus with last timestamp
|
||||
• handle windows whose WM_TRANSIENT_FOR points to themselve
|
||||
• don’t overwrite the original size of floating windows when changing border
|
||||
• don’t errnously render floating fullscreen windows during restart
|
||||
• ensure floating windows don’t drop out of fullscreen when restarting
|
||||
• don’t overwrite the window’s geometry after restartingnext
|
||||
• i3bar: Set `mapped` flag on trayclient creation
|
||||
• i3bar: don't show "EOF" status line error
|
||||
|
||||
┌────────────────────────────┐
|
||||
│ Thanks! │
|
||||
└────────────────────────────┘
|
||||
|
||||
Thanks for testing, bugfixes, discussions and everything I forgot go out to:
|
||||
|
||||
Aleksi Blinnikka, Alexander Berntsen, Alexander Kedrik, Antonio, Arun
|
||||
Persaud, Atte Peltomaki, bo, Campbell Barton, chris, David Coppa, eeemsi,
|
||||
Holger Langenau, Jean-Philippe Ouellet, Jens, jeroentbt, Jonas Maaskola,
|
||||
Julian Ospald, Kernc, Koston, lasers, lkraav, Marcin, Marco Hunsicker,
|
||||
Marcus Crestani, Matthias Thubauville, Maxime, Michael Stapelberg, Peter
|
||||
Boström, Petr Písař, Quentin Glidic, Steve Jones, TonyC, Tony Crisci,
|
||||
Vivien Didelot, Wieland Hoffmann, x33a, xeen
|
||||
|
||||
-- Michael Stapelberg, 2014-06-15
|
|
@ -0,0 +1,124 @@
|
|||
|
||||
┌──────────────────────────────┐
|
||||
│ Release notes for i3 v4.9 │
|
||||
└──────────────────────────────┘
|
||||
|
||||
This is i3 v4.9. This version is considered stable. All users of i3 are
|
||||
strongly encouraged to upgrade.
|
||||
|
||||
Notable new features include mouse button bindings and improved EWMH
|
||||
compatibility, meaning more external pager programs work with i3 now.
|
||||
|
||||
Aside from that, this release contains plenty of bugfixes and little
|
||||
enhancements.
|
||||
|
||||
The new dependency on libxkbcommon ≥ 0.4.0 is notable for distribution
|
||||
packages. This dependency allowed us to drop our last direct dependency
|
||||
on Xlib :).
|
||||
|
||||
It’s also worth mentioning that all i3 repositories are now on GitHub, see
|
||||
http://thread.gmane.org/gmane.comp.window-managers.i3.general/1666 for the
|
||||
announcement.
|
||||
|
||||
┌────────────────────────────┐
|
||||
│ Changes in v4.9 │
|
||||
└────────────────────────────┘
|
||||
|
||||
• docs/ipc: use an actual event type
|
||||
• docs/debugging: use logs.i3wm.org
|
||||
• docs/testsuite: add hint to use xvfb-run
|
||||
• testcases: use Xephyr instead of XDummy
|
||||
• i3-sensible-*: use command -v (built-in) instead of which(1)
|
||||
• i3.xsession.desktop: set DesktopNames (which gdm uses)
|
||||
• i3-save-tree: interpret commandline parameters as utf-8
|
||||
• i3-save-tree: add 'mark' as allowed key to i3-save-tree output
|
||||
• i3bar-protocol: ensure align = left is the default
|
||||
• i3bar: implement custom mouse wheel commands
|
||||
• i3bar: improve error message when a full_text property is missing
|
||||
• i3bar: respect the urgency flag on status blocks
|
||||
• i3bar: inset the urgent background of a status block for consistency with
|
||||
workspace buttons
|
||||
• i3bar: suspend the child when bars are fully obscured
|
||||
• i3bar: use Pango markup
|
||||
• ipc: implement the window::close event
|
||||
• ipc: implement the window::move event
|
||||
• ipc: implement the window::floating event
|
||||
• ipc: implement the window::urgent event
|
||||
• ipc: set ws reply "num" member to -1 when named
|
||||
• ipc: add deco_rect property to con in ipc response
|
||||
• ipc: include workspace con in workspace event
|
||||
• ewmh: implement property _NET_NUMBER_OF_DESKTOPS
|
||||
• ewmh: implement property _NET_DESKTOP_VIEWPORT
|
||||
• ewmh: implement property _NET_DESKTOP_NAMES
|
||||
• ewmh: handle _NET_CURRENT_DESKTOP requests
|
||||
• ewmh: handle _NET_CLOSE_WINDOW requests
|
||||
• ewmh: handle _NET_WM_MOVERESIZE requests
|
||||
• implement mouse bindings (e.g. bindsym button3 kill)
|
||||
• add mouse binding --whole-window flag
|
||||
• add mouse binding --release flag
|
||||
• switch to xcb-xkb and libxkbcommon, removing our last direct Xlib dep
|
||||
• make “move [direction]” work with criteria
|
||||
• make “move <window|container> to <absolute> position” work with criteria
|
||||
• “workspace <n>” and “move to workspace <n>” now look for a workspace
|
||||
starting with number <n> (unless there is a workspace exactly matching that
|
||||
number). I.e., “workspace 4” will go to a workspace called “4: www” unless
|
||||
you have a workspace “4”
|
||||
• “focus <direction>” now focuses floating containers when there are no
|
||||
tiling containers on the destination output
|
||||
• take the motif border into account when calculating floating window
|
||||
geometry
|
||||
• revert “Disable pointer warps when focus_follows_mouse is disabled” as it
|
||||
was unexpected by a number of users. Sorry for the back-and-forth
|
||||
• handle WM_CLASS changes
|
||||
• raise floating windows on “focus <direction>”
|
||||
• align lower line of bar decoration to border width
|
||||
• parse tray_output as a word, not string
|
||||
• allow to validate the config file without X
|
||||
• do not resend focus on click, fixes compatibility problems with some wine
|
||||
or mono apps (e.g. Office 2010)
|
||||
• don't draw borders wider than actual width
|
||||
• prevent workspace change during global fullscreen
|
||||
• extend the fullscreen command (fullscreen <enable|toggle|disable> [global])
|
||||
• fix start_application() doc about which shell is used
|
||||
|
||||
┌────────────────────────────┐
|
||||
│ Bugfixes │
|
||||
└────────────────────────────┘
|
||||
|
||||
• i3-dmenu-desktop: quote path
|
||||
• i3bar: fix a double free when changing color configuration
|
||||
• i3bar: render bars after the first chunk of JSON
|
||||
• i3bar: add a sync call to confirm reparents before exiting (fixes tray
|
||||
restart issues)
|
||||
• i3bar: correctly calculate clicks on i3bar status blocks
|
||||
• i3bar: make click events on status blocks work with 'workspace_buttons no'
|
||||
• retina support: convert logical to physical pixels for default_border_width
|
||||
• retina support: treat everything up to 120 dpi as 96 dpi
|
||||
• don’t set input focus if not accepted (fixes problems with xfce4-notifyd)
|
||||
• don’t focus unmapped container on manage
|
||||
• create the directory for storing the restart state
|
||||
• avoid changing border width when changing containers from tiling to
|
||||
floating
|
||||
• layout saving: properly restore workspace containers
|
||||
• rerender the decoration when the container requires a pixmap and doesn’t
|
||||
have one
|
||||
• don’t set focus in con_set_layout() on invisible workspaces
|
||||
• properly handle windows unsetting WM_TRANSIENT_FOR
|
||||
• use the command parser to properly extract workspace names
|
||||
• copy binding before run (fixes reloads)
|
||||
• revert "Bugfix: Set input focus with last timestamp"
|
||||
• render floating windows during global fullscreen
|
||||
• actually parse client.placeholder
|
||||
|
||||
┌────────────────────────────┐
|
||||
│ Thanks! │
|
||||
└────────────────────────────┘
|
||||
|
||||
Thanks for testing, bugfixes, discussions and everything I forgot go out to:
|
||||
|
||||
Alexander Monakov, aszlig, cornerman, dmurph, Mats, dsargrad, hercek, hjem,
|
||||
Ingo, Ingo Bürk, Janus, javier, jefvel, Lukas K, Marein Konings, Mats,
|
||||
Michael Stapelberg, Mii, nikolaus, okraits, Peter, smlb, sur5r, Tony Crisci,
|
||||
val, vals, xeen, Yves-Alexis
|
||||
|
||||
-- Michael Stapelberg, 2015-02-28
|
|
@ -92,6 +92,7 @@ else
|
|||
XCB_CFLAGS += $(call cflags_for_lib, xcb-util)
|
||||
XCB_LIBS += $(call ldflags_for_lib, xcb-util)
|
||||
endif
|
||||
XCB_XKB_LIBS := $(call ldflags_for_lib, xcb-xkb,xcb-xkb)
|
||||
|
||||
# XCB keyboard stuff
|
||||
XCB_KBD_CFLAGS := $(call cflags_for_lib, xcb-keysyms)
|
||||
|
@ -105,9 +106,10 @@ XCB_WM_LIBS := $(call ldflags_for_lib, xcb-icccm,xcb-icccm)
|
|||
XCB_WM_LIBS += $(call ldflags_for_lib, xcb-xinerama,xcb-xinerama)
|
||||
XCB_WM_LIBS += $(call ldflags_for_lib, xcb-randr,xcb-randr)
|
||||
|
||||
# Xlib
|
||||
X11_CFLAGS := $(call cflags_for_lib, x11)
|
||||
X11_LIBS := $(call ldflags_for_lib, x11,X11)
|
||||
XKB_COMMON_CFLAGS := $(call cflags_for_lib, xkbcommon,xkbcommon)
|
||||
XKB_COMMON_LIBS := $(call ldflags_for_lib, xkbcommon,xkbcommon)
|
||||
XKB_COMMON_X11_CFLAGS := $(call cflags_for_lib, xkbcommon-x11,xkbcommon-x11)
|
||||
XKB_COMMON_X11_LIBS := $(call ldflags_for_lib, xkbcommon-x11,xkbcommon-x11)
|
||||
|
||||
# Xcursor
|
||||
XCURSOR_CFLAGS := $(call cflags_for_lib, xcb-cursor)
|
||||
|
|
|
@ -1,8 +1,23 @@
|
|||
i3-wm (4.7.3-1) unstable; urgency=low
|
||||
i3-wm (4.8.1-1) unstable; urgency=medium
|
||||
|
||||
* NOT YET RELEASED
|
||||
|
||||
-- Michael Stapelberg <stapelberg@debian.org> Thu, 23 Jan 2014 23:11:48 +0100
|
||||
-- Michael Stapelberg <stapelberg@debian.org> Sun, 15 Jun 2014 19:37:32 +0200
|
||||
|
||||
i3-wm (4.8-2) unstable; urgency=medium
|
||||
|
||||
* Backport two bugfixes:
|
||||
- backport-dpi-fix.patch (Closes: #778460)
|
||||
- backport-i3bar-tray-fix.patch (Closes: #778461)
|
||||
|
||||
-- Michael Stapelberg <stapelberg@debian.org> Sun, 15 Feb 2015 13:24:42 +0100
|
||||
|
||||
i3-wm (4.8-1) unstable; urgency=medium
|
||||
|
||||
* New upstream release.
|
||||
* Bump standards-version to 3.9.5 (no changes necessary)
|
||||
|
||||
-- Michael Stapelberg <stapelberg@debian.org> Sun, 15 Jun 2014 19:15:29 +0200
|
||||
|
||||
i3-wm (4.7.2-1) unstable; urgency=low
|
||||
|
||||
|
|
|
@ -10,6 +10,9 @@ Build-Depends: debhelper (>= 7.0.50~),
|
|||
libxcb-randr0-dev,
|
||||
libxcb-icccm4-dev,
|
||||
libxcb-cursor-dev,
|
||||
libxcb-xkb-dev,
|
||||
libxkbcommon-dev (>= 0.4.0),
|
||||
libxkbcommon-x11-dev (>= 0.4.0),
|
||||
asciidoc (>= 8.4.4),
|
||||
xmlto,
|
||||
docbook-xml,
|
||||
|
@ -21,7 +24,7 @@ Build-Depends: debhelper (>= 7.0.50~),
|
|||
libcairo2-dev,
|
||||
libpango1.0-dev,
|
||||
libpod-simple-perl
|
||||
Standards-Version: 3.9.4
|
||||
Standards-Version: 3.9.5
|
||||
Homepage: http://i3wm.org/
|
||||
|
||||
Package: i3
|
||||
|
@ -38,7 +41,7 @@ Architecture: any
|
|||
Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}, x11-utils
|
||||
Provides: x-window-manager
|
||||
Suggests: rxvt-unicode | x-terminal-emulator
|
||||
Recommends: xfonts-base, fonts-dejavu-core, libanyevent-i3-perl, libjson-xs-perl
|
||||
Recommends: xfonts-base, fonts-dejavu-core, libanyevent-i3-perl (>= 0.12), libjson-xs-perl
|
||||
Description: improved dynamic tiling window manager
|
||||
Key features of i3 are good documentation, reasonable defaults (changeable in
|
||||
a simple configuration file) and good multi-monitor support. The user
|
||||
|
|
|
@ -9,4 +9,5 @@ man/i3-sensible-pager.1
|
|||
man/i3-sensible-editor.1
|
||||
man/i3-sensible-terminal.1
|
||||
man/i3-dmenu-desktop.1
|
||||
man/i3-save-tree.1
|
||||
man/i3bar.1
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
Description: list x-terminal-emulator as one of i3-sensible-terminal’s choices
|
||||
Author: Michael Stapelberg <stapelberg@debian.org>
|
||||
Origin: vendor
|
||||
Forwarded: not-needed
|
||||
Last-Update: 2011-12-28
|
||||
|
||||
---
|
||||
|
||||
Index: i3-4.1.1/man/i3-sensible-terminal.man
|
||||
===================================================================
|
||||
--- i3-4.1.1.orig/man/i3-sensible-terminal.man 2011-12-28 23:56:55.487581000 +0100
|
||||
+++ i3-4.1.1/man/i3-sensible-terminal.man 2011-12-28 23:57:06.725802633 +0100
|
||||
@@ -22,6 +22,7 @@
|
||||
It tries to start one of the following (in that order):
|
||||
|
||||
* $TERMINAL (this is a non-standard variable)
|
||||
+* x-terminal-emulator
|
||||
* urxvt
|
||||
* rxvt
|
||||
* terminator
|
|
@ -1,2 +0,0 @@
|
|||
use-x-terminal-emulator.patch
|
||||
manpage-x-terminal-emulator.patch
|
|
@ -1,25 +0,0 @@
|
|||
Description: i3-sensible-terminal: try x-terminal-emulator first
|
||||
Author: Michael Stapelberg <stapelberg@debian.org>
|
||||
Origin: vendor
|
||||
Forwarded: not-needed
|
||||
Last-Update: 2012-09-19
|
||||
|
||||
---
|
||||
|
||||
Index: i3-4.3/i3-sensible-terminal
|
||||
===================================================================
|
||||
--- i3-4.3.orig/i3-sensible-terminal 2012-09-19 18:08:09.000000000 +0200
|
||||
+++ i3-4.3/i3-sensible-terminal 2012-09-19 18:32:06.393883488 +0200
|
||||
@@ -4,11 +4,7 @@
|
||||
#
|
||||
# This script tries to exec a terminal emulator by trying some known terminal
|
||||
# emulators.
|
||||
-#
|
||||
-# Distributions/packagers should enhance this script with a
|
||||
-# distribution-specific mechanism to find the preferred terminal emulator. On
|
||||
-# Debian, there is the x-terminal-emulator symlink for example.
|
||||
-for terminal in $TERMINAL urxvt rxvt terminator Eterm aterm xterm gnome-terminal roxterm xfce4-terminal; do
|
||||
+for terminal in $TERMINAL x-terminal-emulator urxvt rxvt terminator Eterm aterm xterm gnome-terminal roxterm xfce4-terminal; do
|
||||
if which $terminal > /dev/null 2>&1; then
|
||||
exec $terminal "$@"
|
||||
fi
|
|
@ -72,15 +72,17 @@ i3-msg 'debuglog on; shmlog on; reload'
|
|||
No matter whether i3 misbehaved in some way without crashing or whether it just
|
||||
crashed, the logfile provides all information necessary to debug the problem.
|
||||
|
||||
To save a compressed version of the logfile (suitable for attaching it to a
|
||||
bugreport), use:
|
||||
--------------------------------------------------------------------
|
||||
DISPLAY=:0 i3-dump-log | bzip2 -c > /tmp/i3.log.bz2
|
||||
--------------------------------------------------------------------
|
||||
To upload a compressed version of the logfile (for a bugreport), use:
|
||||
------------------------------------------------------------------------------
|
||||
DISPLAY=:0 i3-dump-log | bzip2 -c | curl --data-binary @- http://logs.i3wm.org
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
This command does not depend on i3 (it also works while i3 displays
|
||||
the crash dialog), but it requires a working X11 connection.
|
||||
|
||||
After running it, you will get a URL to the logfile. Please include that URL in
|
||||
your bug report.
|
||||
|
||||
== On crashes: Obtaining a backtrace
|
||||
|
||||
When i3 crashes, it will display a dialog stating “i3 just crashed”, offering
|
||||
|
|
|
@ -956,10 +956,10 @@ 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.
|
||||
|
||||
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.
|
||||
it in the corresponding bugreport at https://github.com/i3/i3/issues In case
|
||||
there is no bugreport yet, please create one.
|
||||
|
||||
After you are done, please submit your work for review at http://cr.i3wm.org/
|
||||
After you are done, please submit your work for review 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
|
||||
|
|
|
@ -119,7 +119,8 @@ click_events::
|
|||
full_text::
|
||||
The most simple block you can think of is one which just includes the
|
||||
only required key, the +full_text+ key. i3bar will display the string
|
||||
value and that’s it.
|
||||
value parsed as
|
||||
https://developer.gnome.org/pango/stable/PangoMarkupFormat.html[Pango markup].
|
||||
short_text::
|
||||
Where appropriate, the +short_text+ (string) entry should also be
|
||||
provided. It will be used in case the status line needs to be shortened
|
||||
|
@ -148,7 +149,7 @@ min_width::
|
|||
when you want to set a sensible minimum width regardless of which font you
|
||||
are using, and at what particular size.
|
||||
align::
|
||||
Align text on the +center+ (default), +right+ or +left+ of the block, when
|
||||
Align text on the +center+, +right+ or +left+ (default) of the block, when
|
||||
the minimum width of the latter, specified by the +min_width+ key, is not
|
||||
reached.
|
||||
name and instance::
|
||||
|
|
84
docs/ipc
84
docs/ipc
|
@ -1,7 +1,7 @@
|
|||
IPC interface (interprocess communication)
|
||||
==========================================
|
||||
Michael Stapelberg <michael@i3wm.org>
|
||||
February 2014
|
||||
October 2014
|
||||
|
||||
This document describes how to interface with i3 from a separate process. This
|
||||
is useful for example to remote-control i3 (to write test cases for example) or
|
||||
|
@ -156,7 +156,7 @@ following properties:
|
|||
|
||||
num (integer)::
|
||||
The logical number of the workspace. Corresponds to the command
|
||||
to switch to this workspace.
|
||||
to switch to this workspace. For named workspaces, this will be -1.
|
||||
name (string)::
|
||||
The name of this workspace (by default num+1), as changed by the
|
||||
user. Encoded in UTF-8.
|
||||
|
@ -316,6 +316,10 @@ window_rect (map)::
|
|||
So, when using the +default+ layout, you will have a 2 pixel border on
|
||||
each side, making the window_rect +{ "x": 2, "y": 0, "width": 632,
|
||||
"height": 366 }+ (for example).
|
||||
deco_rect (map)::
|
||||
The coordinates of the *window decoration* inside its container. These
|
||||
coordinates are relative to the container and do not include the actual
|
||||
client window.
|
||||
geometry (map)::
|
||||
The original geometry the window specified when i3 mapped it. Used when
|
||||
switching a window to floating mode, for example.
|
||||
|
@ -613,7 +617,7 @@ you can register to an event.
|
|||
*Example:*
|
||||
---------------------------------
|
||||
type: SUBSCRIBE
|
||||
payload: [ "workspace", "focus" ]
|
||||
payload: [ "workspace", "output" ]
|
||||
---------------------------------
|
||||
|
||||
|
||||
|
@ -638,6 +642,9 @@ window (3)::
|
|||
barconfig_update (4)::
|
||||
Sent when the hidden_state or mode field in the barconfig of any bar
|
||||
instance was updated and when the config is reloaded.
|
||||
binding (5)::
|
||||
Sent when a configured command binding is triggered with the keyboard or
|
||||
mouse
|
||||
|
||||
*Example:*
|
||||
--------------------------------------------------------------------
|
||||
|
@ -661,15 +668,16 @@ if ($is_event) {
|
|||
|
||||
This event consists of a single serialized map containing a property
|
||||
+change (string)+ which indicates the type of the change ("focus", "init",
|
||||
"empty", "urgent").
|
||||
"empty", "urgent"). A +current (object)+ property will be present with the
|
||||
affected workspace whenever the type of event affects a workspace (otherwise,
|
||||
it will be +null).
|
||||
|
||||
Moreover, when the change is "focus", an +old (object)+ and a +current
|
||||
(object)+ properties will be present with the previous and current
|
||||
workspace respectively. When the first switch occurs (when i3 focuses
|
||||
the workspace visible at the beginning) there is no previous
|
||||
workspace, and the +old+ property will be set to +null+. Also note
|
||||
that if the previous is empty it will get destroyed when switching,
|
||||
but will still be present in the "old" property.
|
||||
When the change is "focus", an +old (object)+ property will be present with the
|
||||
previous workspace. When the first switch occurs (when i3 focuses the
|
||||
workspace visible at the beginning) there is no previous workspace, and the
|
||||
+old+ property will be set to +null+. Also note that if the previous is empty
|
||||
it will get destroyed when switching, but will still be present in the "old"
|
||||
property.
|
||||
|
||||
*Example:*
|
||||
---------------------
|
||||
|
@ -717,9 +725,13 @@ This event consists of a single serialized map containing a property
|
|||
+change (string)+ which indicates the type of the change
|
||||
|
||||
* +new+ - the window has become managed by i3
|
||||
* +close+ - the window has closed
|
||||
* +focus+ - the window has received input focus
|
||||
* +title+ - the window's title has changed
|
||||
* +fullscreen_mode+ - the window has entered or exited fullscreen mode
|
||||
* +move+ - the window has changed its position in the tree
|
||||
* +floating+ - the window has transitioned to or from floating
|
||||
* +urgent+ - the window has become urgent or lost its urgent status
|
||||
|
||||
Additionally a +container (object)+ field will be present, which consists
|
||||
of the window's parent container. Be aware that for the "new" event, the
|
||||
|
@ -745,6 +757,47 @@ This event consists of a single serialized map reporting on options from the
|
|||
barconfig of the specified bar_id that were updated in i3. This event is the
|
||||
same as a +GET_BAR_CONFIG+ reply for the bar with the given id.
|
||||
|
||||
=== binding event
|
||||
|
||||
This event consists of a single serialized map reporting on the details of a
|
||||
binding that ran a command because of user input. The +change (sring)+ field
|
||||
indicates what sort of binding event was triggered (right now it will always be
|
||||
+"run"+ but may be expanded in the future).
|
||||
|
||||
The +binding (object)+ field contains details about the binding that was run:
|
||||
|
||||
command (string)::
|
||||
The i3 command that is configured to run for this binding.
|
||||
mods (array of strings)::
|
||||
The modifier keys that were configured with this binding.
|
||||
input_code (integer)::
|
||||
If the binding was configured with +bindcode+, this will be the key code
|
||||
that was given for the binding. If the binding is a mouse binding, it will be
|
||||
the number of the mouse button that was pressed. Otherwise it will be 0.
|
||||
symbol (string or null)::
|
||||
If this is a keyboard binding that was configured with +bindsym+, this
|
||||
field will contain the given symbol. Otherwise it will be +null+.
|
||||
input_type (string)::
|
||||
This will be +"keyboard"+ or +"mouse"+ depending on whether or not this was
|
||||
a keyboard or a mouse binding.
|
||||
|
||||
*Example:*
|
||||
---------------------------
|
||||
{
|
||||
"change": "run",
|
||||
"binding": {
|
||||
"command": "nop",
|
||||
"mods": [
|
||||
"shift",
|
||||
"ctrl"
|
||||
],
|
||||
"input_code": 0,
|
||||
"symbol": "t",
|
||||
"input_type": "keyboard"
|
||||
}
|
||||
}
|
||||
---------------------------
|
||||
|
||||
== See also (existing libraries)
|
||||
|
||||
[[libraries]]
|
||||
|
@ -754,15 +807,14 @@ all this on your own). This list names some (if you wrote one, please let me
|
|||
know):
|
||||
|
||||
C::
|
||||
i3 includes a headerfile +i3/ipc.h+ which provides you all constants.
|
||||
|
||||
https://github.com/acrisci/i3ipc-glib
|
||||
* i3 includes a headerfile +i3/ipc.h+ which provides you all constants.
|
||||
* https://github.com/acrisci/i3ipc-glib
|
||||
Go::
|
||||
* https://github.com/proxypoke/i3ipc
|
||||
JavaScript::
|
||||
* https://github.com/acrisci/i3ipc-gjs
|
||||
Lua::
|
||||
* https:/github.com/acrisci/i3ipc-lua
|
||||
* https://github.com/acrisci/i3ipc-lua
|
||||
Perl::
|
||||
* https://metacpan.org/module/AnyEvent::I3
|
||||
Python::
|
||||
|
@ -770,4 +822,4 @@ Python::
|
|||
* https://github.com/whitelynx/i3ipc (not maintained)
|
||||
* https://github.com/ziberna/i3-py (not maintained)
|
||||
Ruby::
|
||||
http://github.com/badboy/i3-ipc
|
||||
* http://github.com/badboy/i3-ipc
|
||||
|
|
|
@ -74,6 +74,9 @@ client, simply called +cpan+. It comes with every Perl installation and can be
|
|||
used to install the testsuite. Many users prefer to use the more modern
|
||||
+cpanminus+ instead, though (because it asks no questions and just works):
|
||||
|
||||
The tests additionally require +Xephyr(1)+ to run a nested X server. Install
|
||||
+xserver-xephyr+ on Debian or +xorg-xserver-xephyr+ on Arch Linux.
|
||||
|
||||
.Installing testsuite dependencies using cpanminus (preferred)
|
||||
--------------------------------------------------------------------------------
|
||||
$ cd ~/i3/testcases
|
||||
|
@ -102,7 +105,12 @@ more testcases. Also, it takes care of starting up a separate instance of i3
|
|||
with an appropriate configuration file and creates a folder for each run
|
||||
containing the appropriate i3 logfile for each testcase. The latest folder can
|
||||
always be found under the symlink +latest/+. Unless told differently, it will
|
||||
run the tests on a separate X server instance (using the Xdummy script).
|
||||
run the tests on a separate X server instance (using Xephyr).
|
||||
|
||||
Xephyr will open a window where you can inspect the running test. You can run
|
||||
the tests without an X session with Xvfb, such as with +xvfb-run
|
||||
./complete-run+. This will also speed up the tests signficantly especially on
|
||||
machines without a powerful video card.
|
||||
|
||||
.Example invocation of complete-run.pl+
|
||||
---------------------------------------
|
||||
|
@ -146,12 +154,11 @@ $ 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
|
||||
$ ./complete-run.pl --parallel=1 --keep-xserver-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+.
|
||||
This will show the output of Xephyr, which is the X server implementation we
|
||||
use for testing.
|
||||
|
||||
==== IPC interface
|
||||
|
||||
|
@ -175,10 +182,9 @@ manager.
|
|||
=== Filesystem structure
|
||||
|
||||
In the git root of i3, the testcases live in the folder +testcases+. This
|
||||
folder contains the +complete-run.pl+ and +Xdummy+ scripts and a base
|
||||
configuration file which will be used for the tests. The different testcases
|
||||
(their file extension is .t, not .pl) themselves can be found in the
|
||||
conventionally named subfolder +t+:
|
||||
folder contains the +complete-run.pl+ and a base configuration file which will
|
||||
be used for the tests. The different testcases (their file extension is .t, not
|
||||
.pl) themselves can be found in the conventionally named subfolder +t+:
|
||||
|
||||
.Filesystem structure
|
||||
--------------------------------------------
|
||||
|
@ -197,7 +203,6 @@ conventionally named subfolder +t+:
|
|||
│ │ ├── omitted for brevity
|
||||
│ │ ├── ...
|
||||
│ │ └── 74-regress-focus-toggle.t
|
||||
│ └── Xdummy
|
||||
--------------------------------------------
|
||||
|
||||
== Anatomy of a testcase
|
||||
|
|
|
@ -91,7 +91,7 @@ To display a window in fullscreen mode or to go out of fullscreen mode again,
|
|||
press +$mod+f+.
|
||||
|
||||
There is also a global fullscreen mode in i3 in which the client will span all
|
||||
available outputs (the command is +fullscreen global+).
|
||||
available outputs (the command is +fullscreen toggle global+).
|
||||
|
||||
=== Opening other applications
|
||||
|
||||
|
@ -153,6 +153,7 @@ to upgrade to a newer version of i3) you can use +$mod+Shift+r+.
|
|||
=== Exiting i3
|
||||
|
||||
To cleanly exit i3 without killing your X server, you can use +$mod+Shift+e+.
|
||||
By default, a dialog will ask you to confirm if you really want to quit.
|
||||
|
||||
=== Floating
|
||||
|
||||
|
@ -366,7 +367,7 @@ bindcode [--release] [Modifiers+]keycode command
|
|||
*Examples*:
|
||||
--------------------------------
|
||||
# Fullscreen
|
||||
bindsym $mod+f fullscreen
|
||||
bindsym $mod+f fullscreen toggle
|
||||
|
||||
# Restart
|
||||
bindsym $mod+Shift+r restart
|
||||
|
@ -393,6 +394,41 @@ umlauts or special characters 'and' having some comfortably reachable key
|
|||
bindings. For example, when typing, capslock+1 or capslock+2 for switching
|
||||
workspaces is totally convenient. Try it :-).
|
||||
|
||||
[[mousebindings]]
|
||||
|
||||
=== Mouse bindings
|
||||
|
||||
A mouse binding makes i3 execute a command upon pressing a specific mouse
|
||||
button in the scope of the clicked container (see <<command_criteria>>). You
|
||||
can configure mouse bindings in a similar way to key bindings.
|
||||
|
||||
*Syntax*:
|
||||
----------------------------------
|
||||
bindsym [--release] [--whole-window] [Modifiers+]button[n] command
|
||||
----------------------------------
|
||||
|
||||
By default, the binding will only run when you click on the titlebar of the
|
||||
window. If the +--whole-window+ flag is given, it will run when any part of the
|
||||
window is clicked. If the +--release+ flag is given, it will run when the mouse
|
||||
button is released.
|
||||
|
||||
*Examples*:
|
||||
--------------------------------
|
||||
# The middle button over a titlebar kills the window
|
||||
bindsym --release button2 kill
|
||||
|
||||
# The middle button and a modifer over any part of the window kills the window
|
||||
bindsym --whole-window $mod+button2 kill
|
||||
|
||||
# The right button toggles floating
|
||||
bindsym button3 floating toggle
|
||||
bindsym $mod+button3 floating toggle
|
||||
|
||||
# The side buttons move the window around
|
||||
bindsym button9 move left
|
||||
bindsym button8 move right
|
||||
--------------------------------
|
||||
|
||||
[[floating_modifier]]
|
||||
|
||||
=== The floating modifier
|
||||
|
@ -1069,6 +1105,27 @@ bar {
|
|||
|
||||
Available modifiers are Mod1-Mod5, Shift, Control (see +xmodmap(1)+).
|
||||
|
||||
=== Mouse button commands
|
||||
|
||||
Specifies a command to run when a button was pressed on i3bar to override the
|
||||
default behavior. Currently only the mouse wheel buttons are supported. This is
|
||||
useful for disabling the scroll wheel action or running scripts that implement
|
||||
custom behavior for these buttons.
|
||||
|
||||
*Syntax*:
|
||||
---------------------
|
||||
wheel_up_cmd <command>
|
||||
wheel_down_cmd <command>
|
||||
---------------------
|
||||
|
||||
*Example*:
|
||||
---------------------
|
||||
bar {
|
||||
wheel_up_cmd nop
|
||||
wheel_down_cmd exec ~/.i3/scripts/custom_wheel_down
|
||||
}
|
||||
---------------------
|
||||
|
||||
=== Bar ID
|
||||
|
||||
Specifies the bar ID for the configured bar instance. If this option is missing,
|
||||
|
@ -1446,9 +1503,13 @@ 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+
|
||||
(or +floating toggle+):
|
||||
To make the current window (!) fullscreen, use +fullscreen enable+ (or
|
||||
+fullscreen enable global+ for the global mode), to leave either fullscreen
|
||||
mode use +fullscreen disable+, and to toggle between these two states use
|
||||
+fullscreen toggle+ (or +fullscreen toggle global+).
|
||||
|
||||
Likewise, to make the current window floating (or tiling again) use +floating
|
||||
enable+ respectively +floating disable+ (or +floating toggle+):
|
||||
|
||||
*Syntax*:
|
||||
--------------
|
||||
|
@ -1469,7 +1530,7 @@ bindsym $mod+x layout toggle
|
|||
bindsym $mod+x layout toggle all
|
||||
|
||||
# Toggle fullscreen
|
||||
bindsym $mod+f fullscreen
|
||||
bindsym $mod+f fullscreen toggle
|
||||
|
||||
# Toggle floating/tiling
|
||||
bindsym $mod+t floating toggle
|
||||
|
@ -1564,6 +1625,10 @@ container to the next/previous workspace and +move container to workspace curren
|
|||
See <<move_to_outputs>> for how to move a container/workspace to a different
|
||||
RandR output.
|
||||
|
||||
Workspace names are parsed as
|
||||
https://developer.gnome.org/pango/stable/PangoMarkupFormat.html[Pango markup]
|
||||
by i3bar.
|
||||
|
||||
[[back_and_forth]]
|
||||
To switch back to the previously focused workspace, use +workspace
|
||||
back_and_forth+; likewise, you can move containers to the previously focused
|
||||
|
@ -1585,6 +1650,7 @@ move [window|container] [to] workspace <prev|next|current>
|
|||
-------------------------
|
||||
bindsym $mod+1 workspace 1
|
||||
bindsym $mod+2 workspace 2
|
||||
bindsym $mod+3 workspace 3:<span foreground="red">vim</span>
|
||||
...
|
||||
|
||||
bindsym $mod+Shift+1 move container to workspace 1
|
||||
|
|
|
@ -4,8 +4,8 @@ CLEAN_TARGETS += clean-i3-config-wizard
|
|||
|
||||
i3_config_wizard_SOURCES := $(wildcard i3-config-wizard/*.c)
|
||||
i3_config_wizard_HEADERS := $(wildcard i3-config-wizard/*.h)
|
||||
i3_config_wizard_CFLAGS = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(X11_CFLAGS) $(PANGO_CFLAGS)
|
||||
i3_config_wizard_LIBS = $(XCB_LIBS) $(XCB_KBD_LIBS) $(X11_LIBS) $(PANGO_LIBS)
|
||||
i3_config_wizard_CFLAGS = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(PANGO_CFLAGS) $(XKB_COMMON_CFLAGS) $(XKB_COMMON_X11_CFLAGS)
|
||||
i3_config_wizard_LIBS = $(XCB_LIBS) $(XCB_KBD_LIBS) $(PANGO_LIBS) $(XKB_COMMON_LIBS) $(XKB_COMMON_X11_LIBS)
|
||||
|
||||
i3_config_wizard_OBJECTS := $(i3_config_wizard_SOURCES:.c=.o)
|
||||
|
||||
|
|
|
@ -43,6 +43,9 @@
|
|||
#include <xcb/xcb_event.h>
|
||||
#include <xcb/xcb_keysyms.h>
|
||||
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#include <xkbcommon/xkbcommon-x11.h>
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/keysym.h>
|
||||
#include <X11/XKBlib.h>
|
||||
|
@ -83,7 +86,9 @@ static xcb_pixmap_t pixmap;
|
|||
static xcb_gcontext_t pixmap_gc;
|
||||
static xcb_key_symbols_t *symbols;
|
||||
xcb_window_t root;
|
||||
Display *dpy;
|
||||
static struct xkb_keymap *xkb_keymap;
|
||||
static uint8_t xkb_base_event;
|
||||
static uint8_t xkb_base_error;
|
||||
|
||||
static void finish();
|
||||
|
||||
|
@ -250,12 +255,24 @@ static char *next_state(const cmdp_token *token) {
|
|||
* This reduces a lot of confusion for users who switch keyboard
|
||||
* layouts from qwerty to qwertz or other slight variations of
|
||||
* qwerty (yes, that happens quite often). */
|
||||
KeySym sym = XkbKeycodeToKeysym(dpy, keycode, 0, 0);
|
||||
if (!keysym_used_on_other_key(sym, keycode))
|
||||
const xkb_keysym_t *syms;
|
||||
int num = xkb_keymap_key_get_syms_by_level(xkb_keymap, keycode, 0, 0, &syms);
|
||||
if (num == 0)
|
||||
errx(1, "xkb_keymap_key_get_syms_by_level returned no symbols for keycode %d", keycode);
|
||||
if (!keysym_used_on_other_key(syms[0], keycode))
|
||||
level = 0;
|
||||
}
|
||||
KeySym sym = XkbKeycodeToKeysym(dpy, keycode, 0, level);
|
||||
char *str = XKeysymToString(sym);
|
||||
|
||||
const xkb_keysym_t *syms;
|
||||
int num = xkb_keymap_key_get_syms_by_level(xkb_keymap, keycode, 0, level, &syms);
|
||||
if (num == 0)
|
||||
errx(1, "xkb_keymap_key_get_syms_by_level returned no symbols for keycode %d", keycode);
|
||||
if (num > 1)
|
||||
printf("xkb_keymap_key_get_syms_by_level (keycode = %d) returned %d symbolsinstead of 1, using only the first one.\n", keycode, num);
|
||||
|
||||
char str[4096];
|
||||
if (xkb_keysym_get_name(syms[0], str, sizeof(str)) == -1)
|
||||
errx(EXIT_FAILURE, "xkb_keysym_get_name(%u) failed", syms[0]);
|
||||
const char *release = get_string("release");
|
||||
char *res;
|
||||
char *modrep = (modifiers == NULL ? sstrdup("") : sstrdup(modifiers));
|
||||
|
@ -642,8 +659,14 @@ static void handle_button_press(xcb_button_press_event_t *event) {
|
|||
static void finish() {
|
||||
printf("creating \"%s\"...\n", config_path);
|
||||
|
||||
if (!(dpy = XOpenDisplay(NULL)))
|
||||
errx(1, "Could not connect to X11");
|
||||
struct xkb_context *xkb_context;
|
||||
|
||||
if ((xkb_context = xkb_context_new(0)) == NULL)
|
||||
errx(1, "could not create xkbcommon context");
|
||||
|
||||
int32_t device_id = xkb_x11_get_core_keyboard_device_id(conn);
|
||||
if ((xkb_keymap = xkb_x11_keymap_new_from_device(xkb_context, conn, device_id, 0)) == NULL)
|
||||
errx(1, "xkb_x11_keymap_new_from_device failed");
|
||||
|
||||
FILE *kc_config = fopen(SYSCONFDIR "/i3/config.keycodes", "r");
|
||||
if (kc_config == NULL)
|
||||
|
@ -797,6 +820,16 @@ int main(int argc, char *argv[]) {
|
|||
xcb_connection_has_error(conn))
|
||||
errx(1, "Cannot open display\n");
|
||||
|
||||
if (xkb_x11_setup_xkb_extension(conn,
|
||||
XKB_X11_MIN_MAJOR_XKB_VERSION,
|
||||
XKB_X11_MIN_MINOR_XKB_VERSION,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
&xkb_base_event,
|
||||
&xkb_base_error) != 1)
|
||||
errx(EXIT_FAILURE, "Could not setup XKB extension.");
|
||||
|
||||
if (socket_path == NULL)
|
||||
socket_path = root_atom_contents("I3_SOCKET_PATH", conn, screen);
|
||||
|
||||
|
|
|
@ -3,12 +3,12 @@
|
|||
#include <err.h>
|
||||
|
||||
#define die(...) errx(EXIT_FAILURE, __VA_ARGS__);
|
||||
#define FREE(pointer) do { \
|
||||
#define FREE(pointer) \
|
||||
do { \
|
||||
if (pointer != NULL) { \
|
||||
free(pointer); \
|
||||
pointer = NULL; \
|
||||
} \
|
||||
} \
|
||||
while (0)
|
||||
} while (0)
|
||||
|
||||
extern xcb_window_t root;
|
||||
|
|
|
@ -3,13 +3,13 @@
|
|||
#include <err.h>
|
||||
|
||||
#define die(...) errx(EXIT_FAILURE, __VA_ARGS__);
|
||||
#define FREE(pointer) do { \
|
||||
#define FREE(pointer) \
|
||||
do { \
|
||||
if (pointer != NULL) { \
|
||||
free(pointer); \
|
||||
pointer = NULL; \
|
||||
} \
|
||||
} \
|
||||
while (0)
|
||||
} while (0)
|
||||
|
||||
#define xmacro(atom) xcb_atom_t A_##atom;
|
||||
#include "atoms.xmacro"
|
||||
|
|
|
@ -92,6 +92,7 @@ my %allowed_keys = map { ($_, 1) } qw(
|
|||
name
|
||||
geometry
|
||||
window_properties
|
||||
mark
|
||||
);
|
||||
|
||||
sub strip_containers {
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
# Hopefully one of these is installed (no flamewars about preference please!):
|
||||
for editor in $VISUAL $EDITOR nano vim vi emacs pico qe mg jed gedit mc-edit; do
|
||||
if which $editor > /dev/null 2>&1; then
|
||||
if command -v $editor > /dev/null 2>&1; then
|
||||
exec $editor "$@"
|
||||
fi
|
||||
done
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
# We don't use 'more' because it will exit if the file is too short.
|
||||
# Worst case scenario we'll open the file in your editor.
|
||||
for pager in $PAGER less most w3m i3-sensible-editor; do
|
||||
if which $pager > /dev/null 2>&1; then
|
||||
if command -v $pager > /dev/null 2>&1; then
|
||||
exec $pager "$@"
|
||||
fi
|
||||
done
|
||||
|
|
|
@ -5,11 +5,11 @@
|
|||
# This script tries to exec a terminal emulator by trying some known terminal
|
||||
# emulators.
|
||||
#
|
||||
# Distributions/packagers should enhance this script with a
|
||||
# distribution-specific mechanism to find the preferred terminal emulator. On
|
||||
# Debian, there is the x-terminal-emulator symlink for example.
|
||||
for terminal in $TERMINAL urxvt rxvt terminator Eterm aterm xterm gnome-terminal roxterm xfce4-terminal; do
|
||||
if which $terminal > /dev/null 2>&1; then
|
||||
# We welcome patches that add distribution-specific mechanisms to find the
|
||||
# preferred terminal emulator. On Debian, there is the x-terminal-emulator
|
||||
# symlink for example.
|
||||
for terminal in $TERMINAL x-terminal-emulator urxvt rxvt terminator Eterm aterm xterm gnome-terminal roxterm xfce4-terminal termite lxterminal; do
|
||||
if command -v $terminal > /dev/null 2>&1; then
|
||||
exec $terminal "$@"
|
||||
fi
|
||||
done
|
||||
|
|
|
@ -75,7 +75,7 @@ bindsym Mod1+h split h
|
|||
bindsym Mod1+v split v
|
||||
|
||||
# enter fullscreen mode for the focused container
|
||||
bindsym Mod1+f fullscreen
|
||||
bindsym Mod1+f fullscreen toggle
|
||||
|
||||
# change container layout (stacked, tabbed, toggle split)
|
||||
bindsym Mod1+s layout stacking
|
||||
|
|
|
@ -69,7 +69,7 @@ bindcode $mod+43 split h
|
|||
bindcode $mod+55 split v
|
||||
|
||||
# enter fullscreen mode for the focused container
|
||||
bindcode $mod+41 fullscreen
|
||||
bindcode $mod+41 fullscreen toggle
|
||||
|
||||
# change container layout (stacked, tabbed, toggle split)
|
||||
bindcode $mod+39 layout stacking
|
||||
|
|
|
@ -5,3 +5,4 @@ Exec=i3
|
|||
TryExec=i3
|
||||
Type=Application
|
||||
X-LightDM-DesktopName=i3
|
||||
DesktopNames=i3
|
||||
|
|
|
@ -4,8 +4,8 @@ CLEAN_TARGETS += clean-i3bar
|
|||
|
||||
i3bar_SOURCES := $(wildcard i3bar/src/*.c)
|
||||
i3bar_HEADERS := $(wildcard i3bar/include/*.h)
|
||||
i3bar_CFLAGS = $(XCB_CFLAGS) $(X11_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS)
|
||||
i3bar_LIBS = $(XCB_LIBS) $(X11_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS)
|
||||
i3bar_CFLAGS = $(XCB_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS)
|
||||
i3bar_LIBS = $(XCB_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(XCB_XKB_LIBS)
|
||||
|
||||
i3bar_OBJECTS := $(i3bar_SOURCES:.c=.o)
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ struct rect_t {
|
|||
};
|
||||
|
||||
typedef enum {
|
||||
/* First value to make it the default. */
|
||||
ALIGN_LEFT,
|
||||
ALIGN_CENTER,
|
||||
ALIGN_RIGHT
|
||||
|
|
|
@ -18,10 +18,14 @@ typedef enum {
|
|||
} position_t;
|
||||
|
||||
/* Bar display mode (hide unless modifier is pressed or show in dock mode or always hide in invisible mode) */
|
||||
typedef enum { M_DOCK = 0, M_HIDE = 1, M_INVISIBLE = 2 } bar_display_mode_t;
|
||||
typedef enum { M_DOCK = 0,
|
||||
M_HIDE = 1,
|
||||
M_INVISIBLE = 2 } bar_display_mode_t;
|
||||
|
||||
typedef struct config_t {
|
||||
int modifier;
|
||||
char *wheel_up_cmd;
|
||||
char *wheel_down_cmd;
|
||||
position_t position;
|
||||
int verbose;
|
||||
struct xcb_color_strings_t colors;
|
||||
|
@ -38,7 +42,8 @@ typedef struct config_t {
|
|||
bar_display_mode_t 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;
|
||||
enum { S_HIDE = 0,
|
||||
S_SHOW = 1 } hidden_state;
|
||||
} config_t;
|
||||
|
||||
config_t config;
|
||||
|
|
|
@ -40,6 +40,7 @@ struct i3_output {
|
|||
char* name; /* Name of the output */
|
||||
bool active; /* If the output is active */
|
||||
bool primary; /* If it is the primary output */
|
||||
bool visible; /* If the bar is visible on this output */
|
||||
int ws; /* The number of the currently visible ws */
|
||||
rect rect; /* The rect (relative to the root-win) */
|
||||
|
||||
|
|
|
@ -18,7 +18,8 @@
|
|||
#define STARTS_WITH(string, len, needle) ((len >= strlen(needle)) && strncasecmp(string, needle, strlen(needle)) == 0)
|
||||
|
||||
/* Securely free p */
|
||||
#define FREE(p) do { \
|
||||
#define FREE(p) \
|
||||
do { \
|
||||
if (p != NULL) { \
|
||||
free(p); \
|
||||
p = NULL; \
|
||||
|
@ -26,7 +27,8 @@
|
|||
} while (0)
|
||||
|
||||
/* Securely fee single-linked list */
|
||||
#define FREE_SLIST(l, type) do { \
|
||||
#define FREE_SLIST(l, type) \
|
||||
do { \
|
||||
type *walk = SLIST_FIRST(l); \
|
||||
while (!SLIST_EMPTY(l)) { \
|
||||
SLIST_REMOVE_HEAD(l, slist); \
|
||||
|
@ -36,7 +38,8 @@
|
|||
} while (0)
|
||||
|
||||
/* Securely fee tail-queues */
|
||||
#define FREE_TAILQ(l, type) do { \
|
||||
#define FREE_TAILQ(l, type) \
|
||||
do { \
|
||||
type *walk = TAILQ_FIRST(l); \
|
||||
while (!TAILQ_EMPTY(l)) { \
|
||||
TAILQ_REMOVE(l, TAILQ_FIRST(l), tailq); \
|
||||
|
@ -49,7 +52,8 @@
|
|||
#undef DLOG
|
||||
#endif
|
||||
/* Use cool logging-macros */
|
||||
#define DLOG(fmt, ...) do { \
|
||||
#define DLOG(fmt, ...) \
|
||||
do { \
|
||||
if (config.verbose) { \
|
||||
printf("[%s:%d] " fmt, __FILE__, __LINE__, ##__VA_ARGS__); \
|
||||
} \
|
||||
|
@ -60,6 +64,7 @@
|
|||
#if defined(ELOG)
|
||||
#undef ELOG
|
||||
#endif
|
||||
#define ELOG(fmt, ...) do { \
|
||||
#define ELOG(fmt, ...) \
|
||||
do { \
|
||||
fprintf(stderr, "[%s:%d] ERROR: " fmt, __FILE__, __LINE__, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
|
|
@ -182,21 +182,21 @@ static int stdin_boolean(void *context, int val) {
|
|||
static int stdin_string(void *context, const unsigned char *val, size_t len) {
|
||||
parser_ctx *ctx = context;
|
||||
if (strcasecmp(ctx->last_map_key, "full_text") == 0) {
|
||||
ctx->block.full_text = i3string_from_utf8_with_length((const char *)val, len);
|
||||
ctx->block.full_text = i3string_from_markup_with_length((const char *)val, len);
|
||||
}
|
||||
if (strcasecmp(ctx->last_map_key, "color") == 0) {
|
||||
sasprintf(&(ctx->block.color), "%.*s", len, val);
|
||||
}
|
||||
if (strcasecmp(ctx->last_map_key, "align") == 0) {
|
||||
if (len == strlen("left") && !strncmp((const char *)val, "left", strlen("left"))) {
|
||||
ctx->block.align = ALIGN_LEFT;
|
||||
if (len == strlen("center") && !strncmp((const char *)val, "center", strlen("center"))) {
|
||||
ctx->block.align = ALIGN_CENTER;
|
||||
} else if (len == strlen("right") && !strncmp((const char *)val, "right", strlen("right"))) {
|
||||
ctx->block.align = ALIGN_RIGHT;
|
||||
} else {
|
||||
ctx->block.align = ALIGN_CENTER;
|
||||
ctx->block.align = ALIGN_LEFT;
|
||||
}
|
||||
} else if (strcasecmp(ctx->last_map_key, "min_width") == 0) {
|
||||
i3String *text = i3string_from_utf8_with_length((const char *)val, len);
|
||||
i3String *text = i3string_from_markup_with_length((const char *)val, len);
|
||||
ctx->block.min_width = (uint32_t)predict_text_width(text);
|
||||
i3string_free(text);
|
||||
}
|
||||
|
@ -233,7 +233,7 @@ static int stdin_end_map(void *context) {
|
|||
/* Ensure we have a full_text set, so that when it is missing (or null),
|
||||
* i3bar doesn’t crash and the user gets an annoying message. */
|
||||
if (!new_block->full_text)
|
||||
new_block->full_text = i3string_from_utf8("SPEC VIOLATION (null)");
|
||||
new_block->full_text = i3string_from_utf8("SPEC VIOLATION: full_text is NULL!");
|
||||
if (new_block->urgent)
|
||||
ctx->has_urgent = true;
|
||||
TAILQ_INSERT_TAIL(&statusline_head, new_block, blocks);
|
||||
|
@ -304,7 +304,7 @@ static void read_flat_input(char *buffer, int length) {
|
|||
buffer[length - 1] = '\0';
|
||||
else
|
||||
buffer[length] = '\0';
|
||||
first->full_text = i3string_from_utf8(buffer);
|
||||
first->full_text = i3string_from_markup(buffer);
|
||||
}
|
||||
|
||||
static bool read_json_input(unsigned char *input, int length) {
|
||||
|
|
|
@ -112,6 +112,20 @@ static int config_string_cb(void *params_, const unsigned char *val, size_t _len
|
|||
return 1;
|
||||
}
|
||||
|
||||
if (!strcmp(cur_key, "wheel_up_cmd")) {
|
||||
DLOG("wheel_up_cmd = %.*s\n", len, val);
|
||||
FREE(config.wheel_up_cmd);
|
||||
sasprintf(&config.wheel_up_cmd, "%.*s", len, val);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!strcmp(cur_key, "wheel_down_cmd")) {
|
||||
DLOG("wheel_down_cmd = %.*s\n", len, val);
|
||||
FREE(config.wheel_down_cmd);
|
||||
sasprintf(&config.wheel_down_cmd, "%.*s", len, val);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!strcmp(cur_key, "position")) {
|
||||
DLOG("position = %.*s\n", len, val);
|
||||
config.position = (len == 3 && !strncmp((const char *)val, "top", strlen("top")) ? POS_TOP : POS_BOT);
|
||||
|
|
|
@ -159,6 +159,9 @@ void got_bar_config_update(char *event) {
|
|||
if (found_id == NULL)
|
||||
return;
|
||||
|
||||
/* reconfigure the bar based on the current outputs */
|
||||
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_OUTPUTS, NULL);
|
||||
|
||||
free_colors(&(config.colors));
|
||||
|
||||
/* update the configuration with the received settings */
|
||||
|
@ -169,6 +172,8 @@ void got_bar_config_update(char *event) {
|
|||
reconfig_windows(true);
|
||||
}
|
||||
|
||||
/* update fonts and colors */
|
||||
init_xcb_late(config.fontname);
|
||||
init_colors(&(config.colors));
|
||||
realloc_sl_buffer();
|
||||
|
||||
|
|
|
@ -123,12 +123,12 @@ static int workspaces_string_cb(void *params_, const unsigned char *val, size_t
|
|||
|
||||
/* Offset may be equal to length, in which case display the number */
|
||||
params->workspaces_walk->name = (offset < len
|
||||
? i3string_from_utf8_with_length(ws_name + offset, len - offset)
|
||||
: i3string_from_utf8(ws_num));
|
||||
? i3string_from_markup_with_length(ws_name + offset, len - offset)
|
||||
: i3string_from_markup(ws_num));
|
||||
|
||||
} else {
|
||||
/* Default case: just save the name */
|
||||
params->workspaces_walk->name = i3string_from_utf8_with_length(ws_name, len);
|
||||
params->workspaces_walk->name = i3string_from_markup_with_length(ws_name, len);
|
||||
}
|
||||
|
||||
/* Save its rendered width */
|
||||
|
|
328
i3bar/src/xcb.c
328
i3bar/src/xcb.c
|
@ -8,6 +8,7 @@
|
|||
*
|
||||
*/
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/xkb.h>
|
||||
#include <xcb/xproto.h>
|
||||
#include <xcb/xcb_aux.h>
|
||||
|
||||
|
@ -63,8 +64,7 @@ static i3Font font;
|
|||
int bar_height;
|
||||
|
||||
/* These are only relevant for XKB, which we only need for grabbing modifiers */
|
||||
Display *xkb_dpy;
|
||||
int xkb_event_base;
|
||||
int xkb_base;
|
||||
int mod_pressed = 0;
|
||||
|
||||
/* Because the statusline is the same on all outputs, we have
|
||||
|
@ -117,6 +117,12 @@ int _xcb_request_failed(xcb_void_cookie_t cookie, char *err_msg, int line) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
uint32_t get_sep_offset(struct status_block *block) {
|
||||
if (!block->no_separator && block->sep_block_width > 0)
|
||||
return block->sep_block_width / 2 + block->sep_block_width % 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Redraws the statusline to the buffer
|
||||
*
|
||||
|
@ -148,15 +154,15 @@ void refresh_statusline(void) {
|
|||
block->x_offset = padding_width;
|
||||
break;
|
||||
case ALIGN_CENTER:
|
||||
block->x_offset = padding_width / logical_px(2);
|
||||
block->x_append = padding_width / logical_px(2) + padding_width % logical_px(2);
|
||||
block->x_offset = padding_width / 2;
|
||||
block->x_append = padding_width / 2 + padding_width % 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If this is not the last block, add some pixels for a separator. */
|
||||
if (TAILQ_NEXT(block, blocks) != NULL)
|
||||
block->width += block->sep_block_width;
|
||||
statusline_width += block->sep_block_width;
|
||||
|
||||
statusline_width += block->width + block->x_offset + block->x_append;
|
||||
}
|
||||
|
@ -168,7 +174,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 + logical_px(5)};
|
||||
xcb_rectangle_t rect = {0, 0, root_screen->width_in_pixels, bar_height};
|
||||
xcb_poly_fill_rectangle(xcb_connection, statusline_pm, statusline_clear, 1, &rect);
|
||||
|
||||
/* Draw the text of each block. */
|
||||
|
@ -176,22 +182,41 @@ void refresh_statusline(void) {
|
|||
TAILQ_FOREACH(block, &statusline_head, blocks) {
|
||||
if (i3string_get_num_bytes(block->full_text) == 0)
|
||||
continue;
|
||||
uint32_t fg_color;
|
||||
|
||||
uint32_t colorpixel = (block->color ? get_colorpixel(block->color) : colors.bar_fg);
|
||||
set_font_colors(statusline_ctx, colorpixel, colors.bar_bg);
|
||||
draw_text(block->full_text, statusline_pm, statusline_ctx, x + block->x_offset, 1, block->width);
|
||||
x += block->width + block->x_offset + block->x_append;
|
||||
/* If this block is urgent, draw it with the defined color and border. */
|
||||
if (block->urgent) {
|
||||
fg_color = colors.urgent_ws_fg;
|
||||
|
||||
if (TAILQ_NEXT(block, blocks) != NULL && !block->no_separator && block->sep_block_width > 0) {
|
||||
uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND;
|
||||
|
||||
/* Draw the background */
|
||||
uint32_t bg_color = colors.urgent_ws_bg;
|
||||
uint32_t bg_values[] = { bg_color, bg_color };
|
||||
xcb_change_gc(xcb_connection, statusline_ctx, mask, bg_values);
|
||||
|
||||
/* The urgent background “overshoots” by 2 px so that the text that
|
||||
* is printed onto it will not be look so cut off. */
|
||||
xcb_rectangle_t bg_rect = { x - logical_px(2), logical_px(1), block->width + logical_px(4), bar_height - logical_px(2) };
|
||||
xcb_poly_fill_rectangle(xcb_connection, statusline_pm, statusline_ctx, 1, &bg_rect);
|
||||
} else {
|
||||
fg_color = (block->color ? get_colorpixel(block->color) : colors.bar_fg);
|
||||
}
|
||||
|
||||
set_font_colors(statusline_ctx, fg_color, colors.bar_bg);
|
||||
draw_text(block->full_text, statusline_pm, statusline_ctx, x + block->x_offset, 3, block->width);
|
||||
x += block->width + block->sep_block_width + block->x_offset + block->x_append;
|
||||
|
||||
uint32_t sep_offset = get_sep_offset(block);
|
||||
if (TAILQ_NEXT(block, blocks) != NULL && sep_offset > 0) {
|
||||
/* This is not the last block, draw a separator. */
|
||||
uint32_t sep_offset = block->sep_block_width / 2 + block->sep_block_width % 2;
|
||||
uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_LINE_WIDTH;
|
||||
uint32_t values[] = {colors.sep_fg, colors.bar_bg, logical_px(1)};
|
||||
xcb_change_gc(xcb_connection, statusline_ctx, mask, values);
|
||||
xcb_poly_line(xcb_connection, XCB_COORD_MODE_ORIGIN, statusline_pm,
|
||||
statusline_ctx, 2,
|
||||
(xcb_point_t[]) {{x - sep_offset, 2},
|
||||
{x - sep_offset, font.height - 2}});
|
||||
(xcb_point_t[]) { { x - sep_offset, logical_px(4) },
|
||||
{ x - sep_offset, bar_height - logical_px(4) } });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -331,22 +356,31 @@ void handle_button(xcb_button_press_event_t *event) {
|
|||
continue;
|
||||
tray_width += (font.height + logical_px(2));
|
||||
}
|
||||
if (tray_width > 0)
|
||||
tray_width += logical_px(2);
|
||||
|
||||
int block_x = 0, last_block_x;
|
||||
int offset = (walk->rect.w - (statusline_width + tray_width)) - logical_px(10);
|
||||
int offset = walk->rect.w - statusline_width - tray_width - logical_px(4);
|
||||
|
||||
x = original_x - offset;
|
||||
if (x >= 0) {
|
||||
struct status_block *block;
|
||||
int sep_offset_remainder = 0;
|
||||
|
||||
TAILQ_FOREACH (block, &statusline_head, blocks) {
|
||||
if (i3string_get_num_bytes(block->full_text) == 0)
|
||||
continue;
|
||||
|
||||
last_block_x = block_x;
|
||||
block_x += block->width + block->x_offset + block->x_append;
|
||||
block_x += block->width + block->x_offset + block->x_append
|
||||
+ get_sep_offset(block) + sep_offset_remainder;
|
||||
|
||||
if (x <= block_x && x >= last_block_x) {
|
||||
send_block_clicked(event->detail, block->name, block->instance, event->root_x, event->root_y);
|
||||
return;
|
||||
}
|
||||
|
||||
sep_offset_remainder = block->sep_block_width - get_sep_offset(block);
|
||||
}
|
||||
}
|
||||
x = original_x;
|
||||
|
@ -370,6 +404,14 @@ void handle_button(xcb_button_press_event_t *event) {
|
|||
* If there is no more workspace, don’t even send the workspace
|
||||
* command, otherwise (with workspace auto_back_and_forth) we’d end
|
||||
* up on the wrong workspace. */
|
||||
|
||||
/* If `wheel_up_cmd [COMMAND]` was specified, it should override
|
||||
* the default behavior */
|
||||
if (config.wheel_up_cmd) {
|
||||
i3_send_msg(I3_IPC_MESSAGE_TYPE_COMMAND, config.wheel_up_cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cur_ws == TAILQ_FIRST(walk->workspaces))
|
||||
return;
|
||||
|
||||
|
@ -380,6 +422,14 @@ void handle_button(xcb_button_press_event_t *event) {
|
|||
* If there is no more workspace, don’t even send the workspace
|
||||
* command, otherwise (with workspace auto_back_and_forth) we’d end
|
||||
* up on the wrong workspace. */
|
||||
|
||||
/* if `wheel_down_cmd [COMMAND]` was specified, it should override
|
||||
* the default behavior */
|
||||
if (config.wheel_down_cmd) {
|
||||
i3_send_msg(I3_IPC_MESSAGE_TYPE_COMMAND, config.wheel_down_cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cur_ws == TAILQ_LAST(walk->workspaces, ws_head))
|
||||
return;
|
||||
|
||||
|
@ -446,6 +496,39 @@ void handle_button(xcb_button_press_event_t *event) {
|
|||
free(buffer);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle visibility notifications: when none of the bars are visible, e.g.
|
||||
* if windows are in full-screen on each output, suspend the child process.
|
||||
*
|
||||
*/
|
||||
static void handle_visibility_notify(xcb_visibility_notify_event_t *event) {
|
||||
bool visible = (event->state != XCB_VISIBILITY_FULLY_OBSCURED);
|
||||
int num_visible = 0;
|
||||
i3_output *output;
|
||||
|
||||
SLIST_FOREACH (output, outputs, slist) {
|
||||
if (!output->active) {
|
||||
continue;
|
||||
}
|
||||
if (output->bar == event->window) {
|
||||
if (output->visible == visible) {
|
||||
return;
|
||||
}
|
||||
output->visible = visible;
|
||||
}
|
||||
num_visible += output->visible;
|
||||
}
|
||||
|
||||
if (num_visible == 0) {
|
||||
stop_child();
|
||||
} else if (num_visible == visible) {
|
||||
/* Wake the child only when transitioning from 0 to 1 visible bar.
|
||||
* We cannot transition from 0 to 2 or more visible bars at once since
|
||||
* visibility events are delivered to each window separately */
|
||||
cont_child();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Adjusts the size of the tray window and alignment of the tray clients by
|
||||
* configuring their respective x coordinates. To be called when mapping or
|
||||
|
@ -854,7 +937,66 @@ void xcb_chk_cb(struct ev_loop *loop, ev_check *watcher, int revents) {
|
|||
}
|
||||
|
||||
while ((event = xcb_poll_for_event(xcb_connection)) != NULL) {
|
||||
switch (event->response_type & ~0x80) {
|
||||
int type = (event->response_type & ~0x80);
|
||||
|
||||
if (type == xkb_base && xkb_base > -1) {
|
||||
DLOG("received an xkb event\n");
|
||||
|
||||
xcb_xkb_state_notify_event_t *state = (xcb_xkb_state_notify_event_t *)event;
|
||||
if (state->xkbType == XCB_XKB_STATE_NOTIFY) {
|
||||
int modstate = state->mods & config.modifier;
|
||||
|
||||
#define DLOGMOD(modmask, status) \
|
||||
do { \
|
||||
switch (modmask) { \
|
||||
case ShiftMask: \
|
||||
DLOG("ShiftMask got " #status "!\n"); \
|
||||
break; \
|
||||
case ControlMask: \
|
||||
DLOG("ControlMask got " #status "!\n"); \
|
||||
break; \
|
||||
case Mod1Mask: \
|
||||
DLOG("Mod1Mask got " #status "!\n"); \
|
||||
break; \
|
||||
case Mod2Mask: \
|
||||
DLOG("Mod2Mask got " #status "!\n"); \
|
||||
break; \
|
||||
case Mod3Mask: \
|
||||
DLOG("Mod3Mask got " #status "!\n"); \
|
||||
break; \
|
||||
case Mod4Mask: \
|
||||
DLOG("Mod4Mask got " #status "!\n"); \
|
||||
break; \
|
||||
case Mod5Mask: \
|
||||
DLOG("Mod5Mask got " #status "!\n"); \
|
||||
break; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
if (modstate != mod_pressed) {
|
||||
if (modstate == 0) {
|
||||
DLOGMOD(config.modifier, released);
|
||||
if (!activated_mode)
|
||||
hide_bars();
|
||||
} else {
|
||||
DLOGMOD(config.modifier, pressed);
|
||||
activated_mode = false;
|
||||
unhide_bars();
|
||||
}
|
||||
mod_pressed = modstate;
|
||||
}
|
||||
#undef DLOGMOD
|
||||
}
|
||||
|
||||
free(event);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case XCB_VISIBILITY_NOTIFY:
|
||||
/* Visibility change: a bar is [un]obscured by other window */
|
||||
handle_visibility_notify((xcb_visibility_notify_event_t *)event);
|
||||
break;
|
||||
case XCB_EXPOSE:
|
||||
/* Expose-events happen, when the window needs to be redrawn */
|
||||
redraw_bars();
|
||||
|
@ -900,76 +1042,6 @@ void xcb_chk_cb(struct ev_loop *loop, ev_check *watcher, int revents) {
|
|||
void xcb_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to bind to the modifier per XKB. Sadly, XCB does not implement this
|
||||
*
|
||||
*/
|
||||
void xkb_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
|
||||
XkbEvent ev;
|
||||
int modstate = 0;
|
||||
|
||||
DLOG("Got XKB-Event!\n");
|
||||
|
||||
while (XPending(xkb_dpy)) {
|
||||
XNextEvent(xkb_dpy, (XEvent *)&ev);
|
||||
|
||||
if (ev.type != xkb_event_base) {
|
||||
ELOG("No Xkb-Event!\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ev.any.xkb_type != XkbStateNotify) {
|
||||
ELOG("No State Notify!\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned int mods = ev.state.mods;
|
||||
modstate = mods & config.modifier;
|
||||
}
|
||||
|
||||
#define DLOGMOD(modmask, status) \
|
||||
do { \
|
||||
switch (modmask) { \
|
||||
case ShiftMask: \
|
||||
DLOG("ShiftMask got " #status "!\n"); \
|
||||
break; \
|
||||
case ControlMask: \
|
||||
DLOG("ControlMask got " #status "!\n"); \
|
||||
break; \
|
||||
case Mod1Mask: \
|
||||
DLOG("Mod1Mask got " #status "!\n"); \
|
||||
break; \
|
||||
case Mod2Mask: \
|
||||
DLOG("Mod2Mask got " #status "!\n"); \
|
||||
break; \
|
||||
case Mod3Mask: \
|
||||
DLOG("Mod3Mask got " #status "!\n"); \
|
||||
break; \
|
||||
case Mod4Mask: \
|
||||
DLOG("Mod4Mask got " #status "!\n"); \
|
||||
break; \
|
||||
case Mod5Mask: \
|
||||
DLOG("Mod5Mask got " #status "!\n"); \
|
||||
break; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
if (modstate != mod_pressed) {
|
||||
if (modstate == 0) {
|
||||
DLOGMOD(config.modifier, released);
|
||||
if (!activated_mode)
|
||||
hide_bars();
|
||||
} else {
|
||||
DLOGMOD(config.modifier, pressed);
|
||||
activated_mode = false;
|
||||
unhide_bars();
|
||||
}
|
||||
mod_pressed = modstate;
|
||||
}
|
||||
|
||||
#undef DLOGMOD
|
||||
}
|
||||
|
||||
/*
|
||||
* Early initialization of the connection to X11: Everything which does not
|
||||
* depend on 'config'.
|
||||
|
@ -1053,44 +1125,23 @@ char *init_xcb_early() {
|
|||
*
|
||||
*/
|
||||
void register_xkb_keyevents() {
|
||||
if (xkb_dpy == NULL) {
|
||||
int xkb_major, xkb_minor, xkb_errbase, xkb_err;
|
||||
xkb_major = XkbMajorVersion;
|
||||
xkb_minor = XkbMinorVersion;
|
||||
|
||||
xkb_dpy = XkbOpenDisplay(NULL,
|
||||
&xkb_event_base,
|
||||
&xkb_errbase,
|
||||
&xkb_major,
|
||||
&xkb_minor,
|
||||
&xkb_err);
|
||||
|
||||
if (xkb_dpy == NULL) {
|
||||
ELOG("No XKB!\n");
|
||||
const xcb_query_extension_reply_t *extreply;
|
||||
extreply = xcb_get_extension_data(conn, &xcb_xkb_id);
|
||||
if (!extreply->present) {
|
||||
ELOG("xkb is not present on this server\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (fcntl(ConnectionNumber(xkb_dpy), F_SETFD, FD_CLOEXEC) == -1) {
|
||||
ELOG("Could not set FD_CLOEXEC on xkbdpy: %s\n", strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int i1;
|
||||
if (!XkbQueryExtension(xkb_dpy, &i1, &xkb_event_base, &xkb_errbase, &xkb_major, &xkb_minor)) {
|
||||
ELOG("XKB not supported by X-server!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (!XkbSelectEvents(xkb_dpy, XkbUseCoreKbd, XkbStateNotifyMask, XkbStateNotifyMask)) {
|
||||
ELOG("Could not grab Key!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
xkb_io = smalloc(sizeof(ev_io));
|
||||
ev_io_init(xkb_io, &xkb_io_cb, ConnectionNumber(xkb_dpy), EV_READ);
|
||||
ev_io_start(main_loop, xkb_io);
|
||||
XFlush(xkb_dpy);
|
||||
}
|
||||
DLOG("initializing xcb-xkb\n");
|
||||
xcb_xkb_use_extension(conn, XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION);
|
||||
xcb_xkb_select_events(conn,
|
||||
XCB_XKB_ID_USE_CORE_KBD,
|
||||
XCB_XKB_EVENT_TYPE_STATE_NOTIFY,
|
||||
0,
|
||||
XCB_XKB_EVENT_TYPE_STATE_NOTIFY,
|
||||
0xff,
|
||||
0xff,
|
||||
NULL);
|
||||
xkb_base = extreply->first_event;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1098,13 +1149,14 @@ void register_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;
|
||||
}
|
||||
xcb_xkb_select_events(conn,
|
||||
XCB_XKB_ID_USE_CORE_KBD,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0xff,
|
||||
0xff,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1462,6 +1514,12 @@ void reconfig_windows(bool redraw_bars) {
|
|||
values[2] = XCB_EVENT_MASK_EXPOSURE |
|
||||
XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT |
|
||||
XCB_EVENT_MASK_BUTTON_PRESS;
|
||||
if (config.hide_on_modifier == M_DOCK) {
|
||||
/* If the bar is normally visible, catch visibility change events to suspend
|
||||
* the status process when the bar is obscured by full-screened windows. */
|
||||
values[2] |= XCB_EVENT_MASK_VISIBILITY_CHANGE;
|
||||
walk->visible = true;
|
||||
}
|
||||
xcb_void_cookie_t win_cookie = xcb_create_window_checked(xcb_connection,
|
||||
root_screen->root_depth,
|
||||
walk->bar,
|
||||
|
@ -1720,18 +1778,18 @@ void draw_bars(bool unhide) {
|
|||
/* We assume the tray icons are quadratic (we use the font
|
||||
* *height* as *width* of the icons) because we configured them
|
||||
* like this. */
|
||||
traypx += font.height + 2;
|
||||
traypx += font.height + logical_px(2);
|
||||
}
|
||||
/* Add 2px of padding if there are any tray icons */
|
||||
if (traypx > 0)
|
||||
traypx += 2;
|
||||
traypx += logical_px(2);
|
||||
xcb_copy_area(xcb_connection,
|
||||
statusline_pm,
|
||||
outputs_walk->buffer,
|
||||
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, (int)statusline_width), font.height + 2);
|
||||
MAX(0, (int16_t)(statusline_width - outputs_walk->rect.w + logical_px(4))), 0,
|
||||
MAX(0, (int16_t)(outputs_walk->rect.w - statusline_width - traypx - logical_px(4))), 0,
|
||||
MIN(outputs_walk->rect.w - traypx - logical_px(4), (int)statusline_width), bar_height);
|
||||
}
|
||||
|
||||
if (!config.disable_ws) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
xmacro(_NET_SUPPORTED)
|
||||
xmacro(_NET_SUPPORTING_WM_CHECK)
|
||||
xmacro(_NET_WM_NAME)
|
||||
xmacro(_NET_WM_MOVERESIZE)
|
||||
xmacro(_NET_WM_STATE_FULLSCREEN)
|
||||
xmacro(_NET_WM_STATE_DEMANDS_ATTENTION)
|
||||
xmacro(_NET_WM_STATE_MODAL)
|
||||
|
@ -16,7 +17,11 @@ xmacro(_NET_WM_STRUT_PARTIAL)
|
|||
xmacro(_NET_CLIENT_LIST)
|
||||
xmacro(_NET_CLIENT_LIST_STACKING)
|
||||
xmacro(_NET_CURRENT_DESKTOP)
|
||||
xmacro(_NET_NUMBER_OF_DESKTOPS)
|
||||
xmacro(_NET_DESKTOP_NAMES)
|
||||
xmacro(_NET_DESKTOP_VIEWPORT)
|
||||
xmacro(_NET_ACTIVE_WINDOW)
|
||||
xmacro(_NET_CLOSE_WINDOW)
|
||||
xmacro(_NET_STARTUP_ID)
|
||||
xmacro(_NET_WORKAREA)
|
||||
xmacro(WM_PROTOCOLS)
|
||||
|
@ -34,3 +39,4 @@ xmacro(I3_PID)
|
|||
xmacro(_NET_REQUEST_FRAME_EXTENTS)
|
||||
xmacro(_NET_FRAME_EXTENTS)
|
||||
xmacro(_MOTIF_WM_HINTS)
|
||||
xmacro(WM_CHANGE_STATE)
|
||||
|
|
|
@ -24,7 +24,7 @@ const char *DEFAULT_BINDING_MODE;
|
|||
*
|
||||
*/
|
||||
Binding *configure_binding(const char *bindtype, const char *modifiers, const char *input_code,
|
||||
const char *release, const char *command, const char *mode);
|
||||
const char *release, const char *whole_window, const char *command, const char *mode);
|
||||
|
||||
/**
|
||||
* Grab the bound keys (tell X to send us keypress events for those keycodes)
|
||||
|
@ -61,9 +61,15 @@ void switch_mode(const char *new_mode);
|
|||
void check_for_duplicate_bindings(struct context *context);
|
||||
|
||||
/**
|
||||
* Runs the given binding and handles parse errors. Returns a CommandResult for
|
||||
* running the binding's command. Caller should render tree if
|
||||
* needs_tree_render is true. Free with command_result_free().
|
||||
* Frees the binding. If bind is null, it simply returns.
|
||||
*/
|
||||
void binding_free(Binding *bind);
|
||||
|
||||
/**
|
||||
* Runs the given binding and handles parse errors. If con is passed, it will
|
||||
* execute the command binding with that container selected by criteria.
|
||||
* Returns a CommandResult for running the binding's command. Caller should
|
||||
* render tree if needs_tree_render is true. Free with command_result_free().
|
||||
*
|
||||
*/
|
||||
CommandResult *run_binding(Binding *bind);
|
||||
CommandResult *run_binding(Binding *bind, Con *con);
|
||||
|
|
|
@ -187,10 +187,10 @@ void cmd_focus_level(I3_CMD, char *level);
|
|||
void cmd_focus(I3_CMD);
|
||||
|
||||
/**
|
||||
* Implementation of 'fullscreen [global]'.
|
||||
* Implementation of 'fullscreen [enable|disable|toggle] [global]'.
|
||||
*
|
||||
*/
|
||||
void cmd_fullscreen(I3_CMD, char *fullscreen_mode);
|
||||
void cmd_fullscreen(I3_CMD, char *action, char *fullscreen_mode);
|
||||
|
||||
/**
|
||||
* Implementation of 'move <direction> [<pixels> [px]]'.
|
||||
|
|
|
@ -44,6 +44,14 @@ struct CommandResult {
|
|||
bool needs_tree_render;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses a string (or word, if as_word is true). Extracted out of
|
||||
* parse_command so that it can be used in src/workspace.c for interpreting
|
||||
* workspace commands.
|
||||
*
|
||||
*/
|
||||
char *parse_string(const char **walk, bool as_word);
|
||||
|
||||
/**
|
||||
* Parses and executes the given command. If a caller-allocated yajl_gen is
|
||||
* passed, a json reply will be generated in the format specified by the ipc
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
*/
|
||||
Con *con_new_skeleton(Con *parent, i3Window *window);
|
||||
|
||||
|
||||
/* A wrapper for con_new_skeleton, to retain the old con_new behaviour
|
||||
*
|
||||
*/
|
||||
|
@ -173,6 +172,18 @@ void con_fix_percent(Con *con);
|
|||
*/
|
||||
void con_toggle_fullscreen(Con *con, int fullscreen_mode);
|
||||
|
||||
/**
|
||||
* Enables fullscreen mode for the given container, if necessary.
|
||||
*
|
||||
*/
|
||||
void con_enable_fullscreen(Con *con, fullscreen_mode_t fullscreen_mode);
|
||||
|
||||
/**
|
||||
* Disables fullscreen mode for the given container, if necessary.
|
||||
*
|
||||
*/
|
||||
void con_disable_fullscreen(Con *con);
|
||||
|
||||
/**
|
||||
* Moves the given container to the currently focused container on the given
|
||||
* workspace.
|
||||
|
@ -345,3 +356,9 @@ void con_set_urgency(Con *con, bool urgent);
|
|||
*
|
||||
*/
|
||||
char *con_get_tree_representation(Con *con);
|
||||
|
||||
/**
|
||||
* force parent split containers to be redrawn
|
||||
*
|
||||
*/
|
||||
void con_force_split_parents_redraw(Con *con);
|
||||
|
|
|
@ -241,10 +241,13 @@ struct Barconfig {
|
|||
char *socket_path;
|
||||
|
||||
/** 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;
|
||||
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;
|
||||
enum { S_HIDE = 0,
|
||||
S_SHOW = 1 } hidden_state;
|
||||
|
||||
/** Bar modifier (to show bar when in hide mode). */
|
||||
enum {
|
||||
|
@ -258,8 +261,17 @@ struct Barconfig {
|
|||
M_MOD5 = 7
|
||||
} modifier;
|
||||
|
||||
/** Command that should be run when mouse wheel up button is pressed over
|
||||
* i3bar to override the default behavior. */
|
||||
char *wheel_up_cmd;
|
||||
|
||||
/** Command that should be run when mouse wheel down button is pressed over
|
||||
* i3bar to override the default behavior. */
|
||||
char *wheel_down_cmd;
|
||||
|
||||
/** Bar position (bottom by default). */
|
||||
enum { P_BOTTOM = 0, P_TOP = 1 } position;
|
||||
enum { P_BOTTOM = 0,
|
||||
P_TOP = 1 } position;
|
||||
|
||||
/** Command that should be run to execute i3bar, give a full path if i3bar is not
|
||||
* in your $PATH.
|
||||
|
@ -314,6 +326,20 @@ struct Barconfig {
|
|||
TAILQ_ENTRY(Barconfig) configs;
|
||||
};
|
||||
|
||||
/**
|
||||
* Finds the configuration file to use (either the one specified by
|
||||
* override_configpath), the user’s one or the system default) and calls
|
||||
* parse_file().
|
||||
*
|
||||
* If you specify override_configpath, only this path is used to look for a
|
||||
* configuration file.
|
||||
*
|
||||
* If use_nagbar is false, don't try to start i3-nagbar but log the errors to
|
||||
* stdout/stderr instead.
|
||||
*
|
||||
*/
|
||||
bool parse_configuration(const char *override_configpath, bool use_nagbar);
|
||||
|
||||
/**
|
||||
* Reads the configuration from ~/.i3/config or /etc/i3/config if not found.
|
||||
*
|
||||
|
|
|
@ -61,10 +61,10 @@ CFGFUN(color_single, const char *colorclass, const char *color);
|
|||
CFGFUN(floating_modifier, const char *modifiers);
|
||||
CFGFUN(new_window, const char *windowtype, const char *border, const long width);
|
||||
CFGFUN(workspace, const char *workspace, const char *output);
|
||||
CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *command);
|
||||
CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *whole_window, const char *command);
|
||||
|
||||
CFGFUN(enter_mode, const char *mode);
|
||||
CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *command);
|
||||
CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *whole_window, const char *command);
|
||||
|
||||
CFGFUN(bar_font, const char *font);
|
||||
CFGFUN(bar_mode, const char *mode);
|
||||
|
@ -73,6 +73,8 @@ 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);
|
||||
CFGFUN(bar_wheel_up_cmd, const char *command);
|
||||
CFGFUN(bar_wheel_down_cmd, const char *command);
|
||||
CFGFUN(bar_position, const char *position);
|
||||
CFGFUN(bar_i3bar_command, const char *i3bar_command);
|
||||
CFGFUN(bar_color, const char *colorclass, const char *border, const char *background, const char *text);
|
||||
|
|
|
@ -33,7 +33,10 @@ struct ConfigResultIR *parse_config(const char *input, struct context *context);
|
|||
|
||||
/**
|
||||
* Parses the given file by first replacing the variables, then calling
|
||||
* parse_config and possibly launching i3-nagbar.
|
||||
* parse_config and launching i3-nagbar if use_nagbar is true.
|
||||
*
|
||||
* The return value is a boolean indicating whether there were errors during
|
||||
* parsing.
|
||||
*
|
||||
*/
|
||||
void parse_file(const char *f);
|
||||
bool parse_file(const char *f, bool use_nagbar);
|
||||
|
|
|
@ -47,17 +47,25 @@ typedef struct Match Match;
|
|||
typedef struct Assignment Assignment;
|
||||
typedef struct Window i3Window;
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Helper types
|
||||
*****************************************************************************/
|
||||
typedef enum { D_LEFT, D_RIGHT, D_UP, D_DOWN } direction_t;
|
||||
typedef enum { NO_ORIENTATION = 0, HORIZ, VERT } orientation_t;
|
||||
typedef enum { BS_NORMAL = 0, BS_NONE = 1, BS_PIXEL = 2 } border_style_t;
|
||||
typedef enum { D_LEFT,
|
||||
D_RIGHT,
|
||||
D_UP,
|
||||
D_DOWN } direction_t;
|
||||
typedef enum { NO_ORIENTATION = 0,
|
||||
HORIZ,
|
||||
VERT } orientation_t;
|
||||
typedef enum { BS_NORMAL = 0,
|
||||
BS_NONE = 1,
|
||||
BS_PIXEL = 2 } border_style_t;
|
||||
|
||||
/** parameter to specify whether tree_close() and x_window_kill() should kill
|
||||
* only this specific window or the whole X11 client */
|
||||
typedef enum { DONT_KILL_WINDOW = 0, KILL_WINDOW = 1, KILL_CLIENT = 2 } kill_window_t;
|
||||
typedef enum { DONT_KILL_WINDOW = 0,
|
||||
KILL_WINDOW = 1,
|
||||
KILL_CLIENT = 2 } kill_window_t;
|
||||
|
||||
/** describes if the window is adjacent to the output (physical screen) edges. */
|
||||
typedef enum { ADJ_NONE = 0,
|
||||
|
@ -247,6 +255,11 @@ struct Binding {
|
|||
B_UPON_KEYRELEASE_IGNORE_MODS = 2,
|
||||
} release;
|
||||
|
||||
/** If this is true for a mouse binding, the binding should be executed
|
||||
* when the button is pressed over any part of the window, not just the
|
||||
* title bar (default). */
|
||||
bool whole_window;
|
||||
|
||||
uint32_t number_keycodes;
|
||||
|
||||
/** Keycode to bind */
|
||||
|
@ -267,7 +280,6 @@ struct Binding {
|
|||
* This is an array of number_keycodes size. */
|
||||
xcb_keycode_t *translated_to;
|
||||
|
||||
|
||||
/** Command, like in command mode */
|
||||
char *command;
|
||||
|
||||
|
@ -367,7 +379,9 @@ struct Window {
|
|||
bool doesnt_accept_focus;
|
||||
|
||||
/** Whether the window says it is a dock window */
|
||||
enum { W_NODOCK = 0, W_DOCK_TOP = 1, W_DOCK_BOTTOM = 2 } dock;
|
||||
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;
|
||||
|
@ -407,7 +421,9 @@ struct Match {
|
|||
M_DOCK_BOTTOM = 3
|
||||
} dock;
|
||||
xcb_window_t id;
|
||||
enum { M_ANY = 0, M_TILING, M_FLOATING } floating;
|
||||
enum { M_ANY = 0,
|
||||
M_TILING,
|
||||
M_FLOATING } floating;
|
||||
Con *con_id;
|
||||
|
||||
/* Where the window looking for a match should be inserted:
|
||||
|
@ -419,7 +435,9 @@ struct Match {
|
|||
* (dockareas)
|
||||
*
|
||||
*/
|
||||
enum { M_HERE = 0, M_ASSIGN_WS, M_BELOW } insert_where;
|
||||
enum { M_HERE = 0,
|
||||
M_ASSIGN_WS,
|
||||
M_BELOW } insert_where;
|
||||
|
||||
TAILQ_ENTRY(Match) matches;
|
||||
|
||||
|
@ -470,7 +488,9 @@ struct Assignment {
|
|||
};
|
||||
|
||||
/** Fullscreen modes. Used by Con.fullscreen_mode. */
|
||||
typedef enum { CF_NONE = 0, CF_OUTPUT = 1, CF_GLOBAL = 2 } fullscreen_mode_t;
|
||||
typedef enum { CF_NONE = 0,
|
||||
CF_OUTPUT = 1,
|
||||
CF_GLOBAL = 2 } fullscreen_mode_t;
|
||||
|
||||
/**
|
||||
* A 'Con' represents everything from the X11 root window down to a single X11 window.
|
||||
|
|
|
@ -18,6 +18,24 @@
|
|||
*/
|
||||
void ewmh_update_current_desktop(void);
|
||||
|
||||
/**
|
||||
* Updates _NET_NUMBER_OF_DESKTOPS which we interpret as the number of
|
||||
* noninternal workspaces.
|
||||
*/
|
||||
void ewmh_update_number_of_desktops(void);
|
||||
|
||||
/**
|
||||
* Updates _NET_DESKTOP_NAMES: "The names of all virtual desktops. This is a
|
||||
* list of NULL-terminated strings in UTF-8 encoding"
|
||||
*/
|
||||
void ewmh_update_desktop_names(void);
|
||||
|
||||
/**
|
||||
* Updates _NET_DESKTOP_VIEWPORT, which is an array of pairs of cardinals that
|
||||
* define the top left corner of each desktop's viewport.
|
||||
*/
|
||||
void ewmh_update_desktop_viewport(void);
|
||||
|
||||
/**
|
||||
* Updates _NET_ACTIVE_WINDOW with the currently focused window.
|
||||
*
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <xcb/randr.h>
|
||||
|
||||
extern int randr_base;
|
||||
extern int xkb_base;
|
||||
|
||||
/**
|
||||
* Adds the given sequence to the list of events which are ignored.
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <sys/resource.h>
|
||||
|
||||
#include <xcb/xcb_keysyms.h>
|
||||
#include <xcb/xkb.h>
|
||||
|
||||
#include <X11/XKBlib.h>
|
||||
|
||||
|
|
|
@ -100,3 +100,6 @@ typedef struct i3_ipc_header {
|
|||
|
||||
/** Bar config update will be triggered to update the bar config */
|
||||
#define I3_IPC_EVENT_BARCONFIG_UPDATE (I3_IPC_EVENT_MASK | 4)
|
||||
|
||||
/** The binding event will be triggered when bindings run */
|
||||
#define I3_IPC_EVENT_BINDING (I3_IPC_EVENT_MASK | 5)
|
||||
|
|
|
@ -89,11 +89,17 @@ void ipc_shutdown(void);
|
|||
void dump_node(yajl_gen gen, Con *con, bool inplace_restart);
|
||||
|
||||
/**
|
||||
* For the workspace "focus" event we send, along the usual "change" field,
|
||||
* also the current and previous workspace, in "current" and "old"
|
||||
* respectively.
|
||||
* Generates a json workspace event. Returns a dynamically allocated yajl
|
||||
* generator. Free with yajl_gen_free().
|
||||
*/
|
||||
void ipc_send_workspace_focus_event(Con *current, Con *old);
|
||||
yajl_gen ipc_marshal_workspace_event(const char *change, Con *current, Con *old);
|
||||
|
||||
/**
|
||||
* For the workspace events we send, along with the usual "change" field, also
|
||||
* the workspace container in "current". For focus events, we send the
|
||||
* previously focused workspace in "old".
|
||||
*/
|
||||
void ipc_send_workspace_event(const char *change, Con *current, Con *old);
|
||||
|
||||
/**
|
||||
* For the window events we send, along the usual "change" field,
|
||||
|
@ -105,3 +111,8 @@ void ipc_send_window_event(const char *property, Con *con);
|
|||
* For the barconfig update events, we send the serialized barconfig.
|
||||
*/
|
||||
void ipc_send_barconfig_update_event(Barconfig *barconfig);
|
||||
|
||||
/**
|
||||
* For the binding events, we send the serialized binding struct.
|
||||
*/
|
||||
void ipc_send_binding_event(const char *event_type, Binding *bind);
|
||||
|
|
|
@ -141,6 +141,12 @@ int sasprintf(char **strp, const char *fmt, ...);
|
|||
*/
|
||||
i3String *i3string_from_utf8(const char *from_utf8);
|
||||
|
||||
/**
|
||||
* Build an i3String from an UTF-8 encoded string in Pango markup.
|
||||
*
|
||||
*/
|
||||
i3String *i3string_from_markup(const char *from_markup);
|
||||
|
||||
/**
|
||||
* Build an i3String from an UTF-8 encoded string with fixed length.
|
||||
* To be used when no proper NUL-terminaison is available.
|
||||
|
@ -149,6 +155,13 @@ i3String *i3string_from_utf8(const char *from_utf8);
|
|||
*/
|
||||
i3String *i3string_from_utf8_with_length(const char *from_utf8, size_t num_bytes);
|
||||
|
||||
/**
|
||||
* Build an i3String from an UTF-8 encoded string in Pango markup with fixed
|
||||
* length.
|
||||
*
|
||||
*/
|
||||
i3String *i3string_from_markup_with_length(const char *from_markup, size_t num_bytes);
|
||||
|
||||
/**
|
||||
* Build an i3String from an UCS-2 encoded string.
|
||||
* Returns the newly-allocated i3String.
|
||||
|
@ -193,6 +206,11 @@ const xcb_char2b_t *i3string_as_ucs2(i3String *str);
|
|||
*/
|
||||
size_t i3string_get_num_bytes(i3String *str);
|
||||
|
||||
/**
|
||||
* Whether the given i3String is in Pango markup.
|
||||
*/
|
||||
bool i3string_is_markup(i3String *str);
|
||||
|
||||
/**
|
||||
* Returns the number of glyphs in an i3String.
|
||||
*
|
||||
|
@ -290,7 +308,8 @@ uint32_t get_mod_mask_for(uint32_t keysym,
|
|||
|
||||
/**
|
||||
* Loads a font for usage, also getting its height. If fallback is true,
|
||||
* the fonts 'fixed' or '-misc-*' will be loaded instead of exiting.
|
||||
* the fonts 'fixed' or '-misc-*' will be loaded instead of exiting. If any
|
||||
* font was previously loaded, it will be freed.
|
||||
*
|
||||
*/
|
||||
i3Font load_font(const char *pattern, const bool fallback);
|
||||
|
@ -302,7 +321,8 @@ i3Font load_font(const char *pattern, const bool fallback);
|
|||
void set_font(i3Font *font);
|
||||
|
||||
/**
|
||||
* Frees the resources taken by the current font.
|
||||
* Frees the resources taken by the current font. If no font was previously
|
||||
* loaded, it simply returns.
|
||||
*
|
||||
*/
|
||||
void free_font(void);
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
#pragma once
|
||||
|
||||
/**
|
||||
* Moves the current container in the given direction (TOK_LEFT, TOK_RIGHT,
|
||||
* Moves the given container in the given direction (TOK_LEFT, TOK_RIGHT,
|
||||
* TOK_UP, TOK_DOWN from cmdparse.l)
|
||||
*
|
||||
*/
|
||||
void tree_move(int direction);
|
||||
void tree_move(Con *con, int direction);
|
||||
|
|
132
include/queue.h
132
include/queue.h
|
@ -124,29 +124,35 @@ struct { \
|
|||
/*
|
||||
* Singly-linked List functions.
|
||||
*/
|
||||
#define SLIST_INIT(head) { \
|
||||
#define SLIST_INIT(head) \
|
||||
{ \
|
||||
SLIST_FIRST(head) = SLIST_END(head); \
|
||||
}
|
||||
|
||||
#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
|
||||
#define SLIST_INSERT_AFTER(slistelm, elm, field) \
|
||||
do { \
|
||||
(elm)->field.sle_next = (slistelm)->field.sle_next; \
|
||||
(slistelm)->field.sle_next = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_INSERT_HEAD(head, elm, field) do { \
|
||||
#define SLIST_INSERT_HEAD(head, elm, field) \
|
||||
do { \
|
||||
(elm)->field.sle_next = (head)->slh_first; \
|
||||
(head)->slh_first = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_REMOVE_NEXT(head, elm, field) do { \
|
||||
#define SLIST_REMOVE_NEXT(head, elm, field) \
|
||||
do { \
|
||||
(elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_REMOVE_HEAD(head, field) do { \
|
||||
#define SLIST_REMOVE_HEAD(head, field) \
|
||||
do { \
|
||||
(head)->slh_first = (head)->slh_first->field.sle_next; \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_REMOVE(head, elm, type, field) do { \
|
||||
#define SLIST_REMOVE(head, elm, type, field) \
|
||||
do { \
|
||||
if ((head)->slh_first == (elm)) { \
|
||||
SLIST_REMOVE_HEAD((head), field); \
|
||||
} else { \
|
||||
|
@ -154,8 +160,7 @@ struct { \
|
|||
\
|
||||
while (curelm->field.sle_next != (elm)) \
|
||||
curelm = curelm->field.sle_next; \
|
||||
curelm->field.sle_next = \
|
||||
curelm->field.sle_next->field.sle_next; \
|
||||
curelm->field.sle_next = curelm->field.sle_next->field.sle_next; \
|
||||
_Q_INVALIDATE((elm)->field.sle_next); \
|
||||
} \
|
||||
} while (0)
|
||||
|
@ -193,45 +198,48 @@ struct { \
|
|||
/*
|
||||
* List functions.
|
||||
*/
|
||||
#define LIST_INIT(head) do { \
|
||||
#define LIST_INIT(head) \
|
||||
do { \
|
||||
LIST_FIRST(head) = LIST_END(head); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
|
||||
#define LIST_INSERT_AFTER(listelm, elm, field) \
|
||||
do { \
|
||||
if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
|
||||
(listelm)->field.le_next->field.le_prev = \
|
||||
&(elm)->field.le_next; \
|
||||
(listelm)->field.le_next->field.le_prev = &(elm)->field.le_next; \
|
||||
(listelm)->field.le_next = (elm); \
|
||||
(elm)->field.le_prev = &(listelm)->field.le_next; \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
|
||||
#define LIST_INSERT_BEFORE(listelm, elm, field) \
|
||||
do { \
|
||||
(elm)->field.le_prev = (listelm)->field.le_prev; \
|
||||
(elm)->field.le_next = (listelm); \
|
||||
*(listelm)->field.le_prev = (elm); \
|
||||
(listelm)->field.le_prev = &(elm)->field.le_next; \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_HEAD(head, elm, field) do { \
|
||||
#define LIST_INSERT_HEAD(head, elm, field) \
|
||||
do { \
|
||||
if (((elm)->field.le_next = (head)->lh_first) != NULL) \
|
||||
(head)->lh_first->field.le_prev = &(elm)->field.le_next; \
|
||||
(head)->lh_first = (elm); \
|
||||
(elm)->field.le_prev = &(head)->lh_first; \
|
||||
} while (0)
|
||||
|
||||
#define LIST_REMOVE(elm, field) do { \
|
||||
#define LIST_REMOVE(elm, field) \
|
||||
do { \
|
||||
if ((elm)->field.le_next != NULL) \
|
||||
(elm)->field.le_next->field.le_prev = \
|
||||
(elm)->field.le_prev; \
|
||||
(elm)->field.le_next->field.le_prev = (elm)->field.le_prev; \
|
||||
*(elm)->field.le_prev = (elm)->field.le_next; \
|
||||
_Q_INVALIDATE((elm)->field.le_prev); \
|
||||
_Q_INVALIDATE((elm)->field.le_next); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_REPLACE(elm, elm2, field) do { \
|
||||
#define LIST_REPLACE(elm, elm2, field) \
|
||||
do { \
|
||||
if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
|
||||
(elm2)->field.le_next->field.le_prev = \
|
||||
&(elm2)->field.le_next; \
|
||||
(elm2)->field.le_next->field.le_prev = &(elm2)->field.le_next; \
|
||||
(elm2)->field.le_prev = (elm)->field.le_prev; \
|
||||
*(elm2)->field.le_prev = (elm2); \
|
||||
_Q_INVALIDATE((elm)->field.le_prev); \
|
||||
|
@ -271,30 +279,35 @@ struct { \
|
|||
/*
|
||||
* Simple queue functions.
|
||||
*/
|
||||
#define SIMPLEQ_INIT(head) do { \
|
||||
#define SIMPLEQ_INIT(head) \
|
||||
do { \
|
||||
(head)->sqh_first = NULL; \
|
||||
(head)->sqh_last = &(head)->sqh_first; \
|
||||
} while (0)
|
||||
|
||||
#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
|
||||
#define SIMPLEQ_INSERT_HEAD(head, elm, field) \
|
||||
do { \
|
||||
if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
|
||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||
(head)->sqh_first = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
|
||||
#define SIMPLEQ_INSERT_TAIL(head, elm, field) \
|
||||
do { \
|
||||
(elm)->field.sqe_next = NULL; \
|
||||
*(head)->sqh_last = (elm); \
|
||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||
} while (0)
|
||||
|
||||
#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) \
|
||||
do { \
|
||||
if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL) \
|
||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||
(listelm)->field.sqe_next = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
|
||||
#define SIMPLEQ_REMOVE_HEAD(head, field) \
|
||||
do { \
|
||||
if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
|
||||
(head)->sqh_last = &(head)->sqh_first; \
|
||||
} while (0)
|
||||
|
@ -344,49 +357,52 @@ struct { \
|
|||
/*
|
||||
* Tail queue functions.
|
||||
*/
|
||||
#define TAILQ_INIT(head) do { \
|
||||
#define TAILQ_INIT(head) \
|
||||
do { \
|
||||
(head)->tqh_first = NULL; \
|
||||
(head)->tqh_last = &(head)->tqh_first; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
|
||||
#define TAILQ_INSERT_HEAD(head, elm, field) \
|
||||
do { \
|
||||
if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
|
||||
(head)->tqh_first->field.tqe_prev = \
|
||||
&(elm)->field.tqe_next; \
|
||||
(head)->tqh_first->field.tqe_prev = &(elm)->field.tqe_next; \
|
||||
else \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
(head)->tqh_first = (elm); \
|
||||
(elm)->field.tqe_prev = &(head)->tqh_first; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
|
||||
#define TAILQ_INSERT_TAIL(head, elm, field) \
|
||||
do { \
|
||||
(elm)->field.tqe_next = NULL; \
|
||||
(elm)->field.tqe_prev = (head)->tqh_last; \
|
||||
*(head)->tqh_last = (elm); \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) \
|
||||
do { \
|
||||
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL) \
|
||||
(elm)->field.tqe_next->field.tqe_prev = \
|
||||
&(elm)->field.tqe_next; \
|
||||
(elm)->field.tqe_next->field.tqe_prev = &(elm)->field.tqe_next; \
|
||||
else \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
(listelm)->field.tqe_next = (elm); \
|
||||
(elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
|
||||
#define TAILQ_INSERT_BEFORE(listelm, elm, field) \
|
||||
do { \
|
||||
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
|
||||
(elm)->field.tqe_next = (listelm); \
|
||||
*(listelm)->field.tqe_prev = (elm); \
|
||||
(listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_REMOVE(head, elm, field) do { \
|
||||
#define TAILQ_REMOVE(head, elm, field) \
|
||||
do { \
|
||||
if (((elm)->field.tqe_next) != NULL) \
|
||||
(elm)->field.tqe_next->field.tqe_prev = \
|
||||
(elm)->field.tqe_prev; \
|
||||
(elm)->field.tqe_next->field.tqe_prev = (elm)->field.tqe_prev; \
|
||||
else \
|
||||
(head)->tqh_last = (elm)->field.tqe_prev; \
|
||||
*(elm)->field.tqe_prev = (elm)->field.tqe_next; \
|
||||
|
@ -394,10 +410,10 @@ struct { \
|
|||
_Q_INVALIDATE((elm)->field.tqe_next); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_REPLACE(head, elm, elm2, field) do { \
|
||||
#define TAILQ_REPLACE(head, elm, elm2, field) \
|
||||
do { \
|
||||
if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
|
||||
(elm2)->field.tqe_next->field.tqe_prev = \
|
||||
&(elm2)->field.tqe_next; \
|
||||
(elm2)->field.tqe_next->field.tqe_prev = &(elm2)->field.tqe_next; \
|
||||
else \
|
||||
(head)->tqh_last = &(elm2)->field.tqe_next; \
|
||||
(elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
|
||||
|
@ -407,7 +423,8 @@ struct { \
|
|||
} while (0)
|
||||
|
||||
/* Swaps two consecutive elements. 'second' *MUST* follow 'first' */
|
||||
#define TAILQ_SWAP(first, second, head, field) do { \
|
||||
#define TAILQ_SWAP(first, second, head, field) \
|
||||
do { \
|
||||
*((first)->field.tqe_prev) = (second); \
|
||||
(second)->field.tqe_prev = (first)->field.tqe_prev; \
|
||||
(first)->field.tqe_prev = &((second)->field.tqe_next); \
|
||||
|
@ -461,12 +478,14 @@ struct { \
|
|||
/*
|
||||
* Circular queue functions.
|
||||
*/
|
||||
#define CIRCLEQ_INIT(head) do { \
|
||||
#define CIRCLEQ_INIT(head) \
|
||||
do { \
|
||||
(head)->cqh_first = CIRCLEQ_END(head); \
|
||||
(head)->cqh_last = CIRCLEQ_END(head); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) \
|
||||
do { \
|
||||
(elm)->field.cqe_next = (listelm)->field.cqe_next; \
|
||||
(elm)->field.cqe_prev = (listelm); \
|
||||
if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \
|
||||
|
@ -476,7 +495,8 @@ struct { \
|
|||
(listelm)->field.cqe_next = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
|
||||
#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) \
|
||||
do { \
|
||||
(elm)->field.cqe_next = (listelm); \
|
||||
(elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
|
||||
if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \
|
||||
|
@ -486,7 +506,8 @@ struct { \
|
|||
(listelm)->field.cqe_prev = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
|
||||
#define CIRCLEQ_INSERT_HEAD(head, elm, field) \
|
||||
do { \
|
||||
(elm)->field.cqe_next = (head)->cqh_first; \
|
||||
(elm)->field.cqe_prev = CIRCLEQ_END(head); \
|
||||
if ((head)->cqh_last == CIRCLEQ_END(head)) \
|
||||
|
@ -496,7 +517,8 @@ struct { \
|
|||
(head)->cqh_first = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
|
||||
#define CIRCLEQ_INSERT_TAIL(head, elm, field) \
|
||||
do { \
|
||||
(elm)->field.cqe_next = CIRCLEQ_END(head); \
|
||||
(elm)->field.cqe_prev = (head)->cqh_last; \
|
||||
if ((head)->cqh_first == CIRCLEQ_END(head)) \
|
||||
|
@ -506,29 +528,27 @@ struct { \
|
|||
(head)->cqh_last = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_REMOVE(head, elm, field) do { \
|
||||
#define CIRCLEQ_REMOVE(head, elm, field) \
|
||||
do { \
|
||||
if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \
|
||||
(head)->cqh_last = (elm)->field.cqe_prev; \
|
||||
else \
|
||||
(elm)->field.cqe_next->field.cqe_prev = \
|
||||
(elm)->field.cqe_prev; \
|
||||
(elm)->field.cqe_next->field.cqe_prev = (elm)->field.cqe_prev; \
|
||||
if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \
|
||||
(head)->cqh_first = (elm)->field.cqe_next; \
|
||||
else \
|
||||
(elm)->field.cqe_prev->field.cqe_next = \
|
||||
(elm)->field.cqe_next; \
|
||||
(elm)->field.cqe_prev->field.cqe_next = (elm)->field.cqe_next; \
|
||||
_Q_INVALIDATE((elm)->field.cqe_prev); \
|
||||
_Q_INVALIDATE((elm)->field.cqe_next); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \
|
||||
if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \
|
||||
CIRCLEQ_END(head)) \
|
||||
#define CIRCLEQ_REPLACE(head, elm, elm2, field) \
|
||||
do { \
|
||||
if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == CIRCLEQ_END(head)) \
|
||||
(head)->cqh_last = (elm2); \
|
||||
else \
|
||||
(elm2)->field.cqe_next->field.cqe_prev = (elm2); \
|
||||
if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \
|
||||
CIRCLEQ_END(head)) \
|
||||
if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == CIRCLEQ_END(head)) \
|
||||
(head)->cqh_first = (elm2); \
|
||||
else \
|
||||
(elm2)->field.cqe_prev->field.cqe_next = (elm2); \
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
* (immediately), the application is reparented to init (process-id 1), which
|
||||
* correctly handles childs, so we don’t have to do it :-).
|
||||
*
|
||||
* The shell is determined by looking for the SHELL environment variable. If
|
||||
* it does not exist, /bin/sh is used.
|
||||
* The shell used to start applications is the system's bourne shell (i.e.,
|
||||
* /bin/sh).
|
||||
*
|
||||
* The no_startup_id flag determines whether a startup notification context
|
||||
* (and ID) should be created, which is the default and encouraged behavior.
|
||||
|
|
|
@ -15,12 +15,14 @@
|
|||
#include "data.h"
|
||||
|
||||
#define die(...) errx(EXIT_FAILURE, __VA_ARGS__);
|
||||
#define exit_if_null(pointer, ...) { if (pointer == NULL) die(__VA_ARGS__); }
|
||||
#define exit_if_null(pointer, ...) \
|
||||
{ \
|
||||
if (pointer == NULL) \
|
||||
die(__VA_ARGS__); \
|
||||
}
|
||||
#define STARTS_WITH(string, needle) (strncasecmp(string, needle, strlen(needle)) == 0)
|
||||
#define CIRCLEQ_NEXT_OR_NULL(head, elm, field) (CIRCLEQ_NEXT(elm, field) != CIRCLEQ_END(head) ? \
|
||||
CIRCLEQ_NEXT(elm, field) : NULL)
|
||||
#define CIRCLEQ_PREV_OR_NULL(head, elm, field) (CIRCLEQ_PREV(elm, field) != CIRCLEQ_END(head) ? \
|
||||
CIRCLEQ_PREV(elm, field) : NULL)
|
||||
#define CIRCLEQ_NEXT_OR_NULL(head, elm, field) (CIRCLEQ_NEXT(elm, field) != CIRCLEQ_END(head) ? CIRCLEQ_NEXT(elm, field) : NULL)
|
||||
#define CIRCLEQ_PREV_OR_NULL(head, elm, field) (CIRCLEQ_PREV(elm, field) != CIRCLEQ_END(head) ? CIRCLEQ_PREV(elm, field) : NULL)
|
||||
#define FOR_TABLE(workspace) \
|
||||
for (int cols = 0; cols < (workspace)->cols; cols++) \
|
||||
for (int rows = 0; rows < (workspace)->rows; rows++)
|
||||
|
@ -43,13 +45,13 @@
|
|||
break; \
|
||||
}
|
||||
|
||||
#define FREE(pointer) do { \
|
||||
#define FREE(pointer) \
|
||||
do { \
|
||||
if (pointer != NULL) { \
|
||||
free(pointer); \
|
||||
pointer = NULL; \
|
||||
} \
|
||||
} \
|
||||
while (0)
|
||||
} while (0)
|
||||
|
||||
#define CALL(obj, member, ...) obj->member(obj, ##__VA_ARGS__)
|
||||
|
||||
|
|
|
@ -100,7 +100,6 @@ void workspace_back_and_forth(void);
|
|||
*/
|
||||
Con *workspace_back_and_forth_get(void);
|
||||
|
||||
|
||||
#if 0
|
||||
/**
|
||||
* Assigns the given workspace to the given screen by correctly updating its
|
||||
|
|
|
@ -108,7 +108,6 @@ void xcb_raise_window(xcb_connection_t *conn, xcb_window_t window);
|
|||
*/
|
||||
void xcb_set_window_rect(xcb_connection_t *conn, xcb_window_t window, Rect r);
|
||||
|
||||
|
||||
bool xcb_reply_contains_atom(xcb_get_property_reply_t *prop, xcb_atom_t atom);
|
||||
|
||||
/**
|
||||
|
|
15
libi3/dpi.c
15
libi3/dpi.c
|
@ -1,3 +1,10 @@
|
|||
/*
|
||||
* vim:ts=4:sw=4:expandtab
|
||||
*
|
||||
* i3 - an improved dynamic tiling window manager
|
||||
* © 2009-2014 Michael Stapelberg and contributors (see also: LICENSE)
|
||||
*
|
||||
*/
|
||||
#include "libi3.h"
|
||||
#include <math.h>
|
||||
|
||||
|
@ -12,5 +19,13 @@ extern xcb_screen_t *root_screen;
|
|||
int logical_px(const int logical) {
|
||||
const int dpi = (double)root_screen->height_in_pixels * 25.4 /
|
||||
(double)root_screen->height_in_millimeters;
|
||||
/* There are many misconfigurations out there, i.e. systems with screens
|
||||
* whose dpi is in fact higher than 96 dpi, but not significantly higher,
|
||||
* so software was never adapted. We could tell people to reconfigure their
|
||||
* systems to 96 dpi in order to get the behavior they expect/are used to,
|
||||
* but since we can easily detect this case in code, let’s do it for them.
|
||||
*/
|
||||
if ((dpi / 96.0) < 1.25)
|
||||
return logical;
|
||||
return ceil((dpi / 96.0) * logical);
|
||||
}
|
||||
|
|
41
libi3/font.c
41
libi3/font.c
|
@ -102,7 +102,8 @@ static bool load_pango_font(i3Font *font, const char *desc) {
|
|||
*
|
||||
*/
|
||||
static void draw_text_pango(const char *text, size_t text_len,
|
||||
xcb_drawable_t drawable, int x, int y, int max_width) {
|
||||
xcb_drawable_t drawable, int x, int y,
|
||||
int max_width, bool is_markup) {
|
||||
/* Create the Pango layout */
|
||||
/* root_visual_type is cached in load_pango_font */
|
||||
cairo_surface_t *surface = cairo_xcb_surface_create(conn, drawable,
|
||||
|
@ -116,6 +117,9 @@ static void draw_text_pango(const char *text, size_t text_len,
|
|||
pango_layout_set_wrap(layout, PANGO_WRAP_CHAR);
|
||||
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
|
||||
|
||||
if (is_markup)
|
||||
pango_layout_set_markup(layout, text, text_len);
|
||||
else
|
||||
pango_layout_set_text(layout, text, text_len);
|
||||
|
||||
/* Do the drawing */
|
||||
|
@ -135,7 +139,7 @@ static void draw_text_pango(const char *text, size_t text_len,
|
|||
* Calculate the text width using Pango rendering.
|
||||
*
|
||||
*/
|
||||
static int predict_text_width_pango(const char *text, size_t text_len) {
|
||||
static int predict_text_width_pango(const char *text, size_t text_len, bool is_markup) {
|
||||
/* Create a dummy Pango layout */
|
||||
/* root_visual_type is cached in load_pango_font */
|
||||
cairo_surface_t *surface = cairo_xcb_surface_create(conn, root_screen->root, root_visual_type, 1, 1);
|
||||
|
@ -145,7 +149,12 @@ static int predict_text_width_pango(const char *text, size_t text_len) {
|
|||
/* Get the font width */
|
||||
gint width;
|
||||
pango_layout_set_font_description(layout, savedFont->specific.pango_desc);
|
||||
|
||||
if (is_markup)
|
||||
pango_layout_set_markup(layout, text, text_len);
|
||||
else
|
||||
pango_layout_set_text(layout, text, text_len);
|
||||
|
||||
pango_cairo_update_layout(cr, layout);
|
||||
pango_layout_get_pixel_size(layout, &width, NULL);
|
||||
|
||||
|
@ -160,13 +169,23 @@ static int predict_text_width_pango(const char *text, size_t text_len) {
|
|||
|
||||
/*
|
||||
* Loads a font for usage, also getting its metrics. If fallback is true,
|
||||
* the fonts 'fixed' or '-misc-*' will be loaded instead of exiting.
|
||||
* the fonts 'fixed' or '-misc-*' will be loaded instead of exiting. If any
|
||||
* font was previously loaded, it will be freed.
|
||||
*
|
||||
*/
|
||||
i3Font load_font(const char *pattern, const bool fallback) {
|
||||
/* if any font was previously loaded, free it now */
|
||||
free_font();
|
||||
|
||||
i3Font font;
|
||||
font.type = FONT_TYPE_NONE;
|
||||
|
||||
/* No XCB connction, return early because we're just validating the
|
||||
* configuration file. */
|
||||
if (conn == NULL) {
|
||||
return font;
|
||||
}
|
||||
|
||||
#if PANGO_SUPPORT
|
||||
/* Try to load a pango font if specified */
|
||||
if (strlen(pattern) > strlen("pango:") && !strncmp(pattern, "pango:", strlen("pango:"))) {
|
||||
|
@ -251,10 +270,15 @@ void set_font(i3Font *font) {
|
|||
}
|
||||
|
||||
/*
|
||||
* Frees the resources taken by the current font.
|
||||
* Frees the resources taken by the current font. If no font was previously
|
||||
* loaded, it simply returns.
|
||||
*
|
||||
*/
|
||||
void free_font(void) {
|
||||
/* if there is no saved font, simply return */
|
||||
if (savedFont == NULL)
|
||||
return;
|
||||
|
||||
free(savedFont->pattern);
|
||||
switch (savedFont->type) {
|
||||
case FONT_TYPE_NONE:
|
||||
|
@ -277,6 +301,8 @@ void free_font(void) {
|
|||
assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
savedFont = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -366,7 +392,7 @@ void draw_text(i3String *text, xcb_drawable_t drawable,
|
|||
case FONT_TYPE_PANGO:
|
||||
/* Render the text using Pango */
|
||||
draw_text_pango(i3string_as_utf8(text), i3string_get_num_bytes(text),
|
||||
drawable, x, y, max_width);
|
||||
drawable, x, y, max_width, i3string_is_markup(text));
|
||||
return;
|
||||
#endif
|
||||
default:
|
||||
|
@ -405,7 +431,7 @@ void draw_text_ascii(const char *text, xcb_drawable_t drawable,
|
|||
case FONT_TYPE_PANGO:
|
||||
/* Render the text using Pango */
|
||||
draw_text_pango(text, strlen(text),
|
||||
drawable, x, y, max_width);
|
||||
drawable, x, y, max_width, false);
|
||||
return;
|
||||
#endif
|
||||
default:
|
||||
|
@ -501,7 +527,8 @@ int predict_text_width(i3String *text) {
|
|||
#if PANGO_SUPPORT
|
||||
case FONT_TYPE_PANGO:
|
||||
/* Calculate extents using Pango */
|
||||
return predict_text_width_pango(i3string_as_utf8(text), i3string_get_num_bytes(text));
|
||||
return predict_text_width_pango(i3string_as_utf8(text), i3string_get_num_bytes(text),
|
||||
i3string_is_markup(text));
|
||||
#endif
|
||||
default:
|
||||
assert(false);
|
||||
|
|
|
@ -20,6 +20,7 @@ struct _i3String {
|
|||
xcb_char2b_t *ucs2;
|
||||
size_t num_glyphs;
|
||||
size_t num_bytes;
|
||||
bool is_markup;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -39,6 +40,19 @@ i3String *i3string_from_utf8(const char *from_utf8) {
|
|||
return str;
|
||||
}
|
||||
|
||||
/*
|
||||
* Build an i3String from an UTF-8 encoded string in Pango markup.
|
||||
*
|
||||
*/
|
||||
i3String *i3string_from_markup(const char *from_markup) {
|
||||
i3String *str = i3string_from_utf8(from_markup);
|
||||
|
||||
/* Set the markup flag */
|
||||
str->is_markup = true;
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
/*
|
||||
* Build an i3String from an UTF-8 encoded string with fixed length.
|
||||
* To be used when no proper NUL-terminaison is available.
|
||||
|
@ -59,6 +73,20 @@ i3String *i3string_from_utf8_with_length(const char *from_utf8, size_t num_bytes
|
|||
return str;
|
||||
}
|
||||
|
||||
/*
|
||||
* Build an i3String from an UTF-8 encoded string in Pango markup with fixed
|
||||
* length.
|
||||
*
|
||||
*/
|
||||
i3String *i3string_from_markup_with_length(const char *from_markup, size_t num_bytes) {
|
||||
i3String *str = i3string_from_utf8_with_length(from_markup, num_bytes);
|
||||
|
||||
/* set the markup flag */
|
||||
str->is_markup = true;
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
/*
|
||||
* Build an i3String from an UCS-2 encoded string.
|
||||
* Returns the newly-allocated i3String.
|
||||
|
@ -133,6 +161,13 @@ size_t i3string_get_num_bytes(i3String *str) {
|
|||
return str->num_bytes;
|
||||
}
|
||||
|
||||
/*
|
||||
* Whether the given i3String is in Pango markup.
|
||||
*/
|
||||
bool i3string_is_markup(i3String *str) {
|
||||
return str->is_markup;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the number of glyphs in an i3String.
|
||||
*
|
||||
|
|
|
@ -7,7 +7,7 @@ template::[header-declarations]
|
|||
<refentrytitle>{mantitle}</refentrytitle>
|
||||
<manvolnum>{manvolnum}</manvolnum>
|
||||
<refmiscinfo class="source">i3</refmiscinfo>
|
||||
<refmiscinfo class="version">4.8</refmiscinfo>
|
||||
<refmiscinfo class="version">4.9</refmiscinfo>
|
||||
<refmiscinfo class="manual">i3 Manual</refmiscinfo>
|
||||
</refmeta>
|
||||
<refnamediv>
|
||||
|
|
|
@ -22,6 +22,7 @@ is appropriate for the distribution.
|
|||
It tries to start one of the following (in that order):
|
||||
|
||||
* $TERMINAL (this is a non-standard variable)
|
||||
* x-terminal-emulator (only present on Debian and derivatives)
|
||||
* urxvt
|
||||
* rxvt
|
||||
* terminator
|
||||
|
|
|
@ -230,7 +230,7 @@ bindsym Mod1+h split h
|
|||
bindsym Mod1+v split v
|
||||
|
||||
# enter fullscreen mode for the focused container
|
||||
bindsym Mod1+f fullscreen
|
||||
bindsym Mod1+f fullscreen toggle
|
||||
|
||||
# change container layout (stacked, tabbed, default)
|
||||
bindsym Mod1+s layout stacking
|
||||
|
|
|
@ -156,12 +156,28 @@ state KILL:
|
|||
end
|
||||
-> call cmd_kill($kill_mode)
|
||||
|
||||
# fullscreen enable|toggle [global]
|
||||
# fullscreen disable
|
||||
# fullscreen [global]
|
||||
state FULLSCREEN:
|
||||
fullscreen_mode = 'global'
|
||||
-> call cmd_fullscreen($fullscreen_mode)
|
||||
action = 'disable'
|
||||
-> call cmd_fullscreen($action, "output")
|
||||
action = 'enable', 'toggle'
|
||||
-> FULLSCREEN_MODE
|
||||
action = ''
|
||||
-> FULLSCREEN_COMPAT
|
||||
|
||||
state FULLSCREEN_MODE:
|
||||
mode = 'global'
|
||||
-> call cmd_fullscreen($action, $mode)
|
||||
end
|
||||
-> call cmd_fullscreen($fullscreen_mode)
|
||||
-> call cmd_fullscreen($action, "output")
|
||||
|
||||
state FULLSCREEN_COMPAT:
|
||||
mode = 'global'
|
||||
-> call cmd_fullscreen("toggle", $mode)
|
||||
end
|
||||
-> call cmd_fullscreen("toggle", "output")
|
||||
|
||||
# split v|h|vertical|horizontal
|
||||
state SPLIT:
|
||||
|
|
|
@ -278,6 +278,8 @@ state FONT:
|
|||
state BINDING:
|
||||
release = '--release'
|
||||
->
|
||||
whole_window = '--whole-window'
|
||||
->
|
||||
modifiers = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Shift', 'Control', 'Ctrl', 'Mode_switch', '$mod'
|
||||
->
|
||||
'+'
|
||||
|
@ -288,8 +290,10 @@ state BINDING:
|
|||
state BINDCOMMAND:
|
||||
release = '--release'
|
||||
->
|
||||
whole_window = '--whole-window'
|
||||
->
|
||||
command = string
|
||||
-> call cfg_binding($bindtype, $modifiers, $key, $release, $command)
|
||||
-> call cfg_binding($bindtype, $modifiers, $key, $release, $whole_window, $command)
|
||||
|
||||
################################################################################
|
||||
# Mode configuration
|
||||
|
@ -333,8 +337,10 @@ state MODE_BINDING:
|
|||
state MODE_BINDCOMMAND:
|
||||
release = '--release'
|
||||
->
|
||||
whole_window = '--whole-window'
|
||||
->
|
||||
command = string
|
||||
-> call cfg_mode_binding($bindtype, $modifiers, $key, $release, $command); MODE
|
||||
-> call cfg_mode_binding($bindtype, $modifiers, $key, $release, $whole_window, $command); MODE
|
||||
|
||||
################################################################################
|
||||
# Bar configuration (i3bar)
|
||||
|
@ -358,6 +364,8 @@ state BAR:
|
|||
'hidden_state' -> BAR_HIDDEN_STATE
|
||||
'id' -> BAR_ID
|
||||
'modifier' -> BAR_MODIFIER
|
||||
'wheel_up_cmd' -> BAR_WHEEL_UP_CMD
|
||||
'wheel_down_cmd' -> BAR_WHEEL_DOWN_CMD
|
||||
'position' -> BAR_POSITION
|
||||
'output' -> BAR_OUTPUT
|
||||
'tray_output' -> BAR_TRAY_OUTPUT
|
||||
|
@ -403,6 +411,14 @@ state BAR_MODIFIER:
|
|||
modifier = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Control', 'Ctrl', 'Shift'
|
||||
-> call cfg_bar_modifier($modifier); BAR
|
||||
|
||||
state BAR_WHEEL_UP_CMD:
|
||||
command = string
|
||||
-> call cfg_bar_wheel_up_cmd($command); BAR
|
||||
|
||||
state BAR_WHEEL_DOWN_CMD:
|
||||
command = string
|
||||
-> call cfg_bar_wheel_down_cmd($command); BAR
|
||||
|
||||
state BAR_POSITION:
|
||||
position = 'top', 'bottom'
|
||||
-> call cfg_bar_position($position); BAR
|
||||
|
@ -412,7 +428,7 @@ state BAR_OUTPUT:
|
|||
-> call cfg_bar_output($output); BAR
|
||||
|
||||
state BAR_TRAY_OUTPUT:
|
||||
output = string
|
||||
output = word
|
||||
-> call cfg_bar_tray_output($output); BAR
|
||||
|
||||
state BAR_FONT:
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
*/
|
||||
#include "all.h"
|
||||
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
|
||||
pid_t command_error_nagbar_pid = -1;
|
||||
|
||||
/*
|
||||
|
@ -47,10 +49,11 @@ static struct Mode *mode_from_name(const char *name) {
|
|||
*
|
||||
*/
|
||||
Binding *configure_binding(const char *bindtype, const char *modifiers, const char *input_code,
|
||||
const char *release, const char *command, const char *modename) {
|
||||
const char *release, const char *whole_window, const char *command, const char *modename) {
|
||||
Binding *new_binding = scalloc(sizeof(Binding));
|
||||
DLOG("bindtype %s, modifiers %s, input code %s, release %s\n", bindtype, modifiers, input_code, release);
|
||||
new_binding->release = (release != NULL ? B_UPON_KEYRELEASE : B_UPON_KEYPRESS);
|
||||
new_binding->whole_window = (whole_window != NULL);
|
||||
if (strcmp(bindtype, "bindsym") == 0) {
|
||||
new_binding->input_type = (strncasecmp(input_code, "button", (sizeof("button") - 1)) == 0
|
||||
? B_MOUSE
|
||||
|
@ -263,8 +266,8 @@ void translate_keysyms(void) {
|
|||
continue;
|
||||
|
||||
/* We need to translate the symbol to a keycode */
|
||||
keysym = XStringToKeysym(bind->symbol);
|
||||
if (keysym == NoSymbol) {
|
||||
keysym = xkb_keysym_from_name(bind->symbol, XKB_KEYSYM_NO_FLAGS);
|
||||
if (keysym == XKB_KEY_NoSymbol) {
|
||||
ELOG("Could not translate string to key symbol: \"%s\"\n",
|
||||
bind->symbol);
|
||||
continue;
|
||||
|
@ -379,18 +382,57 @@ void check_for_duplicate_bindings(struct context *context) {
|
|||
}
|
||||
|
||||
/*
|
||||
* Runs the given binding and handles parse errors. Returns a CommandResult for
|
||||
* running the binding's command. Caller should render tree if
|
||||
* needs_tree_render is true. Free with command_result_free().
|
||||
* Creates a dynamically allocated copy of bind.
|
||||
*/
|
||||
static Binding *binding_copy(Binding *bind) {
|
||||
Binding *ret = smalloc(sizeof(Binding));
|
||||
*ret = *bind;
|
||||
if (bind->symbol != NULL)
|
||||
ret->symbol = strdup(bind->symbol);
|
||||
if (bind->command != NULL)
|
||||
ret->command = strdup(bind->command);
|
||||
if (bind->translated_to != NULL) {
|
||||
ret->translated_to = smalloc(sizeof(xcb_keycode_t) * bind->number_keycodes);
|
||||
memcpy(ret->translated_to, bind->translated_to, sizeof(xcb_keycode_t) * bind->number_keycodes);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Frees the binding. If bind is null, it simply returns.
|
||||
*/
|
||||
void binding_free(Binding *bind) {
|
||||
if (bind == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
FREE(bind->symbol);
|
||||
FREE(bind->translated_to);
|
||||
FREE(bind->command);
|
||||
FREE(bind);
|
||||
}
|
||||
|
||||
/*
|
||||
* Runs the given binding and handles parse errors. If con is passed, it will
|
||||
* execute the command binding with that container selected by criteria.
|
||||
* Returns a CommandResult for running the binding's command. Caller should
|
||||
* render tree if needs_tree_render is true. Free with command_result_free().
|
||||
*
|
||||
*/
|
||||
CommandResult *run_binding(Binding *bind) {
|
||||
/* We need to copy the command since “reload” may be part of the command,
|
||||
* and then the memory that bind->command points to may not contain the
|
||||
CommandResult *run_binding(Binding *bind, Con *con) {
|
||||
char *command;
|
||||
|
||||
/* We need to copy the binding and command since “reload” may be part of
|
||||
* the command, and then the memory that bind points to may not contain the
|
||||
* same data anymore. */
|
||||
char *command_copy = sstrdup(bind->command);
|
||||
CommandResult *result = parse_command(command_copy, NULL);
|
||||
free(command_copy);
|
||||
if (con == NULL)
|
||||
command = sstrdup(bind->command);
|
||||
else
|
||||
sasprintf(&command, "[con_id=\"%d\"] %s", con, bind->command);
|
||||
|
||||
Binding *bind_cp = binding_copy(bind);
|
||||
CommandResult *result = parse_command(command, NULL);
|
||||
free(command);
|
||||
|
||||
if (result->needs_tree_render)
|
||||
tree_render();
|
||||
|
@ -414,7 +456,8 @@ CommandResult *run_binding(Binding *bind) {
|
|||
free(pageraction);
|
||||
}
|
||||
|
||||
/* TODO: emit event for running a binding */
|
||||
ipc_send_binding_event("run", bind_cp);
|
||||
binding_free(bind_cp);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
39
src/click.c
39
src/click.c
|
@ -177,6 +177,34 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod
|
|||
if (con->parent->type == CT_DOCKAREA)
|
||||
goto done;
|
||||
|
||||
/* if the user has bound an action to this click, it should override the
|
||||
* default behavior. */
|
||||
if (dest == CLICK_DECORATION || dest == CLICK_INSIDE) {
|
||||
Binding *bind = get_binding_from_xcb_event((xcb_generic_event_t *)event);
|
||||
/* clicks over a window decoration will always trigger the binding and
|
||||
* clicks on the inside of the window will only trigger a binding if
|
||||
* the --whole-window flag was given for the binding. */
|
||||
if (bind && (dest == CLICK_DECORATION || bind->whole_window)) {
|
||||
CommandResult *result = run_binding(bind, con);
|
||||
|
||||
/* ASYNC_POINTER eats the event */
|
||||
xcb_allow_events(conn, XCB_ALLOW_ASYNC_POINTER, event->time);
|
||||
xcb_flush(conn);
|
||||
|
||||
if (result->needs_tree_render)
|
||||
tree_render();
|
||||
|
||||
command_result_free(result);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* There is no default behavior for button release events so we are done. */
|
||||
if (event->response_type == XCB_BUTTON_RELEASE) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Any click in a workspace should focus that workspace. If the
|
||||
* workspace is on another output we need to do a workspace_show in
|
||||
* order for i3bar (and others) to notice the change in workspace. */
|
||||
|
@ -191,7 +219,6 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod
|
|||
|
||||
if (ws != focused_workspace)
|
||||
workspace_show(ws);
|
||||
focused_id = XCB_NONE;
|
||||
|
||||
/* get the floating con */
|
||||
Con *floatingcon = con_inside_floating(con);
|
||||
|
@ -300,6 +327,7 @@ done:
|
|||
xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time);
|
||||
xcb_flush(conn);
|
||||
tree_render();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -313,9 +341,10 @@ done:
|
|||
*/
|
||||
int handle_button_press(xcb_button_press_event_t *event) {
|
||||
Con *con;
|
||||
DLOG("Button %d pressed on window 0x%08x (child 0x%08x) at (%d, %d) (root %d, %d)\n",
|
||||
event->state, event->event, event->child, event->event_x, event->event_y,
|
||||
event->root_x, event->root_y);
|
||||
DLOG("Button %d %s on window 0x%08x (child 0x%08x) at (%d, %d) (root %d, %d)\n",
|
||||
event->state, (event->response_type == XCB_BUTTON_PRESS ? "press" : "release"),
|
||||
event->event, event->child, event->event_x, event->event_y, event->root_x,
|
||||
event->root_y);
|
||||
|
||||
last_timestamp = event->time;
|
||||
|
||||
|
@ -328,7 +357,7 @@ int handle_button_press(xcb_button_press_event_t *event) {
|
|||
if (!(con = con_by_frame_id(event->event))) {
|
||||
/* If the root window is clicked, find the relevant output from the
|
||||
* click coordinates and focus the output's active workspace. */
|
||||
if (event->event == root) {
|
||||
if (event->event == root && event->response_type == XCB_BUTTON_PRESS) {
|
||||
Con *output, *ws;
|
||||
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
|
||||
if (con_is_internal(output) ||
|
||||
|
|
180
src/commands.c
180
src/commands.c
|
@ -513,7 +513,27 @@ void cmd_move_con_to_workspace_name(I3_CMD, char *name) {
|
|||
|
||||
LOG("should move window to workspace %s\n", name);
|
||||
/* get the workspace */
|
||||
Con *ws = workspace_get(name, NULL);
|
||||
Con *ws = NULL;
|
||||
Con *output = NULL;
|
||||
|
||||
/* first look for a workspace with this name */
|
||||
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
|
||||
GREP_FIRST(ws, output_get_content(output), !strcasecmp(child->name, name));
|
||||
}
|
||||
|
||||
/* if the name is plain digits, we interpret this as a "workspace number"
|
||||
* command */
|
||||
if (!ws && name_is_digits(name)) {
|
||||
long parsed_num = ws_name_to_number(name);
|
||||
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
|
||||
GREP_FIRST(ws, output_get_content(output),
|
||||
child->num == parsed_num);
|
||||
}
|
||||
}
|
||||
|
||||
/* if no workspace was found, make a new one */
|
||||
if (!ws)
|
||||
ws = workspace_get(name, NULL);
|
||||
|
||||
ws = maybe_auto_back_and_forth_workspace(ws);
|
||||
|
||||
|
@ -550,12 +570,9 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *which) {
|
|||
/* get the workspace */
|
||||
Con *output, *workspace = NULL;
|
||||
|
||||
char *endptr = NULL;
|
||||
long parsed_num = strtol(which, &endptr, 10);
|
||||
if (parsed_num == LONG_MIN ||
|
||||
parsed_num == LONG_MAX ||
|
||||
parsed_num < 0 ||
|
||||
endptr == which) {
|
||||
long parsed_num = ws_name_to_number(which);
|
||||
|
||||
if (parsed_num == -1) {
|
||||
LOG("Could not parse initial part of \"%s\" as a number.\n", which);
|
||||
// TODO: better error message
|
||||
yerror("Could not parse number");
|
||||
|
@ -927,7 +944,7 @@ void cmd_append_layout(I3_CMD, char *path) {
|
|||
restore_open_placeholder_windows(parent);
|
||||
|
||||
if (content == JSON_CONTENT_WORKSPACE)
|
||||
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"restored\"}");
|
||||
ipc_send_workspace_event("restored", parent, NULL);
|
||||
|
||||
cmd_output->needs_tree_render = true;
|
||||
}
|
||||
|
@ -941,6 +958,12 @@ void cmd_workspace(I3_CMD, char *which) {
|
|||
|
||||
DLOG("which=%s\n", which);
|
||||
|
||||
if (con_get_fullscreen_con(croot, CF_GLOBAL)) {
|
||||
LOG("Cannot switch workspace while in global fullscreen\n");
|
||||
ysuccess(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(which, "next") == 0)
|
||||
ws = workspace_next();
|
||||
else if (strcmp(which, "prev") == 0)
|
||||
|
@ -969,16 +992,18 @@ void cmd_workspace(I3_CMD, char *which) {
|
|||
void cmd_workspace_number(I3_CMD, char *which) {
|
||||
Con *output, *workspace = NULL;
|
||||
|
||||
char *endptr = NULL;
|
||||
long parsed_num = strtol(which, &endptr, 10);
|
||||
if (parsed_num == LONG_MIN ||
|
||||
parsed_num == LONG_MAX ||
|
||||
parsed_num < 0 ||
|
||||
endptr == which) {
|
||||
if (con_get_fullscreen_con(croot, CF_GLOBAL)) {
|
||||
LOG("Cannot switch workspace while in global fullscreen\n");
|
||||
ysuccess(false);
|
||||
return;
|
||||
}
|
||||
|
||||
long parsed_num = ws_name_to_number(which);
|
||||
|
||||
if (parsed_num == -1) {
|
||||
LOG("Could not parse initial part of \"%s\" as a number.\n", which);
|
||||
// TODO: better error message
|
||||
yerror("Could not parse number");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1007,6 +1032,12 @@ void cmd_workspace_number(I3_CMD, char *which) {
|
|||
*
|
||||
*/
|
||||
void cmd_workspace_back_and_forth(I3_CMD) {
|
||||
if (con_get_fullscreen_con(croot, CF_GLOBAL)) {
|
||||
LOG("Cannot switch workspace while in global fullscreen\n");
|
||||
ysuccess(false);
|
||||
return;
|
||||
}
|
||||
|
||||
workspace_back_and_forth();
|
||||
|
||||
cmd_output->needs_tree_render = true;
|
||||
|
@ -1025,10 +1056,39 @@ void cmd_workspace_name(I3_CMD, char *name) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (con_get_fullscreen_con(croot, CF_GLOBAL)) {
|
||||
LOG("Cannot switch workspace while in global fullscreen\n");
|
||||
ysuccess(false);
|
||||
return;
|
||||
}
|
||||
|
||||
DLOG("should switch to workspace %s\n", name);
|
||||
if (maybe_back_and_forth(cmd_output, name))
|
||||
return;
|
||||
workspace_show_by_name(name);
|
||||
|
||||
Con *ws = NULL;
|
||||
Con *output = NULL;
|
||||
|
||||
/* first look for a workspace with this name */
|
||||
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
|
||||
GREP_FIRST(ws, output_get_content(output), !strcasecmp(child->name, name));
|
||||
}
|
||||
|
||||
/* if the name is only digits, we interpret this as a "workspace number"
|
||||
* command */
|
||||
if (!ws && name_is_digits(name)) {
|
||||
long parsed_num = ws_name_to_number(name);
|
||||
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
|
||||
GREP_FIRST(ws, output_get_content(output),
|
||||
child->num == parsed_num);
|
||||
}
|
||||
}
|
||||
|
||||
/* if no workspace was found, make a new one */
|
||||
if (!ws)
|
||||
ws = workspace_get(name, NULL);
|
||||
|
||||
workspace_show(ws);
|
||||
|
||||
cmd_output->needs_tree_render = true;
|
||||
// XXX: default reply for now, make this a better reply
|
||||
|
@ -1253,7 +1313,7 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) {
|
|||
create_workspace_on_output(current_output, ws->parent);
|
||||
|
||||
/* notify the IPC listeners */
|
||||
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"init\"}");
|
||||
ipc_send_workspace_event("init", ws, NULL);
|
||||
}
|
||||
DLOG("Detaching\n");
|
||||
|
||||
|
@ -1274,7 +1334,7 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) {
|
|||
TAILQ_FOREACH(floating_con, &(ws->floating_head), floating_windows)
|
||||
floating_fix_coordinates(floating_con, &(old_content->rect), &(content->rect));
|
||||
|
||||
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"move\"}");
|
||||
ipc_send_workspace_event("move", ws, NULL);
|
||||
if (workspace_was_visible) {
|
||||
/* Focus the moved workspace on the destination output. */
|
||||
workspace_show(ws);
|
||||
|
@ -1538,20 +1598,26 @@ void cmd_focus(I3_CMD) {
|
|||
}
|
||||
|
||||
/*
|
||||
* Implementation of 'fullscreen [global]'.
|
||||
* Implementation of 'fullscreen enable|toggle [global]' and
|
||||
* 'fullscreen disable'
|
||||
*
|
||||
*/
|
||||
void cmd_fullscreen(I3_CMD, char *fullscreen_mode) {
|
||||
if (fullscreen_mode == NULL)
|
||||
fullscreen_mode = "output";
|
||||
DLOG("toggling fullscreen, mode = %s\n", fullscreen_mode);
|
||||
void cmd_fullscreen(I3_CMD, char *action, char *fullscreen_mode) {
|
||||
fullscreen_mode_t mode = strcmp(fullscreen_mode, "global") == 0 ? CF_GLOBAL : CF_OUTPUT;
|
||||
DLOG("%s fullscreen, mode = %s\n", action, fullscreen_mode);
|
||||
owindow *current;
|
||||
|
||||
HANDLE_EMPTY_MATCH;
|
||||
|
||||
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
||||
con_toggle_fullscreen(current->con, (strcmp(fullscreen_mode, "global") == 0 ? CF_GLOBAL : CF_OUTPUT));
|
||||
if (strcmp(action, "toggle") == 0) {
|
||||
con_toggle_fullscreen(current->con, mode);
|
||||
} else if (strcmp(action, "enable") == 0) {
|
||||
con_enable_fullscreen(current->con, mode);
|
||||
} else if (strcmp(action, "disable") == 0) {
|
||||
con_disable_fullscreen(current->con);
|
||||
}
|
||||
}
|
||||
|
||||
cmd_output->needs_tree_render = true;
|
||||
|
@ -1567,11 +1633,16 @@ void cmd_move_direction(I3_CMD, char *direction, char *move_px) {
|
|||
// TODO: We could either handle this in the parser itself as a separate token (and make the stack typed) or we need a better way to convert a string to a number with error checking
|
||||
int px = atoi(move_px);
|
||||
|
||||
/* TODO: make 'move' work with criteria. */
|
||||
owindow *current;
|
||||
HANDLE_EMPTY_MATCH;
|
||||
|
||||
Con *initially_focused = focused;
|
||||
|
||||
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||
DLOG("moving in direction %s, px %s\n", direction, move_px);
|
||||
if (con_is_floating(focused)) {
|
||||
if (con_is_floating(current->con)) {
|
||||
DLOG("floating move with %d pixels\n", px);
|
||||
Rect newrect = focused->parent->rect;
|
||||
Rect newrect = current->con->parent->rect;
|
||||
if (strcmp(direction, "left") == 0) {
|
||||
newrect.x -= px;
|
||||
} else if (strcmp(direction, "right") == 0) {
|
||||
|
@ -1581,11 +1652,16 @@ void cmd_move_direction(I3_CMD, char *direction, char *move_px) {
|
|||
} else if (strcmp(direction, "down") == 0) {
|
||||
newrect.y += px;
|
||||
}
|
||||
floating_reposition(focused->parent, newrect);
|
||||
floating_reposition(current->con->parent, newrect);
|
||||
} else {
|
||||
tree_move((strcmp(direction, "right") == 0 ? D_RIGHT : (strcmp(direction, "left") == 0 ? D_LEFT : (strcmp(direction, "up") == 0 ? D_UP : D_DOWN))));
|
||||
tree_move(current->con, (strcmp(direction, "right") == 0 ? D_RIGHT : (strcmp(direction, "left") == 0 ? D_LEFT : (strcmp(direction, "up") == 0 ? D_UP : D_DOWN))));
|
||||
cmd_output->needs_tree_render = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* the move command should not disturb focus */
|
||||
if (focused != initially_focused)
|
||||
con_focus(initially_focused);
|
||||
|
||||
// XXX: default reply for now, make this a better reply
|
||||
ysuccess(true);
|
||||
|
@ -1685,7 +1761,7 @@ void cmd_reload(I3_CMD) {
|
|||
load_configuration(conn, NULL, true);
|
||||
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\"}");
|
||||
ipc_send_workspace_event("reload", NULL, NULL);
|
||||
/* Send an update event for the barconfig just in case it has changed */
|
||||
update_barconfig();
|
||||
|
||||
|
@ -1779,33 +1855,45 @@ void cmd_focus_output(I3_CMD, char *name) {
|
|||
void cmd_move_window_to_position(I3_CMD, char *method, char *cx, char *cy) {
|
||||
int x = atoi(cx);
|
||||
int y = atoi(cy);
|
||||
bool has_error = false;
|
||||
|
||||
if (!con_is_floating(focused)) {
|
||||
owindow *current;
|
||||
HANDLE_EMPTY_MATCH;
|
||||
|
||||
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||
if (!con_is_floating(current->con)) {
|
||||
ELOG("Cannot change position. The window/container is not floating\n");
|
||||
yerror("Cannot change position. The window/container is not floating.");
|
||||
return;
|
||||
|
||||
if (!has_error) {
|
||||
yerror("Cannot change position of a window/container because it is not floating.");
|
||||
has_error = true;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcmp(method, "absolute") == 0) {
|
||||
focused->parent->rect.x = x;
|
||||
focused->parent->rect.y = y;
|
||||
current->con->parent->rect.x = x;
|
||||
current->con->parent->rect.y = y;
|
||||
|
||||
DLOG("moving to absolute position %d %d\n", x, y);
|
||||
floating_maybe_reassign_ws(focused->parent);
|
||||
floating_maybe_reassign_ws(current->con->parent);
|
||||
cmd_output->needs_tree_render = true;
|
||||
}
|
||||
|
||||
if (strcmp(method, "position") == 0) {
|
||||
Rect newrect = focused->parent->rect;
|
||||
Rect newrect = current->con->parent->rect;
|
||||
|
||||
DLOG("moving to position %d %d\n", x, y);
|
||||
newrect.x = x;
|
||||
newrect.y = y;
|
||||
|
||||
floating_reposition(focused->parent, newrect);
|
||||
floating_reposition(current->con->parent, newrect);
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: default reply for now, make this a better reply
|
||||
if (!has_error)
|
||||
ysuccess(true);
|
||||
}
|
||||
|
||||
|
@ -1937,15 +2025,8 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) {
|
|||
/* Change the name and try to parse it as a number. */
|
||||
FREE(workspace->name);
|
||||
workspace->name = sstrdup(new_name);
|
||||
char *endptr = NULL;
|
||||
long parsed_num = strtol(new_name, &endptr, 10);
|
||||
if (parsed_num == LONG_MIN ||
|
||||
parsed_num == LONG_MAX ||
|
||||
parsed_num < 0 ||
|
||||
endptr == new_name)
|
||||
workspace->num = -1;
|
||||
else
|
||||
workspace->num = parsed_num;
|
||||
|
||||
workspace->num = ws_name_to_number(new_name);
|
||||
LOG("num = %d\n", workspace->num);
|
||||
|
||||
/* By re-attaching, the sort order will be correct afterwards. */
|
||||
|
@ -1959,7 +2040,10 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) {
|
|||
cmd_output->needs_tree_render = true;
|
||||
ysuccess(true);
|
||||
|
||||
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"rename\"}");
|
||||
ipc_send_workspace_event("rename", workspace, NULL);
|
||||
ewmh_update_desktop_names();
|
||||
ewmh_update_desktop_viewport();
|
||||
ewmh_update_current_desktop();
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -204,6 +204,61 @@ static void next_state(const cmdp_token *token) {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses a string (or word, if as_word is true). Extracted out of
|
||||
* parse_command so that it can be used in src/workspace.c for interpreting
|
||||
* workspace commands.
|
||||
*
|
||||
*/
|
||||
char *parse_string(const char **walk, bool as_word) {
|
||||
const char *beginning = *walk;
|
||||
/* Handle quoted strings (or words). */
|
||||
if (**walk == '"') {
|
||||
beginning++;
|
||||
(*walk)++;
|
||||
while (**walk != '\0' && (**walk != '"' || *(*walk - 1) == '\\'))
|
||||
(*walk)++;
|
||||
} else {
|
||||
if (!as_word) {
|
||||
/* For a string (starting with 's'), the delimiters are
|
||||
* comma (,) and semicolon (;) which introduce a new
|
||||
* operation or command, respectively. Also, newlines
|
||||
* end a command. */
|
||||
while (**walk != ';' && **walk != ',' &&
|
||||
**walk != '\0' && **walk != '\r' &&
|
||||
**walk != '\n')
|
||||
(*walk)++;
|
||||
} else {
|
||||
/* For a word, the delimiters are white space (' ' or
|
||||
* '\t'), closing square bracket (]), comma (,) and
|
||||
* semicolon (;). */
|
||||
while (**walk != ' ' && **walk != '\t' &&
|
||||
**walk != ']' && **walk != ',' &&
|
||||
**walk != ';' && **walk != '\r' &&
|
||||
**walk != '\n' && **walk != '\0')
|
||||
(*walk)++;
|
||||
}
|
||||
}
|
||||
if (*walk == beginning)
|
||||
return NULL;
|
||||
|
||||
char *str = scalloc(*walk - beginning + 1);
|
||||
/* We copy manually to handle escaping of characters. */
|
||||
int inpos, outpos;
|
||||
for (inpos = 0, outpos = 0;
|
||||
inpos < (*walk - beginning);
|
||||
inpos++, outpos++) {
|
||||
/* We only handle escaped double quotes to not break
|
||||
* backwards compatibility with people using \w in
|
||||
* regular expressions etc. */
|
||||
if (beginning[inpos] == '\\' && beginning[inpos + 1] == '"')
|
||||
inpos++;
|
||||
str[outpos] = beginning[inpos];
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses and executes the given command. If a caller-allocated yajl_gen is
|
||||
* passed, a json reply will be generated in the format specified by the ipc
|
||||
|
@ -262,48 +317,8 @@ CommandResult *parse_command(const char *input, yajl_gen gen) {
|
|||
|
||||
if (strcmp(token->name, "string") == 0 ||
|
||||
strcmp(token->name, "word") == 0) {
|
||||
const char *beginning = walk;
|
||||
/* Handle quoted strings (or words). */
|
||||
if (*walk == '"') {
|
||||
beginning++;
|
||||
walk++;
|
||||
while (*walk != '\0' && (*walk != '"' || *(walk - 1) == '\\'))
|
||||
walk++;
|
||||
} else {
|
||||
if (token->name[0] == 's') {
|
||||
/* For a string (starting with 's'), the delimiters are
|
||||
* comma (,) and semicolon (;) which introduce a new
|
||||
* operation or command, respectively. Also, newlines
|
||||
* end a command. */
|
||||
while (*walk != ';' && *walk != ',' &&
|
||||
*walk != '\0' && *walk != '\r' &&
|
||||
*walk != '\n')
|
||||
walk++;
|
||||
} else {
|
||||
/* For a word, the delimiters are white space (' ' or
|
||||
* '\t'), closing square bracket (]), comma (,) and
|
||||
* semicolon (;). */
|
||||
while (*walk != ' ' && *walk != '\t' &&
|
||||
*walk != ']' && *walk != ',' &&
|
||||
*walk != ';' && *walk != '\r' &&
|
||||
*walk != '\n' && *walk != '\0')
|
||||
walk++;
|
||||
}
|
||||
}
|
||||
if (walk != beginning) {
|
||||
char *str = scalloc(walk - beginning + 1);
|
||||
/* We copy manually to handle escaping of characters. */
|
||||
int inpos, outpos;
|
||||
for (inpos = 0, outpos = 0;
|
||||
inpos < (walk - beginning);
|
||||
inpos++, outpos++) {
|
||||
/* We only handle escaped double quotes to not break
|
||||
* backwards compatibility with people using \w in
|
||||
* regular expressions etc. */
|
||||
if (beginning[inpos] == '\\' && beginning[inpos + 1] == '"')
|
||||
inpos++;
|
||||
str[outpos] = beginning[inpos];
|
||||
}
|
||||
char *str = parse_string(&walk, (token->name[0] != 's'));
|
||||
if (str != NULL) {
|
||||
if (token->identifier)
|
||||
push_string(token->identifier, str);
|
||||
/* If we are at the end of a quoted string, skip the ending
|
||||
|
|
153
src/con.c
153
src/con.c
|
@ -12,18 +12,7 @@
|
|||
*
|
||||
*/
|
||||
#include "all.h"
|
||||
|
||||
char *colors[] = {
|
||||
"#ff0000",
|
||||
"#00FF00",
|
||||
"#0000FF",
|
||||
"#ff00ff",
|
||||
"#00ffff",
|
||||
"#ffff00",
|
||||
"#aa0000",
|
||||
"#00aa00",
|
||||
"#0000aa",
|
||||
"#aa00aa"};
|
||||
#include "yajl_utils.h"
|
||||
|
||||
static void con_on_remove_child(Con *con);
|
||||
|
||||
|
@ -31,7 +20,7 @@ static void con_on_remove_child(Con *con);
|
|||
* force parent split containers to be redrawn
|
||||
*
|
||||
*/
|
||||
static void con_force_split_parents_redraw(Con *con) {
|
||||
void con_force_split_parents_redraw(Con *con) {
|
||||
Con *parent = con;
|
||||
|
||||
while (parent && parent->type != CT_WORKSPACE && parent->type != CT_DOCKAREA) {
|
||||
|
@ -59,16 +48,7 @@ Con *con_new_skeleton(Con *parent, i3Window *window) {
|
|||
new->depth = window->depth;
|
||||
else
|
||||
new->depth = XCB_COPY_FROM_PARENT;
|
||||
static int cnt = 0;
|
||||
DLOG("opening window %d\n", cnt);
|
||||
|
||||
/* TODO: remove window coloring after test-phase */
|
||||
DLOG("color %s\n", colors[cnt]);
|
||||
new->name = strdup(colors[cnt]);
|
||||
//uint32_t cp = get_colorpixel(colors[cnt]);
|
||||
cnt++;
|
||||
if ((cnt % (sizeof(colors) / sizeof(char *))) == 0)
|
||||
cnt = 0;
|
||||
DLOG("opening window\n");
|
||||
|
||||
TAILQ_INIT(&(new->floating_head));
|
||||
TAILQ_INIT(&(new->nodes_head));
|
||||
|
@ -231,6 +211,7 @@ void con_focus(Con *con) {
|
|||
con->urgent = false;
|
||||
con_update_parents_urgency(con);
|
||||
workspace_update_urgent_flag(con_get_workspace(con));
|
||||
ipc_send_window_event("urgent", con);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -585,37 +566,27 @@ void con_fix_percent(Con *con) {
|
|||
*
|
||||
*/
|
||||
void con_toggle_fullscreen(Con *con, int fullscreen_mode) {
|
||||
Con *workspace, *fullscreen;
|
||||
|
||||
if (con->type == CT_WORKSPACE) {
|
||||
DLOG("You cannot make a workspace fullscreen.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
DLOG("toggling fullscreen for %p / %s\n", con, con->name);
|
||||
if (con->fullscreen_mode == CF_NONE) {
|
||||
/* 1: check if there already is a fullscreen con */
|
||||
if (fullscreen_mode == CF_GLOBAL)
|
||||
fullscreen = con_get_fullscreen_con(croot, CF_GLOBAL);
|
||||
else {
|
||||
workspace = con_get_workspace(con);
|
||||
fullscreen = con_get_fullscreen_con(workspace, CF_OUTPUT);
|
||||
}
|
||||
if (fullscreen != NULL) {
|
||||
/* Disable fullscreen for the currently fullscreened
|
||||
* container and enable it for the one the user wants
|
||||
* to have in fullscreen mode. */
|
||||
LOG("Disabling fullscreen for (%p/%s) upon user request\n",
|
||||
fullscreen, fullscreen->name);
|
||||
fullscreen->fullscreen_mode = CF_NONE;
|
||||
|
||||
if (con->fullscreen_mode == CF_NONE)
|
||||
con_enable_fullscreen(con, fullscreen_mode);
|
||||
else
|
||||
con_disable_fullscreen(con);
|
||||
}
|
||||
|
||||
/* 2: enable fullscreen */
|
||||
/*
|
||||
* Sets the specified fullscreen mode for the given container, sends the
|
||||
* “fullscreen_mode” event and changes the XCB fullscreen property of the
|
||||
* container’s window, if any.
|
||||
*
|
||||
*/
|
||||
static void con_set_fullscreen_mode(Con *con, fullscreen_mode_t fullscreen_mode) {
|
||||
con->fullscreen_mode = fullscreen_mode;
|
||||
} else {
|
||||
/* 1: disable fullscreen */
|
||||
con->fullscreen_mode = CF_NONE;
|
||||
}
|
||||
|
||||
DLOG("mode now: %d\n", con->fullscreen_mode);
|
||||
|
||||
|
@ -638,6 +609,79 @@ void con_toggle_fullscreen(Con *con, int fullscreen_mode) {
|
|||
A__NET_WM_STATE, XCB_ATOM_ATOM, 32, num, values);
|
||||
}
|
||||
|
||||
/*
|
||||
* Enables fullscreen mode for the given container, if necessary.
|
||||
*
|
||||
* If the container’s mode is already CF_OUTPUT or CF_GLOBAL, the container is
|
||||
* kept fullscreen but its mode is set to CF_GLOBAL and CF_OUTPUT,
|
||||
* respectively.
|
||||
*
|
||||
* Other fullscreen containers will be disabled first, if they hide the new
|
||||
* one.
|
||||
*
|
||||
*/
|
||||
void con_enable_fullscreen(Con *con, fullscreen_mode_t fullscreen_mode) {
|
||||
if (con->type == CT_WORKSPACE) {
|
||||
DLOG("You cannot make a workspace fullscreen.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
assert(fullscreen_mode == CF_GLOBAL || fullscreen_mode == CF_OUTPUT);
|
||||
|
||||
if (fullscreen_mode == CF_GLOBAL)
|
||||
DLOG("enabling global fullscreen for %p / %s\n", con, con->name);
|
||||
else
|
||||
DLOG("enabling fullscreen for %p / %s\n", con, con->name);
|
||||
|
||||
if (con->fullscreen_mode == fullscreen_mode) {
|
||||
DLOG("fullscreen already enabled for %p / %s\n", con, con->name);
|
||||
return;
|
||||
}
|
||||
|
||||
Con *con_ws = con_get_workspace(con);
|
||||
|
||||
/* Disable any fullscreen container that would conflict the new one. */
|
||||
Con *fullscreen = con_get_fullscreen_con(croot, CF_GLOBAL);
|
||||
if (fullscreen == NULL)
|
||||
fullscreen = con_get_fullscreen_con(con_ws, CF_OUTPUT);
|
||||
if (fullscreen != NULL)
|
||||
con_disable_fullscreen(fullscreen);
|
||||
|
||||
/* Set focus to new fullscreen container. Unless in global fullscreen mode
|
||||
* and on another workspace restore focus afterwards.
|
||||
* Switch to the container’s workspace if mode is global. */
|
||||
Con *cur_ws = con_get_workspace(focused);
|
||||
Con *old_focused = focused;
|
||||
if (fullscreen_mode == CF_GLOBAL && cur_ws != con_ws)
|
||||
workspace_show(con_ws);
|
||||
con_focus(con);
|
||||
if (fullscreen_mode != CF_GLOBAL && cur_ws != con_ws)
|
||||
con_focus(old_focused);
|
||||
|
||||
con_set_fullscreen_mode(con, fullscreen_mode);
|
||||
}
|
||||
|
||||
/*
|
||||
* Disables fullscreen mode for the given container regardless of the mode, if
|
||||
* necessary.
|
||||
*
|
||||
*/
|
||||
void con_disable_fullscreen(Con *con) {
|
||||
if (con->type == CT_WORKSPACE) {
|
||||
DLOG("You cannot make a workspace fullscreen.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
DLOG("disabling fullscreen for %p / %s\n", con, con->name);
|
||||
|
||||
if (con->fullscreen_mode == CF_NONE) {
|
||||
DLOG("fullscreen already disabled for %p / %s\n", con, con->name);
|
||||
return;
|
||||
}
|
||||
|
||||
con_set_fullscreen_mode(con, CF_NONE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Moves the given container to the currently focused container on the given
|
||||
* workspace.
|
||||
|
@ -832,6 +876,8 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool
|
|||
}
|
||||
|
||||
CALL(parent, on_remove_child);
|
||||
|
||||
ipc_send_window_event("move", con);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1390,8 +1436,15 @@ static void con_on_remove_child(Con *con) {
|
|||
if (con->type == CT_WORKSPACE) {
|
||||
if (TAILQ_EMPTY(&(con->focus_head)) && !workspace_is_visible(con)) {
|
||||
LOG("Closing old workspace (%p / %s), it is empty\n", con, con->name);
|
||||
yajl_gen gen = ipc_marshal_workspace_event("empty", con, NULL);
|
||||
tree_close(con, DONT_KILL_WINDOW, false, false);
|
||||
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"empty\"}");
|
||||
|
||||
const unsigned char *payload;
|
||||
ylength length;
|
||||
y(get_buf, &payload, &length);
|
||||
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, (const char *)payload);
|
||||
|
||||
y(free);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -1603,14 +1656,16 @@ void con_set_urgency(Con *con, bool urgent) {
|
|||
|
||||
con_update_parents_urgency(con);
|
||||
|
||||
if (con->urgent == urgent)
|
||||
LOG("Urgency flag changed to %d\n", con->urgent);
|
||||
|
||||
Con *ws;
|
||||
/* Set the urgency flag on the workspace, if a workspace could be found
|
||||
* (for dock clients, that is not the case). */
|
||||
if ((ws = con_get_workspace(con)) != NULL)
|
||||
workspace_update_urgent_flag(ws);
|
||||
|
||||
if (con->urgent == urgent) {
|
||||
LOG("Urgency flag changed to %d\n", con->urgent);
|
||||
ipc_send_window_event("urgent", con);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
21
src/config.c
21
src/config.c
|
@ -11,9 +11,7 @@
|
|||
*
|
||||
*/
|
||||
#include "all.h"
|
||||
|
||||
/* We need Xlib for XStringToKeysym */
|
||||
#include <X11/Xlib.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
|
||||
char *current_configpath = NULL;
|
||||
Config config;
|
||||
|
@ -114,12 +112,19 @@ static char *get_config_path(const char *override_configpath) {
|
|||
* parse_file().
|
||||
*
|
||||
*/
|
||||
static void parse_configuration(const char *override_configpath) {
|
||||
bool parse_configuration(const char *override_configpath, bool use_nagbar) {
|
||||
char *path = get_config_path(override_configpath);
|
||||
LOG("Parsing configfile %s\n", path);
|
||||
FREE(current_configpath);
|
||||
current_configpath = path;
|
||||
parse_file(path);
|
||||
|
||||
/* initialize default bindings if we're just validating the config file */
|
||||
if (!use_nagbar && bindings == NULL) {
|
||||
bindings = scalloc(sizeof(struct bindings_head));
|
||||
TAILQ_INIT(bindings);
|
||||
}
|
||||
|
||||
return parse_file(path, use_nagbar);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -142,9 +147,7 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
|
|||
while (!TAILQ_EMPTY(bindings)) {
|
||||
bind = TAILQ_FIRST(bindings);
|
||||
TAILQ_REMOVE(bindings, bind, bindings);
|
||||
FREE(bind->translated_to);
|
||||
FREE(bind->command);
|
||||
FREE(bind);
|
||||
binding_free(bind);
|
||||
}
|
||||
FREE(bindings);
|
||||
SLIST_REMOVE(&modes, mode, Mode, modes);
|
||||
|
@ -262,7 +265,7 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
|
|||
if (config.workspace_urgency_timer == 0)
|
||||
config.workspace_urgency_timer = 0.5;
|
||||
|
||||
parse_configuration(override_configpath);
|
||||
parse_configuration(override_configpath, true);
|
||||
|
||||
if (reload) {
|
||||
translate_keysyms();
|
||||
|
|
|
@ -171,8 +171,8 @@ CFGFUN(font, const char *font) {
|
|||
font_pattern = sstrdup(font);
|
||||
}
|
||||
|
||||
CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *command) {
|
||||
configure_binding(bindtype, modifiers, key, release, command, DEFAULT_BINDING_MODE);
|
||||
CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *whole_window, const char *command) {
|
||||
configure_binding(bindtype, modifiers, key, release, whole_window, command, DEFAULT_BINDING_MODE);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
|
@ -181,8 +181,8 @@ CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, co
|
|||
|
||||
static char *current_mode;
|
||||
|
||||
CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *command) {
|
||||
configure_binding(bindtype, modifiers, key, release, command, current_mode);
|
||||
CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *whole_window, const char *command) {
|
||||
configure_binding(bindtype, modifiers, key, release, whole_window, command, current_mode);
|
||||
}
|
||||
|
||||
CFGFUN(enter_mode, const char *modename) {
|
||||
|
@ -271,13 +271,15 @@ CFGFUN(new_window, const char *windowtype, const char *border, const long width)
|
|||
}
|
||||
|
||||
if (strcmp(windowtype, "new_window") == 0) {
|
||||
DLOG("default tiled border style = %d and border width = %d\n", border_style, border_width);
|
||||
DLOG("default tiled border style = %d and border width = %d (%d physical px)\n",
|
||||
border_style, border_width, logical_px(border_width));
|
||||
config.default_border = border_style;
|
||||
config.default_border_width = border_width;
|
||||
config.default_border_width = logical_px(border_width);
|
||||
} else {
|
||||
DLOG("default floating border style = %d and border width = %d\n", border_style, border_width);
|
||||
DLOG("default floating border style = %d and border width = %d (%d physical px)\n",
|
||||
border_style, border_width, logical_px(border_width));
|
||||
config.default_floating_border = border_style;
|
||||
config.default_floating_border_width = border_width;
|
||||
config.default_floating_border_width = logical_px(border_width);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -460,6 +462,16 @@ CFGFUN(bar_modifier, const char *modifier) {
|
|||
current_bar.modifier = M_SHIFT;
|
||||
}
|
||||
|
||||
CFGFUN(bar_wheel_up_cmd, const char *command) {
|
||||
FREE(current_bar.wheel_up_cmd);
|
||||
current_bar.wheel_up_cmd = sstrdup(command);
|
||||
}
|
||||
|
||||
CFGFUN(bar_wheel_down_cmd, const char *command) {
|
||||
FREE(current_bar.wheel_down_cmd);
|
||||
current_bar.wheel_down_cmd = sstrdup(command);
|
||||
}
|
||||
|
||||
CFGFUN(bar_position, const char *position) {
|
||||
current_bar.position = (strcmp(position, "top") == 0 ? P_TOP : P_BOTTOM);
|
||||
}
|
||||
|
|
|
@ -840,7 +840,7 @@ static char *migrate_config(char *input, off_t size) {
|
|||
* parse_config and possibly launching i3-nagbar.
|
||||
*
|
||||
*/
|
||||
void parse_file(const char *f) {
|
||||
bool parse_file(const char *f, bool use_nagbar) {
|
||||
SLIST_HEAD(variables_head, Variable) variables = SLIST_HEAD_INITIALIZER(&variables);
|
||||
int fd, ret, read_bytes = 0;
|
||||
struct stat stbuf;
|
||||
|
@ -1000,7 +1000,7 @@ void parse_file(const char *f) {
|
|||
|
||||
check_for_duplicate_bindings(context);
|
||||
|
||||
if (context->has_errors || context->has_warnings) {
|
||||
if (use_nagbar && (context->has_errors || context->has_warnings)) {
|
||||
ELOG("FYI: You are using i3 version " I3_VERSION "\n");
|
||||
if (version == 3)
|
||||
ELOG("Please convert your configfile first, then fix any remaining errors (see above).\n");
|
||||
|
@ -1030,6 +1030,8 @@ void parse_file(const char *f) {
|
|||
free(pageraction);
|
||||
}
|
||||
|
||||
bool has_errors = context->has_errors;
|
||||
|
||||
FREE(context->line_copy);
|
||||
free(context);
|
||||
free(new);
|
||||
|
@ -1042,6 +1044,8 @@ void parse_file(const char *f) {
|
|||
SLIST_REMOVE_HEAD(&variables, variables);
|
||||
FREE(current);
|
||||
}
|
||||
|
||||
return !has_errors;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
99
src/ewmh.c
99
src/ewmh.c
|
@ -40,6 +40,102 @@ void ewmh_update_current_desktop(void) {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Updates _NET_NUMBER_OF_DESKTOPS which we interpret as the number of
|
||||
* noninternal workspaces.
|
||||
*/
|
||||
void ewmh_update_number_of_desktops(void) {
|
||||
Con *output;
|
||||
uint32_t idx = 0;
|
||||
|
||||
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
|
||||
Con *ws;
|
||||
TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
|
||||
if (STARTS_WITH(ws->name, "__"))
|
||||
continue;
|
||||
++idx;
|
||||
}
|
||||
}
|
||||
|
||||
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root,
|
||||
A__NET_NUMBER_OF_DESKTOPS, XCB_ATOM_CARDINAL, 32, 1, &idx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Updates _NET_DESKTOP_NAMES: "The names of all virtual desktops. This is a
|
||||
* list of NULL-terminated strings in UTF-8 encoding"
|
||||
*/
|
||||
void ewmh_update_desktop_names(void) {
|
||||
Con *output;
|
||||
int msg_length = 0;
|
||||
|
||||
/* count the size of the property message to set */
|
||||
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
|
||||
Con *ws;
|
||||
TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
|
||||
if (STARTS_WITH(ws->name, "__"))
|
||||
continue;
|
||||
msg_length += strlen(ws->name) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
char desktop_names[msg_length];
|
||||
int current_position = 0;
|
||||
|
||||
/* fill the buffer with the names of the i3 workspaces */
|
||||
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
|
||||
Con *ws;
|
||||
TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
|
||||
if (STARTS_WITH(ws->name, "__"))
|
||||
continue;
|
||||
|
||||
for (size_t i = 0; i < strlen(ws->name) + 1; i++) {
|
||||
desktop_names[current_position++] = ws->name[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root,
|
||||
A__NET_DESKTOP_NAMES, A_UTF8_STRING, 8, msg_length, desktop_names);
|
||||
}
|
||||
|
||||
/*
|
||||
* Updates _NET_DESKTOP_VIEWPORT, which is an array of pairs of cardinals that
|
||||
* define the top left corner of each desktop's viewport.
|
||||
*/
|
||||
void ewmh_update_desktop_viewport(void) {
|
||||
Con *output;
|
||||
int num_desktops = 0;
|
||||
/* count number of desktops */
|
||||
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
|
||||
Con *ws;
|
||||
TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
|
||||
if (STARTS_WITH(ws->name, "__"))
|
||||
continue;
|
||||
|
||||
num_desktops++;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t viewports[num_desktops * 2];
|
||||
|
||||
int current_position = 0;
|
||||
/* fill the viewport buffer */
|
||||
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
|
||||
Con *ws;
|
||||
TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
|
||||
if (STARTS_WITH(ws->name, "__"))
|
||||
continue;
|
||||
|
||||
viewports[current_position++] = output->rect.x;
|
||||
viewports[current_position++] = output->rect.y;
|
||||
}
|
||||
}
|
||||
|
||||
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root,
|
||||
A__NET_DESKTOP_VIEWPORT, XCB_ATOM_CARDINAL, 32, current_position, &viewports);
|
||||
}
|
||||
|
||||
/*
|
||||
* Updates _NET_ACTIVE_WINDOW with the currently focused window.
|
||||
*
|
||||
|
@ -138,5 +234,6 @@ void ewmh_setup_hints(void) {
|
|||
/* I’m not entirely sure if we need to keep _NET_WM_NAME on root. */
|
||||
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_WM_NAME, A_UTF8_STRING, 8, strlen("i3"), "i3");
|
||||
|
||||
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTED, XCB_ATOM_ATOM, 32, 19, supported_atoms);
|
||||
/* only send the first 24 atoms (last one is _NET_CLOSE_WINDOW) increment that number when adding supported atoms */
|
||||
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTED, XCB_ATOM_ATOM, 32, 24, supported_atoms);
|
||||
}
|
||||
|
|
|
@ -298,16 +298,22 @@ void floating_enable(Con *con, bool automatic) {
|
|||
|
||||
/* Check if we need to re-assign it to a different workspace because of its
|
||||
* coordinates and exit if that was done successfully. */
|
||||
if (floating_maybe_reassign_ws(nc))
|
||||
if (floating_maybe_reassign_ws(nc)) {
|
||||
ipc_send_window_event("floating", con);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Sanitize coordinates: Check if they are on any output */
|
||||
if (get_output_containing(nc->rect.x, nc->rect.y) != NULL)
|
||||
if (get_output_containing(nc->rect.x, nc->rect.y) != NULL) {
|
||||
ipc_send_window_event("floating", con);
|
||||
return;
|
||||
}
|
||||
|
||||
ELOG("No output found at destination coordinates, centering floating window on current ws\n");
|
||||
nc->rect.x = ws->rect.x + (ws->rect.width / 2) - (nc->rect.width / 2);
|
||||
nc->rect.y = ws->rect.y + (ws->rect.height / 2) - (nc->rect.height / 2);
|
||||
|
||||
ipc_send_window_event("floating", con);
|
||||
}
|
||||
|
||||
void floating_disable(Con *con, bool automatic) {
|
||||
|
@ -351,6 +357,8 @@ void floating_disable(Con *con, bool automatic) {
|
|||
|
||||
if (set_focus)
|
||||
con_focus(con);
|
||||
|
||||
ipc_send_window_event("floating", con);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
176
src/handlers.c
176
src/handlers.c
|
@ -21,6 +21,8 @@
|
|||
#include <libsn/sn-monitor.h>
|
||||
|
||||
int randr_base = -1;
|
||||
int xkb_base = -1;
|
||||
int xkb_current_group;
|
||||
|
||||
/* After mapping/unmapping windows, a notify event is generated. However, we don’t want it,
|
||||
since it’d trigger an infinite loop of switching between the different windows when
|
||||
|
@ -282,7 +284,6 @@ static void handle_map_request(xcb_map_request_event_t *event) {
|
|||
add_ignore_event(event->sequence, -1);
|
||||
|
||||
manage_window(event->window, cookie, false);
|
||||
x_push_changes(croot);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -487,7 +488,6 @@ static void handle_unmap_notify_event(xcb_unmap_notify_event_t *event) {
|
|||
|
||||
tree_close(con, DONT_KILL_WINDOW, false, false);
|
||||
tree_render();
|
||||
x_push_changes(croot);
|
||||
|
||||
ignore_end:
|
||||
/* If the client (as opposed to i3) destroyed or unmapped a window, an
|
||||
|
@ -651,6 +651,19 @@ static void handle_expose_event(xcb_expose_event_t *event) {
|
|||
return;
|
||||
}
|
||||
|
||||
#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0
|
||||
#define _NET_WM_MOVERESIZE_SIZE_TOP 1
|
||||
#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2
|
||||
#define _NET_WM_MOVERESIZE_SIZE_RIGHT 3
|
||||
#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4
|
||||
#define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5
|
||||
#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6
|
||||
#define _NET_WM_MOVERESIZE_SIZE_LEFT 7
|
||||
#define _NET_WM_MOVERESIZE_MOVE 8 /* movement only */
|
||||
#define _NET_WM_MOVERESIZE_SIZE_KEYBOARD 9 /* size via keyboard */
|
||||
#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD 10 /* move via keyboard */
|
||||
#define _NET_WM_MOVERESIZE_CANCEL 11 /* cancel operation */
|
||||
|
||||
/*
|
||||
* Handle client messages (EWMH)
|
||||
*
|
||||
|
@ -791,6 +804,98 @@ static void handle_client_message(xcb_client_message_event_t *event) {
|
|||
XCB_ATOM_CARDINAL, 32, 4,
|
||||
&r);
|
||||
xcb_flush(conn);
|
||||
} else if (event->type == A_WM_CHANGE_STATE) {
|
||||
/* http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.4 */
|
||||
Con *con = con_by_window_id(event->window);
|
||||
|
||||
if (con && event->data.data32[0] == 3) {
|
||||
/* this request is so we can play some animiation showing the
|
||||
* window physically moving to the tray before we close it (I
|
||||
* think) */
|
||||
DLOG("Client has requested iconic state. Closing this con. (con = %p)\n", con);
|
||||
tree_close(con, DONT_KILL_WINDOW, false, false);
|
||||
tree_render();
|
||||
} else {
|
||||
DLOG("Not handling WM_CHANGE_STATE request. (window = %d, state = %d)\n", event->window, event->data.data32[0]);
|
||||
}
|
||||
|
||||
} else if (event->type == A__NET_CURRENT_DESKTOP) {
|
||||
/* This request is used by pagers and bars to change the current
|
||||
* desktop likely as a result of some user action. We interpret this as
|
||||
* a request to focus the given workspace. See
|
||||
* http://standards.freedesktop.org/wm-spec/latest/ar01s03.html#idm140251368135008
|
||||
* */
|
||||
Con *output;
|
||||
uint32_t idx = 0;
|
||||
DLOG("Request to change current desktop to index %d\n", event->data.data32[0]);
|
||||
|
||||
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
|
||||
Con *ws;
|
||||
TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
|
||||
if (STARTS_WITH(ws->name, "__"))
|
||||
continue;
|
||||
|
||||
if (idx == event->data.data32[0]) {
|
||||
/* data32[1] is a timestamp used to prevent focus race conditions */
|
||||
if (event->data.data32[1])
|
||||
last_timestamp = event->data.data32[1];
|
||||
|
||||
DLOG("Handling request to focus workspace %s\n", ws->name);
|
||||
|
||||
workspace_show(ws);
|
||||
tree_render();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
++idx;
|
||||
}
|
||||
}
|
||||
} else if (event->type == A__NET_CLOSE_WINDOW) {
|
||||
/*
|
||||
* Pagers wanting to close a window MUST send a _NET_CLOSE_WINDOW
|
||||
* client message request to the root window.
|
||||
* http://standards.freedesktop.org/wm-spec/wm-spec-latest.html#idm140200472668896
|
||||
*/
|
||||
Con *con = con_by_window_id(event->window);
|
||||
if (con) {
|
||||
DLOG("Handling _NET_CLOSE_WINDOW request (con = %p)\n", con);
|
||||
|
||||
if (event->data.data32[0])
|
||||
last_timestamp = event->data.data32[0];
|
||||
|
||||
tree_close(con, KILL_WINDOW, false, false);
|
||||
tree_render();
|
||||
} else {
|
||||
DLOG("Couldn't find con for _NET_CLOSE_WINDOW request. (window = %d)\n", event->window);
|
||||
}
|
||||
} else if (event->type == A__NET_WM_MOVERESIZE) {
|
||||
/*
|
||||
* Client-side decorated Gtk3 windows emit this signal when being
|
||||
* dragged by their GtkHeaderBar
|
||||
*/
|
||||
Con *con = con_by_window_id(event->window);
|
||||
if (!con || !con_is_floating(con)) {
|
||||
DLOG("Couldn't find con for _NET_WM_MOVERESIZE request, or con not floating (window = %d)\n", event->window);
|
||||
return;
|
||||
}
|
||||
DLOG("Handling _NET_WM_MOVERESIZE request (con = %p)\n", con);
|
||||
uint32_t direction = event->data.data32[2];
|
||||
uint32_t x_root = event->data.data32[0];
|
||||
uint32_t y_root = event->data.data32[1];
|
||||
/* construct fake xcb_button_press_event_t */
|
||||
xcb_button_press_event_t fake = {
|
||||
.root_x = x_root,
|
||||
.root_y = y_root,
|
||||
.event_x = x_root - (con->rect.x),
|
||||
.event_y = y_root - (con->rect.y)};
|
||||
if (direction == _NET_WM_MOVERESIZE_MOVE) {
|
||||
floating_drag_window(con->parent, &fake);
|
||||
} else if (direction >= _NET_WM_MOVERESIZE_SIZE_TOPLEFT && direction <= _NET_WM_MOVERESIZE_SIZE_LEFT) {
|
||||
floating_resize_window(con->parent, FALSE, &fake);
|
||||
} else {
|
||||
DLOG("_NET_WM_MOVERESIZE direction %d not implemented\n", direction);
|
||||
}
|
||||
} else {
|
||||
DLOG("unhandled clientmessage\n");
|
||||
return;
|
||||
|
@ -1045,6 +1150,30 @@ static void handle_focus_in(xcb_focus_in_event_t *event) {
|
|||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handles the WM_CLASS property for assignments and criteria selection.
|
||||
*
|
||||
*/
|
||||
static bool handle_class_change(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
|
||||
xcb_atom_t name, xcb_get_property_reply_t *prop) {
|
||||
Con *con;
|
||||
if ((con = con_by_window_id(window)) == NULL || con->window == NULL)
|
||||
return false;
|
||||
|
||||
if (prop == NULL) {
|
||||
prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn,
|
||||
false, window, XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 0, 32),
|
||||
NULL);
|
||||
|
||||
if (prop == NULL)
|
||||
return false;
|
||||
}
|
||||
|
||||
window_update_class(con->window, prop, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Returns false if the event could not be processed (e.g. the window could not
|
||||
* be found), true otherwise */
|
||||
typedef bool (*cb_property_handler_t)(void *data, xcb_connection_t *c, uint8_t state, xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *property);
|
||||
|
@ -1062,7 +1191,8 @@ static struct property_handler_t property_handlers[] = {
|
|||
{0, UINT_MAX, handle_normal_hints},
|
||||
{0, UINT_MAX, handle_clientleader_change},
|
||||
{0, UINT_MAX, handle_transient_for},
|
||||
{0, 128, handle_windowrole_change}};
|
||||
{0, 128, handle_windowrole_change},
|
||||
{0, 128, handle_class_change}};
|
||||
#define NUM_HANDLERS (sizeof(property_handlers) / sizeof(struct property_handler_t))
|
||||
|
||||
/*
|
||||
|
@ -1080,6 +1210,7 @@ void property_handlers_init(void) {
|
|||
property_handlers[4].atom = A_WM_CLIENT_LEADER;
|
||||
property_handlers[5].atom = XCB_ATOM_WM_TRANSIENT_FOR;
|
||||
property_handlers[6].atom = A_WM_WINDOW_ROLE;
|
||||
property_handlers[7].atom = XCB_ATOM_WM_CLASS;
|
||||
}
|
||||
|
||||
static void property_notify(uint8_t state, xcb_window_t window, xcb_atom_t atom) {
|
||||
|
@ -1115,12 +1246,50 @@ static void property_notify(uint8_t state, xcb_window_t window, xcb_atom_t atom)
|
|||
*
|
||||
*/
|
||||
void handle_event(int type, xcb_generic_event_t *event) {
|
||||
DLOG("event type %d, xkb_base %d\n", type, xkb_base);
|
||||
if (randr_base > -1 &&
|
||||
type == randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY) {
|
||||
handle_screen_change(event);
|
||||
return;
|
||||
}
|
||||
|
||||
if (xkb_base > -1 && type == xkb_base) {
|
||||
DLOG("xkb event, need to handle it.\n");
|
||||
|
||||
xcb_xkb_state_notify_event_t *state = (xcb_xkb_state_notify_event_t *)event;
|
||||
if (state->xkbType == XCB_XKB_MAP_NOTIFY) {
|
||||
if (event_is_ignored(event->sequence, type)) {
|
||||
DLOG("Ignoring map notify event for sequence %d.\n", state->sequence);
|
||||
} else {
|
||||
DLOG("xkb map notify, sequence %d, time %d\n", state->sequence, state->time);
|
||||
add_ignore_event(event->sequence, type);
|
||||
ungrab_all_keys(conn);
|
||||
translate_keysyms();
|
||||
grab_all_keys(conn, false);
|
||||
}
|
||||
} else if (state->xkbType == XCB_XKB_STATE_NOTIFY) {
|
||||
DLOG("xkb state group = %d\n", state->group);
|
||||
|
||||
/* See The XKB Extension: Library Specification, section 14.1 */
|
||||
/* We check if the current group (each group contains
|
||||
* two levels) has been changed. Mode_switch activates
|
||||
* group XkbGroup2Index */
|
||||
if (xkb_current_group == state->group)
|
||||
return;
|
||||
xkb_current_group = state->group;
|
||||
if (state->group == XCB_XKB_GROUP_1) {
|
||||
DLOG("Mode_switch disabled\n");
|
||||
ungrab_all_keys(conn);
|
||||
grab_all_keys(conn, false);
|
||||
} else {
|
||||
DLOG("Mode_switch enabled\n");
|
||||
grab_all_keys(conn, false);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case XCB_KEY_PRESS:
|
||||
case XCB_KEY_RELEASE:
|
||||
|
@ -1128,6 +1297,7 @@ void handle_event(int type, xcb_generic_event_t *event) {
|
|||
break;
|
||||
|
||||
case XCB_BUTTON_PRESS:
|
||||
case XCB_BUTTON_RELEASE:
|
||||
handle_button_press((xcb_button_press_event_t *)event);
|
||||
break;
|
||||
|
||||
|
|
|
@ -5,8 +5,8 @@ CLEAN_TARGETS += clean-i3
|
|||
i3_SOURCES := $(filter-out $(i3_SOURCES_GENERATED),$(wildcard src/*.c))
|
||||
i3_HEADERS_CMDPARSER := $(wildcard include/GENERATED_*.h)
|
||||
i3_HEADERS := $(filter-out $(i3_HEADERS_CMDPARSER),$(wildcard include/*.h))
|
||||
i3_CFLAGS = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(XCB_WM_CFLAGS) $(X11_CFLAGS) $(XCURSOR_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) $(PCRE_CFLAGS) $(LIBSN_CFLAGS)
|
||||
i3_LIBS = $(XCB_LIBS) $(XCB_KBD_LIBS) $(XCB_WM_LIBS) $(X11_LIBS) $(XCURSOR_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(PCRE_LIBS) $(LIBSN_LIBS) -lm -lpthread
|
||||
i3_CFLAGS = $(XKB_COMMON_CFLAGS) $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(XCB_WM_CFLAGS) $(XCURSOR_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) $(PCRE_CFLAGS) $(LIBSN_CFLAGS)
|
||||
i3_LIBS = $(XKB_COMMON_LIBS) $(XCB_LIBS) $(XCB_XKB_LIBS) $(XCB_KBD_LIBS) $(XCB_WM_LIBS) $(XCURSOR_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(PCRE_LIBS) $(LIBSN_LIBS) -lm -lpthread
|
||||
|
||||
# When using clang, we use pre-compiled headers to speed up the build. With
|
||||
# gcc, this actually makes the build slower.
|
||||
|
|
134
src/ipc.c
134
src/ipc.c
|
@ -151,6 +151,60 @@ static void dump_rect(yajl_gen gen, const char *name, Rect r) {
|
|||
y(map_close);
|
||||
}
|
||||
|
||||
static void dump_binding(yajl_gen gen, Binding *bind) {
|
||||
y(map_open);
|
||||
ystr("input_code");
|
||||
y(integer, bind->keycode);
|
||||
|
||||
ystr("input_type");
|
||||
ystr((const char*)(bind->input_type == B_KEYBOARD ? "keyboard" : "mouse"));
|
||||
|
||||
ystr("symbol");
|
||||
if (bind->symbol == NULL)
|
||||
y(null);
|
||||
else
|
||||
ystr(bind->symbol);
|
||||
|
||||
ystr("command");
|
||||
ystr(bind->command);
|
||||
|
||||
ystr("mods");
|
||||
y(array_open);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (bind->mods & (1 << i)) {
|
||||
switch (1 << i) {
|
||||
case XCB_MOD_MASK_SHIFT:
|
||||
ystr("shift");
|
||||
break;
|
||||
case XCB_MOD_MASK_LOCK:
|
||||
ystr("lock");
|
||||
break;
|
||||
case XCB_MOD_MASK_CONTROL:
|
||||
ystr("ctrl");
|
||||
break;
|
||||
case XCB_MOD_MASK_1:
|
||||
ystr("Mod1");
|
||||
break;
|
||||
case XCB_MOD_MASK_2:
|
||||
ystr("Mod2");
|
||||
break;
|
||||
case XCB_MOD_MASK_3:
|
||||
ystr("Mod3");
|
||||
break;
|
||||
case XCB_MOD_MASK_4:
|
||||
ystr("Mod4");
|
||||
break;
|
||||
case XCB_MOD_MASK_5:
|
||||
ystr("Mod5");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
y(array_close);
|
||||
|
||||
y(map_close);
|
||||
}
|
||||
|
||||
void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
|
||||
y(map_open);
|
||||
ystr("id");
|
||||
|
@ -293,14 +347,17 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
|
|||
y(integer, con->current_border_width);
|
||||
|
||||
dump_rect(gen, "rect", con->rect);
|
||||
dump_rect(gen, "deco_rect", con->deco_rect);
|
||||
dump_rect(gen, "window_rect", con->window_rect);
|
||||
dump_rect(gen, "geometry", con->geometry);
|
||||
|
||||
ystr("name");
|
||||
if (con->window && con->window->name)
|
||||
ystr(i3string_as_utf8(con->window->name));
|
||||
else
|
||||
else if (con->name != NULL)
|
||||
ystr(con->name);
|
||||
else
|
||||
y(null);
|
||||
|
||||
if (con->type == CT_WORKSPACE) {
|
||||
ystr("num");
|
||||
|
@ -337,6 +394,11 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
|
|||
ystr(i3string_as_utf8(con->window->name));
|
||||
}
|
||||
|
||||
ystr("transient_for");
|
||||
if (con->window->transient_for == XCB_NONE)
|
||||
y(null);
|
||||
else y(integer, con->window->transient_for);
|
||||
|
||||
y(map_close);
|
||||
}
|
||||
|
||||
|
@ -512,6 +574,16 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) {
|
|||
break;
|
||||
}
|
||||
|
||||
if (config->wheel_up_cmd) {
|
||||
ystr("wheel_up_cmd");
|
||||
ystr(config->wheel_up_cmd);
|
||||
}
|
||||
|
||||
if (config->wheel_down_cmd) {
|
||||
ystr("wheel_down_cmd");
|
||||
ystr(config->wheel_down_cmd);
|
||||
}
|
||||
|
||||
ystr("position");
|
||||
if (config->position == P_BOTTOM)
|
||||
ystr("bottom");
|
||||
|
@ -600,9 +672,6 @@ IPC_HANDLER(get_workspaces) {
|
|||
y(map_open);
|
||||
|
||||
ystr("num");
|
||||
if (ws->num == -1)
|
||||
y(null);
|
||||
else
|
||||
y(integer, ws->num);
|
||||
|
||||
ystr("name");
|
||||
|
@ -1051,20 +1120,22 @@ int ipc_create_socket(const char *filename) {
|
|||
}
|
||||
|
||||
/*
|
||||
* For the workspace "focus" event we send, along the usual "change" field,
|
||||
* also the current and previous workspace, in "current" and "old"
|
||||
* respectively.
|
||||
* Generates a json workspace event. Returns a dynamically allocated yajl
|
||||
* generator. Free with yajl_gen_free().
|
||||
*/
|
||||
void ipc_send_workspace_focus_event(Con *current, Con *old) {
|
||||
yajl_gen ipc_marshal_workspace_event(const char *change, Con *current, Con *old) {
|
||||
setlocale(LC_NUMERIC, "C");
|
||||
yajl_gen gen = ygenalloc();
|
||||
|
||||
y(map_open);
|
||||
|
||||
ystr("change");
|
||||
ystr("focus");
|
||||
ystr(change);
|
||||
|
||||
ystr("current");
|
||||
if (current == NULL)
|
||||
y(null);
|
||||
else
|
||||
dump_node(gen, current, false);
|
||||
|
||||
ystr("old");
|
||||
|
@ -1075,13 +1146,26 @@ void ipc_send_workspace_focus_event(Con *current, Con *old) {
|
|||
|
||||
y(map_close);
|
||||
|
||||
setlocale(LC_NUMERIC, "");
|
||||
|
||||
return gen;
|
||||
}
|
||||
|
||||
/*
|
||||
* For the workspace events we send, along with the usual "change" field, also
|
||||
* the workspace container in "current". For focus events, we send the
|
||||
* previously focused workspace in "old".
|
||||
*/
|
||||
void ipc_send_workspace_event(const char *change, Con *current, Con *old) {
|
||||
yajl_gen gen = ipc_marshal_workspace_event(change, current, old);
|
||||
|
||||
const unsigned char *payload;
|
||||
ylength length;
|
||||
y(get_buf, &payload, &length);
|
||||
|
||||
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, (const char *)payload);
|
||||
|
||||
y(free);
|
||||
setlocale(LC_NUMERIC, "");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1132,3 +1216,33 @@ void ipc_send_barconfig_update_event(Barconfig *barconfig) {
|
|||
y(free);
|
||||
setlocale(LC_NUMERIC, "");
|
||||
}
|
||||
|
||||
/*
|
||||
* For the binding events, we send the serialized binding struct.
|
||||
*/
|
||||
void ipc_send_binding_event(const char *event_type, Binding *bind) {
|
||||
DLOG("Issue IPC binding %s event (sym = %s, code = %d)\n", event_type, bind->symbol, bind->keycode);
|
||||
|
||||
setlocale(LC_NUMERIC, "C");
|
||||
|
||||
yajl_gen gen = ygenalloc();
|
||||
|
||||
y(map_open);
|
||||
|
||||
ystr("change");
|
||||
ystr(event_type);
|
||||
|
||||
ystr("binding");
|
||||
dump_binding(gen, bind);
|
||||
|
||||
y(map_close);
|
||||
|
||||
const unsigned char *payload;
|
||||
ylength length;
|
||||
y(get_buf, &payload, &length);
|
||||
|
||||
ipc_send_event("binding", I3_IPC_EVENT_BINDING, (const char *)payload);
|
||||
|
||||
y(free);
|
||||
setlocale(LC_NUMERIC, "");
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ void handle_key_press(xcb_key_press_event_t *event) {
|
|||
if (bind == NULL)
|
||||
return;
|
||||
|
||||
CommandResult *result = run_binding(bind);
|
||||
CommandResult *result = run_binding(bind, NULL);
|
||||
|
||||
if (result->needs_tree_render)
|
||||
tree_render();
|
||||
|
|
|
@ -24,6 +24,7 @@ static Con *json_node;
|
|||
static Con *to_focus;
|
||||
static bool parsing_swallows;
|
||||
static bool parsing_rect;
|
||||
static bool parsing_deco_rect;
|
||||
static bool parsing_window_rect;
|
||||
static bool parsing_geometry;
|
||||
static bool parsing_focus;
|
||||
|
@ -47,7 +48,7 @@ static int json_start_map(void *ctx) {
|
|||
match_init(current_swallow);
|
||||
TAILQ_INSERT_TAIL(&(json_node->swallow_head), current_swallow, matches);
|
||||
} else {
|
||||
if (!parsing_rect && !parsing_window_rect && !parsing_geometry) {
|
||||
if (!parsing_rect && !parsing_deco_rect && !parsing_window_rect && !parsing_geometry) {
|
||||
if (last_key && strcasecmp(last_key, "floating_nodes") == 0) {
|
||||
DLOG("New floating_node\n");
|
||||
Con *ws = con_get_workspace(json_node);
|
||||
|
@ -68,7 +69,7 @@ static int json_start_map(void *ctx) {
|
|||
|
||||
static int json_end_map(void *ctx) {
|
||||
LOG("end of map\n");
|
||||
if (!parsing_swallows && !parsing_rect && !parsing_window_rect && !parsing_geometry) {
|
||||
if (!parsing_swallows && !parsing_rect && !parsing_deco_rect && !parsing_window_rect && !parsing_geometry) {
|
||||
/* Set a few default values to simplify manually crafted layout files. */
|
||||
if (json_node->layout == L_DEFAULT) {
|
||||
DLOG("Setting layout = L_SPLITH\n");
|
||||
|
@ -113,11 +114,6 @@ static int json_end_map(void *ctx) {
|
|||
|
||||
/* Set num accordingly so that i3bar will properly sort it. */
|
||||
json_node->num = ws_name_to_number(json_node->name);
|
||||
} else {
|
||||
// TODO: remove this in the “next” branch.
|
||||
if (json_node->name == NULL || strcmp(json_node->name, "") == 0) {
|
||||
json_node->name = sstrdup("#ff0000");
|
||||
}
|
||||
}
|
||||
|
||||
LOG("attaching\n");
|
||||
|
@ -126,11 +122,10 @@ static int json_end_map(void *ctx) {
|
|||
x_con_init(json_node, json_node->depth);
|
||||
json_node = json_node->parent;
|
||||
}
|
||||
if (parsing_rect)
|
||||
|
||||
parsing_rect = false;
|
||||
if (parsing_window_rect)
|
||||
parsing_deco_rect = false;
|
||||
parsing_window_rect = false;
|
||||
if (parsing_geometry)
|
||||
parsing_geometry = false;
|
||||
return 1;
|
||||
}
|
||||
|
@ -180,6 +175,9 @@ static int json_key(void *ctx, const unsigned char *val, size_t len) {
|
|||
if (strcasecmp(last_key, "rect") == 0)
|
||||
parsing_rect = true;
|
||||
|
||||
if (strcasecmp(last_key, "deco_rect") == 0)
|
||||
parsing_deco_rect = true;
|
||||
|
||||
if (strcasecmp(last_key, "window_rect") == 0)
|
||||
parsing_window_rect = true;
|
||||
|
||||
|
@ -553,6 +551,7 @@ void tree_append_json(Con *con, const char *filename, char **errormsg) {
|
|||
to_focus = NULL;
|
||||
parsing_swallows = false;
|
||||
parsing_rect = false;
|
||||
parsing_deco_rect = false;
|
||||
parsing_window_rect = false;
|
||||
parsing_geometry = false;
|
||||
parsing_focus = false;
|
||||
|
|
156
src/main.c
156
src/main.c
|
@ -36,10 +36,6 @@ int listen_fds;
|
|||
* temporarily for drag_pointer(). */
|
||||
static struct ev_check *xcb_check;
|
||||
|
||||
static int xkb_event_base;
|
||||
|
||||
int xkb_current_group;
|
||||
|
||||
extern Con *focused;
|
||||
|
||||
char **start_argv;
|
||||
|
@ -70,9 +66,6 @@ struct ev_loop *main_loop;
|
|||
|
||||
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;
|
||||
|
||||
|
@ -94,12 +87,6 @@ struct ws_assignments_head ws_assignments = TAILQ_HEAD_INITIALIZER(ws_assignment
|
|||
|
||||
/* We hope that those are supported and set them to true */
|
||||
bool xcursor_supported = true;
|
||||
bool xkb_supported = true;
|
||||
|
||||
/* This will be set to true when -C is used so that functions can behave
|
||||
* slightly differently. We don’t want i3-nagbar to be started when validating
|
||||
* the config, for example. */
|
||||
bool only_check_config = false;
|
||||
|
||||
/*
|
||||
* This callback is only a dummy, see xcb_prepare_cb and xcb_check_cb.
|
||||
|
@ -166,73 +153,6 @@ void main_set_x11_cb(bool enable) {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* When using xmodmap to change the keyboard mapping, this event
|
||||
* is only sent via XKB. Therefore, we need this special handler.
|
||||
*
|
||||
*/
|
||||
static void xkb_got_event(EV_P_ struct ev_io *w, int revents) {
|
||||
DLOG("Handling XKB event\n");
|
||||
XkbEvent ev;
|
||||
|
||||
/* When using xmodmap, every change (!) gets an own event.
|
||||
* Therefore, we just read all events and only handle the
|
||||
* mapping_notify once. */
|
||||
bool mapping_changed = false;
|
||||
while (XPending(xkbdpy)) {
|
||||
XNextEvent(xkbdpy, (XEvent *)&ev);
|
||||
/* While we should never receive a non-XKB event,
|
||||
* better do sanity checking */
|
||||
if (ev.type != xkb_event_base)
|
||||
continue;
|
||||
|
||||
if (ev.any.xkb_type == XkbMapNotify) {
|
||||
mapping_changed = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ev.any.xkb_type != XkbStateNotify) {
|
||||
ELOG("Unknown XKB event received (type %d)\n", ev.any.xkb_type);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* See The XKB Extension: Library Specification, section 14.1 */
|
||||
/* We check if the current group (each group contains
|
||||
* two levels) has been changed. Mode_switch activates
|
||||
* group XkbGroup2Index */
|
||||
if (xkb_current_group == ev.state.group)
|
||||
continue;
|
||||
|
||||
xkb_current_group = ev.state.group;
|
||||
|
||||
if (ev.state.group == XkbGroup2Index) {
|
||||
DLOG("Mode_switch enabled\n");
|
||||
grab_all_keys(conn, true);
|
||||
}
|
||||
|
||||
if (ev.state.group == XkbGroup1Index) {
|
||||
DLOG("Mode_switch disabled\n");
|
||||
ungrab_all_keys(conn);
|
||||
grab_all_keys(conn, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (!mapping_changed)
|
||||
return;
|
||||
|
||||
DLOG("Keyboard mapping changed, updating keybindings\n");
|
||||
xcb_key_symbols_free(keysyms);
|
||||
keysyms = xcb_key_symbols_alloc(conn);
|
||||
|
||||
xcb_numlock_mask = aio_get_mod_mask_for(XCB_NUM_LOCK, keysyms);
|
||||
|
||||
ungrab_all_keys(conn);
|
||||
DLOG("Re-grabbing...\n");
|
||||
translate_keysyms();
|
||||
grab_all_keys(conn, (xkb_current_group == XkbGroup2Index));
|
||||
DLOG("Done\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Exit handler which destroys the main_loop. Will trigger cleanup handlers.
|
||||
*
|
||||
|
@ -276,6 +196,7 @@ int main(int argc, char *argv[]) {
|
|||
bool force_xinerama = false;
|
||||
char *fake_outputs = NULL;
|
||||
bool disable_signalhandler = false;
|
||||
bool only_check_config = false;
|
||||
static struct option long_options[] = {
|
||||
{"no-autostart", no_argument, 0, 'a'},
|
||||
{"config", required_argument, 0, 'c'},
|
||||
|
@ -441,10 +362,14 @@ int main(int argc, char *argv[]) {
|
|||
}
|
||||
}
|
||||
|
||||
if (only_check_config) {
|
||||
exit(parse_configuration(override_configpath, false) ? 0 : 1);
|
||||
}
|
||||
|
||||
/* If the user passes more arguments, we act like i3-msg would: Just send
|
||||
* the arguments as an IPC message to i3. This allows for nice semantic
|
||||
* commands such as 'i3 border none'. */
|
||||
if (!only_check_config && optind < argc) {
|
||||
if (optind < argc) {
|
||||
/* We enable verbose mode so that the user knows what’s going on.
|
||||
* This should make it easier to find mistakes when the user passes
|
||||
* arguments by mistake. */
|
||||
|
@ -567,10 +492,6 @@ int main(int argc, char *argv[]) {
|
|||
xcb_query_pointer_cookie_t pointercookie = xcb_query_pointer(conn, root);
|
||||
|
||||
load_configuration(conn, override_configpath, false);
|
||||
if (only_check_config) {
|
||||
LOG("Done checking configuration file. Exiting.\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (config.ipc_socket_path == NULL) {
|
||||
/* Fall back to a file name in /tmp/ based on the PID */
|
||||
|
@ -597,21 +518,7 @@ int main(int argc, char *argv[]) {
|
|||
#include "atoms.xmacro"
|
||||
#undef xmacro
|
||||
|
||||
/* Initialize the Xlib connection */
|
||||
xlibdpy = xkbdpy = XOpenDisplay(NULL);
|
||||
|
||||
/* Try to load the X cursors and initialize the XKB extension */
|
||||
if (xlibdpy == NULL) {
|
||||
ELOG("ERROR: XOpenDisplay() failed, disabling libXcursor/XKB support\n");
|
||||
xcursor_supported = false;
|
||||
xkb_supported = false;
|
||||
} else if (fcntl(ConnectionNumber(xlibdpy), F_SETFD, FD_CLOEXEC) == -1) {
|
||||
ELOG("Could not set FD_CLOEXEC on xkbdpy\n");
|
||||
return 1;
|
||||
} else {
|
||||
xcursor_load_cursors();
|
||||
/*init_xkb();*/
|
||||
}
|
||||
|
||||
/* Set a cursor for the root window (otherwise the root window will show no
|
||||
cursor until the first client is launched). */
|
||||
|
@ -620,27 +527,22 @@ int main(int argc, char *argv[]) {
|
|||
else
|
||||
xcb_set_root_cursor(XCURSOR_CURSOR_POINTER);
|
||||
|
||||
if (xkb_supported) {
|
||||
int errBase,
|
||||
major = XkbMajorVersion,
|
||||
minor = XkbMinorVersion;
|
||||
|
||||
if (fcntl(ConnectionNumber(xkbdpy), F_SETFD, FD_CLOEXEC) == -1) {
|
||||
fprintf(stderr, "Could not set FD_CLOEXEC on xkbdpy\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int i1;
|
||||
if (!XkbQueryExtension(xkbdpy, &i1, &xkb_event_base, &errBase, &major, &minor)) {
|
||||
fprintf(stderr, "XKB not supported by X-server\n");
|
||||
xkb_supported = false;
|
||||
}
|
||||
/* end of ugliness */
|
||||
|
||||
if (xkb_supported && !XkbSelectEvents(xkbdpy, XkbUseCoreKbd, XkbMapNotifyMask | XkbStateNotifyMask, XkbMapNotifyMask | XkbStateNotifyMask)) {
|
||||
fprintf(stderr, "Could not set XKB event mask\n");
|
||||
return 1;
|
||||
}
|
||||
const xcb_query_extension_reply_t *extreply;
|
||||
extreply = xcb_get_extension_data(conn, &xcb_xkb_id);
|
||||
if (!extreply->present) {
|
||||
DLOG("xkb is not present on this server\n");
|
||||
} else {
|
||||
DLOG("initializing xcb-xkb\n");
|
||||
xcb_xkb_use_extension(conn, XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION);
|
||||
xcb_xkb_select_events(conn,
|
||||
XCB_XKB_ID_USE_CORE_KBD,
|
||||
XCB_XKB_EVENT_TYPE_STATE_NOTIFY | XCB_XKB_EVENT_TYPE_MAP_NOTIFY,
|
||||
0,
|
||||
XCB_XKB_EVENT_TYPE_STATE_NOTIFY | XCB_XKB_EVENT_TYPE_MAP_NOTIFY,
|
||||
0xff,
|
||||
0xff,
|
||||
NULL);
|
||||
xkb_base = extreply->first_event;
|
||||
}
|
||||
|
||||
restore_connect();
|
||||
|
@ -769,25 +671,19 @@ int main(int argc, char *argv[]) {
|
|||
x_set_i3_atoms();
|
||||
ewmh_update_workarea();
|
||||
|
||||
/* Set the _NET_CURRENT_DESKTOP property. */
|
||||
/* Set the ewmh desktop properties. */
|
||||
ewmh_update_current_desktop();
|
||||
ewmh_update_number_of_desktops();
|
||||
ewmh_update_desktop_names();
|
||||
ewmh_update_desktop_viewport();
|
||||
|
||||
struct ev_io *xcb_watcher = scalloc(sizeof(struct ev_io));
|
||||
struct ev_io *xkb = scalloc(sizeof(struct ev_io));
|
||||
xcb_check = scalloc(sizeof(struct ev_check));
|
||||
struct ev_prepare *xcb_prepare = scalloc(sizeof(struct ev_prepare));
|
||||
|
||||
ev_io_init(xcb_watcher, xcb_got_event, xcb_get_file_descriptor(conn), EV_READ);
|
||||
ev_io_start(main_loop, xcb_watcher);
|
||||
|
||||
if (xkb_supported) {
|
||||
ev_io_init(xkb, xkb_got_event, ConnectionNumber(xkbdpy), EV_READ);
|
||||
ev_io_start(main_loop, xkb);
|
||||
|
||||
/* Flush the buffer so that libev can properly get new events */
|
||||
XFlush(xkbdpy);
|
||||
}
|
||||
|
||||
ev_check_init(xcb_check, xcb_check_cb);
|
||||
ev_check_start(main_loop, xcb_check);
|
||||
|
||||
|
|
21
src/manage.c
21
src/manage.c
|
@ -436,11 +436,6 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
|
|||
if (nc->geometry.width == 0)
|
||||
nc->geometry = (Rect) {geom->x, geom->y, geom->width, geom->height};
|
||||
|
||||
if (want_floating) {
|
||||
DLOG("geometry = %d x %d\n", nc->geometry.width, nc->geometry.height);
|
||||
floating_enable(nc, true);
|
||||
}
|
||||
|
||||
if (motif_border_style != BS_NORMAL) {
|
||||
DLOG("MOTIF_WM_HINTS specifies decorations (border_style = %d)\n", motif_border_style);
|
||||
if (want_floating) {
|
||||
|
@ -450,12 +445,18 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
|
|||
}
|
||||
}
|
||||
|
||||
if (nc->border_style == BS_PIXEL) {
|
||||
/* if the border style is BS_PIXEL, explicitly set the border width of
|
||||
* the new container */
|
||||
nc->current_border_width = (want_floating ? config.default_floating_border_width : config.default_border_width);
|
||||
if (want_floating) {
|
||||
DLOG("geometry = %d x %d\n", nc->geometry.width, nc->geometry.height);
|
||||
/* automatically set the border to the default value if a motif border
|
||||
* was not specified */
|
||||
bool automatic_border = (motif_border_style == BS_NORMAL);
|
||||
|
||||
floating_enable(nc, automatic_border);
|
||||
}
|
||||
|
||||
/* explicitly set the border width to the default */
|
||||
nc->current_border_width = (want_floating ? config.default_floating_border_width : config.default_border_width);
|
||||
|
||||
/* to avoid getting an UnmapNotify event due to reparenting, we temporarily
|
||||
* declare no interest in any state change event of this window */
|
||||
values[0] = XCB_NONE;
|
||||
|
@ -511,7 +512,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
|
|||
|
||||
/* Defer setting focus after the 'new' event has been sent to ensure the
|
||||
* proper window event sequence. */
|
||||
if (set_focus && nc->mapped) {
|
||||
if (set_focus && !nc->window->doesnt_accept_focus && nc->mapped) {
|
||||
DLOG("Now setting focus.\n");
|
||||
con_focus(nc);
|
||||
}
|
||||
|
|
10
src/move.c
10
src/move.c
|
@ -128,22 +128,21 @@ static void move_to_output_directed(Con *con, direction_t direction) {
|
|||
|
||||
tree_flatten(croot);
|
||||
|
||||
ipc_send_workspace_focus_event(ws, old_ws);
|
||||
ipc_send_workspace_event("focus", ws, old_ws);
|
||||
}
|
||||
|
||||
/*
|
||||
* Moves the current container in the given direction (D_LEFT, D_RIGHT,
|
||||
* Moves the given container in the given direction (D_LEFT, D_RIGHT,
|
||||
* D_UP, D_DOWN).
|
||||
*
|
||||
*/
|
||||
void tree_move(int direction) {
|
||||
void tree_move(Con *con, int direction) {
|
||||
position_t position;
|
||||
Con *target;
|
||||
|
||||
DLOG("Moving in direction %d\n", direction);
|
||||
|
||||
/* 1: get the first parent with the same orientation */
|
||||
Con *con = focused;
|
||||
|
||||
if (con->type == CT_WORKSPACE) {
|
||||
DLOG("Not moving workspace\n");
|
||||
|
@ -206,6 +205,7 @@ void tree_move(int direction) {
|
|||
TAILQ_INSERT_HEAD(&(swap->parent->focus_head), con, focused);
|
||||
|
||||
DLOG("Swapped.\n");
|
||||
ipc_send_window_event("move", con);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -213,6 +213,7 @@ void tree_move(int direction) {
|
|||
/* If we couldn't find a place to move it on this workspace,
|
||||
* try to move it to a workspace on a different output */
|
||||
move_to_output_directed(con, direction);
|
||||
ipc_send_window_event("move", con);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -264,4 +265,5 @@ end:
|
|||
FREE(con->deco_render_params);
|
||||
|
||||
tree_flatten(croot);
|
||||
ipc_send_window_event("move", con);
|
||||
}
|
||||
|
|
|
@ -298,6 +298,8 @@ void render_con(Con *con, bool render_fullscreen) {
|
|||
while (transient_con != NULL &&
|
||||
transient_con->window != NULL &&
|
||||
transient_con->window->transient_for != XCB_NONE) {
|
||||
DLOG("transient_con = 0x%08x, transient_con->window->transient_for = 0x%08x, fullscreen_id = 0x%08x\n",
|
||||
transient_con->window->id, transient_con->window->transient_for, fullscreen->window->id);
|
||||
if (transient_con->window->transient_for == fullscreen->window->id) {
|
||||
is_transient_for = true;
|
||||
break;
|
||||
|
|
|
@ -197,6 +197,7 @@ static void open_placeholder_window(Con *con) {
|
|||
/* Set the same name as was stored in the layout file. While perhaps
|
||||
* slightly confusing in the first instant, this brings additional
|
||||
* clarity to which placeholder is waiting for which actual window. */
|
||||
if (con->name != NULL)
|
||||
xcb_change_property(restore_conn, XCB_PROP_MODE_REPLACE, placeholder,
|
||||
A__NET_WM_NAME, A_UTF8_STRING, 8, strlen(con->name), con->name);
|
||||
DLOG("Created placeholder window 0x%08x for leaf container %p / %s\n",
|
||||
|
|
|
@ -123,8 +123,8 @@ void startup_sequence_delete(struct Startup_Sequence *sequence) {
|
|||
* the application is reparented to init (process-id 1), which correctly handles
|
||||
* childs, so we don’t have to do it :-).
|
||||
*
|
||||
* The shell is determined by looking for the SHELL environment variable. If it
|
||||
* does not exist, /bin/sh is used.
|
||||
* The shell used to start applications is the system's bourne shell (i.e.,
|
||||
* /bin/sh).
|
||||
*
|
||||
* The no_startup_id flag determines whether a startup notification context
|
||||
* (and ID) should be created, which is the default and encouraged behavior.
|
||||
|
|
28
src/tree.c
28
src/tree.c
|
@ -255,6 +255,7 @@ bool tree_close(Con *con, kill_window_t kill_window, bool dont_kill_parent, bool
|
|||
* X11 Errors are returned when the window was already destroyed */
|
||||
add_ignore_event(cookie.sequence, 0);
|
||||
}
|
||||
ipc_send_window_event("close", con);
|
||||
FREE(con->window->class_class);
|
||||
FREE(con->window->class_instance);
|
||||
i3string_free(con->window->name);
|
||||
|
@ -406,8 +407,7 @@ void tree_split(Con *con, orientation_t orientation) {
|
|||
Con *parent = con->parent;
|
||||
|
||||
/* Force re-rendering to make the indicator border visible. */
|
||||
FREE(con->deco_render_params);
|
||||
FREE(parent->deco_render_params);
|
||||
con_force_split_parents_redraw(con);
|
||||
|
||||
/* if we are in a container whose parent contains only one
|
||||
* child (its split functionality is unused so far), we just change the
|
||||
|
@ -581,6 +581,13 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap)
|
|||
return true;
|
||||
|
||||
Con *focus = con_descend_direction(workspace, direction);
|
||||
|
||||
/* special case: if there was no tiling con to focus and the workspace
|
||||
* has a floating con in the focus stack, focus the top of the focus
|
||||
* stack (which may be floating) */
|
||||
if (focus == workspace)
|
||||
focus = con_descend_focused(workspace);
|
||||
|
||||
if (focus) {
|
||||
con_focus(focus);
|
||||
x_set_warp_to(&(focus->rect));
|
||||
|
@ -591,8 +598,10 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap)
|
|||
Con *parent = con->parent;
|
||||
|
||||
if (con->type == CT_FLOATING_CON) {
|
||||
if (orientation != HORIZ)
|
||||
return false;
|
||||
|
||||
/* left/right focuses the previous/next floating container */
|
||||
if (orientation == HORIZ) {
|
||||
Con *next;
|
||||
if (way == 'n')
|
||||
next = TAILQ_NEXT(con, floating_windows);
|
||||
|
@ -611,13 +620,16 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap)
|
|||
if (!next)
|
||||
return false;
|
||||
|
||||
/* Raise the floating window on top of other windows preserving
|
||||
* relative stack order */
|
||||
while (TAILQ_LAST(&(parent->floating_head), floating_head) != next) {
|
||||
Con *last = TAILQ_LAST(&(parent->floating_head), floating_head);
|
||||
TAILQ_REMOVE(&(parent->floating_head), last, floating_windows);
|
||||
TAILQ_INSERT_HEAD(&(parent->floating_head), last, floating_windows);
|
||||
}
|
||||
|
||||
con_focus(con_descend_focused(next));
|
||||
return true;
|
||||
} else {
|
||||
/* up/down cycles through the Z-index */
|
||||
/* TODO: implement cycling through the z-index */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* If the orientation does not match or there is no other con to focus, we
|
||||
|
|
|
@ -125,7 +125,8 @@ void window_update_name_legacy(i3Window *win, xcb_get_property_reply_t *prop, bo
|
|||
*/
|
||||
void window_update_leader(i3Window *win, xcb_get_property_reply_t *prop) {
|
||||
if (prop == NULL || xcb_get_property_value_length(prop) == 0) {
|
||||
DLOG("CLIENT_LEADER not set.\n");
|
||||
DLOG("CLIENT_LEADER not set on window 0x%08x.\n", win->id);
|
||||
win->leader = XCB_NONE;
|
||||
FREE(prop);
|
||||
return;
|
||||
}
|
||||
|
@ -149,7 +150,8 @@ void window_update_leader(i3Window *win, xcb_get_property_reply_t *prop) {
|
|||
*/
|
||||
void window_update_transient_for(i3Window *win, xcb_get_property_reply_t *prop) {
|
||||
if (prop == NULL || xcb_get_property_value_length(prop) == 0) {
|
||||
DLOG("TRANSIENT_FOR not set.\n");
|
||||
DLOG("TRANSIENT_FOR not set on window 0x%08x.\n", win->id);
|
||||
win->transient_for = XCB_NONE;
|
||||
FREE(prop);
|
||||
return;
|
||||
}
|
||||
|
@ -160,7 +162,7 @@ void window_update_transient_for(i3Window *win, xcb_get_property_reply_t *prop)
|
|||
return;
|
||||
}
|
||||
|
||||
DLOG("Transient for changed to %08x\n", transient_for);
|
||||
DLOG("Transient for changed to 0x%08x (window 0x%08x)\n", transient_for, win->id);
|
||||
|
||||
win->transient_for = transient_for;
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
*
|
||||
*/
|
||||
#include "all.h"
|
||||
#include "yajl_utils.h"
|
||||
|
||||
/* Stores a copy of the name of the last used workspace for the workspace
|
||||
* back-and-forth switching. */
|
||||
|
@ -91,7 +92,10 @@ Con *workspace_get(const char *num, bool *created) {
|
|||
|
||||
con_attach(workspace, content, false);
|
||||
|
||||
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"init\"}");
|
||||
ipc_send_workspace_event("init", workspace, NULL);
|
||||
ewmh_update_number_of_desktops();
|
||||
ewmh_update_desktop_names();
|
||||
ewmh_update_desktop_viewport();
|
||||
if (created != NULL)
|
||||
*created = true;
|
||||
} else if (created != NULL) {
|
||||
|
@ -123,7 +127,7 @@ Con *create_workspace_on_output(Output *output, Con *content) {
|
|||
strncasecmp(bind->command, "workspace", strlen("workspace")) != 0)
|
||||
continue;
|
||||
DLOG("relevant command = %s\n", bind->command);
|
||||
char *target = bind->command + strlen("workspace ");
|
||||
const char *target = bind->command + strlen("workspace ");
|
||||
while ((*target == ' ' || *target == '\t') && target != '\0')
|
||||
target++;
|
||||
/* We check if this is the workspace
|
||||
|
@ -139,16 +143,16 @@ Con *create_workspace_on_output(Output *output, Con *content) {
|
|||
strncasecmp(target, "back_and_forth", strlen("back_and_forth")) == 0 ||
|
||||
strncasecmp(target, "current", strlen("current")) == 0)
|
||||
continue;
|
||||
if (*target == '"')
|
||||
target++;
|
||||
if (strncasecmp(target, "__", strlen("__")) == 0) {
|
||||
char *target_name = parse_string(&target, false);
|
||||
if (target_name == NULL)
|
||||
continue;
|
||||
if (strncasecmp(target_name, "__", strlen("__")) == 0) {
|
||||
LOG("Cannot create workspace \"%s\". Names starting with __ are i3-internal.\n", target);
|
||||
free(target_name);
|
||||
continue;
|
||||
}
|
||||
FREE(ws->name);
|
||||
ws->name = strdup(target);
|
||||
if (ws->name[strlen(ws->name) - 1] == '"')
|
||||
ws->name[strlen(ws->name) - 1] = '\0';
|
||||
ws->name = target_name;
|
||||
DLOG("trying name *%s*\n", ws->name);
|
||||
|
||||
/* Ensure that this workspace is not assigned to a different output —
|
||||
|
@ -176,15 +180,7 @@ Con *create_workspace_on_output(Output *output, Con *content) {
|
|||
if (!exists) {
|
||||
/* Set ->num to the number of the workspace, if the name actually
|
||||
* is a number or starts with a number */
|
||||
char *endptr = NULL;
|
||||
long parsed_num = strtol(ws->name, &endptr, 10);
|
||||
if (parsed_num == LONG_MIN ||
|
||||
parsed_num == LONG_MAX ||
|
||||
parsed_num < 0 ||
|
||||
endptr == ws->name)
|
||||
ws->num = -1;
|
||||
else
|
||||
ws->num = parsed_num;
|
||||
ws->num = ws_name_to_number(ws->name);
|
||||
LOG("Used number %d for workspace with name %s\n", ws->num, ws->name);
|
||||
|
||||
break;
|
||||
|
@ -325,11 +321,14 @@ static void workspace_reassign_sticky(Con *con) {
|
|||
static void workspace_defer_update_urgent_hint_cb(EV_P_ ev_timer *w, int revents) {
|
||||
Con *con = w->data;
|
||||
|
||||
if (con->urgent) {
|
||||
DLOG("Resetting urgency flag of con %p by timer\n", con);
|
||||
con->urgent = false;
|
||||
con_update_parents_urgency(con);
|
||||
workspace_update_urgent_flag(con_get_workspace(con));
|
||||
ipc_send_window_event("urgent", con);
|
||||
tree_render();
|
||||
}
|
||||
|
||||
ev_timer_stop(main_loop, con->urgency_timer);
|
||||
FREE(con->urgency_timer);
|
||||
|
@ -385,6 +384,7 @@ static void _workspace_show(Con *workspace) {
|
|||
* focus and thereby immediately destroy it */
|
||||
if (next->urgent && (int)(config.workspace_urgency_timer * 1000) > 0) {
|
||||
/* focus for now… */
|
||||
next->urgent = false;
|
||||
con_focus(next);
|
||||
|
||||
/* … but immediately reset urgency flags; they will be set to false by
|
||||
|
@ -410,7 +410,7 @@ static void _workspace_show(Con *workspace) {
|
|||
} else
|
||||
con_focus(next);
|
||||
|
||||
ipc_send_workspace_focus_event(workspace, current);
|
||||
ipc_send_workspace_event("focus", workspace, current);
|
||||
|
||||
DLOG("old = %p / %s\n", old, (old ? old->name : "(null)"));
|
||||
/* Close old workspace if necessary. This must be done *after* doing
|
||||
|
@ -422,8 +422,19 @@ static void _workspace_show(Con *workspace) {
|
|||
/* check if this workspace is currently visible */
|
||||
if (!workspace_is_visible(old)) {
|
||||
LOG("Closing old workspace (%p / %s), it is empty\n", old, old->name);
|
||||
yajl_gen gen = ipc_marshal_workspace_event("empty", old, NULL);
|
||||
tree_close(old, DONT_KILL_WINDOW, false, false);
|
||||
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"empty\"}");
|
||||
|
||||
const unsigned char *payload;
|
||||
ylength length;
|
||||
y(get_buf, &payload, &length);
|
||||
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, (const char *)payload);
|
||||
|
||||
y(free);
|
||||
|
||||
ewmh_update_number_of_desktops();
|
||||
ewmh_update_desktop_names();
|
||||
ewmh_update_desktop_viewport();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -764,7 +775,7 @@ void workspace_update_urgent_flag(Con *ws) {
|
|||
DLOG("Workspace urgency flag changed from %d to %d\n", old_flag, ws->urgent);
|
||||
|
||||
if (old_flag != ws->urgent)
|
||||
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"urgent\"}");
|
||||
ipc_send_workspace_event("urgent", ws, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
30
src/x.c
30
src/x.c
|
@ -15,9 +15,9 @@
|
|||
/* Stores the X11 window ID of the currently focused window */
|
||||
xcb_window_t focused_id = XCB_NONE;
|
||||
|
||||
/* Because 'focused_id' might be reset to force input focus (after click to
|
||||
* raise), we separately keep track of the X11 window ID to be able to always
|
||||
* tell whether the focused window actually changed. */
|
||||
/* Because 'focused_id' might be reset to force input focus, we separately keep
|
||||
* track of the X11 window ID to be able to always tell whether the focused
|
||||
* window actually changed. */
|
||||
static xcb_window_t last_focused = XCB_NONE;
|
||||
|
||||
/* Stores coordinates to warp mouse pointer to if set */
|
||||
|
@ -430,16 +430,16 @@ void x_draw_decoration(Con *con) {
|
|||
xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &leftline);
|
||||
}
|
||||
if (!(borders_to_hide & ADJ_RIGHT_SCREEN_EDGE)) {
|
||||
xcb_rectangle_t rightline = {r->width + br.width + br.x, 0, r->width, r->height};
|
||||
xcb_rectangle_t rightline = {r->width + (br.width + br.x), 0, -(br.width + br.x), r->height};
|
||||
xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &rightline);
|
||||
}
|
||||
if (!(borders_to_hide & ADJ_LOWER_SCREEN_EDGE)) {
|
||||
xcb_rectangle_t bottomline = {0, r->height + br.height + br.y, r->width, r->height};
|
||||
xcb_rectangle_t bottomline = {br.x, r->height + (br.height + br.y), r->width + br.width, -(br.height + br.y)};
|
||||
xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &bottomline);
|
||||
}
|
||||
/* 1pixel border needs an additional line at the top */
|
||||
if (p->border_style == BS_PIXEL && !(borders_to_hide & ADJ_UPPER_SCREEN_EDGE)) {
|
||||
xcb_rectangle_t topline = {br.x, 0, con->rect.width + br.width + br.x, br.y};
|
||||
xcb_rectangle_t topline = {br.x, 0, r->width + br.width, br.y};
|
||||
xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &topline);
|
||||
}
|
||||
|
||||
|
@ -453,10 +453,10 @@ void x_draw_decoration(Con *con) {
|
|||
xcb_change_gc(conn, con->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {p->color->indicator});
|
||||
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}});
|
||||
{r->width + (br.width + br.x), br.y, -(br.width + br.x), r->height + br.height}});
|
||||
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}});
|
||||
{br.x, r->height + (br.height + br.y), r->width + br.width, -(br.height + br.y)}});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -473,12 +473,12 @@ void x_draw_decoration(Con *con) {
|
|||
/* 5: draw two unconnected horizontal lines in border color */
|
||||
xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {p->color->border});
|
||||
Rect *dr = &(con->deco_rect);
|
||||
int deco_diff_l = 2;
|
||||
int deco_diff_r = 2;
|
||||
if (parent->layout == L_TABBED) {
|
||||
if (TAILQ_PREV(con, nodes_head, nodes) != NULL)
|
||||
adjacent_t borders_to_hide = con_adjacent_borders(con) & config.hide_edge_borders;
|
||||
int deco_diff_l = borders_to_hide & ADJ_LEFT_SCREEN_EDGE ? 0 : con->current_border_width;
|
||||
int deco_diff_r = borders_to_hide & ADJ_RIGHT_SCREEN_EDGE ? 0 : con-> current_border_width;
|
||||
if (parent->layout == L_TABBED ||
|
||||
(parent->layout == L_STACKED && TAILQ_NEXT(con, nodes) != NULL)) {
|
||||
deco_diff_l = 0;
|
||||
if (TAILQ_NEXT(con, nodes) != NULL)
|
||||
deco_diff_r = 0;
|
||||
}
|
||||
xcb_segment_t segments[] = {
|
||||
|
@ -1023,7 +1023,7 @@ void x_push_changes(Con *con) {
|
|||
values[0] = CHILD_EVENT_MASK & ~(XCB_EVENT_MASK_FOCUS_CHANGE);
|
||||
xcb_change_window_attributes(conn, focused->window->id, XCB_CW_EVENT_MASK, values);
|
||||
}
|
||||
xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, to_focus, last_timestamp);
|
||||
xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, to_focus, XCB_CURRENT_TIME);
|
||||
if (focused->window != NULL) {
|
||||
values[0] = CHILD_EVENT_MASK;
|
||||
xcb_change_window_attributes(conn, focused->window->id, XCB_CW_EVENT_MASK, values);
|
||||
|
@ -1041,7 +1041,7 @@ void x_push_changes(Con *con) {
|
|||
|
||||
if (focused_id == XCB_NONE) {
|
||||
DLOG("Still no window focused, better set focus to the root window\n");
|
||||
xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, root, last_timestamp);
|
||||
xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, root, XCB_CURRENT_TIME);
|
||||
ewmh_update_active_window(XCB_WINDOW_NONE);
|
||||
focused_id = root;
|
||||
}
|
||||
|
|
|
@ -8,4 +8,3 @@ inc
|
|||
META.yml
|
||||
i3-cfg-for-*
|
||||
-
|
||||
Xdummy.so
|
||||
|
|
|
@ -11,6 +11,7 @@ WriteMakefile(
|
|||
'AnyEvent::I3' => '0.15',
|
||||
'X11::XCB' => '0.09',
|
||||
'Inline' => 0,
|
||||
'Inline::C' => 0,
|
||||
'ExtUtils::PkgConfig' => 0,
|
||||
'Test::More' => '0.94',
|
||||
'IPC::Run' => 0,
|
||||
|
|
1930
testcases/Xdummy
1930
testcases/Xdummy
File diff suppressed because it is too large
Load Diff
|
@ -19,7 +19,7 @@ use Time::HiRes qw(time);
|
|||
use IO::Handle;
|
||||
# these are shipped with the testsuite
|
||||
use lib qw(lib);
|
||||
use StartXDummy;
|
||||
use StartXServer;
|
||||
use StatusLine;
|
||||
use TestWorker;
|
||||
# the following modules are not shipped with Perl
|
||||
|
@ -43,7 +43,7 @@ sub Log { say $log "@_" }
|
|||
|
||||
my %timings;
|
||||
my $help = 0;
|
||||
# Number of tests to run in parallel. Important to know how many Xdummy
|
||||
# Number of tests to run in parallel. Important to know how many Xephyr
|
||||
# instances we need to start (unless @displays are given). Defaults to
|
||||
# num_cores * 2.
|
||||
my $parallel = undef;
|
||||
|
@ -55,11 +55,11 @@ my %options = (
|
|||
coverage => 0,
|
||||
restart => 0,
|
||||
);
|
||||
my $keep_xdummy_output = 0;
|
||||
my $keep_xserver_output = 0;
|
||||
|
||||
my $result = GetOptions(
|
||||
"coverage-testing" => \$options{coverage},
|
||||
"keep-xdummy-output" => \$keep_xdummy_output,
|
||||
"keep-xserver-output" => \$keep_xserver_output,
|
||||
"valgrind" => \$options{valgrind},
|
||||
"strace" => \$options{strace},
|
||||
"xtrace" => \$options{xtrace},
|
||||
|
@ -86,6 +86,9 @@ foreach my $binary (@binaries) {
|
|||
die "$binary is not an executable" unless -x $binary;
|
||||
}
|
||||
|
||||
qx(Xephyr -help 2>&1);
|
||||
die "Xephyr was not found in your path. Please install Xephyr (xserver-xephyr on Debian)." if $?;
|
||||
|
||||
@displays = split(/,/, join(',', @displays));
|
||||
@displays = map { s/ //g; $_ } @displays;
|
||||
|
||||
|
@ -97,9 +100,9 @@ my @testfiles = @ARGV;
|
|||
|
||||
my $numtests = scalar @testfiles;
|
||||
|
||||
# No displays specified, let’s start some Xdummy instances.
|
||||
# No displays specified, let’s start some Xephyr instances.
|
||||
if (@displays == 0) {
|
||||
@displays = start_xdummy($parallel, $numtests, $keep_xdummy_output);
|
||||
@displays = start_xserver($parallel, $numtests, $keep_xserver_output);
|
||||
}
|
||||
|
||||
# 1: create an output directory for this test-run
|
||||
|
@ -115,8 +118,7 @@ symlink("$outdir", "latest") or die "Could not symlink latest to $outdir";
|
|||
# connect to all displays for two reasons:
|
||||
# 1: check if the display actually works
|
||||
# 2: keep the connection open so that i3 is not the only client. this prevents
|
||||
# the X server from exiting (Xdummy will restart it, but not quick enough
|
||||
# sometimes)
|
||||
# the X server from exiting
|
||||
my @single_worker;
|
||||
for my $display (@displays) {
|
||||
my $screen;
|
||||
|
@ -131,7 +133,7 @@ for my $display (@displays) {
|
|||
|
||||
# Read previous timing information, if available. We will be able to roughly
|
||||
# predict the test duration and schedule a good order for the tests.
|
||||
my $timingsjson = StartXDummy::slurp('.last_run_timings.json');
|
||||
my $timingsjson = StartXServer::slurp('.last_run_timings.json');
|
||||
%timings = %{decode_json($timingsjson)} if length($timingsjson) > 0;
|
||||
|
||||
# Re-order the files so that those which took the longest time in the previous
|
||||
|
@ -220,7 +222,7 @@ printf("\t%s with %.2f seconds\n", $_, $timings{$_})
|
|||
if ($numtests == 1) {
|
||||
say '';
|
||||
say 'Test output:';
|
||||
say StartXDummy::slurp($logfile);
|
||||
say StartXServer::slurp($logfile);
|
||||
}
|
||||
|
||||
END { cleanup() }
|
||||
|
@ -346,7 +348,7 @@ complete-run.pl [files...]
|
|||
|
||||
=head1 EXAMPLE
|
||||
|
||||
To run the whole testsuite on a reasonable number of Xdummy instances (your
|
||||
To run the whole testsuite on a reasonable number of Xephyr instances (your
|
||||
running X11 will not be touched), run:
|
||||
./complete-run.pl
|
||||
|
||||
|
@ -365,11 +367,11 @@ will parallelize the tests:
|
|||
# Run tests on the second X server
|
||||
./complete-run.pl -d :1
|
||||
|
||||
# Run four tests in parallel on some Xdummy servers
|
||||
# Run four tests in parallel on some Xephyr servers
|
||||
./complete-run.pl -d :1,:2,:3,:4
|
||||
|
||||
Note that it is not necessary to specify this anymore. If omitted,
|
||||
complete-run.pl will start (num_cores * 2) Xdummy instances.
|
||||
complete-run.pl will start (num_cores * 2) Xephyr instances.
|
||||
|
||||
=item B<--valgrind>
|
||||
|
||||
|
@ -392,8 +394,8 @@ Exits i3 cleanly (instead of kill -9) to make coverage testing work properly.
|
|||
|
||||
=item B<--parallel>
|
||||
|
||||
Number of Xdummy instances to start (if you don’t want to start num_cores * 2
|
||||
Number of Xephyr instances to start (if you don’t want to start num_cores * 2
|
||||
instances for some reason).
|
||||
|
||||
# Run all tests on a single Xdummy instance
|
||||
# Run all tests on a single Xephyr instance
|
||||
./complete-run.pl -p 1
|
||||
|
|
|
@ -20,7 +20,7 @@ bindsym Mod1+h split h
|
|||
bindsym Mod1+v split v
|
||||
|
||||
# Fullscreen (Mod1+f)
|
||||
bindsym Mod1+f fullscreen
|
||||
bindsym Mod1+f fullscreen toggle
|
||||
|
||||
# Stacking (Mod1+s)
|
||||
bindsym Mod1+s layout stacking
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package StartXDummy;
|
||||
package StartXServer;
|
||||
# vim:ts=4:sw=4:expandtab
|
||||
|
||||
use strict;
|
||||
|
@ -7,7 +7,7 @@ use Exporter 'import';
|
|||
use Time::HiRes qw(sleep);
|
||||
use v5.10;
|
||||
|
||||
our @EXPORT = qw(start_xdummy);
|
||||
our @EXPORT = qw(start_xserver);
|
||||
|
||||
my @pids;
|
||||
my $x_socketpath = '/tmp/.X11-unix/X';
|
||||
|
@ -19,15 +19,15 @@ sub slurp {
|
|||
<$fh>;
|
||||
}
|
||||
|
||||
# forks an Xdummy or Xdmx process
|
||||
# forks an X server process
|
||||
sub fork_xserver {
|
||||
my $keep_xdummy_output = shift;
|
||||
my $keep_xserver_output = shift;
|
||||
my $displaynum = shift;
|
||||
my $pid = fork();
|
||||
die "Could not fork: $!" unless defined($pid);
|
||||
if ($pid == 0) {
|
||||
# Child, close stdout/stderr, then start Xdummy.
|
||||
if (!$keep_xdummy_output) {
|
||||
# Child, close stdout/stderr, then start Xephyr
|
||||
if (!$keep_xserver_output) {
|
||||
close STDOUT;
|
||||
close STDERR;
|
||||
}
|
||||
|
@ -60,16 +60,17 @@ sub wait_for_x {
|
|||
}
|
||||
}
|
||||
|
||||
=head2 start_xdummy($parallel)
|
||||
=head2 start_xserver($parallel)
|
||||
|
||||
Starts C<$parallel> (or number of cores * 2 if undef) Xdummy processes (see
|
||||
the file ./Xdummy) and returns two arrayrefs: a list of X11 display numbers to
|
||||
the Xdummy processes and a list of PIDs of the processes.
|
||||
Starts C<$parallel> (or number of cores * 2 if undef) Xephyr processes (see
|
||||
http://www.freedesktop.org/wiki/Software/Xephyr/) and returns two arrayrefs: a
|
||||
list of X11 display numbers to the Xephyr processes and a list of PIDs of the
|
||||
processes.
|
||||
|
||||
=cut
|
||||
|
||||
sub start_xdummy {
|
||||
my ($parallel, $numtests, $keep_xdummy_output) = @_;
|
||||
sub start_xserver {
|
||||
my ($parallel, $numtests, $keep_xserver_output) = @_;
|
||||
|
||||
my @displays = ();
|
||||
my @childpids = ();
|
||||
|
@ -78,11 +79,8 @@ sub start_xdummy {
|
|||
my $child = waitpid -1, POSIX::WNOHANG;
|
||||
@pids = grep { $_ != $child } @pids;
|
||||
return unless @pids == 0;
|
||||
print STDERR "All Xdummy processes died.\n";
|
||||
print STDERR "Use ./complete-run.pl --parallel 1 --keep-xdummy-output\n";
|
||||
print STDERR "";
|
||||
print STDERR "A frequent cause for this is missing the DUMMY Xorg module,\n";
|
||||
print STDERR "package xserver-xorg-video-dummy on Debian.\n";
|
||||
print STDERR "All X server processes died.\n";
|
||||
print STDERR "Use ./complete-run.pl --parallel 1 --keep-xserver-output\n";
|
||||
exit 1;
|
||||
};
|
||||
|
||||
|
@ -104,16 +102,13 @@ sub start_xdummy {
|
|||
my ($displaynum) = map { /(\d+)$/ } reverse sort glob($x_socketpath . '*');
|
||||
$displaynum++;
|
||||
|
||||
say "Starting $parallel Xdummy instances, starting at :$displaynum...";
|
||||
say "Starting $parallel Xephyr instances, starting at :$displaynum...";
|
||||
|
||||
my @sockets_waiting;
|
||||
for (1 .. $parallel) {
|
||||
# We use -config /dev/null to prevent Xdummy from using the system
|
||||
# Xorg configuration. The tests should be independant from the
|
||||
# actual system X configuration.
|
||||
my $socket = fork_xserver($keep_xdummy_output, $displaynum,
|
||||
'./Xdummy', ":$displaynum", '-config', '/dev/null',
|
||||
'-configdir', '/dev/null', '-nolisten', 'tcp');
|
||||
my $socket = fork_xserver($keep_xserver_output, $displaynum,
|
||||
'Xephyr', ":$displaynum", '-screen', '1280x800',
|
||||
'-nolisten', 'tcp');
|
||||
push(@displays, ":$displaynum");
|
||||
push(@sockets_waiting, $socket);
|
||||
$displaynum++;
|
|
@ -337,6 +337,7 @@ sub open_window {
|
|||
$args{name} //= 'Window ' . counter_window();
|
||||
|
||||
my $window = $x->root->create_child(%args);
|
||||
$window->add_hint('input');
|
||||
|
||||
if ($before_map) {
|
||||
# TODO: investigate why _create is not needed
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue