Merge branch 'next' into master
This commit is contained in:
commit
8629c2e208
|
@ -0,0 +1,47 @@
|
|||
language: c
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
before_install:
|
||||
# The travis VMs run on Ubuntu 12.04 which is very old and a huge pain to get
|
||||
# into a state where we can build a recent version of i3 :(.
|
||||
- "echo 'deb http://archive.ubuntu.com/ubuntu/ trusty main universe' | sudo tee /etc/apt/sources.list.d/trusty.list"
|
||||
- "echo 'APT::Default-Release \"precise\";' | sudo tee /etc/apt/apt.conf.d/default-release"
|
||||
- "echo 'deb http://archive.ubuntu.com/ubuntu/ utopic main universe' | sudo tee /etc/apt/sources.list.d/utopic.list"
|
||||
|
||||
- "echo 'Package: libc6' > /tmp/pin"
|
||||
- "echo 'Pin: release n=trusty' >> /tmp/pin"
|
||||
- "echo 'Pin-Priority: 999' >> /tmp/pin"
|
||||
- "echo '' >> /tmp/pin"
|
||||
|
||||
- "echo 'Package: libxkbcommon*' >> /tmp/pin"
|
||||
- "echo 'Pin: release n=trusty' >> /tmp/pin"
|
||||
- "echo 'Pin-Priority: 999' >> /tmp/pin"
|
||||
- "echo '' >> /tmp/pin"
|
||||
|
||||
- "echo 'Package: libyajl*' >> /tmp/pin"
|
||||
- "echo 'Pin: release n=trusty' >> /tmp/pin"
|
||||
- "echo 'Pin-Priority: 999' >> /tmp/pin"
|
||||
- "echo '' >> /tmp/pin"
|
||||
|
||||
- "echo 'Package: libxcb-image*' >> /tmp/pin"
|
||||
- "echo 'Pin: release n=trusty' >> /tmp/pin"
|
||||
- "echo 'Pin-Priority: 999' >> /tmp/pin"
|
||||
- "echo '' >> /tmp/pin"
|
||||
|
||||
- sudo cp /tmp/pin /etc/apt/preferences.d/trustypin
|
||||
- sudo apt-get update
|
||||
- sudo apt-get install -t trusty libc6 libc6-dev
|
||||
- sudo apt-get install --no-install-recommends devscripts equivs
|
||||
- sudo apt-get install -t utopic clang-format-3.5
|
||||
- clang-format-3.5 --version
|
||||
install:
|
||||
- sudo mk-build-deps --install --remove --tool 'apt-get --no-install-recommends' debian/control
|
||||
# Install as many dependencies as possible via apt because cpanm is not very reliable/easy to debug.
|
||||
- sudo apt-get install --no-install-recommends libanyevent-perl libanyevent-i3-perl libextutils-pkgconfig-perl xcb-proto cpanminus xvfb xserver-xephyr xauth libinline-perl libxml-simple-perl libmouse-perl libmousex-nativetraits-perl libextutils-depends-perl perl-modules libtest-deep-perl libtest-exception-perl libxml-parser-perl libtest-simple-perl libtest-fatal-perl libdata-dump-perl libtest-differences-perl libxml-tokeparser-perl libtest-use-ok-perl libipc-run-perl
|
||||
- sudo /bin/sh -c 'cpanm -n -v X11::XCB || true'
|
||||
- sudo /bin/sh -c 'cpanm -n -v AnyEvent::I3 || true'
|
||||
script:
|
||||
- CFLAGS="-Wformat -Wformat-security -Wextra -Wno-unused-parameter -Werror" make -j
|
||||
- (cd testcases && xvfb-run ./complete-run.pl --parallel=1 || (cat latest/complete-run.log; false))
|
||||
- clang-format-3.5 -i $(find . -name "*.[ch]" | tr '\n' ' ') && git diff --exit-code || (echo 'Code was not formatted using clang-format!'; false)
|
|
@ -0,0 +1,68 @@
|
|||
|
||||
┌──────────────────────────────┐
|
||||
│ Release notes for i3 v4.10.1 │
|
||||
└──────────────────────────────┘
|
||||
|
||||
This is i3 v4.10.1. This version is considered stable. All users of i3 are
|
||||
strongly encouraged to upgrade.
|
||||
|
||||
This release contains mostly bugfixes, but we felt it was necessary since there
|
||||
are two important changes in behavior: we have reverted the pango markup
|
||||
parsing by default (introduced with i3 v4.9) and the change in how the
|
||||
“workspace” command behaves (introduced with i3 v4.9). Both of them broke some
|
||||
user’s setups, which is not acceptable. In order to help us avoid such mistakes
|
||||
in the future, please consider using the i3 git version — it is typically
|
||||
stable.
|
||||
|
||||
PS: The v4.10 release did not contain any of the commits we meant to release
|
||||
due to a human error in our release automation. Hence the v4.10.1 release.
|
||||
|
||||
┌────────────────────────────┐
|
||||
│ Changes in i3 v4.10.1 │
|
||||
└────────────────────────────┘
|
||||
|
||||
• i3bar: cut long statuslines from the left
|
||||
• i3bar: add support for the short_text property
|
||||
• i3-sensible-terminal: launch i3-nagbar when no terminal is found
|
||||
• i3-config-wizard: switch modifier on key up/down
|
||||
• docs/layout-saving: added a troubleshooting section
|
||||
• docs: degender all the terms
|
||||
• Revert "Workspace command number selection"
|
||||
• don’t parse blocks as markup by default
|
||||
• Allow escaping backslashes in commands.
|
||||
• switch default font from “DejaVu Sans Mono 8” to “monospace 8”, which is
|
||||
typically a synonym, except for users who prefer a different font.
|
||||
• When renaming a workspace, look for assignments and move the renamed
|
||||
workspace to the appropriate output.
|
||||
• i3-save-tree: make --workspace optional by defaulting to the focused
|
||||
workspace
|
||||
• Allow nop command without argument
|
||||
|
||||
┌────────────────────────────┐
|
||||
│ Bugfixes │
|
||||
└────────────────────────────┘
|
||||
|
||||
• i3bar: buffer the statusline to avoid flickering
|
||||
• i3bar: fix click events for workspace buttons with long statusline
|
||||
• i3bar: set correct initial position when reconfiguring
|
||||
• i3bar: reconfigure strut partial on reload
|
||||
• i3-nagbar: fix sizes/positioning on hi-dpi displays
|
||||
• i3-config-wizard: fix sizes/positioning on hi-dpi displays
|
||||
• i3-input: fix sizes/positioning on hi-dpi displays
|
||||
• Fix scrolling in window decoration with hidden cursor.
|
||||
• workspace rename focus mismatch
|
||||
• Don’t overwrite border width when already set (placeholders).
|
||||
• fix a segfault during config file validation
|
||||
• Restore placeholder windows after restarting.
|
||||
• Don’t focus placeholder windows.
|
||||
|
||||
┌────────────────────────────┐
|
||||
│ Thanks! │
|
||||
└────────────────────────────┘
|
||||
|
||||
Thanks for testing, bugfixes, discussions and everything I forgot go out to:
|
||||
|
||||
Chih-Chyuan Hwang, Deiz, Diana Dinosaur, Ingo Bürk, Michael Hofmann,
|
||||
Michael Tipton, Micha Rosenbaum, shdown, Tony Crisci
|
||||
|
||||
-- Michael Stapelberg, 2015-03-29
|
|
@ -1,36 +0,0 @@
|
|||
|
||||
┌──────────────────────────────┐
|
||||
│ Release notes for i3 v4.9.1 │
|
||||
└──────────────────────────────┘
|
||||
|
||||
This is i3 v4.9.1. This version is considered stable. All users of i3 are
|
||||
strongly encouraged to upgrade.
|
||||
|
||||
This is a bugfix release for i3 v4.9.
|
||||
|
||||
┌────────────────────────────┐
|
||||
│ Bugfixes │
|
||||
└────────────────────────────┘
|
||||
|
||||
• i3bar: fix incorrect y-offset for text
|
||||
• fix key bindings on big-endian platforms
|
||||
• fix key bindings using Mode_switch
|
||||
• fix keyboard layout change detection
|
||||
• revert "Handle WM_CHANGE_STATE requests for iconic state" (fixes problems
|
||||
with application windows disappearing, like SDL-based games when switching
|
||||
workspaces)
|
||||
• insert id-based match at HEAD, not TAIL (fixes window swallowing not
|
||||
working when the criteria match the placeholder window)
|
||||
• improve error messages on failing commands
|
||||
• replace ~ in filepath when calling append_layout
|
||||
• properly error out when the layout file cannot be read
|
||||
|
||||
┌────────────────────────────┐
|
||||
│ Thanks! │
|
||||
└────────────────────────────┘
|
||||
|
||||
Thanks for testing, bugfixes, discussions and everything I forgot go out to:
|
||||
|
||||
Steven McDonald, Ton van den Heuvel, Ingo Bürk
|
||||
|
||||
-- Michael Stapelberg, 2015-03-07
|
|
@ -1,8 +1,20 @@
|
|||
i3-wm (4.9.1-1) experimental; urgency=medium
|
||||
i3-wm (4.10.1-1) experimental; urgency=medium
|
||||
|
||||
* NOT YET RELEASED.
|
||||
|
||||
-- Michael Stapelberg <stapelberg@debian.org> Sat, 28 Feb 2015 15:04:25 +0100
|
||||
-- Michael Stapelberg <stapelberg@debian.org> Sun, 29 Mar 2015 18:08:13 +0200
|
||||
|
||||
i3-wm (4.10-1) experimental; urgency=medium
|
||||
|
||||
* New upstream release.
|
||||
|
||||
-- Michael Stapelberg <stapelberg@debian.org> Sun, 29 Mar 2015 17:46:09 +0200
|
||||
|
||||
i3-wm (4.9.1-1) experimental; urgency=medium
|
||||
|
||||
* New upstream release.
|
||||
|
||||
-- Michael Stapelberg <stapelberg@debian.org> Sat, 07 Mar 2015 20:01:46 +0100
|
||||
|
||||
i3-wm (4.9-1) experimental; urgency=medium
|
||||
|
||||
|
|
|
@ -40,8 +40,7 @@ Package: i3-wm
|
|||
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 (>= 0.12), libjson-xs-perl
|
||||
Recommends: xfonts-base, fonts-dejavu-core, libanyevent-i3-perl (>= 0.12), libjson-xs-perl, rxvt-unicode | x-terminal-emulator
|
||||
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
|
||||
|
|
|
@ -1012,7 +1012,7 @@ gets started in any way) and the window(s) which appear.
|
|||
|
||||
Imagine for example using dmenu: The user starts dmenu by pressing Mod+d, dmenu
|
||||
gets started with PID 3390. The user then decides to launch Firefox, which
|
||||
takes a long time. So he enters firefox into dmenu and presses enter. Firefox
|
||||
takes a long time. So they enter firefox into dmenu and press enter. Firefox
|
||||
gets started with PID 4001. When it finally finishes loading, it creates an X11
|
||||
window and uses MapWindow to make it visible. This is the first time i3
|
||||
actually gets in touch with Firefox. It decides to map the window, but it has
|
||||
|
|
|
@ -117,10 +117,8 @@ click_events::
|
|||
=== Blocks in detail
|
||||
|
||||
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 parsed as
|
||||
https://developer.gnome.org/pango/stable/PangoMarkupFormat.html[Pango markup].
|
||||
The +full_text+ will be displayed by i3bar on the status line. This is the
|
||||
only required key.
|
||||
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
|
||||
|
@ -172,6 +170,10 @@ separator_block_width::
|
|||
this gap, a separator line will be drawn unless +separator+ is
|
||||
disabled. Normally, you want to set this to an odd value (the default
|
||||
is 9 pixels), since the separator line is drawn in the middle.
|
||||
markup::
|
||||
A string that indicates how the text of the block should be parsed. Set to
|
||||
+"pango"+ to use https://developer.gnome.org/pango/stable/PangoMarkupFormat.html[Pango markup].
|
||||
Set to +"none"+ to not use any markup (default).
|
||||
|
||||
If you want to put in your own entries into a block, prefix the key with an
|
||||
underscore (_). i3bar will ignore all keys it doesn’t understand, and prefixing
|
||||
|
|
|
@ -231,3 +231,31 @@ layouts, you can either use a JSON parser that supports these deviations (for
|
|||
example libyajl), transform the layout file to a JSON-conforming file, or
|
||||
link:http://cr.i3wm.org/[submit a patch] to make +i3-save-tree(1)+ optionally
|
||||
output standard-conforming JSON.
|
||||
|
||||
== Troubleshooting
|
||||
|
||||
=== Restoring a vertically split workspace
|
||||
|
||||
When using +i3-save-tree+ with the +--workspace+ switch, only the *contents* of
|
||||
the workspace will be dumped. This means that properties of the workspace
|
||||
itself will be lost.
|
||||
|
||||
This is relevant for, e.g., a vertically split container as the base container of
|
||||
a workspace. Since the split mode is a property of the workspace, it will not be
|
||||
stored. In this case, you will have to manually wrap your layout in such a
|
||||
container:
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
// vim:ts=4:sw=4:et
|
||||
{
|
||||
// this is a manually added container to restore the vertical split
|
||||
"layout": "splitv",
|
||||
"percent": 0.5,
|
||||
"type": "con",
|
||||
"nodes": [
|
||||
|
||||
// the dumped workspace layout goes here
|
||||
|
||||
]
|
||||
}
|
||||
--------------------------------------------------------------------------------
|
||||
|
|
|
@ -1249,6 +1249,24 @@ bar {
|
|||
}
|
||||
--------------------------------------------------------------
|
||||
|
||||
=== Custom separator symbol
|
||||
|
||||
Specifies a custom symbol to be used for the separator as opposed to the vertical,
|
||||
one pixel thick separator. Note that you may have to adjust the +sep_block_width+
|
||||
property.
|
||||
|
||||
*Syntax*:
|
||||
-------------------------
|
||||
separator_symbol <symbol>
|
||||
-------------------------
|
||||
|
||||
*Example*:
|
||||
------------------------
|
||||
bar {
|
||||
separator_symbol ":|:"
|
||||
}
|
||||
------------------------
|
||||
|
||||
=== Workspace buttons
|
||||
|
||||
Specifies whether workspace buttons should be shown or not. This is useful if
|
||||
|
@ -2011,6 +2029,27 @@ bindsym $mod+minus scratchpad show
|
|||
bindsym mod4+s [title="^Sup ::"] scratchpad show
|
||||
------------------------------------------------
|
||||
|
||||
=== Nop
|
||||
|
||||
There is a no operation command +nop+ which allows you to override default
|
||||
behavior. This can be useful for, e.g., disabling a focus change on clicks with
|
||||
the middle mouse button.
|
||||
|
||||
The optional +comment+ argument is ignored, but will be printed to the log file
|
||||
for debugging purposes.
|
||||
|
||||
*Syntax*:
|
||||
---------------
|
||||
nop [<comment>]
|
||||
---------------
|
||||
|
||||
*Example*:
|
||||
----------------------------------------------
|
||||
# Disable focus change for clicks on titlebars
|
||||
# with the middle mouse button
|
||||
bindsym button2 nop
|
||||
----------------------------------------------
|
||||
|
||||
=== i3bar control
|
||||
|
||||
There are two options in the configuration of each i3bar instance that can be
|
||||
|
|
|
@ -67,6 +67,11 @@
|
|||
#include "xcb.h"
|
||||
#include "libi3.h"
|
||||
|
||||
#define row_y(row) \
|
||||
(((row)-1) * font.height + logical_px(4))
|
||||
#define window_height() \
|
||||
(row_y(15) + font.height)
|
||||
|
||||
enum { STEP_WELCOME,
|
||||
STEP_GENERATE } current_step = STEP_WELCOME;
|
||||
enum { MOD_Mod1,
|
||||
|
@ -80,6 +85,7 @@ xcb_screen_t *root_screen;
|
|||
static xcb_get_modifier_mapping_reply_t *modmap_reply;
|
||||
static i3Font font;
|
||||
static i3Font bold_font;
|
||||
static int char_width;
|
||||
static char *socket_path;
|
||||
static xcb_window_t win;
|
||||
static xcb_pixmap_t pixmap;
|
||||
|
@ -493,70 +499,70 @@ static char *resolve_tilde(const char *path) {
|
|||
*/
|
||||
static int handle_expose() {
|
||||
/* re-draw the background */
|
||||
xcb_rectangle_t border = {0, 0, 300, (15 * font.height) + 8};
|
||||
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]) {get_colorpixel("#000000")});
|
||||
xcb_rectangle_t border = {0, 0, logical_px(300), window_height()};
|
||||
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){get_colorpixel("#000000")});
|
||||
xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &border);
|
||||
|
||||
set_font(&font);
|
||||
|
||||
#define txt(x, row, text) \
|
||||
draw_text_ascii(text, pixmap, pixmap_gc, \
|
||||
x, (row - 1) * font.height + 4, 300 - x * 2)
|
||||
x, row_y(row), logical_px(500) - x * 2)
|
||||
|
||||
if (current_step == STEP_WELCOME) {
|
||||
/* restore font color */
|
||||
set_font_colors(pixmap_gc, get_colorpixel("#FFFFFF"), get_colorpixel("#000000"));
|
||||
|
||||
txt(10, 2, "You have not configured i3 yet.");
|
||||
txt(10, 3, "Do you want me to generate ~/.i3/config?");
|
||||
txt(85, 5, "Yes, generate ~/.i3/config");
|
||||
txt(85, 7, "No, I will use the defaults");
|
||||
txt(logical_px(10), 2, "You have not configured i3 yet.");
|
||||
txt(logical_px(10), 3, "Do you want me to generate ~/.i3/config?");
|
||||
txt(logical_px(85), 5, "Yes, generate ~/.i3/config");
|
||||
txt(logical_px(85), 7, "No, I will use the defaults");
|
||||
|
||||
/* green */
|
||||
set_font_colors(pixmap_gc, get_colorpixel("#00FF00"), get_colorpixel("#000000"));
|
||||
txt(25, 5, "<Enter>");
|
||||
txt(logical_px(25), 5, "<Enter>");
|
||||
|
||||
/* red */
|
||||
set_font_colors(pixmap_gc, get_colorpixel("#FF0000"), get_colorpixel("#000000"));
|
||||
txt(31, 7, "<ESC>");
|
||||
txt(logical_px(31), 7, "<ESC>");
|
||||
}
|
||||
|
||||
if (current_step == STEP_GENERATE) {
|
||||
set_font_colors(pixmap_gc, get_colorpixel("#FFFFFF"), get_colorpixel("#000000"));
|
||||
|
||||
txt(10, 2, "Please choose either:");
|
||||
txt(85, 4, "Win as default modifier");
|
||||
txt(85, 5, "Alt as default modifier");
|
||||
txt(10, 7, "Afterwards, press");
|
||||
txt(85, 9, "to write ~/.i3/config");
|
||||
txt(85, 10, "to abort");
|
||||
txt(logical_px(10), 2, "Please choose either:");
|
||||
txt(logical_px(85), 4, "Win as default modifier");
|
||||
txt(logical_px(85), 5, "Alt as default modifier");
|
||||
txt(logical_px(10), 7, "Afterwards, press");
|
||||
txt(logical_px(85), 9, "to write ~/.i3/config");
|
||||
txt(logical_px(85), 10, "to abort");
|
||||
|
||||
/* the not-selected modifier */
|
||||
if (modifier == MOD_Mod4)
|
||||
txt(31, 5, "<Alt>");
|
||||
txt(logical_px(31), 5, "<Alt>");
|
||||
else
|
||||
txt(31, 4, "<Win>");
|
||||
txt(logical_px(31), 4, "<Win>");
|
||||
|
||||
/* the selected modifier */
|
||||
set_font(&bold_font);
|
||||
set_font_colors(pixmap_gc, get_colorpixel("#FFFFFF"), get_colorpixel("#000000"));
|
||||
if (modifier == MOD_Mod4)
|
||||
txt(10, 4, "-> <Win>");
|
||||
txt(logical_px(10), 4, "-> <Win>");
|
||||
else
|
||||
txt(10, 5, "-> <Alt>");
|
||||
txt(logical_px(10), 5, "-> <Alt>");
|
||||
|
||||
/* green */
|
||||
set_font(&font);
|
||||
set_font_colors(pixmap_gc, get_colorpixel("#00FF00"), get_colorpixel("#000000"));
|
||||
txt(25, 9, "<Enter>");
|
||||
txt(logical_px(25), 9, "<Enter>");
|
||||
|
||||
/* red */
|
||||
set_font_colors(pixmap_gc, get_colorpixel("#FF0000"), get_colorpixel("#000000"));
|
||||
txt(31, 10, "<ESC>");
|
||||
txt(logical_px(31), 10, "<ESC>");
|
||||
}
|
||||
|
||||
/* Copy the contents of the pixmap to the real window */
|
||||
xcb_copy_area(conn, pixmap, win, pixmap_gc, 0, 0, 0, 0, /* */ 500, 500);
|
||||
xcb_copy_area(conn, pixmap, win, pixmap_gc, 0, 0, 0, 0, logical_px(500), logical_px(500));
|
||||
xcb_flush(conn);
|
||||
|
||||
return 1;
|
||||
|
@ -592,6 +598,12 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press
|
|||
finish();
|
||||
}
|
||||
|
||||
/* Swap between modifiers when up or down is pressed. */
|
||||
if (sym == XK_Up || sym == XK_Down) {
|
||||
modifier = (modifier == MOD_Mod1) ? MOD_Mod4 : MOD_Mod1;
|
||||
handle_expose();
|
||||
}
|
||||
|
||||
/* cancel any time */
|
||||
if (sym == XK_Escape)
|
||||
exit(0);
|
||||
|
@ -637,14 +649,16 @@ static void handle_button_press(xcb_button_press_event_t *event) {
|
|||
if (current_step != STEP_GENERATE)
|
||||
return;
|
||||
|
||||
if (event->event_x >= 32 && event->event_x <= 68 &&
|
||||
event->event_y >= 45 && event->event_y <= 54) {
|
||||
if (event->event_x < logical_px(32) ||
|
||||
event->event_x > (logical_px(32) + char_width * 5))
|
||||
return;
|
||||
|
||||
if (event->event_y >= row_y(4) && event->event_y <= (row_y(4) + font.height)) {
|
||||
modifier = MOD_Mod4;
|
||||
handle_expose();
|
||||
}
|
||||
|
||||
if (event->event_x >= 32 && event->event_x <= 68 &&
|
||||
event->event_y >= 56 && event->event_y <= 70) {
|
||||
if (event->event_y >= row_y(5) && event->event_y <= (row_y(5) + font.height)) {
|
||||
modifier = MOD_Mod1;
|
||||
handle_expose();
|
||||
}
|
||||
|
@ -760,8 +774,8 @@ static void finish() {
|
|||
int main(int argc, char *argv[]) {
|
||||
config_path = resolve_tilde("~/.i3/config");
|
||||
socket_path = getenv("I3SOCK");
|
||||
char *pattern = "-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1";
|
||||
char *patternbold = "-misc-fixed-bold-r-normal--13-120-75-75-C-70-iso10646-1";
|
||||
char *pattern = "pango:monospace 8";
|
||||
char *patternbold = "pango:monospace bold 8";
|
||||
int o, option_index = 0;
|
||||
|
||||
static struct option long_options[] = {
|
||||
|
@ -858,6 +872,10 @@ int main(int argc, char *argv[]) {
|
|||
font = load_font(pattern, true);
|
||||
bold_font = load_font(patternbold, true);
|
||||
|
||||
/* Determine character width in the default font. */
|
||||
set_font(&font);
|
||||
char_width = predict_text_width(i3string_from_utf8("a"));
|
||||
|
||||
/* Open an input window */
|
||||
win = xcb_generate_id(conn);
|
||||
xcb_create_window(
|
||||
|
@ -865,12 +883,12 @@ int main(int argc, char *argv[]) {
|
|||
XCB_COPY_FROM_PARENT,
|
||||
win, /* the window id */
|
||||
root, /* parent == root */
|
||||
490, 297, 300, 205, /* dimensions */
|
||||
logical_px(490), logical_px(297), logical_px(300), window_height(), /* dimensions */
|
||||
0, /* X11 border = 0, we draw our own */
|
||||
XCB_WINDOW_CLASS_INPUT_OUTPUT,
|
||||
XCB_WINDOW_CLASS_COPY_FROM_PARENT, /* copy visual from parent */
|
||||
XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK,
|
||||
(uint32_t[]) {
|
||||
(uint32_t[]){
|
||||
0, /* back pixel: black */
|
||||
XCB_EVENT_MASK_EXPOSURE |
|
||||
XCB_EVENT_MASK_BUTTON_PRESS});
|
||||
|
@ -914,7 +932,7 @@ int main(int argc, char *argv[]) {
|
|||
/* Create pixmap */
|
||||
pixmap = xcb_generate_id(conn);
|
||||
pixmap_gc = xcb_generate_id(conn);
|
||||
xcb_create_pixmap(conn, root_screen->root_depth, pixmap, win, 500, 500);
|
||||
xcb_create_pixmap(conn, root_screen->root_depth, pixmap, win, logical_px(500), logical_px(500));
|
||||
xcb_create_gc(conn, pixmap_gc, pixmap, 0, 0);
|
||||
|
||||
/* Grab the keyboard to get all input */
|
||||
|
|
|
@ -306,7 +306,7 @@ for my $app (keys %apps) {
|
|||
# };
|
||||
|
||||
# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
# ┃ Run dmenu to ask the user for her choice ┃
|
||||
# ┃ Run dmenu to ask the user for their choice ┃
|
||||
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||
|
||||
# open2 will just make dmenu’s STDERR go to our own STDERR.
|
||||
|
|
|
@ -43,8 +43,7 @@ static int check_for_wrap(void) {
|
|||
* of the log. */
|
||||
wrap_count = header->wrap_count;
|
||||
const int len = (logbuffer + header->offset_last_wrap) - walk;
|
||||
if (write(STDOUT_FILENO, walk, len) != len)
|
||||
err(EXIT_FAILURE, "write()");
|
||||
swrite(STDOUT_FILENO, walk, len);
|
||||
walk = logbuffer + sizeof(i3_shmlog_header);
|
||||
return 1;
|
||||
}
|
||||
|
@ -52,12 +51,8 @@ static int check_for_wrap(void) {
|
|||
static void print_till_end(void) {
|
||||
check_for_wrap();
|
||||
const int len = (logbuffer + header->offset_next_write) - walk;
|
||||
const int n = write(STDOUT_FILENO, walk, len);
|
||||
if (len != n)
|
||||
err(EXIT_FAILURE, "write()");
|
||||
if (n > 0) {
|
||||
walk += n;
|
||||
}
|
||||
swrite(STDOUT_FILENO, walk, len);
|
||||
walk += len;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
|
|
@ -129,10 +129,11 @@ static int handle_expose(void *data, xcb_connection_t *conn, xcb_expose_event_t
|
|||
printf("expose!\n");
|
||||
|
||||
/* re-draw the background */
|
||||
xcb_rectangle_t border = {0, 0, 500, font.height + 8}, inner = {2, 2, 496, font.height + 8 - 4};
|
||||
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]) {get_colorpixel("#FF0000")});
|
||||
xcb_rectangle_t border = {0, 0, logical_px(500), font.height + logical_px(8)},
|
||||
inner = {logical_px(2), logical_px(2), logical_px(496), font.height + logical_px(8) - logical_px(4)};
|
||||
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){get_colorpixel("#FF0000")});
|
||||
xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &border);
|
||||
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]) {get_colorpixel("#000000")});
|
||||
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){get_colorpixel("#000000")});
|
||||
xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &inner);
|
||||
|
||||
/* restore font color */
|
||||
|
@ -140,17 +141,17 @@ static int handle_expose(void *data, xcb_connection_t *conn, xcb_expose_event_t
|
|||
|
||||
/* draw the prompt … */
|
||||
if (prompt != NULL) {
|
||||
draw_text(prompt, pixmap, pixmap_gc, 4, 4, 492);
|
||||
draw_text(prompt, pixmap, pixmap_gc, logical_px(4), logical_px(4), logical_px(492));
|
||||
}
|
||||
/* … and the text */
|
||||
if (input_position > 0) {
|
||||
i3String *input = i3string_from_ucs2(glyphs_ucs, input_position);
|
||||
draw_text(input, pixmap, pixmap_gc, prompt_offset + 4, 4, 492);
|
||||
draw_text(input, pixmap, pixmap_gc, prompt_offset + logical_px(4), logical_px(4), logical_px(492));
|
||||
i3string_free(input);
|
||||
}
|
||||
|
||||
/* Copy the contents of the pixmap to the real window */
|
||||
xcb_copy_area(conn, pixmap, win, pixmap_gc, 0, 0, 0, 0, /* */ 500, font.height + 8);
|
||||
xcb_copy_area(conn, pixmap, win, pixmap_gc, 0, 0, 0, 0, logical_px(500), font.height + logical_px(8));
|
||||
xcb_flush(conn);
|
||||
|
||||
return 1;
|
||||
|
@ -234,6 +235,9 @@ static void finish_input() {
|
|||
static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press_event_t *event) {
|
||||
printf("Keypress %d, state raw = %d\n", event->detail, event->state);
|
||||
|
||||
// TODO: port the input handling code from i3lock once libxkbcommon ≥ 0.5.0
|
||||
// is available in distros.
|
||||
|
||||
/* See the documentation of xcb_key_symbols_get_keysym for this one.
|
||||
* Basically: We get either col 0 or col 1, depending on whether shift is
|
||||
* pressed. */
|
||||
|
@ -313,7 +317,7 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press
|
|||
int main(int argc, char *argv[]) {
|
||||
format = strdup("%s");
|
||||
socket_path = getenv("I3SOCK");
|
||||
char *pattern = sstrdup("-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1");
|
||||
char *pattern = sstrdup("pango:monospace 8");
|
||||
int o, option_index = 0;
|
||||
|
||||
static struct option long_options[] = {
|
||||
|
@ -405,12 +409,12 @@ int main(int argc, char *argv[]) {
|
|||
XCB_COPY_FROM_PARENT,
|
||||
win, /* the window id */
|
||||
root, /* parent == root */
|
||||
50, 50, 500, font.height + 8, /* dimensions */
|
||||
logical_px(50), logical_px(50), logical_px(500), font.height + logical_px(8), /* dimensions */
|
||||
0, /* X11 border = 0, we draw our own */
|
||||
XCB_WINDOW_CLASS_INPUT_OUTPUT,
|
||||
XCB_WINDOW_CLASS_COPY_FROM_PARENT, /* copy visual from parent */
|
||||
XCB_CW_BACK_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK,
|
||||
(uint32_t[]) {
|
||||
(uint32_t[]){
|
||||
0, /* back pixel: black */
|
||||
1, /* override redirect: don’t manage this window */
|
||||
XCB_EVENT_MASK_EXPOSURE});
|
||||
|
@ -421,7 +425,7 @@ int main(int argc, char *argv[]) {
|
|||
/* Create pixmap */
|
||||
pixmap = xcb_generate_id(conn);
|
||||
pixmap_gc = xcb_generate_id(conn);
|
||||
xcb_create_pixmap(conn, root_screen->root_depth, pixmap, win, 500, font.height + 8);
|
||||
xcb_create_pixmap(conn, root_screen->root_depth, pixmap, win, logical_px(500), font.height + logical_px(8));
|
||||
xcb_create_gc(conn, pixmap_gc, pixmap, 0, 0);
|
||||
|
||||
/* Set input focus (we have override_redirect=1, so the wm will not do
|
||||
|
|
|
@ -341,7 +341,7 @@ sub convert_command {
|
|||
# NOTE: This is not 100% accurate, as it only works for one level
|
||||
# of nested containers. As this is a common use case, we use 'focus
|
||||
# parent; $command' nevertheless. For advanced use cases, the user
|
||||
# has to modify his config.
|
||||
# has to modify their config.
|
||||
print "$statement $key focus parent; $command\n";
|
||||
}
|
||||
return;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* © 2009-2013 Michael Stapelberg and contributors (see also: LICENSE)
|
||||
*
|
||||
* i3-nagbar is a utility which displays a nag message, for example in the case
|
||||
* when the user has an error in his configuration file.
|
||||
* when the user has an error in their configuration file.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
|
@ -131,7 +131,7 @@ static void handle_button_release(xcb_connection_t *conn, xcb_button_release_eve
|
|||
printf("button released on x = %d, y = %d\n",
|
||||
event->event_x, event->event_y);
|
||||
/* If the user hits the close button, we exit(0) */
|
||||
if (event->event_x >= (rect.width - 32))
|
||||
if (event->event_x >= (rect.width - logical_px(32)))
|
||||
exit(0);
|
||||
button_t *button = get_button_at(event->event_x, event->event_y);
|
||||
if (!button)
|
||||
|
@ -164,7 +164,9 @@ static void handle_button_release(xcb_connection_t *conn, xcb_button_release_eve
|
|||
char *link_path;
|
||||
char *exe_path = get_exe_path(argv0);
|
||||
sasprintf(&link_path, "%s.nagbar_cmd", script_path);
|
||||
symlink(exe_path, link_path);
|
||||
if (symlink(exe_path, link_path) == -1) {
|
||||
err(EXIT_FAILURE, "Failed to symlink %s to %s", link_path, exe_path);
|
||||
}
|
||||
|
||||
char *terminal_cmd;
|
||||
sasprintf(&terminal_cmd, "i3-sensible-terminal -e %s", link_path);
|
||||
|
@ -188,21 +190,23 @@ static void handle_button_release(xcb_connection_t *conn, xcb_button_release_eve
|
|||
*/
|
||||
static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) {
|
||||
/* re-draw the background */
|
||||
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]) {color_background});
|
||||
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){color_background});
|
||||
xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &rect);
|
||||
|
||||
/* restore font color */
|
||||
set_font_colors(pixmap_gc, color_text, color_background);
|
||||
draw_text(prompt, pixmap, pixmap_gc,
|
||||
4 + 4, 4 + 4, rect.width - 4 - 4);
|
||||
logical_px(4) + logical_px(4),
|
||||
logical_px(4) + logical_px(4),
|
||||
rect.width - logical_px(4) - logical_px(4));
|
||||
|
||||
/* render close button */
|
||||
const char *close_button_label = "X";
|
||||
int line_width = 4;
|
||||
int line_width = logical_px(4);
|
||||
/* set width to the width of the label */
|
||||
int w = predict_text_width(i3string_from_utf8(close_button_label));
|
||||
/* account for left/right padding, which seems to be set to 8px (total) below */
|
||||
w += 8;
|
||||
w += logical_px(8);
|
||||
int y = rect.width;
|
||||
uint32_t values[3];
|
||||
values[0] = color_button_background;
|
||||
|
@ -212,23 +216,25 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) {
|
|||
xcb_rectangle_t close = {y - w - (2 * line_width), 0, w + (2 * line_width), rect.height};
|
||||
xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &close);
|
||||
|
||||
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]) {color_border});
|
||||
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){color_border});
|
||||
xcb_point_t points[] = {
|
||||
{y - w - (2 * line_width), line_width / 2},
|
||||
{y - (line_width / 2), line_width / 2},
|
||||
{y - (line_width / 2), (rect.height - (line_width / 2)) - 2},
|
||||
{y - w - (2 * line_width), (rect.height - (line_width / 2)) - 2},
|
||||
{y - (line_width / 2), (rect.height - (line_width / 2)) - logical_px(2)},
|
||||
{y - w - (2 * line_width), (rect.height - (line_width / 2)) - logical_px(2)},
|
||||
{y - w - (2 * line_width), line_width / 2}};
|
||||
xcb_poly_line(conn, XCB_COORD_MODE_ORIGIN, pixmap, pixmap_gc, 5, points);
|
||||
|
||||
values[0] = 1;
|
||||
set_font_colors(pixmap_gc, color_text, color_button_background);
|
||||
/* the x term here seems to set left/right padding */
|
||||
draw_text_ascii(close_button_label, pixmap, pixmap_gc, y - w - line_width + w / 2 - 4,
|
||||
4 + 4 - 1, rect.width - y + w + line_width - w / 2 + 4);
|
||||
draw_text_ascii(close_button_label, pixmap, pixmap_gc,
|
||||
y - w - line_width + w / 2 - logical_px(4),
|
||||
logical_px(4) + logical_px(3),
|
||||
rect.width - y + w + line_width - w / 2 + logical_px(4));
|
||||
y -= w;
|
||||
|
||||
y -= 20;
|
||||
y -= logical_px(20);
|
||||
|
||||
/* render custom buttons */
|
||||
line_width = 1;
|
||||
|
@ -236,21 +242,21 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) {
|
|||
/* set w to the width of the label */
|
||||
w = predict_text_width(buttons[c].label);
|
||||
/* account for left/right padding, which seems to be set to 12px (total) below */
|
||||
w += 12;
|
||||
y -= 30;
|
||||
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]) {color_button_background});
|
||||
close = (xcb_rectangle_t) {y - w - (2 * line_width), 2, w + (2 * line_width), rect.height - 6};
|
||||
w += logical_px(12);
|
||||
y -= logical_px(30);
|
||||
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){color_button_background});
|
||||
close = (xcb_rectangle_t){y - w - (2 * line_width), logical_px(2), w + (2 * line_width), rect.height - logical_px(6)};
|
||||
xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &close);
|
||||
|
||||
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]) {color_border});
|
||||
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){color_border});
|
||||
buttons[c].x = y - w - (2 * line_width);
|
||||
buttons[c].width = w;
|
||||
xcb_point_t points2[] = {
|
||||
{y - w - (2 * line_width), (line_width / 2) + 2},
|
||||
{y - (line_width / 2), (line_width / 2) + 2},
|
||||
{y - (line_width / 2), (rect.height - 4 - (line_width / 2))},
|
||||
{y - w - (2 * line_width), (rect.height - 4 - (line_width / 2))},
|
||||
{y - w - (2 * line_width), (line_width / 2) + 2}};
|
||||
{y - w - (2 * line_width), (line_width / 2) + logical_px(2)},
|
||||
{y - (line_width / 2), (line_width / 2) + logical_px(2)},
|
||||
{y - (line_width / 2), (rect.height - logical_px(4) - (line_width / 2))},
|
||||
{y - w - (2 * line_width), (rect.height - logical_px(4) - (line_width / 2))},
|
||||
{y - w - (2 * line_width), (line_width / 2) + logical_px(2)}};
|
||||
xcb_poly_line(conn, XCB_COORD_MODE_ORIGIN, pixmap, pixmap_gc, 5, points2);
|
||||
|
||||
values[0] = color_text;
|
||||
|
@ -258,13 +264,15 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) {
|
|||
set_font_colors(pixmap_gc, color_text, color_button_background);
|
||||
/* the x term seems to set left/right padding */
|
||||
draw_text(buttons[c].label, pixmap, pixmap_gc,
|
||||
y - w - line_width + 6, 4 + 3, rect.width - y + w + line_width - 6);
|
||||
y - w - line_width + logical_px(6),
|
||||
logical_px(4) + logical_px(3),
|
||||
rect.width - y + w + line_width - logical_px(6));
|
||||
|
||||
y -= w;
|
||||
}
|
||||
|
||||
/* border line at the bottom */
|
||||
line_width = 2;
|
||||
line_width = logical_px(2);
|
||||
values[0] = color_border_bottom;
|
||||
values[1] = line_width;
|
||||
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND | XCB_GC_LINE_WIDTH, values);
|
||||
|
@ -316,7 +324,7 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
argv0 = argv[0];
|
||||
|
||||
char *pattern = sstrdup("-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1");
|
||||
char *pattern = sstrdup("pango:monospace 8");
|
||||
int o, option_index = 0;
|
||||
enum { TYPE_ERROR = 0,
|
||||
TYPE_WARNING = 1 } bar_type = TYPE_ERROR;
|
||||
|
@ -410,12 +418,12 @@ int main(int argc, char *argv[]) {
|
|||
XCB_COPY_FROM_PARENT,
|
||||
win, /* the window id */
|
||||
root, /* parent == root */
|
||||
50, 50, 500, font.height + 8 + 8 /* 8 px padding */, /* dimensions */
|
||||
50, 50, 500, font.height + logical_px(8) + logical_px(8) /* 8 px padding */, /* dimensions */
|
||||
0, /* x11 border = 0, we draw our own */
|
||||
XCB_WINDOW_CLASS_INPUT_OUTPUT,
|
||||
XCB_WINDOW_CLASS_COPY_FROM_PARENT, /* copy visual from parent */
|
||||
XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK,
|
||||
(uint32_t[]) {
|
||||
(uint32_t[]){
|
||||
0, /* back pixel: black */
|
||||
XCB_EVENT_MASK_EXPOSURE |
|
||||
XCB_EVENT_MASK_STRUCTURE_NOTIFY |
|
||||
|
@ -465,7 +473,7 @@ int main(int argc, char *argv[]) {
|
|||
} __attribute__((__packed__)) strut_partial;
|
||||
memset(&strut_partial, 0, sizeof(strut_partial));
|
||||
|
||||
strut_partial.top = font.height + 6;
|
||||
strut_partial.top = font.height + logical_px(6);
|
||||
strut_partial.top_start_x = 0;
|
||||
strut_partial.top_end_x = 800;
|
||||
|
||||
|
@ -481,7 +489,7 @@ int main(int argc, char *argv[]) {
|
|||
/* Create pixmap */
|
||||
pixmap = xcb_generate_id(conn);
|
||||
pixmap_gc = xcb_generate_id(conn);
|
||||
xcb_create_pixmap(conn, root_screen->root_depth, pixmap, win, 500, font.height + 8);
|
||||
xcb_create_pixmap(conn, root_screen->root_depth, pixmap, win, 500, font.height + logical_px(8));
|
||||
xcb_create_gc(conn, pixmap_gc, pixmap, 0, 0);
|
||||
|
||||
/* Grab the keyboard to get all input */
|
||||
|
@ -512,7 +520,7 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
case XCB_CONFIGURE_NOTIFY: {
|
||||
xcb_configure_notify_event_t *configure_notify = (xcb_configure_notify_event_t *)event;
|
||||
rect = (xcb_rectangle_t) {
|
||||
rect = (xcb_rectangle_t){
|
||||
configure_notify->x,
|
||||
configure_notify->y,
|
||||
configure_notify->width,
|
||||
|
|
31
i3-save-tree
31
i3-save-tree
|
@ -13,6 +13,7 @@ use POSIX qw(locale_h);
|
|||
use File::Find;
|
||||
use File::Basename qw(basename);
|
||||
use File::Temp qw(tempfile);
|
||||
use List::Util qw(first);
|
||||
use Getopt::Long;
|
||||
use Pod::Usage;
|
||||
use AnyEvent::I3;
|
||||
|
@ -41,11 +42,7 @@ my $result = GetOptions(
|
|||
|
||||
die "Could not parse command line options" unless $result;
|
||||
|
||||
if (!defined($workspace) && !defined($output)) {
|
||||
die "One of --workspace or --output need to be specified";
|
||||
}
|
||||
|
||||
unless (defined($workspace) ^ defined($output)) {
|
||||
if (defined($workspace) && defined($output)) {
|
||||
die "Only one of --workspace or --output can be specified";
|
||||
}
|
||||
|
||||
|
@ -57,6 +54,15 @@ if (!$i3->connect->recv) {
|
|||
die "Could not connect to i3";
|
||||
}
|
||||
|
||||
sub get_current_workspace {
|
||||
my $current = first { $_->{focused} } @{$i3->get_workspaces->recv};
|
||||
return $current->{name};
|
||||
}
|
||||
|
||||
if (!defined($workspace) && !defined($output)) {
|
||||
$workspace = get_current_workspace();
|
||||
}
|
||||
|
||||
sub filter_containers {
|
||||
my ($tree, $pred) = @_;
|
||||
|
||||
|
@ -216,7 +222,7 @@ my $tree = $i3->get_tree->recv;
|
|||
my $dump;
|
||||
if (defined($workspace)) {
|
||||
$dump = filter_containers($tree, sub {
|
||||
$_->{type} eq 'workspace' && $_->{name} eq $workspace
|
||||
$_->{type} eq 'workspace' && ($_->{name} eq $workspace || ($workspace =~ /^\d+$/ && $_->{num} eq $workspace))
|
||||
});
|
||||
} else {
|
||||
$dump = filter_containers($tree, sub {
|
||||
|
@ -246,7 +252,7 @@ for my $key (qw(nodes floating_nodes)) {
|
|||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
i3-save-tree [--workspace=name] [--output=name]
|
||||
i3-save-tree [--workspace=name|number] [--output=name]
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
|
@ -259,19 +265,20 @@ specification. When a window is mapped (made visible on the screen) that
|
|||
matches the specification, i3 will put it into that place and kill the
|
||||
placeholder.
|
||||
|
||||
If neither argument is specified, the currently focused workspace will be used.
|
||||
|
||||
=head1 OPTIONS
|
||||
|
||||
=over
|
||||
|
||||
=item B<--workspace=name>
|
||||
=item B<--workspace=name|number>
|
||||
|
||||
Specifies the workspace that should be dumped, e.g. 1. Either this or --output
|
||||
need to be specified.
|
||||
Specifies the workspace that should be dumped, e.g. 1. This can either be a
|
||||
name or the number of a workspace.
|
||||
|
||||
=item B<--output=name>
|
||||
|
||||
Specifies the output that should be dumped, e.g. LVDS-1. Either this or
|
||||
--workspace need to be specified.
|
||||
Specifies the output that should be dumped, e.g. LVDS-1.
|
||||
|
||||
=back
|
||||
|
||||
|
|
|
@ -13,3 +13,5 @@ for terminal in $TERMINAL x-terminal-emulator urxvt rxvt terminator Eterm aterm
|
|||
exec $terminal "$@"
|
||||
fi
|
||||
done
|
||||
|
||||
i3-nagbar -m 'i3-sensible-terminal could not find a terminal emulator. Please install one.'
|
||||
|
|
|
@ -11,9 +11,12 @@
|
|||
|
||||
# Font for window titles. Will also be used by the bar unless a different font
|
||||
# is used in the bar {} block below.
|
||||
font pango:monospace 8
|
||||
|
||||
# This font is widely installed, provides lots of unicode glyphs, right-to-left
|
||||
# text rendering and scalability on retina/hidpi displays (thanks to pango).
|
||||
font pango:DejaVu Sans Mono 8
|
||||
#font pango:DejaVu Sans Mono 8
|
||||
|
||||
# Before i3 v4.8, we used to recommend this one as the default:
|
||||
# font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
|
||||
# The font above is very space-efficient, that is, it looks good, sharp and
|
||||
|
@ -166,7 +169,7 @@ bar {
|
|||
|
||||
#######################################################################
|
||||
# automatically start i3-config-wizard to offer the user to create a
|
||||
# keysym-based config which used his favorite modifier (alt or windows)
|
||||
# keysym-based config which used their favorite modifier (alt or windows)
|
||||
#
|
||||
# i3-config-wizard will not launch if there already is a config file
|
||||
# in ~/.i3/config.
|
||||
|
|
|
@ -12,9 +12,12 @@ set $mod Mod1
|
|||
|
||||
# Font for window titles. Will also be used by the bar unless a different font
|
||||
# is used in the bar {} block below.
|
||||
font pango:monospace 8
|
||||
|
||||
# This font is widely installed, provides lots of unicode glyphs, right-to-left
|
||||
# text rendering and scalability on retina/hidpi displays (thanks to pango).
|
||||
font pango:DejaVu Sans Mono 8
|
||||
#font pango:DejaVu Sans Mono 8
|
||||
|
||||
# Before i3 v4.8, we used to recommend this one as the default:
|
||||
# font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
|
||||
# The font above is very space-efficient, that is, it looks good, sharp and
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* i3bar - an xcb-based status- and ws-bar for i3
|
||||
* © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
|
||||
*
|
||||
* child.c: Getting Input for the statusline
|
||||
* child.c: Getting input for the statusline
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
@ -41,7 +41,7 @@ typedef struct {
|
|||
} i3bar_child;
|
||||
|
||||
/*
|
||||
* Start a child-process with the specified command and reroute stdin.
|
||||
* Start a child process with the specified command and reroute stdin.
|
||||
* We actually start a $SHELL to execute the command so we don't have to care
|
||||
* about arguments and such
|
||||
*
|
||||
|
@ -49,26 +49,26 @@ typedef struct {
|
|||
void start_child(char *command);
|
||||
|
||||
/*
|
||||
* kill()s the child-process (if any). Called when exit()ing.
|
||||
* kill()s the child process (if any). Called when exit()ing.
|
||||
*
|
||||
*/
|
||||
void kill_child_at_exit(void);
|
||||
|
||||
/*
|
||||
* kill()s the child-process (if any) and closes and
|
||||
* free()s the stdin- and sigchild-watchers
|
||||
* kill()s the child process (if any) and closes and
|
||||
* free()s the stdin- and SIGCHLD-watchers
|
||||
*
|
||||
*/
|
||||
void kill_child(void);
|
||||
|
||||
/*
|
||||
* Sends a SIGSTOP to the child-process (if existent)
|
||||
* Sends a SIGSTOP to the child process (if existent)
|
||||
*
|
||||
*/
|
||||
void stop_child(void);
|
||||
|
||||
/*
|
||||
* Sends a SIGCONT to the child-process (if existent)
|
||||
* Sends a SIGCONT to the child process (if existent)
|
||||
*
|
||||
*/
|
||||
void cont_child(void);
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
typedef struct rect_t rect;
|
||||
|
||||
struct ev_loop *main_loop;
|
||||
char *statusline;
|
||||
char *statusline_buffer;
|
||||
|
||||
struct rect_t {
|
||||
int x;
|
||||
|
@ -37,13 +35,21 @@ typedef enum {
|
|||
* up one status line. */
|
||||
struct status_block {
|
||||
i3String *full_text;
|
||||
i3String *short_text;
|
||||
|
||||
char *color;
|
||||
|
||||
/* min_width can be specified either as a numeric value (in pixels) or as a
|
||||
* string. For strings, we set min_width to the measured text width of
|
||||
* min_width_str. */
|
||||
uint32_t min_width;
|
||||
char *min_width_str;
|
||||
|
||||
blockalign_t align;
|
||||
|
||||
bool urgent;
|
||||
bool no_separator;
|
||||
bool is_markup;
|
||||
|
||||
/* The amount of pixels necessary to render a separater after the block. */
|
||||
uint32_t sep_block_width;
|
||||
|
|
|
@ -35,6 +35,7 @@ typedef struct config_t {
|
|||
char *bar_id;
|
||||
char *command;
|
||||
char *fontname;
|
||||
i3String *separator_symbol;
|
||||
char *tray_output;
|
||||
int num_outputs;
|
||||
char **outputs;
|
||||
|
@ -49,7 +50,7 @@ typedef struct config_t {
|
|||
config_t config;
|
||||
|
||||
/**
|
||||
* Start parsing the received bar configuration json-string
|
||||
* Start parsing the received bar configuration JSON string
|
||||
*
|
||||
*/
|
||||
void parse_config_json(char *json);
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
/*
|
||||
* Initiate a connection to i3.
|
||||
* socket-path must be a valid path to the ipc_socket of i3
|
||||
* socket_path must be a valid path to the ipc_socket of i3
|
||||
*
|
||||
*/
|
||||
int init_connection(const char *socket_path);
|
||||
|
@ -25,7 +25,7 @@ int init_connection(const char *socket_path);
|
|||
void destroy_connection(void);
|
||||
|
||||
/*
|
||||
* Sends a Message to i3.
|
||||
* Sends a message to i3.
|
||||
* type must be a valid I3_IPC_MESSAGE_TYPE (see i3/ipc.h for further information)
|
||||
*
|
||||
*/
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* i3bar - an xcb-based status- and ws-bar for i3
|
||||
* © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
|
||||
*
|
||||
* mode.c: Handle mode-event and show current binding mode in the bar
|
||||
* mode.c: Handle "mode" event and show current binding mode in the bar
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
@ -22,7 +22,7 @@ struct mode {
|
|||
typedef struct mode mode;
|
||||
|
||||
/*
|
||||
* Start parsing the received json-string
|
||||
* Start parsing the received JSON string
|
||||
*
|
||||
*/
|
||||
void parse_mode_json(char *json);
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* i3bar - an xcb-based status- and ws-bar for i3
|
||||
* © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
|
||||
*
|
||||
* outputs.c: Maintaining the output-list
|
||||
* outputs.c: Maintaining the outputs list
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
@ -19,13 +19,13 @@ SLIST_HEAD(outputs_head, i3_output);
|
|||
struct outputs_head* outputs;
|
||||
|
||||
/*
|
||||
* Start parsing the received json-string
|
||||
* Start parsing the received JSON string
|
||||
*
|
||||
*/
|
||||
void parse_outputs_json(char* json);
|
||||
|
||||
/*
|
||||
* Initiate the output-list
|
||||
* Initiate the outputs list
|
||||
*
|
||||
*/
|
||||
void init_outputs(void);
|
||||
|
@ -42,7 +42,7 @@ struct i3_output {
|
|||
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) */
|
||||
rect rect; /* The rect (relative to the root window) */
|
||||
|
||||
xcb_window_t bar; /* The id of the bar of the output */
|
||||
xcb_pixmap_t buffer; /* An extra pixmap for double-buffering */
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
} \
|
||||
} while (0)
|
||||
|
||||
/* Securely fee single-linked list */
|
||||
/* Securely free single-linked list */
|
||||
#define FREE_SLIST(l, type) \
|
||||
do { \
|
||||
type *walk = SLIST_FIRST(l); \
|
||||
|
@ -37,7 +37,7 @@
|
|||
} \
|
||||
} while (0)
|
||||
|
||||
/* Securely fee tail-queues */
|
||||
/* Securely free tail queue */
|
||||
#define FREE_TAILQ(l, type) \
|
||||
do { \
|
||||
type *walk = TAILQ_FIRST(l); \
|
||||
|
@ -51,7 +51,7 @@
|
|||
#if defined(DLOG)
|
||||
#undef DLOG
|
||||
#endif
|
||||
/* Use cool logging-macros */
|
||||
/* Use cool logging macros */
|
||||
#define DLOG(fmt, ...) \
|
||||
do { \
|
||||
if (config.verbose) { \
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* i3bar - an xcb-based status- and ws-bar for i3
|
||||
* © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
|
||||
*
|
||||
* workspaces.c: Maintaining the workspace-lists
|
||||
* workspaces.c: Maintaining the workspace lists
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
@ -18,13 +18,13 @@ typedef struct i3_ws i3_ws;
|
|||
TAILQ_HEAD(ws_head, i3_ws);
|
||||
|
||||
/*
|
||||
* Start parsing the received json-string
|
||||
* Start parsing the received JSON string
|
||||
*
|
||||
*/
|
||||
void parse_workspaces_json(char *json);
|
||||
|
||||
/*
|
||||
* free() all workspace data-structures
|
||||
* free() all workspace data structures
|
||||
*
|
||||
*/
|
||||
void free_workspaces(void);
|
||||
|
@ -36,7 +36,7 @@ struct i3_ws {
|
|||
int name_width; /* The rendered width of the name */
|
||||
bool visible; /* If the ws is currently visible on an output */
|
||||
bool focused; /* If the ws is currently focused */
|
||||
bool urgent; /* If the urgent-hint of the ws is set */
|
||||
bool urgent; /* If the urgent hint of the ws is set */
|
||||
rect rect; /* The rect of the ws (not used (yet)) */
|
||||
struct i3_output *output; /* The current output of the ws */
|
||||
|
||||
|
|
|
@ -65,14 +65,14 @@ void init_xcb_late(char *fontname);
|
|||
void init_colors(const struct xcb_color_strings_t *colors);
|
||||
|
||||
/*
|
||||
* Cleanup the xcb-stuff.
|
||||
* Cleanup the xcb stuff.
|
||||
* Called once, before the program terminates.
|
||||
*
|
||||
*/
|
||||
void clean_xcb(void);
|
||||
|
||||
/*
|
||||
* Get the earlier requested atoms and save them in the prepared data-structure
|
||||
* Get the earlier requested atoms and save them in the prepared data structure
|
||||
*
|
||||
*/
|
||||
void get_atoms(void);
|
||||
|
@ -90,7 +90,7 @@ void kick_tray_clients(i3_output *output);
|
|||
|
||||
/*
|
||||
* We need to set the _NET_SYSTEM_TRAY_COLORS atom on the tray selection window
|
||||
* to make GTK+ 3 applets with Symbolic Icons visible. If the colors are unset,
|
||||
* to make GTK+ 3 applets with symbolic icons visible. If the colors are unset,
|
||||
* they assume a light background.
|
||||
* See also https://bugzilla.gnome.org/show_bug.cgi?id=679591
|
||||
*
|
||||
|
@ -104,7 +104,7 @@ void init_tray_colors(void);
|
|||
void destroy_window(i3_output *output);
|
||||
|
||||
/*
|
||||
* Reallocate the statusline-buffer
|
||||
* Reallocate the statusline buffer
|
||||
*
|
||||
*/
|
||||
void realloc_sl_buffer(void);
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* i3bar - an xcb-based status- and ws-bar for i3
|
||||
* © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
|
||||
*
|
||||
* child.c: Getting Input for the statusline
|
||||
* child.c: Getting input for the statusline
|
||||
*
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
|
@ -30,7 +30,7 @@
|
|||
/* Global variables for child_*() */
|
||||
i3bar_child child;
|
||||
|
||||
/* stdin- and sigchild-watchers */
|
||||
/* stdin- and SIGCHLD-watchers */
|
||||
ev_io *stdin_io;
|
||||
ev_child *child_sig;
|
||||
|
||||
|
@ -54,26 +54,43 @@ typedef struct parser_ctx {
|
|||
|
||||
parser_ctx parser_context;
|
||||
|
||||
/* The buffer statusline points to */
|
||||
struct statusline_head statusline_head = TAILQ_HEAD_INITIALIZER(statusline_head);
|
||||
char *statusline_buffer = NULL;
|
||||
/* Used temporarily while reading a statusline */
|
||||
struct statusline_head statusline_buffer = TAILQ_HEAD_INITIALIZER(statusline_buffer);
|
||||
|
||||
int child_stdin;
|
||||
|
||||
/*
|
||||
* Clears all blocks from the statusline structure in memory and frees their
|
||||
* associated resources.
|
||||
* Remove all blocks from the given statusline.
|
||||
* If free_resources is set, the fields of each status block will be free'd.
|
||||
*/
|
||||
static void clear_status_blocks() {
|
||||
static void clear_statusline(struct statusline_head *head, bool free_resources) {
|
||||
struct status_block *first;
|
||||
while (!TAILQ_EMPTY(&statusline_head)) {
|
||||
first = TAILQ_FIRST(&statusline_head);
|
||||
while (!TAILQ_EMPTY(head)) {
|
||||
first = TAILQ_FIRST(head);
|
||||
if (free_resources) {
|
||||
I3STRING_FREE(first->full_text);
|
||||
TAILQ_REMOVE(&statusline_head, first, blocks);
|
||||
I3STRING_FREE(first->short_text);
|
||||
FREE(first->color);
|
||||
FREE(first->name);
|
||||
FREE(first->instance);
|
||||
FREE(first->min_width_str);
|
||||
}
|
||||
|
||||
TAILQ_REMOVE(head, first, blocks);
|
||||
free(first);
|
||||
}
|
||||
}
|
||||
|
||||
static void copy_statusline(struct statusline_head *from, struct statusline_head *to) {
|
||||
struct status_block *current;
|
||||
TAILQ_FOREACH(current, from, blocks) {
|
||||
struct status_block *new_block = smalloc(sizeof(struct status_block));
|
||||
memcpy(new_block, current, sizeof(struct status_block));
|
||||
TAILQ_INSERT_TAIL(to, new_block, blocks);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Replaces the statusline in memory with an error message. Pass a format
|
||||
* string and format parameters as you would in `printf'. The next time
|
||||
|
@ -81,12 +98,12 @@ static void clear_status_blocks() {
|
|||
* the space allocated for the statusline.
|
||||
*/
|
||||
__attribute__((format(printf, 1, 2))) static void set_statusline_error(const char *format, ...) {
|
||||
clear_status_blocks();
|
||||
clear_statusline(&statusline_head, true);
|
||||
|
||||
char *message;
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vasprintf(&message, format, args);
|
||||
(void)vasprintf(&message, format, args);
|
||||
|
||||
struct status_block *err_block = scalloc(sizeof(struct status_block));
|
||||
err_block->full_text = i3string_from_utf8("Error: ");
|
||||
|
@ -108,16 +125,13 @@ __attribute__((format(printf, 1, 2))) static void set_statusline_error(const cha
|
|||
}
|
||||
|
||||
/*
|
||||
* Stop and free() the stdin- and sigchild-watchers
|
||||
* Stop and free() the stdin- and SIGCHLD-watchers
|
||||
*
|
||||
*/
|
||||
void cleanup(void) {
|
||||
if (stdin_io != NULL) {
|
||||
ev_io_stop(main_loop, stdin_io);
|
||||
FREE(stdin_io);
|
||||
FREE(statusline_buffer);
|
||||
/* statusline pointed to memory within statusline_buffer */
|
||||
statusline = NULL;
|
||||
}
|
||||
|
||||
if (child_sig != NULL) {
|
||||
|
@ -130,20 +144,12 @@ void cleanup(void) {
|
|||
|
||||
/*
|
||||
* The start of a new array is the start of a new status line, so we clear all
|
||||
* previous entries.
|
||||
*
|
||||
* previous entries from the buffer.
|
||||
*/
|
||||
static int stdin_start_array(void *context) {
|
||||
struct status_block *first;
|
||||
while (!TAILQ_EMPTY(&statusline_head)) {
|
||||
first = TAILQ_FIRST(&statusline_head);
|
||||
I3STRING_FREE(first->full_text);
|
||||
FREE(first->color);
|
||||
FREE(first->name);
|
||||
FREE(first->instance);
|
||||
TAILQ_REMOVE(&statusline_head, first, blocks);
|
||||
free(first);
|
||||
}
|
||||
// the blocks are still used by statusline_head, so we won't free the
|
||||
// resources here.
|
||||
clear_statusline(&statusline_buffer, false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -172,10 +178,13 @@ static int stdin_boolean(void *context, int val) {
|
|||
parser_ctx *ctx = context;
|
||||
if (strcasecmp(ctx->last_map_key, "urgent") == 0) {
|
||||
ctx->block.urgent = val;
|
||||
return 1;
|
||||
}
|
||||
if (strcasecmp(ctx->last_map_key, "separator") == 0) {
|
||||
ctx->block.no_separator = !val;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -183,9 +192,19 @@ 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_markup_with_length((const char *)val, len);
|
||||
return 1;
|
||||
}
|
||||
if (strcasecmp(ctx->last_map_key, "short_text") == 0) {
|
||||
ctx->block.short_text = i3string_from_markup_with_length((const char *)val, len);
|
||||
return 1;
|
||||
}
|
||||
if (strcasecmp(ctx->last_map_key, "color") == 0) {
|
||||
sasprintf(&(ctx->block.color), "%.*s", len, val);
|
||||
return 1;
|
||||
}
|
||||
if (strcasecmp(ctx->last_map_key, "markup") == 0) {
|
||||
ctx->block.is_markup = (len == strlen("pango") && !strncasecmp((const char *)val, "pango", strlen("pango")));
|
||||
return 1;
|
||||
}
|
||||
if (strcasecmp(ctx->last_map_key, "align") == 0) {
|
||||
if (len == strlen("center") && !strncmp((const char *)val, "center", strlen("center"))) {
|
||||
|
@ -195,23 +214,30 @@ static int stdin_string(void *context, const unsigned char *val, size_t len) {
|
|||
} else {
|
||||
ctx->block.align = ALIGN_LEFT;
|
||||
}
|
||||
} else if (strcasecmp(ctx->last_map_key, "min_width") == 0) {
|
||||
i3String *text = i3string_from_markup_with_length((const char *)val, len);
|
||||
ctx->block.min_width = (uint32_t)predict_text_width(text);
|
||||
i3string_free(text);
|
||||
return 1;
|
||||
}
|
||||
if (strcasecmp(ctx->last_map_key, "min_width") == 0) {
|
||||
char *copy = (char *)malloc(len + 1);
|
||||
strncpy(copy, (const char *)val, len);
|
||||
copy[len] = 0;
|
||||
ctx->block.min_width_str = copy;
|
||||
return 1;
|
||||
}
|
||||
if (strcasecmp(ctx->last_map_key, "name") == 0) {
|
||||
char *copy = (char *)malloc(len + 1);
|
||||
strncpy(copy, (const char *)val, len);
|
||||
copy[len] = 0;
|
||||
ctx->block.name = copy;
|
||||
return 1;
|
||||
}
|
||||
if (strcasecmp(ctx->last_map_key, "instance") == 0) {
|
||||
char *copy = (char *)malloc(len + 1);
|
||||
strncpy(copy, (const char *)val, len);
|
||||
copy[len] = 0;
|
||||
ctx->block.instance = copy;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -219,13 +245,20 @@ static int stdin_integer(void *context, long long val) {
|
|||
parser_ctx *ctx = context;
|
||||
if (strcasecmp(ctx->last_map_key, "min_width") == 0) {
|
||||
ctx->block.min_width = (uint32_t)val;
|
||||
return 1;
|
||||
}
|
||||
if (strcasecmp(ctx->last_map_key, "separator_block_width") == 0) {
|
||||
ctx->block.sep_block_width = (uint32_t)val;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* When a map is finished, we have an entire status block.
|
||||
* Move it from the parser's context to the statusline buffer.
|
||||
*/
|
||||
static int stdin_end_map(void *context) {
|
||||
parser_ctx *ctx = context;
|
||||
struct status_block *new_block = smalloc(sizeof(struct status_block));
|
||||
|
@ -236,15 +269,37 @@ static int stdin_end_map(void *context) {
|
|||
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);
|
||||
|
||||
if (new_block->min_width_str) {
|
||||
i3String *text = i3string_from_utf8(new_block->min_width_str);
|
||||
i3string_set_markup(text, new_block->is_markup);
|
||||
new_block->min_width = (uint32_t)predict_text_width(text);
|
||||
i3string_free(text);
|
||||
}
|
||||
|
||||
i3string_set_markup(new_block->full_text, new_block->is_markup);
|
||||
|
||||
if (new_block->short_text != NULL)
|
||||
i3string_set_markup(new_block->short_text, new_block->is_markup);
|
||||
|
||||
TAILQ_INSERT_TAIL(&statusline_buffer, new_block, blocks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* When an array is finished, we have an entire statusline.
|
||||
* Copy it from the buffer to the actual statusline.
|
||||
*/
|
||||
static int stdin_end_array(void *context) {
|
||||
DLOG("copying statusline_buffer to statusline_head\n");
|
||||
clear_statusline(&statusline_head, true);
|
||||
copy_statusline(&statusline_buffer, &statusline_head);
|
||||
|
||||
DLOG("dumping statusline:\n");
|
||||
struct status_block *current;
|
||||
TAILQ_FOREACH(current, &statusline_head, blocks) {
|
||||
DLOG("full_text = %s\n", i3string_as_utf8(current->full_text));
|
||||
DLOG("short_text = %s\n", (current->short_text == NULL ? NULL : i3string_as_utf8(current->short_text)));
|
||||
DLOG("color = %s\n", current->color);
|
||||
}
|
||||
DLOG("end of dump\n");
|
||||
|
@ -386,8 +441,8 @@ void stdin_io_first_line_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
|
|||
}
|
||||
|
||||
/*
|
||||
* We received a sigchild, meaning, that the child-process terminated.
|
||||
* We simply free the respective data-structures and don't care for input
|
||||
* We received a SIGCHLD, meaning, that the child process terminated.
|
||||
* We simply free the respective data structures and don't care for input
|
||||
* anymore
|
||||
*
|
||||
*/
|
||||
|
@ -415,16 +470,27 @@ void child_write_output(void) {
|
|||
if (child.click_events) {
|
||||
const unsigned char *output;
|
||||
size_t size;
|
||||
ssize_t n;
|
||||
|
||||
yajl_gen_get_buf(gen, &output, &size);
|
||||
write(child_stdin, output, size);
|
||||
write(child_stdin, "\n", 1);
|
||||
|
||||
n = writeall(child_stdin, output, size);
|
||||
if (n != -1)
|
||||
n = writeall(child_stdin, "\n", 1);
|
||||
|
||||
yajl_gen_clear(gen);
|
||||
|
||||
if (n == -1) {
|
||||
child.click_events = false;
|
||||
kill_child();
|
||||
set_statusline_error("child_write_output failed");
|
||||
draw_bars(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Start a child-process with the specified command and reroute stdin.
|
||||
* Start a child process with the specified command and reroute stdin.
|
||||
* We actually start a $SHELL to execute the command so we don't have to care
|
||||
* about arguments and such.
|
||||
*
|
||||
|
@ -552,7 +618,7 @@ void send_block_clicked(int button, const char *name, const char *instance, int
|
|||
}
|
||||
|
||||
/*
|
||||
* kill()s the child-process (if any). Called when exit()ing.
|
||||
* kill()s the child process (if any). Called when exit()ing.
|
||||
*
|
||||
*/
|
||||
void kill_child_at_exit(void) {
|
||||
|
@ -564,8 +630,8 @@ void kill_child_at_exit(void) {
|
|||
}
|
||||
|
||||
/*
|
||||
* kill()s the child-process (if existent) and closes and
|
||||
* free()s the stdin- and sigchild-watchers
|
||||
* kill()s the child process (if existent) and closes and
|
||||
* free()s the stdin- and SIGCHLD-watchers
|
||||
*
|
||||
*/
|
||||
void kill_child(void) {
|
||||
|
@ -580,7 +646,7 @@ void kill_child(void) {
|
|||
}
|
||||
|
||||
/*
|
||||
* Sends a SIGSTOP to the child-process (if existent)
|
||||
* Sends a SIGSTOP to the child process (if existent)
|
||||
*
|
||||
*/
|
||||
void stop_child(void) {
|
||||
|
@ -591,7 +657,7 @@ void stop_child(void) {
|
|||
}
|
||||
|
||||
/*
|
||||
* Sends a SIGCONT to the child-process (if existent)
|
||||
* Sends a SIGCONT to the child process (if existent)
|
||||
*
|
||||
*/
|
||||
void cont_child(void) {
|
||||
|
|
|
@ -38,7 +38,7 @@ static int config_map_key_cb(void *params_, const unsigned char *keyVal, size_t
|
|||
}
|
||||
|
||||
/*
|
||||
* Parse a null-value (current_workspace)
|
||||
* Parse a null value (current_workspace)
|
||||
*
|
||||
*/
|
||||
static int config_null_cb(void *params_) {
|
||||
|
@ -144,6 +144,13 @@ static int config_string_cb(void *params_, const unsigned char *val, size_t _len
|
|||
return 1;
|
||||
}
|
||||
|
||||
if (!strcmp(cur_key, "separator_symbol")) {
|
||||
DLOG("separator = %.*s\n", len, val);
|
||||
I3STRING_FREE(config.separator_symbol);
|
||||
config.separator_symbol = i3string_from_utf8_with_length((const char *)val, len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!strcmp(cur_key, "outputs")) {
|
||||
DLOG("+output %.*s\n", len, val);
|
||||
int new_num_outputs = config.num_outputs + 1;
|
||||
|
@ -231,7 +238,7 @@ static yajl_callbacks outputs_callbacks = {
|
|||
};
|
||||
|
||||
/*
|
||||
* Start parsing the received bar configuration json-string
|
||||
* Start parsing the received bar configuration JSON string
|
||||
*
|
||||
*/
|
||||
void parse_config_json(char *json) {
|
||||
|
@ -241,13 +248,13 @@ void parse_config_json(char *json) {
|
|||
|
||||
state = yajl_parse(handle, (const unsigned char *)json, strlen(json));
|
||||
|
||||
/* FIXME: Proper errorhandling for JSON-parsing */
|
||||
/* FIXME: Proper error handling for JSON parsing */
|
||||
switch (state) {
|
||||
case yajl_status_ok:
|
||||
break;
|
||||
case yajl_status_client_canceled:
|
||||
case yajl_status_error:
|
||||
ELOG("Could not parse config-reply!\n");
|
||||
ELOG("Could not parse config reply!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -32,15 +32,15 @@ typedef void (*handler_t)(char *);
|
|||
*
|
||||
*/
|
||||
void got_command_reply(char *reply) {
|
||||
/* TODO: Error handling for command-replies */
|
||||
/* TODO: Error handling for command replies */
|
||||
}
|
||||
|
||||
/*
|
||||
* Called, when we get a reply with workspaces-data
|
||||
* Called, when we get a reply with workspaces data
|
||||
*
|
||||
*/
|
||||
void got_workspace_reply(char *reply) {
|
||||
DLOG("Got Workspace-Data!\n");
|
||||
DLOG("Got workspace data!\n");
|
||||
parse_workspaces_json(reply);
|
||||
draw_bars(false);
|
||||
}
|
||||
|
@ -51,18 +51,18 @@ void got_workspace_reply(char *reply) {
|
|||
*
|
||||
*/
|
||||
void got_subscribe_reply(char *reply) {
|
||||
DLOG("Got Subscribe Reply: %s\n", reply);
|
||||
/* TODO: Error handling for subscribe-commands */
|
||||
DLOG("Got subscribe reply: %s\n", reply);
|
||||
/* TODO: Error handling for subscribe commands */
|
||||
}
|
||||
|
||||
/*
|
||||
* Called, when we get a reply with outputs-data
|
||||
* Called, when we get a reply with outputs data
|
||||
*
|
||||
*/
|
||||
void got_output_reply(char *reply) {
|
||||
DLOG("Parsing Outputs-JSON...\n");
|
||||
DLOG("Parsing outputs JSON...\n");
|
||||
parse_outputs_json(reply);
|
||||
DLOG("Reconfiguring Windows...\n");
|
||||
DLOG("Reconfiguring windows...\n");
|
||||
realloc_sl_buffer();
|
||||
reconfig_windows(false);
|
||||
|
||||
|
@ -80,7 +80,7 @@ void got_output_reply(char *reply) {
|
|||
*/
|
||||
void got_bar_config(char *reply) {
|
||||
DLOG("Received bar config \"%s\"\n", reply);
|
||||
/* We initiate the main-function by requesting infos about the outputs and
|
||||
/* We initiate the main function by requesting infos about the outputs and
|
||||
* workspaces. Everything else (creating the bars, showing the right workspace-
|
||||
* buttons and more) is taken care of by the event-drivenness of the code */
|
||||
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_OUTPUTS, NULL);
|
||||
|
@ -104,7 +104,7 @@ void got_bar_config(char *reply) {
|
|||
FREE(config.command);
|
||||
}
|
||||
|
||||
/* Data-structure to easily call the reply-handlers later */
|
||||
/* Data structure to easily call the reply handlers later */
|
||||
handler_t reply_handlers[] = {
|
||||
&got_command_reply,
|
||||
&got_workspace_reply,
|
||||
|
@ -116,20 +116,20 @@ handler_t reply_handlers[] = {
|
|||
};
|
||||
|
||||
/*
|
||||
* Called, when a workspace-event arrives (i.e. the user changed the workspace)
|
||||
* Called, when a workspace event arrives (i.e. the user changed the workspace)
|
||||
*
|
||||
*/
|
||||
void got_workspace_event(char *event) {
|
||||
DLOG("Got Workspace Event!\n");
|
||||
DLOG("Got workspace event!\n");
|
||||
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called, when an output-event arrives (i.e. the screen-configuration changed)
|
||||
* Called, when an output event arrives (i.e. the screen configuration changed)
|
||||
*
|
||||
*/
|
||||
void got_output_event(char *event) {
|
||||
DLOG("Got Output Event!\n");
|
||||
DLOG("Got output event!\n");
|
||||
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_OUTPUTS, NULL);
|
||||
if (!config.disable_ws) {
|
||||
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL);
|
||||
|
@ -137,11 +137,11 @@ void got_output_event(char *event) {
|
|||
}
|
||||
|
||||
/*
|
||||
* Called, when a mode-event arrives (i3 changed binding mode).
|
||||
* Called, when a mode event arrives (i3 changed binding mode).
|
||||
*
|
||||
*/
|
||||
void got_mode_event(char *event) {
|
||||
DLOG("Got Mode Event!\n");
|
||||
DLOG("Got mode event!\n");
|
||||
parse_mode_json(event);
|
||||
draw_bars(false);
|
||||
}
|
||||
|
@ -180,7 +180,7 @@ void got_bar_config_update(char *event) {
|
|||
draw_bars(false);
|
||||
}
|
||||
|
||||
/* Data-structure to easily call the event-handlers later */
|
||||
/* Data structure to easily call the event handlers later */
|
||||
handler_t event_handlers[] = {
|
||||
&got_workspace_event,
|
||||
&got_output_event,
|
||||
|
@ -201,7 +201,7 @@ void got_data(struct ev_loop *loop, ev_io *watcher, int events) {
|
|||
uint32_t header_len = strlen(I3_IPC_MAGIC) + sizeof(uint32_t) * 2;
|
||||
char *header = smalloc(header_len);
|
||||
|
||||
/* We first parse the fixed-length IPC-header, to know, how much data
|
||||
/* We first parse the fixed-length IPC header, to know, how much data
|
||||
* we have to expect */
|
||||
uint32_t rec = 0;
|
||||
while (rec < header_len) {
|
||||
|
@ -268,7 +268,7 @@ void got_data(struct ev_loop *loop, ev_io *watcher, int events) {
|
|||
}
|
||||
|
||||
/*
|
||||
* Sends a Message to i3.
|
||||
* Sends a message to i3.
|
||||
* type must be a valid I3_IPC_MESSAGE_TYPE (see i3/ipc.h for further information)
|
||||
*
|
||||
*/
|
||||
|
@ -296,18 +296,7 @@ int i3_send_msg(uint32_t type, const char *payload) {
|
|||
if (payload != NULL)
|
||||
strncpy(walk, payload, len);
|
||||
|
||||
uint32_t written = 0;
|
||||
|
||||
while (to_write > 0) {
|
||||
int n = write(i3_connection->fd, buffer + written, to_write);
|
||||
if (n == -1) {
|
||||
ELOG("write() failed: %s\n", strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
to_write -= n;
|
||||
written += n;
|
||||
}
|
||||
swrite(i3_connection->fd, buffer, to_write);
|
||||
|
||||
FREE(buffer);
|
||||
|
||||
|
@ -316,7 +305,7 @@ int i3_send_msg(uint32_t type, const char *payload) {
|
|||
|
||||
/*
|
||||
* Initiate a connection to i3.
|
||||
* socket-path must be a valid path to the ipc_socket of i3
|
||||
* socket_path must be a valid path to the ipc_socket of i3
|
||||
*
|
||||
*/
|
||||
int init_connection(const char *socket_path) {
|
||||
|
|
|
@ -60,7 +60,7 @@ void print_usage(char *elf_name) {
|
|||
printf("\n");
|
||||
printf("-b, --bar_id <bar_id>\tBar ID for which to get the configuration\n");
|
||||
printf("-s, --socket <sock_path>\tConnect to i3 via <sock_path>\n");
|
||||
printf("-h, --help Display this help-message and exit\n");
|
||||
printf("-h, --help Display this help message and exit\n");
|
||||
printf("-v, --version Display version number and exit\n");
|
||||
printf("\n");
|
||||
printf(" PLEASE NOTE that i3bar will be automatically started by i3\n"
|
||||
|
@ -71,7 +71,7 @@ void print_usage(char *elf_name) {
|
|||
|
||||
/*
|
||||
* We watch various signals, that are there to make our application stop.
|
||||
* If we get one of those, we ev_unloop() and invoke the cleanup-routines
|
||||
* If we get one of those, we ev_unloop() and invoke the cleanup routines
|
||||
* in main() with that
|
||||
*
|
||||
*/
|
||||
|
@ -140,7 +140,7 @@ int main(int argc, char **argv) {
|
|||
}
|
||||
|
||||
if (socket_path == NULL) {
|
||||
ELOG("No Socket Path Specified, default to %s\n", i3_default_sock_path);
|
||||
ELOG("No socket path specified, default to %s\n", i3_default_sock_path);
|
||||
socket_path = expand_path(i3_default_sock_path);
|
||||
}
|
||||
|
||||
|
@ -150,7 +150,7 @@ int main(int argc, char **argv) {
|
|||
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_BAR_CONFIG, config.bar_id);
|
||||
}
|
||||
|
||||
/* We listen to SIGTERM/QUIT/INT and try to exit cleanly, by stopping the main-loop.
|
||||
/* We listen to SIGTERM/QUIT/INT and try to exit cleanly, by stopping the main loop.
|
||||
* We only need those watchers on the stack, so putting them on the stack saves us
|
||||
* some calls to free() */
|
||||
ev_signal *sig_term = smalloc(sizeof(ev_signal));
|
||||
|
@ -166,13 +166,11 @@ int main(int argc, char **argv) {
|
|||
ev_signal_start(main_loop, sig_hup);
|
||||
|
||||
/* From here on everything should run smooth for itself, just start listening for
|
||||
* events. We stop simply stop the event-loop, when we are finished */
|
||||
* events. We stop simply stop the event loop, when we are finished */
|
||||
ev_loop(main_loop, 0);
|
||||
|
||||
kill_child();
|
||||
|
||||
FREE(statusline_buffer);
|
||||
|
||||
clean_xcb();
|
||||
ev_default_destroy();
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* i3bar - an xcb-based status- and ws-bar for i3
|
||||
* © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
|
||||
*
|
||||
* mode.c: Handle mode-event and show current binding mode in the bar
|
||||
* mode.c: Handle mode event and show current binding mode in the bar
|
||||
*
|
||||
*/
|
||||
#include <string.h>
|
||||
|
@ -48,7 +48,7 @@ static int mode_string_cb(void *params_, const unsigned char *val, size_t len) {
|
|||
/*
|
||||
* Parse a key.
|
||||
*
|
||||
* Essentially we just save it in the parsing-state
|
||||
* Essentially we just save it in the parsing state
|
||||
*
|
||||
*/
|
||||
static int mode_map_key_cb(void *params_, const unsigned char *keyVal, size_t keyLen) {
|
||||
|
@ -69,11 +69,11 @@ static yajl_callbacks mode_callbacks = {
|
|||
};
|
||||
|
||||
/*
|
||||
* Start parsing the received json-string
|
||||
* Start parsing the received JSON string
|
||||
*
|
||||
*/
|
||||
void parse_mode_json(char *json) {
|
||||
/* FIXME: Fasciliate stream-processing, i.e. allow starting to interpret
|
||||
/* FIXME: Fasciliate stream processing, i.e. allow starting to interpret
|
||||
* JSON in chunks */
|
||||
struct mode_json_params params;
|
||||
|
||||
|
@ -90,13 +90,13 @@ void parse_mode_json(char *json) {
|
|||
|
||||
state = yajl_parse(handle, (const unsigned char *)json, strlen(json));
|
||||
|
||||
/* FIXME: Propper errorhandling for JSON-parsing */
|
||||
/* FIXME: Propper error handling for JSON parsing */
|
||||
switch (state) {
|
||||
case yajl_status_ok:
|
||||
break;
|
||||
case yajl_status_client_canceled:
|
||||
case yajl_status_error:
|
||||
ELOG("Could not parse mode-event!\n");
|
||||
ELOG("Could not parse mode event!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* i3bar - an xcb-based status- and ws-bar for i3
|
||||
* © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
|
||||
*
|
||||
* outputs.c: Maintaining the output-list
|
||||
* outputs.c: Maintaining the outputs list
|
||||
*
|
||||
*/
|
||||
#include <string.h>
|
||||
|
@ -27,7 +27,7 @@ struct outputs_json_params {
|
|||
};
|
||||
|
||||
/*
|
||||
* Parse a null-value (current_workspace)
|
||||
* Parse a null value (current_workspace)
|
||||
*
|
||||
*/
|
||||
static int outputs_null_cb(void *params_) {
|
||||
|
@ -139,7 +139,7 @@ static int outputs_string_cb(void *params_, const unsigned char *val, size_t len
|
|||
}
|
||||
|
||||
/*
|
||||
* We hit the start of a json-map (rect or a new output)
|
||||
* We hit the start of a JSON map (rect or a new output)
|
||||
*
|
||||
*/
|
||||
static int outputs_start_map_cb(void *params_) {
|
||||
|
@ -221,7 +221,7 @@ static int outputs_end_map_cb(void *params_) {
|
|||
/*
|
||||
* Parse a key.
|
||||
*
|
||||
* Essentially we just save it in the parsing-state
|
||||
* Essentially we just save it in the parsing state
|
||||
*
|
||||
*/
|
||||
static int outputs_map_key_cb(void *params_, const unsigned char *keyVal, size_t keyLen) {
|
||||
|
@ -247,7 +247,7 @@ static yajl_callbacks outputs_callbacks = {
|
|||
};
|
||||
|
||||
/*
|
||||
* Initiate the output-list
|
||||
* Initiate the outputs list
|
||||
*
|
||||
*/
|
||||
void init_outputs(void) {
|
||||
|
@ -256,7 +256,7 @@ void init_outputs(void) {
|
|||
}
|
||||
|
||||
/*
|
||||
* Start parsing the received json-string
|
||||
* Start parsing the received JSON string
|
||||
*
|
||||
*/
|
||||
void parse_outputs_json(char *json) {
|
||||
|
@ -279,7 +279,7 @@ void parse_outputs_json(char *json) {
|
|||
break;
|
||||
case yajl_status_client_canceled:
|
||||
case yajl_status_error:
|
||||
ELOG("Could not parse outputs-reply!\n");
|
||||
ELOG("Could not parse outputs reply!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* i3bar - an xcb-based status- and ws-bar for i3
|
||||
* © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
|
||||
*
|
||||
* workspaces.c: Maintaining the workspace-lists
|
||||
* workspaces.c: Maintaining the workspace lists
|
||||
*
|
||||
*/
|
||||
#include <string.h>
|
||||
|
@ -135,7 +135,7 @@ static int workspaces_string_cb(void *params_, const unsigned char *val, size_t
|
|||
params->workspaces_walk->name_width =
|
||||
predict_text_width(params->workspaces_walk->name);
|
||||
|
||||
DLOG("Got Workspace canonical: %s, name: '%s', name_width: %d, glyphs: %zu\n",
|
||||
DLOG("Got workspace canonical: %s, name: '%s', name_width: %d, glyphs: %zu\n",
|
||||
params->workspaces_walk->canonical_name,
|
||||
i3string_as_utf8(params->workspaces_walk->name),
|
||||
params->workspaces_walk->name_width,
|
||||
|
@ -167,7 +167,7 @@ static int workspaces_string_cb(void *params_, const unsigned char *val, size_t
|
|||
}
|
||||
|
||||
/*
|
||||
* We hit the start of a json-map (rect or a new output)
|
||||
* We hit the start of a JSON map (rect or a new output)
|
||||
*
|
||||
*/
|
||||
static int workspaces_start_map_cb(void *params_) {
|
||||
|
@ -195,7 +195,7 @@ static int workspaces_start_map_cb(void *params_) {
|
|||
/*
|
||||
* Parse a key.
|
||||
*
|
||||
* Essentially we just save it in the parsing-state
|
||||
* Essentially we just save it in the parsing state
|
||||
*
|
||||
*/
|
||||
static int workspaces_map_key_cb(void *params_, const unsigned char *keyVal, size_t keyLen) {
|
||||
|
@ -219,11 +219,11 @@ static yajl_callbacks workspaces_callbacks = {
|
|||
};
|
||||
|
||||
/*
|
||||
* Start parsing the received json-string
|
||||
* Start parsing the received JSON string
|
||||
*
|
||||
*/
|
||||
void parse_workspaces_json(char *json) {
|
||||
/* FIXME: Fasciliate stream-processing, i.e. allow starting to interpret
|
||||
/* FIXME: Fasciliate stream processing, i.e. allow starting to interpret
|
||||
* JSON in chunks */
|
||||
struct workspaces_json_params params;
|
||||
|
||||
|
@ -239,13 +239,13 @@ void parse_workspaces_json(char *json) {
|
|||
|
||||
state = yajl_parse(handle, (const unsigned char *)json, strlen(json));
|
||||
|
||||
/* FIXME: Propper errorhandling for JSON-parsing */
|
||||
/* FIXME: Propper error handling for JSON parsing */
|
||||
switch (state) {
|
||||
case yajl_status_ok:
|
||||
break;
|
||||
case yajl_status_client_canceled:
|
||||
case yajl_status_error:
|
||||
ELOG("Could not parse workspaces-reply!\n");
|
||||
ELOG("Could not parse workspaces reply!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
break;
|
||||
}
|
||||
|
@ -256,7 +256,7 @@ void parse_workspaces_json(char *json) {
|
|||
}
|
||||
|
||||
/*
|
||||
* free() all workspace data-structures. Does not free() the heads of the tailqueues.
|
||||
* free() all workspace data structures. Does not free() the heads of the tailqueues.
|
||||
*
|
||||
*/
|
||||
void free_workspaces(void) {
|
||||
|
|
427
i3bar/src/xcb.c
427
i3bar/src/xcb.c
|
@ -34,7 +34,7 @@
|
|||
#include "common.h"
|
||||
#include "libi3.h"
|
||||
|
||||
/* We save the Atoms in an easy to access array, indexed by an enum */
|
||||
/* We save the atoms in an easy to access array, indexed by an enum */
|
||||
enum {
|
||||
#define ATOM_DO(name) name,
|
||||
#include "xcb_atoms.def"
|
||||
|
@ -63,6 +63,9 @@ static i3Font font;
|
|||
/* Overall height of the bar (based on font size) */
|
||||
int bar_height;
|
||||
|
||||
/* Cached width of the custom separator if one was set */
|
||||
int separator_symbol_width;
|
||||
|
||||
/* These are only relevant for XKB, which we only need for grabbing modifiers */
|
||||
int xkb_base;
|
||||
int mod_pressed = 0;
|
||||
|
@ -74,7 +77,7 @@ xcb_gcontext_t statusline_clear;
|
|||
xcb_pixmap_t statusline_pm;
|
||||
uint32_t statusline_width;
|
||||
|
||||
/* Event-Watchers, to interact with the user */
|
||||
/* Event watchers, to interact with the user */
|
||||
ev_prepare *xcb_prep;
|
||||
ev_check *xcb_chk;
|
||||
ev_io *xcb_io;
|
||||
|
@ -106,7 +109,26 @@ struct xcb_colors_t {
|
|||
};
|
||||
struct xcb_colors_t colors;
|
||||
|
||||
/* We define xcb_request_failed as a macro to include the relevant line-number */
|
||||
/* Horizontal offset between a workspace label and button borders */
|
||||
static const int ws_hoff_px = 4;
|
||||
|
||||
/* Vertical offset between a workspace label and button borders */
|
||||
static const int ws_voff_px = 3;
|
||||
|
||||
/* Offset between two workspace buttons */
|
||||
static const int ws_spacing_px = 1;
|
||||
|
||||
/* Offset between the statusline and 1) workspace buttons on the left
|
||||
* 2) the tray or screen edge on the right */
|
||||
static const int sb_hoff_px = 4;
|
||||
|
||||
/* Additional offset between the tray and the statusline, if the tray is not empty */
|
||||
static const int tray_loff_px = 2;
|
||||
|
||||
/* Vertical offset between the bar and a separator */
|
||||
static const int sep_voff_px = 4;
|
||||
|
||||
/* We define xcb_request_failed as a macro to include the relevant line number */
|
||||
#define xcb_request_failed(cookie, err_msg) _xcb_request_failed(cookie, err_msg, __LINE__)
|
||||
int _xcb_request_failed(xcb_void_cookie_t cookie, char *err_msg, int line) {
|
||||
xcb_generic_error_t *err;
|
||||
|
@ -123,11 +145,51 @@ uint32_t get_sep_offset(struct status_block *block) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int get_tray_width(struct tc_head *trayclients) {
|
||||
trayclient *trayclient;
|
||||
int tray_width = 0;
|
||||
TAILQ_FOREACH_REVERSE(trayclient, trayclients, tc_head, tailq) {
|
||||
if (!trayclient->mapped)
|
||||
continue;
|
||||
tray_width += font.height + logical_px(2);
|
||||
}
|
||||
if (tray_width > 0)
|
||||
tray_width += logical_px(tray_loff_px);
|
||||
return tray_width;
|
||||
}
|
||||
|
||||
/*
|
||||
* Draws a separator for the given block if necessary.
|
||||
*
|
||||
*/
|
||||
static void draw_separator(uint32_t x, struct status_block *block) {
|
||||
uint32_t sep_offset = get_sep_offset(block);
|
||||
if (TAILQ_NEXT(block, blocks) == NULL || sep_offset == 0)
|
||||
return;
|
||||
|
||||
uint32_t center_x = x - sep_offset;
|
||||
if (config.separator_symbol == NULL) {
|
||||
/* Draw a classic one pixel, vertical separator. */
|
||||
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[]){{center_x, logical_px(sep_voff_px)},
|
||||
{center_x, bar_height - logical_px(sep_voff_px)}});
|
||||
} else {
|
||||
/* Draw a custom separator. */
|
||||
uint32_t separator_x = MAX(x - block->sep_block_width, center_x - separator_symbol_width / 2);
|
||||
set_font_colors(statusline_ctx, colors.sep_fg, colors.bar_bg);
|
||||
draw_text(config.separator_symbol, statusline_pm, statusline_ctx,
|
||||
separator_x, logical_px(ws_voff_px), x - separator_x);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Redraws the statusline to the buffer
|
||||
*
|
||||
*/
|
||||
void refresh_statusline(void) {
|
||||
void refresh_statusline(bool use_short_text) {
|
||||
struct status_block *block;
|
||||
|
||||
uint32_t old_statusline_width = statusline_width;
|
||||
|
@ -135,6 +197,12 @@ void refresh_statusline(void) {
|
|||
|
||||
/* Predict the text width of all blocks (in pixels). */
|
||||
TAILQ_FOREACH(block, &statusline_head, blocks) {
|
||||
/* Try to use the shorter text if necessary and possible. */
|
||||
if (use_short_text && block->short_text != NULL) {
|
||||
I3STRING_FREE(block->full_text);
|
||||
block->full_text = i3string_copy(block->short_text);
|
||||
}
|
||||
|
||||
if (i3string_get_num_bytes(block->full_text) == 0)
|
||||
continue;
|
||||
|
||||
|
@ -174,7 +242,7 @@ void refresh_statusline(void) {
|
|||
realloc_sl_buffer();
|
||||
|
||||
/* Clear the statusline pixmap. */
|
||||
xcb_rectangle_t rect = {0, 0, root_screen->width_in_pixels, bar_height};
|
||||
xcb_rectangle_t rect = {0, 0, MAX(root_screen->width_in_pixels, statusline_width), bar_height};
|
||||
xcb_poly_fill_rectangle(xcb_connection, statusline_pm, statusline_clear, 1, &rect);
|
||||
|
||||
/* Draw the text of each block. */
|
||||
|
@ -192,32 +260,23 @@ void refresh_statusline(void) {
|
|||
|
||||
/* Draw the background */
|
||||
uint32_t bg_color = colors.urgent_ws_bg;
|
||||
uint32_t bg_values[] = { bg_color, bg_color };
|
||||
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_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);
|
||||
draw_text(block->full_text, statusline_pm, statusline_ctx, x + block->x_offset, logical_px(ws_voff_px), 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 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, logical_px(4) },
|
||||
{ x - sep_offset, bar_height - logical_px(4) } });
|
||||
}
|
||||
/* If this is not the last block, draw a separator. */
|
||||
draw_separator(x, block);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -273,7 +332,7 @@ void unhide_bars(void) {
|
|||
values[2] = walk->rect.w;
|
||||
values[3] = bar_height;
|
||||
values[4] = XCB_STACK_MODE_ABOVE;
|
||||
DLOG("Reconfiguring Window for output %s to %d,%d\n", walk->name, values[0], values[1]);
|
||||
DLOG("Reconfiguring window for output %s to %d,%d\n", walk->name, values[0], values[1]);
|
||||
cookie = xcb_configure_window_checked(xcb_connection,
|
||||
walk->bar,
|
||||
mask,
|
||||
|
@ -317,14 +376,12 @@ void init_colors(const struct xcb_color_strings_t *new_colors) {
|
|||
}
|
||||
|
||||
/*
|
||||
* Handle a button-press-event (i.e. a mouse click on one of our bars).
|
||||
* We determine, whether the click occured on a ws-button or if the scroll-
|
||||
* Handle a button press event (i.e. a mouse click on one of our bars).
|
||||
* We determine, whether the click occured on a workspace button or if the scroll-
|
||||
* wheel was used and change the workspace appropriately
|
||||
*
|
||||
*/
|
||||
void handle_button(xcb_button_press_event_t *event) {
|
||||
i3_ws *cur_ws;
|
||||
|
||||
/* Determine, which bar was clicked */
|
||||
i3_output *walk;
|
||||
xcb_window_t bar = event->event;
|
||||
|
@ -335,45 +392,47 @@ void handle_button(xcb_button_press_event_t *event) {
|
|||
}
|
||||
|
||||
if (walk == NULL) {
|
||||
DLOG("Unknown Bar klicked!\n");
|
||||
DLOG("Unknown bar clicked!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t x = event->event_x >= 0 ? event->event_x : 0;
|
||||
int32_t original_x = x;
|
||||
|
||||
DLOG("Got Button %d\n", event->detail);
|
||||
DLOG("Got button %d\n", event->detail);
|
||||
|
||||
if (child_want_click_events()) {
|
||||
int workspace_width = 0;
|
||||
i3_ws *cur_ws = NULL, *clicked_ws = NULL, *ws_walk;
|
||||
|
||||
TAILQ_FOREACH(ws_walk, walk->workspaces, tailq) {
|
||||
int w = 2 * logical_px(ws_hoff_px) + 2 * logical_px(1) + ws_walk->name_width;
|
||||
if (x >= workspace_width && x <= workspace_width + w)
|
||||
clicked_ws = ws_walk;
|
||||
if (ws_walk->visible)
|
||||
cur_ws = ws_walk;
|
||||
workspace_width += w;
|
||||
if (TAILQ_NEXT(ws_walk, tailq) != NULL)
|
||||
workspace_width += logical_px(ws_spacing_px);
|
||||
}
|
||||
|
||||
if (x > workspace_width && child_want_click_events()) {
|
||||
/* If the child asked for click events,
|
||||
* check if a status block has been clicked. */
|
||||
|
||||
/* First calculate width of tray area */
|
||||
trayclient *trayclient;
|
||||
int tray_width = 0;
|
||||
TAILQ_FOREACH_REVERSE(trayclient, walk->trayclients, tc_head, tailq) {
|
||||
if (!trayclient->mapped)
|
||||
continue;
|
||||
tray_width += (font.height + logical_px(2));
|
||||
}
|
||||
if (tray_width > 0)
|
||||
tray_width += logical_px(2);
|
||||
|
||||
int tray_width = get_tray_width(walk->trayclients);
|
||||
int block_x = 0, last_block_x;
|
||||
int offset = walk->rect.w - statusline_width - tray_width - logical_px(4);
|
||||
int offset = walk->rect.w - statusline_width - tray_width - logical_px(sb_hoff_px);
|
||||
|
||||
x = original_x - offset;
|
||||
if (x >= 0) {
|
||||
struct status_block *block;
|
||||
int sep_offset_remainder = 0;
|
||||
|
||||
TAILQ_FOREACH (block, &statusline_head, blocks) {
|
||||
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
|
||||
+ get_sep_offset(block) + sep_offset_remainder;
|
||||
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);
|
||||
|
@ -386,15 +445,8 @@ void handle_button(xcb_button_press_event_t *event) {
|
|||
x = original_x;
|
||||
}
|
||||
|
||||
/* TODO: Move this to extern get_ws_for_output() */
|
||||
TAILQ_FOREACH (cur_ws, walk->workspaces, tailq) {
|
||||
if (cur_ws->visible) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (cur_ws == NULL) {
|
||||
DLOG("No Workspace active?\n");
|
||||
DLOG("No workspace active?\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -436,17 +488,10 @@ void handle_button(xcb_button_press_event_t *event) {
|
|||
cur_ws = TAILQ_NEXT(cur_ws, tailq);
|
||||
break;
|
||||
case 1:
|
||||
/* Check if this event regards a workspace button */
|
||||
TAILQ_FOREACH(cur_ws, walk->workspaces, tailq) {
|
||||
DLOG("x = %d\n", x);
|
||||
if (x >= 0 && x < cur_ws->name_width + logical_px(10)) {
|
||||
break;
|
||||
}
|
||||
x -= cur_ws->name_width + logical_px(11);
|
||||
}
|
||||
cur_ws = clicked_ws;
|
||||
|
||||
/* Otherwise, focus our currently visible workspace if it is not
|
||||
* already focused */
|
||||
/* if no workspace was clicked, focus our currently visible
|
||||
* workspace if it is not already focused */
|
||||
if (cur_ws == NULL) {
|
||||
TAILQ_FOREACH(cur_ws, walk->workspaces, tailq) {
|
||||
if (cur_ws->visible && !cur_ws->focused)
|
||||
|
@ -471,7 +516,7 @@ void handle_button(xcb_button_press_event_t *event) {
|
|||
size_t namelen = 0;
|
||||
const char *utf8_name = cur_ws->canonical_name;
|
||||
for (const char *walk = utf8_name; *walk != '\0'; walk++) {
|
||||
if (*walk == '"')
|
||||
if (*walk == '"' || *walk == '\\')
|
||||
num_quotes++;
|
||||
/* While we’re looping through the name anyway, we can save one
|
||||
* strlen(). */
|
||||
|
@ -485,7 +530,7 @@ void handle_button(xcb_button_press_event_t *event) {
|
|||
for (inpos = 0, outpos = strlen("workspace \"");
|
||||
inpos < namelen;
|
||||
inpos++, outpos++) {
|
||||
if (utf8_name[inpos] == '"') {
|
||||
if (utf8_name[inpos] == '"' || utf8_name[inpos] == '\\') {
|
||||
buffer[outpos] = '\\';
|
||||
outpos++;
|
||||
}
|
||||
|
@ -498,7 +543,7 @@ void handle_button(xcb_button_press_event_t *event) {
|
|||
|
||||
/*
|
||||
* 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.
|
||||
* if windows are in fullscreen on each output, suspend the child process.
|
||||
*
|
||||
*/
|
||||
static void handle_visibility_notify(xcb_visibility_notify_event_t *event) {
|
||||
|
@ -506,7 +551,7 @@ static void handle_visibility_notify(xcb_visibility_notify_event_t *event) {
|
|||
int num_visible = 0;
|
||||
i3_output *output;
|
||||
|
||||
SLIST_FOREACH (output, outputs, slist) {
|
||||
SLIST_FOREACH(output, outputs, slist) {
|
||||
if (!output->active) {
|
||||
continue;
|
||||
}
|
||||
|
@ -863,7 +908,7 @@ static void handle_property_notify(xcb_property_notify_event_t *event) {
|
|||
DLOG("xembed version = %d\n", xembed[0]);
|
||||
DLOG("xembed flags = %d\n", xembed[1]);
|
||||
bool map_it = ((xembed[1] & XEMBED_MAPPED) == XEMBED_MAPPED);
|
||||
DLOG("map-state now %d\n", map_it);
|
||||
DLOG("map state now %d\n", map_it);
|
||||
if (trayclient->mapped && !map_it) {
|
||||
/* need to unmap the window */
|
||||
xcb_unmap_window(xcb_connection, trayclient->win);
|
||||
|
@ -1002,7 +1047,7 @@ void xcb_chk_cb(struct ev_loop *loop, ev_check *watcher, int revents) {
|
|||
redraw_bars();
|
||||
break;
|
||||
case XCB_BUTTON_PRESS:
|
||||
/* Button-press-events are mouse-buttons clicked on one of our bars */
|
||||
/* Button press events are mouse buttons clicked on one of our bars */
|
||||
handle_button((xcb_button_press_event_t *)event);
|
||||
break;
|
||||
case XCB_CLIENT_MESSAGE:
|
||||
|
@ -1035,7 +1080,7 @@ void xcb_chk_cb(struct ev_loop *loop, ev_check *watcher, int revents) {
|
|||
}
|
||||
|
||||
/*
|
||||
* Dummy Callback. We only need this, so that the Prepare- and Check-Watchers
|
||||
* Dummy callback. We only need this, so that the prepare and check watchers
|
||||
* are triggered
|
||||
*
|
||||
*/
|
||||
|
@ -1048,7 +1093,7 @@ void xcb_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
|
|||
*
|
||||
*/
|
||||
char *init_xcb_early() {
|
||||
/* FIXME: xcb_connect leaks Memory */
|
||||
/* FIXME: xcb_connect leaks memory */
|
||||
xcb_connection = xcb_connect(NULL, &screen);
|
||||
if (xcb_connection_has_error(xcb_connection)) {
|
||||
ELOG("Cannot open display\n");
|
||||
|
@ -1091,7 +1136,7 @@ char *init_xcb_early() {
|
|||
root_screen->width_in_pixels,
|
||||
root_screen->height_in_pixels);
|
||||
|
||||
/* The various Watchers to communicate with xcb */
|
||||
/* The various watchers to communicate with xcb */
|
||||
xcb_io = smalloc(sizeof(ev_io));
|
||||
xcb_prep = smalloc(sizeof(ev_prepare));
|
||||
xcb_chk = smalloc(sizeof(ev_check));
|
||||
|
@ -1109,9 +1154,9 @@ char *init_xcb_early() {
|
|||
|
||||
char *path = root_atom_contents("I3_SOCKET_PATH", xcb_connection, screen);
|
||||
|
||||
if (xcb_request_failed(sl_pm_cookie, "Could not allocate statusline-buffer") ||
|
||||
xcb_request_failed(clear_ctx_cookie, "Could not allocate statusline-buffer-clearcontext") ||
|
||||
xcb_request_failed(sl_ctx_cookie, "Could not allocate statusline-buffer-context")) {
|
||||
if (xcb_request_failed(sl_pm_cookie, "Could not allocate statusline buffer") ||
|
||||
xcb_request_failed(clear_ctx_cookie, "Could not allocate statusline buffer clearcontext") ||
|
||||
xcb_request_failed(sl_ctx_cookie, "Could not allocate statusline buffer context")) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
@ -1119,7 +1164,7 @@ char *init_xcb_early() {
|
|||
}
|
||||
|
||||
/*
|
||||
* Register for xkb keyevents. To grab modifiers without blocking other applications from receiving key-events
|
||||
* Register for xkb keyevents. To grab modifiers without blocking other applications from receiving key events
|
||||
* involving that modifier, we sadly have to use xkb which is not yet fully supported
|
||||
* in xcb.
|
||||
*
|
||||
|
@ -1171,8 +1216,11 @@ void init_xcb_late(char *fontname) {
|
|||
/* Load the font */
|
||||
font = load_font(fontname, true);
|
||||
set_font(&font);
|
||||
DLOG("Calculated Font-height: %d\n", font.height);
|
||||
bar_height = font.height + logical_px(6);
|
||||
DLOG("Calculated font height: %d\n", font.height);
|
||||
bar_height = font.height + 2 * logical_px(ws_voff_px);
|
||||
|
||||
if (config.separator_symbol)
|
||||
separator_symbol_width = predict_text_width(config.separator_symbol);
|
||||
|
||||
xcb_flush(xcb_connection);
|
||||
|
||||
|
@ -1285,7 +1333,7 @@ void init_tray(void) {
|
|||
|
||||
/*
|
||||
* We need to set the _NET_SYSTEM_TRAY_COLORS atom on the tray selection window
|
||||
* to make GTK+ 3 applets with Symbolic Icons visible. If the colors are unset,
|
||||
* to make GTK+ 3 applets with symbolic icons visible. If the colors are unset,
|
||||
* they assume a light background.
|
||||
* See also https://bugzilla.gnome.org/show_bug.cgi?id=679591
|
||||
*
|
||||
|
@ -1325,7 +1373,7 @@ void init_tray_colors(void) {
|
|||
}
|
||||
|
||||
/*
|
||||
* Cleanup the xcb-stuff.
|
||||
* Cleanup the xcb stuff.
|
||||
* Called once, before the program terminates.
|
||||
*
|
||||
*/
|
||||
|
@ -1370,7 +1418,7 @@ void get_atoms(void) {
|
|||
free(reply);
|
||||
|
||||
#include "xcb_atoms.def"
|
||||
DLOG("Got Atoms\n");
|
||||
DLOG("Got atoms\n");
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1435,11 +1483,11 @@ void destroy_window(i3_output *output) {
|
|||
}
|
||||
|
||||
/*
|
||||
* Reallocate the statusline-buffer
|
||||
* Reallocate the statusline buffer
|
||||
*
|
||||
*/
|
||||
void realloc_sl_buffer(void) {
|
||||
DLOG("Re-allocating statusline-buffer, statusline_width = %d, root_screen->width_in_pixels = %d\n",
|
||||
DLOG("Re-allocating statusline buffer, statusline_width = %d, root_screen->width_in_pixels = %d\n",
|
||||
statusline_width, root_screen->width_in_pixels);
|
||||
xcb_free_pixmap(xcb_connection, statusline_pm);
|
||||
statusline_pm = xcb_generate_id(xcb_connection);
|
||||
|
@ -1470,13 +1518,57 @@ void realloc_sl_buffer(void) {
|
|||
mask,
|
||||
vals);
|
||||
|
||||
if (xcb_request_failed(sl_pm_cookie, "Could not allocate statusline-buffer") ||
|
||||
xcb_request_failed(clear_ctx_cookie, "Could not allocate statusline-buffer-clearcontext") ||
|
||||
xcb_request_failed(sl_ctx_cookie, "Could not allocate statusline-buffer-context")) {
|
||||
if (xcb_request_failed(sl_pm_cookie, "Could not allocate statusline buffer") ||
|
||||
xcb_request_failed(clear_ctx_cookie, "Could not allocate statusline buffer clearcontext") ||
|
||||
xcb_request_failed(sl_ctx_cookie, "Could not allocate statusline buffer context")) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/* Strut partial tells i3 where to reserve space for i3bar. This is determined
|
||||
* by the `position` bar config directive. */
|
||||
xcb_void_cookie_t config_strut_partial(i3_output *output) {
|
||||
/* A local struct to save the strut_partial property */
|
||||
struct {
|
||||
uint32_t left;
|
||||
uint32_t right;
|
||||
uint32_t top;
|
||||
uint32_t bottom;
|
||||
uint32_t left_start_y;
|
||||
uint32_t left_end_y;
|
||||
uint32_t right_start_y;
|
||||
uint32_t right_end_y;
|
||||
uint32_t top_start_x;
|
||||
uint32_t top_end_x;
|
||||
uint32_t bottom_start_x;
|
||||
uint32_t bottom_end_x;
|
||||
} __attribute__((__packed__)) strut_partial;
|
||||
memset(&strut_partial, 0, sizeof(strut_partial));
|
||||
|
||||
switch (config.position) {
|
||||
case POS_NONE:
|
||||
break;
|
||||
case POS_TOP:
|
||||
strut_partial.top = bar_height;
|
||||
strut_partial.top_start_x = output->rect.x;
|
||||
strut_partial.top_end_x = output->rect.x + output->rect.w;
|
||||
break;
|
||||
case POS_BOT:
|
||||
strut_partial.bottom = bar_height;
|
||||
strut_partial.bottom_start_x = output->rect.x;
|
||||
strut_partial.bottom_end_x = output->rect.x + output->rect.w;
|
||||
break;
|
||||
}
|
||||
return xcb_change_property(xcb_connection,
|
||||
XCB_PROP_MODE_REPLACE,
|
||||
output->bar,
|
||||
atoms[_NET_WM_STRUT_PARTIAL],
|
||||
XCB_ATOM_CARDINAL,
|
||||
32,
|
||||
12,
|
||||
&strut_partial);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reconfigure all bars and create new bars for recently activated outputs
|
||||
*
|
||||
|
@ -1496,14 +1588,14 @@ void reconfig_windows(bool redraw_bars) {
|
|||
continue;
|
||||
}
|
||||
if (walk->bar == XCB_NONE) {
|
||||
DLOG("Creating Window for output %s\n", walk->name);
|
||||
DLOG("Creating window for output %s\n", walk->name);
|
||||
|
||||
walk->bar = xcb_generate_id(xcb_connection);
|
||||
walk->buffer = xcb_generate_id(xcb_connection);
|
||||
mask = XCB_CW_BACK_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK;
|
||||
/* Black background */
|
||||
values[0] = colors.bar_bg;
|
||||
/* If hide_on_modifier is set to hide or invisible mode, i3 is not supposed to manage our bar-windows */
|
||||
/* If hide_on_modifier is set to hide or invisible mode, i3 is not supposed to manage our bar windows */
|
||||
values[1] = (config.hide_on_modifier == M_DOCK ? 0 : 1);
|
||||
/* We enable the following EventMask fields:
|
||||
* EXPOSURE, to get expose events (we have to re-draw then)
|
||||
|
@ -1565,7 +1657,7 @@ void reconfig_windows(bool redraw_bars) {
|
|||
name);
|
||||
free(name);
|
||||
|
||||
/* We want dock-windows (for now). When override_redirect is set, i3 is ignoring
|
||||
/* We want dock windows (for now). When override_redirect is set, i3 is ignoring
|
||||
* this one */
|
||||
xcb_void_cookie_t dock_cookie = xcb_change_property(xcb_connection,
|
||||
XCB_PROP_MODE_REPLACE,
|
||||
|
@ -1576,51 +1668,9 @@ void reconfig_windows(bool redraw_bars) {
|
|||
1,
|
||||
(unsigned char *)&atoms[_NET_WM_WINDOW_TYPE_DOCK]);
|
||||
|
||||
/* We need to tell i3, where to reserve space for i3bar */
|
||||
/* left, right, top, bottom, left_start_y, left_end_y,
|
||||
* right_start_y, right_end_y, top_start_x, top_end_x, bottom_start_x,
|
||||
* bottom_end_x */
|
||||
/* A local struct to save the strut_partial property */
|
||||
struct {
|
||||
uint32_t left;
|
||||
uint32_t right;
|
||||
uint32_t top;
|
||||
uint32_t bottom;
|
||||
uint32_t left_start_y;
|
||||
uint32_t left_end_y;
|
||||
uint32_t right_start_y;
|
||||
uint32_t right_end_y;
|
||||
uint32_t top_start_x;
|
||||
uint32_t top_end_x;
|
||||
uint32_t bottom_start_x;
|
||||
uint32_t bottom_end_x;
|
||||
} __attribute__((__packed__)) strut_partial;
|
||||
memset(&strut_partial, 0, sizeof(strut_partial));
|
||||
xcb_void_cookie_t strut_cookie = config_strut_partial(walk);
|
||||
|
||||
switch (config.position) {
|
||||
case POS_NONE:
|
||||
break;
|
||||
case POS_TOP:
|
||||
strut_partial.top = bar_height;
|
||||
strut_partial.top_start_x = walk->rect.x;
|
||||
strut_partial.top_end_x = walk->rect.x + walk->rect.w;
|
||||
break;
|
||||
case POS_BOT:
|
||||
strut_partial.bottom = bar_height;
|
||||
strut_partial.bottom_start_x = walk->rect.x;
|
||||
strut_partial.bottom_end_x = walk->rect.x + walk->rect.w;
|
||||
break;
|
||||
}
|
||||
xcb_void_cookie_t strut_cookie = xcb_change_property(xcb_connection,
|
||||
XCB_PROP_MODE_REPLACE,
|
||||
walk->bar,
|
||||
atoms[_NET_WM_STRUT_PARTIAL],
|
||||
XCB_ATOM_CARDINAL,
|
||||
32,
|
||||
12,
|
||||
&strut_partial);
|
||||
|
||||
/* We also want a graphics-context for the bars (it defines the properties
|
||||
/* We also want a graphics context for the bars (it defines the properties
|
||||
* with which we draw to them) */
|
||||
walk->bargc = xcb_generate_id(xcb_connection);
|
||||
xcb_void_cookie_t gc_cookie = xcb_create_gc_checked(xcb_connection,
|
||||
|
@ -1670,15 +1720,21 @@ void reconfig_windows(bool redraw_bars) {
|
|||
XCB_CONFIG_WINDOW_HEIGHT |
|
||||
XCB_CONFIG_WINDOW_STACK_MODE;
|
||||
values[0] = walk->rect.x;
|
||||
if (config.position == POS_TOP)
|
||||
values[1] = walk->rect.y;
|
||||
else
|
||||
values[1] = walk->rect.y + walk->rect.h - bar_height;
|
||||
values[2] = walk->rect.w;
|
||||
values[3] = bar_height;
|
||||
values[4] = XCB_STACK_MODE_ABOVE;
|
||||
|
||||
DLOG("Reconfiguring strut partial property for output %s\n", walk->name);
|
||||
xcb_void_cookie_t strut_cookie = config_strut_partial(walk);
|
||||
|
||||
DLOG("Destroying buffer for output %s\n", walk->name);
|
||||
xcb_free_pixmap(xcb_connection, walk->buffer);
|
||||
|
||||
DLOG("Reconfiguring Window for output %s to %d,%d\n", walk->name, values[0], values[1]);
|
||||
DLOG("Reconfiguring window for output %s to %d,%d\n", walk->name, values[0], values[1]);
|
||||
xcb_void_cookie_t cfg_cookie = xcb_configure_window_checked(xcb_connection,
|
||||
walk->bar,
|
||||
mask,
|
||||
|
@ -1686,7 +1742,7 @@ void reconfig_windows(bool redraw_bars) {
|
|||
|
||||
mask = XCB_CW_OVERRIDE_REDIRECT;
|
||||
values[0] = (config.hide_on_modifier == M_DOCK ? 0 : 1);
|
||||
DLOG("Changing Window attribute override_redirect for output %s to %d\n", walk->name, values[0]);
|
||||
DLOG("Changing window attribute override_redirect for output %s to %d\n", walk->name, values[0]);
|
||||
xcb_void_cookie_t chg_cookie = xcb_change_window_attributes(xcb_connection,
|
||||
walk->bar,
|
||||
mask,
|
||||
|
@ -1723,6 +1779,7 @@ void reconfig_windows(bool redraw_bars) {
|
|||
if (xcb_request_failed(cfg_cookie, "Could not reconfigure window") ||
|
||||
xcb_request_failed(chg_cookie, "Could not change window") ||
|
||||
xcb_request_failed(pm_cookie, "Could not create pixmap") ||
|
||||
xcb_request_failed(strut_cookie, "Could not set strut") ||
|
||||
(redraw_bars && (xcb_request_failed(umap_cookie, "Could not unmap window") ||
|
||||
(config.hide_on_modifier == M_DOCK && xcb_request_failed(map_cookie, "Could not map window"))))) {
|
||||
exit(EXIT_FAILURE);
|
||||
|
@ -1736,10 +1793,10 @@ void reconfig_windows(bool redraw_bars) {
|
|||
*
|
||||
*/
|
||||
void draw_bars(bool unhide) {
|
||||
DLOG("Drawing Bars...\n");
|
||||
int i = 0;
|
||||
DLOG("Drawing bars...\n");
|
||||
int workspace_width = 0;
|
||||
|
||||
refresh_statusline();
|
||||
refresh_statusline(false);
|
||||
|
||||
i3_output *outputs_walk;
|
||||
SLIST_FOREACH(outputs_walk, outputs, slist) {
|
||||
|
@ -1764,39 +1821,11 @@ void draw_bars(bool unhide) {
|
|||
1,
|
||||
&rect);
|
||||
|
||||
if (!TAILQ_EMPTY(&statusline_head)) {
|
||||
DLOG("Printing statusline!\n");
|
||||
|
||||
/* Luckily we already prepared a seperate pixmap containing the rendered
|
||||
* statusline, we just have to copy the relevant parts to the relevant
|
||||
* position */
|
||||
trayclient *trayclient;
|
||||
int traypx = 0;
|
||||
TAILQ_FOREACH(trayclient, outputs_walk->trayclients, tailq) {
|
||||
if (!trayclient->mapped)
|
||||
continue;
|
||||
/* 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 + logical_px(2);
|
||||
}
|
||||
/* Add 2px of padding if there are any tray icons */
|
||||
if (traypx > 0)
|
||||
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 + 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) {
|
||||
i3_ws *ws_walk;
|
||||
TAILQ_FOREACH(ws_walk, outputs_walk->workspaces, tailq) {
|
||||
DLOG("Drawing Button for WS %s at x = %d, len = %d\n",
|
||||
i3string_as_utf8(ws_walk->name), i, ws_walk->name_width);
|
||||
DLOG("Drawing button for WS %s at x = %d, len = %d\n",
|
||||
i3string_as_utf8(ws_walk->name), workspace_width, ws_walk->name_width);
|
||||
uint32_t fg_color = colors.inactive_ws_fg;
|
||||
uint32_t bg_color = colors.inactive_ws_bg;
|
||||
uint32_t border_color = colors.inactive_ws_border;
|
||||
|
@ -1824,10 +1853,10 @@ void draw_bars(bool unhide) {
|
|||
outputs_walk->bargc,
|
||||
mask,
|
||||
vals_border);
|
||||
xcb_rectangle_t rect_border = {i,
|
||||
xcb_rectangle_t rect_border = {workspace_width,
|
||||
logical_px(1),
|
||||
ws_walk->name_width + logical_px(10),
|
||||
font.height + logical_px(4)};
|
||||
ws_walk->name_width + 2 * logical_px(ws_hoff_px) + 2 * logical_px(1),
|
||||
font.height + 2 * logical_px(ws_voff_px) - 2 * logical_px(1)};
|
||||
xcb_poly_fill_rectangle(xcb_connection,
|
||||
outputs_walk->buffer,
|
||||
outputs_walk->bargc,
|
||||
|
@ -1838,10 +1867,10 @@ void draw_bars(bool unhide) {
|
|||
outputs_walk->bargc,
|
||||
mask,
|
||||
vals);
|
||||
xcb_rectangle_t rect = {i + logical_px(1),
|
||||
xcb_rectangle_t rect = {workspace_width + logical_px(1),
|
||||
2 * logical_px(1),
|
||||
ws_walk->name_width + logical_px(8),
|
||||
font.height + logical_px(2)};
|
||||
ws_walk->name_width + 2 * logical_px(ws_hoff_px),
|
||||
font.height + 2 * logical_px(ws_voff_px) - 4 * logical_px(1)};
|
||||
xcb_poly_fill_rectangle(xcb_connection,
|
||||
outputs_walk->buffer,
|
||||
outputs_walk->bargc,
|
||||
|
@ -1849,12 +1878,19 @@ void draw_bars(bool unhide) {
|
|||
&rect);
|
||||
set_font_colors(outputs_walk->bargc, fg_color, bg_color);
|
||||
draw_text(ws_walk->name, outputs_walk->buffer, outputs_walk->bargc,
|
||||
i + logical_px(5), 3 * logical_px(1), ws_walk->name_width);
|
||||
i += logical_px(10) + ws_walk->name_width + logical_px(1);
|
||||
workspace_width + logical_px(ws_hoff_px) + logical_px(1),
|
||||
logical_px(ws_voff_px),
|
||||
ws_walk->name_width);
|
||||
|
||||
workspace_width += 2 * logical_px(ws_hoff_px) + 2 * logical_px(1) + ws_walk->name_width;
|
||||
if (TAILQ_NEXT(ws_walk, tailq) != NULL)
|
||||
workspace_width += logical_px(ws_spacing_px);
|
||||
}
|
||||
}
|
||||
|
||||
if (binding.name && !config.disable_binding_mode_indicator) {
|
||||
workspace_width += logical_px(ws_spacing_px);
|
||||
|
||||
uint32_t fg_color = colors.urgent_ws_fg;
|
||||
uint32_t bg_color = colors.urgent_ws_bg;
|
||||
uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND;
|
||||
|
@ -1864,7 +1900,10 @@ void draw_bars(bool unhide) {
|
|||
outputs_walk->bargc,
|
||||
mask,
|
||||
vals_border);
|
||||
xcb_rectangle_t rect_border = {i, 1, binding.width + 10, font.height + 4};
|
||||
xcb_rectangle_t rect_border = {workspace_width,
|
||||
logical_px(1),
|
||||
binding.width + 2 * logical_px(ws_hoff_px) + 2 * logical_px(1),
|
||||
font.height + 2 * logical_px(ws_voff_px) - 2 * logical_px(1)};
|
||||
xcb_poly_fill_rectangle(xcb_connection,
|
||||
outputs_walk->buffer,
|
||||
outputs_walk->bargc,
|
||||
|
@ -1876,7 +1915,10 @@ void draw_bars(bool unhide) {
|
|||
outputs_walk->bargc,
|
||||
mask,
|
||||
vals);
|
||||
xcb_rectangle_t rect = {i + 1, 2, binding.width + 8, font.height + 2};
|
||||
xcb_rectangle_t rect = {workspace_width + logical_px(1),
|
||||
2 * logical_px(1),
|
||||
binding.width + 2 * logical_px(ws_hoff_px),
|
||||
font.height + 2 * logical_px(ws_voff_px) - 4 * logical_px(1)};
|
||||
xcb_poly_fill_rectangle(xcb_connection,
|
||||
outputs_walk->buffer,
|
||||
outputs_walk->bargc,
|
||||
|
@ -1884,12 +1926,41 @@ void draw_bars(bool unhide) {
|
|||
&rect);
|
||||
|
||||
set_font_colors(outputs_walk->bargc, fg_color, bg_color);
|
||||
draw_text(binding.name, outputs_walk->buffer, outputs_walk->bargc, i + 5, 3, binding.width);
|
||||
draw_text(binding.name,
|
||||
outputs_walk->buffer,
|
||||
outputs_walk->bargc,
|
||||
workspace_width + logical_px(ws_hoff_px) + logical_px(1),
|
||||
logical_px(ws_voff_px),
|
||||
binding.width);
|
||||
|
||||
unhide = true;
|
||||
workspace_width += 2 * logical_px(ws_hoff_px) + 2 * logical_px(1) + binding.width;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
if (!TAILQ_EMPTY(&statusline_head)) {
|
||||
DLOG("Printing statusline!\n");
|
||||
|
||||
int tray_width = get_tray_width(outputs_walk->trayclients);
|
||||
uint32_t max_statusline_width = outputs_walk->rect.w - workspace_width - tray_width - 2 * logical_px(sb_hoff_px);
|
||||
|
||||
/* If the statusline is too long, try to use short texts. */
|
||||
if (statusline_width > max_statusline_width)
|
||||
refresh_statusline(true);
|
||||
|
||||
/* Luckily we already prepared a seperate pixmap containing the rendered
|
||||
* statusline, we just have to copy the relevant parts to the relevant
|
||||
* position */
|
||||
int visible_statusline_width = MIN(statusline_width, max_statusline_width);
|
||||
xcb_copy_area(xcb_connection,
|
||||
statusline_pm,
|
||||
outputs_walk->buffer,
|
||||
outputs_walk->bargc,
|
||||
(int16_t)(statusline_width - visible_statusline_width), 0,
|
||||
(int16_t)(outputs_walk->rect.w - tray_width - logical_px(sb_hoff_px) - visible_statusline_width), 0,
|
||||
(int16_t)visible_statusline_width, (int16_t)bar_height);
|
||||
}
|
||||
|
||||
workspace_width = 0;
|
||||
}
|
||||
|
||||
/* Assure the bar is hidden/unhidden according to the specified hidden_state and mode */
|
||||
|
|
|
@ -105,7 +105,7 @@ struct Config {
|
|||
|
||||
/** By default, focus follows mouse. If the user explicitly wants to
|
||||
* turn this off (and instead rely only on the keyboard for changing
|
||||
* focus), we allow him to do this with this relatively special option.
|
||||
* focus), we allow them to do this with this relatively special option.
|
||||
* It is not planned to add any different focus models. */
|
||||
bool disable_focus_follows_mouse;
|
||||
|
||||
|
@ -285,6 +285,9 @@ struct Barconfig {
|
|||
/** Font specification for all text rendered on the bar. */
|
||||
char *font;
|
||||
|
||||
/** A custom separator to use instead of a vertical line. */
|
||||
char *separator_symbol;
|
||||
|
||||
/** Hide workspace buttons? Configuration option is 'workspace_buttons no'
|
||||
* but we invert the bool to get the correct default when initializing with
|
||||
* zero. */
|
||||
|
|
|
@ -67,6 +67,7 @@ CFGFUN(enter_mode, const char *mode);
|
|||
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_separator_symbol, const char *separator);
|
||||
CFGFUN(bar_mode, const char *mode);
|
||||
CFGFUN(bar_hidden_state, const char *hidden_state);
|
||||
CFGFUN(bar_id, const char *bar_id);
|
||||
|
|
|
@ -450,7 +450,7 @@ struct Match {
|
|||
/**
|
||||
* An Assignment makes specific windows go to a specific workspace/output or
|
||||
* run a command for that window. With this mechanism, the user can -- for
|
||||
* example -- assign his browser to workspace "www". Checking if a window is
|
||||
* example -- assign their browser to workspace "www". Checking if a window is
|
||||
* assigned works by comparing the Match data structure with the window (see
|
||||
* match_matches_window()).
|
||||
*
|
||||
|
@ -460,7 +460,6 @@ struct Assignment {
|
|||
*
|
||||
* A_COMMAND = run the specified command for the matching window
|
||||
* A_TO_WORKSPACE = assign the matching window to the specified workspace
|
||||
* A_TO_OUTPUT = assign the matching window to the specified output
|
||||
*
|
||||
* While the type is a bitmask, only one value can be set at a time. It is
|
||||
* a bitmask to allow filtering for multiple types, for example in the
|
||||
|
@ -470,18 +469,16 @@ struct Assignment {
|
|||
enum {
|
||||
A_ANY = 0,
|
||||
A_COMMAND = (1 << 0),
|
||||
A_TO_WORKSPACE = (1 << 1),
|
||||
A_TO_OUTPUT = (1 << 2)
|
||||
A_TO_WORKSPACE = (1 << 1)
|
||||
} type;
|
||||
|
||||
/** the criteria to check if a window matches */
|
||||
Match match;
|
||||
|
||||
/** destination workspace/output/command, depending on the type */
|
||||
/** destination workspace/command, depending on the type */
|
||||
union {
|
||||
char *command;
|
||||
char *workspace;
|
||||
char *output;
|
||||
} dest;
|
||||
|
||||
TAILQ_ENTRY(Assignment) assignments;
|
||||
|
|
|
@ -19,7 +19,7 @@ void handle_key_press(xcb_key_press_event_t *event);
|
|||
/**
|
||||
* Kills the commanderror i3-nagbar process, if any.
|
||||
*
|
||||
* Called when reloading/restarting, since the user probably fixed his wrong
|
||||
* Called when reloading/restarting, since the user probably fixed their wrong
|
||||
* keybindings.
|
||||
*
|
||||
* If wait_for_it is set (restarting), this function will waitpid(), otherwise,
|
||||
|
|
|
@ -134,6 +134,20 @@ char *sstrdup(const char *str);
|
|||
*/
|
||||
int sasprintf(char **strp, const char *fmt, ...);
|
||||
|
||||
/**
|
||||
* Wrapper around correct write which returns -1 (meaning that
|
||||
* write failed) or count (meaning that all bytes were written)
|
||||
*
|
||||
*/
|
||||
ssize_t writeall(int fd, const void *buf, size_t count);
|
||||
|
||||
/**
|
||||
* Safe-wrapper around writeall which exits if it returns -1 (meaning that
|
||||
* write failed)
|
||||
*
|
||||
*/
|
||||
ssize_t swrite(int fd, const void *buf, size_t count);
|
||||
|
||||
/**
|
||||
* Build an i3String from an UTF-8 encoded string.
|
||||
* Returns the newly-allocated i3String.
|
||||
|
@ -169,6 +183,12 @@ i3String *i3string_from_markup_with_length(const char *from_markup, size_t num_b
|
|||
*/
|
||||
i3String *i3string_from_ucs2(const xcb_char2b_t *from_ucs2, size_t num_glyphs);
|
||||
|
||||
/**
|
||||
* Copies the given i3string.
|
||||
* Note that this will not free the source string.
|
||||
*/
|
||||
i3String *i3string_copy(i3String *str);
|
||||
|
||||
/**
|
||||
* Free an i3String.
|
||||
*
|
||||
|
@ -211,6 +231,11 @@ size_t i3string_get_num_bytes(i3String *str);
|
|||
*/
|
||||
bool i3string_is_markup(i3String *str);
|
||||
|
||||
/**
|
||||
* Set whether the i3String should use Pango markup.
|
||||
*/
|
||||
void i3string_set_markup(i3String *str, bool is_markup);
|
||||
|
||||
/**
|
||||
* Returns the number of glyphs in an i3String.
|
||||
*
|
||||
|
|
|
@ -14,3 +14,10 @@
|
|||
*
|
||||
*/
|
||||
Con *output_get_content(Con *output);
|
||||
|
||||
/**
|
||||
* Returns an 'output' corresponding to one of left/right/down/up or a specific
|
||||
* output name.
|
||||
*
|
||||
*/
|
||||
Output *get_output_from_string(Output *current_output, const char *output_str);
|
||||
|
|
|
@ -44,6 +44,12 @@ void startup_sequence_delete(struct Startup_Sequence *sequence);
|
|||
*/
|
||||
void startup_monitor_event(SnMonitorEvent *event, void *userdata);
|
||||
|
||||
/**
|
||||
* Renames workspaces that are mentioned in the startup sequences.
|
||||
*
|
||||
*/
|
||||
void startup_sequence_rename_workspace(char *old_name, char *new_name);
|
||||
|
||||
/**
|
||||
* Gets the stored startup sequence for the _NET_STARTUP_ID of a given window.
|
||||
*
|
||||
|
|
|
@ -179,3 +179,10 @@ Con *workspace_attach_to(Con *ws);
|
|||
* The container inherits the layout from the workspace.
|
||||
*/
|
||||
Con *workspace_encapsulate(Con *ws);
|
||||
|
||||
/**
|
||||
* Move the given workspace to the specified output.
|
||||
* This returns true if and only if moving the workspace was successful.
|
||||
*
|
||||
*/
|
||||
bool workspace_move_to_output(Con *ws, char *output);
|
||||
|
|
|
@ -17,6 +17,12 @@ extern xcb_screen_t *root_screen;
|
|||
*
|
||||
*/
|
||||
int logical_px(const int logical) {
|
||||
if (root_screen == NULL) {
|
||||
/* Dpi info may not be available when parsing a config without an X
|
||||
* server, such as for config file validation. */
|
||||
return 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
|
||||
|
|
|
@ -126,7 +126,10 @@ static void draw_text_pango(const char *text, size_t text_len,
|
|||
cairo_set_source_rgb(cr, pango_font_red, pango_font_green, pango_font_blue);
|
||||
pango_cairo_update_layout(cr, layout);
|
||||
pango_layout_get_pixel_size(layout, NULL, &height);
|
||||
cairo_move_to(cr, x, y - 0.5 * (height - savedFont->height));
|
||||
/* Center the piece of text vertically if its height is smaller than the
|
||||
* cached font height, and just let "high" symbols fall out otherwise. */
|
||||
int yoffset = (height < savedFont->height ? 0.5 : 1) * (height - savedFont->height);
|
||||
cairo_move_to(cr, x, y - yoffset);
|
||||
pango_cairo_show_layout(cr, layout);
|
||||
|
||||
/* Free resources */
|
||||
|
@ -455,7 +458,7 @@ static int xcb_query_text_width(const xcb_char2b_t *text, size_t text_len) {
|
|||
cookie, &error);
|
||||
if (reply == NULL) {
|
||||
/* We return a safe estimate because a rendering error is better than
|
||||
* a crash. Plus, the user will see the error in his log. */
|
||||
* a crash. Plus, the user will see the error in their log. */
|
||||
fprintf(stderr, "Could not get text extents (X error code %d)\n",
|
||||
error->error_code);
|
||||
return savedFont->specific.xcb.info->max_bounds.character_width * text_len;
|
||||
|
|
|
@ -32,33 +32,11 @@ int ipc_send_message(int sockfd, const uint32_t message_size,
|
|||
.size = message_size,
|
||||
.type = message_type};
|
||||
|
||||
size_t sent_bytes = 0;
|
||||
int n = 0;
|
||||
|
||||
/* This first loop is basically unnecessary. No operating system has
|
||||
* buffers which cannot fit 14 bytes into them, so the write() will only be
|
||||
* called once. */
|
||||
while (sent_bytes < sizeof(i3_ipc_header_t)) {
|
||||
if ((n = write(sockfd, ((void *)&header) + sent_bytes, sizeof(i3_ipc_header_t) - sent_bytes)) == -1) {
|
||||
if (errno == EAGAIN)
|
||||
continue;
|
||||
if (writeall(sockfd, ((void *)&header), sizeof(i3_ipc_header_t)) == -1)
|
||||
return -1;
|
||||
}
|
||||
|
||||
sent_bytes += n;
|
||||
}
|
||||
|
||||
sent_bytes = 0;
|
||||
|
||||
while (sent_bytes < message_size) {
|
||||
if ((n = write(sockfd, payload + sent_bytes, message_size - sent_bytes)) == -1) {
|
||||
if (errno == EAGAIN)
|
||||
continue;
|
||||
if (writeall(sockfd, payload, message_size) == -1)
|
||||
return -1;
|
||||
}
|
||||
|
||||
sent_bytes += n;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -8,8 +8,10 @@
|
|||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "libi3.h"
|
||||
|
||||
|
@ -56,3 +58,30 @@ int sasprintf(char **strp, const char *fmt, ...) {
|
|||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
ssize_t writeall(int fd, const void *buf, size_t count) {
|
||||
size_t written = 0;
|
||||
ssize_t n = 0;
|
||||
|
||||
while (written < count) {
|
||||
n = write(fd, buf + written, count - written);
|
||||
if (n == -1) {
|
||||
if (errno == EINTR || errno == EAGAIN)
|
||||
continue;
|
||||
return n;
|
||||
}
|
||||
written += (size_t)n;
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
ssize_t swrite(int fd, const void *buf, size_t count) {
|
||||
ssize_t n;
|
||||
|
||||
n = writeall(fd, buf, count);
|
||||
if (n == -1)
|
||||
err(EXIT_FAILURE, "Failed to write %d", fd);
|
||||
else
|
||||
return n;
|
||||
}
|
||||
|
|
|
@ -109,6 +109,16 @@ i3String *i3string_from_ucs2(const xcb_char2b_t *from_ucs2, size_t num_glyphs) {
|
|||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the given i3string.
|
||||
* Note that this will not free the source string.
|
||||
*/
|
||||
i3String *i3string_copy(i3String *str) {
|
||||
i3String *copy = i3string_from_utf8(i3string_as_utf8(str));
|
||||
copy->is_markup = str->is_markup;
|
||||
return copy;
|
||||
}
|
||||
|
||||
/*
|
||||
* Free an i3String.
|
||||
*
|
||||
|
@ -168,6 +178,13 @@ bool i3string_is_markup(i3String *str) {
|
|||
return str->is_markup;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set whether the i3String should use Pango markup.
|
||||
*/
|
||||
void i3string_set_markup(i3String *str, bool is_markup) {
|
||||
str->is_markup = 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.9.1</refmiscinfo>
|
||||
<refmiscinfo class="version">4.10.1</refmiscinfo>
|
||||
<refmiscinfo class="manual">i3 Manual</refmiscinfo>
|
||||
</refmeta>
|
||||
<refnamediv>
|
||||
|
|
|
@ -30,7 +30,7 @@ It tries to start one of the following (in that order):
|
|||
* gedit
|
||||
* mc-edit
|
||||
|
||||
Please don’t complain about the order: If the user has any preference, he will
|
||||
Please don’t complain about the order: If the user has any preference, they will
|
||||
have $VISUAL or $EDITOR set.
|
||||
|
||||
== SEE ALSO
|
||||
|
|
|
@ -23,7 +23,7 @@ It tries to start one of the following (in that order):
|
|||
* w3m
|
||||
* i3-sensible-editor(1)
|
||||
|
||||
Please don’t complain about the order: If the user has any preference, he will
|
||||
Please don’t complain about the order: If the user has any preference, they will
|
||||
have $PAGER set.
|
||||
|
||||
== SEE ALSO
|
||||
|
|
|
@ -33,8 +33,8 @@ It tries to start one of the following (in that order):
|
|||
* roxterm
|
||||
* xfce4-terminal
|
||||
|
||||
Please don’t complain about the order: If the user has any preference, she will
|
||||
have $TERMINAL set or modified her i3 configuration file.
|
||||
Please don’t complain about the order: If the user has any preference, they will
|
||||
have $TERMINAL set or modified their i3 configuration file.
|
||||
|
||||
== SEE ALSO
|
||||
|
||||
|
|
|
@ -353,6 +353,8 @@ state MODE:
|
|||
state NOP:
|
||||
comment = string
|
||||
-> call cmd_nop($comment)
|
||||
end
|
||||
-> call cmd_nop(NULL)
|
||||
|
||||
state SCRATCHPAD:
|
||||
'show'
|
||||
|
|
|
@ -370,6 +370,7 @@ state BAR:
|
|||
'output' -> BAR_OUTPUT
|
||||
'tray_output' -> BAR_TRAY_OUTPUT
|
||||
'font' -> BAR_FONT
|
||||
'separator_symbol' -> BAR_SEPARATOR_SYMBOL
|
||||
'binding_mode_indicator' -> BAR_BINDING_MODE_INDICATOR
|
||||
'workspace_buttons' -> BAR_WORKSPACE_BUTTONS
|
||||
'strip_workspace_numbers' -> BAR_STRIP_WORKSPACE_NUMBERS
|
||||
|
@ -435,6 +436,10 @@ state BAR_FONT:
|
|||
font = string
|
||||
-> call cfg_bar_font($font); BAR
|
||||
|
||||
state BAR_SEPARATOR_SYMBOL:
|
||||
separator = string
|
||||
-> call cfg_bar_separator_symbol($separator); BAR
|
||||
|
||||
state BAR_BINDING_MODE_INDICATOR:
|
||||
value = word
|
||||
-> call cfg_bar_binding_mode_indicator($value); BAR
|
||||
|
|
|
@ -0,0 +1,193 @@
|
|||
#!/bin/zsh
|
||||
# This script is used to prepare a new release of i3.
|
||||
|
||||
export RELEASE_VERSION="4.10.1"
|
||||
export PREVIOUS_VERSION="4.10"
|
||||
export RELEASE_BRANCH="next"
|
||||
|
||||
if [ ! -e "../i3.github.io" ]
|
||||
then
|
||||
echo "../i3.github.io does not exist."
|
||||
echo "Use git clone git://github.com/i3/i3.github.io"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -e "RELEASE-NOTES-${RELEASE_VERSION}" ]
|
||||
then
|
||||
echo "RELEASE-NOTES-${RELEASE_VERSION} not found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if git diff-files --quiet --exit-code debian/changelog
|
||||
then
|
||||
echo "Expected debian/changelog to be changed (containing the changelog for ${RELEASE_VERSION})."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
eval $(gpg-agent --daemon)
|
||||
export GPG_AGENT_INFO
|
||||
|
||||
################################################################################
|
||||
# Section 1: update git and build the release tarball
|
||||
################################################################################
|
||||
|
||||
STARTDIR=$PWD
|
||||
|
||||
TMPDIR=$(mktemp -d)
|
||||
cd $TMPDIR
|
||||
if ! wget http://i3wm.org/downloads/i3-${PREVIOUS_VERSION}.tar.bz2; then
|
||||
echo "Could not download i3-${PREVIOUS_VERSION}.tar.bz2 (required for comparing files)."
|
||||
exit 1
|
||||
fi
|
||||
git clone --quiet --branch "${RELEASE_BRANCH}" file://${STARTDIR}
|
||||
cd i3
|
||||
if [ ! -e "${STARTDIR}/RELEASE-NOTES-${RELEASE_VERSION}" ]; then
|
||||
echo "Required file RELEASE-NOTES-${RELEASE_VERSION} not found."
|
||||
exit 1
|
||||
fi
|
||||
git checkout -b release-${RELEASE_VERSION}
|
||||
cp "${STARTDIR}/RELEASE-NOTES-${RELEASE_VERSION}" "RELEASE-NOTES-${RELEASE_VERSION}"
|
||||
git add RELEASE-NOTES-${RELEASE_VERSION}
|
||||
git rm RELEASE-NOTES-${PREVIOUS_VERSION}
|
||||
sed -i "s,<refmiscinfo class=\"version\">[^<]*</refmiscinfo>,<refmiscinfo class=\"version\">${RELEASE_VERSION}</refmiscinfo>,g" man/asciidoc.conf
|
||||
git commit -a -m "release i3 ${RELEASE_VERSION}"
|
||||
git tag "${RELEASE_VERSION}" -m "release i3 ${RELEASE_VERSION}" --sign --local-user=0x4AC8EE1D
|
||||
|
||||
make dist
|
||||
|
||||
echo "Differences in the release tarball file lists:"
|
||||
|
||||
diff -u \
|
||||
<(tar tf ../i3-${PREVIOUS_VERSION}.tar.bz2 | sed "s,i3-${PREVIOUS_VERSION}/,,g" | sort) \
|
||||
<(tar tf i3-${RELEASE_VERSION}.tar.bz2 | sed "s,i3-${RELEASE_VERSION}/,,g" | sort) \
|
||||
| colordiff
|
||||
|
||||
if ! tar xf i3-${RELEASE_VERSION}.tar.bz2 --to-stdout --strip-components=1 i3-${RELEASE_VERSION}/I3_VERSION | grep -q "^${RELEASE_VERSION} "
|
||||
then
|
||||
echo "I3_VERSION file does not start with ${RELEASE_VERSION}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
gpg --armor -b i3-${RELEASE_VERSION}.tar.bz2
|
||||
|
||||
if [ "${RELEASE_BRANCH}" = "master" ]; then
|
||||
git checkout master
|
||||
git merge --no-ff release-${RELEASE_VERSION} -m "Merge branch 'release-${RELEASE_VERSION}'"
|
||||
git checkout next
|
||||
git merge --no-ff master -m "Merge branch 'master' into next"
|
||||
else
|
||||
git checkout next
|
||||
git merge --no-ff release-${RELEASE_VERSION} -m "Merge branch 'release-${RELEASE_VERSION}'"
|
||||
git checkout master
|
||||
git merge --no-ff next -m "Merge branch 'next' into master"
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
# Section 2: Debian packaging
|
||||
################################################################################
|
||||
|
||||
cd "${TMPDIR}"
|
||||
mkdir debian
|
||||
|
||||
# Copy over the changelog because we expect it to be locally modified in the
|
||||
# start directory.
|
||||
cp "${STARTDIR}/debian/changelog" i3/debian/changelog
|
||||
|
||||
cat > ${TMPDIR}/Dockerfile <<EOT
|
||||
FROM debian:sid
|
||||
RUN sed -i 's,^deb \(.*\),deb \1\ndeb-src \1,g' /etc/apt/sources.list
|
||||
RUN apt-get update && apt-get install -y dpkg-dev devscripts
|
||||
COPY i3/i3-${RELEASE_VERSION}.tar.bz2 /usr/src/i3-wm_${RELEASE_VERSION}.orig.tar.bz2
|
||||
WORKDIR /usr/src/
|
||||
RUN tar xf i3-wm_${RELEASE_VERSION}.orig.tar.bz2
|
||||
WORKDIR /usr/src/i3-${RELEASE_VERSION}
|
||||
COPY i3/debian /usr/src/i3-${RELEASE_VERSION}/debian/
|
||||
RUN mkdir debian/source
|
||||
RUN echo '3.0 (quilt)' > debian/source/format
|
||||
WORKDIR /usr/src
|
||||
RUN mk-build-deps --install --remove --tool 'apt-get --no-install-recommends -y' i3-${RELEASE_VERSION}/debian/control
|
||||
WORKDIR /usr/src/i3-${RELEASE_VERSION}
|
||||
RUN dpkg-buildpackage -sa -j8
|
||||
EOT
|
||||
|
||||
CONTAINER_NAME=$(echo "i3-${TMPDIR}" | sed 's,/,,g')
|
||||
docker build -t i3 .
|
||||
for file in $(docker run --name "${CONTAINER_NAME}" i3 /bin/sh -c "ls /usr/src/i3*_${RELEASE_VERSION}*")
|
||||
do
|
||||
docker cp "${CONTAINER_NAME}:${file}" ${TMPDIR}/debian/
|
||||
done
|
||||
|
||||
echo "Content of resulting package’s .changes file:"
|
||||
cat ${TMPDIR}/debian/*.changes
|
||||
|
||||
# debsign is in devscripts, which is available in fedora and debian
|
||||
debsign -k4AC8EE1D ${TMPDIR}/debian/*.changes
|
||||
|
||||
# TODO: docker cleanup
|
||||
|
||||
################################################################################
|
||||
# Section 3: website
|
||||
################################################################################
|
||||
|
||||
cd ${TMPDIR}
|
||||
git clone --quiet ${STARTDIR}/../i3.github.io
|
||||
cd i3.github.io
|
||||
cp ${TMPDIR}/i3/i3-${RELEASE_VERSION}.tar.bz2* downloads/
|
||||
git add downloads/i3-${RELEASE_VERSION}.tar.bz2*
|
||||
cp ${TMPDIR}/i3/RELEASE-NOTES-${RELEASE_VERSION} downloads/RELEASE-NOTES-${RELEASE_VERSION}.txt
|
||||
git add downloads/RELEASE-NOTES-${RELEASE_VERSION}.txt
|
||||
sed -i "s,<h2>Documentation for i3 v[^<]*</h2>,<h2>Documentation for i3 v${RELEASE_VERSION}</h2>,g" docs/index.html
|
||||
sed -i "s,<span style=\"margin-left: 2em; color: #c0c0c0\">[^<]*</span>,<span style=\"margin-left: 2em; color: #c0c0c0\">${RELEASE_VERSION}</span>,g" index.html
|
||||
sed -i "s,The current stable version is .*$,The current stable version is ${RELEASE_VERSION}.,g" downloads/index.html
|
||||
sed -i "s,<tbody>,<tbody>\n <tr>\n <td>${RELEASE_VERSION}</td>\n <td><a href=\"/downloads/i3-${RELEASE_VERSION}.tar.bz2\">i3-${RELEASE_VERSION}.tar.bz2</a></td>\n <td>$(ls -lh ../i3/i3-${RELEASE_VERSION}.tar.bz2 | awk -F " " {'print $5'} | sed 's/K$/ KiB/g')</td>\n <td><a href=\"/downloads/i3-${RELEASE_VERSION}.tar.bz2.asc\">signature</a></td>\n <td>$(date +'%Y-%m-%d')</td>\n <td><a href=\"/downloads/RELEASE-NOTES-${RELEASE_VERSION}.txt\">release notes</a></td>\n </tr>\n,g" downloads/index.html
|
||||
|
||||
git commit -a -m "add ${RELEASE_VERSION} release"
|
||||
|
||||
mkdir docs/${PREVIOUS_VERSION}
|
||||
tar cf - '--exclude=[0-9]\.[0-9e]*' docs | tar xf - --strip-components=1 -C docs/${PREVIOUS_VERSION}
|
||||
git add docs/${PREVIOUS_VERSION}
|
||||
git commit -a -m "save docs for ${PREVIOUS_VERSION}"
|
||||
|
||||
for i in $(find _docs -maxdepth 1 -and -type f -and \! -regex ".*\.\(html\|man\)$" -and \! -name "Makefile")
|
||||
do
|
||||
base="$(basename $i)"
|
||||
[ -e "${STARTDIR}/docs/${base}" ] && cp "${STARTDIR}/docs/${base}" "_docs/${base}"
|
||||
done
|
||||
|
||||
(cd _docs && make)
|
||||
|
||||
for i in $(find _docs -maxdepth 1 -and -type f -and \! -regex ".*\.\(html\|man\)$" -and \! -name "Makefile")
|
||||
do
|
||||
base="$(basename $i)"
|
||||
[ -e "${STARTDIR}/docs/${base}" ] && cp "_docs/${base}.html" docs/
|
||||
done
|
||||
|
||||
git commit -a -m "update docs for ${RELEASE_VERSION}"
|
||||
|
||||
################################################################################
|
||||
# Section 4: final push instructions
|
||||
################################################################################
|
||||
|
||||
echo "As a final sanity check, install the debian package and see whether i3 works."
|
||||
|
||||
echo "When satisfied, run:"
|
||||
echo " cd ${TMPDIR}/i3"
|
||||
echo " git checkout next"
|
||||
echo " vi debian/changelog"
|
||||
# TODO: can we just set up the remote spec properly?
|
||||
echo " git push git@github.com:i3/i3 next"
|
||||
echo " git push git@github.com:i3/i3 master"
|
||||
echo " git push git@github.com:i3/i3 --tags"
|
||||
echo ""
|
||||
echo " cd ${TMPDIR}/i3.github.io"
|
||||
# TODO: can we just set up the remote spec properly?
|
||||
echo " git push git@github.com:i3/i3.github.io master"
|
||||
echo ""
|
||||
echo " cd ${TMPDIR}/debian"
|
||||
echo " dput *.changes"
|
||||
echo ""
|
||||
echo "Announce on:"
|
||||
echo " twitter"
|
||||
echo " google+"
|
||||
echo " mailing list"
|
||||
echo " #i3 topic"
|
13
src/click.c
13
src/click.c
|
@ -46,6 +46,9 @@ static bool tiling_resize_for_border(Con *con, border_t border, xcb_button_press
|
|||
case BORDER_BOTTOM:
|
||||
search_direction = D_DOWN;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
bool res = resize_find_tiling_participants(&first, &second, search_direction);
|
||||
|
@ -380,11 +383,6 @@ int handle_button_press(xcb_button_press_event_t *event) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (event->child != XCB_NONE) {
|
||||
DLOG("event->child not XCB_NONE, so this is an event which originated from a click into the application, but the application did not handle it.\n");
|
||||
return route_click(con, event, mod_pressed, CLICK_INSIDE);
|
||||
}
|
||||
|
||||
/* Check if the click was on the decoration of a child */
|
||||
Con *child;
|
||||
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
|
||||
|
@ -394,5 +392,10 @@ int handle_button_press(xcb_button_press_event_t *event) {
|
|||
return route_click(child, event, mod_pressed, CLICK_DECORATION);
|
||||
}
|
||||
|
||||
if (event->child != XCB_NONE) {
|
||||
DLOG("event->child not XCB_NONE, so this is an event which originated from a click into the application, but the application did not handle it.\n");
|
||||
return route_click(con, event, mod_pressed, CLICK_INSIDE);
|
||||
}
|
||||
|
||||
return route_click(con, event, mod_pressed, CLICK_BORDER);
|
||||
}
|
||||
|
|
205
src/commands.c
205
src/commands.c
|
@ -65,28 +65,6 @@ static bool definitelyGreaterThan(float a, float b, float epsilon) {
|
|||
return (a - b) > ((fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns an 'output' corresponding to one of left/right/down/up or a specific
|
||||
* output name.
|
||||
*
|
||||
*/
|
||||
static Output *get_output_from_string(Output *current_output, const char *output_str) {
|
||||
Output *output;
|
||||
|
||||
if (strcasecmp(output_str, "left") == 0)
|
||||
output = get_output_next_wrap(D_LEFT, current_output);
|
||||
else if (strcasecmp(output_str, "right") == 0)
|
||||
output = get_output_next_wrap(D_RIGHT, current_output);
|
||||
else if (strcasecmp(output_str, "up") == 0)
|
||||
output = get_output_next_wrap(D_UP, current_output);
|
||||
else if (strcasecmp(output_str, "down") == 0)
|
||||
output = get_output_next_wrap(D_DOWN, current_output);
|
||||
else
|
||||
output = get_output_by_name(output_str);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the output containing the given container.
|
||||
*/
|
||||
|
@ -516,27 +494,7 @@ 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 = 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);
|
||||
Con *ws = workspace_get(name, NULL);
|
||||
|
||||
ws = maybe_auto_back_and_forth_workspace(ws);
|
||||
|
||||
|
@ -1071,30 +1029,7 @@ void cmd_workspace_name(I3_CMD, char *name) {
|
|||
DLOG("should switch to workspace %s\n", name);
|
||||
if (maybe_back_and_forth(cmd_output, name))
|
||||
return;
|
||||
|
||||
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);
|
||||
workspace_show_by_name(name);
|
||||
|
||||
cmd_output->needs_tree_render = true;
|
||||
// XXX: default reply for now, make this a better reply
|
||||
|
@ -1177,28 +1112,13 @@ void cmd_move_con_to_output(I3_CMD, char *name) {
|
|||
|
||||
HANDLE_EMPTY_MATCH;
|
||||
|
||||
/* get the output */
|
||||
Output *current_output = NULL;
|
||||
Output *output;
|
||||
|
||||
// TODO: fix the handling of criteria
|
||||
TAILQ_FOREACH(current, &owindows, owindows)
|
||||
current_output = get_output_of_con(current->con);
|
||||
|
||||
assert(current_output != NULL);
|
||||
|
||||
// TODO: clean this up with commands.spec as soon as we switched away from the lex/yacc command parser
|
||||
if (strcasecmp(name, "up") == 0)
|
||||
output = get_output_next_wrap(D_UP, current_output);
|
||||
else if (strcasecmp(name, "down") == 0)
|
||||
output = get_output_next_wrap(D_DOWN, current_output);
|
||||
else if (strcasecmp(name, "left") == 0)
|
||||
output = get_output_next_wrap(D_LEFT, current_output);
|
||||
else if (strcasecmp(name, "right") == 0)
|
||||
output = get_output_next_wrap(D_RIGHT, current_output);
|
||||
else
|
||||
output = get_output_by_name(name);
|
||||
|
||||
Output *output = get_output_from_string(current_output, name);
|
||||
if (!output) {
|
||||
LOG("No such output found.\n");
|
||||
ysuccess(false);
|
||||
|
@ -1265,101 +1185,12 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) {
|
|||
|
||||
owindow *current;
|
||||
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||
Output *current_output = get_output_of_con(current->con);
|
||||
if (!current_output) {
|
||||
ELOG("Cannot get current output. This is a bug in i3.\n");
|
||||
ysuccess(false);
|
||||
return;
|
||||
}
|
||||
Output *output = get_output_from_string(current_output, name);
|
||||
if (!output) {
|
||||
ELOG("Could not get output from string \"%s\"\n", name);
|
||||
ysuccess(false);
|
||||
return;
|
||||
}
|
||||
|
||||
Con *content = output_get_content(output->con);
|
||||
LOG("got output %p with content %p\n", output, content);
|
||||
|
||||
Con *previously_visible_ws = TAILQ_FIRST(&(content->nodes_head));
|
||||
LOG("Previously visible workspace = %p / %s\n", previously_visible_ws, previously_visible_ws->name);
|
||||
|
||||
Con *ws = con_get_workspace(current->con);
|
||||
LOG("should move workspace %p / %s\n", ws, ws->name);
|
||||
bool workspace_was_visible = workspace_is_visible(ws);
|
||||
|
||||
if (con_num_children(ws->parent) == 1) {
|
||||
LOG("Creating a new workspace to replace \"%s\" (last on its output).\n", ws->name);
|
||||
|
||||
/* check if we can find a workspace assigned to this output */
|
||||
bool used_assignment = false;
|
||||
struct Workspace_Assignment *assignment;
|
||||
TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) {
|
||||
if (strcmp(assignment->output, current_output->name) != 0)
|
||||
continue;
|
||||
|
||||
/* check if this workspace is already attached to the tree */
|
||||
Con *workspace = NULL, *out;
|
||||
TAILQ_FOREACH(out, &(croot->nodes_head), nodes)
|
||||
GREP_FIRST(workspace, output_get_content(out),
|
||||
!strcasecmp(child->name, assignment->name));
|
||||
if (workspace != NULL)
|
||||
continue;
|
||||
|
||||
/* so create the workspace referenced to by this assignment */
|
||||
LOG("Creating workspace from assignment %s.\n", assignment->name);
|
||||
workspace_get(assignment->name, NULL);
|
||||
used_assignment = true;
|
||||
break;
|
||||
}
|
||||
|
||||
/* if we couldn't create the workspace using an assignment, create
|
||||
* it on the output */
|
||||
if (!used_assignment)
|
||||
create_workspace_on_output(current_output, ws->parent);
|
||||
|
||||
/* notify the IPC listeners */
|
||||
ipc_send_workspace_event("init", ws, NULL);
|
||||
}
|
||||
DLOG("Detaching\n");
|
||||
|
||||
/* detach from the old output and attach to the new output */
|
||||
Con *old_content = ws->parent;
|
||||
con_detach(ws);
|
||||
if (workspace_was_visible) {
|
||||
/* The workspace which we just detached was visible, so focus
|
||||
* the next one in the focus-stack. */
|
||||
Con *focus_ws = TAILQ_FIRST(&(old_content->focus_head));
|
||||
LOG("workspace was visible, focusing %p / %s now\n", focus_ws, focus_ws->name);
|
||||
workspace_show(focus_ws);
|
||||
}
|
||||
con_attach(ws, content, false);
|
||||
|
||||
/* fix the coordinates of the floating containers */
|
||||
Con *floating_con;
|
||||
TAILQ_FOREACH(floating_con, &(ws->floating_head), floating_windows)
|
||||
floating_fix_coordinates(floating_con, &(old_content->rect), &(content->rect));
|
||||
|
||||
ipc_send_workspace_event("move", ws, NULL);
|
||||
if (workspace_was_visible) {
|
||||
/* Focus the moved workspace on the destination output. */
|
||||
workspace_show(ws);
|
||||
}
|
||||
|
||||
/* NB: We cannot simply work with previously_visible_ws since it might
|
||||
* have been cleaned up by workspace_show() already, depending on the
|
||||
* focus order/number of other workspaces on the output.
|
||||
* Instead, we loop through the available workspaces and only work with
|
||||
* previously_visible_ws if we still find it. */
|
||||
TAILQ_FOREACH(ws, &(content->nodes_head), nodes) {
|
||||
if (ws != previously_visible_ws)
|
||||
continue;
|
||||
|
||||
/* Call the on_remove_child callback of the workspace which previously
|
||||
* was visible on the destination output. Since it is no longer
|
||||
* visible, it might need to get cleaned up. */
|
||||
CALL(previously_visible_ws, on_remove_child);
|
||||
break;
|
||||
bool success = workspace_move_to_output(ws, name);
|
||||
if (!success) {
|
||||
ELOG("Failed to move workspace to output.\n");
|
||||
ysuccess(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2034,6 +1865,24 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) {
|
|||
Con *parent = workspace->parent;
|
||||
con_detach(workspace);
|
||||
con_attach(workspace, parent, false);
|
||||
|
||||
/* Move the workspace to the correct output if it has an assignment */
|
||||
struct Workspace_Assignment *assignment = NULL;
|
||||
TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) {
|
||||
if (assignment->output == NULL)
|
||||
continue;
|
||||
if (strcmp(assignment->name, workspace->name) != 0 && (!name_is_digits(assignment->name) || ws_name_to_number(assignment->name) != workspace->num)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
workspace_move_to_output(workspace, assignment->output);
|
||||
|
||||
if (previously_focused)
|
||||
workspace_show(con_get_workspace(previously_focused));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* Restore the previous focus since con_attach messes with the focus. */
|
||||
con_focus(previously_focused);
|
||||
|
||||
|
@ -2044,6 +1893,8 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) {
|
|||
ewmh_update_desktop_names();
|
||||
ewmh_update_desktop_viewport();
|
||||
ewmh_update_current_desktop();
|
||||
|
||||
startup_sequence_rename_workspace(old_name, new_name);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -216,7 +216,8 @@ char *parse_string(const char **walk, bool as_word) {
|
|||
if (**walk == '"') {
|
||||
beginning++;
|
||||
(*walk)++;
|
||||
while (**walk != '\0' && (**walk != '"' || *(*walk - 1) == '\\'))
|
||||
for (; **walk != '\0' && **walk != '"'; (*walk)++)
|
||||
if (**walk == '\\' && *(*walk + 1) != '\0')
|
||||
(*walk)++;
|
||||
} else {
|
||||
if (!as_word) {
|
||||
|
@ -248,10 +249,10 @@ char *parse_string(const char **walk, bool as_word) {
|
|||
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] == '"')
|
||||
/* We only handle escaped double quotes and backslashes to not break
|
||||
* backwards compatibility with people using \w in regular expressions
|
||||
* etc. */
|
||||
if (beginning[inpos] == '\\' && (beginning[inpos + 1] == '"' || beginning[inpos + 1] == '\\'))
|
||||
inpos++;
|
||||
str[outpos] = beginning[inpos];
|
||||
}
|
||||
|
|
12
src/con.c
12
src/con.c
|
@ -1149,12 +1149,12 @@ Rect con_border_style_rect(Con *con) {
|
|||
/* Shortcut to avoid calling con_adjacent_borders() on dock containers. */
|
||||
int border_style = con_border_style(con);
|
||||
if (border_style == BS_NONE)
|
||||
return (Rect) {0, 0, 0, 0};
|
||||
return (Rect){0, 0, 0, 0};
|
||||
borders_to_hide = con_adjacent_borders(con) & config.hide_edge_borders;
|
||||
if (border_style == BS_NORMAL) {
|
||||
result = (Rect) {border_width, 0, -(2 * border_width), -(border_width)};
|
||||
result = (Rect){border_width, 0, -(2 * border_width), -(border_width)};
|
||||
} else {
|
||||
result = (Rect) {border_width, border_width, -(2 * border_width), -(2 * border_width)};
|
||||
result = (Rect){border_width, border_width, -(2 * border_width), -(2 * border_width)};
|
||||
}
|
||||
|
||||
/* Floating windows are never adjacent to any other window, so
|
||||
|
@ -1473,7 +1473,7 @@ Rect con_minimum_size(Con *con) {
|
|||
|
||||
if (con_is_leaf(con)) {
|
||||
DLOG("leaf node, returning 75x50\n");
|
||||
return (Rect) {0, 0, 75, 50};
|
||||
return (Rect){0, 0, 75, 50};
|
||||
}
|
||||
|
||||
if (con->type == CT_FLOATING_CON) {
|
||||
|
@ -1493,7 +1493,7 @@ Rect con_minimum_size(Con *con) {
|
|||
}
|
||||
DLOG("stacked/tabbed now, returning %d x %d + deco_rect = %d\n",
|
||||
max_width, max_height, deco_height);
|
||||
return (Rect) {0, 0, max_width, max_height + deco_height};
|
||||
return (Rect){0, 0, max_width, max_height + deco_height};
|
||||
}
|
||||
|
||||
/* For horizontal/vertical split containers we sum up the width (h-split)
|
||||
|
@ -1513,7 +1513,7 @@ Rect con_minimum_size(Con *con) {
|
|||
}
|
||||
}
|
||||
DLOG("split container, returning width = %d x height = %d\n", width, height);
|
||||
return (Rect) {0, 0, width, height};
|
||||
return (Rect){0, 0, width, height};
|
||||
}
|
||||
|
||||
ELOG("Unhandled case, type = %d, layout = %d, split = %d\n",
|
||||
|
|
|
@ -158,8 +158,6 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
|
|||
assign = TAILQ_FIRST(&assignments);
|
||||
if (assign->type == A_TO_WORKSPACE)
|
||||
FREE(assign->dest.workspace);
|
||||
else if (assign->type == A_TO_OUTPUT)
|
||||
FREE(assign->dest.output);
|
||||
else if (assign->type == A_COMMAND)
|
||||
FREE(assign->dest.command);
|
||||
match_free(&(assign->match));
|
||||
|
|
|
@ -421,6 +421,11 @@ CFGFUN(bar_font, const char *font) {
|
|||
current_bar.font = sstrdup(font);
|
||||
}
|
||||
|
||||
CFGFUN(bar_separator_symbol, const char *separator) {
|
||||
FREE(current_bar.separator_symbol);
|
||||
current_bar.separator_symbol = sstrdup(separator);
|
||||
}
|
||||
|
||||
CFGFUN(bar_mode, const char *mode) {
|
||||
current_bar.mode = (strcmp(mode, "dock") == 0 ? M_DOCK : (strcmp(mode, "hide") == 0 ? M_HIDE : M_INVISIBLE));
|
||||
}
|
||||
|
|
|
@ -778,15 +778,10 @@ static char *migrate_config(char *input, off_t size) {
|
|||
|
||||
/* write the whole config file to the pipe, the script will read everything
|
||||
* immediately */
|
||||
int written = 0;
|
||||
int ret;
|
||||
while (written < size) {
|
||||
if ((ret = write(writepipe[1], input + written, size - written)) < 0) {
|
||||
if (writeall(writepipe[1], input, size) == -1) {
|
||||
warn("Could not write to pipe");
|
||||
return NULL;
|
||||
}
|
||||
written += ret;
|
||||
}
|
||||
close(writepipe[1]);
|
||||
|
||||
/* close writing end of the readpipe (connected to the script’s stdout) */
|
||||
|
@ -795,7 +790,7 @@ static char *migrate_config(char *input, off_t size) {
|
|||
/* read the script’s output */
|
||||
int conv_size = 65535;
|
||||
char *converted = malloc(conv_size);
|
||||
int read_bytes = 0;
|
||||
int read_bytes = 0, ret;
|
||||
do {
|
||||
if (read_bytes == conv_size) {
|
||||
conv_size += 65535;
|
||||
|
@ -823,11 +818,11 @@ static char *migrate_config(char *input, off_t size) {
|
|||
fprintf(stderr, "Migration process exit code was != 0\n");
|
||||
if (returncode == 2) {
|
||||
fprintf(stderr, "could not start the migration script\n");
|
||||
/* TODO: script was not found. tell the user to fix his system or create a v4 config */
|
||||
/* TODO: script was not found. tell the user to fix their system or create a v4 config */
|
||||
} else if (returncode == 1) {
|
||||
fprintf(stderr, "This already was a v4 config. Please add the following line to your config file:\n");
|
||||
fprintf(stderr, "# i3 config file (v4)\n");
|
||||
/* TODO: nag the user with a message to include a hint for i3 in his config file */
|
||||
/* TODO: nag the user with a message to include a hint for i3 in their config file */
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -511,7 +511,7 @@ DRAGGING_CB(resize_window_callback) {
|
|||
dest_height = max(dest_height, (int)(dest_width / ratio));
|
||||
}
|
||||
|
||||
con->rect = (Rect) {dest_x, dest_y, dest_width, dest_height};
|
||||
con->rect = (Rect){dest_x, dest_y, dest_width, dest_height};
|
||||
|
||||
/* Obey window size */
|
||||
floating_check_size(con);
|
||||
|
|
101
src/handlers.c
101
src/handlers.c
|
@ -106,7 +106,7 @@ static void check_crossing_screen_boundary(uint32_t x, uint32_t y) {
|
|||
return;
|
||||
}
|
||||
|
||||
/* Focus the output on which the user moved his cursor */
|
||||
/* Focus the output on which the user moved their cursor */
|
||||
Con *old_focused = focused;
|
||||
Con *next = con_descend_focused(output_get_content(output->con));
|
||||
/* Since we are switching outputs, this *must* be a different workspace, so
|
||||
|
@ -149,7 +149,7 @@ static void handle_enter_notify(xcb_enter_notify_event_t *event) {
|
|||
enter_child = true;
|
||||
}
|
||||
|
||||
/* If not, then the user moved his cursor to the root window. In that case, we adjust c_ws */
|
||||
/* If not, then the user moved their cursor to the root window. In that case, we adjust c_ws */
|
||||
if (con == NULL) {
|
||||
DLOG("Getting screen at %d x %d\n", event->root_x, event->root_y);
|
||||
check_crossing_screen_boundary(event->root_x, event->root_y);
|
||||
|
@ -874,12 +874,16 @@ static void handle_client_message(xcb_client_message_event_t *event) {
|
|||
.root_y = y_root,
|
||||
.event_x = x_root - (con->rect.x),
|
||||
.event_y = y_root - (con->rect.y)};
|
||||
if (direction == _NET_WM_MOVERESIZE_MOVE) {
|
||||
switch (direction) {
|
||||
case _NET_WM_MOVERESIZE_MOVE:
|
||||
floating_drag_window(con->parent, &fake);
|
||||
} else if (direction >= _NET_WM_MOVERESIZE_SIZE_TOPLEFT && direction <= _NET_WM_MOVERESIZE_SIZE_LEFT) {
|
||||
break;
|
||||
case _NET_WM_MOVERESIZE_SIZE_TOPLEFT... _NET_WM_MOVERESIZE_SIZE_LEFT:
|
||||
floating_resize_window(con->parent, FALSE, &fake);
|
||||
} else {
|
||||
break;
|
||||
default:
|
||||
DLOG("_NET_WM_MOVERESIZE direction %d not implemented\n", direction);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
DLOG("unhandled clientmessage\n");
|
||||
|
@ -1159,6 +1163,87 @@ static bool handle_class_change(void *data, xcb_connection_t *conn, uint8_t stat
|
|||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handles the _NET_WM_STRUT_PARTIAL property for allocating space for dock clients.
|
||||
*
|
||||
*/
|
||||
static bool handle_strut_partial_change(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
|
||||
xcb_atom_t name, xcb_get_property_reply_t *prop) {
|
||||
DLOG("strut partial change for window 0x%08x\n", window);
|
||||
|
||||
Con *con;
|
||||
if ((con = con_by_window_id(window)) == NULL || con->window == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (prop == NULL) {
|
||||
xcb_generic_error_t *err = NULL;
|
||||
xcb_get_property_cookie_t strut_cookie = xcb_get_property(conn, false, window, A__NET_WM_STRUT_PARTIAL,
|
||||
XCB_GET_PROPERTY_TYPE_ANY, 0, UINT32_MAX);
|
||||
prop = xcb_get_property_reply(conn, strut_cookie, &err);
|
||||
|
||||
if (err != NULL) {
|
||||
DLOG("got error when getting strut partial property: %d\n", err->error_code);
|
||||
free(err);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (prop == NULL) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
DLOG("That is con %p / %s\n", con, con->name);
|
||||
|
||||
window_update_strut_partial(con->window, prop);
|
||||
|
||||
/* we only handle this change for dock clients */
|
||||
if (con->parent == NULL || con->parent->type != CT_DOCKAREA) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Con *search_at = croot;
|
||||
Con *output = con_get_output(con);
|
||||
if (output != NULL) {
|
||||
DLOG("Starting search at output %s\n", output->name);
|
||||
search_at = output;
|
||||
}
|
||||
|
||||
/* find out the desired position of this dock window */
|
||||
if (con->window->reserved.top > 0 && con->window->reserved.bottom == 0) {
|
||||
DLOG("Top dock client\n");
|
||||
con->window->dock = W_DOCK_TOP;
|
||||
} else if (con->window->reserved.top == 0 && con->window->reserved.bottom > 0) {
|
||||
DLOG("Bottom dock client\n");
|
||||
con->window->dock = W_DOCK_BOTTOM;
|
||||
} else {
|
||||
DLOG("Ignoring invalid reserved edges (_NET_WM_STRUT_PARTIAL), using position as fallback:\n");
|
||||
if (con->geometry.y < (search_at->rect.height / 2)) {
|
||||
DLOG("geom->y = %d < rect.height / 2 = %d, it is a top dock client\n",
|
||||
con->geometry.y, (search_at->rect.height / 2));
|
||||
con->window->dock = W_DOCK_TOP;
|
||||
} else {
|
||||
DLOG("geom->y = %d >= rect.height / 2 = %d, it is a bottom dock client\n",
|
||||
con->geometry.y, (search_at->rect.height / 2));
|
||||
con->window->dock = W_DOCK_BOTTOM;
|
||||
}
|
||||
}
|
||||
|
||||
/* find the dockarea */
|
||||
Con *dockarea = con_for_window(search_at, con->window, NULL);
|
||||
assert(dockarea != NULL);
|
||||
|
||||
/* attach the dock to the dock area */
|
||||
con_detach(con);
|
||||
con->parent = dockarea;
|
||||
TAILQ_INSERT_HEAD(&(dockarea->focus_head), con, focused);
|
||||
TAILQ_INSERT_HEAD(&(dockarea->nodes_head), con, nodes);
|
||||
|
||||
tree_render();
|
||||
|
||||
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);
|
||||
|
@ -1177,7 +1262,8 @@ static struct property_handler_t property_handlers[] = {
|
|||
{0, UINT_MAX, handle_clientleader_change},
|
||||
{0, UINT_MAX, handle_transient_for},
|
||||
{0, 128, handle_windowrole_change},
|
||||
{0, 128, handle_class_change}};
|
||||
{0, 128, handle_class_change},
|
||||
{0, UINT_MAX, handle_strut_partial_change}};
|
||||
#define NUM_HANDLERS (sizeof(property_handlers) / sizeof(struct property_handler_t))
|
||||
|
||||
/*
|
||||
|
@ -1196,6 +1282,7 @@ void property_handlers_init(void) {
|
|||
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;
|
||||
property_handlers[8].atom = A__NET_WM_STRUT_PARTIAL;
|
||||
}
|
||||
|
||||
static void property_notify(uint8_t state, xcb_window_t window, xcb_atom_t atom) {
|
||||
|
@ -1315,7 +1402,7 @@ void handle_event(int type, xcb_generic_event_t *event) {
|
|||
handle_motion_notify((xcb_motion_notify_event_t *)event);
|
||||
break;
|
||||
|
||||
/* Enter window = user moved his mouse over the window */
|
||||
/* Enter window = user moved their mouse over the window */
|
||||
case XCB_ENTER_NOTIFY:
|
||||
handle_enter_notify((xcb_enter_notify_event_t *)event);
|
||||
break;
|
||||
|
|
14
src/ipc.c
14
src/ipc.c
|
@ -157,7 +157,7 @@ static void dump_binding(yajl_gen gen, Binding *bind) {
|
|||
y(integer, bind->keycode);
|
||||
|
||||
ystr("input_type");
|
||||
ystr((const char*)(bind->input_type == B_KEYBOARD ? "keyboard" : "mouse"));
|
||||
ystr((const char *)(bind->input_type == B_KEYBOARD ? "keyboard" : "mouse"));
|
||||
|
||||
ystr("symbol");
|
||||
if (bind->symbol == NULL)
|
||||
|
@ -397,7 +397,8 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
|
|||
ystr("transient_for");
|
||||
if (con->window->transient_for == XCB_NONE)
|
||||
y(null);
|
||||
else y(integer, con->window->transient_for);
|
||||
else
|
||||
y(integer, con->window->transient_for);
|
||||
|
||||
y(map_close);
|
||||
}
|
||||
|
@ -449,6 +450,10 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
|
|||
y(array_open);
|
||||
Match *match;
|
||||
TAILQ_FOREACH(match, &(con->swallow_head), matches) {
|
||||
/* We will generate a new restart_mode match specification after this
|
||||
* loop, so skip this one. */
|
||||
if (match->restart_mode)
|
||||
continue;
|
||||
y(map_open);
|
||||
if (match->dock != -1) {
|
||||
ystr("dock");
|
||||
|
@ -593,6 +598,11 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) {
|
|||
YSTR_IF_SET(status_command);
|
||||
YSTR_IF_SET(font);
|
||||
|
||||
if (config->separator_symbol) {
|
||||
ystr("separator_symbol");
|
||||
ystr(config->separator_symbol);
|
||||
}
|
||||
|
||||
ystr("workspace_buttons");
|
||||
y(bool, !config->hide_workspace_buttons);
|
||||
|
||||
|
|
|
@ -105,7 +105,7 @@ static int json_end_map(void *ctx) {
|
|||
int cnt = 1;
|
||||
while (workspace != NULL) {
|
||||
FREE(json_node->name);
|
||||
asprintf(&(json_node->name), "%s_%d", base, cnt++);
|
||||
sasprintf(&(json_node->name), "%s_%d", base, cnt++);
|
||||
workspace = NULL;
|
||||
TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
|
||||
GREP_FIRST(workspace, output_get_content(output), !strcasecmp(child->name, json_node->name));
|
||||
|
|
|
@ -502,7 +502,7 @@ int main(int argc, char *argv[]) {
|
|||
}
|
||||
|
||||
xcb_void_cookie_t cookie;
|
||||
cookie = xcb_change_window_attributes_checked(conn, root, XCB_CW_EVENT_MASK, (uint32_t[]) {ROOT_EVENT_MASK});
|
||||
cookie = xcb_change_window_attributes_checked(conn, root, XCB_CW_EVENT_MASK, (uint32_t[]){ROOT_EVENT_MASK});
|
||||
check_error(conn, cookie, "Another window manager seems to be running");
|
||||
|
||||
xcb_get_geometry_reply_t *greply = xcb_get_geometry_reply(conn, gcookie, NULL);
|
||||
|
@ -742,10 +742,10 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
xcb_create_gc(conn, gc, root->root,
|
||||
XCB_GC_FUNCTION | XCB_GC_PLANE_MASK | XCB_GC_FILL_STYLE | XCB_GC_SUBWINDOW_MODE,
|
||||
(uint32_t[]) {XCB_GX_COPY, ~0, XCB_FILL_STYLE_SOLID, XCB_SUBWINDOW_MODE_INCLUDE_INFERIORS});
|
||||
(uint32_t[]){XCB_GX_COPY, ~0, XCB_FILL_STYLE_SOLID, XCB_SUBWINDOW_MODE_INCLUDE_INFERIORS});
|
||||
|
||||
xcb_copy_area(conn, root->root, pixmap, gc, 0, 0, 0, 0, width, height);
|
||||
xcb_change_window_attributes_checked(conn, root->root, XCB_CW_BACK_PIXMAP, (uint32_t[]) {pixmap});
|
||||
xcb_change_window_attributes_checked(conn, root->root, XCB_CW_BACK_PIXMAP, (uint32_t[]){pixmap});
|
||||
xcb_flush(conn);
|
||||
xcb_free_gc(conn, gc);
|
||||
xcb_free_pixmap(conn, pixmap);
|
||||
|
@ -778,7 +778,7 @@ int main(int argc, char *argv[]) {
|
|||
ELOG("Could not setup signal handler");
|
||||
|
||||
/* Ignore SIGPIPE to survive errors when an IPC client disconnects
|
||||
* while we are sending him a message */
|
||||
* while we are sending them a message */
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
/* Autostarting exec-lines */
|
||||
|
|
13
src/manage.c
13
src/manage.c
|
@ -69,7 +69,7 @@ void restore_geometry(void) {
|
|||
|
||||
/* Strictly speaking, this line doesn’t really belong here, but since we
|
||||
* are syncing, let’s un-register as a window manager first */
|
||||
xcb_change_window_attributes(conn, root, XCB_CW_EVENT_MASK, (uint32_t[]) {XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT});
|
||||
xcb_change_window_attributes(conn, root, XCB_CW_EVENT_MASK, (uint32_t[]){XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT});
|
||||
|
||||
/* Make sure our changes reach the X server, we restart/exit now */
|
||||
xcb_aux_sync(conn);
|
||||
|
@ -253,10 +253,9 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
|
|||
/* See if any container swallows this new window */
|
||||
nc = con_for_window(search_at, cwindow, &match);
|
||||
if (nc == NULL) {
|
||||
/* If not, check if it is assigned to a specific workspace / output */
|
||||
if ((assignment = assignment_for(cwindow, A_TO_WORKSPACE | A_TO_OUTPUT))) {
|
||||
/* If not, check if it is assigned to a specific workspace */
|
||||
if ((assignment = assignment_for(cwindow, A_TO_WORKSPACE))) {
|
||||
DLOG("Assignment matches (%p)\n", match);
|
||||
if (assignment->type == A_TO_WORKSPACE) {
|
||||
Con *assigned_ws = workspace_get(assignment->dest.workspace, NULL);
|
||||
nc = con_descend_tiling_focused(assigned_ws);
|
||||
DLOG("focused on ws %s: %p / %s\n", assigned_ws->name, nc, nc->name);
|
||||
|
@ -268,8 +267,6 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
|
|||
/* set the urgency hint on the window if the workspace is not visible */
|
||||
if (!workspace_is_visible(assigned_ws))
|
||||
urgency_hint = true;
|
||||
}
|
||||
/* TODO: handle assignments with type == A_TO_OUTPUT */
|
||||
} else if (startup_ws) {
|
||||
/* If it’s not assigned, but was started on a specific workspace,
|
||||
* we want to open it there */
|
||||
|
@ -434,7 +431,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
|
|||
* which are not managed by the wm anyways). We store the original geometry
|
||||
* here because it’s used for dock clients. */
|
||||
if (nc->geometry.width == 0)
|
||||
nc->geometry = (Rect) {geom->x, geom->y, geom->width, geom->height};
|
||||
nc->geometry = (Rect){geom->x, geom->y, geom->width, geom->height};
|
||||
|
||||
if (motif_border_style != BS_NORMAL) {
|
||||
DLOG("MOTIF_WM_HINTS specifies decorations (border_style = %d)\n", motif_border_style);
|
||||
|
@ -455,7 +452,9 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
|
|||
}
|
||||
|
||||
/* explicitly set the border width to the default */
|
||||
if (nc->current_border_width == -1) {
|
||||
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 */
|
||||
|
|
22
src/output.c
22
src/output.c
|
@ -24,3 +24,25 @@ Con *output_get_content(Con *output) {
|
|||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns an 'output' corresponding to one of left/right/down/up or a specific
|
||||
* output name.
|
||||
*
|
||||
*/
|
||||
Output *get_output_from_string(Output *current_output, const char *output_str) {
|
||||
Output *output;
|
||||
|
||||
if (strcasecmp(output_str, "left") == 0)
|
||||
output = get_output_next_wrap(D_LEFT, current_output);
|
||||
else if (strcasecmp(output_str, "right") == 0)
|
||||
output = get_output_next_wrap(D_RIGHT, current_output);
|
||||
else if (strcasecmp(output_str, "up") == 0)
|
||||
output = get_output_next_wrap(D_UP, current_output);
|
||||
else if (strcasecmp(output_str, "down") == 0)
|
||||
output = get_output_next_wrap(D_DOWN, current_output);
|
||||
else
|
||||
output = get_output_by_name(output_str);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
|
|
@ -154,7 +154,7 @@ void render_con(Con *con, bool render_fullscreen) {
|
|||
/* depending on the border style, the rect of the child window
|
||||
* needs to be smaller */
|
||||
Rect *inset = &(con->window_rect);
|
||||
*inset = (Rect) {0, 0, con->rect.width, con->rect.height};
|
||||
*inset = (Rect){0, 0, con->rect.width, con->rect.height};
|
||||
if (!render_fullscreen)
|
||||
*inset = rect_add(*inset, con_border_style_rect(con));
|
||||
|
||||
|
|
|
@ -125,9 +125,9 @@ void restore_connect(void) {
|
|||
|
||||
static void update_placeholder_contents(placeholder_state *state) {
|
||||
xcb_change_gc(restore_conn, state->gc, XCB_GC_FOREGROUND,
|
||||
(uint32_t[]) {config.client.placeholder.background});
|
||||
(uint32_t[]){config.client.placeholder.background});
|
||||
xcb_poly_fill_rectangle(restore_conn, state->pixmap, state->gc, 1,
|
||||
(xcb_rectangle_t[]) {{0, 0, state->rect.width, state->rect.height}});
|
||||
(xcb_rectangle_t[]){{0, 0, state->rect.width, state->rect.height}});
|
||||
|
||||
// TODO: make i3font functions per-connection, at least these two for now…?
|
||||
xcb_flush(restore_conn);
|
||||
|
@ -180,7 +180,9 @@ static void update_placeholder_contents(placeholder_state *state) {
|
|||
|
||||
static void open_placeholder_window(Con *con) {
|
||||
if (con_is_leaf(con) &&
|
||||
(con->window == NULL || con->window->id == XCB_NONE)) {
|
||||
(con->window == NULL || con->window->id == XCB_NONE) &&
|
||||
!TAILQ_EMPTY(&(con->swallow_head)) &&
|
||||
con->type == CT_CON) {
|
||||
xcb_window_t placeholder = create_window(
|
||||
restore_conn,
|
||||
con->rect,
|
||||
|
@ -190,10 +192,15 @@ static void open_placeholder_window(Con *con) {
|
|||
XCURSOR_CURSOR_POINTER,
|
||||
true,
|
||||
XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK,
|
||||
(uint32_t[]) {
|
||||
(uint32_t[]){
|
||||
config.client.placeholder.background,
|
||||
XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY,
|
||||
});
|
||||
/* Make i3 not focus this window. */
|
||||
xcb_icccm_wm_hints_t hints;
|
||||
xcb_icccm_wm_hints_set_none(&hints);
|
||||
xcb_icccm_wm_hints_set_input(&hints, 0);
|
||||
xcb_icccm_set_wm_hints(restore_conn, placeholder, &hints);
|
||||
/* 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. */
|
||||
|
@ -211,7 +218,7 @@ static void open_placeholder_window(Con *con) {
|
|||
xcb_create_pixmap(restore_conn, root_depth, state->pixmap,
|
||||
state->window, state->rect.width, state->rect.height);
|
||||
state->gc = xcb_generate_id(restore_conn);
|
||||
xcb_create_gc(restore_conn, state->gc, state->pixmap, XCB_GC_GRAPHICS_EXPOSURES, (uint32_t[]) {0});
|
||||
xcb_create_gc(restore_conn, state->gc, state->pixmap, XCB_GC_GRAPHICS_EXPOSURES, (uint32_t[]){0});
|
||||
update_placeholder_contents(state);
|
||||
TAILQ_INSERT_TAIL(&state_head, state, state);
|
||||
|
||||
|
@ -323,7 +330,7 @@ static void configure_notify(xcb_configure_notify_event_t *event) {
|
|||
xcb_create_pixmap(restore_conn, root_depth, state->pixmap,
|
||||
state->window, state->rect.width, state->rect.height);
|
||||
state->gc = xcb_generate_id(restore_conn);
|
||||
xcb_create_gc(restore_conn, state->gc, state->pixmap, XCB_GC_GRAPHICS_EXPOSURES, (uint32_t[]) {0});
|
||||
xcb_create_gc(restore_conn, state->gc, state->pixmap, XCB_GC_GRAPHICS_EXPOSURES, (uint32_t[]){0});
|
||||
|
||||
update_placeholder_contents(state);
|
||||
xcb_copy_area(restore_conn, state->pixmap, state->window, state->gc,
|
||||
|
|
|
@ -70,8 +70,14 @@ static int backtrace(void) {
|
|||
int stdin_pipe[2],
|
||||
stdout_pipe[2];
|
||||
|
||||
pipe(stdin_pipe);
|
||||
pipe(stdout_pipe);
|
||||
if (pipe(stdin_pipe) == -1) {
|
||||
ELOG("Failed to init stdin_pipe\n");
|
||||
return -1;
|
||||
}
|
||||
if (pipe(stdout_pipe) == -1) {
|
||||
ELOG("Failed to init stdout_pipe\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* close standard streams in case i3 is started from a terminal; gdb
|
||||
* needs to run without controlling terminal for it to work properly in
|
||||
|
@ -129,9 +135,9 @@ static int sig_draw_window(xcb_window_t win, int width, int height, int font_hei
|
|||
/* re-draw the background */
|
||||
xcb_rectangle_t border = {0, 0, width, height},
|
||||
inner = {2, 2, width - 4, height - 4};
|
||||
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]) {get_colorpixel("#FF0000")});
|
||||
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){get_colorpixel("#FF0000")});
|
||||
xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &border);
|
||||
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]) {get_colorpixel("#000000")});
|
||||
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){get_colorpixel("#000000")});
|
||||
xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &inner);
|
||||
|
||||
/* restore font color */
|
||||
|
|
|
@ -257,6 +257,22 @@ void startup_monitor_event(SnMonitorEvent *event, void *userdata) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames workspaces that are mentioned in the startup sequences.
|
||||
*
|
||||
*/
|
||||
void startup_sequence_rename_workspace(char *old_name, char *new_name) {
|
||||
struct Startup_Sequence *current;
|
||||
TAILQ_FOREACH(current, &startup_sequences, sequences) {
|
||||
if (strcmp(current->workspace, old_name) != 0)
|
||||
continue;
|
||||
DLOG("Renaming workspace \"%s\" to \"%s\" in startup sequence %s.\n",
|
||||
old_name, new_name, current->id);
|
||||
free(current->workspace);
|
||||
current->workspace = sstrdup(new_name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the stored startup sequence for the _NET_STARTUP_ID of a given window.
|
||||
*
|
||||
|
|
|
@ -76,7 +76,7 @@ bool tree_restore(const char *path, xcb_get_geometry_reply_t *geometry) {
|
|||
|
||||
/* TODO: refactor the following */
|
||||
croot = con_new(NULL, NULL);
|
||||
croot->rect = (Rect) {
|
||||
croot->rect = (Rect){
|
||||
geometry->x,
|
||||
geometry->y,
|
||||
geometry->width,
|
||||
|
@ -104,6 +104,8 @@ bool tree_restore(const char *path, xcb_get_geometry_reply_t *geometry) {
|
|||
TAILQ_INSERT_HEAD(&(croot->nodes_head), __i3, nodes);
|
||||
}
|
||||
|
||||
restore_open_placeholder_windows(croot);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -118,7 +120,7 @@ void tree_init(xcb_get_geometry_reply_t *geometry) {
|
|||
croot->name = "root";
|
||||
croot->type = CT_ROOT;
|
||||
croot->layout = L_SPLITH;
|
||||
croot->rect = (Rect) {
|
||||
croot->rect = (Rect){
|
||||
geometry->x,
|
||||
geometry->y,
|
||||
geometry->width,
|
||||
|
@ -237,7 +239,7 @@ bool tree_close(Con *con, kill_window_t kill_window, bool dont_kill_parent, bool
|
|||
* unmap the window,
|
||||
* then reparent it to the root window. */
|
||||
xcb_change_window_attributes(conn, con->window->id,
|
||||
XCB_CW_EVENT_MASK, (uint32_t[]) {XCB_NONE});
|
||||
XCB_CW_EVENT_MASK, (uint32_t[]){XCB_NONE});
|
||||
xcb_unmap_window(conn, con->window->id);
|
||||
cookie = xcb_reparent_window(conn, con->window->id, root, 0, 0);
|
||||
|
||||
|
|
22
src/util.c
22
src/util.c
|
@ -42,14 +42,14 @@ bool rect_contains(Rect rect, uint32_t x, uint32_t y) {
|
|||
}
|
||||
|
||||
Rect rect_add(Rect a, Rect b) {
|
||||
return (Rect) {a.x + b.x,
|
||||
return (Rect){a.x + b.x,
|
||||
a.y + b.y,
|
||||
a.width + b.width,
|
||||
a.height + b.height};
|
||||
}
|
||||
|
||||
Rect rect_sub(Rect a, Rect b) {
|
||||
return (Rect) {a.x - b.x,
|
||||
return (Rect){a.x - b.x,
|
||||
a.y - b.y,
|
||||
a.width - b.width,
|
||||
a.height - b.height};
|
||||
|
@ -265,25 +265,13 @@ char *store_restart_layout(void) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
size_t written = 0;
|
||||
while (written < length) {
|
||||
int n = write(fd, payload + written, length - written);
|
||||
/* TODO: correct error-handling */
|
||||
if (n == -1) {
|
||||
perror("write()");
|
||||
if (writeall(fd, payload, length) == -1) {
|
||||
ELOG("Could not write restart layout to \"%s\", layout will be lost: %s\n", filename, strerror(errno));
|
||||
free(filename);
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
if (n == 0) {
|
||||
DLOG("write == 0?\n");
|
||||
free(filename);
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
written += n;
|
||||
DLOG("written: %zd of %zd\n", written, length);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
if (length > 0) {
|
||||
|
|
|
@ -189,7 +189,7 @@ void window_update_strut_partial(i3Window *win, xcb_get_property_reply_t *prop)
|
|||
DLOG("Reserved pixels changed to: left = %d, right = %d, top = %d, bottom = %d\n",
|
||||
strut[0], strut[1], strut[2], strut[3]);
|
||||
|
||||
win->reserved = (struct reservedpx) {strut[0], strut[1], strut[2], strut[3]};
|
||||
win->reserved = (struct reservedpx){strut[0], strut[1], strut[2], strut[3]};
|
||||
|
||||
free(prop);
|
||||
}
|
||||
|
|
108
src/workspace.c
108
src/workspace.c
|
@ -879,3 +879,111 @@ Con *workspace_encapsulate(Con *ws) {
|
|||
|
||||
return new;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the given workspace to the specified output.
|
||||
* This returns true if and only if moving the workspace was successful.
|
||||
*/
|
||||
bool workspace_move_to_output(Con *ws, char *name) {
|
||||
LOG("Trying to move workspace %p / %s to output \"%s\".\n", ws, ws->name, name);
|
||||
|
||||
Con *current_output_con = con_get_output(ws);
|
||||
if (!current_output_con) {
|
||||
ELOG("Could not get the output container for workspace %p / %s.\n", ws, ws->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
Output *current_output = get_output_by_name(current_output_con->name);
|
||||
if (!current_output) {
|
||||
ELOG("Cannot get current output. This is a bug in i3.\n");
|
||||
return false;
|
||||
}
|
||||
Output *output = get_output_from_string(current_output, name);
|
||||
if (!output) {
|
||||
ELOG("Could not get output from string \"%s\"\n", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
Con *content = output_get_content(output->con);
|
||||
LOG("got output %p with content %p\n", output, content);
|
||||
|
||||
Con *previously_visible_ws = TAILQ_FIRST(&(content->nodes_head));
|
||||
LOG("Previously visible workspace = %p / %s\n", previously_visible_ws, previously_visible_ws->name);
|
||||
|
||||
bool workspace_was_visible = workspace_is_visible(ws);
|
||||
if (con_num_children(ws->parent) == 1) {
|
||||
LOG("Creating a new workspace to replace \"%s\" (last on its output).\n", ws->name);
|
||||
|
||||
/* check if we can find a workspace assigned to this output */
|
||||
bool used_assignment = false;
|
||||
struct Workspace_Assignment *assignment;
|
||||
TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) {
|
||||
if (assignment->output == NULL || strcmp(assignment->output, current_output->name) != 0)
|
||||
continue;
|
||||
|
||||
/* check if this workspace is already attached to the tree */
|
||||
Con *workspace = NULL, *out;
|
||||
TAILQ_FOREACH(out, &(croot->nodes_head), nodes)
|
||||
GREP_FIRST(workspace, output_get_content(out),
|
||||
!strcasecmp(child->name, assignment->name));
|
||||
if (workspace != NULL)
|
||||
continue;
|
||||
|
||||
/* so create the workspace referenced to by this assignment */
|
||||
LOG("Creating workspace from assignment %s.\n", assignment->name);
|
||||
workspace_get(assignment->name, NULL);
|
||||
used_assignment = true;
|
||||
break;
|
||||
}
|
||||
|
||||
/* if we couldn't create the workspace using an assignment, create
|
||||
* it on the output */
|
||||
if (!used_assignment)
|
||||
create_workspace_on_output(current_output, ws->parent);
|
||||
|
||||
/* notify the IPC listeners */
|
||||
ipc_send_workspace_event("init", ws, NULL);
|
||||
}
|
||||
DLOG("Detaching\n");
|
||||
|
||||
/* detach from the old output and attach to the new output */
|
||||
Con *old_content = ws->parent;
|
||||
con_detach(ws);
|
||||
if (workspace_was_visible) {
|
||||
/* The workspace which we just detached was visible, so focus
|
||||
* the next one in the focus-stack. */
|
||||
Con *focus_ws = TAILQ_FIRST(&(old_content->focus_head));
|
||||
LOG("workspace was visible, focusing %p / %s now\n", focus_ws, focus_ws->name);
|
||||
workspace_show(focus_ws);
|
||||
}
|
||||
con_attach(ws, content, false);
|
||||
|
||||
/* fix the coordinates of the floating containers */
|
||||
Con *floating_con;
|
||||
TAILQ_FOREACH(floating_con, &(ws->floating_head), floating_windows)
|
||||
floating_fix_coordinates(floating_con, &(old_content->rect), &(content->rect));
|
||||
|
||||
ipc_send_workspace_event("move", ws, NULL);
|
||||
if (workspace_was_visible) {
|
||||
/* Focus the moved workspace on the destination output. */
|
||||
workspace_show(ws);
|
||||
}
|
||||
|
||||
/* NB: We cannot simply work with previously_visible_ws since it might
|
||||
* have been cleaned up by workspace_show() already, depending on the
|
||||
* focus order/number of other workspaces on the output.
|
||||
* Instead, we loop through the available workspaces and only work with
|
||||
* previously_visible_ws if we still find it. */
|
||||
TAILQ_FOREACH(ws, &(content->nodes_head), nodes) {
|
||||
if (ws != previously_visible_ws)
|
||||
continue;
|
||||
|
||||
/* Call the on_remove_child callback of the workspace which previously
|
||||
* was visible on the destination output. Since it is no longer
|
||||
* visible, it might need to get cleaned up. */
|
||||
CALL(previously_visible_ws, on_remove_child);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
32
src/x.c
32
src/x.c
|
@ -352,8 +352,8 @@ void x_draw_decoration(Con *con) {
|
|||
|
||||
Rect *r = &(con->rect);
|
||||
Rect *w = &(con->window_rect);
|
||||
p->con_rect = (struct width_height) {r->width, r->height};
|
||||
p->con_window_rect = (struct width_height) {w->width, w->height};
|
||||
p->con_rect = (struct width_height){r->width, r->height};
|
||||
p->con_window_rect = (struct width_height){w->width, w->height};
|
||||
p->con_deco_rect = con->deco_rect;
|
||||
p->background = config.client.background;
|
||||
p->con_is_leaf = con_is_leaf(con);
|
||||
|
@ -403,7 +403,7 @@ void x_draw_decoration(Con *con) {
|
|||
);
|
||||
#endif
|
||||
|
||||
xcb_change_gc(conn, con->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {config.client.background});
|
||||
xcb_change_gc(conn, con->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){config.client.background});
|
||||
xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, sizeof(background) / sizeof(xcb_rectangle_t), background);
|
||||
}
|
||||
|
||||
|
@ -424,7 +424,7 @@ void x_draw_decoration(Con *con) {
|
|||
* (left, bottom and right part). We don’t just fill the whole
|
||||
* rectangle because some childs are not freely resizable and we want
|
||||
* their background color to "shine through". */
|
||||
xcb_change_gc(conn, con->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {p->color->background});
|
||||
xcb_change_gc(conn, con->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){p->color->background});
|
||||
if (!(borders_to_hide & ADJ_LEFT_SCREEN_EDGE)) {
|
||||
xcb_rectangle_t leftline = {0, 0, br.x, r->height};
|
||||
xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &leftline);
|
||||
|
@ -450,12 +450,12 @@ void x_draw_decoration(Con *con) {
|
|||
if (TAILQ_NEXT(con, nodes) == NULL &&
|
||||
TAILQ_PREV(con, nodes_head, nodes) == NULL &&
|
||||
con->parent->type != CT_FLOATING_CON) {
|
||||
xcb_change_gc(conn, con->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {p->color->indicator});
|
||||
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[]) {
|
||||
xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, (xcb_rectangle_t[]){
|
||||
{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[]) {
|
||||
xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, (xcb_rectangle_t[]){
|
||||
{br.x, r->height + (br.height + br.y), r->width + br.width, -(br.height + br.y)}});
|
||||
}
|
||||
}
|
||||
|
@ -466,16 +466,16 @@ void x_draw_decoration(Con *con) {
|
|||
goto copy_pixmaps;
|
||||
|
||||
/* 4: paint the bar */
|
||||
xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {p->color->background});
|
||||
xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){p->color->background});
|
||||
xcb_rectangle_t drect = {con->deco_rect.x, con->deco_rect.y, con->deco_rect.width, con->deco_rect.height};
|
||||
xcb_poly_fill_rectangle(conn, parent->pixmap, parent->pm_gc, 1, &drect);
|
||||
|
||||
/* 5: draw two unconnected horizontal lines in border color */
|
||||
xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {p->color->border});
|
||||
xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){p->color->border});
|
||||
Rect *dr = &(con->deco_rect);
|
||||
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;
|
||||
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;
|
||||
|
@ -545,12 +545,12 @@ after_title:
|
|||
/* Draw a 1px separator line before and after every tab, so that tabs can
|
||||
* be easily distinguished. */
|
||||
if (parent->layout == L_TABBED) {
|
||||
xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {p->color->border});
|
||||
xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){p->color->border});
|
||||
} else {
|
||||
xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {p->color->background});
|
||||
xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){p->color->background});
|
||||
}
|
||||
xcb_poly_line(conn, XCB_COORD_MODE_ORIGIN, parent->pixmap, parent->pm_gc, 6,
|
||||
(xcb_point_t[]) {
|
||||
(xcb_point_t[]){
|
||||
{dr->x + dr->width, dr->y},
|
||||
{dr->x + dr->width, dr->y + dr->height},
|
||||
{dr->x + dr->width - 1, dr->y},
|
||||
|
@ -559,7 +559,7 @@ after_title:
|
|||
{dr->x, dr->y},
|
||||
});
|
||||
|
||||
xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {p->color->border});
|
||||
xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){p->color->border});
|
||||
xcb_poly_segment(conn, parent->pixmap, parent->pm_gc, 2, segments);
|
||||
|
||||
copy_pixmaps:
|
||||
|
@ -975,9 +975,9 @@ void x_push_changes(Con *con) {
|
|||
Output *target = get_output_containing(mid_x, mid_y);
|
||||
if (current != target) {
|
||||
/* Ignore MotionNotify events generated by warping */
|
||||
xcb_change_window_attributes(conn, root, XCB_CW_EVENT_MASK, (uint32_t[]) {XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT});
|
||||
xcb_change_window_attributes(conn, root, XCB_CW_EVENT_MASK, (uint32_t[]){XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT});
|
||||
xcb_warp_pointer(conn, XCB_NONE, root, 0, 0, 0, 0, mid_x, mid_y);
|
||||
xcb_change_window_attributes(conn, root, XCB_CW_EVENT_MASK, (uint32_t[]) {ROOT_EVENT_MASK});
|
||||
xcb_change_window_attributes(conn, root, XCB_CW_EVENT_MASK, (uint32_t[]){ROOT_EVENT_MASK});
|
||||
}
|
||||
}
|
||||
warp_to = NULL;
|
||||
|
|
|
@ -70,9 +70,9 @@ xcb_window_t create_window(xcb_connection_t *conn, Rect dims,
|
|||
*/
|
||||
void xcb_draw_line(xcb_connection_t *conn, xcb_drawable_t drawable, xcb_gcontext_t gc,
|
||||
uint32_t colorpixel, uint32_t x, uint32_t y, uint32_t to_x, uint32_t to_y) {
|
||||
xcb_change_gc(conn, gc, XCB_GC_FOREGROUND, (uint32_t[]) {colorpixel});
|
||||
xcb_change_gc(conn, gc, XCB_GC_FOREGROUND, (uint32_t[]){colorpixel});
|
||||
xcb_poly_line(conn, XCB_COORD_MODE_ORIGIN, drawable, gc, 2,
|
||||
(xcb_point_t[]) {{x, y}, {to_x, to_y}});
|
||||
(xcb_point_t[]){{x, y}, {to_x, to_y}});
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -81,7 +81,7 @@ void xcb_draw_line(xcb_connection_t *conn, xcb_drawable_t drawable, xcb_gcontext
|
|||
*/
|
||||
void xcb_draw_rect(xcb_connection_t *conn, xcb_drawable_t drawable, xcb_gcontext_t gc,
|
||||
uint32_t colorpixel, uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
|
||||
xcb_change_gc(conn, gc, XCB_GC_FOREGROUND, (uint32_t[]) {colorpixel});
|
||||
xcb_change_gc(conn, gc, XCB_GC_FOREGROUND, (uint32_t[]){colorpixel});
|
||||
xcb_rectangle_t rect = {x, y, width, height};
|
||||
xcb_poly_fill_rectangle(conn, drawable, gc, 1, &rect);
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ void xcursor_load_cursors(void) {
|
|||
*/
|
||||
void xcursor_set_root_cursor(int cursor_id) {
|
||||
xcb_change_window_attributes(conn, root, XCB_CW_CURSOR,
|
||||
(uint32_t[]) {xcursor_get_cursor(cursor_id)});
|
||||
(uint32_t[]){xcursor_get_cursor(cursor_id)});
|
||||
}
|
||||
|
||||
xcb_cursor_t xcursor_get_cursor(enum xcursor_cursor_t c) {
|
||||
|
|
|
@ -227,7 +227,7 @@ if ($numtests == 1) {
|
|||
|
||||
END { cleanup() }
|
||||
|
||||
exit 0;
|
||||
exit ($aggregator->failed > 0);
|
||||
|
||||
#
|
||||
# Takes a test from the beginning of @testfiles and runs it.
|
||||
|
@ -324,8 +324,9 @@ sub take_job {
|
|||
}
|
||||
|
||||
sub cleanup {
|
||||
my $exitcode = $?;
|
||||
$_->() for our @CLEANUP;
|
||||
exit;
|
||||
exit $exitcode;
|
||||
}
|
||||
|
||||
# must be in a begin block because we C<exit 0> above
|
||||
|
|
|
@ -109,7 +109,7 @@ sub activate_i3 {
|
|||
|
||||
if ($args{valgrind}) {
|
||||
$i3cmd =
|
||||
qq|valgrind -v --log-file="$outdir/valgrind-for-$test.log" | .
|
||||
qq|valgrind --log-file="$outdir/valgrind-for-$test.log" | .
|
||||
qq|--suppressions="./valgrind.supp" | .
|
||||
qq|--leak-check=full --track-origins=yes --num-callers=20 | .
|
||||
qq|--tool=memcheck -- $i3cmd|;
|
||||
|
|
|
@ -15,6 +15,15 @@ my %ansi_line_upwards;
|
|||
|
||||
my $tests_total;
|
||||
|
||||
sub noninteractive {
|
||||
# CONTINUOUS_INTEGRATION gets set when running under Travis, see
|
||||
# http://docs.travis-ci.com/user/ci-environment/ and
|
||||
# https://github.com/travis-ci/travis-ci/issues/1337
|
||||
return (! -t STDOUT) || (
|
||||
defined($ENV{CONTINUOUS_INTEGRATION}) &&
|
||||
$ENV{CONTINUOUS_INTEGRATION} eq 'true');
|
||||
}
|
||||
|
||||
# setup %ansi_line_upwards to map all working displays to the
|
||||
# specific movement commands and initialize all status lines
|
||||
sub status_init {
|
||||
|
@ -22,6 +31,8 @@ sub status_init {
|
|||
my $displays = $args{displays};
|
||||
$tests_total = $args{tests};
|
||||
|
||||
return if noninteractive();
|
||||
|
||||
for my $n (1 .. @$displays) {
|
||||
# since we are moving upwards, get $display in reverse order
|
||||
my $display = $displays->[-$n];
|
||||
|
@ -41,6 +52,8 @@ sub status {
|
|||
my ($display, $msg) = @_;
|
||||
my $status = "[$display] $msg";
|
||||
|
||||
return $status if noninteractive();
|
||||
|
||||
print
|
||||
$ansi_save_cursor,
|
||||
$ansi_line_upwards{$display},
|
||||
|
@ -53,6 +66,9 @@ sub status {
|
|||
|
||||
sub status_completed {
|
||||
my $num = shift;
|
||||
|
||||
return if noninteractive();
|
||||
|
||||
print
|
||||
$ansi_save_cursor,
|
||||
$ansi_clear_line,
|
||||
|
|
|
@ -226,7 +226,7 @@ you might have to map it on your own and use this function:
|
|||
sub wait_for_map {
|
||||
my ($win) = @_;
|
||||
my $id = (blessed($win) && $win->isa('X11::XCB::Window')) ? $win->id : $win;
|
||||
wait_for_event 2, sub {
|
||||
wait_for_event 4, sub {
|
||||
$_[0]->{response_type} == MAP_NOTIFY and $_[0]->{window} == $id
|
||||
};
|
||||
}
|
||||
|
@ -248,7 +248,7 @@ event.
|
|||
sub wait_for_unmap {
|
||||
my ($win) = @_;
|
||||
# my $id = (blessed($win) && $win->isa('X11::XCB::Window')) ? $win->id : $win;
|
||||
wait_for_event 2, sub {
|
||||
wait_for_event 4, sub {
|
||||
$_[0]->{response_type} == UNMAP_NOTIFY # and $_[0]->{window} == $id
|
||||
};
|
||||
sync_with_i3();
|
||||
|
@ -718,7 +718,7 @@ sub sync_with_i3 {
|
|||
return $myrnd if $args{dont_wait_for_event};
|
||||
|
||||
# now wait until the reply is here
|
||||
return wait_for_event 2, sub {
|
||||
return wait_for_event 4, sub {
|
||||
my ($event) = @_;
|
||||
# TODO: const
|
||||
return 0 unless $event->{response_type} == 161;
|
||||
|
|
|
@ -143,6 +143,22 @@ wait_for_map $window;
|
|||
@docked = get_dock_clients('top');
|
||||
is(@docked, 1, 'dock client on top');
|
||||
|
||||
# now change strut_partial to reserve space on the bottom and the dock should
|
||||
# be moved to the bottom dock area
|
||||
$x->change_property(
|
||||
PROP_MODE_REPLACE,
|
||||
$window->id,
|
||||
$atomname->id,
|
||||
$atomtype->id,
|
||||
32, # 32 bit integer
|
||||
12,
|
||||
pack('L12', 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 1280, 0)
|
||||
);
|
||||
|
||||
sync_with_i3;
|
||||
@docked = get_dock_clients('bottom');
|
||||
is(@docked, 1, 'dock client on bottom');
|
||||
|
||||
$window->destroy;
|
||||
|
||||
wait_for_unmap $window;
|
||||
|
|
|
@ -142,12 +142,17 @@ is_num_children($first_ws, 2, 'two containers on the first workspace');
|
|||
complete_startup();
|
||||
sync_with_i3;
|
||||
|
||||
# even when renaming the workspace, windows should end up on the correct one
|
||||
cmd "rename workspace $first_ws to temp";
|
||||
|
||||
# Startup has completed but the 30-second deletion time hasn't elapsed,
|
||||
# so this window should still go on the leader's initial workspace.
|
||||
$win = open_window({ dont_map => 1, client_leader => $leader });
|
||||
$win->map;
|
||||
sync_with_i3;
|
||||
|
||||
cmd "rename workspace temp to $first_ws";
|
||||
|
||||
is_num_children($first_ws, 3, 'three containers on the first workspace');
|
||||
|
||||
# Switch to the first workspace and move the focused window to the
|
||||
|
|
|
@ -156,7 +156,7 @@ is(parser_calls('move something to somewhere'),
|
|||
'error for unknown literal ok');
|
||||
|
||||
################################################################################
|
||||
# 3: Verify that escaping of double quotes works correctly
|
||||
# 3: Verify that escaping works correctly
|
||||
################################################################################
|
||||
|
||||
is(parser_calls('workspace "foo"'),
|
||||
|
@ -171,6 +171,18 @@ is(parser_calls('workspace "foo \"bar"'),
|
|||
'cmd_workspace_name(foo "bar)',
|
||||
'Command with escaped double quotes ok');
|
||||
|
||||
is(parser_calls('workspace "foo \\'),
|
||||
'cmd_workspace_name(foo \\)',
|
||||
'Command with single backslash in the end ok');
|
||||
|
||||
is(parser_calls('workspace "foo\\\\bar"'),
|
||||
'cmd_workspace_name(foo\\bar)',
|
||||
'Command with escaped backslashes ok');
|
||||
|
||||
is(parser_calls('workspace "foo\\\\\\"bar"'),
|
||||
'cmd_workspace_name(foo\\"bar)',
|
||||
'Command with escaped double quotes after escaped backslashes ok');
|
||||
|
||||
################################################################################
|
||||
# 4: Verify that resize commands with a "px or ppt"-construction are parsed
|
||||
# correctly
|
||||
|
|
|
@ -31,7 +31,7 @@ my $config = <<EOT;
|
|||
# i3 config file (v4)
|
||||
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
|
||||
|
||||
force_display_urgency_hint 150ms
|
||||
force_display_urgency_hint 500ms
|
||||
EOT
|
||||
my $pid = launch_with_config($config);
|
||||
|
||||
|
@ -70,7 +70,7 @@ is(@urgent, 1, 'window still marked as urgent');
|
|||
# now check if the timer was triggered
|
||||
cmd "workspace $tmp2";
|
||||
|
||||
sleep(0.1);
|
||||
sleep(0.5);
|
||||
@content = @{get_ws_content($tmp1)};
|
||||
@urgent = grep { $_->{urgent} } @content;
|
||||
is(@urgent, 0, 'window not marked as urgent anymore');
|
||||
|
@ -142,7 +142,7 @@ cmd "workspace $tmp3";
|
|||
$split_left->delete_hint('urgency');
|
||||
sync_with_i3;
|
||||
|
||||
sleep(0.2);
|
||||
sleep(0.6);
|
||||
is(count_total_urgent(get_ws($tmp3)), 0, "no more urgent windows on workspace $tmp3");
|
||||
|
||||
exit_gracefully($pid);
|
||||
|
|
|
@ -647,7 +647,7 @@ EOT
|
|||
|
||||
$expected = <<'EOT';
|
||||
cfg_bar_output(LVDS-1)
|
||||
ERROR: CONFIG: Expected one of these tokens: <end>, '#', 'set', 'i3bar_command', 'status_command', 'socket_path', 'mode', 'hidden_state', 'id', 'modifier', 'wheel_up_cmd', 'wheel_down_cmd', 'position', 'output', 'tray_output', 'font', 'binding_mode_indicator', 'workspace_buttons', 'strip_workspace_numbers', 'verbose', 'colors', '}'
|
||||
ERROR: CONFIG: Expected one of these tokens: <end>, '#', 'set', 'i3bar_command', 'status_command', 'socket_path', 'mode', 'hidden_state', 'id', 'modifier', 'wheel_up_cmd', 'wheel_down_cmd', 'position', 'output', 'tray_output', 'font', 'separator_symbol', 'binding_mode_indicator', 'workspace_buttons', 'strip_workspace_numbers', 'verbose', 'colors', '}'
|
||||
ERROR: CONFIG: (in file <stdin>)
|
||||
ERROR: CONFIG: Line 1: bar {
|
||||
ERROR: CONFIG: Line 2: output LVDS-1
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
#!perl
|
||||
# vim:ts=4:sw=4:expandtab
|
||||
#
|
||||
# Please read the following documents before working on tests:
|
||||
# • http://build.i3wm.org/docs/testsuite.html
|
||||
# (or docs/testsuite)
|
||||
#
|
||||
# • http://build.i3wm.org/docs/lib-i3test.html
|
||||
# (alternatively: perldoc ./testcases/lib/i3test.pm)
|
||||
#
|
||||
# • http://build.i3wm.org/docs/ipc.html
|
||||
# (or docs/ipc)
|
||||
#
|
||||
# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
|
||||
# (unless you are already familiar with Perl)
|
||||
#
|
||||
# Test that `workspace {N}` acts like `workspace number {N}` when N is a plain
|
||||
# digit, and likewise for `move to workspace {N}`.
|
||||
# Ticket: #1238
|
||||
# Bug still in: 4.8-16-g3f5a0f0
|
||||
use i3test;
|
||||
|
||||
cmd 'workspace 5:foo';
|
||||
open_window;
|
||||
fresh_workspace;
|
||||
cmd 'workspace 5';
|
||||
|
||||
is(focused_ws, '5:foo',
|
||||
'a command to switch to a workspace with a bare number should switch to a workspace of that number');
|
||||
|
||||
fresh_workspace;
|
||||
my $win = open_window;
|
||||
cmd '[id="' . $win->{id} . '"] move to workspace 5';
|
||||
|
||||
is(@{get_ws('5:foo')->{nodes}}, 2,
|
||||
'a command to move a container to a workspace with a bare number should move that container to a workspace of that number');
|
||||
|
||||
fresh_workspace;
|
||||
cmd 'workspace 7';
|
||||
open_window;
|
||||
cmd 'workspace 7:foo';
|
||||
$win = open_window;
|
||||
|
||||
cmd 'workspace 7';
|
||||
is(focused_ws, '7',
|
||||
'a workspace with a name that is a matching plain number should be preferred when switching');
|
||||
|
||||
cmd '[id="' . $win->{id} . '"] move to workspace 7';
|
||||
is(@{get_ws('7')->{nodes}}, 2,
|
||||
'a workspace with a name that is a matching plain number should be preferred when moving');
|
||||
|
||||
done_testing;
|
|
@ -0,0 +1,90 @@
|
|||
#!perl
|
||||
# vim:ts=4:sw=4:expandtab
|
||||
#
|
||||
# Please read the following documents before working on tests:
|
||||
# • http://build.i3wm.org/docs/testsuite.html
|
||||
# (or docs/testsuite)
|
||||
#
|
||||
# • http://build.i3wm.org/docs/lib-i3test.html
|
||||
# (alternatively: perldoc ./testcases/lib/i3test.pm)
|
||||
#
|
||||
# • http://build.i3wm.org/docs/ipc.html
|
||||
# (or docs/ipc)
|
||||
#
|
||||
# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
|
||||
# (unless you are already familiar with Perl)
|
||||
#
|
||||
#
|
||||
# Tests that workspaces are moved to the assigned output if they
|
||||
# are renamed to an assigned name.
|
||||
# Ticket: #1473
|
||||
|
||||
use i3test i3_autostart => 0;
|
||||
|
||||
my $config = <<EOT;
|
||||
# i3 config file (v4)
|
||||
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
|
||||
fake-outputs 1024x768+0+0,1024x768+1024+0
|
||||
|
||||
workspace 1 output fake-0
|
||||
workspace 2 output fake-1
|
||||
workspace 3:foo output fake-1
|
||||
workspace baz output fake-1
|
||||
EOT
|
||||
|
||||
my $pid = launch_with_config($config);
|
||||
my $i3 = i3(get_socket_path());
|
||||
$i3->connect->recv;
|
||||
|
||||
# Returns the name of the output on which this workspace resides
|
||||
sub get_output_for_workspace {
|
||||
my $ws_name = shift @_;
|
||||
|
||||
foreach (grep { not $_->{name} =~ /^__/ } @{$i3->get_tree->recv->{nodes}}) {
|
||||
my $output = $_->{name};
|
||||
foreach (grep { $_->{name} =~ "content" } @{$_->{nodes}}) {
|
||||
return $output if $_->{nodes}[0]->{name} =~ $ws_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
##########################################################################
|
||||
# Renaming the workspace to an unassigned name does not move the workspace
|
||||
# (regression test)
|
||||
##########################################################################
|
||||
|
||||
cmd 'focus output fake-0';
|
||||
cmd 'rename workspace to unassigned';
|
||||
is(get_output_for_workspace('unassigned'), 'fake-0',
|
||||
'Unassigned workspace should stay on its output when being renamed');
|
||||
|
||||
##########################################################################
|
||||
# Renaming a workspace by number only triggers the assignment
|
||||
##########################################################################
|
||||
|
||||
cmd 'focus output fake-0';
|
||||
cmd 'rename workspace to 2';
|
||||
is(get_output_for_workspace('2'), 'fake-1',
|
||||
'Renaming the workspace to a number should move it to the assigned output');
|
||||
|
||||
##########################################################################
|
||||
# Renaming a workspace by number and name triggers the assignment
|
||||
##########################################################################
|
||||
|
||||
cmd 'focus output fake-0';
|
||||
cmd 'rename workspace to "2:foo"';
|
||||
is(get_output_for_workspace('2:foo'), 'fake-1',
|
||||
'Renaming the workspace to a number and name should move it to the assigned output');
|
||||
|
||||
##########################################################################
|
||||
# Renaming a workspace by name only triggers the assignment
|
||||
##########################################################################
|
||||
|
||||
cmd 'focus output fake-0';
|
||||
cmd 'rename workspace to baz';
|
||||
is(get_output_for_workspace('baz'), 'fake-1',
|
||||
'Renaming the workspace to a number and name should move it to the assigned output');
|
||||
|
||||
|
||||
exit_gracefully($pid);
|
||||
done_testing;
|
Loading…
Reference in New Issue