diff --git a/.github/workflows/Test.yml b/.github/workflows/Test.yml index 84b5b050..4081ece3 100644 --- a/.github/workflows/Test.yml +++ b/.github/workflows/Test.yml @@ -20,8 +20,7 @@ jobs: pacman -S --noconfirm lua pacman -S --noconfirm wayland pacman -S --noconfirm wayland-protocols - pacman -S --noconfirm check - pacman -S --noconfirm gcovr + pacman -S --noconfirm libnotify pacman -S --noconfirm scdoc name: Run in container - run: | diff --git a/include/lib/actions/config.h b/.tmux.conf similarity index 100% rename from include/lib/actions/config.h rename to .tmux.conf diff --git a/README.md b/README.md index f2dc4853..bc3faa1a 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,9 @@ Japokwm is a dynamic tiling wayland compositor where you are able to create new - Create any layout you want with a 3 dimensional Array: ![](edit_layout.gif) - Layout specific configs +- a client to control the windowmanager from the terminal - japokmsg based on + swaymsg +- a dwm based tagging system instead of normal(boring) workspaces ## Download [AUR](https://aur.archlinux.org/packages/japokwm-git) @@ -32,4 +35,4 @@ japokwm is in it's alpha stage and bugs may accour (If you find them please repo - unittests ## Acknowledgements -japokwm forked dwl and uses wlroots to do the heavy lifting +japokwm forked dwl and uses wlroots and parts of sway to do the heavy lifting diff --git a/src/stringop.c b/common/stringop.c similarity index 96% rename from src/stringop.c rename to common/stringop.c index d60d63ad..85f7afde 100644 --- a/src/stringop.c +++ b/common/stringop.c @@ -6,7 +6,6 @@ #include #include #include -#include #include "stringop.h" static const char whitespace[] = " \f\n\r\t\v"; @@ -75,14 +74,13 @@ int lenient_strcmp(char *a, char *b) { } } -struct wlr_list split_string(const char *str, const char *delims) { - struct wlr_list res; - wlr_list_init(&res); +GPtrArray *split_string(const char *str, const char *delims) { + GPtrArray *res = g_ptr_array_new(); char *copy = strdup(str); char *token = strtok(copy, delims); while (token) { - wlr_list_push(&res, strdup(token)); + g_ptr_array_add(res, strdup(token)); token = strtok(NULL, delims); } free(copy); @@ -150,7 +148,6 @@ void free_argv(int argc, char **argv) { } int unescape_string(char *string) { - /* TODO: More C string escapes */ int len = strlen(string); int i; for (i = 0; string[i]; ++i) { @@ -328,3 +325,10 @@ bool expand_path(char **path) { wordfree(&p); return true; } + +void append_string(char **dest, const char *src) +{ + int len = strlen(*dest) + strlen(src) + 1; + *dest = realloc(*dest, len); + strcat(*dest, src); +} diff --git a/config/init.lua b/config/init.lua index 03eba014..327bd869 100644 --- a/config/init.lua +++ b/config/init.lua @@ -1,84 +1,83 @@ config.create_workspaces({"0:1", "1:2", "2:3", "3:4", "4:5", "5:6", "6:7", "7:8"}) +config.set_sloppy_focus(true) + local function on_start() -- execute programs or do what ever you want e.g.: - -- action.exec("...") + -- action.exec("japokmsg 'action.create_output()'") end -event.set_on_start_function(on_start) +-- -- eval string. Everything that will be opened after that will be moved to the +-- -- scratchpad and will be shown +-- action.scratch_show("anki") + +event.add_listener("on_start", on_start) config.set_inner_gaps(0) -local layouts = {"tile", "two_pane", "monocle", "tmp"} +local layouts = {"tmp", "two_pane", "monocle", "tile"} config.create_layout_set("default", layouts) config.set_default_layout(layouts[1]) local termcmd = "/usr/bin/termite" -config.set_keybinds({ - {"mod-p", function() action.exec("rofi -show run") end}, - {"mod-e", function() action.view(info.get_next_empty_workspace(info.get_workspace(), info.direction.right)) end}, - {"mod-period", function() action.toggle_workspace() end}, - {"mod-S-period", function() action.toggle_layout() end}, - {"mod-S-Return", function() action.exec(termcmd) end}, - {"mod-a", function() action.increase_nmaster() end}, - {"mod-x", function() action.decrease_nmaster() end}, - {"mod-k", function() action.focus_on_stack(-1) end}, - {"mod-j", function() action.focus_on_stack(1) end}, - {"mod-S-j", function() action.focus_on_hidden_stack(0) end}, - {"mod-S-k", function() action.focus_on_hidden_stack(-1) end}, - {"mod-S-c", function() action.kill(info.this_container_position()) end}, - {"mod-S-q", function() action.quit() end}, - {"mod-space", function() action.load_next_layout_in_set("default") end}, - {"mod-minus", function() action.move_to_scratchpad(info.this_container_position()) end}, - {"mod-S-minus", function() action.show_scratchpad() end}, - {"mod-S-space", function() action.load_prev_layout_in_set("default") end}, - {"mod-m", function() action.load_layout_in_set("default", 1) end}, - {"mod-S-t", function() action.load_layout_in_set("default", 2) end}, - {"mod-w", function() action.load_layout_in_set("default", 3) end}, - {"mod-S-w", function() action.load_layout_in_set("default", 4) end}, - {"mod-S-p", function() action.load_layout("tmp") end}, - {"mod-b", function() action.toggle_bars() end}, - {"mod-S-h", function() action.resize_main(-1/10) end}, - {"mod-S-l", function() action.resize_main(1/10) end}, - {"mod-S-s", function() action.write_this_overlay("tmp") end}, - {"mod-Return", function() action.zoom() end}, - -- {"mod-Return", function() action.repush(2, 0) end}, - {"mod-1", function() action.view(0) end}, - {"mod-2", function() action.view(1) end}, - {"mod-3", function() action.view(2) end}, - {"mod-4", function() action.view(3) end}, - {"mod-5", function() action.view(4) end}, - {"mod-6", function() action.view(5) end}, - {"mod-7", function() action.view(6) end}, - {"mod-8", function() action.view(7) end}, - {"mod-9", function() action.view(8) end}, - {"mod-C-1", function() action.swap_workspace(info.get_workspace(), 0) end}, - {"mod-C-2", function() action.swap_workspace(info.get_workspace(), 1) end}, - {"mod-C-3", function() action.swap_workspace(info.get_workspace(), 2) end}, - {"mod-C-4", function() action.swap_workspace(info.get_workspace(), 3) end}, - {"mod-C-5", function() action.swap_workspace(info.get_workspace(), 4) end}, - {"mod-C-6", function() action.swap_workspace(info.get_workspace(), 5) end}, - {"mod-C-7", function() action.swap_workspace(info.get_workspace(), 6) end}, - {"mod-C-8", function() action.swap_workspace(info.get_workspace(), 7) end}, - {"mod-C-9", function() action.swap_workspace(info.get_workspace(), 8) end}, - {"mod-S-1", function() action.move_container_to_workspace(0) end}, - {"mod-S-2", function() action.move_container_to_workspace(1) end}, - {"mod-S-3", function() action.move_container_to_workspace(2) end}, - {"mod-S-4", function() action.move_container_to_workspace(3) end}, - {"mod-S-5", function() action.move_container_to_workspace(4) end}, - {"mod-S-6", function() action.move_container_to_workspace(5) end}, - {"mod-S-7", function() action.move_container_to_workspace(6) end}, - {"mod-S-8", function() action.move_container_to_workspace(7) end}, - {"mod-S-9", function() action.move_container_to_workspace(8) end}, - {"mod-r", function() config.reload() end}, - {"mod-t", function() action.set_floating(false) end}, - -- {"mod-period", function() focusmon(1) end}, - -- {"mod-comma", function() focusmon(-1) end}, - -- {"mod-d", function() incnmaster(-1) end}, - {"M1", function() action.focus_container(info.get_container_under_cursor()) end}, - {"mod-M1", function() action.move_resize(info.cursor.mode.move) end}, - {"mod-M2", function() action.move_resize(info.cursor.mode.resize) end}, -}) --- print("execute finished") +config.bind_key("mod-p", function() action.exec("rofi -show run") end) +config.bind_key("mod-e", function() action.view(info.get_next_empty_workspace(info.get_workspace(), info.direction.right)) end) +config.bind_key("mod-period", function() action.toggle_workspace() end) +config.bind_key("mod-S-period", function() action.toggle_layout() end) +config.bind_key("mod-S-Return", function() action.exec(termcmd) end) +config.bind_key("mod-a", function() action.increase_nmaster() end) +config.bind_key("mod-x", function() action.decrease_nmaster() end) +config.bind_key("mod-k", function() action.focus_on_stack(-1) end) +config.bind_key("mod-j", function() action.focus_on_stack(1) end) +config.bind_key("mod-S-j", function() action.focus_on_hidden_stack(0) end) +config.bind_key("mod-S-k", function() action.focus_on_hidden_stack(-1) end) +config.bind_key("mod-S-c", function() action.kill(info.this_container_position()) end) +config.bind_key("mod-S-q", function() action.quit() end) +config.bind_key("mod-space", function() action.load_next_layout_in_set("default") end) +config.bind_key("mod-minus", function() action.move_to_scratchpad(info.this_container_position()) end) +config.bind_key("mod-S-minus", function() action.show_scratchpad() end) +config.bind_key("mod-S-space", function() action.load_prev_layout_in_set("default") end) +config.bind_key("mod-m", function() action.load_layout_in_set("default", 1) end) +config.bind_key("mod-S-t", function() action.load_layout_in_set("default", 2) end) +config.bind_key("mod-w", function() action.load_layout_in_set("default", 3) end) +config.bind_key("mod-S-w", function() action.load_layout_in_set("default", 4) end) +config.bind_key("mod-S-p", function() action.load_layout("tmp") end) +config.bind_key("mod-b", function() action.toggle_bars() end) +config.bind_key("mod-S-h", function() action.resize_main(-1/10) end) +config.bind_key("mod-S-l", function() action.resize_main(1/10) end) +config.bind_key("mod-S-s", function() action.write_this_overlay("tmp") end) +config.bind_key("mod-Return", function() action.zoom() end) +config.bind_key("mod-1", function() action.view(0) end) +config.bind_key("mod-2", function() action.view(1) end) +config.bind_key("mod-3", function() action.view(2) end) +config.bind_key("mod-4", function() action.view(3) end) +config.bind_key("mod-5", function() action.view(4) end) +config.bind_key("mod-6", function() action.view(5) end) +config.bind_key("mod-7", function() action.view(6) end) +config.bind_key("mod-8", function() action.view(7) end) +config.bind_key("mod-9", function() action.view(8) end) +config.bind_key("mod-C-1", function() action.tag_view(1 << 0) end) +config.bind_key("mod-C-2", function() action.tag_view(1 << 1) end) +config.bind_key("mod-C-3", function() action.tag_view(1 << 2) end) +config.bind_key("mod-C-4", function() action.tag_view(1 << 3) end) +config.bind_key("mod-C-5", function() action.tag_view(1 << 4) end) +config.bind_key("mod-C-6", function() action.tag_view(1 << 5) end) +config.bind_key("mod-C-7", function() action.tag_view(1 << 6) end) +config.bind_key("mod-C-8", function() action.tag_view(1 << 7) end) +config.bind_key("mod-C-9", function() action.tag_view(1 << 8) end) +config.bind_key("mod-S-1", function() action.move_container_to_workspace(0) end) +config.bind_key("mod-S-2", function() action.move_container_to_workspace(1) end) +config.bind_key("mod-S-3", function() action.move_container_to_workspace(2) end) +config.bind_key("mod-S-4", function() action.move_container_to_workspace(3) end) +config.bind_key("mod-S-5", function() action.move_container_to_workspace(4) end) +config.bind_key("mod-S-6", function() action.move_container_to_workspace(5) end) +config.bind_key("mod-S-7", function() action.move_container_to_workspace(6) end) +config.bind_key("mod-S-8", function() action.move_container_to_workspace(7) end) +config.bind_key("mod-S-9", function() action.move_container_to_workspace(8) end) +config.bind_key("mod-r", function() config.reload() end) +config.bind_key("mod-t", function() action.set_floating(false) end) +config.bind_key("mod-M1", function() action.move_resize(info.cursor.mode.move) end) +config.bind_key("mod-M2", function() action.move_resize(info.cursor.mode.resize) end) +config.bind_key("M1", function() action.focus_container(info.get_container_under_cursor()) end) diff --git a/config/layouts/spiral/init.lua b/config/layouts/spiral/init.lua index 77fed33f..6b6599ad 100644 --- a/config/layouts/spiral/init.lua +++ b/config/layouts/spiral/init.lua @@ -29,7 +29,7 @@ local layout_data = { {0.5, 0.0, 0.5, 0.5}, {0.75, 0.5, 0.25, 0.5}, {0.5, 0.75, 0.25, 0.25}, - {0.5, 0.5, 0.125, 0.25}, + {0.5, 0.5, 0.125, 0.25}, {0.625, 0.5, 0.125, 0.25}, } } diff --git a/config/layouts/three_columns/init.lua b/config/layouts/three_columns/init.lua index ce47b1af..5557e569 100644 --- a/config/layouts/three_columns/init.lua +++ b/config/layouts/three_columns/init.lua @@ -1,10 +1,10 @@ local layout_data = { { - {0.0, 0.0, 1, 1}, + {0.0, 0.0, 1.0, 1}, }, { - {0.0, 0.0, 0.50, 1}, - {0.50, 0.0, 0.5, 1}, + {0.0, 0.0, 0.5, 1}, + {0.5, 0.0, 0.5, 1}, }, { {0.0, 0.0, 0.5, 1}, @@ -22,7 +22,7 @@ local layout_data = { {0.55, 0.0, 0.225, 0.333}, {0.55, 0.333, 0.225, 0.333}, {0.55, 0.666, 0.225, 0.333}, - {0.775, 0.0, 0.225, 1.0}, + {0.775, 0.0, 0.225, 1}, }, { {0.0, 0.0, 0.55, 1}, @@ -44,8 +44,5 @@ local layout_data = { } layout.set("three_columns", layout_data) -l.config.set_master_layout_data( -{{{0, 0, 1, 1}}, {{0, 0, 0.5, 1}, {0.5, 0, 0.5, 1}}} -) l.config.set_resize_direction(info.direction.right) l.config.set_hidden_edges(info.direction.all) diff --git a/config/layouts/tmp/init.lua b/config/layouts/tmp/init.lua index fb71f650..5075cf5e 100644 --- a/config/layouts/tmp/init.lua +++ b/config/layouts/tmp/init.lua @@ -3,45 +3,122 @@ local layout_data = { {0.0, 0.0, 1, 1}, }, { - {0.0, 0.0, 0.55, 1}, - {0.55, 0.0, 0.45, 1}, + {0.0, 0.2, 1, 0.8}, + {0.0, 0.0, 1, 0.2}, }, { - {0.0, 0.0, 0.55, 1}, - {0.55, 0.0, 0.45, 0.5}, - {0.55, 0.5, 0.45, 0.5}, + {0.0, 0.2, 1, 0.6}, + {0.0, 0.0, 1, 0.2}, + {0.0, 0.8, 1, 0.2}, }, { - {0.0, 0.0, 0.55, 1}, - {0.55, 0.0, 0.45, 0.333}, - {0.55, 0.333, 0.45, 0.333}, - {0.55, 0.666, 0.45, 0.333}, + {0.2, 0.2, 0.8, 0.6}, + {0.2, 0.0, 0.8, 0.2}, + {0.0, 0.8, 1, 0.2}, + {0.0, 0.0, 0.2, 0.8}, }, { - {0.0, 0.0, 0.55, 1}, - {0.55, 0.0, 0.225, 0.333}, - {0.55, 0.333, 0.225, 0.333}, - {0.55, 0.666, 0.225, 0.333}, - {0.775, 0.0, 0.225, 1.0}, + {0.2, 0.2, 0.6, 0.6}, + {0.2, 0.0, 0.8, 0.2}, + {0.0, 0.8, 0.8, 0.2}, + {0.0, 0.0, 0.2, 0.8}, + {0.8, 0.2, 0.2, 0.8}, }, { - {0.0, 0.0, 0.55, 1}, - {0.55, 0.0, 0.225, 0.333}, - {0.55, 0.333, 0.225, 0.333}, - {0.55, 0.666, 0.225, 0.333}, - {0.775, 0.0, 0.225, 0.5}, - {0.775, 0.5, 0.225, 0.5}, + {0.2, 0.2, 0.6, 0.6}, + {0.2, 0.1, 0.8, 0.1}, + {0.0, 0.8, 0.8, 0.2}, + {0.0, 0.1, 0.2, 0.7}, + {0.8, 0.2, 0.2, 0.8}, + {0.0, 0.0, 1, 0.1}, }, { - {0.0, 0.0, 0.55, 1}, - {0.55, 0.0, 0.225, 0.333}, - {0.55, 0.333, 0.225, 0.333}, - {0.55, 0.666, 0.225, 0.333}, - {0.775, 0.0, 0.225, 0.333}, - {0.775, 0.333, 0.225, 0.333}, - {0.775, 0.666, 0.225, 0.333}, - } + {0.2, 0.2, 0.6, 0.6}, + {0.2, 0.1, 0.8, 0.1}, + {0.0, 0.8, 0.8, 0.1}, + {0.0, 0.1, 0.2, 0.7}, + {0.8, 0.2, 0.2, 0.7}, + {0.0, 0.0, 1, 0.1}, + {0.0, 0.9, 1, 0.1}, + }, + { + {0.2, 0.2, 0.6, 0.6}, + {0.2, 0.1, 0.8, 0.1}, + {0.1, 0.8, 0.7, 0.1}, + {0.1, 0.1, 0.1, 0.7}, + {0.8, 0.2, 0.2, 0.7}, + {0.1, 0.0, 0.9, 0.1}, + {0.0, 0.9, 1, 0.1}, + {0.0, 0.0, 0.1, 0.9}, + }, + { + {0.2, 0.2, 0.6, 0.6}, + {0.2, 0.1, 0.7, 0.1}, + {0.1, 0.8, 0.7, 0.1}, + {0.1, 0.1, 0.1, 0.7}, + {0.8, 0.2, 0.1, 0.7}, + {0.1, 0.0, 0.9, 0.1}, + {0.0, 0.9, 0.9, 0.1}, + {0.0, 0.0, 0.1, 0.9}, + {0.9, 0.1, 0.1, 0.9}, + }, + { + {0.2, 0.2, 0.6, 0.6}, + {0.2, 0.1, 0.7, 0.1}, + {0.1, 0.8, 0.7, 0.1}, + {0.1, 0.1, 0.1, 0.7}, + {0.8, 0.2, 0.1, 0.7}, + {0.1, 0.0, 0.45, 0.1}, + {0.0, 0.9, 0.9, 0.1}, + {0.0, 0.0, 0.1, 0.9}, + {0.9, 0.1, 0.1, 0.9}, + {0.55, 0.0, 0.45, 0.1}, + }, + { + {0.2, 0.2, 0.6, 0.6}, + {0.2, 0.1, 0.7, 0.1}, + {0.1, 0.8, 0.7, 0.1}, + {0.1, 0.1, 0.1, 0.7}, + {0.8, 0.2, 0.1, 0.7}, + {0.1, 0.0, 0.45, 0.1}, + {0.0, 0.9, 0.45, 0.1}, + {0.0, 0.0, 0.1, 0.9}, + {0.9, 0.1, 0.1, 0.9}, + {0.55, 0.0, 0.45, 0.1}, + {0.45, 0.9, 0.45, 0.1}, + }, + { + {0.2, 0.2, 0.6, 0.6}, + {0.2, 0.1, 0.7, 0.1}, + {0.1, 0.8, 0.7, 0.1}, + {0.1, 0.1, 0.1, 0.7}, + {0.8, 0.2, 0.1, 0.7}, + {0.1, 0.0, 0.45, 0.1}, + {0.0, 0.9, 0.45, 0.1}, + {0.0, 0.0, 0.1, 0.45}, + {0.9, 0.1, 0.1, 0.9}, + {0.55, 0.0, 0.45, 0.1}, + {0.45, 0.9, 0.45, 0.1}, + {0.0, 0.45, 0.1, 0.45}, + }, + { + {0.2, 0.2, 0.6, 0.6}, + {0.2, 0.1, 0.7, 0.1}, + {0.1, 0.8, 0.7, 0.1}, + {0.1, 0.1, 0.1, 0.7}, + {0.8, 0.2, 0.1, 0.7}, + {0.1, 0.0, 0.45, 0.1}, + {0.0, 0.9, 0.45, 0.1}, + {0.0, 0.0, 0.1, 0.45}, + {0.9, 0.1, 0.1, 0.45}, + {0.55, 0.0, 0.45, 0.1}, + {0.45, 0.9, 0.45, 0.1}, + {0.0, 0.45, 0.1, 0.45}, + {0.9, 0.55, 0.1, 0.45}, + }, } layout.set("tmp", layout_data) -l.config.set_resize_direction(info.direction.right) +l.config.set_resize_data({{2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}}) +l.config.set_resize_direction(info.direction.all) +l.config.set_resize_function(Resize_all) diff --git a/config/layouts/two_pane/init.lua b/config/layouts/two_pane/init.lua index b3ef707e..1fb8caa6 100644 --- a/config/layouts/two_pane/init.lua +++ b/config/layouts/two_pane/init.lua @@ -17,7 +17,7 @@ local function update(n) end layout.set("two_pane", layout_data) -l.event.set_update_function(update) +l.event.add_listener("on_update", update) l.config.set_master_layout_data( {{{0, 0, 1, 1}}, {{0, 0, 0.5, 1}, {0.5, 0, 0.5, 1}}} ) diff --git a/config/tile.lua b/config/tile.lua index f9ae060f..1f3d2a75 100644 --- a/config/tile.lua +++ b/config/tile.lua @@ -5,149 +5,317 @@ local Y = 2 local WIDTH = 3 local HEIGHT = 4 -function Is_equally_affected_by_resize_of(container, container2, d) - local resize = false - if d == info.direction.top then - resize = Is_approx_equal(container2[Y], container[Y]) - elseif d == info.direction.bottom then - resize = Is_approx_equal(container2[Y] + container2[HEIGHT], container[Y] + container[HEIGHT]) - elseif d == info.direction.left then - resize = Is_approx_equal(container2[X], container[X]) - elseif d == info.direction.right then - resize = Is_approx_equal(container2[X] + container2[WIDTH], container[X] + container[WIDTH]) +-- val is between 0 and 1 and represents how far +local function abs_container_to_relative(con, ref_area) + con[X] = con[X] / ref_area[WIDTH] - ref_area[X] + con[Y] = con[Y] / ref_area[HEIGHT] - ref_area[Y] + con[WIDTH] = con[WIDTH] / ref_area[WIDTH] + con[HEIGHT] = con[HEIGHT] / ref_area[HEIGHT] +end + +local function relative_container_to_abs(con, ref_area) + con[X] = con[X] * ref_area[WIDTH] + ref_area[X] + con[Y] = con[Y] * ref_area[HEIGHT] + ref_area[Y] + con[WIDTH] = con[WIDTH] * ref_area[WIDTH] + con[HEIGHT] = con[HEIGHT] * ref_area[HEIGHT] +end + +local function create_container(x, y, width, height) + local con = {} + con[X] = x + con[Y] = y + con[WIDTH] = width + con[HEIGHT] = height + return con +end + +local function get_area_at_zero(area) + local x = 0 + local y = 0 + local width = area[WIDTH] + local height = area[HEIGHT] + local con = create_container(x, y, width, height) + return con +end + +local function get_area_local_con(con, area) + local x1 = con[X] - area[X] + local y1 = con[Y] - area[Y] + local x2 = con[X] + con[WIDTH] - area[X] + local y2 = con[Y] + con[HEIGHT] - area[Y] + local local_con = create_container(x1, y1, x2-x1, y2-y1) + return local_con +end + +local function get_global_con(con, area) + local x1 = con[X] + area[X] + local y1 = con[Y] + area[Y] + local x2 = con[X] + con[WIDTH] + area[X] + local y2 = con[Y] + con[HEIGHT] + area[Y] + local local_con = create_container(x1, y1, x2-x1, y2-y1) + return local_con +end + +local function assign_con_values(con, new_con) + con[X] = new_con[X] + con[Y] = new_con[Y] + con[WIDTH] = new_con[WIDTH] + con[HEIGHT] = new_con[HEIGHT] +end + +local function change_base(con, old_area, new_area) + local local_old_con = get_area_local_con(con, old_area); + local zero_old_area = get_area_at_zero(old_area) + local zero_new_area = get_area_at_zero(new_area) + abs_container_to_relative(local_old_con, zero_old_area) + relative_container_to_abs(local_old_con, zero_new_area) + + local new_con = get_global_con(local_old_con, new_area) + assign_con_values(con, new_con) +end + +local function merge_boxes(box1, box2) + if (box1 == nil) then + return Deep_copy(box2) end - return resize -end - --- finds containers that are affected by the container at i,j -function Get_resize_affected_containers(layout_data, o_layout_data, i, j, d, get_container_func, is_effected_by_func) - local container = layout_data[i][j] - local list = {} - - for j2 = 1, #layout_data[i] do - local con = layout_data[i][j2] - local alt_con = get_container_func(container, d) - - if j ~= j2 then - if is_effected_by_func(o_layout_data[i][j], o_layout_data[i][j2], d) then - -- convert relative to absolute box - local ret_con = {con[X], con[Y], con[WIDTH], con[HEIGHT], i, j2} - ret_con[X] = (ret_con[X]-alt_con[X])/alt_con[WIDTH] - ret_con[Y] = (ret_con[Y]-alt_con[Y])/alt_con[HEIGHT] - ret_con[WIDTH] = ret_con[WIDTH]/alt_con[WIDTH] - ret_con[HEIGHT] = ret_con[HEIGHT]/alt_con[HEIGHT] - table.insert(list, ret_con) - end - end + if (box2 == nil) then + return Deep_copy(box1) end - return list -end + local x1 = math.min(box1[X], box2[X]) + local y1 = math.min(box1[Y], box2[Y]) + local x2 = math.max(box1[X] + box1[WIDTH], box2[X] + box2[WIDTH]) + local y2 = math.max(box1[Y] + box1[HEIGHT], box2[Y] + box2[HEIGHT]) -function Move_this_container(n, d) - -- local i = math.max(math.min(action.get_this_container_count(), #Layout_data), 1) - -- local j = math.min(info.this_container_position(), #Layout_data[i]) - -- local container = Layout_data[i][j] - -- Layout_data[i][j] = Move_container(container, n, d) - -- action.arrange() + local con = create_container(x1, y1, x2 - x1, y2 - y1) + return con end -function Resize_this_container(n, d) - -- local i = math.max(math.min(action.get_this_container_count(), #Layout_data), 1) - -- local j = math.min(action.client_pos(), #Layout_data[i]) - -- Layout_data[i][j] = Resize_container(Layout_data[i][j], n, d) - -- action.arrange() +local function join_containers(con1, con2, con3) + local res_container = merge_boxes(con1, con2) + res_container = merge_boxes(res_container, con3) + return res_container end --- --- returns whether container2 is affected -function Is_affected_by_resize_of(container, container2, d) - local resize = false +local function intersection_of(con1, con2) + local x1 = math.max(con1[X], con2[X]) + local y1 = math.max(con1[Y], con2[Y]) + local x2 = math.min(con1[X] + con1[WIDTH], con2[X] + con2[WIDTH]) + local y2 = math.min(con1[Y] + con1[HEIGHT], con2[Y] + con2[HEIGHT]) + local con = create_container(x1, y1, x2 - x1, y2 - y1) + + if (con[WIDTH] <= 0) then + return nil + end + if (con[HEIGHT] <= 0) then + return nil + end + return con +end - if d == info.direction.top then - local right = Is_container_right_to(container, container2) - local left = Is_container_left_to(container, container2) - local container_is_higher = Is_container_over(container, container2) +local function split_container(con, unaffected_area, old_alpha_area, old_beta_area) + local non_affected_con = intersection_of(con, unaffected_area) + local alpha_con = intersection_of(con, old_alpha_area) + local beta_con = intersection_of(con, old_beta_area) + return non_affected_con, alpha_con, beta_con +end - resize = container_is_higher and not (left or right) - elseif d == info.direction.bottom then - local right = Is_container_right_to(container, container2) - local left = Is_container_left_to(container, container2) - local container_is_lower = Is_container_under(container, container2) +local function apply_resize(lt_data_el, old_unaffected_area, old_alpha_area, new_alpha_area, old_beta_area, new_beta_area) + -- local i = 5 + for i = 1,#lt_data_el do + local con = lt_data_el[i]; - resize = container_is_lower and not (left or right) - elseif d == info.direction.left then - local over = Is_container_over(container, container2) - local under = Is_container_under(container, container2) - local container_is_left = Is_container_left_to(container, container2) + local unaffected_con, alpha_con, beta_con = split_container(con, old_unaffected_area, old_alpha_area, old_beta_area); - resize = container_is_left and not (over or under) - elseif d == info.direction.right then - local over = Is_container_over(container, container2) - local under = Is_container_under(container, container2) - local container_is_right = Is_container_right_to(container, container2) + if (alpha_con ~= nil) then + change_base(alpha_con, old_alpha_area, new_alpha_area) + end + if (beta_con ~= nil) then + change_base(beta_con, old_beta_area, new_beta_area) + end - resize = container_is_right and not (over or under) + lt_data_el[i] = join_containers(unaffected_con, alpha_con, beta_con) end +end - return resize +local function get_cissor_container_left(alpha_area) + local x1 = 0 + local y1 = 0 + local x2 = alpha_area[X] + local y2 = 1 + local con = create_container(x1, y1, x2-x1, y2-y1) + return con end --- TODO refactor and simplify -function Resize_all(lt_data, o_layout_data, i, j, n, d) - if i > #lt_data then - return lt_data +local function get_cissor_container_top(alpha_area) + local x1 = 0 + local y1 = 0 + local x2 = 1 + local y2 = alpha_area[Y] + local con = create_container(x1, y1, x2-x1, y2-y1) + return con +end + +local function get_cissor_container_bottom(alpha_area) + local x1 = 0 + local y1 = alpha_area[Y] + alpha_area[HEIGHT] + local x2 = 1 + local y2 = 1 + local con = create_container(x1, y1, x2-x1, y2-y1) + return con +end + +local function get_cissor_container_right(alpha_area) + local x1 = alpha_area[X] + alpha_area[WIDTH] + local y1 = 0 + local x2 = 1 + local y2 = 1 + local con = create_container(x1, y1, x2-x1, y2-y1) + return con +end + +local function get_opposite_direction(dir) + if dir == info.direction.left then + return info.direction.right + elseif dir == info.direction.right then + return info.direction.left + elseif dir == info.direction.top then + return info.direction.bottom + elseif dir == info.direction.bottom then + return info.direction.top end - if j > #lt_data[i] then - return lt_data +end + +local function get_cissor_container(alpha_area, dir) + local area = nil + if dir == info.direction.left then + area = get_cissor_container_left(alpha_area) + elseif dir == info.direction.right then + area = get_cissor_container_right(alpha_area) + elseif dir == info.direction.top then + area = get_cissor_container_top(alpha_area) + elseif dir == info.direction.bottom then + area = get_cissor_container_bottom(alpha_area) end + return area +end - local directions = Get_directions(d) - local layout_data = Deep_copy(lt_data) - local main_con = layout_data[i][j] +local function get_beta_area(alpha_area, dir) + local area = get_cissor_container(alpha_area, dir) + return area +end - if Is_resize_locked(layout_data, o_layout_data, i, j, n, directions) then - return layout_data +local function get_unaffected_area(old_alpha_area, dir) + local opposite_direction = get_opposite_direction(dir) + local area = get_beta_area(old_alpha_area, opposite_direction) + return area +end + +local function get_alpha_container_horizontal(con) + local x1 = con[X] + local y1 = 0 + local x2 = con[X] + con[WIDTH] + local y2 = 1 + local area = create_container(x1, y1, x2-x1, y2-y1) + return area +end + +local function get_alpha_container_vertical(con) + local x1 = 0 + local y1 = con[Y] + local x2 = 1 + local y2 = con[Y] + con[HEIGHT] + local area = create_container(x1, y1, x2-x1, y2-y1) + return area +end + +local function get_alpha_area_from_container(con, dir) + local area = nil + if dir == info.direction.left or dir == info.direction.right then + area = get_alpha_container_horizontal(con) + elseif dir == info.direction.top or dir == info.direction.bottom then + area = get_alpha_container_vertical(con) end + return area +end - -- apply +local function is_invalid(con) + if con[WIDTH] < 0 then + return true + end + if con[HEIGHT] < 0 then + return true + end + return false +end + +local function apply_resize_function(lt_data_el, o_lt_data_el, i, n, directions) for x = 1,#directions do local dir = directions[x] - local resize_main_containers = Get_resize_affected_containers(layout_data, o_layout_data, i, j, dir, Get_main_container, Is_equally_affected_by_resize_of) - local resize_containers = Get_resize_affected_containers(layout_data, o_layout_data, i, j, dir, Get_alternative_container, Is_affected_by_resize_of) - main_con = Deep_copy(Move_resize(main_con, 0, n, dir)) - local alt_con = Get_alternative_container(main_con, dir) - - for k = 1,#resize_containers do - local li = resize_containers[k][5] - local lj = resize_containers[k][6] - - layout_data[li][lj][X] = alt_con[X] + (resize_containers[k][X] * alt_con[WIDTH]) - layout_data[li][lj][Y] = alt_con[Y] + (resize_containers[k][Y] * alt_con[HEIGHT]) - layout_data[li][lj][WIDTH] = resize_containers[k][WIDTH] * alt_con[WIDTH] - layout_data[li][lj][HEIGHT] = resize_containers[k][HEIGHT] * alt_con[HEIGHT] - end - Deep_copy(main_con, layout_data[i][j]) - for k = 1,#resize_main_containers do - local li = resize_main_containers[k][5] - local lj = resize_main_containers[k][6] - layout_data[li][lj] = Move_resize(layout_data[li][lj], 0, n, dir) + local old_alpha_area = get_alpha_area_from_container(lt_data_el[i], dir) + local new_alpha_area = Move_resize(old_alpha_area, 0, n, dir) + + local old_beta_area = get_beta_area(old_alpha_area, dir) + local new_beta_area = get_beta_area(new_alpha_area, dir) + local old_unaffected_area = get_unaffected_area(old_alpha_area, dir) + + if is_invalid(new_alpha_area) or is_invalid(new_beta_area) then + return end + + apply_resize(lt_data_el, old_unaffected_area, old_alpha_area, new_alpha_area, old_beta_area, new_beta_area) end - return layout_data end -function Resize_main_all(layout_data, o_layout_data, resize_data, n, d) - local i = math.max(math.min(info.get_this_container_count(), #o_layout_data), 1) - for g=1,#resize_data do - for h=1,#resize_data[g] do - if i == resize_data[g][h] then - for j=1,#resize_data[g] do - layout_data = Resize_all(layout_data, o_layout_data, resize_data[g][j], 1, n, d) - end - return layout_data +-- TODO refactor and simplify +local function resize_all(lt_data_el, o_layout_data_el, i, n, d) + if i > #lt_data_el then + return lt_data_el + end + + local directions = Get_directions(d) + local layout_data_element = Deep_copy(lt_data_el) + + -- if Is_resize_locked(layout_data_element, o_layout_data_el, i, n, directions) then + -- return layout_data_element + -- end + + apply_resize_function(layout_data_element, o_layout_data_el, i, n, directions) + + return layout_data_element +end + +local function get_layout_data_element_id(o_layout_data) + return math.max(math.min(info.get_this_container_count(), #o_layout_data), 1) +end + +-- returns 0 if not found +local function get_layout_element(layout_data_element_id, resize_data) + for j=1,#resize_data do + local resize_data_element = resize_data[j] + for h=1, #resize_data[j] do + if layout_data_element_id == resize_data_element[h] then + return j end end end + return 0 +end + +function Resize_main_all(layout_data, o_layout_data, resize_data, n, direction) + local layout_data_element_id = get_layout_data_element_id(o_layout_data) + local layout_id = get_layout_element(layout_data_element_id, resize_data) + if layout_id == 0 then + return layout_data + end + + local resize_element = resize_data[layout_id] + for i=1,#resize_element do + local id = resize_element[i] + if id <= #o_layout_data then + -- local id = 5 + layout_data[id] = resize_all(layout_data[id], o_layout_data[id], 1, n, direction) + end + end return layout_data end diff --git a/config/tileutils.lua b/config/tileutils.lua index 162f78e5..d63bdaaa 100644 --- a/config/tileutils.lua +++ b/config/tileutils.lua @@ -3,47 +3,19 @@ local Y = 2 local WIDTH = 3 local HEIGHT = 4 -local function _copy(obj, target) - local n = 0 - -- clear target table - for k,v in pairs(target) do - if type(v) == "table" then - v.__del = true - n = n + 1 - else - target[k] = nil +function Deep_copy(orig) + local orig_type = type(orig) + local copy + if orig_type == 'table' then + copy = {} + for orig_key, orig_value in next, orig, nil do + copy[Deep_copy(orig_key)] = Deep_copy(orig_value) end + setmetatable(copy, Deep_copy(getmetatable(orig))) + else -- number, string, boolean, etc + copy = orig end - -- copy obj into target - for k,v in pairs(obj) do - if type(v) == "table" then - local t = target[k] - if t then - t.__del = nil - n = n - 1 - else - t = {} - target[k] = t - end - _copy(v, t) - else - target[k] = v - end - end - -- clear no use sub table in target - if n > 0 then - for k,v in pairs(target) do - if type(v) == "table" and v.__del then - target[k] = nil - end - end - end -end - -function Deep_copy(obj, target) - target = target or {} - _copy(obj, target) - return target + return copy end -- set: which window conf set @@ -63,13 +35,13 @@ function Move_container(container, n, d) end -- TODO this is a mess fix it!! -function Is_resize_locked(layout_data, o_layout_data, i, j, n, directions) - local lt_data = Deep_copy(layout_data) - local main_con = lt_data[i][j] +function Is_resize_locked(layout_data_el, o_layout_data_el, i, n, directions) + local lt_data_el = Deep_copy(layout_data_el) + local main_con = lt_data_el[i] for x = 1,#directions do local dir = directions[x] - local resize_containers = Get_resize_affected_containers(lt_data, o_layout_data, i, j, dir, Get_alternative_container, Is_affected_by_resize_of) + local resize_containers = Get_resize_affected_containers(lt_data_el, o_layout_data_el, i, dir, Get_alternative_container, Is_affected_by_resize_of) main_con = Deep_copy(Move_resize(main_con, 0, n, dir)) local alt_con = Get_alternative_container(main_con, dir) @@ -78,9 +50,8 @@ function Is_resize_locked(layout_data, o_layout_data, i, j, n, directions) end for k = 1,#resize_containers do - local li = resize_containers[k][5] - local lj = resize_containers[k][6] - local c = lt_data[li][lj] + local lj = resize_containers[k][5] + local c = lt_data_el[lj] c[X] = alt_con[X] + (resize_containers[k][X] * alt_con[WIDTH]) c[Y] = alt_con[Y] + (resize_containers[k][Y] * alt_con[HEIGHT]) @@ -118,71 +89,6 @@ function Move_resize(container, nmove, nresize, d) return con end --- n: number --- d: amount of digits -function Floor(n, d) - if (n < 1e-10) then return 0 end - local power = 10^d - return math.floor(n * power) / power -end - -function Is_approx_equal(a, b) - return math.abs(a - b) < 0.001 -end - -function Get_current_container(container, d) - local alt = {0, 0, 1, 1} - if d == info.direction.top then - alt[Y] = 0 - alt[HEIGHT] = container[Y] - elseif d == info.direction.bottom then - alt[Y] = container[Y] + container[HEIGHT] - alt[HEIGHT] = 1 - (container[Y] + container[HEIGHT]) - elseif d == info.direction.left then - alt[X] = 0 - alt[WIDTH] = container[X] - elseif d == info.direction.right then - alt[X] = container[X] + container[WIDTH] - alt[WIDTH] = 1 - alt[X] - end - return alt -end - -function Is_container_right_to(ref_con, con) - local right = con[X] > ref_con[X] + ref_con[WIDTH] - right = right or Is_approx_equal(con[X], ref_con[X] + ref_con[WIDTH]) - return right -end - -function Is_container_left_to(ref_con, con) - local left = con[X] + con[WIDTH] < ref_con[X] - left = left or Is_approx_equal(con[X] + con[WIDTH], ref_con[X]) - return left -end - -function Is_container_over(ref_con, con) - local over = con[Y] + con[HEIGHT] < ref_con[Y] - over = over or Is_approx_equal(con[Y] + con[HEIGHT], ref_con[Y]) - return over -end - -function Is_container_under(ref_con, con) - local under = con[Y] > ref_con[Y] + ref_con[HEIGHT] - under = under or Is_approx_equal(con[Y], ref_con[Y] + ref_con[HEIGHT]) - return under -end - -function Does_container_not_intersect_with(ref_con, con) - return Is_container_left_to(ref_con, con) - or Is_container_right_to(ref_con, con) - or Is_container_under(ref_con, con) - or Is_container_over(ref_con, con) -end - -function Load_layout(layout_name) - action.load_layout(layout_name) -end - -- put all directions into a list function Get_directions(d) local list = {} @@ -204,69 +110,3 @@ function Get_directions(d) end return list end - --- if d == info.direction.left then "raytrace" to the left like that and return the --- geometry of that area --- +--------------------------+ --- |< - - - - +---------+ | --- || | | | --- || a | o | | --- || | | | --- |< - - - - +---------+ | --- +--------------------------+ --- where w is the original window and a is the alternative window -function Get_alternative_container(container, d) - local alt_con = {0, 0, 1, 1} - if d == info.direction.top then - alt_con[Y] = 0 - alt_con[HEIGHT] = container[Y] - elseif d == info.direction.bottom then - alt_con[Y] = container[Y] + container[HEIGHT] - alt_con[HEIGHT] = 1 - alt_con[Y] - elseif d == info.direction.left then - alt_con[X] = 0 - alt_con[WIDTH] = container[X] - elseif d == info.direction.right then - alt_con[X] = container[X] + container[WIDTH] - alt_con[WIDTH] = 1 - alt_con[X] - end - return alt_con -end - -function Get_main_container(container, d) - local con = {container[X], container[Y], container[WIDTH], container[HEIGHT]} - if d == info.direction.top then - con[X] = 0 - con[WIDTH] = 1 - elseif d == info.direction.bottom then - con[X] = 0 - con[WIDTH] = 1 - elseif d == info.direction.left then - con[Y] = 0 - con[HEIGHT] = 1 - elseif d == info.direction.right then - con[Y] = 0 - con[HEIGHT] = 1 - end - return con -end - -function Get_edge_container(container, d) - local con = {container[X], container[Y], container[WIDTH], container[HEIGHT]} - if d == info.direction.top then - con[X] = 0 - con[WIDTH] = 1 - elseif d == info.direction.bottom then - con[X] = 0 - con[WIDTH] = 1 - elseif d == info.direction.left then - con[Y] = 0 - con[HEIGHT] = 1 - elseif d == info.direction.right then - con[X] = container[X] + container[WIDTH] - con[Y] = container[Y] + container[HEIGHT] - con[WIDTH] = 1 - con[X] - con[HEIGHT] = 1 - con[Y] - end - return con -end diff --git a/include/bitset/bitset.h b/include/bitset/bitset.h new file mode 100644 index 00000000..cd0dd30b --- /dev/null +++ b/include/bitset/bitset.h @@ -0,0 +1,120 @@ +#ifndef BITSET_H +#define BITSET_H + +#include +#include +#include + +#include "utils/vector.h" + +/****************** DEFINTIIONS ******************/ + +#define BITSET_ERROR VECTOR_ERROR +#define BITSET_SUCCESS VECTOR_SUCCESS + +#define BITSET_INITIALIZER \ + { VECTOR_INITIALIZER, 0 } + +typedef void (*bit_operator_t)(bool*, const bool*); + +/****************** STRUCTURES ******************/ + +typedef struct BitSet { + Vector *bits; + size_t size; +} BitSet; + +/****************** INTERFACE ******************/ + +/* Setup */ +BitSet *bitset_create(size_t minimum_number_of_bits); + +BitSet* bitset_copy(BitSet* source); +int bitset_move(BitSet* destination, BitSet* source); +int bitset_swap(BitSet* destination, BitSet* source); + +void bitset_destroy(BitSet* bitset); + +/* Factory */ +BitSet *bitset_from_value(uint64_t value); + +/* Logical Operations */ +int byte_wise_operation(BitSet* destination, + const BitSet* source, + bit_operator_t byte_operation); + +int bitset_and(BitSet* destination, const BitSet* source); +int bitset_or(BitSet* destination, const BitSet* source); +int bitset_xor(BitSet* destination, const BitSet* source); +int bitset_flip(BitSet* bitset); + +/* Access */ +int bitset_set(BitSet* bitset, size_t index); +int bitset_reset(BitSet* bitset, size_t index); +int bitset_assign(BitSet* bitset, size_t index, bool value); +int bitset_toggle(BitSet* bitset, size_t index); + +int bitset_test(const BitSet* bitset, size_t index); +const uint8_t* byte_const_get(const BitSet* bitset, size_t index); +uint8_t* byte_get(BitSet* bitset, size_t index); + +int bitset_msb(BitSet* bitset); +int bitset_lsb(BitSet* bitset); + +int bitset_reset_all(BitSet* bitset); +int bitset_set_all(BitSet* bitset); +int bitset_set_all_to_mask(BitSet* bitset, uint8_t mask); +int bitset_clear(BitSet* bitset); + +/* Size Management */ +int bitset_push(BitSet* bitset, bool value); +int bitset_push_one(BitSet* bitset); +int bitset_push_zero(BitSet* bitset); +int bitset_pop(BitSet* bitset); + +/* Capacity Management */ +int bitset_reserve(BitSet* bitset, size_t minimum_number_of_bits); +int bitset_grow(BitSet* bitset); +int bitset_shrink(BitSet* bitset); + +/* Information */ +bool bitset_is_initialized(const BitSet* bitset); +size_t bitset_capacity(const BitSet* bitset); +size_t bitset_size_in_bytes(const BitSet* bitset); + +int bitset_count(BitSet* bitset); +int bitset_all(BitSet* bitset); +int bitset_any(BitSet* bitset); +int bitset_none(BitSet* bitset); + +/* Debugging */ +void print_bitset(BitSet *bitset); + +/****************** PRIVATE ******************/ + +#define LAST_BIT_INDEX(value) ((sizeof(value) * 8) - 1) +#define LAST_BIT_MASK(value) (1ULL << LAST_BIT_INDEX(value)) +#define LAST_BIT(value) \ + ((value & LAST_BIT_MASK(value)) >> LAST_BIT_INDEX(value)) + +#define CEIL(x) ((x == ((int)(x))) ? x : ((int)(x)) + 1) +#define BITS_TO_BYTES(bits) CEIL((bits) / 8.0) + +/* Popcount Masks */ +#define POPCOUNT_MASK1 0x55 +#define POPCOUNT_MASK2 0x33 +#define POPCOUNT_MASK3 0x0F + +uint8_t _byte_popcount(uint8_t value); + +int _bitset_increment_size(BitSet* bitset); + +void _bit_and(bool* first, const bool* second); +void _bit_or(bool* first, const bool* second); +void _bit_xor(bool* first, const bool* second); + +size_t _byte_index(size_t index); +uint8_t _bitset_index(size_t index); +uint8_t _bitset_offset(size_t index); + +#endif /* BITSET_H */ diff --git a/include/client.h b/include/client.h index 7a07e838..ef1051a5 100644 --- a/include/client.h +++ b/include/client.h @@ -4,6 +4,8 @@ #include #include +#include "seat.h" + enum shell { XDG_SHELL, X11_MANAGED, X11_UNMANAGED, LAYER_SHELL }; /* client types */ union surface_t { struct wlr_xdg_surface *xdg; @@ -12,11 +14,13 @@ union surface_t { }; struct client { - float ratio; /* containers containing this client */ struct container *con; union surface_t surface; + struct monitor *m; + struct wl_listener activate; + struct wl_listener commit; struct wl_listener set_title; struct wl_listener set_app_id; struct wl_listener map; @@ -40,20 +44,15 @@ struct client { struct client *create_client(enum shell shell_type, union surface_t surface); void destroy_client(struct client *c); -void focus_client(struct client *old, struct client *c); +void focus_client(struct seat *seat, struct client *old, struct client *c); +void focus_surface(struct seat *seat, struct wlr_surface *surface); void client_setsticky(struct client *c, bool sticky); void reset_tiled_client_borders(int border_bx); void reset_floating_client_borders(int border_px); void kill_client(struct client *c); -bool wants_floating(struct client *c); -bool is_popup_menu(struct client *c); - float calc_ratio(float width, float height); -void destroy_notify(struct wl_listener *listener, void *data); -void maprequest(struct wl_listener *listener, void *data); -void unmap_notify(struct wl_listener *listener, void *data); void client_handle_set_title(struct wl_listener *listener, void *data); void client_handle_set_app_id(struct wl_listener *listener, void *data); diff --git a/include/clipboard.h b/include/clipboard.h deleted file mode 100644 index a003ece5..00000000 --- a/include/clipboard.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef CLIPBOARD_H -#define CLIPBOARD_H - -#include - - -void set_primary_selection(struct wl_listener *listener, void *data); -void set_selection(struct wl_listener *listener, void *data); - -extern struct wl_listener request_set_psel; -extern struct wl_listener request_set_sel; -#endif /* CLIPBOARD_H */ diff --git a/include/command.h b/include/command.h index ef3f27c3..bc544813 100644 --- a/include/command.h +++ b/include/command.h @@ -1,6 +1,58 @@ #ifndef COMMAND_H #define COMMAND_H -void execute_command(const char *cmd); +#include "container.h" + +typedef struct cmd_results *sway_cmd(int argc, char **argv); + +struct cmd_handler { + char *command; + sway_cmd *handle; +}; + +/** + * Indicates the result of a command's execution. + */ +enum cmd_status { + CMD_SUCCESS, /**< The command was successful */ + CMD_FAILURE, /**< The command resulted in an error */ + CMD_INVALID, /**< Unknown command or parser error */ + CMD_DEFER, /**< Command execution deferred */ + CMD_BLOCK, + CMD_BLOCK_COMMANDS, + CMD_BLOCK_END +}; + +/** + * Stores the result of executing a command. + */ +struct cmd_results { + enum cmd_status status; + /** + * Human friendly error message, or NULL on success + */ + char *error; +}; + +enum expected_args { + EXPECTED_AT_LEAST, + EXPECTED_AT_MOST, + EXPECTED_EQUAL_TO +}; + +/** + * Allocates a cmd_results object. + */ +struct cmd_results *cmd_results_new(enum cmd_status status, const char *error, ...); +/** + * Frees a cmd_results object. + */ +void free_cmd_results(struct cmd_results *results); + +struct cmd_results *execute_command(char *cmd, struct wlr_seat *seat, + struct container *con); +char *cmd_results_to_json(struct cmd_results *results); + +struct cmd_results *cmd_eval(const char *cmd); #endif /* COMMAND_H */ diff --git a/include/command/commands.h b/include/command/commands.h new file mode 100644 index 00000000..f898c954 --- /dev/null +++ b/include/command/commands.h @@ -0,0 +1,6 @@ +#ifndef KILL_H +#define KILL_H + +struct cmd_results *cmd_create_output(int argc, char **argv); + +#endif /* KILL_H */ diff --git a/include/container.h b/include/container.h index f1dccb1e..9b7943e9 100644 --- a/include/container.h +++ b/include/container.h @@ -7,26 +7,17 @@ #include "client.h" #include "options.h" - -enum focus_actions { - FOCUS_NOOP, - FOCUS_LIFT, -}; +#include "monitor.h" struct container { - /* sticky containers */ - struct wl_list stlink; - - struct wl_listener commit; - /* layout-relative, includes border */ struct wlr_box geom; struct wlr_box prev_geom; struct wlr_box prev_floating_geom; struct client *client; - struct monitor *m; - struct monitor *prev_m; + bool is_xwayland_popup; + bool is_tiled; bool floating; bool focusable; bool has_border; @@ -44,6 +35,9 @@ struct container *create_container(struct client *c, struct monitor *m, bool has void destroy_container(struct container *con); +void add_container_to_tile(struct container *con); +void remove_container_from_tile(struct container *con); + struct container *get_focused_container(struct monitor *m); struct container *xy_to_container(double x, double y); struct container *get_container_on_focus_stack(int ws_id, int position); @@ -56,13 +50,12 @@ struct wlr_box get_monitor_local_box(struct wlr_box box, struct monitor *m); struct wlr_fbox lua_togeometry(lua_State *L); void apply_bounds(struct container *con, struct wlr_box bbox); -void apply_rules(struct container *con); void commit_notify(struct wl_listener *listener, void *data); void container_damage_borders(struct container *con, struct wlr_box *geom); void container_damage_part(struct container *con); void container_damage_whole(struct container *con); -void fix_position(struct container *con); -void focus_container(struct container *con, enum focus_actions a); +void container_fix_position(struct container *con); +void focus_container(struct container *con); void focus_on_hidden_stack(struct monitor *m, int i); void focus_on_stack(struct monitor *m, int i); /* Find the topmost visible client (if any) at point (x, y), including @@ -75,11 +68,17 @@ void set_container_monitor(struct container *con, struct monitor *m); void resize_container(struct container *con, struct wlr_cursor *cursor, int dx, int dy); void move_container(struct container *con, struct wlr_cursor *cursor, int offsetx, int offsety); -int absolute_x_to_container_relative(struct container *con, int x); -int absolute_y_to_container_relative(struct container *con, int y); +void set_container_workspace(struct container *con, struct workspace *ws); +void move_container_to_workspace(struct container *con, struct workspace *ws); + +struct monitor *container_get_monitor(struct container *con); + +int absolute_x_to_container_relative(struct wlr_box geom, int x); +int absolute_y_to_container_relative(struct wlr_box geom, int y); int get_position_in_container_stack(struct container *con); struct container *get_container_from_container_stack_position(int i); bool is_resize_not_in_limit(struct wlr_fbox *geom, struct resize_constraints *resize_constraints); +bool container_is_bar(struct container *con); #endif /* CONTAINER_H */ diff --git a/include/cursor.h b/include/cursor.h index a3eb8304..340e2ba1 100644 --- a/include/cursor.h +++ b/include/cursor.h @@ -1,10 +1,12 @@ #ifndef CURSOR_H #define CURSOR_H -#include "lib/actions/actions.h" #include #include #include +#include + +#include "seat.h" enum cursor_mode { CURSOR_NORMAL, @@ -13,29 +15,62 @@ enum cursor_mode { }; struct cursor { - struct wl_listener request_set_cursor; + struct seat *seat; + struct wlr_surface *image_surface; + const char *image; + + struct wlr_pointer_constraint_v1 *active_constraint; + + struct wl_listener motion; + struct wl_listener motion_absolute; + struct wl_listener button; + struct wl_listener axis; + struct wl_listener frame; + struct wl_listener image_surface_destroy; + struct wl_listener request_set_cursor; + + struct wl_listener constraint_commit; + + struct wl_event_source *hide_source; + + struct wlr_xcursor_manager *xcursor_mgr; + enum cursor_mode cursor_mode; struct wlr_surface *cursor_surface; struct wlr_cursor *wlr_cursor; int hotspot_x; int hotspot_y; + struct wl_client *image_client; + bool active_confine_requires_warp; + bool hidden; + + pixman_region32_t confine; // invalid if active_constraint == NULL }; -void axisnotify(struct wl_listener *listener, void *data); -void buttonpress(struct wl_listener *listener, void *data); -void create_pointer(struct wlr_input_device *device); -void cursorframe(struct wl_listener *listener, void *data); +struct cursor *create_cursor(struct seat *seat); +void destroy_cursor(struct cursor *cursor); + +void cursor_rebase(struct cursor *cursor); +void cursor_set_image(struct cursor *cursor, const char *image, struct wl_client *client); +void handle_cursor_button(struct wl_listener *listener, void *data); +void create_pointer(struct seat *seat, struct seat_device *seat_device); +void handle_cursor_frame(struct wl_listener *listener, void *data); /* This event is raised by the seat when a client provides a cursor image */ void handle_set_cursor(struct wl_listener *listener, void *data); +void cursor_constrain(struct cursor *cursor, struct wlr_pointer_constraint_v1 *constraint); +void handle_new_pointer_constraint(struct wl_listener *listener, void *data); +void cursor_update_image(struct cursor *cursor); -void motion_relative(struct wl_listener *listener, void *data); -void motion_absolute(struct wl_listener *listener, void *data); -void motion_notify(uint32_t time); -/* reload the surface stored in cursor */ -void update_cursor(struct cursor *cursor); -void move_resize(int ui); +void focus_under_cursor(struct cursor *cursor, uint32_t time); +void cursor_handle_activity_from_device(struct cursor *cursor, struct wlr_input_device *device); +void handle_motion_relative(struct wl_listener *listener, void *data); +void handle_motion_absolute(struct wl_listener *listener, void *data); +void motion_notify(struct cursor *cursor, uint32_t time_msec, + struct wlr_input_device *device, double dx, double dy, + double dx_unaccel, double dy_unaccel); +void move_resize(struct cursor *cursor, int ui); void cursor_set_image_surface(struct cursor *cursor, struct wlr_surface *surface, int32_t hotspot_x, int32_t hotspot_y, struct wl_client *client); diff --git a/include/event_handler.h b/include/event_handler.h index 978b8be8..548cd07b 100644 --- a/include/event_handler.h +++ b/include/event_handler.h @@ -2,15 +2,21 @@ #define EVENT_HANDLER_H #include +#include +#include struct event_handler { - int update_func_ref; - int create_container_func_ref; - int on_start_func_ref; - int on_focus_func_ref; + GPtrArray *on_start_func_refs; + GPtrArray *on_focus_func_refs; + GPtrArray *on_update_func_refs; + GPtrArray *on_create_container_func_refs; }; -struct event_handler get_default_event_handler(); +struct event_handler *create_event_handler(); + + +GPtrArray *event_name_to_signal(struct event_handler *event_handler, + const char *event); /* * int n refers to the affected container position in container stack diff --git a/include/input_manager.h b/include/input_manager.h new file mode 100644 index 00000000..1f552cf5 --- /dev/null +++ b/include/input_manager.h @@ -0,0 +1,46 @@ +#ifndef INPUT_MANAGER_H +#define INPUT_MANAGER_H + +#include +#include +#include +#include + +struct input_device { + struct wlr_input_device *wlr_device; + char *identifier; + + struct wl_listener device_destroy; + + bool is_virtual; +}; + +struct input_manager { + GPtrArray *devices; + GPtrArray *seats; + + struct wlr_input_inhibit_manager *inhibit; + struct wlr_keyboard_shortcuts_inhibit_manager_v1 *keyboard_shortcuts_inhibit; + struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard; + struct wlr_virtual_pointer_manager_v1 *virtual_pointer; + + struct wl_listener new_input; + struct wl_listener inhibit_activate; + struct wl_listener inhibit_deactivate; + struct wl_listener keyboard_shortcuts_inhibit_new_inhibitor; + struct wl_listener new_virtual_keyboard; + struct wl_listener new_virtual_pointer; +}; + +struct input_manager *create_input_manager(); +void destroy_input_manager(struct input_manager *input_manager); + +struct seat *input_manager_get_default_seat(); +struct seat *input_manager_get_seat(const char *seat_name); +struct seat *input_manager_seat_from_wlr_seat(struct wlr_seat *wlr_seat); + +char *input_device_get_identifier(struct wlr_input_device *device); + +const char *input_device_get_type(struct input_device *device); + +#endif /* INPUT_MANAGER_H */ diff --git a/include/ipc-json.h b/include/ipc-json.h index de5194e7..a6506ff1 100644 --- a/include/ipc-json.h +++ b/include/ipc-json.h @@ -2,9 +2,11 @@ #define _SWAY_IPC_JSON_H #include #include "client.h" +#include "tagset.h" #include "workspace.h" -json_object *ipc_json_describe_workspace(struct workspace *ws, bool focused); +json_object *ipc_json_describe_tagsets(); +json_object *ipc_json_describe_tag(const char *name, bool is_selected, struct monitor *m); json_object *ipc_json_describe_selected_container(struct monitor *m); #endif diff --git a/include/keybinding.h b/include/keybinding.h index 35c4f0a1..3bfc8410 100644 --- a/include/keybinding.h +++ b/include/keybinding.h @@ -13,6 +13,13 @@ #define MOD_SUPER_L (1 << 6) #define MOD_ISO_LEVEL3_Shift (1 << 7) +struct keybinding { + char *binding; + int lua_func_ref; +}; + +struct keybinding *create_keybinding(); + bool handle_keybinding(int mod, int sym); bool key_state_has_modifiers(size_t mods); diff --git a/include/keyboard.h b/include/keyboard.h index e5b70547..8dc3481f 100644 --- a/include/keyboard.h +++ b/include/keyboard.h @@ -6,10 +6,16 @@ #include #include #include +#include + +#include "seat.h" struct keyboard { - struct wl_list link; - struct wlr_input_device *device; + struct seat *seat; + struct seat_device *seat_device; + + int32_t repeat_rate; + int32_t repeat_delay; struct wl_listener modifiers; struct wl_listener key; @@ -19,7 +25,8 @@ struct keyboard { typedef uint32_t xkb_keysym_t; void cleanupkeyboard(struct wl_listener *listener, void *data); -void create_keyboard(struct wlr_input_device *device); +void create_keyboard(struct seat *seat, struct seat_device *device); +void destroy_keyboard(struct keyboard *kb); void keypress(struct wl_listener *listener, void *data); void keypressmod(struct wl_listener *listener, void *data); diff --git a/include/layer_shell.h b/include/layer_shell.h index 7331ec26..5b22c87b 100644 --- a/include/layer_shell.h +++ b/include/layer_shell.h @@ -3,7 +3,30 @@ #include #include "client.h" +#include "monitor.h" +struct edge { + uint32_t singular_anchor; + uint32_t anchor_triplet; + int *positive_axis; + int *negative_axis; + int margin; +}; + +void map_layer_surface_notify(struct wl_listener *listener, void *data); +void unmap_layer_surface_notify(struct wl_listener *listener, void *data); +void destroy_layer_surface_notify(struct wl_listener *listener, void *data); +void commitlayersurfacenotify(struct wl_listener *listener, void *data); void create_notify_layer_shell(struct wl_listener *listener, void *data); +void arrange_layers(struct monitor *m); +void arrangelayer(struct monitor *m, GPtrArray *array, struct wlr_box *usable_area, bool exclusive); +void apply_exclusive(struct wlr_box *usable_area, + uint32_t anchor, int32_t exclusive, + int32_t margin_top, int32_t margin_right, + int32_t margin_bottom, int32_t margin_left); + +bool layer_shell_is_bar(struct container *con); + +GPtrArray *get_layer_list(struct monitor *m, enum zwlr_layer_shell_v1_layer layer); #endif /* LAYER_SHELL_H */ diff --git a/include/layout.h b/include/layout.h index d8a7b912..fd3fdc69 100644 --- a/include/layout.h +++ b/include/layout.h @@ -27,6 +27,7 @@ struct layout { int n_master_abs; // the amount master windows int nmaster; + int lua_resize_function_ref; int lua_layout_ref; int lua_layout_copy_data_ref; int lua_layout_original_copy_data_ref; @@ -43,7 +44,7 @@ struct layout *create_layout(lua_State *L); void destroy_layout(struct layout *lt); bool is_same_layout(struct layout layout, struct layout layout2); -bool lua_islayout_data(lua_State *L, const char *name); +bool lua_is_layout_data(lua_State *L, const char *name); void lua_copy_table(lua_State *L, int *ref); // copy table and override old value void lua_copy_table_safe(lua_State *L, int *ref); @@ -53,5 +54,5 @@ void copy_layout(struct layout *dest_lt, struct layout *src_lt); // copy layout and override all references with the given ones void copy_layout_safe(struct layout *dest_lt, struct layout *src_lt); -int cmp_layout(const struct layout *lt1, const struct layout *lt2); +int cmp_layout(const void *ptr1, const void *ptr2); #endif /* LAYOUT_H */ diff --git a/include/lib/actions/actions.h b/include/lib/actions/lib_actions.h similarity index 89% rename from include/lib/actions/actions.h rename to include/lib/actions/lib_actions.h index 75e05c74..cbcd498a 100644 --- a/include/lib/actions/actions.h +++ b/include/lib/actions/lib_actions.h @@ -1,5 +1,5 @@ -#ifndef ACTIONS_H -#define ACTIONS_H +#ifndef LIB_ACTIONS_H +#define LIB_ACTIONS_H #include #include #include @@ -8,6 +8,7 @@ #include "client.h" int lib_arrange(lua_State *L); +int lib_create_output(lua_State *L); int lib_focus_container(lua_State *L); int lib_focus_on_hidden_stack(lua_State *L); int lib_focus_on_stack(lua_State *L); @@ -31,6 +32,7 @@ int lib_toggle_layout(lua_State *L); int lib_toggle_view(lua_State *L); int lib_toggle_workspace(lua_State *L); int lib_view(lua_State *L); +int lib_tag_view(lua_State *L); int lib_zoom(lua_State *L); int lib_set_nmaster(lua_State *L); int lib_increase_nmaster(lua_State *L); @@ -38,4 +40,4 @@ int lib_decrease_nmaster(lua_State *L); int lib_move_workspace_to(lua_State *L); int lib_swap_workspace(lua_State *L); -#endif /* ACTIONS_H */ +#endif /* LIB_ACTIONS_H */ diff --git a/include/lib/actions/libcontainer.h b/include/lib/actions/lib_container.h similarity index 100% rename from include/lib/actions/libcontainer.h rename to include/lib/actions/lib_container.h diff --git a/include/lib/config/config.h b/include/lib/config/lib_config.h similarity index 84% rename from include/lib/config/config.h rename to include/lib/config/lib_config.h index 8560feb4..9c2e46b9 100644 --- a/include/lib/config/config.h +++ b/include/lib/config/lib_config.h @@ -1,5 +1,5 @@ -#ifndef CONFIG_H -#define CONFIG_H +#ifndef LIB_CONFIG_H +#define LIB_CONFIG_H #include "options.h" #include @@ -18,21 +18,22 @@ int lib_set_focus_color(lua_State *L); int lib_set_hidden_edges(lua_State *L); int lib_set_hide_edge_borders(lua_State *L); int lib_set_inner_gaps(lua_State *L); -int lib_set_keybinds(lua_State *L); +int lib_bind_key(lua_State *L); int lib_set_layout_constraints(lua_State *L); int lib_set_master_constraints(lua_State *L); int lib_set_master_layout_data(lua_State *L); int lib_set_mod(lua_State *L); -int lib_set_monrules(lua_State *L); +int lib_add_mon_rule(lua_State *L); int lib_set_outer_gaps(lua_State *L); int lib_set_repeat_delay(lua_State *L); int lib_set_repeat_rate(lua_State *L); int lib_set_resize_data(lua_State *L); int lib_set_resize_direction(lua_State *L); +int lib_set_resize_function(lua_State *L); int lib_set_root_color(lua_State *L); -int lib_set_rules(lua_State *L); +int lib_add_rule(lua_State *L); int lib_set_sloppy_focus(lua_State *L); int lib_set_smart_hidden_edges(lua_State *L); int lib_set_tile_borderpx(lua_State *L); -#endif /* CONFIG_H */ +#endif /* LIB_CONFIG_H */ diff --git a/include/lib/config/localconfig.h b/include/lib/config/local_config.h similarity index 89% rename from include/lib/config/localconfig.h rename to include/lib/config/local_config.h index d27f66b4..3ac66539 100644 --- a/include/lib/config/localconfig.h +++ b/include/lib/config/local_config.h @@ -17,6 +17,8 @@ int local_set_master_layout_data(lua_State *L); int local_set_outer_gaps(lua_State *L); int local_set_resize_data(lua_State *L); int local_set_resize_direction(lua_State *L); +int local_set_resize_function(lua_State *L); +int local_set_resize_function(lua_State *L); int local_set_sloppy_focus(lua_State *L); int local_set_tile_borderpx(lua_State *L); diff --git a/include/lib/event_handler/lib_event_handler.h b/include/lib/event_handler/lib_event_handler.h index 1ccb1d92..bbf3427e 100644 --- a/include/lib/event_handler/lib_event_handler.h +++ b/include/lib/event_handler/lib_event_handler.h @@ -4,6 +4,7 @@ #include #include +int lib_add_listener(lua_State *L); int lib_set_create_container_function(lua_State *L); int lib_set_on_focus_function(lua_State *L); int lib_set_on_start_function(lua_State *L); diff --git a/include/lib/event_handler/local_event_handler.h b/include/lib/event_handler/local_event_handler.h index 2559bcb3..fe09a3c4 100644 --- a/include/lib/event_handler/local_event_handler.h +++ b/include/lib/event_handler/local_event_handler.h @@ -4,7 +4,6 @@ #include #include -int local_set_update_function(lua_State *L); -int local_set_create_container_function(lua_State *L); +int local_add_listener(lua_State *L); #endif /* LOCAL_EVENT_HANDLER_H */ diff --git a/include/lib/info/info.h b/include/lib/info/lib_info.h similarity index 92% rename from include/lib/info/info.h rename to include/lib/info/lib_info.h index c8a18dfd..c0516ec8 100644 --- a/include/lib/info/info.h +++ b/include/lib/info/lib_info.h @@ -4,6 +4,7 @@ #include int lib_get_container_under_cursor(lua_State *L); +int lib_get_root_area(lua_State *L); int lib_get_next_empty_workspace(lua_State *L); int lib_get_nmaster(lua_State *L); int lib_get_this_container_count(lua_State *L); diff --git a/include/list_set.h b/include/list_set.h new file mode 100644 index 00000000..a62107f5 --- /dev/null +++ b/include/list_set.h @@ -0,0 +1,48 @@ +#ifndef LIST_SET_H +#define LIST_SET_H + +#include +#include + +#include "utils/coreUtils.h" + +/* + * list_set is used by tagsets and workspaces to hold all containers on them + * */ +struct list_set { + /* list of all one dimensonal lists in list_set */ + GPtrArray2D *all_lists; + /* consists out of the lists of tiled_containers, hidden_containers and + * floating_containers */ + GPtrArray2D *container_lists; + GPtrArray2D *visible_container_lists; + + GPtrArray *floating_containers; + GPtrArray *tiled_containers; + GPtrArray *hidden_containers; + + GPtrArray *independent_containers; + + GPtrArray2D *focus_stack_lists_with_layer_shell; + GPtrArray2D *focus_stack_visible_lists; + GPtrArray2D *focus_stack_lists; + + GPtrArray *focus_stack_layer_background; + GPtrArray *focus_stack_layer_bottom; + GPtrArray *focus_stack_layer_top; + GPtrArray *focus_stack_layer_overlay; + GPtrArray *focus_stack_on_top; + GPtrArray *focus_stack_normal; + GPtrArray *focus_stack_hidden; + GPtrArray *focus_stack_not_focusable; +}; + +struct list_set *create_list_set(); +void destroy_list_set(struct list_set *list_set); + +void append_list_set(struct list_set *dest, struct list_set *src); +void clear_list_set(struct list_set *list_set); +// estimated time efficiency O(n*m) +void list_set_remove_list_set(struct list_set *dest, struct list_set *src); + +#endif /* LIST_SET_H */ diff --git a/include/monitor.h b/include/monitor.h index 72245b44..438892dc 100644 --- a/include/monitor.h +++ b/include/monitor.h @@ -4,9 +4,8 @@ #include #include -#include "container.h" -#include "workspace.h" #include "root.h" +#include "tagset.h" struct monitor { struct wlr_output *wlr_output; @@ -20,7 +19,7 @@ struct monitor { struct wlr_box geom; struct root *root; float scale; - int ws_id; + struct tagset *tagset; }; struct monrule { @@ -31,23 +30,19 @@ struct monrule { /* associated with stlink in container */ extern struct wl_list sticky_stack; -void center_mouse_in_monitor(struct monitor *m); +void center_cursor_in_monitor(struct cursor *cursor, struct monitor *m); void create_monitor(struct wl_listener *listener, void *data); +void create_output(struct wlr_backend *backend, void *data); void destroy_monitor(struct wl_listener *listener, void *data); void scale_monitor(struct monitor *m, float scale); void focus_monitor(struct monitor *m); +void focus_tags(struct BitSet bitset); void transform_monitor(struct monitor *m, enum wl_output_transform transform); void update_monitor_geometries(); -/* * - * selTag[1] = selTag[0] then - * selTag[0] = new value - * */ -void push_selected_workspace(struct monitor *m, struct workspace *ws); - -struct monitor *dirtomon(int dir); struct monitor *output_to_monitor(struct wlr_output *output); struct monitor *xy_to_monitor(double x, double y); +struct tagset *monitor_get_active_tagset(struct monitor *m); struct workspace *monitor_get_active_workspace(struct monitor *m); struct layout *get_layout_in_monitor(struct monitor *m); diff --git a/include/options.h b/include/options.h index 87113211..c435aaa9 100644 --- a/include/options.h +++ b/include/options.h @@ -3,8 +3,8 @@ #include #include -#include #include +#include #include "event_handler.h" #define BLACK {0.0f, 0.0f, 0.0f, 1.0f} @@ -43,11 +43,9 @@ struct options { struct resize_constraints layout_constraints; struct resize_constraints master_constraints; - struct wlr_list tag_names; - struct rule *rules; - size_t rule_count; - struct monrule *monrules; - size_t monrule_count; + GPtrArray *tag_names; + GPtrArray *rules; + GPtrArray *mon_rules; int repeat_rate; int repeat_delay; @@ -57,17 +55,18 @@ struct options { bool arrange_by_focus; int resize_dir; - struct event_handler event_handler; + struct event_handler *event_handler; int tag_names_ref; int default_layout_ref; - int keybinds_ref; enum wlr_edges hidden_edges; bool smart_hidden_edges; + + GPtrArray *keybindings; }; struct options get_default_options(); -void init_tagnames(struct wlr_list *tag_names); +GPtrArray *create_tagnames(); void copy_options(struct options *dest_option, struct options *src_option); #endif /* OPTIONS_H */ diff --git a/include/popup.h b/include/popup.h index 19e1a8c8..e1de0c24 100644 --- a/include/popup.h +++ b/include/popup.h @@ -21,7 +21,7 @@ struct xdg_popup { void popup_handle_destroy(struct wl_listener *listener, void *data); void popup_handle_new_popup(struct wl_listener *listener, void *data); void destroy_popups(); -struct wlr_surface *get_popup_surface_under_cursor(double *sx, double *sy); +struct wlr_surface *get_popup_surface_under_cursor(struct cursor *cursor, double *sx, double *sy); struct xdg_popup *get_latest_popup(); bool popups_exist(); #endif /* POPUP_H */ diff --git a/include/render/render.h b/include/render/render.h index 66bf14eb..774a582b 100644 --- a/include/render/render.h +++ b/include/render/render.h @@ -13,13 +13,7 @@ struct render_data { struct wlr_output *output; struct timespec *when; int x, y; /* layout-relative */ - /* textures that will be rendered with a new frame - * list should be filled with posTexture - * */ - struct wlr_list textures; - /* The textures before doing any kind of transformations - * */ - struct wlr_list base_textures; + pixman_region32_t *damage; }; diff --git a/include/rules/mon_rule.h b/include/rules/mon_rule.h new file mode 100644 index 00000000..d314a702 --- /dev/null +++ b/include/rules/mon_rule.h @@ -0,0 +1,17 @@ +#ifndef MON_RULE_H +#define MON_RULE_H + +#include "monitor.h" + +struct mon_rule { + char *output_name; + int lua_func_ref; +}; + +struct mon_rule *create_mon_rule(const char *output_name, int lua_func_ref); +void destroy_mon_rule(struct mon_rule *mon_rule); + +void apply_mon_rule(struct mon_rule *mon_rule, struct monitor *m); +void apply_mon_rules(GPtrArray *mon_rules, struct monitor *m); + +#endif /* MON_RULE_H */ diff --git a/include/rules/rule.h b/include/rules/rule.h new file mode 100644 index 00000000..8e8dcf73 --- /dev/null +++ b/include/rules/rule.h @@ -0,0 +1,18 @@ +#ifndef RULE_H +#define RULE_H + +#include "container.h" + +struct rule { + char *id; + char *title; + int lua_func_ref; +}; + +struct rule *create_rule(const char *id, const char *title, int lua_func_ref); +void destroy_rule(struct rule *rule); + +void apply_rule(struct rule *rule, struct container *con); +void apply_rules(GPtrArray *rules, struct container *con); + +#endif /* RULE_H */ diff --git a/include/seat.h b/include/seat.h new file mode 100644 index 00000000..1288b0a6 --- /dev/null +++ b/include/seat.h @@ -0,0 +1,41 @@ +#ifndef SEAT_H +#define SEAT_H + +#include +#include +#include + +#include "input_manager.h" + +struct seat { + struct wlr_seat *wlr_seat; + struct cursor *cursor; + + struct wl_listener request_set_selection; + struct wl_listener request_set_primary_selection; + + GPtrArray *devices; +}; + +struct seat_device { + struct seat *seat; + struct input_device *input_device; + struct keyboard *keyboard; +}; + +struct pointer_constraint { + struct cursor *cursor; + struct wlr_pointer_constraint_v1 *wlr_constraint; + + struct wl_listener set_region; + struct wl_listener destroy; +}; + +struct seat *create_seat(const char *seat_name); +void destroy_seat(struct seat *seat); + +void seat_add_device(struct seat *seat, struct input_device *input_device); +void seat_configure_xcursor(struct seat *seat); +void seat_remove_device(struct seat *seat, struct input_device *input_device); + +#endif /* SEAT_H */ diff --git a/include/server.h b/include/server.h index a8108a54..db0c714d 100644 --- a/include/server.h +++ b/include/server.h @@ -2,65 +2,94 @@ #define SERVER_H #include #include +#include +#include #include #include #include +#include +#include #include "cursor.h" #include "layout.h" #include "options.h" #include "xwayland.h" +#include "input_manager.h" +#include "utils/coreUtils.h" struct server { struct wl_display *wl_display; struct wl_event_loop *wl_event_loop; struct wlr_backend *backend; struct wlr_compositor *compositor; - struct wlr_seat *seat; - - struct xwayland xwayland; - struct wl_listener xwayland_ready; struct wlr_xdg_shell *xdg_shell; struct wlr_layer_shell_v1 *layer_shell; struct wlr_xdg_decoration_manager_v1 *xdeco_mgr; - struct cursor cursor; - struct wlr_xcursor_manager *cursor_mgr; + struct input_manager *input_manager; + + struct wlr_virtual_pointer_manager_v1 *virtual_pointer_mgr; + struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard_mgr; + struct wlr_relative_pointer_manager_v1 *relative_pointer_mgr; + struct wlr_input_inhibit_manager *input_inhibitor_mgr; + struct wlr_pointer_constraints_v1 *pointer_constraints; struct layout *default_layout; struct layout_set layout_set; struct wlr_output_layout *output_layout; - struct wl_list keyboards; + GPtrArray *keyboards; + + + GPtrArray *workspaces; - struct wlr_list workspaces; + GPtrArray *scratchpad; - struct wlr_list visual_stack_lists; - struct wlr_list normal_visual_stack_lists; - struct wlr_list layer_visual_stack_lists; + GPtrArray *config_paths; + char *config_file; + char *config_dir; - struct wlr_list tiled_visual_stack; - struct wlr_list floating_visual_stack; - struct wlr_list layer_visual_stack_background; - struct wlr_list layer_visual_stack_bottom; - struct wlr_list layer_visual_stack_top; - struct wlr_list layer_visual_stack_overlay; + struct tagset *previous_tagset; - struct wlr_list scratchpad; + GPtrArray *client_lists; + GPtrArray *normal_clients; + GPtrArray *non_tiled_clients; + GPtrArray *independent_clients; - const char *config_file; - const char *config_dir; + GPtrArray *mons; + GPtrArray *popups; + // X11 popups are handled as containers + GPtrArray *xwayland_popups; - int previous_workspace_id; + GPtrArray *tagsets; - struct wlr_list client_lists; - struct wlr_list normal_clients; - struct wlr_list non_tiled_clients; - struct wlr_list independent_clients; + struct wlr_surface *old_surface; + + GPtrArray2D *visual_stack_lists; + GPtrArray2D *normal_visual_stack_lists; + GPtrArray2D *layer_visual_stack_lists; + + GPtrArray *tiled_visual_stack; + GPtrArray *floating_visual_stack; + GPtrArray *layer_visual_stack_background; + GPtrArray *layer_visual_stack_bottom; + GPtrArray *layer_visual_stack_top; + GPtrArray *layer_visual_stack_overlay; + + /* global event handlers */ + struct wl_listener new_output; + struct wl_listener new_xdeco; + struct wl_listener new_xdg_surface; + struct wl_listener new_layer_shell_surface; + struct wl_listener new_pointer_constraint; + +#if JAPOKWM_HAS_XWAYLAND + struct xwayland xwayland; + struct wl_listener xwayland_ready; - struct wlr_list mons; - struct wlr_list popups; + struct wl_listener new_xwayland_surface; +#endif }; extern struct server server; diff --git a/include/stringop.h b/include/stringop.h index b7216531..bbac6902 100644 --- a/include/stringop.h +++ b/include/stringop.h @@ -3,7 +3,7 @@ #include #include -#include +#include void strip_whitespace(char *str); void strip_quotes(char *str); @@ -16,7 +16,7 @@ char *lenient_strncat(char *dest, const char *src, size_t len); int lenient_strcmp(char *a, char *b); // Simply split a string with delims, free with `list_free_items_and_destroy` -struct wlr_list split_string(const char *str, const char *delims); +GPtrArray *split_string(const char *str, const char *delims); // Splits an argument string, keeping quotes intact char **split_args(const char *str, int *argc); @@ -31,4 +31,6 @@ char *argsep(char **stringp, const char *delim, char *matched_delim); // Expand a path using shell replacements such as $HOME and ~ bool expand_path(char **path); +void append_string(char **string, const char *next_string); + #endif diff --git a/include/tagset.h b/include/tagset.h new file mode 100644 index 00000000..fc9fcefd --- /dev/null +++ b/include/tagset.h @@ -0,0 +1,89 @@ +#ifndef TAGSET_H +#define TAGSET_H + +#include +#include + +#include "bitset/bitset.h" +#include "client.h" +#include "list_set.h" +#include "layout.h" +#include "utils/coreUtils.h" + +/* A tagset consists of a list_set, a struct to hold all relevant containers, + * information on which monitor it belongs to and the list of workspaces it + * currently shows. + * If a tagset is created it will be added to the server's tagsets array. + * If a tagset is destroyed it will be be removed from the server's tagsets + * array. + * If a tagset is loaded list_set represents the containers loaded and events + * such as destroy container can alter this list. + * If a tagset is not loaded the tagset wont change it's state without doing it + * explicitly. + * A tagset will be unloaded when destroyed. + * loading is the action of populating the list_set. + * unloading is the action of cleaning the list_set. + * OVERLAPPING WORKSPACES: + * If another workspace is added to the current tagset it is checked if + * other loaded tagsets, which are on other monitors, contain the same workspace. + * If the workspace is not attached to the other monitor then it is removed from + * this tagset. Otherwise the workspace will be temporarily pulled over to the + * current tagset. As soon as the workspace is unselected it moves back to its + * prior monitor. + * LOADING TAGSETS: + * select the monitor of the tagset and load it. + * SELECTING WORKSPACE: + * create a new tagset with the given workspace and load it. + * CREATING TAGSET: + * All + * */ +struct tagset { + struct monitor *m; + int selected_ws_id; + BitSet *workspaces; + BitSet *loaded_workspaces; + + /* number of all windows in layout even if they are invisible). Note that + * floating windows don't belong to the layout and are thereby not counted */ + int n_all; + + struct list_set *list_set; +}; + +/* this creates a tagset with reference count of 1. Calling focus_tagset + * afterwards also adds a ref counter of 1 therefore use focus_tagset_no_ref + * instead. */ +struct tagset *create_tagset(struct monitor *m, int selected_ws_id, BitSet *workspaces); +void tagset_acquire(struct tagset *tagset); +void tagset_release(struct tagset *tagset); + +void focus_most_recent_container(struct tagset *tagset); +void focus_tagset(struct tagset *tagset); +void tagset_focus_workspace(int ws_id); +void tagset_toggle_add(struct tagset *tagset, BitSet *bitset); +void tagset_focus_tags(int ws_id, struct BitSet *bitset); + +struct container *get_container(struct tagset *tagset, int i); + +GPtrArray *tagset_get_visible_lists(struct tagset *tagset); +GPtrArray *tagset_get_tiled_list(struct tagset *tagset); +GPtrArray *tagset_get_floating_list(struct tagset *tagset); +GPtrArray *tagset_get_hidden_list(struct tagset *tagset); + +void workspace_id_to_tag(BitSet *dest, int ws_id); + +bool exist_on(struct tagset *tagset, struct container *con); +bool tagset_contains_client(struct tagset *tagset, struct client *c); +bool visible_on(struct tagset *tagset, struct container *con); +bool tagset_is_visible(struct tagset *tagset); + +// adds a refcount of 1 to tagset +void focus_tagset(struct tagset *tagset); +void focus_tagset_no_ref(struct tagset *tagset); +// adds a refcount of 1 to tagset +void push_tagset(struct tagset *tagset); +void push_tagset_no_ref(struct tagset *tagset); + +struct layout *tagset_get_layout(struct tagset *tagset); + +#endif /* TAGSET_H */ diff --git a/include/tile/tile.h b/include/tile/tile.h deleted file mode 100644 index 4dcf5089..00000000 --- a/include/tile/tile.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef TILE_H -#define TILE_H -#include "tileUtils.h" -#include -#include -#include -#include - -int resize_main_all(lua_State *L); - -#endif /* TILE_H */ diff --git a/include/tile/tileUtils.h b/include/tile/tileUtils.h index 15a37a51..3206f0ac 100644 --- a/include/tile/tileUtils.h +++ b/include/tile/tileUtils.h @@ -7,18 +7,18 @@ struct client *focustop(struct monitor *m); void arrange(); void arrange_monitor(struct monitor *m); -void arrange_containers(struct workspace *ws, struct wlr_box root_geom, - struct wlr_list *tiled_containers); +void arrange_containers(struct tagset *ts, struct wlr_box root_geom, + GPtrArray *tiled_containers); void resize(struct container *con, struct wlr_box geom); void update_hidden_status_of_containers(struct monitor *m, - struct wlr_list *visible_container_lists, struct wlr_list *tiled_containers, - struct wlr_list *hidden_containers); + GPtrArray *visible_container_lists, GPtrArray *tiled_containers, + GPtrArray *hidden_containers); int lib_get_this_container_count(); -int get_container_count(struct workspace *ws); -int get_container_area_count(struct workspace *ws); -int get_tiled_container_count(struct workspace *ws); -int get_slave_container_count(struct workspace *ws); -int get_floating_container_count(struct workspace *ws); -int get_master_container_count(struct workspace *ws); +int get_container_count(struct tagset *tagset); +int get_container_area_count(struct tagset *tagset); +int get_tiled_container_count(struct tagset *tagset); +int get_slave_container_count(struct tagset *tagset); +int get_floating_container_count(struct tagset *tagset); +int get_master_container_count(struct tagset *tagset); #endif diff --git a/include/translationLayer.h b/include/translationLayer.h index 3ee1e68f..b9a13369 100644 --- a/include/translationLayer.h +++ b/include/translationLayer.h @@ -3,6 +3,6 @@ #include #include -void load_libs(lua_State *L); +void load_lua_api(lua_State *L); #endif /* TRANSLATION_LAYER_H */ diff --git a/include/utils/coreUtils.h b/include/utils/coreUtils.h index b53297cc..a18b0f00 100644 --- a/include/utils/coreUtils.h +++ b/include/utils/coreUtils.h @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -14,9 +13,13 @@ #include #include "options.h" +/* a 2d array type that means that the lists have to be filled with other + * GPtrArray variables + * */ +typedef GPtrArray GPtrArray2D; + /* macros */ -#define MAX(A, B) ((A) > (B) ? (A) : (B)) -#define MIN(A, B) ((A) < (B) ? (A) : (B)) +//NOLINTNEXTLINE #define LENGTH(X) (sizeof X / sizeof X[0]) #define END(A) ((A) + LENGTH(A)) /* number of chars a string should contain */ @@ -31,13 +34,11 @@ #define MIN_CONTAINER_WIDTH 30 #define MIN_CONTAINER_HEIGHT 30 +#define LISTEN(E, L, H) wl_signal_add((E), ((L)->notify = (H), (L))) -/* rules */ -struct rule { - char *id; - char *title; - int lua_func_ref; -}; +#define foreach(item, array)\ + for(int keep = 1, count = 0, size = LENGTH(array); keep && count < size; keep = 1, count++)\ + for(item = array[count]; keep; keep = 0)\ extern struct lua_State *L; @@ -46,7 +47,11 @@ bool file_exists(const char *path); char last_char(const char *str); // returns exactly the same values as strcmp int path_compare(const char *path1, const char *path2); -void join_path(char *base, const char *file); +void join_path(char **base, const char *file); + +void debug_print(const char *fmt, ...); + +int cross_sum(int n, int base); /* * create a lua table that looks like this: @@ -58,6 +63,8 @@ void join_path(char *base, const char *file); */ void lua_get_default_layout_data(lua_State *L); +void lua_get_default_resize_function(lua_State *L); + /* * create a lua table that looks like this: * { @@ -84,46 +91,41 @@ void lua_get_default_master_layout_data(lua_State *L); */ void lua_get_default_resize_data(lua_State *L); -void wlr_list_clear(struct wlr_list *list, void (*destroy_func)(void *)); -bool wlr_list_empty(struct wlr_list *list); +void list_clear(GPtrArray *array, void (*destroy_func)(void *)); +void wlr_list_cat(GPtrArray *dest, GPtrArray *src); -typedef int (*cmp_func_t)(const void*, const void*); -/* return 0 on success and 1 on failure */ -int wlr_list_remove(struct wlr_list *list, - int (*compare)(const void *, const void *), const void *cmp_to); -/* return 0 on success and 1 on failure */ -int remove_in_composed_list(struct wlr_list *lists, - int (*compare)(const void *, const void *), const void *cmp_to); -int find_in_composed_list(struct wlr_list *lists, +/* return true on success and false on failure */ +bool list_remove(GPtrArray *array, int (*compare)(const void *, const void *), const void *cmp_to); +bool remove_in_composed_list(GPtrArray *array, int (*compare)(const void *, const void *), void *cmp_to); +int find_in_composed_list(GPtrArray *lists, int (*compare)(const void *, const void *), const void *cmp_to); -struct wlr_list *find_list_in_composed_list(struct wlr_list *lists, +GPtrArray *find_list_in_composed_list(GPtrArray *arrays, int (*compare)(const void *, const void *), const void *cmp_to); -struct wlr_list *list_insert_into_relative_position(struct wlr_list *lists, - int index, void *item); +GPtrArray *list_insert_into_relative_position(GPtrArray *lists, int index, void *item); // compare pointers and return 0 if they are equal and 1 otherwise int cmp_ptr(const void *ptr1, const void *ptr2); +int cmp_str(const void *s1, const void *s2); void lua_tocolor(float dest_color[static 4]); // like lua_ref but override the old value if *ref > 0 void lua_ref_safe(lua_State *L, int t, int *ref); -void copy_options(struct options *dest_option, struct options *src_option); void print_trace(); /* a composed list is just a list consisting of lists so that if an index i is * given it returns the same value as if all the lists where concatenated */ -void *get_in_composed_list(struct wlr_list *lists, int i); -struct wlr_list *get_list_at_i_in_composed_list(struct wlr_list *lists, int i); -void delete_from_composed_list(struct wlr_list *lists, int i); +void *get_in_composed_list(GPtrArray *arrays, int i); +GPtrArray *get_list_at_i_in_composed_list(GPtrArray *arrays, int i); +void delete_from_composed_list(GPtrArray *arrays, int i); -int length_of_list(struct wlr_list *list); -int length_of_composed_list(struct wlr_list *lists); +int length_of_list(GPtrArray *array); +int length_of_composed_list(GPtrArray *array); int relative_index_to_absolute_index(int i, int j, int length); -void *get_relative_item_in_list(struct wlr_list *list, int i, int j); -void *get_relative_item_in_composed_list(struct wlr_list *lists, int i, int j); +void *get_relative_item_in_list(GPtrArray *array, int i, int j); +void *get_relative_item_in_composed_list(GPtrArray *arrays, int i, int j); int exec(const char *cmd); bool is_approx_equal(double a, double b, double error_range); -#endif +#endif /* COREUTILS */ diff --git a/include/utils/parseConfigUtils.h b/include/utils/parseConfigUtils.h index fd666495..3d4e4e14 100644 --- a/include/utils/parseConfigUtils.h +++ b/include/utils/parseConfigUtils.h @@ -1,11 +1,17 @@ #ifndef PARSE_CONFIG_UTILS_H #define PARSE_CONFIG_UTILS_H +#include #include -#include "utils/coreUtils.h" #include #include #include + +#include "utils/coreUtils.h" #include "layout.h" +#include "rules/rule.h" +#include "rules/mon_rule.h" + +GPtrArray *create_default_config_paths(); /* returns 0 if loading file was successful else return 1 * the error_file argument gets malloced so it has to be freed */ @@ -23,28 +29,14 @@ char *get_config_dir(const char *file); void append_to_lua_path(lua_State *L, const char *path); // get values -bool get_config_bool(lua_State *L, char *name); -char* get_config_str(lua_State *L, char *name); -float get_config_float(lua_State *L, char *name); -int get_config_int(lua_State *L, char *name); int lua_call_safe(lua_State *L, int nargs, int nresults, int msgh); int lua_getglobal_safe(lua_State *L, const char *name); -struct layout get_config_layout(lua_State *L, char *name); -struct monrule get_config_monrule(lua_State *L, char *name); -struct rule get_config_rule(lua_State *L, char *name); -void call_arrange_func(lua_State *L, int funcId, int n); -void call_function(lua_State *L, struct layout lt); +void notify_msg(const char *msg); void handle_error(const char *msg); -// get array values -void get_config_str_arr(lua_State *L, struct wlr_list *resArr, char *name); -void get_config_float_arr(lua_State *L, float *resArr, char *name); -void get_config_int_arr(lua_State *L, int *resArr, char *name); -void get_config_rule_arr(lua_State *L, struct rule **rules, size_t *rule_count, char *name); -void get_config_mon_rule_arr(lua_State *L, struct monrule **monrules, size_t *monrule_count, char *name); - -struct rule get_config_array_rule(lua_State *L, const char* name, size_t i); -char *get_config_array_str(lua_State *L, const char *name, size_t i); -struct monrule get_config_array_monrule(lua_State *L, const char* name, size_t i); +struct rule *get_config_rule(lua_State *L); +const char *get_config_str(lua_State *L, int idx); +const char *get_config_array_str(lua_State *L, const char *name, size_t i); +struct mon_rule *get_config_mon_rule(lua_State *L); #endif /* PARSE_CONFIG_UTILS_H */ diff --git a/include/utils/vector.h b/include/utils/vector.h new file mode 100644 index 00000000..0a4fd65a --- /dev/null +++ b/include/utils/vector.h @@ -0,0 +1,128 @@ +#ifndef VECTOR_H +#define VECTOR_H + +#include +#include + +/***** DEFINITIONS *****/ + +#define VECTOR_MINIMUM_CAPACITY 2 +#define VECTOR_GROWTH_FACTOR 2 +#define VECTOR_SHRINK_THRESHOLD (1 / 4) + +#define VECTOR_ERROR -1 +#define VECTOR_SUCCESS 0 + +#define VECTOR_UNINITIALIZED NULL +#define VECTOR_INITIALIZER \ + { 0, 0, 0, VECTOR_UNINITIALIZED } + +/***** STRUCTURES *****/ + +typedef struct Vector { + size_t size; + size_t capacity; + size_t element_size; + + void* data; +} Vector; + +typedef struct Iterator { + void* pointer; + size_t element_size; +} Iterator; + +/***** METHODS *****/ + +/* Constructor */ +Vector* vector_create(size_t capacity, size_t element_size); + +/* Copy Constructor */ +Vector *vector_copy(Vector* source); + +/* Move Constructor */ +int vector_move(Vector* destination, Vector* source); + +/* Move Assignment */ +void vector_move_assign(Vector* destination, Vector* source); + +int vector_swap(Vector* destination, Vector* source); + +/* Destructor */ +void vector_destroy(Vector* vector); + +/* Insertion */ +int vector_push_back(Vector* vector, void* element); +int vector_push_front(Vector* vector, void* element); +int vector_insert(Vector* vector, size_t index, void* element); +int vector_assign(Vector* vector, size_t index, void* element); + +/* Deletion */ +int vector_pop_back(Vector* vector); +int vector_pop_front(Vector* vector); +int vector_erase(Vector* vector, size_t index); +int vector_clear(Vector* vector); + +/* Lookup */ +void* vector_get(Vector* vector, size_t index); +const void* vector_const_get(const Vector* vector, size_t index); +void* vector_front(Vector* vector); +void* vector_back(Vector* vector); +#define VECTOR_GET_AS(type, vector_pointer, index) \ + *((type*)vector_get((vector_pointer), (index))) + +/* Information */ +bool vector_is_initialized(const Vector* vector); +size_t vector_byte_size(const Vector* vector); +size_t vector_free_space(const Vector* vector); +bool vector_is_empty(const Vector* vector); + +/* Memory management */ +int vector_resize(Vector* vector, size_t new_size); +int vector_reserve(Vector* vector, size_t minimum_capacity); +int vector_shrink_to_fit(Vector* vector); + +/* Iterators */ +Iterator vector_begin(Vector* vector); +Iterator vector_end(Vector* vector); +Iterator vector_iterator(Vector* vector, size_t index); + +void* iterator_get(Iterator* iterator); +#define ITERATOR_GET_AS(type, iterator) *((type*)iterator_get((iterator))) + +void iterator_increment(Iterator* iterator); +void iterator_decrement(Iterator* iterator); + +void* iterator_next(Iterator* iterator); +void* iterator_previous(Iterator* iterator); + +bool iterator_equals(Iterator* first, Iterator* second); +bool iterator_is_before(Iterator* first, Iterator* second); +bool iterator_is_after(Iterator* first, Iterator* second); + +#define VECTOR_FOR_EACH(vector_pointer, iterator_name) \ + for (Iterator(iterator_name) = vector_begin((vector_pointer)), \ + end = vector_end((vector_pointer)); \ + !iterator_equals(&(iterator_name), &end); \ + iterator_increment(&(iterator_name))) + +/***** PRIVATE *****/ + +bool _vector_should_grow(Vector* vector); +bool _vector_should_shrink(Vector* vector); + +size_t _vector_free_bytes(const Vector* vector); +void* _vector_offset(Vector* vector, size_t index); +const void* _vector_const_offset(const Vector* vector, size_t index); + +void _vector_assign(Vector* vector, size_t index, void* element); + +int _vector_move_right(Vector* vector, size_t index); +void _vector_move_left(Vector* vector, size_t index); + +int _vector_adjust_capacity(Vector* vector); +int _vector_reallocate(Vector* vector, size_t new_capacity); + +void _vector_swap(size_t* first, size_t* second); + +#endif /* VECTOR_H */ diff --git a/include/wlr_signal.h b/include/wlr_signal.h new file mode 100644 index 00000000..602863f6 --- /dev/null +++ b/include/wlr_signal.h @@ -0,0 +1,11 @@ +#ifndef WLR_SIGNAL +#define WLR_SIGNAL + +#include +#include + +struct wlr_signal { + GPtrArray *listener_list; +}; + +#endif /* WLR_SIGNAL */ diff --git a/include/workspace.h b/include/workspace.h index 920e889b..d4a03cca 100644 --- a/include/workspace.h +++ b/include/workspace.h @@ -3,102 +3,96 @@ #include #include -#include #include "layout.h" +#include "list_set.h" #include "container.h" +/* when an action should change the workspace and the tagsets associated with it + * you should use this macro. + * NOTE: use to jump to the end of the current action*/ +#define DO_ACTION(workspace, action) \ + do {\ + struct list_set *list_set = workspace->list_set;\ + do {\ + action\ + } while (0);\ + \ + for (int _i = 0; _i < workspace->subscribed_tagsets->len; _i++) {\ + struct tagset *_tagset = g_ptr_array_index(workspace->subscribed_tagsets, _i);\ + list_set = _tagset->list_set;\ + action\ + }\ + } while (0) + /* A tag is simply a workspace that can be focused (like a normal workspace) * and can selected: which just means that all clients on the selected tags * will be combined to be shown on the focused tag * using this struct requires to use tagsetCreate and later tagsetDestroy * */ struct workspace { - size_t id; - const char *name; - struct wlr_list loaded_layouts; + GPtrArray *loaded_layouts; struct layout *previous_layout; struct layout *layout; - struct monitor *m; - - /* number of all windows in layout even if they are invisible). Note that - * floating windows don't belong to the layout and are thereby not counted */ - int n_all; - - /* consists out of the lists of tiled_containers, hidden_containers and - * floating_containers */ - struct wlr_list container_lists; - struct wlr_list visible_container_lists; - - struct wlr_list floating_containers; - struct wlr_list tiled_containers; - struct wlr_list hidden_containers; - - struct wlr_list independent_containers; - - /* 2D lists */ - struct wlr_list focus_stack_lists_with_layer_shell; - struct wlr_list focus_stack_visible_lists; - struct wlr_list focus_stack_lists; - - struct wlr_list focus_stack_layer_background; - struct wlr_list focus_stack_layer_bottom; - struct wlr_list focus_stack_layer_top; - struct wlr_list focus_stack_layer_overlay; - struct wlr_list focus_stack_on_top; - struct wlr_list focus_stack_normal; - struct wlr_list focus_stack_hidden; - struct wlr_list focus_stack_not_focusable; + + size_t id; + char *name; + + // the last monitor the workspace was on + struct monitor *prev_m; + // the latest tagset + struct tagset *tagset; + // the tagset that currently has this workspace selected + struct tagset *selected_tagset; + + struct list_set *list_set; + + GPtrArray *subscribed_tagsets; }; +GPtrArray *create_workspaces(GPtrArray *tag_names); struct workspace *create_workspace(const char *name, size_t id, struct layout *lt); void destroy_workspace(struct workspace *ws); -void update_workspaces(struct wlr_list *workspaces, struct wlr_list *tag_names); -void update_workspace_ids(struct wlr_list *workspaces); +void update_workspaces(GPtrArray *workspaces, GPtrArray *tag_names); +void update_workspace_ids(GPtrArray *workspaces); -bool exist_on(struct container *con, struct workspace *ws); bool is_workspace_occupied(struct workspace *ws); -bool hidden_on(struct container *con, struct workspace *ws); -bool visible_on(struct container *con, struct workspace *ws); -bool workspace_contains_client(struct workspace *ws, struct client *c); -bool workspace_has_clients(struct workspace *ws); +bool workspace_is_visible(struct workspace *ws); +bool workspace_is_active(struct workspace *ws); int get_workspace_container_count(struct workspace *ws); bool is_workspace_empty(struct workspace *ws); -struct container *get_container(struct workspace *ws, int i); - -struct workspace *find_next_unoccupied_workspace(struct wlr_list *workspaces, struct workspace *ws); +struct workspace *find_next_unoccupied_workspace(GPtrArray *workspaces, struct workspace *ws); struct workspace *get_workspace(int id); -struct workspace *get_next_empty_workspace(struct wlr_list *workspaces, size_t i); -struct workspace *get_prev_empty_workspace(struct wlr_list *workspaces, size_t i); +struct workspace *get_next_empty_workspace(GPtrArray *workspaces, size_t i); +struct workspace *get_prev_empty_workspace(GPtrArray *workspaces, size_t i); -struct wlr_list *get_visible_lists(struct workspace *ws); -struct wlr_list *get_tiled_list(struct workspace *ws); -struct wlr_list *get_floating_list(struct workspace *ws); -struct wlr_list *get_hidden_list(struct workspace *ws); +struct tagset *workspace_get_selected_tagset(struct workspace *ws); +struct tagset *workspace_get_tagset(struct workspace *ws); -void add_container_to_containers(struct container *con, struct workspace *ws, int i); -void add_container_to_focus_stack(struct container *con, struct workspace *ws); -void add_container_to_stack(struct container *con); -void focus_most_recent_container(struct workspace *ws, enum focus_actions a); -void focus_next_unoccupied_workspace(struct monitor *m, struct wlr_list *workspaces, struct workspace *ws); -void copy_layout_from_selected_workspace(struct wlr_list *workspaces); -void create_workspaces(struct wlr_list *workspaces, struct wlr_list *tag_names, - struct layout *default_layout); -void destroy_workspaces(struct wlr_list *workspaces); -void load_default_layout(lua_State *L, struct workspace *ws); +struct monitor *workspace_get_selected_monitor(struct workspace *ws); +struct monitor *workspace_get_monitor(struct workspace *ws); + +void focus_next_unoccupied_workspace(struct monitor *m, GPtrArray *workspaces, struct workspace *ws); +void copy_layout_from_selected_workspace(GPtrArray *workspaces); +void destroy_workspaces(GPtrArray *workspaces); +void layout_set_set_layout(lua_State *L); +void push_layout(struct workspace *ws, struct layout *lt); +void load_default_layout(lua_State *L); void load_layout(lua_State *L, const char *name); void reset_loaded_layout(struct workspace *ws); -void remove_loaded_layouts(struct wlr_list *workspaces); -void set_container_workspace(struct container *con, struct workspace *ws); -void layout_set_set_layout(lua_State *L); -void set_selected_layout(struct workspace *ws, struct layout *layout); -void move_container_to_workspace(struct container *con, struct workspace *ws); -void workspace_assign_monitor(struct workspace *ws, struct monitor *m); +void remove_loaded_layouts(GPtrArray *workspaces); void rename_workspace(struct workspace *ws, const char *name); -void focus_workspace(struct monitor *m, struct workspace *ws); -void push_workspace(struct monitor *m, struct workspace *ws); -void push_layout(struct workspace *ws, struct layout *lt); + +void list_set_add_container_to_focus_stack(struct list_set *list_set, struct container *con); +void workspace_add_container_to_containers(struct workspace *ws, struct container *con, int i); +void workspace_add_container_to_focus_stack(struct workspace *ws, struct container *con); +void add_container_to_stack(struct container *con); + +void workspace_remove_container(struct workspace *ws, struct container *con); +void workspace_remove_container_from_focus_stack(struct workspace *ws, struct container *con); +void workspace_remove_independent_container(struct workspace *ws, struct container *con); + #endif /* WORKSPACE_H */ diff --git a/include/xdg_shell.h b/include/xdg_shell.h index d86c2f94..f688e3a3 100644 --- a/include/xdg_shell.h +++ b/include/xdg_shell.h @@ -9,6 +9,9 @@ typedef struct { } Decoration; void create_notify_xdg(struct wl_listener *listener, void *data); +void destroy_notify(struct wl_listener *listener, void *data); +void map_request(struct wl_listener *listener, void *data); +void unmap_notify(struct wl_listener *listener, void *data); void createxdeco(struct wl_listener *listener, void *data); #endif /* XDG_SHELL_H */ diff --git a/include/xwayland.h b/include/xwayland.h index edfa303e..82a28a1d 100644 --- a/include/xwayland.h +++ b/include/xwayland.h @@ -4,6 +4,8 @@ #include #include +#include "client.h" + enum atom_name { NET_WM_WINDOW_TYPE_NORMAL, NET_WM_WINDOW_TYPE_DIALOG, @@ -30,5 +32,13 @@ struct xwayland { void create_notifyx11(struct wl_listener *listener, void *data); void handle_xwayland_ready(struct wl_listener *listener, void *data); void maprequestx11(struct wl_listener *listener, void *data); +void unmap_notifyx11(struct wl_listener *listener, void *data); +void destroy_notifyx11(struct wl_listener *listener, void *data); + +bool xwayland_popups_exist(); + +bool x11_wants_floating(struct client *c); +bool x11_is_popup_menu(struct client *c); +void init_xwayland(struct wl_display *display, struct seat *seat); #endif /* XWAYLAND_H */ diff --git a/japokmsg/ipc-client.c b/japokmsg/ipc-client.c new file mode 100644 index 00000000..3d0d9a2a --- /dev/null +++ b/japokmsg/ipc-client.c @@ -0,0 +1,149 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include +#include +#include "ipc-client.h" +#include "log.h" + +static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'}; + +#define IPC_HEADER_SIZE (sizeof(ipc_magic) + 8) + +char *get_socketpath(void) { + const char *swaysock = getenv("JAPOKWMSOCK"); + if (swaysock) { + return strdup(swaysock); + } + char *line = NULL; + size_t line_size = 0; + FILE *fp = popen("sway --get-socketpath 2>/dev/null", "r"); + if (fp) { + ssize_t nret = getline(&line, &line_size, fp); + pclose(fp); + if (nret > 0) { + // remove trailing newline, if there is one + if (line[nret - 1] == '\n') { + line[nret - 1] = '\0'; + } + return line; + } + } + const char *i3sock = getenv("I3SOCK"); + if (i3sock) { + free(line); + return strdup(i3sock); + } + fp = popen("i3 --get-socketpath 2>/dev/null", "r"); + if (fp) { + ssize_t nret = getline(&line, &line_size, fp); + pclose(fp); + if (nret > 0) { + // remove trailing newline, if there is one + if (line[nret - 1] == '\n') { + line[nret - 1] = '\0'; + } + return line; + } + } + free(line); + return NULL; +} + +int ipc_open_socket(const char *socket_path) { + struct sockaddr_un addr; + int socketfd; + if ((socketfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + sway_abort("Unable to open Unix socket"); + } + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1); + addr.sun_path[sizeof(addr.sun_path) - 1] = 0; + int l = sizeof(struct sockaddr_un); + if (connect(socketfd, (struct sockaddr *)&addr, l) == -1) { + sway_abort("Unable to connect to %s", socket_path); + } + return socketfd; +} + +bool ipc_set_recv_timeout(int socketfd, struct timeval tv) { + if (setsockopt(socketfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) { + sway_log_errno(JAPOKWM_ERROR, "Failed to set ipc recv timeout"); + return false; + } + return true; +} + +struct ipc_response *ipc_recv_response(int socketfd) { + char data[IPC_HEADER_SIZE]; + + size_t total = 0; + while (total < IPC_HEADER_SIZE) { + ssize_t received = recv(socketfd, data + total, IPC_HEADER_SIZE - total, 0); + if (received <= 0) { + sway_abort("Unable to receive IPC response"); + } + total += received; + } + + struct ipc_response *response = malloc(sizeof(struct ipc_response)); + if (!response) { + goto error_1; + } + + memcpy(&response->size, data + sizeof(ipc_magic), sizeof(uint32_t)); + memcpy(&response->type, data + sizeof(ipc_magic) + sizeof(uint32_t), sizeof(uint32_t)); + + char *payload = malloc(response->size + 1); + if (!payload) { + goto error_2; + } + + total = 0; + while (total < response->size) { + ssize_t received = recv(socketfd, payload + total, response->size - total, 0); + if (received < 0) { + sway_abort("Unable to receive IPC response"); + } + total += received; + } + payload[response->size] = '\0'; + response->payload = payload; + + return response; +error_2: + free(response); +error_1: + sway_log(JAPOKWM_ERROR, "Unable to allocate memory for IPC response"); + return NULL; +} + +void free_ipc_response(struct ipc_response *response) { + free(response->payload); + free(response); +} + +char *ipc_single_command(int socketfd, uint32_t type, const char *payload, uint32_t *len) { + char data[IPC_HEADER_SIZE]; + memcpy(data, ipc_magic, sizeof(ipc_magic)); + memcpy(data + sizeof(ipc_magic), len, sizeof(*len)); + memcpy(data + sizeof(ipc_magic) + sizeof(*len), &type, sizeof(type)); + + if (write(socketfd, data, IPC_HEADER_SIZE) == -1) { + sway_abort("Unable to send IPC header"); + } + + if (write(socketfd, payload, *len) == -1) { + sway_abort("Unable to send IPC payload"); + } + + struct ipc_response *resp = ipc_recv_response(socketfd); + char *response = resp->payload; + *len = resp->size; + free(resp); + + return response; +} diff --git a/japokmsg/ipc-client.h b/japokmsg/ipc-client.h new file mode 100644 index 00000000..d3895023 --- /dev/null +++ b/japokmsg/ipc-client.h @@ -0,0 +1,46 @@ +#ifndef _SWAY_IPC_CLIENT_H +#define _SWAY_IPC_CLIENT_H + +#include +#include +#include + +#include "ipc.h" + +/** + * IPC response including type of IPC response, size of payload and the json + * encoded payload string. + */ +struct ipc_response { + uint32_t size; + uint32_t type; + char *payload; +}; + +/** + * Gets the path to the IPC socket from sway. + */ +char *get_socketpath(void); +/** + * Opens the sway socket. + */ +int ipc_open_socket(const char *socket_path); +/** + * Issues a single IPC command and returns the buffer. len will be updated with + * the length of the buffer returned from sway. + */ +char *ipc_single_command(int socketfd, uint32_t type, const char *payload, uint32_t *len); +/** + * Receives a single IPC response and returns an ipc_response. + */ +struct ipc_response *ipc_recv_response(int socketfd); +/** + * Free ipc_response struct + */ +void free_ipc_response(struct ipc_response *response); +/** + * Sets the receive timeout for the IPC socket + */ +bool ipc_set_recv_timeout(int socketfd, struct timeval tv); + +#endif diff --git a/japokmsg/ipc.h b/japokmsg/ipc.h new file mode 100644 index 00000000..01bbe4c9 --- /dev/null +++ b/japokmsg/ipc.h @@ -0,0 +1,11 @@ +#ifndef _SWAY_IPC_H +#define _SWAY_IPC_H + +#define event_mask(ev) (1 << (ev & 0x7F)) + +enum ipc_command_type { + // i3 command types - see i3's I3_REPLY_TYPE constants + IPC_COMMAND = 0, +}; + +#endif diff --git a/japokmsg/japokmsg.1.scd b/japokmsg/japokmsg.1.scd new file mode 100644 index 00000000..b69013b5 --- /dev/null +++ b/japokmsg/japokmsg.1.scd @@ -0,0 +1,120 @@ +swaymsg(1) + +# NAME + +swaymsg - Send messages to a running instance of sway over the IPC socket. + +# SYNOPSIS + +_swaymsg_ [options...] [message] + +# OPTIONS + +*-h, --help* + Show help message and quit. + +*-m, --monitor* + Monitor for responses until killed instead of exiting after the first + response. This can only be used with the IPC message type _subscribe_. If + there is a malformed response or an invalid event type was requested, + swaymsg will stop monitoring and exit. + +*-p, --pretty* + Use pretty output even when not using a tty. + +*-q, --quiet* + Sends the IPC message but does not print the response from sway. + +*-r, --raw* + Use raw output even if using a tty. + +*-s, --socket* + Use the specified socket path. Otherwise, swaymsg will ask sway where the + socket is (which is the value of $SWAYSOCK, then of $I3SOCK). + +*-t, --type* + Specify the type of IPC message. See below. + +*-v, --version* + Print the version (of swaymsg) and quit. + +# IPC MESSAGE TYPES + +** + The message is a sway command (the same commands you can bind to keybindings + in your sway config file). It will be executed immediately. + + See *sway*(5) for a list of commands. + + Tips: + - Command expansion is performed twice: once by swaymsg, and again by sway. + If you have quoted multi-word strings in your command, enclose the entire + command in single-quotes. For example, use + _swaymsg 'output "Foobar Display" enable'_ instead of + _swaymsg output "Foobar Display" enable_. Furthermore, note that comma + separated options also count as multi-word strings, because commas can be + used to execute commands on the same line. + - If you are providing a command that contains a leading hyphen (_-_), insert + two hyphens (_--_) before the command to signal to swaymsg not to parse + anything beyond that point as an option. For example, use + _swaymsg -- mark --add test_ instead of _swaymsg mark --add test_. + +*get\_workspaces* + Gets a JSON-encoded list of workspaces and their status. + +*get\_inputs* + Gets a JSON-encoded list of current inputs. + +*get\_outputs* + Gets a JSON-encoded list of current outputs. + +*get\_tree* + Gets a JSON-encoded layout tree of all open windows, containers, outputs, + workspaces, and so on. + +*get\_seats* + Gets a JSON-encoded list of all seats, + its properties and all assigned devices. + +*get\_marks* + Get a JSON-encoded list of marks. + +*get\_bar\_config* + Get a JSON-encoded configuration for swaybar. + +*get\_version* + Get JSON-encoded version information for the running instance of sway. + +*get\_binding\_modes* + Gets a JSON-encoded list of currently configured binding modes. + +*get\_binding\_state* + Gets JSON-encoded info about the current binding state. + +*get\_config* + Gets a JSON-encoded copy of the current configuration. + +*send\_tick* + Sends a tick event to all subscribed clients. + +*subscribe* + Subscribe to a list of event types. The argument for this type should be + provided in the form of a valid JSON array. If any of the types are invalid + or if a valid JSON array is not provided, this will result in a failure. + +# RETURN CODES + +*0* + Success + +*1* + swaymsg errors such as invalid syntax, unable to connect to the ipc socket + or unable to parse sway's reply + +*2* + Sway returned an error when processing the command (ex. invalid command, + command failed, and invalid subscription request) + +# SEE ALSO + +*sway*(5) *sway-bar*(5) *sway-input*(5) *sway-output*(5) *sway-ipc*(7) diff --git a/japokmsg/log.c b/japokmsg/log.c new file mode 100644 index 00000000..0140aa40 --- /dev/null +++ b/japokmsg/log.c @@ -0,0 +1,126 @@ +#define _POSIX_C_SOURCE 200112L +#include +#include +#include +#include +#include +#include +#include "log.h" + +static terminate_callback_t log_terminate = exit; + +void _sway_abort(const char *format, ...) { + va_list args; + va_start(args, format); + _sway_vlog(JAPOKWM_ERROR, format, args); + va_end(args); + log_terminate(EXIT_FAILURE); +} + +bool _sway_assert(bool condition, const char *format, ...) { + if (condition) { + return true; + } + + va_list args; + va_start(args, format); + _sway_vlog(JAPOKWM_ERROR, format, args); + va_end(args); + +#ifndef NDEBUG + raise(SIGABRT); +#endif + + return false; +} + +static bool colored = true; +static sway_log_importance_t log_importance = JAPOKWM_ERROR; +static struct timespec start_time = {-1, -1}; + +static const char *verbosity_colors[] = { + [JAPOKWM_SILENT] = "", + [JAPOKWM_ERROR ] = "\x1B[1;31m", + [JAPOKWM_INFO ] = "\x1B[1;34m", + [JAPOKWM_DEBUG ] = "\x1B[1;90m", +}; + +static const char *verbosity_headers[] = { + [JAPOKWM_SILENT] = "", + [JAPOKWM_ERROR] = "[ERROR]", + [JAPOKWM_INFO] = "[INFO]", + [JAPOKWM_DEBUG] = "[DEBUG]", +}; + +static void timespec_sub(struct timespec *r, const struct timespec *a, + const struct timespec *b) { + const long NSEC_PER_SEC = 1000000000; + r->tv_sec = a->tv_sec - b->tv_sec; + r->tv_nsec = a->tv_nsec - b->tv_nsec; + if (r->tv_nsec < 0) { + r->tv_sec--; + r->tv_nsec += NSEC_PER_SEC; + } +} + +static void init_start_time(void) { + if (start_time.tv_sec >= 0) { + return; + } + clock_gettime(CLOCK_MONOTONIC, &start_time); +} + +static void sway_log_stderr(sway_log_importance_t verbosity, const char *fmt, + va_list args) { + init_start_time(); + + if (verbosity > log_importance) { + return; + } + + struct timespec ts = {0}; + clock_gettime(CLOCK_MONOTONIC, &ts); + timespec_sub(&ts, &ts, &start_time); + + fprintf(stderr, "%02d:%02d:%02d.%03ld ", (int)(ts.tv_sec / 60 / 60), + (int)(ts.tv_sec / 60 % 60), (int)(ts.tv_sec % 60), + ts.tv_nsec / 1000000); + + unsigned c = (verbosity < SWAY_LOG_IMPORTANCE_LAST) ? verbosity : + SWAY_LOG_IMPORTANCE_LAST - 1; + + if (colored && isatty(STDERR_FILENO)) { + fprintf(stderr, "%s", verbosity_colors[c]); + } else { + fprintf(stderr, "%s ", verbosity_headers[c]); + } + + vfprintf(stderr, fmt, args); + + if (colored && isatty(STDERR_FILENO)) { + fprintf(stderr, "\x1B[0m"); + } + fprintf(stderr, "\n"); +} + +void log_init(sway_log_importance_t verbosity, terminate_callback_t callback) { + init_start_time(); + + if (verbosity < SWAY_LOG_IMPORTANCE_LAST) { + log_importance = verbosity; + } + if (callback) { + log_terminate = callback; + } +} + +void _sway_vlog(sway_log_importance_t verbosity, const char *fmt, va_list args) { + sway_log_stderr(verbosity, fmt, args); +} + +void _sway_log(sway_log_importance_t verbosity, const char *fmt, ...) { + va_list args; + va_start(args, fmt); + sway_log_stderr(verbosity, fmt, args); + va_end(args); +} diff --git a/japokmsg/log.h b/japokmsg/log.h new file mode 100644 index 00000000..96853e44 --- /dev/null +++ b/japokmsg/log.h @@ -0,0 +1,58 @@ +#ifndef _SWAY_LOG_H +#define _SWAY_LOG_H + +#include +#include +#include +#include + +typedef enum { + JAPOKWM_SILENT = 0, + JAPOKWM_ERROR = 1, + JAPOKWM_INFO = 2, + JAPOKWM_DEBUG = 3, + SWAY_LOG_IMPORTANCE_LAST, +} sway_log_importance_t; + +#ifdef __GNUC__ +#define ATTRIB_PRINTF(start, end) __attribute__((format(printf, start, end))) +#else +#define ATTRIB_PRINTF(start, end) +#endif + +void error_handler(int sig); + +typedef void (*terminate_callback_t)(int exit_code); + +// Will log all messages less than or equal to `verbosity` +// The `terminate` callback is called by `sway_abort` +void log_init(sway_log_importance_t verbosity, terminate_callback_t terminate); + +void _sway_log(sway_log_importance_t verbosity, const char *format, ...) ATTRIB_PRINTF(2, 3); +void _sway_vlog(sway_log_importance_t verbosity, const char *format, va_list args) ATTRIB_PRINTF(2, 0); +void _sway_abort(const char *filename, ...) ATTRIB_PRINTF(1, 2); +bool _sway_assert(bool condition, const char* format, ...) ATTRIB_PRINTF(2, 3); + +#ifdef SWAY_REL_SRC_DIR +// strip prefix from __FILE__, leaving the path relative to the project root +#define _SWAY_FILENAME ((const char *)__FILE__ + sizeof(SWAY_REL_SRC_DIR) - 1) +#else +#define _SWAY_FILENAME __FILE__ +#endif + +#define sway_log(verb, fmt, ...) \ + _sway_log(verb, "[%s:%d] " fmt, _SWAY_FILENAME, __LINE__, ##__VA_ARGS__) + +#define sway_vlog(verb, fmt, args) \ + _sway_vlog(verb, "[%s:%d] " fmt, _SWAY_FILENAME, __LINE__, args) + +#define sway_log_errno(verb, fmt, ...) \ + sway_log(verb, fmt ": %s", ##__VA_ARGS__, strerror(errno)) + +#define sway_abort(FMT, ...) \ + _sway_abort("[%s:%d] " FMT, _SWAY_FILENAME, __LINE__, ##__VA_ARGS__) + +#define sway_assert(COND, FMT, ...) \ + _sway_assert(COND, "[%s:%d] %s:" FMT, _SWAY_FILENAME, __LINE__, __PRETTY_FUNCTION__, ##__VA_ARGS__) + +#endif diff --git a/japokmsg/main.c b/japokmsg/main.c new file mode 100644 index 00000000..71bb48f6 --- /dev/null +++ b/japokmsg/main.c @@ -0,0 +1,196 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "stringop.h" +#include "ipc-client.h" +#include "log.h" + +static bool success_object(json_object *result) { + json_object *success; + + if (!json_object_object_get_ex(result, "success", &success)) { + return true; + } + + return json_object_get_boolean(success); +} + +// Iterate results array and return false if any of them failed +static bool success(json_object *r, bool fallback) { + if (!json_object_is_type(r, json_type_array)) { + if (json_object_is_type(r, json_type_object)) { + return success_object(r); + } + return fallback; + } + + size_t results_len = json_object_array_length(r); + if (!results_len) { + return fallback; + } + + for (size_t i = 0; i < results_len; ++i) { + json_object *result = json_object_array_get_idx(r, i); + + if (!success_object(result)) { + return false; + } + } + + return true; +} + +static void pretty_print_cmd(json_object *r) { + json_object *error; + bool is_error_defined = json_object_object_get_ex(r, "error", &error); + if (success_object(r)) { + printf("%s\n", json_object_get_string(error)); + } else { + if (!is_error_defined) { + printf("An unknown error occurred"); + } else { + printf("Error: %s\n", json_object_get_string(error)); + } + } +} + +static void pretty_print(int type, json_object *resp) { + printf("%s\n", json_object_to_json_string_ext(resp, + JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED)); + + json_object *obj; + size_t len = json_object_array_length(resp); + for (size_t i = 0; i < len; ++i) { + obj = json_object_array_get_idx(resp, i); + pretty_print_cmd(obj); + } +} + +int main(int argc, char **argv) { + static bool quiet = false; + char *socket_path = NULL; + char *cmdtype = NULL; + + log_init(JAPOKWM_INFO, NULL); + + static struct option long_options[] = { + {"help", no_argument, NULL, 'h'}, + {"pretty", no_argument, NULL, 'p'}, + {"quiet", no_argument, NULL, 'q'}, + {"config", required_argument, NULL, 'c'}, + {"socket", required_argument, NULL, 's'}, + {"type", required_argument, NULL, 't'}, + {"version", no_argument, NULL, 'v'}, + {0, 0, 0, 0} + }; + + const char *usage = + "Usage: japokmsg [options] [message]\n" + "\n" + " -h, --help Show help message and quit.\n" + " -m, --monitor Monitor until killed (-t SUBSCRIBE only)\n" + " -q, --quiet Be quiet.\n" + " -s, --socket Use the specified socket.\n" + " -t, --type Specify the message type.\n" + " -v, --version Show the version number and quit.\n"; + + int c; + while (1) { + int option_index = 0; + c = getopt_long(argc, argv, "hmqs:t:v", long_options, &option_index); + if (c == -1) { + break; + } + switch (c) { + case 'q': // Quiet + quiet = true; + break; + case 's': // Socket + socket_path = strdup(optarg); + break; + case 't': // Type + cmdtype = strdup(optarg); + break; + case 'v': + fprintf(stdout, "japokmsg version " JAPOKWM_VERSION "\n"); + exit(EXIT_SUCCESS); + break; + default: + fprintf(stderr, "%s", usage); + exit(EXIT_FAILURE); + } + } + + if (!cmdtype) { + cmdtype = strdup("command"); + } + if (!socket_path) { + socket_path = get_socketpath(); + if (!socket_path) { + if (quiet) { + exit(EXIT_FAILURE); + } + sway_abort("Unable to retrieve socket path"); + } + } + + uint32_t type = IPC_COMMAND; + + if (strcasecmp(cmdtype, "command") == 0) { + type = IPC_COMMAND; + } + + free(cmdtype); + + char *command = NULL; + if (optind < argc) { + command = join_args(argv + optind, argc - optind); + } else { + command = strdup(""); + } + + int ret = 0; + int socketfd = ipc_open_socket(socket_path); + struct timeval timeout = {.tv_sec = 3, .tv_usec = 0}; + ipc_set_recv_timeout(socketfd, timeout); + uint32_t len = strlen(command); + char *resp = ipc_single_command(socketfd, type, command, &len); + + printf("works\n"); + // pretty print the json + json_object *obj = json_tokener_parse(resp); + if (obj == NULL) { + if (!quiet) { + fprintf(stderr, "ERROR: Could not parse json response from ipc. " + "This is a bug in japokwm."); + printf("%s\n", resp); + } + ret = 1; + } else { + if (!success(obj, true)) { + ret = 2; + } + printf("quiet: %i\n", quiet); + if (!quiet) { + printf("pretty print\n"); + pretty_print(type, obj); + } + json_object_put(obj); + } + free(command); + free(resp); + close(socketfd); + + free(socket_path); + return ret; +} diff --git a/japokmsg/meson.build b/japokmsg/meson.build new file mode 100644 index 00000000..21340b8a --- /dev/null +++ b/japokmsg/meson.build @@ -0,0 +1,13 @@ +japokmsgFiles = files( + 'main.c', + 'ipc-client.c', + 'log.c', + ) + +executable( + 'japokmsg', + [commonSrcs, japokmsgFiles], + include_directories: include_dirs, + dependencies: [deps], + install: true +) diff --git a/japokwm.desktop b/japokwm.desktop new file mode 100644 index 00000000..08d56855 --- /dev/null +++ b/japokwm.desktop @@ -0,0 +1,5 @@ +[Desktop Entry] +Name=Japokwm +Comment=A Wayland compositor based on wlroots and dwl +Exec=japokwm +Type=Application diff --git a/man/japokwm.5.scd b/man/japokwm.5.scd index 03755cad..bf157dcb 100644 --- a/man/japokwm.5.scd +++ b/man/japokwm.5.scd @@ -112,27 +112,51 @@ integers when a layout is created. :< {"", function() print("test") end} - # NAMESPACES [[ *Namespace* -:< *Description* -| *action* -:< Execute an action +:[ *Description* +| action +: Execute an action | config -:< Configure certain properties or the behavior of the window manager +: Configure certain properties or the behavior of the window manager | container -:< Configure certain properties or the behavior of a container +: Configure certain properties or the behavior of a container | event -:< Bind a function to an event +: Bind a function to an event | layout -:< Configure certain properties or the behavior of a layout +: Configure certain properties or the behavior of a layout | l -:< Configure things locally to the layout +: Configure things locally to the layout + +# EVENTS +[[ *Event name* +:[ *when* +:[ *signature* +:[ *Description* +| on_start +: once when the windowmanager starts +: void function() +: +| on_focus +: when a new window is focused +: void function(int i) +: i: container id +| on_update +: when the state of the layout is updated +: void function(int i) +: i: count of containers +| on_create_container +: when a container is created +: void function(int i) +: i: container id # ACTION *arrange()* ++ Arrange containers to the given information +*create_output()* ++ + just create another output + *decrease_nmaster()* ++ decrease the number of master windows @@ -231,8 +255,14 @@ integers *swap_workspace(i, i2)* swap the workspace i with the workspace i2 ++ ++ - i: integer workspace at i ++ - i2: integer workspace at i2 + i: integer - workspace at i ++ + i2: integer - workspace at i2 + +*tag_view(i)* + tag a view + ++ + i: integer - an integer representing a binary number where each digit + represents a workspace that will be toggled *toggle_bars()* ++ toggles layer_shell bars @@ -274,34 +304,13 @@ integers sticky: boolean - whether container is sticky # EVENT -*set_update_function(func)* ++ - set the update function ++ - ++ - func: function(n) - it will be called every time the another ++ - layout_element will be loaded ++ - ++ - n: integer - n represents the amount of containers - -*set_on_focus_function(func)* ++ - set the update function ++ +*add_listener(event_name, func)* ++ + add a add_listener to an event ++ ++ - func: function(n) - it will be called every time a new container is ++ - selected layout_element will be loaded ++ + event_name: string - identifier of the event ++ + func: function(n) - the function that will be called on event specified by event_name ++ ++ - n: integer - n represents the current container position - -*set_on_start_function(func)* ++ - set the function that is executed on start of the windowmanager. - ++ - func: function() - it will be called when the windowmanager launches - -*set_create_container_function()* ++ - set the create_container function - ++ - func: function(n) - it will be called every time a container is created ++ - by Japokwm ++ - ++ - n: integer - n represents the current container position + n: integer - n represents the amount of containers # LAYOUT *set(name, layout_data)* ++ @@ -324,7 +333,17 @@ integers *get_nmaster()* ++ get the number of master windows ++ ++ - integer - number of master windows + integer - number of master windows + +*get_root_area()* ++ + get the root area + ++ + table(x, y, width, height) ++ + ++ + x: integer - x coordinate ++ + y: integer - y coordinate ++ + width: integer - width of the root area ++ + height: integer - height of the root area *get_next_empty_workspace()* ++ Get next workspaces not used by any window @@ -354,6 +373,21 @@ integers boolean - whether it is in limit # CONFIG +*add_mon_rule(rule)* + add a rule for monitors + ++ + rule: rule - + +*add_rule(rule)* + add a rule for containers + ++ + rule: rule - + +*bind_key(bind, func)* + bind a keybinding to a lua function + ++ + bind: keybinding - the binding that will execute the function + func: function() - the function that will be executed *create_layout_set(name, layouts)* create a new layout_set ++ @@ -406,11 +440,6 @@ integers ++ d: direction -*set_keybinds(k)* - set all keybindings existing in Japokwm and overwrite old keybindings ++ - ++ - k: array(keybinding, ...) - *set_layout_constraints(min_width: a, max_width: b, min_height: c, max_height: d)* ++ Set the minimum and maximum dimensions of resizing any window ++ ++ @@ -450,11 +479,6 @@ integers ++ i: integer -*set_monrules(r)* ++ - set the rules applied for the respective monitor ++ - ++ - r: array(monrule) - *set_outer_gaps(i)* ++ set how large the gap between all the windows and the root is ++ ++ @@ -481,16 +505,24 @@ integers ++ d: direction +*set_resize_function(func)* ++ + the function that handles resizing windows in a layout + ++ + func: function(layout_data, o_layout_data, resize_data, n, direction) - the + function that will be called when you resize your containers ++ + ++ + layout_data: layout_data - the data for the current layout ++ + o_layout_data: layout_data - the layout how it originally was ++ + resize_data: array(array(integer, ...)) - which layout_data_elements to + include when resizing ++ + n: float - how much it should resize ++ + direction: direction - which directions to resize to + *set_root_color(color)* ++ set color of the root ++ ++ color: color -*set_rules(r)* ++ - set the rules ++ - ++ - r: array(rule) - *set_sloppy_focus(b)* ++ set whether to use sloppy focus or not. If sloppy focus is activated you ++ will focus windows by hovering above them. ++ @@ -538,14 +570,13 @@ set_master_layout_data ++ set_outer_gaps ++ set_resize_data ++ set_resize_direction ++ +set_resize_function ++ set_sloppy_focus ++ set_smart_hidden_edges ++ set_tile_borderpx ## EVENT -set_create_container_function ++ -set_update_function ++ - +add_listener # SEE ALSO *japokwm*(1) diff --git a/meson.build b/meson.build index efa62399..742ee838 100644 --- a/meson.build +++ b/meson.build @@ -1,37 +1,50 @@ -project('japokwm', 'c') +project('japokwm', 'c', version: '0.3') pkg = import('pkgconfig') wayland_scanner = find_program('wayland-scanner', native: true) -# this variable will be initialized in protocols dir -protocolsDir = 'protocols' -protocols = files( - protocolsDir + '/wlr-layer-shell-unstable-v1.xml', - protocolsDir + '/xdg-shell.xml', - # protocolsDir + '/wlr-foreign-toplevel-management-unstable-v1.xml', - ) root = meson.source_root() -# generate needed protocol files -srcDir = 'src/' -includeDir = 'include/' -inc = include_directories(includeDir, '/usr/include/json-c/') -foreach p:protocols - pName = run_command('basename', p, '.xml').stdout().strip() - postfix = '-protocol' - cFile = srcDir / pName + postfix +'.c' - cHeader = includeDir / pName + postfix + '.h' - # if cFile doesn't exist create Source - if run_command('[', '-f', cFile, ']').returncode() != 0 - run_command(wayland_scanner, 'private-code', p, cFile) - message('generate: ' + cFile) - endif - # if cHeader doesn't exist create Header - if run_command('[', '-f', cHeader, ']').returncode() != 0 - run_command(wayland_scanner, 'server-header', p, cHeader) - message('generate: ' + cHeader) +cc = meson.get_compiler('c') + +# add version macro +version = '"@0@"'.format(meson.project_version()) +git = find_program('git', native: true, required: false) +if git.found() + git_commit = run_command([git, 'rev-parse', '--short', 'HEAD']) + git_branch = run_command([git, 'rev-parse', '--abbrev-ref', 'HEAD']) + if git_commit.returncode() == 0 and git_branch.returncode() == 0 + version = '"@0@-@1@ (" __DATE__ ", branch \'@2@\')"'.format( + meson.project_version(), + git_commit.stdout().strip(), + git_branch.stdout().strip(), + ) endif -endforeach +endif +add_project_arguments('-DJAPOKWM_VERSION=@0@'.format(version), language: 'c') + +if get_option('xwayland') + add_project_arguments('-DJAPOKWM_HAS_XWAYLAND=1'.format(version), language: 'c') +else + add_project_arguments('-DJAPOKWM_HAS_XWAYLAND=0'.format(version), language: 'c') +endif + +if get_option('debug') + add_project_arguments('-DDEBUG=1'.format(version), language: 'c') +else + add_project_arguments('-DDEBUG=0'.format(version), language: 'c') +endif + +datadir = get_option('datadir') +install_data( + 'japokwm.desktop', + install_dir: join_paths(datadir, 'wayland-sessions') +) + +# generate needed protocol files +src_dir = 'src/' +include_dir = 'include/' +include_dirs = include_directories(include_dir, '/usr/include/json-c/') scdoc = dependency('scdoc', version: '>=1.9.2', native: true) if scdoc.found() @@ -60,8 +73,33 @@ if scdoc.found() endforeach endif +commonSrcs = files( + 'common/stringop.c' + ) + +wayland_server = dependency('wayland-server') +wayland_protos = dependency('wayland-protocols', version: '>=1.14') +wayland_client = dependency('wayland-client') +deps = [\ + dependency('xcb'), + dependency('xkbcommon'), + dependency('wayland-egl'), + wayland_server, + wayland_client, + wayland_protos, + dependency('pixman-1'), + dependency('wlroots', version: '>=0.13'), + dependency('x11'), + dependency('json-c'), + dependency('libnotify'), + dependency('threads'), + dependency('libinput'), + ] + # meson.add_install_script('install.sh') # config files subdir('src') +subdir('protocols') subdir('config') subdir('test') +subdir('japokmsg') diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 00000000..87763fff --- /dev/null +++ b/meson_options.txt @@ -0,0 +1 @@ +option('xwayland', type: 'boolean', value: 'false', description: 'Enable support for X11 applications') diff --git a/protocols/meson.build b/protocols/meson.build new file mode 100644 index 00000000..73f270b5 --- /dev/null +++ b/protocols/meson.build @@ -0,0 +1,67 @@ +wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir') +abs_src_dir = join_paths(meson.source_root(), src_dir) +abs_include_dir = join_paths(meson.source_root(), include_dir) + +wayland_scanner_dep = dependency('wayland-scanner', required: false, native: true) +if wayland_scanner_dep.found() + wayland_scanner = find_program( + wayland_scanner_dep.get_pkgconfig_variable('wayland_scanner'), + native: true, + ) +else + wayland_scanner = find_program('wayland-scanner', native: true) +endif + +protocols = [ + join_paths(wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'), + join_paths(wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'), + join_paths(wl_protocol_dir, 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml'), + join_paths(wl_protocol_dir, 'unstable/tablet/tablet-unstable-v2.xml'), + # 'idle.xml', + # 'wlr-input-inhibitor-unstable-v1.xml', + # 'wlr-output-power-management-unstable-v1.xml', +] + +client_protocols = [ + join_paths(wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'), + join_paths(wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'), + 'wlr-layer-shell-unstable-v1.xml', + # 'wlr-input-inhibitor-unstable-v1.xml', +] + +wl_protos_src = [] +wl_protos_headers = [] + +foreach p : protocols + pName = run_command('basename', p, '.xml').stdout().strip() + postfix = '-protocol' + cFile = abs_src_dir / pName + postfix +'.c' + cHeader = abs_include_dir / pName + postfix + '.h' + # if cFile doesn't exist create Source + if run_command('[', '-f', cFile, ']').returncode() != 0 + run_command(wayland_scanner, 'private-code', p, cFile) + message('generate: ' + cFile) + endif + # if cHeader doesn't exist create Header + if run_command('[', '-f', cHeader, ']').returncode() != 0 + run_command(wayland_scanner, 'server-header', p, cHeader) + message('generate: ' + cHeader) + endif +endforeach + +foreach p : client_protocols + pName = run_command('basename', p, '.xml').stdout().strip() + postfix = '-protocol' + cFile = abs_src_dir / pName + postfix +'.c' + cHeader = abs_include_dir / pName + postfix + '.h' + # if cFile doesn't exist create Source + if run_command('[', '-f', cFile, ']').returncode() != 0 + run_command(wayland_scanner, 'private-code', p, cFile) + message('generate: ' + cFile) + endif + # if cHeader doesn't exist create Header + if run_command('[', '-f', cHeader, ']').returncode() != 0 + run_command(wayland_scanner, 'server-header', p, cHeader) + message('generate: ' + cHeader) + endif +endforeach diff --git a/src/.ninja_deps b/src/.ninja_deps new file mode 100644 index 00000000..9650dcf5 Binary files /dev/null and b/src/.ninja_deps differ diff --git a/src/.ninja_log b/src/.ninja_log new file mode 100644 index 00000000..3926244e --- /dev/null +++ b/src/.ninja_log @@ -0,0 +1,11 @@ +# ninja log v5 +1 13 1623155974400398900 japokwm.1 d709be0ae6b3d933 +1 18 1623155974402399083 japokwm.5 2e7c224ea0b4b5e6 +13 407 1623155974795435021 src/libjapokwm_lib.a.p/ipc-json.c.o 822a981322684198 +2 415 1623155974802435661 src/libjapokwm_lib.a.p/command.c.o 51084e7772568fa0 +2 390 1623156018454609383 src/libjapokwm_lib.a.p/ipc-json.c.o 822a981322684198 +1 394 1623156018458609615 src/libjapokwm_lib.a.p/command.c.o 51084e7772568fa0 +1 305 1623156022653848557 src/libjapokwm_lib.a.p/command.c.o 51084e7772568fa0 +2 425 1623156022777855497 src/libjapokwm_lib.a.p/ipc-json.c.o 822a981322684198 +1 281 1623156040327766113 src/libjapokwm_lib.a.p/command.c.o 51084e7772568fa0 +1 426 1623156040478773394 src/libjapokwm_lib.a.p/ipc-json.c.o 822a981322684198 diff --git a/src/bitset/bitset.c b/src/bitset/bitset.c new file mode 100644 index 00000000..c5da7380 --- /dev/null +++ b/src/bitset/bitset.c @@ -0,0 +1,438 @@ +#include "bitset/bitset.h" +#include "utils/vector.h" +#include "utils/coreUtils.h" +#include +#include + +/****************** INTERFACE ******************/ + +BitSet *bitset_create(size_t minimum_number_of_bits) { + BitSet *bitset = calloc(1, sizeof(BitSet)); + size_t number_of_bytes = BITS_TO_BYTES(minimum_number_of_bits); + + bitset->bits = vector_create(number_of_bytes, sizeof(uint8_t)); + + bitset->size = minimum_number_of_bits; + for (size_t byte = 0; byte < number_of_bytes; ++byte) { + bitset_grow(bitset); + } + + return bitset; +} + +BitSet* bitset_copy(BitSet* source) { + assert(source != NULL); + + BitSet *destination = malloc(sizeof(BitSet)); + destination->bits = vector_copy(source->bits); + destination->size = source->size; + + return destination; +} + +int bitset_move(BitSet* destination, BitSet* source) { + assert(destination != NULL); + assert(source != NULL); + + if (destination == NULL) return BITSET_ERROR; + if (source == NULL) return BITSET_ERROR; + + if (vector_move(destination->bits, source->bits) == VECTOR_ERROR) { + return BITSET_ERROR; + } + destination->size = source->size; + + return BITSET_SUCCESS; +} + +int bitset_swap(BitSet* destination, BitSet* source) { + assert(destination != NULL); + assert(source != NULL); + + if (destination == NULL) return BITSET_ERROR; + if (source == NULL) return BITSET_ERROR; + + if (vector_swap(destination->bits, source->bits) == VECTOR_ERROR) { + return BITSET_ERROR; + } + _vector_swap(&destination->size, &source->size); + + return BITSET_SUCCESS; +} + +void bitset_destroy(BitSet* bitset) { + vector_destroy(bitset->bits); + free(bitset); +} + +BitSet *bitset_from_value(uint64_t value) { + BitSet *bitset = bitset_create(64); + for (size_t bit = 0; bit < 64; ++bit) { + bitset_assign(bitset, bit, LAST_BIT(value)); + value <<= 1; + } + + return bitset; +} + +int bitset_equals(BitSet* bitset1, BitSet* bitset2) +{ + BitSet *bitset = bitset_copy(bitset1); + + for (int i = 0; i < bitset->size; i++) { + bitset_toggle(bitset, i); + } + + bitset_and(bitset, bitset2); + bool equals = bitset_none(bitset); + bitset_destroy(bitset); + return equals; +} + +int bit_wise_operation(BitSet* destination, + const BitSet* source, + bit_operator_t bit_operator) { + size_t smaller_size; + + assert(destination != NULL); + assert(source != NULL); + + if (destination == NULL) return BITSET_ERROR; + if (source == NULL) return BITSET_ERROR; + + smaller_size = MIN(destination->size, source->size); + + for (size_t bit = 0; bit < smaller_size; bit++) { + bool first = bitset_test(destination, bit); + const bool second = bitset_test(source, bit); + + bit_operator(&first, &second); + bitset_assign(destination, bit, first); + } + + return BITSET_SUCCESS; +} + +int bitset_and(BitSet* destination, const BitSet* source) { + return bit_wise_operation(destination, source, _bit_and); +} + +int bitset_or(BitSet* destination, const BitSet* source) { + return bit_wise_operation(destination, source, _bit_or); +} + +int bitset_xor(BitSet* destination, const BitSet* source) { + return bit_wise_operation(destination, source, _bit_xor); +} + +int bitset_flip(BitSet* bitset) { + assert(bitset != NULL); + if (bitset == NULL) return BITSET_ERROR; + + VECTOR_FOR_EACH(bitset->bits, iterator) { + uint8_t* byte = iterator_get(&iterator); + *byte = ~(*byte); + } + + return BITSET_SUCCESS; +} + +int bitset_set(BitSet* bitset, size_t index) { + uint8_t* byte; + if ((byte = byte_get(bitset, index)) == NULL) { + return BITSET_ERROR; + } + + *byte |= _bitset_index(index); + + return BITSET_SUCCESS; +} + +int bitset_reset(BitSet* bitset, size_t index) { + uint8_t* byte; + if ((byte = byte_get(bitset, index)) == NULL) { + return BITSET_ERROR; + } + + *byte &= ~(_bitset_index(index)); + + return BITSET_SUCCESS; +} + +int bitset_assign(BitSet* bitset, size_t index, bool value) { + /* + * I estimate that this is more efficient than the usual "reset, then OR in + * the value" pattern + */ + if (value) { + return bitset_set(bitset, index); + } else { + return bitset_reset(bitset, index); + } +} + +int bitset_toggle(BitSet* bitset, size_t index) { + uint8_t* byte; + if ((byte = byte_get(bitset, index)) == NULL) { + return BITSET_ERROR; + } + + *byte ^= _bitset_index(index); + + return BITSET_SUCCESS; +} + +int bitset_test(const BitSet* bitset, size_t index) { + const uint8_t* byte; + if ((byte = byte_const_get(bitset, index)) == NULL) { + return BITSET_ERROR; + } + + return (*byte & _bitset_index(index)) != 0; +} + +uint8_t* byte_get(BitSet* bitset, size_t index) { + assert(bitset != NULL); + if (bitset == NULL) return NULL; + + return (uint8_t*)vector_get(bitset->bits, _byte_index(index)); +} + +const uint8_t* byte_const_get(const BitSet* bitset, size_t index) { + assert(bitset != NULL); + if (bitset == NULL) return NULL; + + return (const uint8_t*)vector_const_get(bitset->bits, _byte_index(index)); +} + +int bitset_msb(BitSet* bitset) { + return bitset_test(bitset, 0); +} + +int bitset_lsb(BitSet* bitset) { + assert(bitset != NULL); + if (bitset == NULL) return BITSET_ERROR; + + return bitset_test(bitset, bitset->size - 1); +} + +int bitset_reset_all(BitSet* bitset) { + return bitset_set_all_to_mask(bitset, 0); +} + +int bitset_set_all(BitSet* bitset) { + return bitset_set_all_to_mask(bitset, 0xff); +} + +int bitset_set_all_to_mask(BitSet* bitset, uint8_t mask) { + assert(bitset != NULL); + if (bitset == NULL) return BITSET_ERROR; + + VECTOR_FOR_EACH(bitset->bits, byte) { + ITERATOR_GET_AS(uint8_t, &byte) = mask; + } + + return BITSET_SUCCESS; +} + +int bitset_clear(BitSet* bitset) { + assert(bitset != NULL); + if (bitset == NULL) return BITSET_ERROR; + bitset->size = 0; + + return vector_clear(bitset->bits); +} + +/* Size Management */ +int bitset_push(BitSet* bitset, bool value) { + if (value) { + return bitset_push_one(bitset); + } else { + return bitset_push_zero(bitset); + } +} + +int bitset_push_one(BitSet* bitset) { + if (_bitset_increment_size(bitset) == BITSET_ERROR) { + return BITSET_ERROR; + } + + if (bitset_set(bitset, bitset->size - 1) == BITSET_ERROR) { + return BITSET_ERROR; + } + + return BITSET_SUCCESS; +} + +int bitset_push_zero(BitSet* bitset) { + if (_bitset_increment_size(bitset) == BITSET_ERROR) { + return BITSET_ERROR; + } + + if (bitset_reset(bitset, bitset->size - 1) == BITSET_ERROR) { + return BITSET_ERROR; + } + + return BITSET_SUCCESS; +} + +int bitset_pop(BitSet* bitset) { + if (--bitset->size % 8 == 0) { + return bitset_shrink(bitset); + } + return BITSET_SUCCESS; +} + +/* Capacity Management */ +int bitset_reserve(BitSet* bitset, size_t minimum_number_of_bits) { + assert(bitset != NULL); + if (bitset == NULL) return BITSET_ERROR; + + /* ERROR/SUCCESS flags are the same */ + return vector_reserve(bitset->bits, BITS_TO_BYTES(minimum_number_of_bits)); +} + +int bitset_grow(BitSet* bitset) { + uint8_t empty = 0; + + assert(bitset != NULL); + if (bitset == NULL) return BITSET_ERROR; + + /* ERROR/SUCCESS flags are the same */ + return vector_push_back(bitset->bits, &empty); +} + +int bitset_shrink(BitSet* bitset) { + assert(bitset != NULL); + if (bitset == NULL) return BITSET_ERROR; + + /* ERROR/SUCCESS flags are the same */ + return vector_pop_back(bitset->bits); +} + +/* Information */ +bool bitset_is_initialized(const BitSet* bitset) { + assert(bitset != NULL); + if (bitset == NULL) return false; + return vector_is_initialized(bitset->bits); +} + +size_t bitset_capacity(const BitSet* bitset) { + assert(bitset != NULL); + if (bitset == NULL) return false; + return bitset->bits->size * 8; +} + +size_t bitset_size_in_bytes(const BitSet* bitset) { + assert(bitset != NULL); + if (bitset == NULL) return false; + return BITS_TO_BYTES(bitset->size); +} + +int bitset_count(BitSet* bitset) { + size_t count; + + assert(bitset != NULL); + if (bitset == NULL) return BITSET_ERROR; + + count = 0; + VECTOR_FOR_EACH(bitset->bits, byte) { + count += _byte_popcount(ITERATOR_GET_AS(uint8_t, &byte)); + } + + return count; +} + +int bitset_all(BitSet* bitset) { + assert(bitset != NULL); + if (bitset == NULL) return BITSET_ERROR; + + VECTOR_FOR_EACH(bitset->bits, byte) { + if (ITERATOR_GET_AS(uint8_t, &byte) != 0xff) { + return false; + } + } + + return true; +} + +int bitset_any(BitSet* bitset) { + assert(bitset != NULL); + if (bitset == NULL) return BITSET_ERROR; + + VECTOR_FOR_EACH(bitset->bits, byte) { + if (ITERATOR_GET_AS(uint8_t, &byte) != 0) { + return true; + } + } + + return false; +} + +int bitset_none(BitSet* bitset) { + assert(bitset != NULL); + if (bitset == NULL) return BITSET_ERROR; + + VECTOR_FOR_EACH(bitset->bits, byte) { + if (ITERATOR_GET_AS(uint8_t, &byte) != 0) { + return false; + } + } + + return true; +} + +void print_bitset(BitSet *bitset) +{ + for (int i = 0; i < bitset->size; i++) { + debug_print("%i\n", bitset_test(bitset, i)); + } +} + +/****************** PRIVATE ******************/ + +uint8_t _byte_popcount(uint8_t value) { + value = (value & POPCOUNT_MASK1) + ((value >> 1) & POPCOUNT_MASK1); + value = (value & POPCOUNT_MASK2) + ((value >> 2) & POPCOUNT_MASK2); + value = (value & POPCOUNT_MASK3) + ((value >> 4) & POPCOUNT_MASK3); + + return value; +} + +int _bitset_increment_size(BitSet* bitset) { + assert(bitset != NULL); + if (bitset == NULL) return BITSET_ERROR; + + if (bitset->size++ % 8 == 0) { + uint8_t empty = 0; + if (vector_push_back(bitset->bits, &empty) == VECTOR_ERROR) { + return BITSET_ERROR; + } + } + + return BITSET_SUCCESS; +} + +void _bit_and(bool* first, const bool* second) { + *first &= *second; +} + +void _bit_or(bool* first, const bool* second) { + *first |= *second; +} + +void _bit_xor(bool* first, const bool* second) { + *first ^= *second; +} + +size_t _byte_index(size_t index) { + return index / 8; +} + +uint8_t _bitset_index(size_t index) { + return 1 << _bitset_offset(index); +} + +uint8_t _bitset_offset(size_t index) { + return 7 - (index % 8); +} diff --git a/src/client.c b/src/client.c index 3a544769..85a7c830 100644 --- a/src/client.c +++ b/src/client.c @@ -8,7 +8,6 @@ #include "container.h" #include "popup.h" #include "server.h" -#include "tile/tile.h" #include "tile/tileUtils.h" #include "utils/coreUtils.h" #include "utils/parseConfigUtils.h" @@ -98,26 +97,35 @@ static void unfocus_client(struct client *c) } } -void focus_client(struct client *old, struct client *c) +void focus_surface(struct seat *seat, struct wlr_surface *surface) { - struct wlr_keyboard *kb = wlr_seat_get_keyboard(server.seat); + assert(surface != NULL); + struct wlr_seat *wlr_seat = seat->wlr_seat; + struct wlr_keyboard *kb = wlr_seat_get_keyboard(wlr_seat); + + /* Have a client, so focus its top-level wlr_surface */ + wlr_seat_keyboard_notify_enter(wlr_seat, surface, kb->keycodes, + kb->num_keycodes, &kb->modifiers); +} + +void focus_client(struct seat *seat, struct client *old, struct client *c) +{ struct wlr_surface *old_surface = get_base_wlrsurface(old); struct wlr_surface *new_surface = get_base_wlrsurface(c); if (old_surface != new_surface) { + cursor_constrain(seat->cursor, NULL); unfocus_client(old); } /* Update wlroots'c keyboard focus */ if (!c) { - /* With no client, all we have left is to clear focus */ - wlr_seat_keyboard_notify_clear_focus(server.seat); + /* struct wlr_seat *wlr_seat = seat->wlr_seat; */ + wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat); return; } - - /* Have a client, so focus its top-level wlr_surface */ - wlr_seat_keyboard_notify_enter(server.seat, get_wlrsurface(c), kb->keycodes, - kb->num_keycodes, &kb->modifiers); + /* Update wlroots'c keyboard focus */ + focus_surface(seat, get_wlrsurface(c)); /* Activate the new client */ switch (c->type) { @@ -212,10 +220,10 @@ void client_handle_set_app_id(struct wl_listener *listener, void *data) void reset_tiled_client_borders(int border_px) { - for (int i = 0; i < server.normal_clients.length; i++) { - struct client *c = server.normal_clients.items[i]; - struct workspace *ws = get_workspace(selected_monitor->ws_id); - if (!exist_on(c->con, ws)) + for (int i = 0; i < server.normal_clients->len; i++) { + struct client *c = g_ptr_array_index(server.normal_clients, i); + struct tagset *tagset = selected_monitor->tagset; + if (!exist_on(tagset, c->con)) continue; if (c->con->floating) continue; @@ -225,133 +233,13 @@ void reset_tiled_client_borders(int border_px) void reset_floating_client_borders(int border_px) { - for (int i = 0; i < server.normal_clients.length; i++) { - struct client *c = server.normal_clients.items[i]; - struct workspace *ws = get_workspace(selected_monitor->ws_id); - if (!exist_on(c->con, ws)) + for (int i = 0; i < server.normal_clients->len; i++) { + struct client *c = g_ptr_array_index(server.normal_clients, i); + struct tagset *tagset = selected_monitor->tagset; + if (!exist_on(tagset, c->con)) continue; if (!c->con->floating) continue; c->bw = border_px; } } - -bool wants_floating(struct client *c) -{ - if (c->type != X11_MANAGED && c->type != X11_UNMANAGED) - return false; - - struct wlr_xwayland_surface *surface = c->surface.xwayland; - struct xwayland xwayland = server.xwayland; - - if (surface->modal) - return true; - - for (size_t i = 0; i < surface->window_type_len; ++i) { - xcb_atom_t type = surface->window_type[i]; - if (type == xwayland.atoms[NET_WM_WINDOW_TYPE_DIALOG] || - type == xwayland.atoms[NET_WM_WINDOW_TYPE_UTILITY] || - type == xwayland.atoms[NET_WM_WINDOW_TYPE_TOOLBAR] || - type == xwayland.atoms[NET_WM_WINDOW_TYPE_POPUP_MENU] || - type == xwayland.atoms[NET_WM_WINDOW_TYPE_SPLASH]) { - return true; - } - } - - struct wlr_xwayland_surface_size_hints *size_hints = surface->size_hints; - if (size_hints != NULL && - size_hints->min_width > 0 && size_hints->min_height > 0 && - (size_hints->max_width == size_hints->min_width || - size_hints->max_height == size_hints->min_height)) { - return true; - } - - return false; -} - -bool is_popup_menu(struct client *c) -{ - struct wlr_xwayland_surface *surface = c->surface.xwayland; - struct xwayland xwayland = server.xwayland; - for (size_t i = 0; i < surface->window_type_len; ++i) { - xcb_atom_t type = surface->window_type[i]; - if (type == xwayland.atoms[NET_WM_WINDOW_TYPE_POPUP_MENU] || - type == xwayland.atoms[NET_WM_WINDOW_TYPE_POPUP] || - type == xwayland.atoms[NET_WM_WINDOW_TYPE_MENU] || - type == xwayland.atoms[NET_WM_WINDOW_TYPE_NORMAL]) { - return true; - } - } - return false; -} - -void destroy_notify(struct wl_listener *listener, void *data) -{ - /* Called when the surface is destroyed and should never be shown again. */ - struct client *c = wl_container_of(listener, c, destroy); - - wl_list_remove(&c->map.link); - wl_list_remove(&c->unmap.link); - wl_list_remove(&c->destroy.link); - - switch (c->type) { - case LAYER_SHELL: - wl_list_remove(&c->new_popup.link); - break; - case XDG_SHELL: - wl_list_remove(&c->new_popup.link); - wl_list_remove(&c->set_title.link); - wl_list_remove(&c->set_app_id.link); - break; - case X11_MANAGED: - wl_list_remove(&c->set_title.link); - default: - break; - } - - destroy_client(c); -} - -void maprequest(struct wl_listener *listener, void *data) -{ - /* Called when the surface is mapped, or ready to display on-screen. */ - struct client *c = wl_container_of(listener, c, map); - - struct monitor *m = selected_monitor; - if (c->type == LAYER_SHELL) { - m = output_to_monitor(c->surface.layer->output); - } - struct workspace *ws = monitor_get_active_workspace(m); - struct layout *lt = ws->layout; - - c->ws_id = ws->id; - c->bw = lt->options.tile_border_px; - - switch (c->type) { - case LAYER_SHELL: - wlr_list_push(&server.non_tiled_clients, c); - break; - default: - wlr_list_push(&server.normal_clients, c); - } - create_container(c, m, true); - - arrange(); - focus_most_recent_container(get_workspace(m->ws_id), FOCUS_NOOP); -} - -void unmap_notify(struct wl_listener *listener, void *data) -{ - /* Called when the surface is unmapped, and should no longer be shown. */ - struct client *c = wl_container_of(listener, c, unmap); - - container_damage_whole(c->con); - destroy_container(c->con); - c->con = NULL; - - remove_in_composed_list(&server.client_lists, cmp_ptr, c); - - arrange(); - struct monitor *m = selected_monitor; - focus_most_recent_container(get_workspace(m->ws_id), FOCUS_NOOP); -} diff --git a/src/clipboard.c b/src/clipboard.c deleted file mode 100644 index 3c08e0ff..00000000 --- a/src/clipboard.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "clipboard.h" -#include "server.h" -#include -#include - -struct wl_listener request_set_psel = {.notify = set_primary_selection}; -struct wl_listener request_set_sel = {.notify = set_selection}; - -void set_primary_selection(struct wl_listener *listener, void *data) -{ - /* This event is raised by the seat when a client wants to set the selection, - * usually when the user copies something. wlroots allows compositors to - * ignore such requests if they so choose, but in dwl we always honor - */ - struct wlr_seat_request_set_primary_selection_event *event = data; - wlr_seat_set_primary_selection(server.seat, event->source, event->serial); -} - -void set_selection(struct wl_listener *listener, void *data) -{ - /* This event is raised by the seat when a client wants to set the selection, - * usually when the user copies something. wlroots allows compositors to - * ignore such requests if they so choose, but in dwl we always honor - */ - struct wlr_seat_request_set_selection_event *event = data; - wlr_seat_set_selection(server.seat, event->source, event->serial); -} diff --git a/src/command.c b/src/command.c index 83119cfd..7adbbead 100644 --- a/src/command.c +++ b/src/command.c @@ -1,62 +1,85 @@ #include "command.h" -#include -#include -#include -#include -#include -#include "workspace.h" -#include "monitor.h" -#include "stringop.h" -#include "keybinding.h" -#include "server.h" - -void execute_command(const char *_exec) -{ - // Split command list - char *exec = strdup(_exec); - char *head = exec; - char matched_delim = ';'; - const char *cmd = argsep(&head, ";,", &matched_delim); - for (; isspace(*cmd); ++cmd) {} +#include + +#include "utils/parseConfigUtils.h" - if (strcmp(cmd, "") == 0) { - printf("Ignoring empty command.\n"); - return; +struct cmd_results *cmd_results_new(enum cmd_status status, + const char *format, ...) { + struct cmd_results *results = malloc(sizeof(struct cmd_results)); + if (!results) { + /* sway_log(SWAY_ERROR, "Unable to allocate command results"); */ + return NULL; } - printf("Handling command '%s'\n", cmd); - //TODO better handling of argv - int argc; - char **argv = split_args(cmd, &argc); - if (strcmp(argv[0], "exec") != 0 && - strcmp(argv[0], "exec_always") != 0 && - strcmp(argv[0], "mode") != 0) { - for (int i = 1; i < argc; ++i) { - if (*argv[i] == '\"' || *argv[i] == '\'') { - strip_quotes(argv[i]); - } + results->status = status; + if (format) { + char *error = malloc(256); + va_list args; + va_start(args, format); + if (error) { + vsnprintf(error, 256, format, args); } + va_end(args); + results->error = error; + } else { + results->error = NULL; } + return results; +} - // execute command - if (strcmp(argv[0], "workspace") == 0) { - bool handled = false; - int i; - for (i = 0; i < server.workspaces.length; i++) { - struct workspace *ws = get_workspace(i); - if (strcmp(ws->name, argv[1]) == 0) { - handled = true; - break; - } - } - if (handled) { - if (key_state_has_modifiers(MOD_SHIFT)) { - focus_workspace(selected_monitor, get_workspace(i)); - } else { - struct workspace *ws = get_workspace(i); - push_selected_workspace(selected_monitor, ws); - } - } +void free_cmd_results(struct cmd_results *results) { + if (results->error) { + free(results->error); } - free(argv); + free(results); +} + +struct cmd_results *cmd_eval(const char *cmd) +{ + // load is the equivalent to eval and was introduced in lua 5.2 + lua_getglobal_safe(L, "load"); + lua_pushstring(L, cmd); + lua_call_safe(L, 1, 1, 0); + // load returns a function pointer to the evaluated expression now we have + // to call this function + int lua_status = lua_pcall(L, 0, 1, 0); + if (lua_status != LUA_OK) { + const char *errmsg = luaL_checkstring(L, -1); + lua_pop(L, 1); + return cmd_results_new(CMD_FAILURE, errmsg); + } + + const char *text = ""; + if (!lua_isnil(L, -1)) { + text = lua_tostring(L, -1); + } + lua_pop(L, 1); + + return cmd_results_new(CMD_SUCCESS, text); +} + +struct cmd_results *execute_command(char *cmd, struct wlr_seat *seat, + struct container *con) { + struct cmd_results *res = cmd_eval(cmd); + return res; +} + +char *cmd_results_to_json(struct cmd_results *results) { + json_object *result_array = json_object_new_array(); + + json_object *root = json_object_new_object(); + json_object_object_add(root, "success", + json_object_new_boolean(results->status == CMD_SUCCESS)); + if (results->error) { + json_object_object_add(root, "parse_error", + json_object_new_boolean(results->status == CMD_INVALID)); + json_object_object_add( + root, "error", json_object_new_string(results->error)); + } + json_object_array_add(result_array, root); + + const char *json = json_object_to_json_string(result_array); + char *res = strdup(json); + json_object_put(result_array); + return res; } diff --git a/src/command/commands.c b/src/command/commands.c new file mode 100644 index 00000000..c61ff9cd --- /dev/null +++ b/src/command/commands.c @@ -0,0 +1,34 @@ +#include "command/commands.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "monitor.h" +#include "command.h" +#include "server.h" +#include "utils/parseConfigUtils.h" +#include "stringop.h" + +struct cmd_results *cmd_create_output(int argc, char **argv) +{ + if (!wlr_backend_is_multi(server.backend)) { + printf("Expected a multi backend\n"); + } + + bool done = false; + wlr_multi_for_each_backend(server.backend, create_output, &done); + + if (!done) { + return cmd_results_new(CMD_INVALID, + "Can only create outputs for Wayland, X11 or headless backends"); + } + + return cmd_results_new(CMD_SUCCESS, NULL); +} diff --git a/src/container.c b/src/container.c index 3429967f..659b7a6a 100644 --- a/src/container.c +++ b/src/container.c @@ -14,8 +14,11 @@ #include "options.h" #include "utils/parseConfigUtils.h" #include "scratchpad.h" -#include "workspace.h" +#include "tagset.h" #include "ipc-server.h" +#include "layer_shell.h" +#include "workspace.h" +#include "rules/rule.h" static void add_container_to_workspace(struct container *con, struct workspace *ws); @@ -25,56 +28,59 @@ struct container *create_container(struct client *c, struct monitor *m, bool has con->client = c; c->con = con; - // must be done for the container damage event - con->commit.notify = commit_notify; - wl_signal_add(&get_wlrsurface(c)->events.commit, &con->commit); - con->alpha = 1.0f; con->has_border = has_border; con->focusable = true; - add_container_to_workspace(con, get_workspace(m->ws_id)); - - struct workspace *ws = monitor_get_active_workspace(m); - struct layout *lt = ws->layout; - struct event_handler *ev = <->options.event_handler; - - call_create_container_function(ev, get_position_in_container_stack(con)); + con->client->ws_id = m->tagset->selected_ws_id; - apply_rules(con); return con; } void destroy_container(struct container *con) { - printf("destroy_container\n"); - // surfaces cant commit anything anymore if their container is destroyed - wl_list_remove(&con->commit.link); + free(con); +} + +void add_container_to_tile(struct container *con) +{ + assert(!con->is_tiled); + add_container_to_workspace(con, get_workspace(con->client->ws_id)); + + struct monitor *m = container_get_monitor(con); + struct layout *lt = get_layout_in_monitor(m); + struct event_handler *ev = lt->options.event_handler; + call_create_container_function(ev, get_position_in_container_stack(con)); + con->is_tiled = true; + + apply_rules(server.default_layout->options.rules, con); +} + +void remove_container_from_tile(struct container *con) +{ + assert(con->is_tiled); if (con->on_scratchpad) remove_container_from_scratchpad(con); - struct workspace *ws = monitor_get_active_workspace(con->m); + struct workspace *ws = get_workspace(con->client->ws_id); - remove_in_composed_list(&ws->focus_stack_lists, cmp_ptr, con); + workspace_remove_container_from_focus_stack(ws, con); switch (con->client->type) { case LAYER_SHELL: - remove_in_composed_list(&server.layer_visual_stack_lists, - cmp_ptr, con); + remove_in_composed_list(server.layer_visual_stack_lists, cmp_ptr, con); break; case X11_UNMANAGED: - remove_in_composed_list(&server.normal_visual_stack_lists, - cmp_ptr, con); - wlr_list_remove(&ws->independent_containers, cmp_ptr, con); + remove_in_composed_list(server.normal_visual_stack_lists, cmp_ptr, con); + workspace_remove_independent_container(ws, con); break; default: - remove_in_composed_list(&server.normal_visual_stack_lists, - cmp_ptr, con); - remove_in_composed_list(&ws->container_lists, cmp_ptr, con); + remove_in_composed_list(server.normal_visual_stack_lists, cmp_ptr, con); + workspace_remove_container(ws, con); break; } - free(con); + con->is_tiled = false; } void container_damage_borders(struct container *con, struct wlr_box *geom) @@ -84,7 +90,11 @@ void container_damage_borders(struct container *con, struct wlr_box *geom) if (!geom) return; - struct monitor *m = con->m; + struct monitor *m = container_get_monitor(con); + + if (!m) + return; + int border_width = con->client->bw; double ox = geom->x - border_width; double oy = geom->y - border_width; @@ -115,15 +125,15 @@ static void damage_container_area(struct container *con, struct wlr_box *geom, static void container_damage(struct container *con, bool whole) { - for (int i = 0; i < server.mons.length; i++) { - struct monitor *m = server.mons.items[i]; + for (int i = 0; i < server.mons->len; i++) { + struct monitor *m = g_ptr_array_index(server.mons, i); damage_container_area(con, &con->geom, m, whole); } struct client *c = con->client; if (c->resized || c->moved_workspace) { - for (int i = 0; i < server.mons.length; i++) { - struct monitor *m = server.mons.items[i]; + for (int i = 0; i < server.mons->len; i++) { + struct monitor *m = g_ptr_array_index(server.mons, i); damage_container_area(con, &con->prev_geom, m, whole); } c->resized = false; @@ -146,12 +156,12 @@ struct container *get_focused_container(struct monitor *m) if (!m) return NULL; - struct workspace *ws = get_workspace(m->ws_id); + struct tagset *tagset = monitor_get_active_tagset(m); - if (!ws) + if (!tagset) return NULL; - return get_in_composed_list(&ws->focus_stack_lists, 0); + return get_in_composed_list(tagset->list_set->focus_stack_lists, 0); } struct container *xy_to_container(double x, double y) @@ -160,12 +170,11 @@ struct container *xy_to_container(double x, double y) if (!m) return NULL; - for (int i = 0; i < length_of_composed_list(&server.visual_stack_lists); i++) { - struct container *con = - get_in_composed_list(&server.visual_stack_lists, i); + for (int i = 0; i < length_of_composed_list(server.visual_stack_lists); i++) { + struct container *con = get_in_composed_list(server.visual_stack_lists, i); if (!con->focusable) continue; - if (!visible_on(con, get_workspace(m->ws_id))) + if (!visible_on(monitor_get_active_tagset(m), con)) continue; if (!wlr_box_contains_point(&con->geom, x, y)) continue; @@ -176,55 +185,33 @@ struct container *xy_to_container(double x, double y) return NULL; } -void add_container_to_composed_list(struct wlr_list *lists, struct container *con, int i) -{ - if (!con) - return; - struct workspace *ws = get_workspace(con->m->ws_id); - if (!ws) - return; - - struct wlr_list *hidden_containers = get_hidden_list(ws); - struct wlr_list *tiled_containers = get_tiled_list(ws); - struct wlr_list *floating_containers = get_floating_list(ws); - if (con->floating) { - wlr_list_insert(floating_containers, i, con); - return; - } - if (con->hidden) { - wlr_list_insert(hidden_containers, i, con); - return; - } - wlr_list_insert(tiled_containers, i, con); -} - static void add_container_to_workspace(struct container *con, struct workspace *ws) { if (!ws || !con) return; - set_container_monitor(con, ws->m); + struct monitor *m = workspace_get_monitor(ws); + set_container_monitor(con, m); switch (con->client->type) { case LAYER_SHELL: // layer shell programs aren't pushed to the stack because they use the // layer system to set the correct render position - add_container_to_stack(con); if (con->client->surface.layer->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND) { con->focusable = false; } break; case X11_UNMANAGED: - wlr_list_insert(&ws->independent_containers, 0, con); + g_ptr_array_insert(ws->list_set->independent_containers, 0, con); add_container_to_stack(con); break; case XDG_SHELL: case X11_MANAGED: - add_container_to_containers(con, ws, 0); + workspace_add_container_to_containers(ws, con, 0); add_container_to_stack(con); break; } - add_container_to_focus_stack(con, ws); + workspace_add_container_to_focus_stack(ws, con); } struct wlr_box get_center_box(struct wlr_box ref) @@ -315,59 +302,41 @@ void apply_bounds(struct container *con, struct wlr_box box) con->geom.y = box.y; } -void apply_rules(struct container *con) -{ - for (int i = 0; i < server.default_layout->options.rule_count; i++) { - const struct rule r = server.default_layout->options.rules[i]; - bool same_id = strcmp(r.id, con->client->app_id) == 0; - bool id_empty = strcmp(r.id, "") == 0; - bool same_title = strcmp(r.title, con->client->title) == 0; - bool title_empty = strcmp(r.title, "") == 0; - if ((same_id || id_empty) && (same_title || title_empty)) { - lua_geti(L, LUA_REGISTRYINDEX, r.lua_func_ref); - struct monitor *m = con->m; - struct workspace *ws = monitor_get_active_workspace(m); - int position = wlr_list_find(&ws->container_lists, cmp_ptr, con); - lua_pushinteger(L, position); - lua_call_safe(L, 1, 0, 0); - } - } -} - void commit_notify(struct wl_listener *listener, void *data) { - struct container *con = wl_container_of(listener, con, commit); + struct client *c = wl_container_of(listener, c, commit); - if (!con) + if (!c) return; - container_damage_part(con); + struct container *con = c->con; + if (con->is_tiled) { + container_damage_part(c->con); + } } -void focus_container(struct container *con, enum focus_actions a) +void focus_container(struct container *con) { if (!con) return; if (!con->focusable) return; + if (con->is_xwayland_popup) + return; if (con->hidden) return; - struct monitor *m = con->m; + struct monitor *m = container_get_monitor(con); + struct tagset *tagset = monitor_get_active_tagset(m); - if (m->ws_id != con->client->ws_id) + if (!visible_on(tagset, con)) return; struct container *sel = get_focused_container(m); - if (a == FOCUS_LIFT) - lift_container(con); - - struct workspace *ws = monitor_get_active_workspace(m); - /* Put the new client atop the focus stack */ - remove_in_composed_list(&ws->focus_stack_lists, cmp_ptr, con); - add_container_to_focus_stack(con, ws); + remove_in_composed_list(tagset->list_set->focus_stack_lists, cmp_ptr, con); + list_set_add_container_to_focus_stack(tagset->list_set, con); struct container *new_sel = get_focused_container(m); @@ -376,13 +345,14 @@ void focus_container(struct container *con, enum focus_actions a) container_damage_borders(sel, &sel->geom); container_damage_borders(new_sel, &new_sel->geom); - struct layout *lt = ws->layout; - call_on_focus_function(<->options.event_handler, + struct layout *lt = tagset_get_layout(tagset); + call_on_focus_function(lt->options.event_handler, get_position_in_container_stack(con)); struct client *old_c = sel ? sel->client : NULL; struct client *new_c = new_sel ? new_sel->client : NULL; - focus_client(old_c, new_c); + struct seat *seat = input_manager_get_default_seat(); + focus_client(seat, old_c, new_c); } void focus_on_stack(struct monitor *m, int i) @@ -392,12 +362,12 @@ void focus_on_stack(struct monitor *m, int i) if (!sel) return; - struct workspace *ws = get_workspace(m->ws_id); - struct wlr_list *visible_container_lists = get_visible_lists(ws); + struct tagset *tagset = monitor_get_active_tagset(m); + GPtrArray *visible_container_lists = tagset_get_visible_lists(tagset); if (sel->client->type == LAYER_SHELL) { - struct container *con = get_container(ws, 0); - focus_container(con, FOCUS_NOOP); + struct container *con = get_container(tagset, 0); + focus_container(con); return; } @@ -406,9 +376,11 @@ void focus_on_stack(struct monitor *m, int i) get_relative_item_in_composed_list(visible_container_lists, sel_index, i); /* If only one client is visible on selMon, then c == sel */ - focus_container(con, FOCUS_LIFT); + focus_container(con); + lift_container(con); } + // TODO refactor void focus_on_hidden_stack(struct monitor *m, int i) { @@ -419,8 +391,8 @@ void focus_on_hidden_stack(struct monitor *m, int i) if (sel->client->type == LAYER_SHELL) return; - struct workspace *ws = monitor_get_active_workspace(m); - struct wlr_list *hidden_containers = get_hidden_list(ws); + struct tagset *tagset = monitor_get_active_tagset(m); + GPtrArray *hidden_containers = tagset_get_hidden_list(tagset); struct container *con = get_relative_item_in_list(hidden_containers, 0, i); if (!con) @@ -437,26 +409,27 @@ void focus_on_hidden_stack(struct monitor *m, int i) /* replace selected container with a hidden one and move the selected * container to the end of the containers array */ - wlr_list_remove(hidden_containers, cmp_ptr, con); + g_ptr_array_remove(hidden_containers, con); - struct wlr_list *visible_container_lists = get_visible_lists(ws); - struct wlr_list *focus_list = find_list_in_composed_list( + GPtrArray *visible_container_lists = tagset_get_visible_lists(tagset); + GPtrArray *focus_list = find_list_in_composed_list( visible_container_lists, cmp_ptr, sel); - int sel_index = wlr_list_find(focus_list, cmp_ptr, sel); + guint sel_index; + g_ptr_array_find(focus_list, sel, &sel_index); - wlr_list_insert(focus_list, sel_index+1, con); - wlr_list_del(focus_list, sel_index); + g_ptr_array_insert(focus_list, sel_index+1, con); + g_ptr_array_remove_index(focus_list, sel_index); if (i < 0) { - wlr_list_insert(hidden_containers, 0, sel); + g_ptr_array_insert(hidden_containers, 0, sel); } else { - wlr_list_push(hidden_containers, sel); + g_ptr_array_add(hidden_containers, sel); } if (!con) return; - focus_container(con, FOCUS_NOOP); + focus_container(con); arrange(); } @@ -467,25 +440,25 @@ void lift_container(struct container *con) if (con->client->type == LAYER_SHELL) return; - remove_in_composed_list(&server.normal_visual_stack_lists, cmp_ptr, con); + remove_in_composed_list(server.normal_visual_stack_lists, cmp_ptr, con); add_container_to_stack(con); } void repush(int pos1, int pos2) { - /* pos1 > pos2 */ struct monitor *m = selected_monitor; - struct workspace *ws = monitor_get_active_workspace(m); + struct tagset *tagset = monitor_get_active_tagset(m); + GPtrArray *tiled_containers = tagset_get_tiled_list(tagset); - struct container *con = ws->tiled_containers.items[pos1]; - - if (!con) + if (pos1 >= tiled_containers->len) return; - if (con->floating) + if (pos2 >= tiled_containers->len) return; - wlr_list_remove(&ws->tiled_containers, cmp_ptr, con); - wlr_list_insert(&ws->tiled_containers, pos2, con); + struct container *con = g_ptr_array_index(tagset->list_set->tiled_containers, pos1); + + g_ptr_array_remove(tiled_containers, con); + g_ptr_array_insert(tiled_containers, pos2, con); arrange(); @@ -494,23 +467,25 @@ void repush(int pos1, int pos2) arrange(); } -void fix_position(struct container *con) +void container_fix_position(struct container *con) { if (!con) return; - struct workspace *ws = monitor_get_active_workspace(con->m); + struct monitor *m = container_get_monitor(con); + struct tagset *tagset = monitor_get_active_tagset(m); - struct wlr_list *tiled_containers = get_tiled_list(ws); - struct wlr_list *floating_containers = get_floating_list(ws); + GPtrArray *tiled_containers = tagset_get_tiled_list(tagset); + GPtrArray *floating_containers = tagset_get_floating_list(tagset); + struct layout *lt = tagset_get_layout(tagset); if (!con->floating) { - wlr_list_remove(floating_containers, cmp_ptr, con); - int position = MIN(tiled_containers->length, ws->layout->n_tiled_max-1); - wlr_list_insert(tiled_containers, position, con); + g_ptr_array_remove(floating_containers, con); + int position = MIN(tiled_containers->len, lt->n_tiled_max-1); + g_ptr_array_insert(tiled_containers, position, con); } else { - wlr_list_remove(tiled_containers, cmp_ptr, con); - wlr_list_insert(floating_containers, 0, con); + g_ptr_array_remove(tiled_containers, con); + g_ptr_array_insert(floating_containers, 0, con); } } @@ -523,9 +498,9 @@ void set_container_floating(struct container *con, void (*fix_position)(struct c if (con->floating == floating) return; - struct monitor *m = con->m; + struct monitor *m = container_get_monitor(con); + struct layout *lt = get_layout_in_monitor(m); struct workspace *ws = monitor_get_active_workspace(m); - struct layout *lt = ws->layout; con->floating = floating; @@ -539,11 +514,11 @@ void set_container_floating(struct container *con, void (*fix_position)(struct c remove_container_from_scratchpad(con); } - wlr_list_remove(&server.floating_visual_stack, cmp_ptr, con); - wlr_list_insert(&server.tiled_visual_stack, 0, con); + g_ptr_array_remove(server.floating_visual_stack, con); + g_ptr_array_insert(server.tiled_visual_stack, 0, con); } else { - wlr_list_remove(&server.tiled_visual_stack, cmp_ptr, con); - wlr_list_insert(&server.floating_visual_stack, 0, con); + g_ptr_array_remove(server.tiled_visual_stack, con); + g_ptr_array_insert(server.floating_visual_stack, 0, con); } lift_container(con); @@ -562,19 +537,14 @@ void set_container_monitor(struct container *con, struct monitor *m) assert(m != NULL); if (!con) return; - if (con->m == m) + if (con->client->m == m) return; - if (con->prev_m != m) - con->prev_m = con->m; - con->m = m; - - /* ensure that prev_m is not = NULL after this function finished - successfully */ - if (con->prev_m == NULL) - con->prev_m = m; + if (con->client->type == LAYER_SHELL) { + con->client->m = m; + } - struct workspace *ws = get_workspace(m->ws_id); + struct workspace *ws = monitor_get_active_workspace(m); set_container_workspace(con, ws); } @@ -609,23 +579,37 @@ void resize_container(struct container *con, struct wlr_cursor *cursor, int offs { struct wlr_box geom = con->geom; - geom.width = absolute_x_to_container_relative(con, cursor->x - offsetx); - geom.height = absolute_y_to_container_relative(con, cursor->y - offsety); + geom.width = absolute_x_to_container_relative(con->geom, cursor->x - offsetx); + geom.height = absolute_y_to_container_relative(con->geom, cursor->y - offsety); if (con->on_scratchpad) { remove_container_from_scratchpad(con); } resize(con, geom); + container_damage(con, true); +} + +struct monitor *container_get_monitor(struct container *con) +{ + if (!con) + return NULL; + if (con->client->m) { + return con->client->m; + } + + struct workspace *ws = get_workspace(con->client->ws_id); + struct monitor *m = workspace_get_monitor(ws); + return m; } -inline int absolute_x_to_container_relative(struct container *con, int x) +inline int absolute_x_to_container_relative(struct wlr_box geom, int x) { - return x - con->geom.x; + return x - geom.x; } -inline int absolute_y_to_container_relative(struct container *con, int y) +inline int absolute_y_to_container_relative(struct wlr_box geom, int y) { - return y - con->geom.y; + return y - geom.y; } int get_position_in_container_stack(struct container *con) @@ -633,17 +617,17 @@ int get_position_in_container_stack(struct container *con) if (!con) return INVALID_POSITION; - struct monitor *m = con->m; - struct workspace *ws = monitor_get_active_workspace(m); - int position = find_in_composed_list(&ws->container_lists, &cmp_ptr, con); + struct monitor *m = container_get_monitor(con); + struct tagset *tagset = monitor_get_active_tagset(m); + int position = find_in_composed_list(tagset->list_set->container_lists, cmp_ptr, con); return position; } struct container *get_container_from_container_stack_position(int i) { struct monitor *m = selected_monitor; - struct workspace *ws = monitor_get_active_workspace(m); - struct container *con = get_in_composed_list(&ws->container_lists, i); + struct tagset *tagset = monitor_get_active_tagset(m); + struct container *con = get_container(tagset, i); return con; } @@ -658,3 +642,65 @@ bool is_resize_not_in_limit(struct wlr_fbox *geom, struct resize_constraints *re return is_width_not_in_limit || is_height_not_in_limit; } + +void set_container_workspace(struct container *con, struct workspace *ws) +{ + if (!con) + return; + if (!ws) + return; + struct monitor *m = container_get_monitor(con); + struct tagset *tagset = monitor_get_active_tagset(m); + if (tagset->selected_ws_id == ws->id) + return; + + struct workspace *old_ws = get_workspace(con->client->ws_id); + + workspace_remove_container(old_ws, con); + workspace_add_container_to_containers(ws, con, 0); + + workspace_remove_container_from_focus_stack(old_ws, con); + workspace_add_container_to_focus_stack(ws, con); + + con->client->ws_id = ws->id; + ws->prev_m = m; + + if (con->floating) + con->client->bw = ws->layout->options.float_border_px; + else + con->client->bw = ws->layout->options.tile_border_px; +} + +void move_container_to_workspace(struct container *con, struct workspace *ws) +{ + if (!ws) + return; + if (!con) + return; + if (con->client->type == LAYER_SHELL) + return; + + set_container_workspace(con, ws); + con->client->moved_workspace = true; + container_damage_whole(con); + + arrange(); + struct monitor *m = container_get_monitor(con); + struct tagset *selected_tagset = monitor_get_active_tagset(m); + focus_most_recent_container(selected_tagset); + + ipc_event_workspace(); +} + + +bool container_is_bar(struct container *con) +{ + switch (con->client->type) { + case LAYER_SHELL: + return layer_shell_is_bar(con); + break; + default: + return false; + break; + } +} diff --git a/src/cursor.c b/src/cursor.c index fb71dab3..762a7263 100644 --- a/src/cursor.c +++ b/src/cursor.c @@ -1,90 +1,263 @@ #include "cursor.h" +#include +#include +#include + #include "container.h" #include "server.h" #include "tile/tileUtils.h" #include "popup.h" #include "keybinding.h" -#include - -struct wl_listener request_set_cursor = {.notify = handle_set_cursor}; +#include "seat.h" +#include "workspace.h" static struct container *grabc = NULL; static int offsetx, offsety; // TODO refactor this function -static void pointer_focus(struct container *con, struct wlr_surface *surface, double sx, double sy, uint32_t time); +static void pointer_focus(struct seat *seat, struct wlr_surface *surface, double sx, double sy, uint32_t time); +static void warp_to_constraint_cursor_hint(struct cursor *cursor); +static void update_cursor(struct cursor *cursor); -static void pointer_focus(struct container *con, struct wlr_surface *surface, double sx, double sy, uint32_t time) +static void pointer_focus(struct seat *seat, struct wlr_surface *surface, double sx, double sy, uint32_t time) { + struct wlr_seat *wlr_seat = seat->wlr_seat; /* Use top level surface if nothing more specific given */ - if (con && !surface) - surface = get_wlrsurface(con->client); /* If surface is NULL, clear pointer focus */ if (!surface) { - wlr_seat_pointer_notify_clear_focus(server.seat); + /* cursor_rebase(seat->cursor); */ + /* warp_to_constraint_cursor_hint(seat->cursor); */ + wlr_seat_pointer_notify_clear_focus(wlr_seat); return; } /* If surface is already focused, only notify motion */ - if (surface == server.seat->pointer_state.focused_surface) { - wlr_seat_pointer_notify_motion(server.seat, time, sx, sy); + if (surface == seat->wlr_seat->pointer_state.focused_surface) { + wlr_seat_pointer_notify_motion(wlr_seat, time, sx, sy); return; } + + cursor_constrain(seat->cursor, NULL); /* Otherwise, let the client know that the mouse cursor has entered one * of its surfaces, and make keyboard focus follow if desired. */ - wlr_seat_pointer_notify_enter(server.seat, surface, sx, sy); - - if (!con) - return; - - struct workspace *ws = get_workspace(con->m->ws_id); - if (ws->layout->options.sloppy_focus) - focus_container(con, FOCUS_NOOP); + wlr_seat_pointer_notify_enter(wlr_seat, surface, sx, sy); } -void axisnotify(struct wl_listener *listener, void *data) +static void handle_axis_notify(struct wl_listener *listener, void *data) { + struct cursor *cursor = wl_container_of(listener, cursor, axis); /* This event is forwarded by the cursor when a pointer emits an axis event, * for example when you move the scroll wheel. */ struct wlr_event_pointer_axis *event = data; /* Notify the client with pointer focus of the axis event. */ - wlr_seat_pointer_notify_axis(server.seat, + wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, event->orientation, event->delta, event->delta_discrete, event->source); } +void cursor_update_image(struct cursor *cursor) { + cursor_set_image(cursor, "left_ptr", NULL); +} + +static void handle_rebase(struct seat *seat, uint32_t time_msec) +{ + struct cursor *cursor = seat->cursor; + struct wlr_surface *surface = NULL; + double sx = 0.0, sy = 0.0; + + if (surface) { + wlr_seat_pointer_notify_enter(seat->wlr_seat, surface, sx, sy); + wlr_seat_pointer_notify_motion(seat->wlr_seat, time_msec, sx, sy); + } else { + cursor_update_image(cursor); + wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); + } + cursor_update_image(cursor); +} + +static uint32_t get_current_time_msec(void) { + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + return now.tv_sec * 1000 + now.tv_nsec / 1000000; +} + +void cursor_rebase(struct cursor *cursor) +{ + uint32_t time_msec = get_current_time_msec(); + handle_rebase(cursor->seat, time_msec); +} + +static void handle_image_surface_destroy(struct wl_listener *listener, + void *data) { + struct cursor *cursor = wl_container_of(listener, cursor, image_surface_destroy); + cursor_set_image(cursor, NULL, cursor->image_client); + cursor_rebase(cursor); +} + +static void cursor_hide(struct cursor *cursor) { + wlr_cursor_set_image(cursor->wlr_cursor, NULL, 0, 0, 0, 0, 0, 0); + cursor->hidden = true; + wlr_seat_pointer_notify_clear_focus(cursor->seat->wlr_seat); +} + +void cursor_unhide(struct cursor *cursor) { + if (!cursor->hidden) { + return; + } + + cursor->hidden = false; + if (cursor->image_surface) { + cursor_set_image_surface(cursor, + cursor->image_surface, + cursor->hotspot_x, + cursor->hotspot_y, + cursor->image_client); + } else { + const char *image = cursor->image; + cursor->image = NULL; + cursor_set_image(cursor, image, cursor->image_client); + } + cursor_rebase(cursor); + wl_event_source_timer_update(cursor->hide_source, 0); +} + +static int hide_notify(void *data) { + struct cursor *cursor = data; + cursor_hide(cursor); + return 1; +} + +struct cursor *create_cursor(struct seat *seat) +{ + struct cursor *cursor = calloc(1, sizeof(struct cursor)); + + struct wlr_cursor *wlr_cursor = wlr_cursor_create(); + cursor->wlr_cursor = wlr_cursor; + wlr_cursor->data = cursor; + + cursor->seat = seat; + + wlr_cursor_attach_output_layout(wlr_cursor, server.output_layout); + + cursor->hide_source = wl_event_loop_add_timer(server.wl_event_loop, + hide_notify, cursor); + + cursor->xcursor_mgr = wlr_xcursor_manager_create(NULL, 24); + + /* + * wlr_cursor *only* displays an image on screen. It does not move around + * when the pointer moves. However, we can attach input devices to it, and + * it will generate aggregate events for all of them. In these events, we + * can choose how we want to process them, forwarding them to clients and + * moving the cursor around. More detail on this process is described in my + * input handling blog post: + * + * https://drewdevault.com/2018/07/17/Input-handling-in-wlroots.html + * + * And more comments are sprinkled throughout the notify functions above. + */ + LISTEN(&wlr_cursor->events.axis, &cursor->axis, handle_axis_notify); + LISTEN(&wlr_cursor->events.button, &cursor->button, handle_cursor_button); + LISTEN(&wlr_cursor->events.frame, &cursor->frame, handle_cursor_frame); + LISTEN(&wlr_cursor->events.motion, &cursor->motion, handle_motion_relative); + LISTEN(&wlr_cursor->events.motion_absolute, &cursor->motion_absolute, handle_motion_absolute); + + wl_list_init(&cursor->image_surface_destroy.link); + cursor->image_surface_destroy.notify = handle_image_surface_destroy; + + LISTEN(&seat->wlr_seat->events.request_set_cursor, &cursor->request_set_cursor, handle_set_cursor); + + wl_list_init(&cursor->constraint_commit.link); + + return cursor; +} + +void destroy_cursor(struct cursor *cursor) +{ + wl_list_remove(&cursor->axis.link); + wl_list_remove(&cursor->button.link); + wl_list_remove(&cursor->frame.link); + wl_list_remove(&cursor->motion.link); + wl_list_remove(&cursor->motion_absolute.link); + + wl_list_remove(&cursor->request_set_cursor.link); + wl_list_remove(&cursor->image_surface_destroy.link); + + wl_list_remove(&cursor->constraint_commit.link); + + free(cursor); +} -void create_pointer(struct wlr_input_device *device) +static void set_image_surface(struct cursor *cursor, + struct wlr_surface *surface) { + wl_list_remove(&cursor->image_surface_destroy.link); + cursor->image_surface = surface; + if (surface) { + wl_signal_add(&surface->events.destroy, &cursor->image_surface_destroy); + } else { + wl_list_init(&cursor->image_surface_destroy.link); + } +} + +void cursor_set_image(struct cursor *cursor, const char *image, struct wl_client *client) +{ + if (!(cursor->seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER)) { + return; + } + + const char *current_image = cursor->image; + set_image_surface(cursor, NULL); + cursor->image = image; + cursor->hotspot_x = cursor->hotspot_y = 0; + cursor->image_client = client; + + if (cursor->hidden) { + return; + } + + if (!image) { + wlr_cursor_set_image(cursor->wlr_cursor, NULL, 0, 0, 0, 0, 0, 0); + } else if (!current_image || strcmp(current_image, image) != 0) { + wlr_xcursor_manager_set_cursor_image(cursor->xcursor_mgr, image, + cursor->wlr_cursor); + } +} + +void create_pointer(struct seat *seat, struct seat_device *seat_device) { /* We don't do anything special with pointers. All of our pointer handling * is proxied through wlr_cursor. On another compositor, you might take this * opportunity to do libinput configuration on the device to set * acceleration, etc. */ - wlr_cursor_attach_input_device(server.cursor.wlr_cursor, device); + + struct wlr_input_device *device = seat_device->input_device->wlr_device; + wlr_cursor_attach_input_device(seat->cursor->wlr_cursor, device); } -void cursorframe(struct wl_listener *listener, void *data) +void handle_cursor_frame(struct wl_listener *listener, void *data) { + struct cursor *cursor = wl_container_of(listener, cursor, frame); /* This event is forwarded by the cursor when a pointer emits an frame * event. Frame events are sent after regular pointer events to group * multiple events together. For instance, two axis events may happen at the * same time, in which case a frame event won't be sent in between. */ /* Notify the client with pointer focus of the frame event. */ - wlr_seat_pointer_notify_frame(server.seat); + wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); } -static bool handle_move_resize(enum cursor_mode cursor_mode) +static bool handle_move_resize(struct cursor *cursor) { - struct wlr_cursor *cursor = server.cursor.wlr_cursor; + enum cursor_mode cursor_mode = cursor->cursor_mode; + struct wlr_cursor *wlr_cursor = cursor->wlr_cursor; switch (cursor_mode) { case CURSOR_MOVE: - move_container(grabc, cursor, offsetx, offsety); + move_container(grabc, wlr_cursor, offsetx, offsety); return true; break; case CURSOR_RESIZE: - resize_container(grabc, server.cursor.wlr_cursor, 0, 0); + resize_container(grabc, wlr_cursor, 0, 0); return true; break; default: @@ -93,67 +266,124 @@ static bool handle_move_resize(enum cursor_mode cursor_mode) return false; } -void motion_relative(struct wl_listener *listener, void *data) +void cursor_handle_activity_from_device(struct cursor *cursor, struct wlr_input_device *device) +{ + cursor_unhide(cursor); +} + +void handle_motion_relative(struct wl_listener *listener, void *data) { - /* This event is forwarded by the cursor when a pointer emits a _relative_ - * pointer motion event (i.e. a delta) */ + struct cursor *cursor = wl_container_of(listener, cursor, motion); struct wlr_event_pointer_motion *event = data; - /* The cursor doesn't move unless we tell it to. The cursor automatically - * handles constraining the motion to the output layout, as well as any - * special configuration applied for the specific input device which - * generated the event. You can pass NULL for the device if you want to move - * the cursor around without any input. */ - wlr_cursor_move(server.cursor.wlr_cursor, event->device, event->delta_x, event->delta_y); - motion_notify(event->time_msec); + cursor_handle_activity_from_device(cursor, event->device); + + motion_notify(cursor, event->time_msec, event->device, event->delta_x, + event->delta_y, event->unaccel_dx, event->unaccel_dy); } -void motion_absolute(struct wl_listener *listener, void *data) +void handle_motion_absolute(struct wl_listener *listener, void *data) { - /* This event is forwarded by the cursor when a pointer emits an _absolute_ - * motion event, from 0..1 on Each axis. This happens, for example, when - * wlroots is running under a Wayland window rather than KMS+DRM, and you - * move the mouse over the Windows. You could enter the window from any edge, - * so we have to warp the mouse there. There is also some hardware which - * emits these events. */ + struct cursor *cursor = wl_container_of(listener, cursor, motion_absolute); struct wlr_event_pointer_motion_absolute *event = data; - wlr_cursor_warp_absolute(server.cursor.wlr_cursor, event->device, event->x, event->y); - motion_notify(event->time_msec); + cursor_handle_activity_from_device(cursor, event->device); + + double lx, ly; + wlr_cursor_absolute_to_layout_coords(cursor->wlr_cursor, event->device, + event->x, event->y, &lx, &ly); + + double dx = lx - cursor->wlr_cursor->x; + double dy = ly - cursor->wlr_cursor->y; + + motion_notify(cursor, event->time_msec, event->device, dx, dy, dx, dy); } -void motion_notify(uint32_t time) +void focus_under_cursor(struct cursor *cursor, uint32_t time) { - int cursorx = server.cursor.wlr_cursor->x; - int cursory = server.cursor.wlr_cursor->y; + int cursorx = cursor->wlr_cursor->x; + int cursory = cursor->wlr_cursor->y; focus_monitor(xy_to_monitor(cursorx, cursory)); /* If handled successfully return */ - if (handle_move_resize(server.cursor.cursor_mode)) + if (handle_move_resize(cursor)) return; double sx = 0, sy = 0; - struct wlr_surface *popup_surface = get_popup_surface_under_cursor(&sx, &sy); + struct wlr_surface *popup_surface = get_popup_surface_under_cursor(cursor, &sx, &sy); bool is_popup_under_cursor = popup_surface != NULL; - struct wlr_surface *focus_surface = popup_surface; + struct wlr_surface *final_focus_surface = popup_surface; struct container *focus_con = xy_to_container(cursorx, cursory); if (!is_popup_under_cursor && focus_con) { - focus_surface = wlr_surface_surface_at(get_wlrsurface(focus_con->client), - absolute_x_to_container_relative(focus_con, cursorx), - absolute_y_to_container_relative(focus_con, cursory), + final_focus_surface = wlr_surface_surface_at(get_wlrsurface(focus_con->client), + absolute_x_to_container_relative(focus_con->geom, cursorx), + absolute_y_to_container_relative(focus_con->geom, cursory), &sx, &sy); + } + + pointer_focus(cursor->seat, final_focus_surface, sx, sy, time); + + if (focus_con) { + struct workspace *ws = monitor_get_active_workspace(selected_monitor); + if (ws->layout->options.sloppy_focus && + !popups_exist() && !xwayland_popups_exist()) { + focus_container(focus_con); + } + } +} + +void motion_notify(struct cursor *cursor, uint32_t time_msec, + struct wlr_input_device *device, double dx, double dy, + double dx_unaccel, double dy_unaccel) +{ + struct wlr_seat *wlr_seat = cursor->seat->wlr_seat; + wlr_relative_pointer_manager_v1_send_relative_motion( + server.relative_pointer_mgr, + wlr_seat, (uint64_t)time_msec * 1000, + dx, dy, dx_unaccel, dy_unaccel); + + if (!cursor->active_constraint) { + struct container *sel_con = get_focused_container(selected_monitor); + if (sel_con) { + cursor->active_constraint = wlr_pointer_constraints_v1_constraint_for_surface( + server.pointer_constraints, get_wlrsurface(sel_con->client), wlr_seat); + } + } - update_cursor(&server.cursor); + // Only apply pointer constraints to real pointer input. + if (cursor->active_constraint && device->type == WLR_INPUT_DEVICE_POINTER) { + struct container *con = xy_to_container(cursor->wlr_cursor->x, cursor->wlr_cursor->y); + + double sx; + double sy; + if (con) { + sx = cursor->wlr_cursor->x - con->geom.x; + sy = cursor->wlr_cursor->y - con->geom.y; + } else { + sx = cursor->wlr_cursor->x; + sy = cursor->wlr_cursor->y; + } + + double sx_confined, sy_confined; + if (wlr_region_confine(&cursor->confine, sx, sy, sx + dx, sy + dy, + &sx_confined, &sy_confined)) { + dx = sx_confined - sx; + dy = sy_confined - sy; + } } - pointer_focus(focus_con, focus_surface, sx, sy, time); + wlr_cursor_move(cursor->wlr_cursor, device, dx, dy); + + focus_under_cursor(cursor, 0); } -void buttonpress(struct wl_listener *listener, void *data) +void handle_cursor_button(struct wl_listener *listener, void *data) { + struct cursor *cursor = wl_container_of(listener, cursor, button); struct wlr_event_pointer_button *event = data; + struct wlr_seat *wlr_seat = cursor->seat->wlr_seat; switch (event->state) { case WLR_BUTTON_PRESSED: { @@ -161,7 +391,8 @@ void buttonpress(struct wl_listener *listener, void *data) unsigned sym = event->button + 64985; /* get modifiers */ - struct wlr_keyboard *kb = wlr_seat_get_keyboard(server.seat); + struct seat *seat = input_manager_get_default_seat(); + struct wlr_keyboard *kb = wlr_seat_get_keyboard(seat->wlr_seat); int mods = wlr_keyboard_get_modifiers(kb); handle_keybinding(mods, sym); @@ -171,13 +402,13 @@ void buttonpress(struct wl_listener *listener, void *data) /* If you released any buttons, we exit interactive move/resize * mode. */ /* XXX should reset to the pointer focus's current setcursor */ - if (server.cursor.cursor_mode != CURSOR_NORMAL) { - wlr_xcursor_manager_set_cursor_image(server.cursor_mgr, "left_ptr", - server.cursor.wlr_cursor); - server.cursor.cursor_mode = CURSOR_NORMAL; + if (cursor->cursor_mode != CURSOR_NORMAL) { + wlr_xcursor_manager_set_cursor_image(cursor->xcursor_mgr, + "left_ptr", cursor->wlr_cursor); + cursor->cursor_mode = CURSOR_NORMAL; /* Drop the window off on its new monitor */ - struct monitor *m = xy_to_monitor(server.cursor.wlr_cursor->x, - server.cursor.wlr_cursor->y); + struct monitor *m = xy_to_monitor(cursor->wlr_cursor->x, + cursor->wlr_cursor->y); focus_monitor(m); return; } @@ -185,20 +416,19 @@ void buttonpress(struct wl_listener *listener, void *data) } /* If the event wasn't handled by the compositor, notify the client with * pointer focus that a button press has occurred */ - wlr_seat_pointer_notify_button(server.seat, event->time_msec, event->button, + wlr_seat_pointer_notify_button(wlr_seat, event->time_msec, event->button, event->state); } -void move_resize(int ui) +void move_resize(struct cursor *cursor, int ui) { - grabc = xy_to_container(server.cursor.wlr_cursor->x, server.cursor.wlr_cursor->y); + grabc = xy_to_container(cursor->wlr_cursor->x, cursor->wlr_cursor->y); if (!grabc) return; if (grabc->client->type == LAYER_SHELL) return; - struct wlr_cursor *cursor = server.cursor.wlr_cursor; - struct monitor *m = grabc->m; + struct monitor *m = container_get_monitor(grabc); struct layout *lt = get_layout_in_monitor(m); // all floating windows will be tiled. Thats why you can't make new windows // tiled @@ -206,24 +436,23 @@ void move_resize(int ui) return; /* Float the window and tell motion_notify to grab it */ - set_container_floating(grabc, fix_position, true); + set_container_floating(grabc, container_fix_position, true); - switch (server.cursor.cursor_mode = ui) { + struct wlr_cursor *wlr_cursor = cursor->wlr_cursor; + switch (cursor->cursor_mode = ui) { case CURSOR_MOVE: - wlr_xcursor_manager_set_cursor_image(server.cursor_mgr, "fleur", cursor); - wlr_seat_pointer_notify_clear_focus(server.seat); - offsetx = absolute_x_to_container_relative(grabc, cursor->x); - offsety = absolute_y_to_container_relative(grabc, cursor->y); + wlr_xcursor_manager_set_cursor_image(cursor->xcursor_mgr, "fleur", wlr_cursor); + offsetx = absolute_x_to_container_relative(grabc->geom, wlr_cursor->x); + offsety = absolute_y_to_container_relative(grabc->geom, wlr_cursor->y); break; case CURSOR_RESIZE: /* Doesn't work for X11 output - the next absolute motion event * returns the cursor to where it started */ - wlr_cursor_warp_closest(server.cursor.wlr_cursor, NULL, + wlr_cursor_warp_closest(wlr_cursor, NULL, grabc->geom.x + grabc->geom.width, grabc->geom.y + grabc->geom.height); - wlr_xcursor_manager_set_cursor_image(server.cursor_mgr, - "bottom_right_corner", server.cursor.wlr_cursor); - wlr_seat_pointer_notify_clear_focus(server.seat); + wlr_xcursor_manager_set_cursor_image(cursor->xcursor_mgr, + "bottom_right_corner", wlr_cursor); break; default: break; @@ -231,17 +460,38 @@ void move_resize(int ui) arrange(); } +void cursor_set_image_surface(struct cursor *cursor, + struct wlr_surface *surface, int32_t hotspot_x, int32_t hotspot_y, + struct wl_client *client) { + if (!(cursor->seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER)) { + return; + } + + set_image_surface(cursor, surface); + cursor->image = NULL; + cursor->hotspot_x = hotspot_x; + cursor->hotspot_y = hotspot_y; + cursor->image_client = client; + + if (cursor->hidden) { + return; + } + + wlr_cursor_set_surface(cursor->wlr_cursor, surface, hotspot_x, hotspot_y); +} + + void handle_set_cursor(struct wl_listener *listener, void *data) { + struct cursor *cursor = wl_container_of(listener, cursor, request_set_cursor); struct wlr_seat_pointer_request_set_cursor_event *event = data; - struct cursor *cursor = &server.cursor; /* This can be sent by any client, so we check to make sure this one is * actually has pointer focus first. If so, we can tell the cursor to * use the provided surface as the cursor image. It will set the * hardware cursor on the output that it's currently on and continue to * do so as the cursor moves between outputs. */ - if (event->seat_client != server.seat->pointer_state.focused_client) { + if (event->seat_client != cursor->seat->wlr_seat->pointer_state.focused_client) { return; } @@ -252,7 +502,175 @@ void handle_set_cursor(struct wl_listener *listener, void *data) update_cursor(cursor); } -void update_cursor(struct cursor *cursor) +static void handle_pointer_constraint_set_region(struct wl_listener *listener, + void *data) { + struct pointer_constraint *sway_constraint = + wl_container_of(listener, sway_constraint, set_region); + struct cursor *cursor = sway_constraint->cursor; + + cursor->active_confine_requires_warp = true; +} + +static void warp_to_constraint_cursor_hint(struct cursor *cursor) +{ + struct wlr_pointer_constraint_v1 *constraint = cursor->active_constraint; + + if (constraint->current.committed & + WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT) { + double sx = constraint->current.cursor_hint.x; + double sy = constraint->current.cursor_hint.y; + + /* struct sway_view *view = view_from_wlr_surface(constraint->surface); */ + /* struct sway_container *con = view->container; */ + + struct monitor *m = selected_monitor; + struct container *con = get_focused_container(m); + double lx = sx + con->geom.x - m->geom.x; + double ly = sy + con->geom.x - m->geom.y; + + wlr_cursor_warp(cursor->wlr_cursor, NULL, lx, ly); + + // Warp the pointer as well, so that on the next pointer rebase we don't + // send an unexpected synthetic motion event to clients. + wlr_seat_pointer_warp(constraint->seat, sx, sy); + } +} + +void handle_constraint_destroy(struct wl_listener *listener, void *data) { + struct pointer_constraint *pointer_constraint = + wl_container_of(listener, pointer_constraint, destroy); + struct wlr_pointer_constraint_v1 *wlr_constraint = data; + struct cursor *cursor = pointer_constraint->cursor; + + wl_list_remove(&pointer_constraint->set_region.link); + wl_list_remove(&pointer_constraint->destroy.link); + + if (cursor->active_constraint == wlr_constraint) { + warp_to_constraint_cursor_hint(cursor); + + if (cursor->constraint_commit.link.next != NULL) { + wl_list_remove(&cursor->constraint_commit.link); + } + wl_list_init(&cursor->constraint_commit.link); + cursor->active_constraint = NULL; + } + + free(pointer_constraint); +} + +static void check_constraint_region(struct cursor *cursor) { + struct wlr_pointer_constraint_v1 *constraint = cursor->active_constraint; + pixman_region32_t *region = &constraint->region; + struct container *con = get_focused_container(selected_monitor); + if (cursor->active_confine_requires_warp && con) { + cursor->active_confine_requires_warp = false; + + double sx = cursor->wlr_cursor->x - con->geom.x; + double sy = cursor->wlr_cursor->y - con->geom.x; + + if (!pixman_region32_contains_point(region, + floor(sx), floor(sy), NULL)) { + int nboxes; + pixman_box32_t *boxes = pixman_region32_rectangles(region, &nboxes); + if (nboxes > 0) { + double sx = (boxes[0].x1 + boxes[0].x2) / 2.; + double sy = (boxes[0].y1 + boxes[0].y2) / 2.; + + wlr_cursor_warp_closest(cursor->wlr_cursor, NULL, + sx + con->geom.x, + sy + con->geom.x); + + cursor_rebase(cursor); + } + } + } + + // A locked pointer will result in an empty region, thus disallowing all movement + if (constraint->type == WLR_POINTER_CONSTRAINT_V1_CONFINED) { + pixman_region32_copy(&cursor->confine, region); + } else { + pixman_region32_clear(&cursor->confine); + } +} + +static void handle_constraint_commit(struct wl_listener *listener, + void *data) { + struct cursor *cursor = wl_container_of(listener, cursor, constraint_commit); + /* struct wlr_pointer_constraint_v1 *constraint = cursor->active_constraint; */ + /* assert(constraint->surface == data); */ + + cursor_hide(cursor); + check_constraint_region(cursor); +} + +void cursor_constrain(struct cursor *cursor, struct wlr_pointer_constraint_v1 *constraint) { + if (cursor->active_constraint == constraint) { + return; + } + + wl_list_remove(&cursor->constraint_commit.link); + if (cursor->active_constraint) { + if (!constraint) { + warp_to_constraint_cursor_hint(cursor); + } + wlr_pointer_constraint_v1_send_deactivated(cursor->active_constraint); + } + + cursor->active_constraint = constraint; + + if (constraint == NULL) { + wl_list_init(&cursor->constraint_commit.link); + return; + } + + cursor->active_confine_requires_warp = true; + + // FIXME: Big hack, stolen from wlr_pointer_constraints_v1.c:121. + // This is necessary because the focus may be set before the surface + // has finished committing, which means that warping won't work properly, + // since this code will be run *after* the focus has been set. + // That is why we duplicate the code here. + if (pixman_region32_not_empty(&constraint->current.region)) { + pixman_region32_intersect(&constraint->region, + &constraint->surface->input_region, &constraint->current.region); + } else { + pixman_region32_copy(&constraint->region, + &constraint->surface->input_region); + } + + check_constraint_region(cursor); + + wlr_pointer_constraint_v1_send_activated(constraint); + + LISTEN(&constraint->surface->events.commit, &cursor->constraint_commit, handle_constraint_commit); +} + +void handle_new_pointer_constraint(struct wl_listener *listener, void *data) +{ + struct wlr_pointer_constraint_v1 *wlr_constraint = data; + struct seat *seat = wlr_constraint->seat->data; + + struct pointer_constraint *constraint = + calloc(1, sizeof(struct pointer_constraint)); + constraint->cursor = seat->cursor; + constraint->wlr_constraint = wlr_constraint; + + constraint->set_region.notify = handle_pointer_constraint_set_region; + wl_signal_add(&wlr_constraint->events.set_region, &constraint->set_region); + + constraint->destroy.notify = handle_constraint_destroy; + wl_signal_add(&wlr_constraint->events.destroy, &constraint->destroy); + + struct container *con = get_focused_container(selected_monitor); + if (con) { + struct wlr_surface *surface = get_wlrsurface(con->client); + if (surface == wlr_constraint->surface) { + cursor_constrain(seat->cursor, wlr_constraint); + } + } +} + +static void update_cursor(struct cursor *cursor) { /* If we're "grabbing" the server.cursor, don't use the client's image */ /* XXX still need to save the provided surface to restore later */ @@ -260,15 +678,12 @@ void update_cursor(struct cursor *cursor) return; if (!xy_to_container(cursor->wlr_cursor->x, cursor->wlr_cursor->y)) { - wlr_xcursor_manager_set_cursor_image(server.cursor_mgr, - "left_ptr", server.cursor.wlr_cursor); + wlr_xcursor_manager_set_cursor_image(cursor->xcursor_mgr, + "left_ptr", cursor->wlr_cursor); return; } - if (!cursor->cursor_surface) - return; - - if (!server.seat->pointer_state.focused_client) + if (!cursor->seat->wlr_seat->pointer_state.focused_client) return; wlr_cursor_set_surface(cursor->wlr_cursor, cursor->cursor_surface, diff --git a/src/event_handler.c b/src/event_handler.c index deaa1ca1..bea0372d 100644 --- a/src/event_handler.c +++ b/src/event_handler.c @@ -4,46 +4,75 @@ #include "utils/parseConfigUtils.h" -struct event_handler get_default_event_handler() +struct event_handler *create_event_handler() { - struct event_handler event_handler = { - .update_func_ref = 0, - .create_container_func_ref = 0, - }; + struct event_handler *event_handler = calloc(1, sizeof(struct event_handler)); + event_handler->on_start_func_refs = g_ptr_array_new(); + event_handler->on_focus_func_refs = g_ptr_array_new(); + event_handler->on_update_func_refs = g_ptr_array_new(); + event_handler->on_create_container_func_refs = g_ptr_array_new(); return event_handler; } +void destroy_event_handler(struct event_handler *event_handler) +{ + g_ptr_array_free(event_handler->on_start_func_refs, TRUE); + g_ptr_array_free(event_handler->on_focus_func_refs, TRUE); + g_ptr_array_free(event_handler->on_update_func_refs, TRUE); + g_ptr_array_free(event_handler->on_create_container_func_refs, TRUE); +} + +GPtrArray *event_name_to_signal(struct event_handler *event_handler, + const char *event) +{ + const char *events[] = { + "on_start", + "on_focus", + "on_update", + "on_create_container" + }; + for (int i = 0; i < LENGTH(events); i++) { + if (strcmp(event, events[i]) == 0) + return event_handler->on_start_func_refs; + } + return NULL; +} + +static void emit_signal(GPtrArray *func_refs) +{ + for (int i = 0; i < func_refs->len; i++) { + int *ref = g_ptr_array_index(func_refs, i); + lua_rawgeti(L, LUA_REGISTRYINDEX, *ref); + lua_call_safe(L, 0, 0, 0); + } +} + +static void emit_signal_to_one_int_argument_functions(GPtrArray *func_refs, int n) +{ + for (int i = 0; i < func_refs->len; i++) { + int *ref = g_ptr_array_index(func_refs, i); + lua_rawgeti(L, LUA_REGISTRYINDEX, *ref); + lua_pushinteger(L, n); + lua_call_safe(L, 1, 0, 0); + } +} + void call_update_function(struct event_handler *ev, int n) { - if (ev->update_func_ref == 0) - return; - lua_rawgeti(L, LUA_REGISTRYINDEX, ev->update_func_ref); - lua_pushinteger(L, n); - lua_call_safe(L, 1, 0, 0); + emit_signal_to_one_int_argument_functions(ev->on_update_func_refs, n); } void call_create_container_function(struct event_handler *ev, int n) { - if (ev->create_container_func_ref == 0) - return; - lua_rawgeti(L, LUA_REGISTRYINDEX, ev->create_container_func_ref); - lua_pushinteger(L, n); - lua_call_safe(L, 1, 0, 0); + emit_signal_to_one_int_argument_functions(ev->on_create_container_func_refs, n); } void call_on_focus_function(struct event_handler *ev, int n) { - if (ev->on_focus_func_ref == 0) - return; - lua_rawgeti(L, LUA_REGISTRYINDEX, ev->on_focus_func_ref); - lua_pushinteger(L, n); - lua_call_safe(L, 1, 0, 0); + emit_signal_to_one_int_argument_functions(ev->on_focus_func_refs, n); } void call_on_start_function(struct event_handler *ev) { - if (ev->on_start_func_ref == 0) - return; - lua_rawgeti(L, LUA_REGISTRYINDEX, ev->on_start_func_ref); - lua_call_safe(L, 0, 0, 0); + emit_signal(ev->on_start_func_refs); } diff --git a/src/input_manager.c b/src/input_manager.c new file mode 100644 index 00000000..dacaeb1f --- /dev/null +++ b/src/input_manager.c @@ -0,0 +1,302 @@ +#include "input_manager.h" + +#include +#include +#include + +#include "cursor.h" +#include "keyboard.h" +#include "seat.h" +#include "server.h" +#include "stringop.h" +#include "utils/coreUtils.h" + +#define DEFAULT_SEAT "seat0" + +static bool device_is_touchpad(struct input_device *device) +{ + if (device->wlr_device->type != WLR_INPUT_DEVICE_POINTER || + !wlr_input_device_is_libinput(device->wlr_device)) { + return false; + } + + struct libinput_device *libinput_device = + wlr_libinput_get_device_handle(device->wlr_device); + + return libinput_device_config_tap_get_finger_count(libinput_device) > 0; +} + +const char *input_device_get_type(struct input_device *device) +{ + switch (device->wlr_device->type) { + case WLR_INPUT_DEVICE_POINTER: + if (device_is_touchpad(device)) { + return "touchpad"; + } else { + return "pointer"; + } + case WLR_INPUT_DEVICE_KEYBOARD: + return "keyboard"; + case WLR_INPUT_DEVICE_TOUCH: + return "touch"; + case WLR_INPUT_DEVICE_TABLET_TOOL: + return "tablet_tool"; + case WLR_INPUT_DEVICE_TABLET_PAD: + return "tablet_pad"; + case WLR_INPUT_DEVICE_SWITCH: + return "switch"; + } + return "unknown"; +} + +/* static void apply_input_type_config(struct input_device *input_device) { */ +/* const char *device_type = input_device_get_type(input_device); */ +/* struct input_config *type_config = NULL; */ +/* for (int i = 0; i < config->input_type_configs->length; i++) { */ +/* struct input_config *ic = config->input_type_configs->items[i]; */ +/* if (strcmp(ic->identifier + 5, device_type) == 0) { */ +/* type_config = ic; */ +/* break; */ +/* } */ +/* } */ +/* if (type_config == NULL) { */ +/* return; */ +/* } */ + +/* for (int i = 0; i < config->input_configs->length; i++) { */ +/* struct input_config *ic = config->input_configs->items[i]; */ +/* if (strcmp(input_device->identifier, ic->identifier) == 0) { */ +/* struct input_config *current = new_input_config(ic->identifier); */ +/* merge_input_config(current, type_config); */ +/* merge_input_config(current, ic); */ + +/* current->input_type = device_type; */ +/* config->input_configs->items[i] = current; */ +/* free_input_config(ic); */ +/* ic = NULL; */ + +/* break; */ +/* } */ +/* } */ +/* } */ + +static struct input_device *input_device_from_wlr( + struct wlr_input_device *device) { + for (int i = 0; i < server.input_manager->devices->len; i++) { + struct input_device *input_device = + g_ptr_array_index(server.input_manager->devices, i); + if (input_device->wlr_device == device) { + return input_device; + } + } + return NULL; +} + +static void handle_device_destroy(struct wl_listener *listener, void *data) { + struct wlr_input_device *device = data; + + struct input_device *input_device = input_device_from_wlr(device); + + if (!input_device) { + return; + } + + for (int i = 0; i < server.input_manager->seats->len; i++) { + struct seat *seat = g_ptr_array_index(server.input_manager->seats, i); + seat_remove_device(seat, input_device); + } + + wl_list_remove(&input_device->device_destroy.link); + g_ptr_array_remove(server.input_manager->devices, input_device); + + free(input_device->identifier); + free(input_device); +} + +static void handle_new_input(struct wl_listener *listener, void *data) +{ + struct input_manager *input_manager = wl_container_of(listener, input_manager, new_input); + struct wlr_input_device *device = data; + + struct input_device *input_device = calloc(1, sizeof(struct input_device)); + device->data = input_device; + + input_device->wlr_device = device; + input_device->identifier = input_device_get_identifier(device); + g_ptr_array_add(input_manager->devices, input_device); + + /* apply_input_type_config(input_device); */ + + /* sway_input_configure_libinput_device(input_device); */ + + wl_signal_add(&device->events.destroy, &input_device->device_destroy); + input_device->device_destroy.notify = handle_device_destroy; + + /* bool added = false; */ + for (int i = 0; i < input_manager->seats->len; i++) { + struct seat *seat = g_ptr_array_index(input_manager->seats, i); + +/* struct seat_config *seat_config = seat_get_config(seat); */ +/* bool has_attachment = seat_config && */ +/* (seat_config_get_attachment(seat_config, input_device->identifier) || */ +/* seat_config_get_attachment(seat_config, "*")); */ + + /* if (has_attachment) { */ + seat_add_device(seat, input_device); + /* added = true; */ + /* } */ + } + + /* input_manager_verify_fallback_seat(); */ +} + +static void handle_inhibit_activate(struct wl_listener *listener, void *data) +{ +} + +static void handle_inhibit_deactivate(struct wl_listener *listener, void *data) +{ +} + +static void handle_keyboard_shortcuts_inhibit_new_inhibitor( + struct wl_listener *listener, void *data) +{ +} + +static void handle_new_virtual_keyboard(struct wl_listener *listener, void *data) +{ + +} + +struct seat *input_manager_seat_from_wlr_seat(struct wlr_seat *wlr_seat) { + for (int i = 0; i < server.input_manager->seats->len; i++) { + struct seat *seat = g_ptr_array_index(server.input_manager->seats, i); + if (seat->wlr_seat == wlr_seat) { + return seat; + } + } + + return NULL; +} + + +static void handle_new_virtual_pointer(struct wl_listener *listener, void *data) +{ + struct input_manager *input_manager = wl_container_of(listener, input_manager, new_virtual_pointer); + + struct wlr_virtual_pointer_v1_new_pointer_event *event = data; + struct wlr_virtual_pointer_v1 *pointer = event->new_pointer; + struct wlr_input_device *device = &pointer->input_device; + + struct seat *seat = event->suggested_seat ? + input_manager_seat_from_wlr_seat(event->suggested_seat) : + input_manager_get_default_seat(); + + struct input_device *input_device = calloc(1, sizeof(struct input_device)); + device->data = input_device; + + input_device->is_virtual = true; + input_device->wlr_device = device; + input_device->identifier = input_device_get_identifier(device); + g_ptr_array_add(input_manager->devices, &input_device); + + wl_signal_add(&device->events.destroy, &input_device->device_destroy); + input_device->device_destroy.notify = handle_device_destroy; + + seat_add_device(seat, input_device); + + if (event->suggested_output) { + wlr_cursor_map_input_to_output(seat->cursor->wlr_cursor, device, + event->suggested_output); + } +} + +struct input_manager *create_input_manager() +{ + struct input_manager *input_manager = calloc(1, sizeof(struct input_manager)); + + input_manager->devices = g_ptr_array_new(); + input_manager->seats = g_ptr_array_new(); + + LISTEN(&server.backend->events.new_input, &input_manager->new_input, + handle_new_input); + + input_manager->inhibit = wlr_input_inhibit_manager_create(server.wl_display); + LISTEN(&input_manager->inhibit->events.activate, &input_manager->inhibit_activate, + handle_inhibit_activate); + LISTEN(&input_manager->inhibit->events.deactivate, &input_manager->inhibit_deactivate, + handle_inhibit_deactivate); + + input_manager->keyboard_shortcuts_inhibit = + wlr_keyboard_shortcuts_inhibit_v1_create(server.wl_display); + LISTEN(&input_manager->keyboard_shortcuts_inhibit->events.new_inhibitor, + &input_manager->keyboard_shortcuts_inhibit_new_inhibitor, + handle_keyboard_shortcuts_inhibit_new_inhibitor); + + input_manager->virtual_pointer = wlr_virtual_pointer_manager_v1_create(server.wl_display); + LISTEN(&input_manager->virtual_pointer->events.new_virtual_pointer, + &input_manager->new_virtual_pointer, handle_new_virtual_pointer); + + input_manager->virtual_keyboard = wlr_virtual_keyboard_manager_v1_create(server.wl_display); + LISTEN(&input_manager->virtual_keyboard->events.new_virtual_keyboard, + &input_manager->new_virtual_keyboard, handle_new_virtual_keyboard); + + return input_manager; +} + +void destroy_input_manager(struct input_manager *input_manager) +{ + wl_list_remove(&input_manager->new_input.link); + wl_list_remove(&input_manager->inhibit_activate.link); + wl_list_remove(&input_manager->inhibit_deactivate.link); + wl_list_remove(&input_manager->keyboard_shortcuts_inhibit_new_inhibitor.link); + wl_list_remove(&input_manager->new_virtual_keyboard.link); + wl_list_remove(&input_manager->new_virtual_pointer.link); + free(input_manager); +} + +struct seat *input_manager_get_default_seat() +{ + return input_manager_get_seat(DEFAULT_SEAT); +} + +struct seat *input_manager_get_seat(const char *seat_name) +{ + for (int i = 0; i < server.input_manager->seats->len; i++) { + struct seat *seat = g_ptr_array_index(server.input_manager->seats, i); + if (strcmp(seat->wlr_seat->name, seat_name) == 0) { + return seat; + } + } + + return NULL; +} + +char *input_device_get_identifier(struct wlr_input_device *device) +{ + int vendor = device->vendor; + int product = device->product; + char *name = strdup(device->name ? device->name : ""); + strip_whitespace(name); + + char *p = name; + for (; *p; ++p) { + // There are in fact input devices with unprintable characters in its name + if (*p == ' ' || !isprint(*p)) { + *p = '_'; + } + } + + const char *fmt = "%d:%d:%s"; + int len = snprintf(NULL, 0, fmt, vendor, product, name) + 1; + char *identifier = malloc(len); + if (!identifier) { + printf("Unable to allocate unique input device name\n"); + return NULL; + } + + snprintf(identifier, len, fmt, vendor, product, name); + free(name); + return identifier; +} + diff --git a/src/ipc-json.c b/src/ipc-json.c index b6f5a666..1d04bd3c 100644 --- a/src/ipc-json.c +++ b/src/ipc-json.c @@ -16,6 +16,7 @@ #include "workspace.h" #include "monitor.h" #include "utils/coreUtils.h" +#include "stringop.h" static json_object *ipc_json_create_rect(struct wlr_box *box) { json_object *rect = json_object_new_object(); @@ -74,25 +75,111 @@ struct focus_inactive_data { json_object *object; }; -json_object *ipc_json_describe_workspace(struct workspace *ws, bool focused) +// note: full_name must use malloced memory +static void add_infix(char **full_name, const char *prefix, const char *postfix) +{ + const char *delimiter = ":"; + + GPtrArray *content = split_string(*full_name, delimiter); + char *position = strdup(""); + char *name; + char *content0 = g_ptr_array_index(content, 0); + if (content->len > 1) { + position = realloc(position, strlen(content0)+strlen(delimiter)+1); + strcpy(position, content0); + strcat(position, delimiter); + int name_byte_len = strlen(*full_name)-strlen(position)+1; + name = malloc(name_byte_len); + memmove(name, *full_name + strlen(position), name_byte_len); + } else { + name = content0; + } + + int full_name_string_length = strlen(position) + strlen(prefix) + + strlen(name) + strlen(postfix) + 1; + *full_name = realloc(*full_name, full_name_string_length); + strcpy(*full_name, position); + strcat(*full_name, prefix); + strcat(*full_name, name); + strcat(*full_name, postfix); + + free(name); + free(position); +} + +static bool is_workspace_extern(struct workspace *ws) +{ + if (!ws->selected_tagset) + return false; + if (!ws->tagset) + return false; + bool is_extern = ws->tagset->m != ws->selected_tagset->m; + return is_extern; +} + +static bool is_workspace_the_selected_one(struct workspace *ws) +{ + if (!ws->selected_tagset) + return false; + return ws->selected_tagset->selected_ws_id == ws->id + && tagset_is_visible(ws->selected_tagset) + && !is_workspace_extern(ws); +} + +json_object *ipc_json_describe_tagsets() +{ + json_object *array = json_object_new_array(); + + for (int i = 0; i < server.workspaces->len; i++) { + struct workspace *ws = get_workspace(i); + struct monitor *m = workspace_get_monitor(ws); + + if (!m) + continue; + if (!workspace_is_visible(ws)) + continue; + + char *full_name = strdup(ws->name); + if (is_workspace_the_selected_one(ws)) { + add_infix(&full_name, "*", "*"); + } + + bool is_active = workspace_is_active(ws); + json_object *tagset_object = ipc_json_describe_tag(full_name, is_active, m); + json_object_array_add(array, tagset_object); + + // for the second monitor + if (is_workspace_extern(ws)) { + struct monitor *selected_monitor = workspace_get_selected_monitor(ws); + char *hidden_name = strdup(ws->name); + add_infix(&hidden_name, "(", ")"); + json_object *tagset_object = ipc_json_describe_tag(hidden_name, false, selected_monitor); + json_object_array_add(array, tagset_object); + free(hidden_name); + } + free(full_name); + } + return array; +} + +json_object *ipc_json_describe_tag(const char *name, bool is_active_workspace, struct monitor *m) { - struct monitor *m = ws->m; struct wlr_box box; box = m->geom; - char *s = strdup(ws->name); + char *s = strdup(name); - json_object *object = ipc_json_create_node(0, s, focused, NULL, &box); + json_object *object = ipc_json_create_node(0, s, is_active_workspace, NULL, &box); json_object *children = json_object_new_array(); json_object_object_add(object, "nodes", children); int num; - if (isdigit(ws->name[0])) { + if (isdigit(name[0])) { errno = 0; char *endptr = NULL; - long long parsed_num = strtoll(ws->name, &endptr, 10); - if (errno != 0 || parsed_num > INT32_MAX || parsed_num < 0 || endptr == ws->name) { + long long parsed_num = strtoll(name, &endptr, 10); + if (errno != 0 || parsed_num > INT32_MAX || parsed_num < 0 || endptr == name) { num = -1; } else { num = (int) parsed_num; @@ -103,8 +190,8 @@ json_object *ipc_json_describe_workspace(struct workspace *ws, bool focused) json_object_object_add(object, "num", json_object_new_int(num)); json_object_object_add(object, "fullscreen_mode", json_object_new_int(0)); - json_object_object_add(object, "output", ws->m ? - json_object_new_string(ws->m->wlr_output->name) : NULL); + json_object_object_add(object, "output", m ? + json_object_new_string(m->wlr_output->name) : NULL); json_object_object_add(object, "type", json_object_new_string("workspace")); json_object_object_add(object, "urgent", json_object_new_boolean(false)); @@ -148,7 +235,7 @@ json_object *ipc_json_describe_selected_container(struct monitor *m) json_object_object_get_ex(monitor_object, "nodes", &monitor_children); struct workspace *ws = monitor_get_active_workspace(selected_monitor); - json_object *workspace_object = ipc_json_describe_workspace(ws, false); + json_object *workspace_object = ipc_json_describe_tag(ws->name, true, selected_monitor); json_object_array_add(monitor_children, workspace_object); json_object *workspace_children; json_object_object_get_ex(monitor_object, "nodes", &workspace_children); @@ -159,4 +246,3 @@ json_object *ipc_json_describe_selected_container(struct monitor *m) return root_object; } - diff --git a/src/ipc-server.c b/src/ipc-server.c index e870e0d5..d4acac08 100644 --- a/src/ipc-server.c +++ b/src/ipc-server.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include "ipc-json.h" @@ -30,7 +29,7 @@ static int ipc_socket = -1; static struct sockaddr_un *ipc_sockaddr = NULL; -static struct wlr_list ipc_client_list; +static GPtrArray *ipc_client_list; static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'}; @@ -124,11 +123,12 @@ void ipc_init(struct wl_event_loop *wl_event_loop) { printf("Unable to listen on IPC socket\n"); } - // Set i3 IPC socket path so that i3-msg works out of the box - /* setenv("I3SOCK", ipc_sockaddr->sun_path, 1); */ + // Set SWAY IPC socket path so that waybar automatically shows + // workspaces(tags) setenv("SWAYSOCK", ipc_sockaddr->sun_path, 1); + setenv("JAPOKWMSOCK", ipc_sockaddr->sun_path, 1); - wlr_list_init(&ipc_client_list); + ipc_client_list = g_ptr_array_new(); wl_event_loop_add_fd(wl_event_loop, ipc_socket, WL_EVENT_READABLE, ipc_handle_connection, wl_event_loop); @@ -149,7 +149,7 @@ struct sockaddr_un *ipc_user_sockaddr(void) { dir = "/tmp"; } if (path_size <= snprintf(ipc_sockaddr->sun_path, path_size, - "%s/sway-ipc.%u.%i.sock", dir, getuid(), getpid())) { + "%s/japokwm-ipc.%u.%i.sock", dir, getuid(), getpid())) { printf("Socket path won't fit into ipc_sockaddr->sun_path\n"); } @@ -198,7 +198,7 @@ int ipc_handle_connection(int fd, uint32_t mask, void *data) { return 0; } - wlr_list_push(&ipc_client_list, client); + g_ptr_array_add(ipc_client_list, client); return 0; } @@ -271,8 +271,8 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { static void ipc_send_event(const char *json_string, enum ipc_command_type event) { struct ipc_client *client; - for (size_t i = 0; i < ipc_client_list.length; i++) { - client = ipc_client_list.items[i]; + for (size_t i = 0; i < ipc_client_list->len; i++) { + client = g_ptr_array_index(ipc_client_list, i); if ((client->subscribed_events & event_mask(event)) == 0) { continue; } @@ -344,10 +344,10 @@ void ipc_client_disconnect(struct ipc_client *client) { wl_event_source_remove(client->writable_event_source); } size_t i = 0; - while (i < ipc_client_list.length && ipc_client_list.items[i] != client) { + while (i < ipc_client_list->len && g_ptr_array_index(ipc_client_list, i) != client) { i++; } - wlr_list_del(&ipc_client_list, i); + g_ptr_array_remove_index(ipc_client_list, i); free(client->write_buffer); close(client->fd); free(client); @@ -384,35 +384,31 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt switch (payload_type) { case IPC_COMMAND: - execute_command(buf); - ipc_send_reply(client, payload_type, "", strlen("")); + { + char *line = strtok(buf, "\n"); + while (line) { + size_t line_length = strlen(line); + if (line + line_length >= buf + payload_length) { + break; + } + line[line_length] = ';'; + line = strtok(NULL, "\n"); + } + + struct cmd_results *results = execute_command(buf, NULL, NULL); + /* transaction_commit_dirty(); */ + char *json = cmd_results_to_json(results); + int length = strlen(json); + ipc_send_reply(client, payload_type, json, (uint32_t)length); + free(json); + free(results); goto exit_cleanup; - break; + } case IPC_GET_WORKSPACES: { - json_object *array = json_object_new_array(); - - for (int i = 0; i < server.mons.length; i++) { - struct monitor *m = server.mons.items[i]; - for (int ws_id = 0; ws_id < server.workspaces.length; ws_id++) { - printf("ws_id: %i\n", ws_id); - struct workspace *ws = get_workspace(ws_id); - bool has_clients = workspace_has_clients(ws); - bool is_workspace_selected = m->ws_id == ws_id; - bool is_workspace_active = selected_monitor->ws_id == ws_id; - printf("has_clients: %i\n", has_clients); - printf("is_workspace_selected: %i\n", is_workspace_selected); - printf("is_workspace_active: %i\n", is_workspace_active); - if (!has_clients && !is_workspace_selected) - continue; - if (ws->m != m) - continue; - - json_object *ws_object = ipc_json_describe_workspace( - ws, is_workspace_active); - json_object_array_add(array, ws_object); - } - } + json_object *array; + + array = ipc_json_describe_tagsets(NULL); const char *json_string = json_object_get_string(array); ipc_send_reply(client, payload_type, json_string, @@ -424,6 +420,8 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt case IPC_SUBSCRIBE: { // TODO: Check if they're permitted to use these events + // NOTE: this will probably be fixed by sway, if so copy its + // implementation and call it a day struct json_object *request = json_tokener_parse(buf); bool is_tick = false; @@ -438,7 +436,7 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt const char msg[] = "{\"success\": false}"; ipc_send_reply(client, payload_type, msg, strlen(msg)); json_object_put(request); - wlr_log(WLR_INFO, "Unsupported event type in subscribe request"); + printf("Unsupported event type in subscribe request\n"); goto exit_cleanup; } } diff --git a/src/japokwm.1 b/src/japokwm.1 new file mode 100644 index 00000000..a1cb976c --- /dev/null +++ b/src/japokwm.1 @@ -0,0 +1,121 @@ +.\" Generated by scdoc 1.11.1 +.\" Complete documentation for this program is not available as a GNU info page +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.nh +.ad l +.\" Begin generated content: +.TH "japokwm" "1" "2021-06-08" +.P +.SH NAME +.P +japokwm - tiling made easy +.P +.SH SYNOPSIS +.P +\fBjapokwm\fR [options.\&.\&.\&] [command] +.P +.SH OPTIONS +.P +\fB-h, --help\fR +.RS 4 +Show help message and quit.\& +.P +.RE +\fB-c, --config\fR +.RS 4 +Specifies a config file.\& +.P +.RE +\fB-p, --path\fR +.RS 4 +Specifies the directory where the config files are located in (Note: -c +option overwrites the init.\&lua found in the path) +.P +.RE +.P +.P +.P +.P +.SH DESCRIPTION +.P +japokwm was created to create a tiling window manager where you can +create new layouts as easily as possible.\& We have to thank everyone that created +the wlroots library and the dwl project.\& Both of them made this project +possible.\& +.P +You can run japokwm directly from a tty, or via a Wayland-compatible login manager.\& +.P +.SH CONFIGURATION +.P +japokwm searches for a the init.\&lua in the following locations, in this order: +.P +.RS 4 +.ie n \{\ +\h'-04'1.\h'+03'\c +.\} +.el \{\ +.IP 1. 4 +.\} +~/.\&japokwm/config +.RE +.RS 4 +.ie n \{\ +\h'-04'2.\h'+03'\c +.\} +.el \{\ +.IP 2. 4 +.\} +$XDG_CONFIG_HOME/sway/config (suggested location) +.RE +.RS 4 +.ie n \{\ +\h'-04'3.\h'+03'\c +.\} +.el \{\ +.IP 3. 4 +.\} +~/.\&i3/config +.RE +.RS 4 +.ie n \{\ +\h'-04'4.\h'+03'\c +.\} +.el \{\ +.IP 4. 4 +.\} +$XDG_CONFIG_HOME/i3/config +.RE +.RS 4 +.ie n \{\ +\h'-04'5.\h'+03'\c +.\} +.el \{\ +.IP 5. 4 +.\} +/etc/sway/config +.RE +.RS 4 +.ie n \{\ +\h'-04'6.\h'+03'\c +.\} +.el \{\ +.IP 6. 4 +.\} +/etc/i3/config + +.RE +.P +If unset, $XDG_CONFIG_HOME defaults to \fB~/.\&config\fR.\& +.P +An error is raised when no config file is found.\& The recommended default +configuration is usually installed to \fB/etc/sway/config\fR; you are encouraged to +copy this to \fB~/.\&config/sway/config\fR and edit it from there.\& +.P +For information on the config file, see \fBjapokwm\fR(5).\& +.P +.SH AUTHORS +.P +Maintained by Jakob Schlanstedt +.P +.SH SEE ALSO diff --git a/src/japokwm.5 b/src/japokwm.5 new file mode 100644 index 00000000..b889e839 --- /dev/null +++ b/src/japokwm.5 @@ -0,0 +1,947 @@ +.\" Generated by scdoc 1.11.1 +.\" Complete documentation for this program is not available as a GNU info page +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.nh +.ad l +.\" Begin generated content: +.TH "japokwm" "5" "2021-06-08" +.P +.SH NAME +.P +japokwm - tiling made easy +.P +.SH DESCRIPTION +A japokwm configuration file is a lua file that is executed by japokwm.\& You can +use built-in functions with it to change the behavior of your window manager and +change various settings.\& An example config is likely present in +/etc/japokwm/init.\&lua for you to check out.\& +.P +.SH Terminology +\fBcontainer\fR +.br + an array consisting of 4 floats where they represent the relative x, y, +.br + width and height respectively +.br +\fBrelative x/y/width/height\fR +.br + when x = 0 you are at the left side of the screen.\& At x = 1 you are on +.br + the right side.\& At x = 0.\&5 you are at half the screen +.br + when width = 0 it is literally 0.\& When it is 1 it is as wide as the +.br + current monitor.\& When width = 0.\&5 it means half the width of the monitor +.br +\fBroot\fR +.RS 4 +everything you see when you don't have a window open +.RE +\fBwindow\fR +.br + the container and the content you see in it +.P +.SH Types +Here are types listed which are used in Japokwm's config file.\& Those Types are +important because you won't understand the function definitions otherwise.\& +.TS +allbox;l lx lx +l lx lx +l lx lx +l lx lx +l lx lx +l lx lx +l lx lx +l lx lx. +T{ +\fBTypes\fR +T} T{ +\fBDescription\fR +T} T{ +\fBExample\fR +T} +T{ +nil +T} T{ +basic lua nil +T} T{ +nil +T} +T{ +string +T} T{ +basic lua string +T} T{ +"string" +T} +T{ +number +T} T{ +basic lua number +T} T{ +3.\&5 +T} +T{ +integer +T} T{ +basic lua number that must be a whole number +T} T{ +3 +T} +T{ +function +T} T{ +basic lua number that must be a whole number +T} T{ +function() print("execute") end +T} +T{ +boolean +T} T{ +basic lua boolean +T} T{ +true | false +T} +T{ +direction +T} T{ +an exist +T} T{ +true | false +T} +.TE +.sp 1 +In Japokwm multiple types of tables are used to different kinds of information.\& +We gave those special tables certain names so that you can tell them apart more +easily.\& To show the content of a table we write one as follows: +.P +.nf +.RS 4 +table(string: string, string: integer, \&.\&.\&.) +.fi +.RE +.P +This table consists out of elements consisting out of keys of type string and a +value of type string and elements consisting out of keys of type string and +values of type integers which can exist anywhere between 0 and infinity times +denoted by ".\&.\&.\&" +.P +Tables without keys are called arrays and are denoted as follows: +.P +.nf +.RS 4 +array(string, integer, \&.\&.\&.) +.fi +.RE +.P +This is an array with a string, then an integer followed by any number of +integers +.P +.TS +allbox;l lx lx +l lx lx +l lx lx +l lx lx +l lx lx +l lx lx +l lx lx +l lx lx +l lx lx. +T{ +\fBTypes\fR +T} T{ +\fBDescription\fR +T} T{ +\fBExample\fR +T} +T{ +container +T} T{ +array(number, number, number, number) where each number represents x, y, width or height in the given order +T} T{ +{0.\&3, 0.\&1, 0.\&5, 0.\&7} +T} +T{ +layout_data_element +T} T{ +array(container, .\&.\&.\&) +T} T{ +{{0.\&3, 0.\&1, 0.\&5, 0.\&7}, {0.\&3, 0.\&1, 0.\&5, 0.\&7},} +T} +T{ +layout_data +T} T{ +array(layout_data_element, .\&.\&.\&) where the first layout_data_element will be used +T} T{ +{ { {0.\&3, 0.\&1, 0.\&5, 0.\&7}, {0.\&3, 0.\&1, 0.\&5, 0.\&7}, }, { {0.\&3, 0.\&1, 0.\&5, 0.\&7}, {0.\&3, 0.\&1, 0.\&5, 0.\&7},} } +T} +T{ +layout_info +T} T{ +array(string) The 1st argument represents the directory/name of the layout +T} T{ +{"main"} -- the directory/name is "main" +T} +T{ +color +T} T{ +array(number, number, number, number) the arguments represents rgba respectively between 0.\&.\&1 +T} T{ +{0, 1, 1, 1} -- cyan +T} +T{ +keybinding +T} T{ +array(string, function()) the arguments are the keys given by the string ++ and the function representing the action that will be taken +T} T{ +{"M-S-e", function() print("test") end} +T} +T{ +rule +T} T{ +array(string, string, function(n)) the arguments are class name and rule ++ respectively.\& The class and name are obtained from the window and the rule ++ is just a function that gets the container id.\& The function will be ++ executed when the layout is rearrange.\& +T} T{ +{"anki", "", function() print("test") end} +T} +T{ +monrule +T} T{ +array(string, function()) the arguments are name and rule respectively.\& The class and name are obtained from the window and the rule is just a function without arguments.\& The function will be executed when a layout is created.\& +T} T{ +{"", function() print("test") end} +T} +.TE +.sp 1 +.P +.SH NAMESPACES +.TS +allbox;l lx +l lx +l lx +l lx +l lx +l lx +l lx. +T{ +\fBNamespace\fR +T} T{ +\fBDescription\fR +T} +T{ +\fBaction\fR +T} T{ +Execute an action +T} +T{ +config +T} T{ +Configure certain properties or the behavior of the window manager +T} +T{ +container +T} T{ +Configure certain properties or the behavior of a container +T} +T{ +event +T} T{ +Bind a function to an event +T} +T{ +layout +T} T{ +Configure certain properties or the behavior of a layout +T} +T{ +l +T} T{ +Configure things locally to the layout +T} +.TE +.sp 1 +.SH ACTION +\fBarrange()\fR +.br + Arrange containers to the given information +.P +\fBdecrease_nmaster()\fR +.br + decrease the number of master windows +.P +\fBexec(cmd)\fR +.br + execute a shell command in a new pid +.br + +.br + cmd: string - the command +.P +\fBfocus_container(i)\fR +.br + focus the container at i +.br + +.br + i: integer - position on the stack +.P +\fBfocus_on_hidden_stack(i)\fR +.br + replace current container with a hidden container +.br + +.br + i: integer - relative position on the hidden stack +.P +\fBfocus_on_stack(i)\fR +.br + focuses the container at relative i +.br + +.br + i: integer - relative position on the stack +.P +\fBincrease_nmaster()\fR +.br + increase the number of master windows +.P +\fBkill(i)\fR +.br + kill the window at position i + +.br + i: integer - position on the stack +.P +\fBload_layout(l)\fR +.br + load a layout +.br + +.br + l: table(a: string, b: string) where +.P +\fBload_layout_in_set(set, i: position)\fR +.br + load a layout in the layout set +.br + +.br + set: string - the name of the set +.br + i: the layout +.P +\fBload_next_layout_in_set(set)\fR +.br + load the next layout in the layout set +.br + +.br + set: the name of the set +.P +\fBload_prev_layout_in_set(set)\fR +.br + load the previous layout in the layout set +.br + +.br + set: string - the name of the set +.P +\fBmove_container_to_workspace(i)\fR +.br + move the selected container to the workspace i +.br + +.br + i: integer +.P +\fBmove_resize(e)\fR +.br + move and resize the selected container to the mouse +.br + +.br + e: enum - given by info.\&cursor.\&mode +.P +\fBmove_to_scratchpad(i)\fR +.br + move the window at position i to the scratchpad + +.br + i: integer - position on the stack +.P +\fBquit()\fR +.br + quit the window manager +.P +\fBrepush(i, j)\fR +.br + push the container into the master area +.br + +.br + i: integer - container that needs to repushed +.br + j: integer - new position it will be pushed to +.P +\fBresize_main(n)\fR +.br + resize the master area +.br + +.br + n: number - relative size (between 0 and 1) +.P +\fBset_floating(b)\fR +.br + set the selected container's floating status +.br + +.br + b: boolean - status(true/false) +.P +\fBset_nmaster(i)\fR +.RS 4 +set the amount of master windows +.br + +.br + i: integer - the number of master windows +.P +.RE +\fBshow_scratchpad()\fR +.RS 4 +show the first window on the scratchpad.\& If it is already visible hide it +instead and move it to the end of the scratchpad.\& +.P +.RE +\fBswap_workspace(i, i2)\fR +.RS 4 +swap the workspace i with the workspace i2 +.br + +.br + i: integer workspace at i +.br + i2: integer workspace at i2 +.P +.RE +\fBtoggle_bars()\fR +.br + toggles layer_shell bars +.P +\fBtoggle_floating()\fR +.br + toggles if current container is floating +.P +\fBtoggle_layout()\fR +.br + toggle previous layout +.P +\fBtoggle_workspace()\fR +.br + toggle previous workspace +.P +\fBview(i)\fR +.br + view the ith workspace +.br + +.br + i: integer - ith workspace +.P +\fBzoom()\fR +.br + like dwm's zoom +.P +.SH CONTAINER +\fBcontainer_set_alpha(i, alpha)\fR +.br + set the opacity of the container +.br + +.br + i: integer - position of the container +.RS 4 +alpha: float - new alpha value of the container +.P +.RE +\fBcontainer_set_ratio(i, ratio)\fR +.br + set the ratio of the container +.br + +.br + i: integer - position of the container +.RS 4 +ratio: float - ratio of the container (if 0 it is interpreted as no ratio) +.P +.RE +\fBcontainer_set_sticky(i, sticky)\fR +.br + make container sticky +.br + +.br + i: integer - position of the container +.RS 4 +sticky: boolean - whether container is sticky +.P +.RE +.SH EVENT +\fBset_update_function(func)\fR +.br + set the update function +.br + +.br + func: function(n) - it will be called every time the another +.br + layout_element will be loaded +.br + +.br + n: integer - n represents the amount of containers +.P +\fBset_on_focus_function(func)\fR +.br + set the update function +.br + +.br + func: function(n) - it will be called every time a new container is +.br + selected layout_element will be loaded +.br + +.br + n: integer - n represents the current container position +.P +\fBset_on_start_function(func)\fR +.br + set the function that is executed on start of the windowmanager.\& + +.br + func: function() - it will be called when the windowmanager launches +.P +\fBset_create_container_function()\fR +.br + set the create_container function + +.br + func: function(n) - it will be called every time a container is created +.br + by Japokwm +.br + +.br + n: integer - n represents the current container position +.P +.SH LAYOUT +\fBset(name, layout_data)\fR +.br + set layout +.br + +.br + name: (string) - name of this layout +.RS 4 +layout_data: layout_data - layout_data for the layout +.P +.RE +.SH INFO +\fBget_this_container_count()\fR +.br + get the amount of visible containers in this workspace + +.br + integer - number of containers +.P +\fBthis_container_position()\fR +.br + get the position the container is at +.br + +.br + integer - position of the focused container +.P +\fBget_nmaster()\fR +.br + get the number of master windows +.br + +.br + integer - number of master windows +.P +\fBget_next_empty_workspace()\fR +.br + Get next workspaces not used by any window + +.br + integer - workspace id of this empty workspace +.P +\fBget_workspace()\fR +.br + Get the workspaces id of the current workspace + +.br + integer - workspace id +.P +\fBget_container_under_cursor()\fR +.br + get the container beneath the cursor +.br + +.br + integer - container id +.P +\fBis_container_not_in_limit()\fR +.br + returns whether a container doesn't violate min/max_width/height of the +.RS 4 +constraints +.br + +.br + boolean - whether it is in limit +.P +.RE +\fBis_container_not_in_master_limit()\fR +.br + returns whether a container doesn't violate min/max_width/height of the +.br + master constraints +.br + +.br + boolean - whether it is in limit +.P +.SH CONFIG +.P +\fBcreate_layout_set(name, layouts)\fR +.RS 4 +create a new layout_set +.br + +.br + name: string - name of the layout set +.br + layouts: layout_info - layouts that belong to this set +.P +.RE +\fBcreate_workspaces(names)\fR +.RS 4 +create or recreate all workspaces with an array of names for each +.br + workspace.\& The order of the workspace can be set by prefixing the names +.br + with: "%i:" where %i represents the position the workspace will be on.\& +.br + +.br + names: array(string) +.P +.RE +\fBreload()\fR +.br + reload the config file +.P +\fBset_arrange_by_focus(b)\fR +.br + if b is true windows will be tiled by means of the focus stack + +.br + b: boolean +.P +\fBset_border_color(color)\fR +.br + set the border color of windows +.br + +.br + color: color +.P +\fBset_default_layout(l)\fR +.br + set the default layout +.br + +.br + l: string - name/directory of the layout +.P +\fBset_float_borderpx(f)\fR +.br + set the border width of floating windows in pixel +.br + +.br + f: border width in pixel +.P +\fBset_focus_color(color)\fR +.br + set the color of the focused window +.br + +.br + color: color +.P +\fBset_focus_color(color)\fR +.br + set the color of the focused window +.br + +.br + color: color +.P +\fBset_hidden_edges(d)\fR +.br + set the directions edge borders will be hidden +.br + +.br + d: direction +.P +\fBset_keybinds(k)\fR +.RS 4 +set all keybindings existing in Japokwm and overwrite old keybindings +.br + +.br + k: array(keybinding, .\&.\&.\&) +.P +.RE +\fBset_layout_constraints(min_width: a, max_width: b, min_height: c, max_height: d)\fR +.br + Set the minimum and maximum dimensions of resizing any window +.br + +.br + a, b, c, d: number - relative width/height +.P +\fBset_master_constraints(min_width: a, max_width: b, min_height: c, max_height: d)\fR +.br + Set the minimum and maximum dimensions of resizing the master area +.br + +.br + a, b, c, d: number - relative width/height +.P +\fBset_master_layout_data(data)\fR +.br + set the way the windows in the master area are tiled depending on nmaster +.br + +.br + data: layout_data +.P +\fBset_mod(i)\fR +.br + set the modifier which is any number between 1 and 4.\& This causes the +.br + "mod" string to be replaced such that they correspond to the correct +.br + modifiers: +.P +.TS +allbox;l lx lx +lx lx lx +lx lx lx +lx lx lx +lx lx lx. +T{ +Number +T} T{ +Mod +T} T{ +Literally +T} +T{ +1 +T} T{ +Alt +T} T{ +"Alt_L" +T} +T{ +2 +T} T{ +Number lock +T} T{ +"Num_Lock" +T} +T{ +3 +T} T{ +AltGr +T} T{ +"ISO_Level3_Shift" +T} +T{ +4 +T} T{ +Super +T} T{ +"Super_L" +T} +.TE +.sp 1 + +.br + i: integer +.P +\fBset_monrules(r)\fR +.br + set the rules applied for the respective monitor +.br + +.br + r: array(monrule) +.P +\fBset_outer_gaps(i)\fR +.br + set how large the gap between all the windows and the root is +.br + +.br + i: integer - the size of those gaps in pixel +.P +\fBset_repeat_delay(i)\fR +.br + how long do you need to wait before the keyboard starts to repeat in +.br + +.br + i: integer - i is the delay given in milliseconds +.P +\fBset_repeat_rate(i)\fR +.br + how often will a character repeat +.br + +.br + i: integer - i is the repeat rate given in milliseconds +.P +\fBset_resize_data(data)\fR +.RS 4 +set the resize direction of the layout +.br + ; TODO improve this +.RE + +.br + data: array(array(integer, .\&.\&.\&)) +.P +\fBset_resize_direction(d)\fR +.br + set the resize direction of the layout +.br + +.br + d: direction +.P +\fBset_root_color(color)\fR +.br + set color of the root +.br + +.br + color: color +.P +\fBset_rules(r)\fR +.br + set the rules +.br + +.br + r: array(rule) +.P +\fBset_sloppy_focus(b)\fR +.br + set whether to use sloppy focus or not.\& If sloppy focus is activated you +.br + will focus windows by hovering above them.\& +.br + +.br + b: boolean +.P +\fBset_smart_hidden_edges(b)\fR +.br + if true edges are only hidden (see set_hidden_edges) if the number of +.br + containers in the current workspace <= 1 +.br + +.br + b: boolean +.P +\fBset_tile_borderpx(i)\fR +.br + set the border width of tiled windows in pixel +.br + +.br + i: integer - border width in pixel +.P +.SH MONITOR +\fBset_scale(n)\fR +.br + scale the monitor by n + +.br + n: number - the percentage the monitor will be scaled e.\&g.\& 1 = 100% +.P +\fBset_transform(e)\fR +.br + scale the monitor by n + +.br + % TODO: what is this enum?\& +.RS 4 +e: WL_LIST_TRANSFORMATION - an enum provided by info.\&monitor.\&transform +.P +.RE +.SH LOCAL +config beginning with l are considered local config and only apply for the +current layout.\& There are basically the same functions as usual but only some of +them can be used locally.\& Here is a list of available functions: +.P +.SS CONFIG +set_arrange_by_focus +.br +set_border_color +.br +set_float_borderpx +.br +set_focus_color +.br +set_hidden_edges +.br +set_inner_gaps +.br +set_layout_constraints +.br +set_master_constraints +.br +set_master_layout_data +.br +set_outer_gaps +.br +set_resize_data +.br +set_resize_direction +.br +set_sloppy_focus +.br +set_smart_hidden_edges +.br +set_tile_borderpx +.P +.SS EVENT +set_create_container_function +.br +set_update_function +.br + +.P +.SH SEE ALSO +\fBjapokwm\fR(1) diff --git a/src/keybinding.c b/src/keybinding.c index 9ea1f63e..45a340d9 100644 --- a/src/keybinding.c +++ b/src/keybinding.c @@ -1,8 +1,10 @@ #include "keybinding.h" +#include "input_manager.h" #include "server.h" #include "tile/tileUtils.h" #include "utils/parseConfigUtils.h" #include "stringop.h" +#include "workspace.h" const char *mods[8] = {"Shift_L", "Caps_Lock", "Control_L", "Alt_L", "", "", "Super_L", "ISO_Level3_Shift"}; const char *modkeys[4] = {"Alt_L", "Num_Lock", "ISO_Level3_Shift", "Super_L"}; @@ -38,7 +40,7 @@ static void sym_to_binding(char *dest, int mods, int sym) static void resolve_keybind_element(char *sym_dest, const char *bind) { struct monitor *m = selected_monitor; - struct workspace *ws = get_workspace(m->ws_id); + struct workspace *ws = monitor_get_active_workspace(m); struct layout *lt = ws->layout; if (strcmp(bind, "mod") == 0) { @@ -70,71 +72,69 @@ static void resolve_keybind_element(char *sym_dest, const char *bind) static bool is_same_keybind_element(const char *bind, const char *bind2) { - struct wlr_list bindarr = split_string(bind, "-"); - struct wlr_list bind2arr = split_string(bind2, "-"); + GPtrArray *bindarr = split_string(bind, "-"); + GPtrArray *bind2arr = split_string(bind2, "-"); - if (bind2arr.length == 0) + if (bind2arr->len == 0) return true; - if (bindarr.length != bind2arr.length) + if (bindarr->len != bind2arr->len) return false; // remove all resolved items out of bind2arr found in bindarr - for (int i = 0; i < bindarr.length; i++) { - int str1len = strlen(bind2arr.items[i]); + for (int i = 0; i < bindarr->len; i++) { + int str1len = strlen(g_ptr_array_index(bind2arr, i)); char bindelem[str1len]; - resolve_keybind_element(bindelem, bindarr.items[i]); + resolve_keybind_element(bindelem, g_ptr_array_index(bindarr, i)); - for (int j = 0; j < bind2arr.length; j++) { - int str2len = strlen(bind2arr.items[j]); + for (int j = 0; j < bind2arr->len; j++) { + int str2len = strlen(g_ptr_array_index(bind2arr, j)); char bind2elem[str2len]; - resolve_keybind_element(bind2elem, bind2arr.items[j]); + resolve_keybind_element(bind2elem, g_ptr_array_index(bind2arr, j)); - if (strcmp(bindarr.items[i], bind2elem) == 0) { - wlr_list_del(&bind2arr, j); + if (strcmp(g_ptr_array_index(bindarr, i), bind2elem) == 0) { + g_ptr_array_remove_index(bind2arr, j); break; } } } // if no items remain in bind2arr the bind must be correct - bool ret = bind2arr.length == 0; + bool ret = bind2arr->len == 0; return ret; } static bool is_same_keybind(const char *bind, const char *bind2) { - bool same = false; - same = is_same_keybind_element(bind, bind2); + bool same = is_same_keybind_element(bind, bind2); return same; } -static bool process_binding(lua_State *L, char *bind, int lua_ref) +static bool process_binding(lua_State *L, char *bind, GPtrArray *keybindings) { bool handled = false; - lua_rawgeti(L, LUA_REGISTRYINDEX, lua_ref); - int len = lua_rawlen(L, -1); - for (int i = 0; i < len; i++) { - lua_rawgeti(L, -1, i+1); - lua_rawgeti(L, -1, 1); - const char *ref = luaL_checkstring(L, -1); - lua_pop(L, 1); - if (is_same_keybind(bind, ref)) { - lua_rawgeti(L, -1, 2); + for (int i = 0; i < keybindings->len; i++) { + struct keybinding *keybinding = g_ptr_array_index(keybindings, i); + if (is_same_keybind(bind, keybinding->binding)) { + lua_rawgeti(L, LUA_REGISTRYINDEX, keybinding->lua_func_ref); lua_call_safe(L, 0, 0, 0); handled = true; } - lua_pop(L, 1); } - lua_pop(L, 1); return handled; } +struct keybinding *create_keybinding() +{ + struct keybinding *keybinding = calloc(1, sizeof(struct keybinding)); + return keybinding; +} + bool handle_keybinding(int mods, int sym) { char bind[128] = ""; sym_to_binding(bind, mods, sym); - bool handled = process_binding(L, bind, server.default_layout->options.keybinds_ref); + bool handled = process_binding(L, bind, server.default_layout->options.keybindings); return handled; } diff --git a/src/keyboard.c b/src/keyboard.c index 5e47908a..d2e783a1 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -1,13 +1,16 @@ #include "keyboard.h" +#include "seat.h" #include "server.h" #include "keybinding.h" +#include "utils/coreUtils.h" static bool handle_VT_keys(struct keyboard *kb, uint32_t keycode) { const xkb_keysym_t *syms; + struct wlr_input_device *wlr_device = kb->seat_device->input_device->wlr_device; int nsyms = - xkb_state_key_get_syms(kb->device->keyboard->xkb_state, keycode, &syms); + xkb_state_key_get_syms(wlr_device->keyboard->xkb_state, keycode, &syms); bool handled = false; for (int i = 0; i < nsyms; i++) { @@ -34,44 +37,48 @@ void cleanupkeyboard(struct wl_listener *listener, void *data) struct wlr_input_device *device = data; struct keyboard *kb = device->data; - wl_list_remove(&kb->destroy.link); - wl_list_remove(&kb->link); - free(kb); + destroy_keyboard(kb); } -void create_keyboard(struct wlr_input_device *device) +void create_keyboard(struct seat *seat, struct seat_device *seat_device) { - struct xkb_context *context; - struct xkb_keymap *keymap; - struct keyboard *kb; - - kb = device->data = calloc(1, sizeof(*kb)); - kb->device = device; + struct wlr_input_device *wlr_device = seat_device->input_device->wlr_device; + struct keyboard *kb = wlr_device->data = calloc(1, sizeof(struct keyboard)); + kb->seat_device = seat_device; + kb->seat = seat; /* Prepare an XKB keymap and assign it to the keyboard. */ - context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - keymap = xkb_map_new_from_names(context, NULL, + struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + struct xkb_keymap *keymap = xkb_map_new_from_names(context, NULL, XKB_KEYMAP_COMPILE_NO_FLAGS); - wlr_keyboard_set_keymap(device->keyboard, keymap); + wlr_keyboard_set_keymap(wlr_device->keyboard, keymap); xkb_keymap_unref(keymap); xkb_context_unref(context); - wlr_keyboard_set_repeat_info(device->keyboard, + wlr_keyboard_set_repeat_info(wlr_device->keyboard, server.default_layout->options.repeat_rate, server.default_layout->options.repeat_delay); /* Here we set up listeners for keyboard events. */ - kb->modifiers.notify = keypressmod; - wl_signal_add(&device->keyboard->events.modifiers, &kb->modifiers); - kb->key.notify = keypress; - wl_signal_add(&device->keyboard->events.key, &kb->key); - kb->destroy.notify = cleanupkeyboard; - wl_signal_add(&device->events.destroy, &kb->destroy); + LISTEN(&wlr_device->keyboard->events.modifiers, &kb->modifiers, keypressmod); + LISTEN(&wlr_device->keyboard->events.key, &kb->key, keypress); + LISTEN(&wlr_device->events.destroy, &kb->destroy, cleanupkeyboard); - wlr_seat_set_keyboard(server.seat, device); + wlr_seat_set_keyboard(seat->wlr_seat, wlr_device); /* And add the keyboard to our list of server.keyboards */ - wl_list_insert(&server.keyboards, &kb->link); + g_ptr_array_add(server.keyboards, kb); +} + +void destroy_keyboard(struct keyboard *kb) +{ + if (!kb) + return; + wl_list_remove(&kb->modifiers.link); + wl_list_remove(&kb->key.link); + wl_list_remove(&kb->destroy.link); + g_ptr_array_remove(server.keyboards, kb); + free(kb); } void keypress(struct wl_listener *listener, void *data) @@ -86,12 +93,13 @@ void keypress(struct wl_listener *listener, void *data) /* Get a list of keysyms based on the keymap for this keyboard */ const xkb_keysym_t *syms; struct xkb_state *state; - xkb_state_key_get_one_sym(kb->device->keyboard->xkb_state, keycode); + struct wlr_input_device *wlr_device = kb->seat_device->input_device->wlr_device; + xkb_state_key_get_one_sym(wlr_device->keyboard->xkb_state, keycode); /* create new state to clear the shift modifier to get a instead of A */ - state = xkb_state_new(kb->device->keyboard->keymap); + state = xkb_state_new(wlr_device->keyboard->keymap); int nsyms = xkb_state_key_get_syms(state, keycode, &syms); - uint32_t mods = wlr_keyboard_get_modifiers(kb->device->keyboard); + uint32_t mods = wlr_keyboard_get_modifiers(wlr_device->keyboard); bool handled = false; /* On _press_, attempt to process a compositor keybinding. */ @@ -106,9 +114,10 @@ void keypress(struct wl_listener *listener, void *data) } if (!handled) { + struct wlr_seat *wlr_seat = kb->seat_device->seat->wlr_seat; /* Pass unhandled keycodes along to the client. */ - wlr_seat_set_keyboard(server.seat, kb->device); - wlr_seat_keyboard_notify_key(server.seat, event->time_msec, + wlr_seat_set_keyboard(wlr_seat, wlr_device); + wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, event->keycode, event->state); } } @@ -124,8 +133,9 @@ void keypressmod(struct wl_listener *listener, void *data) * to the same seat. You can swap out the underlying wlr_keyboard like this * and wlr_seat handles this transparently. */ - wlr_seat_set_keyboard(server.seat, kb->device); + struct wlr_seat *wlr_seat = kb->seat->wlr_seat; + wlr_seat_set_keyboard(wlr_seat, kb->seat_device->input_device->wlr_device); /* Send modifiers to the client. */ - wlr_seat_keyboard_notify_modifiers(server.seat, - &kb->device->keyboard->modifiers); + wlr_seat_keyboard_notify_modifiers(wlr_seat, + &kb->seat_device->input_device->wlr_device->keyboard->modifiers); } diff --git a/src/layer_shell.c b/src/layer_shell.c index 35922155..e37594f5 100644 --- a/src/layer_shell.c +++ b/src/layer_shell.c @@ -1,36 +1,334 @@ #include "layer_shell.h" +#include +#include +#include + #include "monitor.h" #include "popup.h" -#include "stdlib.h" +#include "server.h" +#include "container.h" +#include "tile/tileUtils.h" +#include "render/render.h" +#include "input_manager.h" void create_notify_layer_shell(struct wl_listener *listener, void *data) { - /* This event is raised when wlr_xdg_shell receives a new xdg surface from a - * client, either a toplevel (application window) or popup. */ - struct wlr_layer_surface_v1 *layer_surface = data; - struct client *c; + struct wlr_layer_surface_v1 *wlr_layer_surface = data; + + if (!wlr_layer_surface->output) { + wlr_layer_surface->output = selected_monitor->wlr_output; + } - /* Allocate a Client for this surface */ union surface_t surface; - surface.layer = layer_surface; - c = layer_surface->data = create_client(LAYER_SHELL, surface); - - if (!c->surface.layer->output) { - c->surface.layer->output = selected_monitor->wlr_output; - } - struct monitor *m = output_to_monitor(c->surface.layer->output); - wlr_layer_surface_v1_configure(c->surface.layer, m->geom.width, m->geom.height); - - /* Listen to the various events it can emit */ - c->map.notify = maprequest; - wl_signal_add(&layer_surface->events.map, &c->map); - c->unmap.notify = unmap_notify; - wl_signal_add(&layer_surface->events.unmap, &c->unmap); - c->destroy.notify = destroy_notify; - wl_signal_add(&layer_surface->events.destroy, &c->destroy); - - /* popups */ - c->new_popup.notify = popup_handle_new_popup; - wl_signal_add(&layer_surface->events.new_popup, &c->new_popup); + surface.layer = wlr_layer_surface; + struct client *client = create_client(LAYER_SHELL, surface); + + LISTEN(&wlr_layer_surface->surface->events.commit, &client->commit, commitlayersurfacenotify); + LISTEN(&wlr_layer_surface->events.map, &client->map, map_layer_surface_notify); + LISTEN(&wlr_layer_surface->events.unmap, &client->unmap, unmap_layer_surface_notify); + LISTEN(&wlr_layer_surface->events.destroy, &client->destroy, destroy_layer_surface_notify); + LISTEN(&wlr_layer_surface->events.new_popup, &client->new_popup, popup_handle_new_popup); + LISTEN(&wlr_layer_surface->events.destroy, &client->new_popup, popup_handle_new_popup); + + struct monitor *m = wlr_layer_surface->output->data; + client->m = m; + struct container *con = create_container(client, m, false); + enum zwlr_layer_shell_v1_layer layer = wlr_layer_surface->client_pending.layer; + g_ptr_array_add(get_layer_list(m, layer), con); + + // Temporarily set the layer's current state to client_pending + // so that we can easily arrange it + struct wlr_layer_surface_v1_state old_state = wlr_layer_surface->current; + wlr_layer_surface->current = wlr_layer_surface->client_pending; + arrange_layers(m); + wlr_layer_surface->current = old_state; +} + +void map_layer_surface_notify(struct wl_listener *listener, void *data) +{ + struct client *c = wl_container_of(listener, c, map); + /* wlr_surface_send_enter(get_wlrsurface(c), c->surface.layer->output); */ + /* motion_notify(0); */ + add_container_to_tile(c->con); +} + +void unmap_layer_surface(struct client *c) +{ + /* struct container *sel_container = get_focused_container(selected_monitor); */ + /* c->surface.layer->mapped = 0; */ + /* if (get_wlrsurface(c) == server.seat->keyboard_state.focused_surface) */ + /* focus_container(sel_container, FOCUS_NOOP); */ + /* motion_notify(0); */ +} + +void unmap_layer_surface_notify(struct wl_listener *listener, void *data) +{ + struct client *c = wl_container_of(listener, c, unmap); + unmap_layer_surface(c); + remove_container_from_tile(c->con); + container_damage_whole(c->con); +} + +void destroy_layer_surface_notify(struct wl_listener *listener, void *data) +{ + struct client *c = wl_container_of(listener, c, destroy); + + if (c->surface.layer->mapped) + unmap_layer_surface(c); + remove_in_composed_list(server.layer_visual_stack_lists, cmp_ptr, c->con); + + wl_list_remove(&c->commit.link); + wl_list_remove(&c->map.link); + wl_list_remove(&c->unmap.link); + wl_list_remove(&c->destroy.link); + wl_list_remove(&c->new_popup.link); + + if (c->surface.layer->output) { + struct monitor *m = c->surface.layer->output->data; + if (m) + arrange_layers(m); + c->surface.layer->output = NULL; + } + + destroy_container(c->con); + destroy_client(c); +} + +void commitlayersurfacenotify(struct wl_listener *listener, void *data) +{ + struct client *c = wl_container_of(listener, c, commit); + struct wlr_layer_surface_v1 *wlr_layer_surface = c->surface.layer; + struct wlr_output *wlr_output = wlr_layer_surface->output; + + if (!wlr_output) + return; + + struct monitor *m = wlr_output->data; + arrange_layers(m); + struct container *con = c->con; + container_damage_part(con); + + if (c->surface.layer->current.layer != wlr_layer_surface->current.layer) { + remove_in_composed_list(server.layer_visual_stack_lists, cmp_ptr, con); + g_ptr_array_insert(get_layer_list(m, wlr_layer_surface->current.layer), 0, con); + } +} + +bool layer_shell_is_bar(struct container *con) +{ + assert(con->client->type == LAYER_SHELL); + + + struct wlr_layer_surface_v1 *wlr_layer_surface = con->client->surface.layer; + struct wlr_layer_surface_v1_state *state = &wlr_layer_surface->current; + + bool is_exclusive = state->exclusive_zone >= 0; + bool is_anchord_on_three_edges = cross_sum(state->anchor, 2) == 3; + bool is_anchord_on_one_edge = cross_sum(state->anchor, 2) == 1; + + return is_exclusive && (is_anchord_on_one_edge || is_anchord_on_three_edges); +} + +GPtrArray *get_layer_list(struct monitor *m, enum zwlr_layer_shell_v1_layer layer) +{ + GPtrArray *layer_list = NULL; + switch (layer) { + case ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND: + layer_list = g_ptr_array_index(server.layer_visual_stack_lists, 3); + break; + case ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM: + layer_list = g_ptr_array_index(server.layer_visual_stack_lists, 2); + break; + case ZWLR_LAYER_SHELL_V1_LAYER_TOP: + layer_list = g_ptr_array_index(server.layer_visual_stack_lists, 1); + break; + case ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY: + layer_list = g_ptr_array_index(server.layer_visual_stack_lists, 0); + break; + } + return layer_list; +} + +void arrange_layers(struct monitor *m) +{ + struct wlr_box usable_area = m->geom; + uint32_t layers_above_shell[] = { + ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, + ZWLR_LAYER_SHELL_V1_LAYER_TOP, + }; + + // Arrange exclusive surfaces from top->bottom + arrangelayer(m, server.layer_visual_stack_overlay, &usable_area, true); + arrangelayer(m, server.layer_visual_stack_top, &usable_area, true); + arrangelayer(m, server.layer_visual_stack_bottom, &usable_area, true); + arrangelayer(m, server.layer_visual_stack_background, &usable_area, true); + + if (memcmp(&usable_area, &m->root->geom, sizeof(struct wlr_box))) { + m->root->geom = usable_area; + arrange(m); + } + + // Arrange non-exlusive surfaces from top->bottom + arrangelayer(m, server.layer_visual_stack_overlay, &usable_area, false); + arrangelayer(m, server.layer_visual_stack_top, &usable_area, false); + arrangelayer(m, server.layer_visual_stack_bottom, &usable_area, false); + arrangelayer(m, server.layer_visual_stack_background, &usable_area, false); + + struct seat *seat = input_manager_get_default_seat(); + struct wlr_keyboard *kb = wlr_seat_get_keyboard(seat->wlr_seat); + // Find topmost keyboard interactive layer, if such a layer exists + for (size_t i = 0; i < LENGTH(layers_above_shell); i++) { + GPtrArray *layer_list = get_layer_list(m, layers_above_shell[i]); + for (int j = layer_list->len-1; j >= 0; j--) { + struct container *con = g_ptr_array_index(layer_list, j); + struct client *c = con->client; + struct wlr_layer_surface_v1 *layer_surface = c->surface.layer; + if (layer_surface->current.keyboard_interactive && layer_surface->mapped) { + // Deactivate the focused client. + // TODO fix this + focus_container(NULL); + wlr_seat_keyboard_notify_enter(seat->wlr_seat, + get_wlrsurface(c), + kb->keycodes, kb->num_keycodes, + &kb->modifiers); + return; + } + } + } +} + +void arrangelayer(struct monitor *m, GPtrArray *array, struct wlr_box *usable_area, bool exclusive) +{ + struct wlr_box full_area = m->geom; + + for (int i = 0; i < array->len; i++) { + struct container *con = g_ptr_array_index(array, i); + + if (!visible_on(monitor_get_active_tagset(m), con)) + continue; + + struct wlr_layer_surface_v1 *wlr_layer_surface = con->client->surface.layer; + struct wlr_layer_surface_v1_state *state = &wlr_layer_surface->current; + struct wlr_box bounds; + struct wlr_box box = { + .width = state->desired_width, + .height = state->desired_height + }; + const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT + | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; + const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP + | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; + + bool is_exclusive = (state->exclusive_zone > 0); + if (exclusive != is_exclusive) + continue; + + bounds = state->exclusive_zone == -1 ? full_area : *usable_area; + + // Horizontal axis + if ((state->anchor & both_horiz) && box.width == 0) { + box.x = bounds.x; + box.width = bounds.width; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { + box.x = bounds.x; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { + box.x = bounds.x + (bounds.width - box.width); + } else { + box.x = bounds.x + ((bounds.width / 2) - (box.width / 2)); + } + // Vertical axis + if ((state->anchor & both_vert) && box.height == 0) { + box.y = bounds.y; + box.height = bounds.height; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { + box.y = bounds.y; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { + box.y = bounds.y + (bounds.height - box.height); + } else { + box.y = bounds.y + ((bounds.height / 2) - (box.height / 2)); + } + // Margin + if ((state->anchor & both_horiz) == both_horiz) { + box.x += state->margin.left; + box.width -= state->margin.left + state->margin.right; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { + box.x += state->margin.left; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { + box.x -= state->margin.right; + } + if ((state->anchor & both_vert) == both_vert) { + box.y += state->margin.top; + box.height -= state->margin.top + state->margin.bottom; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { + box.y += state->margin.top; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { + box.y -= state->margin.bottom; + } + if (box.width < 0 || box.height < 0) { + wlr_layer_surface_v1_close(wlr_layer_surface); + continue; + } + con->geom = box; + + if (state->exclusive_zone > 0) + apply_exclusive(usable_area, state->anchor, state->exclusive_zone, + state->margin.top, state->margin.right, + state->margin.bottom, state->margin.left); + wlr_layer_surface_v1_configure(wlr_layer_surface, box.width, box.height); + } +} + +void apply_exclusive(struct wlr_box *usable_area, + uint32_t anchor, int32_t exclusive, + int32_t margin_top, int32_t margin_right, + int32_t margin_bottom, int32_t margin_left) { + struct edge edges[] = { + { // Top + .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, + .anchor_triplet = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, + .positive_axis = &usable_area->y, + .negative_axis = &usable_area->height, + .margin = margin_top, + }, + { // Bottom + .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, + .anchor_triplet = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, + .positive_axis = NULL, + .negative_axis = &usable_area->height, + .margin = margin_bottom, + }, + { // Left + .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT, + .anchor_triplet = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, + .positive_axis = &usable_area->x, + .negative_axis = &usable_area->width, + .margin = margin_left, + }, + { // Right + .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT, + .anchor_triplet = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, + .positive_axis = NULL, + .negative_axis = &usable_area->width, + .margin = margin_right, + } + }; + for (size_t i = 0; i < LENGTH(edges); i++) { + if ((anchor == edges[i].singular_anchor || anchor == edges[i].anchor_triplet) + && exclusive + edges[i].margin > 0) { + if (edges[i].positive_axis) + *edges[i].positive_axis += exclusive + edges[i].margin; + if (edges[i].negative_axis) + *edges[i].negative_axis -= exclusive + edges[i].margin; + break; + } + } } diff --git a/src/layout.c b/src/layout.c index f568d5da..4404de78 100644 --- a/src/layout.c +++ b/src/layout.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "server.h" #include "utils/coreUtils.h" @@ -30,9 +31,13 @@ struct layout *create_layout(lua_State *L) lua_get_default_layout_data(L); lua_ref_safe(L, LUA_REGISTRYINDEX, <->lua_layout_copy_data_ref); + lua_createtable(L, 0, 0); lua_ref_safe(L, LUA_REGISTRYINDEX, <->lua_layout_ref); + lua_get_default_resize_function(L); + lua_ref_safe(L, LUA_REGISTRYINDEX, <->lua_resize_function_ref); + return lt; } @@ -53,6 +58,7 @@ void lua_copy_table(lua_State *L, int *ref) void lua_copy_table_safe(lua_State *L, int *ref) { + assert(lua_istable(L, -1)); lua_getglobal_safe(L, "Deep_copy"); lua_insert(L, -2); lua_call_safe(L, 1, 1, 0); @@ -63,23 +69,23 @@ void lua_copy_table_safe(lua_State *L, int *ref) struct resize_constraints lua_toresize_constrains(lua_State *L) { - struct resize_constraints resize_constrains; + struct resize_constraints resize_constraints; lua_getfield(L, -1, "min_width"); - resize_constrains.min_width = luaL_checknumber(L, -1); + resize_constraints.min_width = luaL_checknumber(L, -1); lua_pop(L, 1); lua_getfield(L, -1, "max_width"); - resize_constrains.max_width = luaL_checknumber(L, -1); + resize_constraints.max_width = luaL_checknumber(L, -1); lua_pop(L, 1); lua_getfield(L, -1, "min_height"); - resize_constrains.min_height = luaL_checknumber(L, -1); + resize_constraints.min_height = luaL_checknumber(L, -1); lua_pop(L, 1); lua_getfield(L, -1, "max_height"); - resize_constrains.max_height = luaL_checknumber(L, -1); + resize_constraints.max_height = luaL_checknumber(L, -1); lua_pop(L, 1); - return resize_constrains; + return resize_constraints; } bool is_same_layout(struct layout layout, struct layout layout2) @@ -96,6 +102,7 @@ void copy_layout(struct layout *dest_lt, struct layout *src_lt) dest_lt->lua_layout_original_copy_data_ref = 0; dest_lt->lua_layout_ref = 0; dest_lt->lua_master_layout_data_ref = 0; + dest_lt->lua_resize_function_ref = 0; copy_layout_safe(dest_lt, src_lt); } @@ -124,11 +131,6 @@ void copy_layout_safe(struct layout *dest_lt, struct layout *src_lt) lua_copy_table_safe(L, &dest_lt->lua_layout_original_copy_data_ref); } - if (src_lt->lua_layout_original_copy_data_ref > 0) { - lua_rawgeti(L, LUA_REGISTRYINDEX, src_lt->lua_layout_original_copy_data_ref); - lua_copy_table_safe(L, &dest_lt->lua_layout_original_copy_data_ref); - } - if (src_lt->lua_master_layout_data_ref > 0) { lua_get_default_master_layout_data(L); lua_ref_safe(L, LUA_REGISTRYINDEX, &dest_lt->lua_master_layout_data_ref); @@ -139,12 +141,17 @@ void copy_layout_safe(struct layout *dest_lt, struct layout *src_lt) lua_ref_safe(L, LUA_REGISTRYINDEX, &dest_lt->lua_resize_data_ref); } + if (src_lt->lua_resize_function_ref > 0) { + lua_get_default_resize_function(L); + lua_ref_safe(L, LUA_REGISTRYINDEX, &dest_lt->lua_resize_function_ref); + } + copy_options(&dest_lt->options, &src_lt->options); return; } -bool lua_islayout_data(lua_State *L, const char *name) +bool lua_is_layout_data(lua_State *L, const char *name) { if (!lua_istable(L, -1)) return false; @@ -179,7 +186,9 @@ bool lua_islayout_data(lua_State *L, const char *name) return true; } -int cmp_layout(const struct layout *lt1, const struct layout *lt2) +int cmp_layout(const void *ptr1, const void *ptr2) { - return strcmp(lt1->symbol, lt2->symbol); + const struct layout *lt1 = ptr1; + const struct layout *lt2 = ptr2; + return strcmp(lt1->symbol, lt2->symbol) == 0; } diff --git a/src/lib/actions/actions.c b/src/lib/actions/lib_actions.c similarity index 77% rename from src/lib/actions/actions.c rename to src/lib/actions/lib_actions.c index e6b4f8bc..5d486b7d 100644 --- a/src/lib/actions/actions.c +++ b/src/lib/actions/lib_actions.c @@ -1,4 +1,4 @@ -#include "lib/actions/actions.h" +#include "lib/actions/lib_actions.h" #include #include @@ -7,6 +7,7 @@ #include #include #include +#include #include "container.h" #include "ipc-server.h" @@ -30,6 +31,24 @@ int lib_arrange(lua_State *L) return 0; } +int lib_create_output(lua_State *L) +{ + if (!wlr_backend_is_multi(server.backend)) { + lua_pushstring(L, "Expected a multi backend"); + return 1; + } + + bool done = false; + wlr_multi_for_each_backend(server.backend, create_output, &done); + + if (!done) { + lua_pushstring(L, "Can only create outputs for Wayland, X11 or headless backends"); + return 1; + } + + return 0; +} + int lib_focus_container(lua_State *L) { int pos = luaL_checkinteger(L, -1); @@ -39,7 +58,7 @@ int lib_focus_container(lua_State *L) if (!con) return 0; - focus_container(con, FOCUS_NOOP); + focus_container(con); return 0; } @@ -57,11 +76,11 @@ int lib_resize_main(lua_State *L) lua_pop(L, 1); struct monitor *m = selected_monitor; - struct workspace *ws = get_workspace(m->ws_id); + struct workspace *ws = monitor_get_active_workspace(m); struct layout *lt = ws->layout; int dir = lt->options.resize_dir; - lua_getglobal_safe(L, "Resize_main_all"); + lua_rawgeti(L, LUA_REGISTRYINDEX, lt->lua_resize_function_ref); lua_rawgeti(L, LUA_REGISTRYINDEX, lt->lua_layout_copy_data_ref); lua_rawgeti(L, LUA_REGISTRYINDEX, lt->lua_layout_original_copy_data_ref); lua_rawgeti(L, LUA_REGISTRYINDEX, lt->lua_resize_data_ref); @@ -85,7 +104,7 @@ int lib_set_floating(lua_State *L) struct monitor *m = selected_monitor; set_container_monitor(sel, m); - set_container_floating(sel, fix_position, floating); + set_container_floating(sel, container_fix_position, floating); arrange(); return 0; @@ -153,9 +172,10 @@ int lib_focus_on_hidden_stack(lua_State *L) int lib_move_resize(lua_State *L) { + struct seat *seat = input_manager_get_default_seat(); int ui = luaL_checkinteger(L, -1); lua_pop(L, 1); - move_resize(ui); + move_resize(seat->cursor, ui); return 0; } @@ -164,7 +184,7 @@ int lib_move_to_scratchpad(lua_State *L) int i = luaL_checkinteger(L, -1); lua_pop(L, 1); struct monitor *m = selected_monitor; - struct container *con = get_container(get_workspace(m->ws_id), i); + struct container *con = get_container(m->tagset, i); move_to_scratchpad(con, 0); return 0; } @@ -178,18 +198,40 @@ int lib_view(lua_State *L) if (!m) return 0; - struct workspace *ws = get_workspace(ws_id); - if (!ws) + tagset_focus_workspace(ws_id); + arrange(); + return 0; +} + +int lib_tag_view(lua_State *L) +{ + uint64_t tags_dec = luaL_checkinteger(L, -1); + + lua_pop(L, 1); + + struct monitor *m = selected_monitor; + if (!m) return 0; - push_workspace(m, ws); + BitSet *tmp_bitset = bitset_from_value(tags_dec); + BitSet *bitset = bitset_create(server.workspaces->len); + for (int i = 0; i < bitset->size; i++) { + int last_bit_id = tmp_bitset->size - 1; + bitset_assign(bitset, i, bitset_test(tmp_bitset, last_bit_id - i)); + } + bitset_destroy(tmp_bitset); + + tagset_toggle_add(m->tagset, bitset); + arrange(); return 0; } int lib_toggle_view(lua_State *L) { struct monitor *m = selected_monitor; - focus_most_recent_container(get_workspace(m->ws_id), FOCUS_LIFT); + focus_most_recent_container(m->tagset); + struct container *sel = get_focused_container(m); + lift_container(sel); arrange(false); return 0; } @@ -199,7 +241,7 @@ int lib_toggle_floating(lua_State *L) struct container *sel = get_focused_container(selected_monitor); if (!sel) return 0; - set_container_floating(sel, fix_position, !sel->floating); + set_container_floating(sel, container_fix_position, !sel->floating); arrange(); return 0; } @@ -219,7 +261,6 @@ int lib_move_container_to_workspace(lua_State *L) int lib_quit(lua_State *L) { - printf("quit\n"); wl_display_terminate(server.wl_display); close_error_file(); return 0; @@ -228,18 +269,19 @@ int lib_quit(lua_State *L) int lib_zoom(lua_State *L) { struct monitor *m = selected_monitor; - struct workspace *ws = monitor_get_active_workspace(m); + struct tagset *tagset = monitor_get_active_tagset(m); struct container *sel = get_focused_container(m); if (!sel) return 0; - int position = wlr_list_find(&ws->tiled_containers, cmp_ptr, sel); - if (position == INVALID_POSITION) + guint position; + bool found = g_ptr_array_find(tagset->list_set->tiled_containers, sel, &position); + if (!found) return 0; - if (sel == ws->tiled_containers.items[0]) { + if (sel == g_ptr_array_index(tagset->list_set->tiled_containers, 0)) { repush(1, 0); } else { repush(position, 0); @@ -248,12 +290,12 @@ int lib_zoom(lua_State *L) arrange(); // focus new master window - struct container *con0 = get_container(ws, 0); - focus_container(con0, FOCUS_NOOP); + struct container *con = get_container(tagset, 0); + focus_container(con); struct layout *lt = get_layout_in_monitor(m); if (lt->options.arrange_by_focus) { - focus_most_recent_container(ws, FOCUS_NOOP); + focus_most_recent_container(tagset); arrange(); } return 0; @@ -307,7 +349,6 @@ int lib_load_prev_layout_in_set(lua_State *L) lua_rawgeti(L, LUA_REGISTRYINDEX, server.layout_set.layout_sets_ref); if (!lua_is_index_defined(L, layout_set_key)) { - printf("is nil return\n"); lua_pop(L, 1); return 0; } @@ -372,8 +413,8 @@ int lib_kill(lua_State *L) int i = luaL_checkinteger(L, -1); lua_pop(L, 1); - struct workspace *ws = get_workspace(m->ws_id); - struct container *con = get_container(ws, i); + struct tagset *tagset = monitor_get_active_tagset(m); + struct container *con = get_container(tagset, i); if (!con) return 0; @@ -394,8 +435,7 @@ int lib_toggle_layout(lua_State *L) int lib_toggle_workspace(lua_State *L) { - struct monitor *m = selected_monitor; - push_workspace(m, get_workspace(server.previous_workspace_id)); + push_tagset(server.previous_tagset); return 0; } @@ -408,22 +448,24 @@ int lib_swap_workspace(lua_State *L) lua_pop(L, 1); struct monitor *m = selected_monitor; - struct workspace *ws = monitor_get_active_workspace(m); + struct tagset *tagset = monitor_get_active_tagset(m); - for (int i = 0; i < ws->tiled_containers.length; i++) { + for (int i = 0; i < tagset->list_set->tiled_containers->len; i++) { struct container *con = get_container(0, i); - if (exist_on(con, get_workspace(ws_id1))) { + struct workspace *ws1 = get_workspace(ws_id1); + if (exist_on(workspace_get_tagset(ws1), con)) { con->client->ws_id = ws_id2; continue; } - if (exist_on(con, get_workspace(ws_id2))) { + struct workspace *ws2 = get_workspace(ws_id2); + if (exist_on(workspace_get_tagset(ws2), con)) { con->client->ws_id = ws_id1; continue; } } arrange(); - focus_most_recent_container(ws, FOCUS_NOOP); + focus_most_recent_container(tagset); root_damage_whole(m->root); return 0; } diff --git a/src/lib/actions/libcontainer.c b/src/lib/actions/lib_container.c similarity index 72% rename from src/lib/actions/libcontainer.c rename to src/lib/actions/lib_container.c index be1d577f..8bbeefb6 100644 --- a/src/lib/actions/libcontainer.c +++ b/src/lib/actions/lib_container.c @@ -1,4 +1,4 @@ -#include "lib/actions/libcontainer.h" +#include "lib/actions/lib_container.h" #include "container.h" #include "client.h" #include "tile/tileUtils.h" @@ -12,7 +12,11 @@ int container_set_sticky(lua_State *L) lua_pop(L, 1); struct monitor *m = selected_monitor; - struct container *con = get_container(get_workspace(m->ws_id), i); + struct container *con = get_container(m->tagset, i); + + if (!con) + return 0; + client_setsticky(con->client, sticky); return 0; } @@ -25,8 +29,8 @@ int container_set_ratio(lua_State *L) lua_pop(L, 1); struct monitor *m = selected_monitor; - struct workspace *ws = get_workspace(m->ws_id); - struct container *con = get_container(ws, position); + struct tagset *ts = monitor_get_active_tagset(m); + struct container *con = get_container(ts, position); if (!con) return 0; @@ -43,8 +47,8 @@ int container_set_alpha(lua_State *L) lua_pop(L, 1); struct monitor *m = selected_monitor; - struct workspace *ws = get_workspace(m->ws_id); - struct container *con = get_container(ws, position); + struct tagset *ts = monitor_get_active_tagset(m); + struct container *con = get_container(ts, position); if (!con) return 0; diff --git a/src/lib/config/config.c b/src/lib/config/lib_config.c similarity index 72% rename from src/lib/config/config.c rename to src/lib/config/lib_config.c index 181b43f6..8ae81b7e 100644 --- a/src/lib/config/config.c +++ b/src/lib/config/lib_config.c @@ -1,4 +1,4 @@ -#include "lib/config/config.h" +#include "lib/config/lib_config.h" #include "utils/gapUtils.h" #include "utils/coreUtils.h" #include "utils/parseConfigUtils.h" @@ -7,16 +7,17 @@ #include "ipc-server.h" #include "monitor.h" #include "workspace.h" +#include "keybinding.h" +#include "rules/rule.h" +#include "rules/mon_rule.h" int lib_reload(lua_State *L) { - struct workspace *ws = get_workspace(selected_monitor->ws_id); - server.default_layout->options = get_default_options(); - remove_loaded_layouts(&server.workspaces); + remove_loaded_layouts(server.workspaces); load_config(L); - load_default_layout(L, ws); + load_default_layout(L); ipc_event_workspace(); @@ -136,39 +137,26 @@ int lib_set_default_layout(lua_State *L) // TODO refactor this function hard to read int lib_create_workspaces(lua_State *L) { - struct wlr_list *tag_names = &server.default_layout->options.tag_names; - wlr_list_clear(tag_names, NULL); + GPtrArray *tag_names = server.default_layout->options.tag_names; + list_clear(tag_names, NULL); size_t len = lua_rawlen(L, -1); for (int i = 0; i < len; i++) { - char *ws_name = get_config_array_str(L, "workspaces", i+1); - wlr_list_push(tag_names, ws_name); + const char *ws_name = get_config_array_str(L, "workspaces", i+1); + g_ptr_array_add(tag_names, strdup(ws_name)); } lua_pop(L, 1); - update_workspaces(&server.workspaces, tag_names); - - ipc_event_workspace(); - return 0; } -int lib_set_rules(lua_State *L) +int lib_add_rule(lua_State *L) { - if (server.default_layout->options.rules) - free(server.default_layout->options.rules); - - size_t len = lua_rawlen(L, -1); - server.default_layout->options.rule_count = len; - server.default_layout->options.rules = calloc(len, sizeof(struct rule)); - struct rule *rules = server.default_layout->options.rules; - - for (int i = 0; i < server.default_layout->options.rule_count; i++) { - struct rule r = get_config_array_rule(L, "rules", i+1); - rules[i] = r; - } - + GPtrArray *rules = server.default_layout->options.rules; + struct rule *rule = get_config_rule(L); lua_pop(L, 1); + + g_ptr_array_add(rules, rule); return 0; } @@ -185,28 +173,23 @@ int lib_create_layout_set(lua_State *L) return 0; } -int lib_set_monrules(lua_State *L) +int lib_add_mon_rule(lua_State *L) { - if (server.default_layout->options.monrules) - free(server.default_layout->options.monrules); - - size_t len = lua_rawlen(L, -1); - server.default_layout->options.monrule_count = len; - server.default_layout->options.monrules = calloc(len, sizeof(struct monrule)); - struct monrule *rules = server.default_layout->options.monrules; - - for (int i = 0; i < len; i++) { - struct monrule r = get_config_array_monrule(L, "rules", i+1); - rules[i] = r; - } - + struct mon_rule *mon_rule = get_config_mon_rule(L); lua_pop(L, 1); + + GPtrArray *mon_rules = server.default_layout->options.mon_rules; + g_ptr_array_add(mon_rules, mon_rule); return 0; } -int lib_set_keybinds(lua_State *L) +int lib_bind_key(lua_State *L) { - lua_copy_table_safe(L, &server.default_layout->options.keybinds_ref); + struct keybinding *keybinding = create_keybinding(); + lua_ref_safe(L, LUA_REGISTRYINDEX, &keybinding->lua_func_ref); + keybinding->binding = strdup(luaL_checkstring(L, -1)); + lua_pop(L, 1); + g_ptr_array_add(server.default_layout->options.keybindings, keybinding); return 0; } @@ -231,9 +214,15 @@ int lib_set_resize_direction(lua_State *L) return 0; } +int lib_set_resize_function(lua_State *L) +{ + lua_ref_safe(L, LUA_REGISTRYINDEX, &server.default_layout->lua_resize_function_ref); + return 0; +} + int lib_set_master_layout_data(lua_State *L) { - if (lua_islayout_data(L, "master_layout_data")) + if (lua_is_layout_data(L, "master_layout_data")) lua_copy_table_safe(L, &server.default_layout->lua_master_layout_data_ref); else lua_pop(L, 1); diff --git a/src/lib/config/localconfig.c b/src/lib/config/local_config.c similarity index 91% rename from src/lib/config/localconfig.c rename to src/lib/config/local_config.c index 833db359..c71fab62 100644 --- a/src/lib/config/localconfig.c +++ b/src/lib/config/local_config.c @@ -1,8 +1,9 @@ -#include "lib/config/localconfig.h" +#include "lib/config/local_config.h" #include "monitor.h" #include "utils/gapUtils.h" #include "utils/coreUtils.h" +#include "workspace.h" int local_set_arrange_by_focus(lua_State *L) { @@ -124,11 +125,18 @@ int local_set_resize_direction(lua_State *L) return 0; } +int local_set_resize_function(lua_State *L) +{ + struct layout *lt = get_layout_in_monitor(selected_monitor); + lua_ref_safe(L, LUA_REGISTRYINDEX, <->lua_resize_function_ref); + return 0; +} + int local_set_master_layout_data(lua_State *L) { struct layout *lt = get_layout_in_monitor(selected_monitor); - if (lua_islayout_data(L, "master_layout_data")) + if (lua_is_layout_data(L, "master_layout_data")) lua_copy_table_safe(L, <->lua_master_layout_data_ref); else lua_pop(L, 1); diff --git a/src/lib/event_handler/lib_event_handler.c b/src/lib/event_handler/lib_event_handler.c index 44eb1f50..770b2c63 100644 --- a/src/lib/event_handler/lib_event_handler.c +++ b/src/lib/event_handler/lib_event_handler.c @@ -1,32 +1,19 @@ #include "lib/event_handler/lib_event_handler.h" +#include + #include "utils/coreUtils.h" #include "server.h" -int lib_set_update_function(lua_State *L) -{ - lua_ref_safe(L, LUA_REGISTRYINDEX, - &server.default_layout->options.event_handler.update_func_ref); - return 0; -} - -int lib_set_create_container_function(lua_State *L) -{ - lua_ref_safe(L, LUA_REGISTRYINDEX, - &server.default_layout->options.event_handler.create_container_func_ref); - return 0; -} - -int lib_set_on_focus_function(lua_State *L) +int lib_add_listener(lua_State *L) { - lua_ref_safe(L, LUA_REGISTRYINDEX, - &server.default_layout->options.event_handler.on_focus_func_ref); - return 0; -} + int *func_ref = calloc(1, sizeof(int)); + lua_ref_safe(L, LUA_REGISTRYINDEX, func_ref); + const char *event = luaL_checkstring(L, -1); + lua_pop(L, 1); -int lib_set_on_start_function(lua_State *L) -{ - lua_ref_safe(L, LUA_REGISTRYINDEX, - &server.default_layout->options.event_handler.on_start_func_ref); + struct event_handler *event_handler = server.default_layout->options.event_handler; + GPtrArray *signal = event_name_to_signal(event_handler, event); + g_ptr_array_add(signal, func_ref); return 0; } diff --git a/src/lib/event_handler/local_event_handler.c b/src/lib/event_handler/local_event_handler.c index 19860029..64814b25 100644 --- a/src/lib/event_handler/local_event_handler.c +++ b/src/lib/event_handler/local_event_handler.c @@ -1,22 +1,20 @@ #include "lib/event_handler/local_event_handler.h" +#include "monitor.h" #include "utils/coreUtils.h" #include "server.h" -int local_set_update_function(lua_State *L) +int local_add_listener(lua_State *L) { struct layout *lt = get_layout_in_monitor(selected_monitor); - struct event_handler *ev = <->options.event_handler; + struct event_handler *event_handler = lt->options.event_handler; - lua_ref_safe(L, LUA_REGISTRYINDEX, &ev->update_func_ref); - return 0; -} - -int local_set_create_container_function(lua_State *L) -{ - struct layout *lt = get_layout_in_monitor(selected_monitor); - struct event_handler *ev = <->options.event_handler; + int *func_ref = calloc(1, sizeof(int)); + lua_ref_safe(L, LUA_REGISTRYINDEX, func_ref); + const char *event = luaL_checkstring(L, -1); + lua_pop(L, 1); - lua_ref_safe(L, LUA_REGISTRYINDEX, &ev->create_container_func_ref); + GPtrArray *signal = event_name_to_signal(event_handler, event); + g_ptr_array_add(signal, func_ref); return 0; } diff --git a/src/lib/info/info.c b/src/lib/info/lib_info.c similarity index 66% rename from src/lib/info/info.c rename to src/lib/info/lib_info.c index b513225d..1169e9ae 100644 --- a/src/lib/info/info.c +++ b/src/lib/info/lib_info.c @@ -1,17 +1,19 @@ -#include "lib/info/info.h" +#include "lib/info/lib_info.h" #include #include "container.h" +#include "monitor.h" #include "server.h" #include "tile/tileUtils.h" +#include "workspace.h" int lib_get_this_container_count(lua_State *L) { struct monitor *m = selected_monitor; - struct workspace *ws = monitor_get_active_workspace(m); + struct tagset *tagset = monitor_get_active_tagset(m); - int i = get_slave_container_count(ws) + 1; + int i = get_slave_container_count(tagset) + 1; lua_pushinteger(L, i); return 1; } @@ -32,15 +34,14 @@ int lib_get_next_empty_workspace(lua_State *L) lua_pop(L, 1); int id = luaL_checkinteger(L, -1); lua_pop(L, 1); - printf("lib_get_next_empty_workspace: %i\n", id); struct workspace *ws; switch (dir) { case WLR_DIRECTION_LEFT: - ws = get_prev_empty_workspace(&server.workspaces, id); + ws = get_prev_empty_workspace(server.workspaces, id); break; case WLR_DIRECTION_RIGHT: - ws = get_next_empty_workspace(&server.workspaces, id); + ws = get_next_empty_workspace(server.workspaces, id); break; default: ws = get_workspace(id); @@ -61,20 +62,37 @@ int lib_get_nmaster(lua_State *L) int lib_get_workspace(lua_State *L) { struct monitor *m = selected_monitor; - lua_pushinteger(L, m->ws_id); + lua_pushinteger(L, m->tagset->selected_ws_id); return 1; } int lib_get_container_under_cursor(lua_State *L) { - struct wlr_cursor *cursor = server.cursor.wlr_cursor; + struct seat *seat = input_manager_get_default_seat(); + struct wlr_cursor *wlr_cursor = seat->cursor->wlr_cursor; - struct container *con = xy_to_container(cursor->x, cursor->y); + struct container *con = xy_to_container(wlr_cursor->x, wlr_cursor->y); int pos = get_position_in_container_stack(con); lua_pushinteger(L, pos); return 1; } +int lib_get_root_area(lua_State *L) +{ + struct monitor *m = selected_monitor; + struct root *root = m->root; + lua_createtable(L, 1, 0); + lua_pushinteger(L, root->geom.x); + lua_rawseti(L, -2, 1); + lua_pushinteger(L, root->geom.y); + lua_rawseti(L, -2, 2); + lua_pushinteger(L, root->geom.width); + lua_rawseti(L, -2, 3); + lua_pushinteger(L, root->geom.height); + lua_rawseti(L, -2, 4); + return 1; +} + int lib_is_container_not_in_limit(lua_State *L) { struct layout *lt = get_layout_in_monitor(selected_monitor); diff --git a/src/lib/layout/lib_layout.c b/src/lib/layout/lib_layout.c index b7eeb7c1..61fe07a2 100644 --- a/src/lib/layout/lib_layout.c +++ b/src/lib/layout/lib_layout.c @@ -4,13 +4,14 @@ #include "monitor.h" #include "server.h" #include "utils/coreUtils.h" +#include "workspace.h" // TODO refactor int lib_set_layout(lua_State *L) { int ref = 0; // 2. argument -- layout_set - if (lua_islayout_data(L, "layout_data")) { + if (lua_is_layout_data(L, "layout_data")) { lua_ref_safe(L, LUA_REGISTRYINDEX, &ref); } else { lua_pop(L, 1); @@ -30,12 +31,13 @@ int lib_set_layout(lua_State *L) struct workspace *ws = monitor_get_active_workspace(selected_monitor); - int i = wlr_list_find(&ws->loaded_layouts, (cmp_func_t)cmp_layout, <); - if (i != -1) { - struct layout *old_lt = ws->loaded_layouts.items[i]; + guint i; + bool found = g_ptr_array_find_with_equal_func(ws->loaded_layouts, lt, cmp_layout, &i); + if (found) { + struct layout *old_lt = g_ptr_array_index(ws->loaded_layouts, i); lt->lua_layout_copy_data_ref = old_lt->lua_layout_copy_data_ref; } else { - wlr_list_insert(&ws->loaded_layouts, 0, lt); + g_ptr_array_insert(ws->loaded_layouts, 0, lt); if (ref > 0) { lt->lua_layout_copy_data_ref = ref; diff --git a/src/list_set.c b/src/list_set.c new file mode 100644 index 00000000..4220c5a7 --- /dev/null +++ b/src/list_set.c @@ -0,0 +1,131 @@ +#include "list_set.h" + +#include + +#include "ipc-server.h" +#include "server.h" +#include "utils/coreUtils.h" +#include "container.h" + +struct list_set *create_list_set() +{ + struct list_set *list_set = calloc(1, sizeof(struct list_set)); + + list_set->container_lists = g_ptr_array_new(); + list_set->visible_container_lists = g_ptr_array_new(); + + list_set->independent_containers = g_ptr_array_new(); + list_set->tiled_containers = g_ptr_array_new(); + list_set->hidden_containers = g_ptr_array_new(); + list_set->floating_containers = g_ptr_array_new(); + + g_ptr_array_add(list_set->container_lists, list_set->tiled_containers); + g_ptr_array_add(list_set->container_lists, list_set->floating_containers); + g_ptr_array_add(list_set->container_lists, list_set->hidden_containers); + + g_ptr_array_add(list_set->visible_container_lists, list_set->tiled_containers); + g_ptr_array_add(list_set->visible_container_lists, list_set->floating_containers); + + list_set->focus_stack_lists = g_ptr_array_new(); + list_set->focus_stack_visible_lists = g_ptr_array_new(); + list_set->focus_stack_lists_with_layer_shell = g_ptr_array_new(); + + list_set->focus_stack_layer_background = g_ptr_array_new(); + list_set->focus_stack_layer_bottom = g_ptr_array_new(); + list_set->focus_stack_layer_top = g_ptr_array_new(); + list_set->focus_stack_layer_overlay = g_ptr_array_new(); + list_set->focus_stack_on_top = g_ptr_array_new(); + list_set->focus_stack_normal = g_ptr_array_new(); + list_set->focus_stack_hidden = g_ptr_array_new(); + list_set->focus_stack_not_focusable = g_ptr_array_new(); + + g_ptr_array_add(list_set->focus_stack_lists, list_set->focus_stack_layer_top); + g_ptr_array_add(list_set->focus_stack_lists, list_set->focus_stack_on_top); + g_ptr_array_add(list_set->focus_stack_lists, list_set->focus_stack_normal); + g_ptr_array_add(list_set->focus_stack_lists, list_set->focus_stack_not_focusable); + g_ptr_array_add(list_set->focus_stack_lists, list_set->focus_stack_hidden); + + g_ptr_array_add(list_set->focus_stack_lists_with_layer_shell, list_set->focus_stack_layer_overlay); + g_ptr_array_add(list_set->focus_stack_lists_with_layer_shell, list_set->focus_stack_layer_top); + g_ptr_array_add(list_set->focus_stack_lists_with_layer_shell, list_set->focus_stack_on_top); + g_ptr_array_add(list_set->focus_stack_lists_with_layer_shell, list_set->focus_stack_normal); + g_ptr_array_add(list_set->focus_stack_lists_with_layer_shell, list_set->focus_stack_not_focusable); + g_ptr_array_add(list_set->focus_stack_lists_with_layer_shell, list_set->focus_stack_layer_bottom); + g_ptr_array_add(list_set->focus_stack_lists_with_layer_shell, list_set->focus_stack_layer_background); + + g_ptr_array_add(list_set->focus_stack_visible_lists, list_set->focus_stack_on_top); + g_ptr_array_add(list_set->focus_stack_visible_lists, list_set->focus_stack_normal); + g_ptr_array_add(list_set->focus_stack_visible_lists, list_set->focus_stack_not_focusable); + + list_set->all_lists = g_ptr_array_new(); + g_ptr_array_add(list_set->all_lists, list_set->floating_containers); + g_ptr_array_add(list_set->all_lists, list_set->tiled_containers); + g_ptr_array_add(list_set->all_lists, list_set->hidden_containers); + g_ptr_array_add(list_set->all_lists, list_set->independent_containers); + g_ptr_array_add(list_set->all_lists, list_set->focus_stack_layer_background); + g_ptr_array_add(list_set->all_lists, list_set->focus_stack_layer_bottom); + g_ptr_array_add(list_set->all_lists, list_set->focus_stack_layer_top); + g_ptr_array_add(list_set->all_lists, list_set->focus_stack_layer_overlay); + g_ptr_array_add(list_set->all_lists, list_set->focus_stack_on_top); + g_ptr_array_add(list_set->all_lists, list_set->focus_stack_normal); + g_ptr_array_add(list_set->all_lists, list_set->focus_stack_hidden); + g_ptr_array_add(list_set->all_lists, list_set->focus_stack_not_focusable); + return list_set; +} + +void destroy_list_set(struct list_set *list_set) +{ + g_ptr_array_free(list_set->container_lists, TRUE); + g_ptr_array_free(list_set->visible_container_lists, TRUE); + g_ptr_array_free(list_set->independent_containers, TRUE); + g_ptr_array_free(list_set->tiled_containers, TRUE); + g_ptr_array_free(list_set->hidden_containers, TRUE); + g_ptr_array_free(list_set->floating_containers, TRUE); + g_ptr_array_free(list_set->focus_stack_lists, TRUE); + g_ptr_array_free(list_set->focus_stack_visible_lists, TRUE); + g_ptr_array_free(list_set->focus_stack_lists_with_layer_shell, TRUE); + g_ptr_array_free(list_set->focus_stack_layer_background, TRUE); + g_ptr_array_free(list_set->focus_stack_layer_bottom, TRUE); + g_ptr_array_free(list_set->focus_stack_layer_top, TRUE); + g_ptr_array_free(list_set->focus_stack_layer_overlay, TRUE); + g_ptr_array_free(list_set->focus_stack_on_top, TRUE); + g_ptr_array_free(list_set->focus_stack_normal, TRUE); + g_ptr_array_free(list_set->focus_stack_hidden, TRUE); + g_ptr_array_free(list_set->focus_stack_not_focusable, TRUE); + g_ptr_array_free(list_set->all_lists, TRUE); + free(list_set); +} + +void append_list_set(struct list_set *dest, struct list_set *src) +{ + for (int i = 0; i < dest->all_lists->len; i++) { + GPtrArray *dest_list = g_ptr_array_index(dest->all_lists, i); + GPtrArray *src_list = g_ptr_array_index(src->all_lists, i); + wlr_list_cat(dest_list, src_list); + } +} + +void clear_list_set(struct list_set *list_set) +{ + for (int i = 0; i < list_set->all_lists->len; i++) { + GPtrArray *dest_list = g_ptr_array_index(list_set->all_lists, i); + list_clear(dest_list, NULL); + } +} + +void list_set_remove_list_set(struct list_set *dest, struct list_set *src) +{ + for (int i = 0; i < src->all_lists->len; i++) { + GPtrArray *src_containers = g_ptr_array_index(src->all_lists, i); + GPtrArray *dest_list = g_ptr_array_index(dest->all_lists, i); + for (int j = 0; j < src_containers->len; j++) { + struct container *con = g_ptr_array_index(src_containers, j); + + guint position; + bool found = g_ptr_array_find(dest_list, con, &position); + if (found) { + g_ptr_array_remove_index(dest_list, position); + } + } + } +} diff --git a/src/main.c b/src/main.c index acb5a718..471d2809 100644 --- a/src/main.c +++ b/src/main.c @@ -14,9 +14,11 @@ #include #include #include +#include +#include +#include #include -#include "clipboard.h" #include "ipc-server.h" #include "keyboard.h" #include "layer_shell.h" @@ -35,58 +37,17 @@ static void handle_new_inputdevice(struct wl_listener *listener, void *data); static void run(char *startup_cmd); static int setup(); -/* global event handlers */ -static struct wl_listener cursor_axis = {.notify = axisnotify}; -static struct wl_listener cursor_button = {.notify = buttonpress}; -static struct wl_listener cursor_frame = {.notify = cursorframe}; -static struct wl_listener cursor_motion = {.notify = motion_relative}; -static struct wl_listener cursor_motion_absolute = {.notify = motion_absolute}; -static struct wl_listener new_input = {.notify = handle_new_inputdevice}; -static struct wl_listener new_output = {.notify = create_monitor}; -static struct wl_listener new_xdeco = {.notify = createxdeco}; -static struct wl_listener new_xdg_surface = {.notify = create_notify_xdg}; -static struct wl_listener new_layer_shell_surface = {.notify = create_notify_layer_shell}; - -static struct wl_listener new_xwayland_surface = {.notify = create_notifyx11}; - static void cleanup() { close_error_file(); +#if JAPOKWM_HAS_XWAYLAND wlr_xwayland_destroy(server.xwayland.wlr_xwayland); +#endif wl_display_destroy_clients(server.wl_display); - wlr_xcursor_manager_destroy(server.cursor_mgr); - wlr_cursor_destroy(server.cursor.wlr_cursor); wlr_output_layout_destroy(server.output_layout); } -static void handle_new_inputdevice(struct wl_listener *listener, void *data) -{ - /* This event is raised by the backend when a new input device becomes - * available. */ - struct wlr_input_device *device = data; - uint32_t caps; - switch (device->type) { - case WLR_INPUT_DEVICE_KEYBOARD: - create_keyboard(device); - break; - case WLR_INPUT_DEVICE_POINTER: - create_pointer(device); - break; - default: - /* XXX handle other input device types */ - break; - } - /* We need to let the wlr_seat know what our capabilities are, which is - * communiciated to the client. In dwl we always have a server.cursor, even - * if there are no pointer devices, so we always include that capability. */ - /* XXX do we actually require a cursor? */ - caps = WL_SEAT_CAPABILITY_POINTER; - if (!wl_list_empty(&server.keyboards)) - caps |= WL_SEAT_CAPABILITY_KEYBOARD; - wlr_seat_set_capabilities(server.seat, caps); -} - static void run(char *startup_cmd) { pid_t startup_pid = -1; @@ -95,7 +56,7 @@ static void run(char *startup_cmd) const char *socket = wl_display_add_socket_auto(server.wl_display); if (!socket) - wlr_log(WLR_INFO, "startup: display_add_socket_auto"); + printf("startup: display_add_socket_auto\n"); /* Set the WAYLAND_DISPLAY environment variable to our socket and run the * startup command if requested. */ @@ -104,28 +65,27 @@ static void run(char *startup_cmd) /* Start the backend. This will enumerate outputs and inputs, become the DRM * master, etc */ if (!wlr_backend_start(server.backend)) - wlr_log(WLR_INFO, "startup: backend_start"); + printf("startup: backend_start"); /* Now that outputs are initialized, choose initial selMon based on * cursor position, and set default cursor image */ update_monitor_geometries(); - struct monitor *m = xy_to_monitor(server.cursor.wlr_cursor->x, server.cursor.wlr_cursor->y); + struct seat *seat = input_manager_get_default_seat(); + struct cursor *cursor = seat->cursor; + struct monitor *m = xy_to_monitor(cursor->wlr_cursor->x, cursor->wlr_cursor->y); focus_monitor(m); /* XXX hack to get cursor to display in its initial location (100, 100) * instead of (0, 0) and then jumping. still may not be fully * initialized, as the image/coordinates are not transformed for the * monitor when displayed here */ - wlr_cursor_warp_closest(server.cursor.wlr_cursor, NULL, server.cursor.wlr_cursor->x, server.cursor.wlr_cursor->y); - wlr_xcursor_manager_set_cursor_image(server.cursor_mgr, "left_ptr", server.cursor.wlr_cursor); + wlr_cursor_warp_closest(seat->cursor->wlr_cursor, NULL, cursor->wlr_cursor->x, cursor->wlr_cursor->y); + wlr_xcursor_manager_set_cursor_image(seat->cursor->xcursor_mgr, "left_ptr", cursor->wlr_cursor); if (startup_cmd) { startup_pid = fork(); - if (startup_pid < 0) - wlr_log(WLR_ERROR, "startup: fork"); if (startup_pid == 0) { execl("/bin/sh", "/bin/sh", "-c", startup_cmd, (void *)NULL); - wlr_log(WLR_ERROR, "startup: execl"); } } /* Run the Wayland event loop. This does not return until you exit the @@ -143,11 +103,9 @@ static void run(char *startup_cmd) static int setup() { L = luaL_newstate(); - luaL_openlibs(L); - load_libs(L); + load_lua_api(L); init_error_file(); - server.default_layout = create_layout(L); server.layout_set = get_default_layout_set(); init_utils(L); @@ -157,6 +115,8 @@ static int setup() server.wl_display = wl_display_create(); server.wl_event_loop = wl_display_get_event_loop(server.wl_display); ipc_init(server.wl_event_loop); + server.default_layout = create_layout(L); + load_config(L); /* The backend is a wlroots feature which abstracts the underlying input and * output hardware. The autocreate option will choose the most suitable @@ -164,7 +124,7 @@ static int setup() * if an X11 server is running. The NULL argument here optionally allows you * to pass in a custom renderer if wlr_renderer doesnt). */ if (!(server.backend = wlr_backend_autocreate(server.wl_display))) { - wlr_log(WLR_INFO, "couldn't create backend"); + printf("couldn't create backend\n"); return EXIT_FAILURE; } @@ -187,17 +147,17 @@ static int setup() wlr_gamma_control_manager_v1_create(server.wl_display); wlr_primary_selection_v1_device_manager_create(server.wl_display); wlr_viewporter_create(server.wl_display); + wlr_idle_create(server.wl_display); + wlr_idle_inhibit_v1_create(server.wl_display); /* Creates an output layout, which a wlroots utility for working with an * arrangement of screens in a physical layout. */ server.output_layout = wlr_output_layout_create(); wlr_xdg_output_manager_v1_create(server.wl_display, server.output_layout); - /* Configure textures */ - wlr_list_init(&render_data.textures); /* Configure a listener to be notified when new outputs are available on the * backend. */ - wl_signal_add(&server.backend->events.new_output, &new_output); + wl_signal_add(&server.backend->events.new_output, &server.new_output); /* Set up our client lists and the xdg-shell. The xdg-shell is a * Wayland protocol which is used for application windows. For more @@ -207,7 +167,7 @@ static int setup() */ server.xdg_shell = wlr_xdg_shell_create(server.wl_display); - wl_signal_add(&server.xdg_shell->events.new_surface, &new_xdg_surface); + wl_signal_add(&server.xdg_shell->events.new_surface, &server.new_xdg_surface); // remove csd(client side decorations) completely from xdg based windows wlr_server_decoration_manager_set_default_mode( wlr_server_decoration_manager_create(server.wl_display), @@ -215,43 +175,26 @@ static int setup() server.layer_shell = wlr_layer_shell_v1_create(server.wl_display); wl_signal_add(&server.layer_shell->events.new_surface, - &new_layer_shell_surface); + &server.new_layer_shell_surface); - /* Use xdg_decoration protocol to negotiate server-side decorations */ - server.xdeco_mgr = wlr_xdg_decoration_manager_v1_create(server.wl_display); - wl_signal_add(&server.xdeco_mgr->events.new_toplevel_decoration, &new_xdeco); + server.input_inhibitor_mgr = wlr_input_inhibit_manager_create(server.wl_display); - /* - * Creates a server.cursor, which is a wlroots utility for tracking the cursor - * image shown on screen. - */ - server.cursor.wlr_cursor = wlr_cursor_create(); - wlr_cursor_attach_output_layout(server.cursor.wlr_cursor, server.output_layout); + /* setup virtual pointer manager*/ + server.virtual_pointer_mgr = wlr_virtual_pointer_manager_v1_create(server.wl_display); - /* Creates an xcursor manager, another wlroots utility which loads up - * Xcursor themes to source cursor images from and makes sure that cursor - * images are available at all scale factors on the screen (necessary for - * HiDPI support). Scaled cursors will be loaded with each output. */ - server.cursor_mgr = wlr_xcursor_manager_create(NULL, 24); + /* setup virtual keyboard manager */ + server.virtual_keyboard_mgr = wlr_virtual_keyboard_manager_v1_create(server.wl_display); - /* - * wlr_cursor *only* displays an image on screen. It does not move around - * when the pointer moves. However, we can attach input devices to it, and - * it will generate aggregate events for all of them. In these events, we - * can choose how we want to process them, forwarding them to clients and - * moving the cursor around. More detail on this process is described in my - * input handling blog post: - * - * https://drewdevault.com/2018/07/17/Input-handling-in-wlroots.html - * - * And more comments are sprinkled throughout the notify functions above. - */ - wl_signal_add(&server.cursor.wlr_cursor->events.motion, &cursor_motion); - wl_signal_add(&server.cursor.wlr_cursor->events.motion_absolute, - &cursor_motion_absolute); - wl_signal_add(&server.cursor.wlr_cursor->events.button, &cursor_button); - wl_signal_add(&server.cursor.wlr_cursor->events.axis, &cursor_axis); - wl_signal_add(&server.cursor.wlr_cursor->events.frame, &cursor_frame); + /* setup relative pointer manager */ + server.relative_pointer_mgr = wlr_relative_pointer_manager_v1_create(server.wl_display); + /* wl_signal_add(&server.virtual_keyboard_mgr->events.new_virtual_keyboard, &new_virtual_keyboard); */ + + /* Use xdg_decoration protocol to negotiate server-side decorations */ + server.xdeco_mgr = wlr_xdg_decoration_manager_v1_create(server.wl_display); + wl_signal_add(&server.xdeco_mgr->events.new_toplevel_decoration, &server.new_xdeco); + + server.pointer_constraints = wlr_pointer_constraints_v1_create(server.wl_display); + wl_signal_add(&server.pointer_constraints->events.new_constraint, &server.new_pointer_constraint); /* * Configures a seat, which is a single "seat" at which a user sits and @@ -259,31 +202,12 @@ static int setup() * pointer, touch, and drawing tablet device. We also rig up a listener to * let us know when new input devices are available on the backend. */ - wl_list_init(&server.keyboards); - wl_signal_add(&server.backend->events.new_input, &new_input); - server.seat = wlr_seat_create(server.wl_display, "seat0"); - wl_signal_add(&server.seat->events.request_set_cursor, &request_set_cursor); - wl_signal_add(&server.seat->events.request_set_primary_selection, &request_set_psel); - wl_signal_add(&server.seat->events.request_set_selection, &request_set_sel); - - /* - * Initialise the XWayland X server. - * It will be started when the first X client is started. - */ - server.xwayland.wlr_xwayland = wlr_xwayland_create(server.wl_display, - server.compositor, true); - if (server.xwayland.wlr_xwayland) { - server.xwayland_ready.notify = handle_xwayland_ready; - wl_signal_add(&server.xwayland.wlr_xwayland->events.ready, &server.xwayland_ready); - wl_signal_add(&server.xwayland.wlr_xwayland->events.new_surface, &new_xwayland_surface); - wlr_xwayland_set_seat(server.xwayland.wlr_xwayland, server.seat); - - setenv("DISPLAY", server.xwayland.wlr_xwayland->display_name, true); - } else { - wlr_log(WLR_ERROR, "failed to setup XWayland X server, continuing without it"); - unsetenv("DISPLAY"); - } + server.input_manager = create_input_manager(); + struct seat *seat = create_seat("seat0"); +#ifdef JAPOKWM_HAS_XWAYLAND + init_xwayland(server.wl_display, seat); +#endif return 0; } @@ -309,6 +233,11 @@ void print_help() "\n"); } +void print_version() +{ + printf("japokwm "JAPOKWM_VERSION"\n"); +} + void print_usage() { printf("Usage: japokwm [options] [command]\n\n"); @@ -317,6 +246,10 @@ void print_usage() int main(int argc, char *argv[]) { +#if DEBUG + setbuf(stdout, NULL); +#endif + init_server(); char *startup_cmd = ""; @@ -326,12 +259,13 @@ int main(int argc, char *argv[]) {"config", required_argument, NULL, 'c'}, {"path", required_argument, NULL, 'p'}, {"startup", no_argument, NULL, 's'}, + {"version", no_argument, NULL, 'v'}, {0, 0, 0, 0} }; int c; int option_index = 0; - while ((c = getopt_long(argc, argv, "h:c:p:s", long_options, &option_index)) != -1) { + while ((c = getopt_long(argc, argv, "h:c:p:s:v", long_options, &option_index)) != -1) { switch (c) { case 's': startup_cmd = optarg; @@ -340,12 +274,15 @@ int main(int argc, char *argv[]) server.config_file = optarg; break; case 'p': - server.config_dir = optarg; + g_ptr_array_insert(server.config_paths, 0, optarg); break; case 'h': print_help(); return EXIT_SUCCESS; break; + case 'v': + print_version(); + return EXIT_SUCCESS; default: print_usage(); return EXIT_SUCCESS; @@ -356,9 +293,6 @@ int main(int argc, char *argv[]) return EXIT_SUCCESS; } - // TODO delete to increase performance - setbuf(stdout, NULL); - // Wayland requires XDG_RUNTIME_DIR for creating its communications // socket if (!getenv("XDG_RUNTIME_DIR")) { @@ -367,7 +301,7 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } if (setup()) { - wlr_log(WLR_ERROR, "didn't find file"); + printf("failed to setup japokwm\n"); return EXIT_FAILURE; } diff --git a/src/meson.build b/src/meson.build index 47505d59..396da430 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,15 +1,5 @@ -add_project_arguments('-rdynamic', '-DWLR_USE_UNSTABLE', '-Werror', '-Wno-unused-function','-Wno-uninitialized', language: 'c') - -deps = [\ - dependency('xcb'), - dependency('xkbcommon'), - dependency('wayland-server'), - dependency('wayland-egl'), - dependency('pixman-1'), - dependency('wlroots', version: '>=0.13'), - dependency('x11'), - dependency('json-c'), - ] +add_project_arguments('-rdynamic', '-DWLR_USE_UNSTABLE', '-Werror', +'-Wno-unused-function', '-Wno-uninitialized', '-Wno-sizeof-pointer-div', language: 'c') foreach name : ['lua', 'lua5.3', 'lua-5.3', 'lua53'] luaDep = dependency(name, version: '>=5.3', required: false) @@ -24,29 +14,45 @@ deps += [luaDep] main = files('main.c') srcs = files( + 'rules/rule.c', + 'rules/mon_rule.c', + 'input_manager.c', + 'seat.c', + 'bitset/bitset.c', + 'utils/vector.c', 'client.c', 'command.c', 'container.c', 'cursor.c', + 'event_handler.c', 'ipc-json.c', 'ipc-server.c', 'keybinding.c', + 'keyboard.c', + 'layer_shell.c', 'layout.c', - 'lib/actions/actions.c', - 'lib/actions/libcontainer.c', - 'lib/config/config.c', - 'lib/config/localconfig.c', - 'lib/info/info.c', + 'layout_set.c', + 'command/commands.c', + 'lib/actions/lib_actions.c', + 'lib/actions/lib_container.c', + 'lib/config/lib_config.c', + 'lib/config/local_config.c', + 'lib/event_handler/lib_event_handler.c', + 'lib/event_handler/local_event_handler.c', + 'lib/info/lib_info.c', 'lib/layout/lib_layout.c', + 'lib/monitor/lib_monitor.c', + 'list_set.c', + 'main.c', 'monitor.c', 'options.c', 'output.c', 'popup.c', 'render/render.c', 'root.c', + 'scratchpad.c', 'server.c', - 'stringop.c', - 'tile/tile.c', + 'tagset.c', 'tile/tileUtils.c', 'translationLayer.c', 'utils/coreUtils.c', @@ -54,33 +60,23 @@ srcs = files( 'utils/parseConfigUtils.c', 'utils/stringUtils.c', 'utils/writeFile.c', - 'wlr-layer-shell-unstable-v1-protocol.c', + 'wlr_signal.c', 'workspace.c', - 'xdg-shell-protocol.c', - 'xwayland.c', - 'keyboard.c', - 'layout_set.c', - 'event_handler.c', - 'lib/event_handler/lib_event_handler.c', - 'lib/event_handler/local_event_handler.c', - 'layer_shell.c', - 'lib/monitor/lib_monitor.c', 'xdg_shell.c', - 'clipboard.c', - 'scratchpad.c', + 'xwayland.c', ) libName = 'japokwm_lib' wmlib = static_library(libName, - [srcs], + [srcs, commonSrcs], dependencies: deps, - include_directories: inc, + include_directories: include_dirs, link_args: ['-lm'], ) executable('japokwm', [main], dependencies: deps, - include_directories: inc, + include_directories: include_dirs, link_with: [wmlib], install: true, ) diff --git a/src/monitor.c b/src/monitor.c index 08a98f11..1d4ce7d2 100644 --- a/src/monitor.c +++ b/src/monitor.c @@ -1,19 +1,21 @@ #include "monitor.h" #include #include -#include #include #include #include #include +#include +#include +#include #include "ipc-server.h" -#include "lib/actions/actions.h" #include "render/render.h" #include "server.h" #include "tile/tileUtils.h" #include "workspace.h" #include "utils/parseConfigUtils.h" +#include "layer_shell.h" struct wl_list sticky_stack; @@ -22,7 +24,6 @@ struct monitor *selected_monitor; static void handle_output_damage_frame(struct wl_listener *listener, void *data); static void handle_output_frame(struct wl_listener *listener, void *data); static void handle_output_mode(struct wl_listener *listener, void *data); -static void evaluate_monrules(struct wlr_output *output); void create_monitor(struct wl_listener *listener, void *data) { @@ -50,16 +51,14 @@ void create_monitor(struct wl_listener *listener, void *data) m->frame.notify = handle_output_frame; wl_signal_add(&m->wlr_output->events.frame, &m->frame); - wlr_xcursor_manager_load(server.cursor_mgr, 1); - /* Set up event listeners */ m->destroy.notify = destroy_monitor; wl_signal_add(&output->events.destroy, &m->destroy); m->mode.notify = handle_output_mode; wl_signal_add(&output->events.mode, &m->mode); - bool is_first_monitor = server.mons.length == 0; - wlr_list_push(&server.mons, m); + bool is_first_monitor = server.mons->len == 0; + g_ptr_array_add(server.mons, m); /* Adds this to the output layout. The add_auto function arranges outputs * from left-to-right in the order they appear. A more sophisticated @@ -76,48 +75,53 @@ void create_monitor(struct wl_listener *listener, void *data) m->geom = *wlr_output_layout_get_box(server.output_layout, m->wlr_output); m->root = create_root(m, m->geom); - m->ws_id = INVALID_WORKSPACE_ID; if (is_first_monitor) { + focus_monitor(m); - load_config(L); - if (server.default_layout->options.tag_names.length <= 0) { + if (server.default_layout->options.tag_names->len <= 0) { handle_error("tag_names is empty, loading default tag_names"); - init_tagnames(&server.default_layout->options.tag_names); + create_tagnames(&server.default_layout->options.tag_names); } - create_workspaces(&server.workspaces, - &server.default_layout->options.tag_names, server.default_layout); + server.workspaces = create_workspaces(server.default_layout->options.tag_names); - call_on_start_function(&server.default_layout->options.event_handler); + call_on_start_function(server.default_layout->options.event_handler); } - evaluate_monrules(output); + apply_mon_rules(server.default_layout->options.mon_rules, m); - struct workspace *ws = get_workspace(0); - focus_next_unoccupied_workspace(m, &server.workspaces, ws); - // TODO is this needed? - ws = monitor_get_active_workspace(m); - load_default_layout(L, ws); - copy_layout_from_selected_workspace(&server.workspaces); + focus_next_unoccupied_workspace(m, server.workspaces, get_workspace(0)); + load_default_layout(L); + struct workspace *ws = monitor_get_active_workspace(m); + copy_layout_from_selected_workspace(server.workspaces); set_root_color(m->root, ws->layout->options.root_color); if (!wlr_output_commit(output)) return; } -static void evaluate_monrules(struct wlr_output *output) +void create_output(struct wlr_backend *backend, void *data) { - for (int i = 0; i < server.default_layout->options.monrule_count; i++) { - struct monrule r = server.default_layout->options.monrules[i]; - if (!r.name || strstr(output->name, r.name)) { - if (!r.lua_func_ref) - continue; - lua_rawgeti(L, LUA_REGISTRYINDEX, r.lua_func_ref); - lua_call_safe(L, 0, 0, 0); - } + bool *done = data; + if (*done) { + return; } + + if (wlr_backend_is_wl(backend)) { + wlr_wl_output_create(backend); + *done = true; + } else if (wlr_backend_is_headless(backend)) { + wlr_headless_add_output(backend, 1920, 1080); + *done = true; + } +/* #if WLR_HAS_X11_BACKEND */ + else if (wlr_backend_is_x11(backend)) { + wlr_x11_output_create(backend); + *done = true; + } +/* #endif */ } static void handle_output_frame(struct wl_listener *listener, void *data) @@ -157,25 +161,61 @@ static void handle_output_mode(struct wl_listener *listener, void *data) struct monitor *m = wl_container_of(listener, m, mode); m->geom = *wlr_output_layout_get_box(server.output_layout, m->wlr_output); arrange_monitor(m); + arrange_layers(m); } void destroy_monitor(struct wl_listener *listener, void *data) { struct monitor *m = wl_container_of(listener, m, destroy); - focus_workspace(m, NULL); + wl_list_remove(&m->mode.link); + wl_list_remove(&m->frame.link); + wl_list_remove(&m->damage_frame.link); + wl_list_remove(&m->destroy.link); + + tagset_release(m->tagset); + destroy_root(m->root); - wlr_list_remove(&server.mons, cmp_ptr, m); + g_ptr_array_remove(server.mons, m); + m->wlr_output->data = NULL; + + if (server.previous_tagset) { + if (server.previous_tagset->m == m) { + tagset_release(server.previous_tagset); + server.previous_tagset = NULL; + } + } + + int len = length_of_composed_list(server.client_lists); + int j = 0; + while (j < len) { + struct client *c = get_in_composed_list(server.client_lists, j); + if (c->m == m) { + kill_client(c); + g_ptr_array_remove_index(server.client_lists, j); + len--; + } else { + j++; + } + } + + free(m); + + if (server.mons->len <= 0) + return; + + struct monitor *new_focused_monitor = g_ptr_array_index(server.mons, 0); + focus_monitor(new_focused_monitor); } -void center_mouse_in_monitor(struct monitor *m) +void center_cursor_in_monitor(struct cursor *cursor, struct monitor *m) { if (!m) return; int xcenter = m->geom.x + m->geom.width/2; int ycenter = m->geom.y + m->geom.height/2; - wlr_cursor_warp(server.cursor.wlr_cursor, NULL, xcenter, ycenter); + wlr_cursor_warp(cursor->wlr_cursor, NULL, xcenter, ycenter); } void scale_monitor(struct monitor *m, float scale) @@ -193,8 +233,8 @@ void transform_monitor(struct monitor *m, enum wl_output_transform transform) void update_monitor_geometries() { - for (int i = 0; i < server.mons.length; i++) { - struct monitor *m = server.mons.items[i]; + for (int i = 0; i < server.mons->len; i++) { + struct monitor *m = g_ptr_array_index(server.mons, i); m->geom = *wlr_output_layout_get_box(server.output_layout, m->wlr_output); } } @@ -208,40 +248,26 @@ void focus_monitor(struct monitor *m) /* wlr_xwayland_set_seat(server.xwayland.wlr_xwayland, m->wlr_output.) */ - struct workspace *ws = get_workspace(m->ws_id); + struct tagset *tagset = monitor_get_active_tagset(m); if (selected_monitor) { - struct workspace *sel_ws = monitor_get_active_workspace(selected_monitor); - for (int i = 0; i < sel_ws->floating_containers.length; i++) { - struct container *con = sel_ws->floating_containers.items[i]; - if (visible_on(con, get_workspace(sel_ws->id))) { + struct tagset *sel_ts = monitor_get_active_tagset(selected_monitor); + for (int i = 0; i < sel_ts->list_set->floating_containers->len; i++) { + struct container *con = g_ptr_array_index(sel_ts->list_set->floating_containers, i); + if (visible_on(sel_ts, con)) { + struct workspace *ws = get_workspace(tagset->selected_ws_id); move_container_to_workspace(con, ws); } } } selected_monitor = m; - focus_workspace(m, ws); -} - -void push_selected_workspace(struct monitor *m, struct workspace *ws) -{ - if (!m || !ws) - return; - focus_workspace(m, get_workspace(ws->id)); -} - -struct monitor *dirtomon(int dir) -{ - struct monitor *m = selected_monitor; - int m_index = wlr_list_find(&server.mons, cmp_ptr, m); - - return get_relative_item_in_list(&server.mons, m_index, dir); + focus_tagset(tagset); } struct monitor *output_to_monitor(struct wlr_output *output) { - for (int i = 0; i < server.mons.length; i++) { - struct monitor *m = server.mons.items[i]; + for (int i = 0; i < server.mons->len; i++) { + struct monitor *m = g_ptr_array_index(server.mons, i); if (m->wlr_output == output) { return m; } @@ -256,15 +282,24 @@ struct monitor *xy_to_monitor(double x, double y) return o ? o->data : NULL; } +struct tagset *monitor_get_active_tagset(struct monitor *m) +{ + if (!m) + return NULL; + + return m->tagset; +} + inline struct workspace *monitor_get_active_workspace(struct monitor *m) { if (!m) return NULL; - return get_workspace(m->ws_id); + struct tagset *tagset = monitor_get_active_tagset(m); + return get_workspace(tagset->selected_ws_id); } inline struct layout *get_layout_in_monitor(struct monitor *m) { - return get_workspace(m->ws_id)->layout; + return monitor_get_active_workspace(m)->layout; } diff --git a/src/options.c b/src/options.c index 9162669e..4151da3c 100644 --- a/src/options.c +++ b/src/options.c @@ -1,23 +1,27 @@ #include "options.h" #include +#include #include "client.h" #include "utils/coreUtils.h" #include "layout.h" +#include "keybinding.h" +#include "utils/vector.h" -void init_tagnames(struct wlr_list *tag_names) +GPtrArray *create_tagnames() { - wlr_list_init(tag_names); - wlr_list_push(tag_names, "1:1"); - wlr_list_push(tag_names, "2:2"); - wlr_list_push(tag_names, "3:3"); - wlr_list_push(tag_names, "4:4"); - wlr_list_push(tag_names, "5:5"); - wlr_list_push(tag_names, "6:6"); - wlr_list_push(tag_names, "7:7"); - wlr_list_push(tag_names, "8:8"); - wlr_list_push(tag_names, "9:9"); + GPtrArray *tag_names = g_ptr_array_new(); + g_ptr_array_add(tag_names, "1:1"); + g_ptr_array_add(tag_names, "2:2"); + g_ptr_array_add(tag_names, "3:3"); + g_ptr_array_add(tag_names, "4:4"); + g_ptr_array_add(tag_names, "5:5"); + g_ptr_array_add(tag_names, "6:6"); + g_ptr_array_add(tag_names, "7:7"); + g_ptr_array_add(tag_names, "8:8"); + g_ptr_array_add(tag_names, "9:9"); + return tag_names; } struct options get_default_options() @@ -45,18 +49,17 @@ struct options get_default_options() .float_border_px = 3, .inner_gap = 0, .outer_gap = 0, - .event_handler = get_default_event_handler(), - .monrule_count = 0, - .monrules = NULL, - .rule_count = 0, - .rules = NULL, + .event_handler = create_event_handler(), + .mon_rules = g_ptr_array_new(), + .rules = g_ptr_array_new(), .modkey = 0, .arrange_by_focus = false, .hidden_edges = WLR_EDGE_NONE, .smart_hidden_edges = false, }; - init_tagnames(&options.tag_names); + options.tag_names = create_tagnames(); + options.keybindings = g_ptr_array_new(); return options; } diff --git a/src/popup.c b/src/popup.c index 96535737..4c1caf34 100644 --- a/src/popup.c +++ b/src/popup.c @@ -104,14 +104,15 @@ static void popup_damage(struct xdg_popup *xdg_popup, bool whole) void popup_handle_new_popup(struct wl_listener *listener, void *data) { - struct client *c = wl_container_of(listener, c, new_popup); + struct client *client = wl_container_of(listener, client, new_popup); struct wlr_xdg_popup *xdg_popup = data; - struct container *con = c->con; - if (con->m != selected_monitor) + struct container *con = client->con; + struct monitor *m = container_get_monitor(con); + if (m != selected_monitor) return; - struct xdg_popup *popup = create_popup(con->m, xdg_popup, con->geom, con); - wlr_list_insert(&server.popups, 0, popup); + struct xdg_popup *popup = create_popup(m, xdg_popup, con->geom, con); + g_ptr_array_insert(server.popups, 0, popup); } static void popup_handle_new_subpopup(struct wl_listener *listener, void *data) @@ -122,21 +123,21 @@ static void popup_handle_new_subpopup(struct wl_listener *listener, void *data) struct xdg_popup *popup = create_popup(parent_popup->m, xdg_popup, parent_popup->geom, parent_popup->toplevel); - wlr_list_insert(&server.popups, 0, popup); + g_ptr_array_insert(server.popups, 0, popup); } void popup_handle_destroy(struct wl_listener *listener, void *data) { struct xdg_popup *popup = wl_container_of(listener, popup, destroy); - wlr_list_remove(&server.popups, cmp_ptr, popup); + list_remove(server.popups, cmp_ptr, popup); destroy_popup(popup); } -struct wlr_surface *get_popup_surface_under_cursor(double *sx, double *sy) +struct wlr_surface *get_popup_surface_under_cursor(struct cursor *cursor, double *sx, double *sy) { - int cursorx = server.cursor.wlr_cursor->x; - int cursory = server.cursor.wlr_cursor->y; + int cursorx = cursor->wlr_cursor->x; + int cursory = cursor->wlr_cursor->y; if (!popups_exist()) return NULL; @@ -155,15 +156,15 @@ struct wlr_surface *get_popup_surface_under_cursor(double *sx, double *sy) con->client->surface.xdg, /* absolute mouse position to relative in regards to * the client */ - absolute_x_to_container_relative(con, cursorx), - absolute_y_to_container_relative(con, cursory), + absolute_x_to_container_relative(con->geom, cursorx), + absolute_y_to_container_relative(con->geom, cursory), sx, sy); break; case LAYER_SHELL: surface = wlr_layer_surface_v1_surface_at( con->client->surface.layer, - absolute_x_to_container_relative(con, cursorx), - absolute_y_to_container_relative(con, cursory), + absolute_x_to_container_relative(con->geom, cursorx), + absolute_y_to_container_relative(con->geom, cursory), sx, sy); break; default: @@ -187,12 +188,12 @@ inline struct xdg_popup *get_latest_popup() if (!popups_exist()) return NULL; - struct xdg_popup *popup = server.popups.items[0]; + struct xdg_popup *popup = g_ptr_array_index(server.popups, 0); return popup; } inline bool popups_exist() { - return server.popups.length > 0; + return server.popups->len > 0; } diff --git a/src/render/render.c b/src/render/render.c index 215fb97c..06111c39 100644 --- a/src/render/render.c +++ b/src/render/render.c @@ -16,6 +16,7 @@ #include "server.h" #include "tile/tileUtils.h" #include "utils/gapUtils.h" +#include "layer_shell.h" struct wlr_renderer *drw; struct render_data render_data; @@ -222,7 +223,7 @@ static void scissor_output(struct wlr_output *output, pixman_box32_t *rect) // TODO refactor the name it doesn't represent what this does perfectly static enum wlr_edges get_hidden_edges(struct container *con, struct wlr_box *borders, enum wlr_edges hidden_edges) { - struct monitor *m = con->m; + struct monitor *m = container_get_monitor(con); enum wlr_edges containers_hidden_edges = WLR_EDGE_NONE; // hide edges if needed @@ -254,7 +255,8 @@ static enum wlr_edges get_hidden_edges(struct container *con, struct wlr_box *bo static void render_borders(struct container *con, struct monitor *m, pixman_region32_t *output_damage) { - struct container *sel = get_focused_container(con->m); + struct monitor *container_monitor = container_get_monitor(con); + struct container *sel = get_focused_container(container_monitor); if (con->has_border) { double ox, oy; @@ -273,10 +275,10 @@ static void render_borders(struct container *con, struct monitor *m, pixman_regi }; enum wlr_edges hidden_edges = WLR_EDGE_NONE; - struct workspace *ws = monitor_get_active_workspace(m); - struct layout *lt = ws->layout; + struct tagset *tagset = monitor_get_active_tagset(m); + struct layout *lt = tagset_get_layout(tagset); if (lt->options.smart_hidden_edges) { - if (ws->tiled_containers.length <= 1) { + if (tagset->list_set->tiled_containers->len <= 1) { hidden_edges = get_hidden_edges(con, borders, lt->options.hidden_edges); } } else { @@ -298,9 +300,9 @@ static void render_containers(struct monitor *m, pixman_region32_t *output_damag { /* Each subsequent window we render is rendered on top of the last. Because * our stacking list is ordered front-to-back, we iterate over it backwards. */ - for (int i = length_of_composed_list(&server.normal_visual_stack_lists); i >= 0; i--) { - struct container *con = get_in_composed_list(&server.normal_visual_stack_lists, i); - if (!visible_on(con, get_workspace(m->ws_id))) + for (int i = length_of_composed_list(server.normal_visual_stack_lists)-1; i >= 0; i--) { + struct container *con = get_in_composed_list(server.normal_visual_stack_lists, i); + if (!visible_on(monitor_get_active_tagset(m), con)) continue; render_borders(con, m, output_damage); @@ -321,20 +323,15 @@ static void render_layershell(struct monitor *m, enum zwlr_layer_shell_v1_layer { /* Each subsequent window we render is rendered on top of the last. Because * our stacking list is ordered front-to-back, we iterate over it backwards. */ - for (int i = 0; i < length_of_composed_list(&server.layer_visual_stack_lists); i++) { - struct container *con = get_in_composed_list(&server.layer_visual_stack_lists, i); + GPtrArray *layer_list = get_layer_list(m, layer); + for (int i = 0; i < layer_list->len; i++) { + struct container *con = g_ptr_array_index(layer_list, i); - if (con->client->type != LAYER_SHELL) - continue; - if (con->client->surface.layer->current.layer != layer) - continue; - if (!visible_on(con, get_workspace(m->ws_id))) + if (!visible_on(monitor_get_active_tagset(m), con)) continue; struct wlr_surface *surface = get_wlrsurface(con->client); - con->geom.width = surface->current.width; - con->geom.height = surface->current.height; - render_surface_iterator(m, surface, con->geom, output_damage, con->alpha); + render_surface_iterator(m, surface, con->geom, output_damage, 1.0); struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); @@ -344,9 +341,9 @@ static void render_layershell(struct monitor *m, enum zwlr_layer_shell_v1_layer static void render_independents(struct monitor *m, pixman_region32_t *output_damage) { - struct workspace *ws = monitor_get_active_workspace(m); - for (int i = 0; i < ws->independent_containers.length; i++) { - struct container *con = ws->independent_containers.items[i]; + struct tagset *tagset = monitor_get_active_tagset(m); + for (int i = 0; i < tagset->list_set->independent_containers->len; i++) { + struct container *con = g_ptr_array_index(tagset->list_set->independent_containers, i); struct wlr_surface *surface = get_wlrsurface(con->client); con->geom.width = surface->current.width; @@ -361,8 +358,8 @@ static void render_independents(struct monitor *m, pixman_region32_t *output_dam static void render_popups(struct monitor *m, pixman_region32_t *output_damage) { - for (int i = 0; i < server.popups.length; i++) { - struct xdg_popup *popup = server.popups.items[i]; + for (int i = 0; i < server.popups->len; i++) { + struct xdg_popup *popup = g_ptr_array_index(server.popups, i); struct wlr_surface *surface = popup->xdg->base->surface; render_surface_iterator(m, surface, popup->geom, output_damage, 1.0f); @@ -401,7 +398,6 @@ void render_monitor(struct monitor *m, pixman_region32_t *damage) render_layershell(m, ZWLR_LAYER_SHELL_V1_LAYER_TOP, damage); render_layershell(m, ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, damage); - wlr_list_for_each(&render_data.textures, (void*)render_texture); render_popups(m, damage); /* Hardware cursors are rendered by the GPU on a separate plane, and can be diff --git a/src/root.c b/src/root.c index c6739fff..d085366f 100644 --- a/src/root.c +++ b/src/root.c @@ -1,12 +1,14 @@ #include "root.h" #include +#include #include "client.h" #include "container.h" #include "server.h" #include "utils/coreUtils.h" #include "tile/tileUtils.h" +#include "layer_shell.h" struct anchor { uint32_t singular_anchor; @@ -77,16 +79,14 @@ static struct wlr_box fit_root_area(struct root *root) struct wlr_box d_box = root->m->geom; struct wlr_box box = root->geom; - for (int i = 0; i < length_of_composed_list(&server.layer_visual_stack_lists); i++) { - struct container *con = - get_in_composed_list(&server.layer_visual_stack_lists, i); + for (int i = 0; i < length_of_composed_list(server.layer_visual_stack_lists); i++) { + struct container *con = get_in_composed_list(server.layer_visual_stack_lists, i); - struct workspace *ws = get_workspace(root->m->ws_id); - if (!exist_on(con, ws)) - continue; + /* struct tagset *tagset = monitor_get_active_tagset(root->m); */ + /* if (!exist_on(tagset, con)) */ + /* continue; */ - struct client *c = con->client; - struct wlr_layer_surface_v1_state *current = &c->surface.layer->current; + struct wlr_layer_surface_v1_state *current = &con->client->surface.layer->current; int anchor = current->anchor; // desired_width and desired_height are == 0 if nothing is desired @@ -118,26 +118,26 @@ static struct wlr_box fit_root_area(struct root *root) return box; } -static void configure_layer_shell_container_geom(struct container *con, struct wlr_box ref) -{ - if (!con->client) - return; - if (con->client->type != LAYER_SHELL) - return; +/* static void configure_layer_shell_container_geom(struct container *con, struct wlr_box ref) */ +/* { */ +/* if (!con->client) */ +/* return; */ +/* if (con->client->type != LAYER_SHELL) */ +/* return; */ - struct monitor *m = con->m; - int desired_width = con->client->surface.layer->current.desired_width; - int desired_height = con->client->surface.layer->current.desired_height; +/* struct monitor *m = container_get_monitor(con); */ +/* int desired_width = con->client->surface.layer->current.desired_width; */ +/* int desired_height = con->client->surface.layer->current.desired_height; */ - struct wlr_box geom = { - .x = ref.x + m->geom.x, - .y = ref.y + m->geom.y, - .width = desired_width != 0 ? desired_width : m->geom.width, - .height = desired_height != 0 ? desired_height : m->geom.height, - }; +/* struct wlr_box geom = { */ +/* .x = ref.x + m->geom.x, */ +/* .y = ref.y + m->geom.y, */ +/* .width = desired_width != 0 ? desired_width : m->geom.width, */ +/* .height = desired_height != 0 ? desired_height : m->geom.height, */ +/* }; */ - resize(con, geom); -} +/* resize(con, geom); */ +/* } */ void set_root_color(struct root *root, float color[static 4]) { @@ -151,47 +151,6 @@ void set_root_geom(struct root *root, struct wlr_box geom) if (root->consider_layer_shell) { root->geom = fit_root_area(root); } - - // arrange layer stack based programs - for (int i = 0; i < length_of_composed_list(&server.layer_visual_stack_lists); i++) { - struct container *con = - get_in_composed_list(&server.layer_visual_stack_lists, i); - - struct workspace *ws = get_workspace(root->m->ws_id); - if (!exist_on(con, ws)) - continue; - - struct monitor *m = root->m; - struct client *c = con->client; - struct wlr_layer_surface_v1_state *current = &c->surface.layer->current; - int anchor = current->anchor; - - struct wlr_box geom = root->geom; - geom.x = 0; - geom.y = 0; - - if (equals_anchor(&anchors.top, anchor)) - geom.y = 0; - if (equals_anchor(&anchors.left, anchor)) - geom.x = 0; - if (equals_anchor(&anchors.bottom, anchor)) - /* geom.y = m->geom.height - con->geom.height; */ - geom.y = m->geom.height; - if (equals_anchor(&anchors.right, anchor)) - /* geom.x = m->geom.width - con->geom.width; */ - geom.x = m->geom.width; - - configure_layer_shell_container_geom(con, geom); - - if (con->client->surface.layer->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND) { - continue; - } - if (con->floating) { - continue; - } - - con->hidden = !root->consider_layer_shell; - } } void root_damage_whole(struct root *root) @@ -206,6 +165,14 @@ void root_damage_whole(struct root *root) void set_bars_visible(struct monitor *m, bool visible) { m->root->consider_layer_shell = visible; + for (int i = 0; i < length_of_composed_list(server.layer_visual_stack_lists); i++) { + struct container *con = get_in_composed_list(server.layer_visual_stack_lists, i); + + if (!container_is_bar(con)) + continue; + + con->hidden = !visible; + } wlr_output_damage_add_whole(m->damage); } diff --git a/src/rules/mon_rule.c b/src/rules/mon_rule.c new file mode 100644 index 00000000..1de9f884 --- /dev/null +++ b/src/rules/mon_rule.c @@ -0,0 +1,41 @@ +#include "rules/mon_rule.h" + +#include +#include + +#include "utils/parseConfigUtils.h" + +struct mon_rule *create_mon_rule(const char *output_name, int lua_func_ref) +{ + struct mon_rule *mon_rule = calloc(1, sizeof(struct mon_rule)); + mon_rule->output_name = strdup(output_name); + mon_rule->lua_func_ref = lua_func_ref; + return mon_rule; +} + +void destroy_mon_rule(struct mon_rule *mon_rule) +{ + free(mon_rule->output_name); + free(mon_rule); +} + +void apply_mon_rule(struct mon_rule *mon_rule, struct monitor *m) +{ + if (mon_rule->lua_func_ref <= 0) + return; + + if (strstr(m->wlr_output->name, mon_rule->output_name)) { + if (mon_rule->lua_func_ref <= 0) + return; + lua_rawgeti(L, LUA_REGISTRYINDEX, mon_rule->lua_func_ref); + lua_call_safe(L, 0, 0, 0); + } +} + +void apply_mon_rules(GPtrArray *mon_rules, struct monitor *m) +{ + for (int i = 0; i < mon_rules->len; i++) { + struct mon_rule *mon_rule = g_ptr_array_index(mon_rules, i); + apply_mon_rule(mon_rule, m); + } +} diff --git a/src/rules/rule.c b/src/rules/rule.c new file mode 100644 index 00000000..1f664243 --- /dev/null +++ b/src/rules/rule.c @@ -0,0 +1,50 @@ +#include "rules/rule.h" + +#include +#include + +#include "server.h" +#include "workspace.h" +#include "utils/parseConfigUtils.h" + +struct rule *create_rule(const char *id, const char *title, int lua_func_ref) +{ + struct rule *rule = calloc(1, sizeof(struct rule)); + rule->id = strdup(id); + rule->title = strdup(title); + rule->lua_func_ref = lua_func_ref; + return rule; +} + +void destroy_rule(struct rule *rule) +{ + free(rule->id); + free(rule->title); + free(rule); +} + +void apply_rule(struct rule *rule, struct container *con) +{ + if (rule->lua_func_ref <= 0) + return; + + bool same_id = g_strcmp0(rule->id, con->client->app_id) == 0; + bool id_empty = g_strcmp0(rule->id, "") == 0; + bool same_title = g_strcmp0(rule->title, con->client->title) == 0; + bool title_empty = g_strcmp0(rule->title, "") == 0; + if ((same_id || id_empty) && (same_title || title_empty)) { + lua_rawgeti(L, LUA_REGISTRYINDEX, rule->lua_func_ref); + int position = get_position_in_container_stack(con); + lua_pushinteger(L, position); + lua_call_safe(L, 1, 0, 0); + } +} + +void apply_rules(GPtrArray *rules, struct container *con) +{ + for (int i = 0; i < rules->len; i++) { + struct rule *rule = g_ptr_array_index(rules, i); + apply_rule(rule, con); + } +} + diff --git a/src/scratchpad.c b/src/scratchpad.c index 8110bf5c..ab1791fd 100644 --- a/src/scratchpad.c +++ b/src/scratchpad.c @@ -3,75 +3,93 @@ #include "monitor.h" #include "server.h" #include "tile/tileUtils.h" +#include "workspace.h" // TODO rewrite this function so it is easier to read void move_to_scratchpad(struct container *con, int position) { if (!con) return; + if (con->on_scratchpad) { + remove_container_from_scratchpad(con); + } - struct monitor *m = con->m; - struct workspace *ws = monitor_get_active_workspace(m); + struct monitor *m = container_get_monitor(con); + struct tagset *tagset = monitor_get_active_tagset(m); con->on_scratchpad = true; - set_container_floating(con, fix_position, true); + set_container_floating(con, container_fix_position, true); - if (server.scratchpad.length == 0) { - wlr_list_push(&server.scratchpad, con); + if (server.scratchpad->len== 0) { + g_ptr_array_add(server.scratchpad, con); } else { int new_position = relative_index_to_absolute_index(0, - position, server.scratchpad.length+1); - wlr_list_insert(&server.scratchpad, new_position, con); + position, server.scratchpad->len+1); + g_ptr_array_insert(server.scratchpad, new_position, con); } - remove_in_composed_list(&ws->container_lists, cmp_ptr, con); - wlr_list_remove(&ws->focus_stack_normal, cmp_ptr, con); - remove_in_composed_list(&server.visual_stack_lists, cmp_ptr, con); + remove_in_composed_list(tagset->list_set->container_lists, cmp_ptr, con); + list_remove(tagset->list_set->focus_stack_normal, cmp_ptr, con); + remove_in_composed_list(server.visual_stack_lists, cmp_ptr, con); container_damage_whole(con); - focus_most_recent_container(get_workspace(m->ws_id), FOCUS_NOOP); + focus_most_recent_container(tagset); con->hidden = true; arrange(); } void remove_container_from_scratchpad(struct container *con) { - wlr_list_remove(&server.scratchpad, cmp_ptr, con); + list_remove(server.scratchpad, cmp_ptr, con); con->on_scratchpad = false; } -void show_scratchpad() +static void hide_container(struct container *con) { - if (server.scratchpad.length <= 0) + struct monitor *m = container_get_monitor(con); + struct container *sel = get_focused_container(m); + + if (!sel->on_scratchpad) { + focus_container(con); + lift_container(con); return; + } + + struct workspace *ws = get_workspace(con->client->ws_id); + workspace_remove_container(ws, con); + workspace_remove_container_from_focus_stack(ws, con); + + list_remove(server.scratchpad, cmp_ptr, con); + move_to_scratchpad(con, -1); + con->hidden = true; + arrange(); +} + +static void show_container(struct container *con) +{ + struct monitor *m = container_get_monitor(con); - struct monitor *m = selected_monitor; struct workspace *ws = monitor_get_active_workspace(m); - struct container *sel = get_focused_container(m); - struct container *con = server.scratchpad.items[0]; + workspace_add_container_to_containers(ws, con, 0); + workspace_add_container_to_focus_stack(ws, con); - if (con->hidden) { - wlr_list_push(&ws->floating_containers, con); - wlr_list_insert(&ws->focus_stack_normal, 0, con); - wlr_list_insert(&server.floating_visual_stack, 0, con); + resize(con, get_center_box(m->geom)); - resize(con, get_center_box(m->geom)); + con->hidden = false; + focus_container(con); + lift_container(con); + arrange(); +} - con->hidden = false; - focus_container(con, FOCUS_LIFT); +void show_scratchpad() +{ + if (server.scratchpad->len <= 0) + return; + + struct container *con = g_ptr_array_index(server.scratchpad, 0); + if (con->hidden) { + show_container(con); } else { - if (!sel->on_scratchpad) { - focus_container(con, FOCUS_LIFT); - return; - } - - wlr_list_remove(&ws->floating_containers, cmp_ptr, con); - wlr_list_remove(&ws->focus_stack_normal, cmp_ptr, con); - wlr_list_remove(&server.floating_visual_stack, cmp_ptr, con); - - wlr_list_remove(&server.scratchpad, cmp_ptr, con); - move_to_scratchpad(con, -1); - con->hidden = true; + hide_container(con); } - arrange(); } diff --git a/src/seat.c b/src/seat.c new file mode 100644 index 00000000..b2dee677 --- /dev/null +++ b/src/seat.c @@ -0,0 +1,289 @@ +#include "seat.h" + +#include +#include +#include +#include +#include +#include + +#include "monitor.h" +#include "container.h" +#include "server.h" +#include "utils/coreUtils.h" +#include "keyboard.h" + +static void handle_set_selection(struct wl_listener *listener, void *data) +{ + struct seat *seat = wl_container_of(listener, seat, request_set_selection); + struct wlr_seat_request_set_selection_event *event = data; + wlr_seat_set_selection(seat->wlr_seat, event->source, event->serial); +} + +void handle_set_primary_selection(struct wl_listener *listener, void *data) +{ + struct seat *seat = wl_container_of(listener, seat, request_set_primary_selection); + struct wlr_seat_request_set_primary_selection_event *event = data; + wlr_seat_set_primary_selection(seat->wlr_seat, event->source, event->serial); +} + +struct seat *create_seat(const char *seat_name) +{ + struct seat *seat = calloc(1, sizeof(struct seat)); + + seat->wlr_seat = wlr_seat_create(server.wl_display, seat_name); + seat->wlr_seat->data = seat; + + g_ptr_array_add(server.input_manager->seats, seat); + + seat->cursor = create_cursor(seat); + + LISTEN(&seat->wlr_seat->events.request_set_selection, + &seat->request_set_selection, + handle_set_selection); + LISTEN(&seat->wlr_seat->events.request_set_primary_selection, + &seat->request_set_primary_selection, + handle_set_primary_selection); + + seat->devices = g_ptr_array_new(); + + return seat; +} + +void destroy_seat(struct seat *seat) +{ + g_ptr_array_remove(server.input_manager->seats, seat); + + g_ptr_array_free(seat->devices, false); + + free(seat); +} + +static struct seat_device *seat_get_device(struct seat *seat, + struct input_device *input_device) { + + for (int i = 0; i < seat->devices->len; i++) { + struct seat_device *seat_device = g_ptr_array_index(seat->devices, i); + if (seat_device->input_device == input_device) { + return seat_device; + } + } + + return NULL; +} + +static void seat_device_destroy(struct seat_device *seat_device) { + if (!seat_device) { + return; + } + + destroy_keyboard(seat_device->keyboard); + wlr_cursor_detach_input_device(seat_device->seat->cursor->wlr_cursor, + seat_device->input_device->wlr_device); + g_ptr_array_remove(seat_device->seat->devices, seat_device); + free(seat_device); +} + +static void seat_update_capabilities(struct seat *seat) { + uint32_t caps = 0; + uint32_t previous_caps = seat->wlr_seat->capabilities; + for (int i = 0; i < seat->devices->len; i++) { + struct seat_device *seat_device = g_ptr_array_index(seat->devices, i); + + switch (seat_device->input_device->wlr_device->type) { + case WLR_INPUT_DEVICE_KEYBOARD: + caps |= WL_SEAT_CAPABILITY_KEYBOARD; + break; + case WLR_INPUT_DEVICE_POINTER: + caps |= WL_SEAT_CAPABILITY_POINTER; + break; + case WLR_INPUT_DEVICE_TOUCH: + caps |= WL_SEAT_CAPABILITY_TOUCH; + break; + case WLR_INPUT_DEVICE_TABLET_TOOL: + caps |= WL_SEAT_CAPABILITY_POINTER; + break; + case WLR_INPUT_DEVICE_SWITCH: + case WLR_INPUT_DEVICE_TABLET_PAD: + break; + } + } + + // Hide cursor if seat doesn't have pointer capability. + // We must call cursor_set_image while the wlr_seat has the capabilities + // otherwise it's a no op. + if ((caps & WL_SEAT_CAPABILITY_POINTER) == 0) { + cursor_set_image(seat->cursor, NULL, NULL); + wlr_seat_set_capabilities(seat->wlr_seat, caps); + } else { + wlr_seat_set_capabilities(seat->wlr_seat, caps); + if ((previous_caps & WL_SEAT_CAPABILITY_POINTER) == 0) { + cursor_set_image(seat->cursor, "left_ptr", NULL); + } + } +} + + +void seat_remove_device(struct seat *seat, struct input_device *input_device) +{ + struct seat_device *seat_device = seat_get_device(seat, input_device); + + if (!seat_device) { + return; + } + + seat_device_destroy(seat_device); + + seat_update_capabilities(seat); +} + +static bool xcursor_manager_is_named(const struct wlr_xcursor_manager *manager, + const char *name) { + return (!manager->name && !name) || + (name && manager->name && strcmp(name, manager->name) == 0); +} + +void seat_configure_xcursor(struct seat *seat) { + unsigned cursor_size = 24; + const char *cursor_theme = NULL; + + if (seat == input_manager_get_default_seat()) { + char cursor_size_fmt[16]; + snprintf(cursor_size_fmt, sizeof(cursor_size_fmt), "%u", cursor_size); + setenv("XCURSOR_SIZE", cursor_size_fmt, 1); + if (cursor_theme != NULL) { + setenv("XCURSOR_THEME", cursor_theme, 1); + } + +#if HAVE_XWAYLAND + if (server.xwayland.wlr_xwayland && (!server.xwayland.xcursor_manager || + !xcursor_manager_is_named(server.xwayland.xcursor_manager, + cursor_theme) || + server.xwayland.xcursor_manager->size != cursor_size)) { + + wlr_xcursor_manager_destroy(server.xwayland.xcursor_manager); + + server.xwayland.xcursor_manager = + wlr_xcursor_manager_create(cursor_theme, cursor_size); + sway_assert(server.xwayland.xcursor_manager, + "Cannot create XCursor manager for theme"); + + wlr_xcursor_manager_load(server.xwayland.xcursor_manager, 1); + struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor( + server.xwayland.xcursor_manager, "left_ptr", 1); + if (xcursor != NULL) { + struct wlr_xcursor_image *image = xcursor->images[0]; + wlr_xwayland_set_cursor( + server.xwayland.wlr_xwayland, image->buffer, + image->width * 4, image->width, image->height, + image->hotspot_x, image->hotspot_y); + } + } +#endif + } + + /* Create xcursor manager if we don't have one already, or if the + * theme has changed */ + if (!seat->cursor->xcursor_mgr || + !xcursor_manager_is_named( + seat->cursor->xcursor_mgr, cursor_theme) || + seat->cursor->xcursor_mgr->size != cursor_size) { + + wlr_xcursor_manager_destroy(seat->cursor->xcursor_mgr); + seat->cursor->xcursor_mgr = + wlr_xcursor_manager_create(cursor_theme, cursor_size); + if (!seat->cursor->xcursor_mgr) { + printf("Cannot create XCursor manager for theme '%s'\n", cursor_theme); + } + } + +/* for (int i = 0; i < root->outputs->length; ++i) { */ +/* struct sway_output *sway_output = root->outputs->items[i]; */ +/* struct wlr_output *output = sway_output->wlr_output; */ +/* bool result = */ +/* wlr_xcursor_manager_load(seat->cursor->xcursor_manager, */ +/* output->scale); */ +/* if (!result) { */ +/* sway_log(SWAY_ERROR, */ +/* "Cannot load xcursor theme for output '%s' with scale %f", */ +/* output->name, output->scale); */ +/* } */ +/* } */ + + // Reset the cursor so that we apply it to outputs that just appeared + cursor_set_image(seat->cursor, NULL, NULL); + cursor_set_image(seat->cursor, "left_ptr", NULL); + wlr_cursor_warp(seat->cursor->wlr_cursor, NULL, seat->cursor->wlr_cursor->x, + seat->cursor->wlr_cursor->y); +} + + +static void seat_configure_pointer(struct seat *seat, + struct seat_device *seat_device) { + create_pointer(seat, seat_device); + if ((seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER) == 0) { + seat_configure_xcursor(seat); + } + /* wlr_cursor_attach_input_device(seat->cursor->wlr_cursor, */ + /* seat_device->input_device->wlr_device); */ + /* seat_apply_input_config(seat, seat_device); */ + /* wl_event_source_timer_update( */ + /* seat->cursor->hide_source, cursor_get_timeout(seat->cursor)); */ +} + +static void seat_configure_keyboard(struct seat *seat, + struct seat_device *seat_device) { + if (!seat_device->keyboard) { + create_keyboard(seat, seat_device); + } + /* sway_keyboard_configure(seat_device->keyboard); */ + wlr_seat_set_keyboard(seat->wlr_seat, seat_device->input_device->wlr_device); + /* struct sway_node *focus = seat_get_focus(seat); */ + /* if (focus && node_is_view(focus)) { */ + /* // force notify reenter to pick up the new configuration */ + /* wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat); */ + /* seat_keyboard_notify_enter(seat, focus->sway_container->view->surface); */ + /* } */ +} + +void seat_configure_device(struct seat *seat, + struct input_device *input_device) { + struct seat_device *seat_device = seat_get_device(seat, input_device); + if (!seat_device) { + return; + } + + switch (input_device->wlr_device->type) { + case WLR_INPUT_DEVICE_POINTER: + seat_configure_pointer(seat, seat_device); + break; + case WLR_INPUT_DEVICE_KEYBOARD: + seat_configure_keyboard(seat, seat_device); + break; + default: + break; + } +} + +void seat_add_device(struct seat *seat, struct input_device *input_device) { + if (seat_get_device(seat, input_device)) { + seat_configure_device(seat, input_device); + return; + } + + struct seat_device *seat_device = + calloc(1, sizeof(struct seat_device)); + if (!seat_device) { + printf("could not allocate seat device"); + return; + } + + seat_device->seat = seat; + seat_device->input_device = input_device; + g_ptr_array_add(seat->devices, seat_device); + + seat_configure_device(seat, input_device); + + seat_update_capabilities(seat); +} + diff --git a/src/server.c b/src/server.c index 5657fc6e..46498914 100644 --- a/src/server.c +++ b/src/server.c @@ -1,56 +1,87 @@ #include "server.h" + +#include "layer_shell.h" +#include "monitor.h" #include "utils/coreUtils.h" +#include "utils/parseConfigUtils.h" +#include "xdg_shell.h" struct server server; +static void init_lists(struct server *m); + +static void init_lists(struct server *server) +{ + server->visual_stack_lists = g_ptr_array_new(); + server->normal_visual_stack_lists = g_ptr_array_new(); + server->layer_visual_stack_lists = g_ptr_array_new(); + + server->tiled_visual_stack = g_ptr_array_new(); + server->floating_visual_stack = g_ptr_array_new(); + server->layer_visual_stack_background = g_ptr_array_new(); + server->layer_visual_stack_bottom = g_ptr_array_new(); + server->layer_visual_stack_top = g_ptr_array_new(); + server->layer_visual_stack_overlay = g_ptr_array_new(); + + g_ptr_array_add(server->visual_stack_lists, server->layer_visual_stack_overlay); + g_ptr_array_add(server->visual_stack_lists, server->layer_visual_stack_top); + g_ptr_array_add(server->visual_stack_lists, server->floating_visual_stack); + g_ptr_array_add(server->visual_stack_lists, server->tiled_visual_stack); + g_ptr_array_add(server->visual_stack_lists, server->layer_visual_stack_bottom); + g_ptr_array_add(server->visual_stack_lists, server->layer_visual_stack_background); + + g_ptr_array_add(server->normal_visual_stack_lists, server->floating_visual_stack); + g_ptr_array_add(server->normal_visual_stack_lists, server->tiled_visual_stack); + + g_ptr_array_add(server->layer_visual_stack_lists, server->layer_visual_stack_overlay); + g_ptr_array_add(server->layer_visual_stack_lists, server->layer_visual_stack_top); + g_ptr_array_add(server->layer_visual_stack_lists, server->layer_visual_stack_bottom); + g_ptr_array_add(server->layer_visual_stack_lists, server->layer_visual_stack_background); +} + +static void init_event_handlers(struct server *server) +{ + server->new_output = (struct wl_listener){.notify = create_monitor}; + server->new_xdeco = (struct wl_listener){.notify = createxdeco}; + server->new_xdg_surface = (struct wl_listener){.notify = create_notify_xdg}; + server->new_layer_shell_surface = (struct wl_listener){.notify = create_notify_layer_shell}; + server->new_pointer_constraint = (struct wl_listener){.notify = handle_new_pointer_constraint}; + +#if JAPOKWM_HAS_XWAYLAND + server->new_xwayland_surface = (struct wl_listener){.notify = create_notifyx11}; +#endif +} + void init_server() { server = (struct server) { - .config_file = "", - .config_dir = "", - .previous_workspace_id = INVALID_POSITION, + .previous_tagset = NULL, }; + init_lists(&server); + init_event_handlers(&server); + wl_list_init(&sticky_stack); - wlr_list_init(&server.mons); - wlr_list_init(&server.popups); - wlr_list_init(&server.visual_stack_lists); - wlr_list_init(&server.normal_visual_stack_lists); - wlr_list_init(&server.layer_visual_stack_lists); - - wlr_list_init(&server.tiled_visual_stack); - wlr_list_init(&server.floating_visual_stack); - wlr_list_init(&server.layer_visual_stack_background); - wlr_list_init(&server.layer_visual_stack_bottom); - wlr_list_init(&server.layer_visual_stack_top); - wlr_list_init(&server.layer_visual_stack_overlay); - - wlr_list_push(&server.visual_stack_lists, &server.layer_visual_stack_overlay); - wlr_list_push(&server.visual_stack_lists, &server.layer_visual_stack_top); - wlr_list_push(&server.visual_stack_lists, &server.floating_visual_stack); - wlr_list_push(&server.visual_stack_lists, &server.tiled_visual_stack); - wlr_list_push(&server.visual_stack_lists, &server.layer_visual_stack_bottom); - wlr_list_push(&server.visual_stack_lists, &server.layer_visual_stack_background); - - wlr_list_push(&server.normal_visual_stack_lists, &server.floating_visual_stack); - wlr_list_push(&server.normal_visual_stack_lists, &server.tiled_visual_stack); - - wlr_list_push(&server.layer_visual_stack_lists, &server.layer_visual_stack_overlay); - wlr_list_push(&server.layer_visual_stack_lists, &server.layer_visual_stack_top); - wlr_list_push(&server.layer_visual_stack_lists, &server.layer_visual_stack_bottom); - wlr_list_push(&server.layer_visual_stack_lists, &server.layer_visual_stack_background); - - wlr_list_init(&server.scratchpad); - wlr_list_init(&server.workspaces); - - wlr_list_init(&server.client_lists); - - wlr_list_init(&server.normal_clients); - wlr_list_init(&server.non_tiled_clients); - wlr_list_init(&server.independent_clients); - - wlr_list_push(&server.client_lists, &server.normal_clients); - wlr_list_push(&server.client_lists, &server.non_tiled_clients); - wlr_list_push(&server.client_lists, &server.independent_clients); + + server.mons = g_ptr_array_new(); + server.popups = g_ptr_array_new(); + server.xwayland_popups = g_ptr_array_new(); + + server.scratchpad = g_ptr_array_new(); + server.keyboards = g_ptr_array_new(); + server.config_paths = create_default_config_paths(); + server.workspaces = g_ptr_array_new(); + + server.client_lists = g_ptr_array_new(); + + server.normal_clients = g_ptr_array_new(); + server.non_tiled_clients = g_ptr_array_new(); + server.independent_clients = g_ptr_array_new(); + + g_ptr_array_add(server.client_lists, server.normal_clients); + g_ptr_array_add(server.client_lists, server.non_tiled_clients); + g_ptr_array_add(server.client_lists, server.independent_clients); + + server.tagsets = g_ptr_array_new(); } diff --git a/src/tagset.c b/src/tagset.c new file mode 100644 index 00000000..b3f361f6 --- /dev/null +++ b/src/tagset.c @@ -0,0 +1,601 @@ +#include "tagset.h" + +#include + +#include "bitset/bitset.h" +#include "list_set.h" +#include "server.h" +#include "workspace.h" +#include "utils/coreUtils.h" +#include "utils/parseConfigUtils.h" +#include "tile/tileUtils.h" +#include "ipc-server.h" +#include "monitor.h" +#include "cursor.h" + +static void tagset_write_to_workspaces(struct tagset *tagset); +static void tagset_assign_workspace(struct tagset *tagset, struct workspace *ws, bool load); +static void tagset_assign_workspaces(struct tagset *tagset, BitSet *workspaces); +static void tagset_set_tag(struct tagset *tagset, struct workspace *ws, bool load); +static void tagset_set_tags(struct tagset *tagset, BitSet *workspaces); +static void tagset_load_workspaces(struct tagset *tagset, BitSet *workspaces); +static void tagset_load_workspace(struct tagset *tagset, struct workspace *ws); +static void tagset_unload_workspaces(struct tagset *tagset); +static void tagset_unload_workspace(struct tagset *tagset, struct workspace *ws); +static void tagset_clear_workspaces(struct tagset *tagset); +static void tagset_workspaces_disconnect(struct tagset *tagset); +static void tagset_workspaces_connect(struct tagset *tagset); + +static void tagset_workspace_connect(struct tagset *tagset, struct workspace *ws); + +static void tagset_subscribe_to_workspace(struct tagset *tagset, struct workspace *ws); + +static void tagset_subscribe_to_workspace(struct tagset *tagset, struct workspace *ws) +{ + g_ptr_array_add(ws->subscribed_tagsets, tagset); + append_list_set(tagset->list_set, ws->list_set); +} + +static void tagset_assign_workspace(struct tagset *tagset, struct workspace *ws, bool load) +{ + if (!tagset) + return; + + bitset_assign(tagset->workspaces, ws->id, load); +} + +static void tagset_assign_workspaces(struct tagset *tagset, BitSet *workspaces) +{ + tagset->workspaces = bitset_copy(workspaces); +} + +static void tagset_set_tag(struct tagset *tagset, struct workspace *ws, bool load) +{ + if (!tagset) + return; + + bitset_assign(tagset->workspaces, ws->id, load); +} + +void tagset_set_tags(struct tagset *tagset, BitSet *bitset) +{ + tagset_write_to_workspaces(tagset); + tagset_workspaces_disconnect(tagset); + tagset_assign_workspaces(tagset, bitset); + tagset_workspaces_connect(tagset); +} + + +// you should use tagset_write_to_workspace to unload workspaces first else +static void tagset_load_workspaces(struct tagset *tagset, BitSet *workspaces) +{ + assert(tagset != NULL); + + for (size_t i = 0; i < tagset->workspaces->size; i++) { + bool bit = bitset_test(tagset->workspaces, i); + + if (!bit) + continue; + + struct workspace *ws = get_workspace(i); + tagset_load_workspace(tagset, ws); + } +} + +static void tagset_clean_destroyed_tagset(struct tagset *tagset) +{ + if (!tagset) + return; + + for (size_t i = 0; i < tagset->workspaces->size; i++) { + struct workspace *ws = get_workspace(i); + if (ws->tagset == tagset) { + ws->tagset = NULL; + } + } +} + +// remove all references from workspace to tagset and bounce a workspace back to +// its original tagset or if already on it remove it from it +static void tagset_workspace_disconnect(struct tagset *tagset, struct workspace *ws) +{ + if (!tagset) + return; + tagset_unload_workspace(tagset, ws); + ws->tagset = NULL; + if (ws->selected_tagset != tagset && ws->selected_tagset) { + // move the workspace back + ws->tagset = ws->selected_tagset; + tagset_load_workspace(ws->tagset, ws); + } +} + +static void tagset_workspaces_disconnect(struct tagset *tagset) +{ + if (!tagset) + return; + + struct workspace *sel_ws = get_workspace(tagset->selected_ws_id); + tagset_workspace_disconnect(sel_ws->tagset, sel_ws); + + for (size_t i = 0; i < tagset->workspaces->size; i++) { + bool bit = bitset_test(tagset->workspaces, i); + + if (!bit) + continue; + + struct workspace *ws = get_workspace(i); + tagset_workspace_disconnect(tagset, ws); + } + + +} + +static void tagset_workspace_connect(struct tagset *tagset, struct workspace *ws) +{ + ws->prev_m = tagset->m; + + tagset_unload_workspace(ws->tagset, ws); + ws->tagset = tagset; + if (tagset->selected_ws_id == ws->id) { + ws->selected_tagset = tagset; + } + tagset_load_workspace(tagset, ws); +} + +static void tagset_workspaces_connect(struct tagset *tagset) +{ + if (!tagset) + return; + + for (size_t i = 0; i < tagset->workspaces->size; i++) { + bool bit = bitset_test(tagset->workspaces, i); + + if (!bit) + continue; + + struct workspace *ws = get_workspace(i); + tagset_workspace_connect(tagset, ws); + } +} + +static void tagset_update_visible_tagset(struct tagset *tagset) +{ + for (size_t i = 0; i < tagset->workspaces->size; i++) { + bool bit = bitset_test(tagset->workspaces, i); + + if (!bit) + continue; + + struct workspace *ws = get_workspace(i); + ws->prev_m = tagset->m; + } +} + +static void tagset_load_workspace(struct tagset *tagset, struct workspace *ws) +{ + assert(tagset != NULL); + assert(ws != NULL); + bool bit = bitset_test(tagset->loaded_workspaces, ws->id); + if (bit) + return; + + bitset_set(tagset->loaded_workspaces, ws->id); + tagset_subscribe_to_workspace(tagset, ws); +} + +static void tagset_unsubscribe_from_workspace(struct tagset *tagset, struct workspace *ws) +{ + g_ptr_array_remove(ws->subscribed_tagsets, tagset); +} + +static void tagset_unload_workspace(struct tagset *tagset, struct workspace *ws) +{ + if (!tagset) + return; + assert(ws != NULL); + + bool bit = bitset_test(tagset->loaded_workspaces, ws->id); + if (!bit) + return; + + bitset_reset(tagset->loaded_workspaces, ws->id); + list_set_remove_list_set(tagset->list_set, ws->list_set); + tagset_unsubscribe_from_workspace(tagset, ws); +} + +static void tagset_unload_workspaces(struct tagset *tagset) +{ + assert(tagset != NULL); + + clear_list_set(tagset->list_set); + + for (int i = 0; i < tagset->loaded_workspaces->size; i++) { + bool bit = bitset_test(tagset->loaded_workspaces, i); + + if (!bit) + continue; + + struct workspace *ws = get_workspace(i); + tagset_unsubscribe_from_workspace(tagset, ws); + bitset_reset(tagset->loaded_workspaces, i); + } +} + +struct tagset *create_tagset(struct monitor *m, int selected_ws_id, BitSet *workspaces) +{ + struct tagset *tagset = g_rc_box_new0(struct tagset); + tagset->m = m; + + tagset->selected_ws_id = selected_ws_id; + + tagset->list_set = create_list_set(); + + tagset->workspaces = bitset_create(server.workspaces->len); + tagset->loaded_workspaces = bitset_create(server.workspaces->len); + tagset_set_tags(tagset, workspaces); + + g_ptr_array_add(server.tagsets, tagset); + return tagset; +} + +static void _destroy_tagset(void *tagset_ptr) +{ + if (!tagset_ptr) + return; + + struct tagset *tagset = (struct tagset *)tagset_ptr; + + struct workspace *selected_workspace = get_workspace(tagset->selected_ws_id); + if (selected_workspace->selected_tagset == tagset) { + selected_workspace->selected_tagset = NULL; + } + + tagset_clean_destroyed_tagset(tagset); + + g_ptr_array_remove(server.tagsets, tagset); + bitset_destroy(tagset->workspaces); + bitset_destroy(tagset->loaded_workspaces); + destroy_list_set(tagset->list_set); +} + +void tagset_acquire(struct tagset *tagset) +{ + if (!tagset) + return; + g_rc_box_acquire(tagset); +} + +void tagset_release(struct tagset *tagset) +{ + if (!tagset) + return; + g_rc_box_release_full(tagset, _destroy_tagset); +} + +void focus_most_recent_container(struct tagset *tagset) +{ + struct container *con = get_in_composed_list(tagset->list_set->focus_stack_lists, 0); + + if (!con) { + con = get_container(tagset, 0); + if (!con) { + ipc_event_window(); + return; + } + } + + focus_container(con); +} + +static void tagset_move_sticky_containers(struct tagset *old_tagset, struct tagset *tagset) +{ + if (!old_tagset) + return; + + struct workspace *ws = get_workspace(tagset->selected_ws_id); + int len = length_of_composed_list(old_tagset->list_set->container_lists); + int pos = len-1; + for (int i = len-1; i >= 0; i--) { + struct container *con = get_in_composed_list(old_tagset->list_set->container_lists, pos); + if (con->client->sticky) { + debug_print("move container: %p\n", con); + move_container_to_workspace(con, ws); + } + pos--; + } +} + +void focus_tagset_no_ref(struct tagset *tagset) +{ + if(!tagset) + return; + + struct monitor *m = tagset->m; + + focus_monitor(m); + selected_monitor = m; + + struct tagset *old_tagset = m->tagset; + + tagset_write_to_workspaces(m->tagset); + tagset_move_sticky_containers(old_tagset, tagset); + tagset_workspaces_disconnect(old_tagset); + tagset_workspaces_connect(tagset); + tagset_release(m->tagset); + m->tagset = tagset; + ipc_event_workspace(); + + arrange(); + focus_most_recent_container(tagset); + root_damage_whole(m->root); + + struct seat *seat = input_manager_get_default_seat(); + cursor_rebase(seat->cursor); +} + +void focus_tagset(struct tagset *tagset) +{ + if(!tagset) + return; + + tagset_acquire(tagset); + focus_tagset_no_ref(tagset); +} + +static void tagset_save_to_workspace(struct tagset *tagset, struct workspace *ws) +{ + for (int i = 0; i < ws->list_set->all_lists->len; i++) { + GPtrArray *dest_list = g_ptr_array_index(ws->list_set->all_lists, i); + GPtrArray *src_list = g_ptr_array_index(tagset->list_set->all_lists, i); + for (int j = 0; j < src_list->len; j++) { + struct container *con = g_ptr_array_index(src_list, j); + if (con->client->ws_id != ws->id) + continue; + g_ptr_array_add(dest_list, con); + } + } +} + +static void tagset_clear_workspaces(struct tagset *tagset) +{ + for (int i = 0; i < tagset->loaded_workspaces->size; i++) { + bool bit = bitset_test(tagset->loaded_workspaces, i); + + if (!bit) + continue; + + struct workspace *ws = get_workspace(i); + clear_list_set(ws->list_set); + } +} + +static void tagset_append_to_workspaces(struct tagset *tagset) +{ + for (int i = 0; i < tagset->list_set->all_lists->len; i++) { + GPtrArray *tagset_list = g_ptr_array_index(tagset->list_set->all_lists, i); + for (int j = 0; j < tagset_list->len; j++) { + struct container *con = g_ptr_array_index(tagset_list, j); + struct workspace *ws = get_workspace(con->client->ws_id); + GPtrArray *ws_list = g_ptr_array_index(ws->list_set->all_lists, i); + + g_ptr_array_add(ws_list, con); + } + } +} + +static void tagset_write_to_workspaces(struct tagset *tagset) +{ + if (!tagset) + return; + + tagset_clear_workspaces(tagset); + tagset_append_to_workspaces(tagset); +} + +struct layout *tagset_get_layout(struct tagset *tagset) +{ + struct workspace *ws = get_workspace(tagset->selected_ws_id); + return ws->layout; +} + +static void _set_previous_tagset(struct tagset *tagset) +{ + tagset_acquire(tagset); + tagset_release(server.previous_tagset); + server.previous_tagset = tagset; +} + +void push_tagset_no_ref(struct tagset *tagset) +{ + struct monitor *m = selected_monitor; + + if (m->tagset != tagset) { + _set_previous_tagset(m->tagset); + } + + focus_tagset_no_ref(tagset); +} + +void push_tagset(struct tagset *tagset) +{ + if (!tagset) + return; + tagset_acquire(tagset); + + push_tagset_no_ref(tagset); +} + +static void handle_too_few_workspaces(uint32_t ws_id) +{ + // no number has more than 11 digits when int is 32 bit long + char name[12]; + // TODO explain why +1 + sprintf(name, "%d:%d", server.workspaces->len, server.workspaces->len+1); + struct workspace *ws = create_workspace(name, server.workspaces->len, server.default_layout); + g_ptr_array_add(server.workspaces, ws); + for (int i = 0; i < server.tagsets->len; i++) { + struct tagset *tagset = g_ptr_array_index(server.tagsets, i); + bitset_push(tagset->workspaces, 0); + bitset_push(tagset->loaded_workspaces, 0); + } +} + +void tagset_focus_workspace(int ws_id) +{ + if (ws_id >= server.workspaces->len) { + handle_too_few_workspaces(ws_id); + } + + BitSet *bitset = bitset_create(server.workspaces->len); + bitset_set(bitset, ws_id); + tagset_focus_tags(ws_id, bitset); +} + +void tagset_toggle_add(struct tagset *tagset, BitSet *bitset) +{ + if (!tagset) + return; + + BitSet *new_bitset = bitset_copy(bitset); + bitset_xor(new_bitset, tagset->workspaces); + + tagset_set_tags(tagset, new_bitset); + ipc_event_workspace(); +} + +void tagset_focus_tags(int ws_id, struct BitSet *bitset) +{ + struct workspace *ws = get_workspace(ws_id); + struct monitor *ws_m = workspace_get_monitor(ws); + struct monitor *m = ws_m ? ws_m : selected_monitor; + + struct tagset *tagset = workspace_get_selected_tagset(ws); + if (tagset) { + tagset_set_tags(tagset, bitset); + push_tagset(tagset); + } else { + tagset = create_tagset(m, ws_id, bitset); + push_tagset_no_ref(tagset); + } +} + +struct container *get_container(struct tagset *tagset, int i) +{ + struct list_set *list_set = tagset->list_set; + if (!list_set) + return NULL; + + return get_in_composed_list(list_set->container_lists, i); +} + +static bool container_intersects_with_monitor(struct container *con, struct monitor *m) +{ + if (!con) + return false; + if (!m) + return false; + + struct wlr_box tmp_geom; + return wlr_box_intersection(&tmp_geom, &con->geom, &m->geom); +} + +GPtrArray *tagset_get_visible_lists(struct tagset *tagset) +{ + struct layout *lt = tagset_get_layout(tagset); + + if (lt->options.arrange_by_focus) + return tagset->list_set->focus_stack_visible_lists; + else + return tagset->list_set->visible_container_lists; +} + +GPtrArray *tagset_get_tiled_list(struct tagset *tagset) +{ + struct layout *lt = tagset_get_layout(tagset); + + if (lt->options.arrange_by_focus) + return tagset->list_set->focus_stack_normal; + else + return tagset->list_set->tiled_containers; +} + +GPtrArray *tagset_get_floating_list(struct tagset *tagset) +{ + struct layout *lt = tagset_get_layout(tagset); + + if (lt->options.arrange_by_focus) + return tagset->list_set->focus_stack_normal; + else + return tagset->list_set->floating_containers; +} + +GPtrArray *tagset_get_hidden_list(struct tagset *tagset) +{ + struct layout *lt = tagset_get_layout(tagset); + + if (lt->options.arrange_by_focus) + return tagset->list_set->focus_stack_hidden; + else + return tagset->list_set->hidden_containers; +} + +void workspace_id_to_tag(BitSet *dest, int ws_id) +{ + bitset_set(dest, ws_id); +} + +bool tagset_contains_client(struct tagset *tagset, struct client *c) +{ + BitSet *bitset = bitset_create(server.workspaces->len); + workspace_id_to_tag(bitset, c->ws_id); + bitset_and(bitset, tagset->workspaces); + bool contains = bitset_any(bitset); + bitset_destroy(bitset); + return contains; +} + +bool visible_on(struct tagset *tagset, struct container *con) +{ + if (!con) + return false; + if (con->hidden) + return false; + + return exist_on(tagset, con); +} + +bool tagset_is_visible(struct tagset *tagset) +{ + if (!tagset) + return false; + assert(tagset->m != NULL); + + return tagset->m->tagset == tagset; +} + +bool exist_on(struct tagset *tagset, struct container *con) +{ + if (!con || !tagset) + return false; + struct monitor *m = container_get_monitor(con); + if (m != tagset->m) { + if (con->floating) + return container_intersects_with_monitor(con, tagset->m) + && tagset_contains_client(m->tagset, con->client); + else + return false; + } + + struct client *c = con->client; + + if (!c) + return false; + + if (c->type == LAYER_SHELL) + return true; + if (c->sticky) + return true; + + return tagset_contains_client(tagset, c); +} diff --git a/src/tile/tile.c b/src/tile/tile.c deleted file mode 100644 index 01fad00a..00000000 --- a/src/tile/tile.c +++ /dev/null @@ -1,10 +0,0 @@ -#include "tile/tile.h" -#include -#include -#include -#include -#include - -#include "utils/coreUtils.h" -#include "tile/tileUtils.h" -#include "client.h" diff --git a/src/tile/tileUtils.c b/src/tile/tileUtils.c index 5a636135..ca781b1c 100644 --- a/src/tile/tileUtils.c +++ b/src/tile/tileUtils.c @@ -9,7 +9,6 @@ #include #include #include -#include #include "container.h" #include "monitor.h" @@ -19,49 +18,54 @@ #include "utils/gapUtils.h" #include "utils/parseConfigUtils.h" #include "event_handler.h" +#include "layer_shell.h" static void arrange_container(struct container *con, int arrange_position, struct wlr_box root_geom, int inner_gap); void arrange() { - for (int i = 0; i < server.mons.length; i++) { - struct monitor *m = server.mons.items[i]; + for (int i = 0; i < server.mons->len; i++) { + struct monitor *m = g_ptr_array_index(server.mons, i); arrange_monitor(m); } - - update_cursor(&server.cursor); } -static int get_layout_container_area_count(struct workspace *ws) +static void set_layout_ref(struct layout *lt, int n_area) { - struct layout *lt = ws->layout; lua_rawgeti(L, LUA_REGISTRYINDEX, lt->lua_layout_copy_data_ref); - int len = luaL_len(L, -1); - int container_area_count = get_container_area_count(ws); - int n_area = MAX(MIN(len, container_area_count), 1); - lua_rawgeti(L, -1, n_area); - // TODO refactor - len = luaL_len(L, -1); + int len = luaL_len(L, -1); n_area = MAX(MIN(len, n_area), 1); lua_ref_safe(L, LUA_REGISTRYINDEX, <->lua_layout_ref); + + lua_pop(L, 1); +} + +static int get_layout_container_area_count(struct tagset *tagset) +{ + struct layout *lt = tagset_get_layout(tagset); + lua_rawgeti(L, LUA_REGISTRYINDEX, lt->lua_layout_copy_data_ref); + + int len = luaL_len(L, -1); + int container_area_count = get_container_area_count(tagset); + int n_area = MAX(MIN(len, container_area_count), 1); + lua_pop(L, 1); return n_area; } -static int get_layout_container_max_area_count(struct workspace *ws) +static int get_layout_container_max_area_count(struct tagset *tagset) { - struct layout *lt = ws->layout; + struct layout *lt = tagset_get_layout(tagset); lua_rawgeti(L, LUA_REGISTRYINDEX, lt->lua_layout_copy_data_ref); int len = luaL_len(L, -1); lua_rawgeti(L, -1, len); - // TODO refactor int max_n_area = luaL_len(L, -1); @@ -69,26 +73,28 @@ static int get_layout_container_max_area_count(struct workspace *ws) return max_n_area; } -static void update_layout_counters(struct workspace *ws) +static void update_layout_counters(struct tagset *tagset) { - struct layout *lt = ws->layout; - - ws->n_all = get_container_count(ws); - lt->n_area = get_layout_container_area_count(ws); - lt->n_area_max = get_layout_container_max_area_count(ws); - lt->n_master_abs = get_master_container_count(ws); - lt->n_floating = get_floating_container_count(ws); + struct layout *lt = tagset_get_layout(tagset); + + tagset->n_all = get_container_count(tagset); + lt->n_area = get_layout_container_area_count(tagset); + set_layout_ref(lt, lt->n_area); + lt->n_area_max = get_layout_container_max_area_count(tagset); + lt->n_master_abs = get_master_container_count(tagset); + lt->n_floating = get_floating_container_count(tagset); lt->n_tiled = lt->n_area + lt->n_master_abs-1; lt->n_tiled_max = lt->n_area_max + lt->n_master_abs-1; lt->n_visible = lt->n_tiled + lt->n_floating; - lt->n_hidden = ws->n_all - lt->n_visible; + lt->n_hidden = tagset->n_all - lt->n_visible; } static struct wlr_fbox lua_unbox_layout_geom(lua_State *L, int i) { struct wlr_fbox geom; - if (luaL_len(L, -1) < i) - wlr_log(WLR_ERROR, "index to high: index %i len %lli", i, luaL_len(L, -1)); + if (luaL_len(L, -1) < i) { + debug_print("index to high: index %i len %lli", i, luaL_len(L, -1)); + } lua_rawgeti(L, -1, i); @@ -147,16 +153,16 @@ static struct wlr_box get_nth_geom_in_layout(lua_State *L, struct layout *lt, return box; } -int get_slave_container_count(struct workspace *ws) +int get_slave_container_count(struct tagset *tagset) { - struct layout *lt = ws->layout; - int abs_count = get_tiled_container_count(ws); + struct layout *lt = tagset_get_layout(tagset); + int abs_count = get_tiled_container_count(tagset); return MAX(abs_count - lt->nmaster, 0); } -int get_floating_container_count(struct workspace *ws) +int get_floating_container_count(struct tagset *tagset) { - struct layout *lt = ws->layout; + struct layout *lt = tagset_get_layout(tagset); // there are no floating windows when using arrange by focus if (lt->options.arrange_by_focus) @@ -164,8 +170,8 @@ int get_floating_container_count(struct workspace *ws) int n = 0; - for (int i = 0; i < ws->floating_containers.length; i++) { - struct container *con = get_container(ws, i); + for (int i = 0; i < tagset->list_set->floating_containers->len; i++) { + struct container *con = get_container(tagset, i); if (con->client->type == LAYER_SHELL) continue; n++; @@ -173,17 +179,17 @@ int get_floating_container_count(struct workspace *ws) return n; } -int get_master_container_count(struct workspace *ws) +int get_master_container_count(struct tagset *ts) { - int abs_count = get_tiled_container_count(ws); - int slave_container_count = get_slave_container_count(ws); + int abs_count = get_tiled_container_count(ts); + int slave_container_count = get_slave_container_count(ts); return MAX(abs_count - slave_container_count, 0); } // amount of slave containers plus the one master area -int get_container_area_count(struct workspace *ws) +int get_container_area_count(struct tagset *ts) { - return get_slave_container_count(ws) + 1; + return get_slave_container_count(ts) + 1; } void arrange_monitor(struct monitor *m) @@ -191,23 +197,23 @@ void arrange_monitor(struct monitor *m) m->geom = *wlr_output_layout_get_box(server.output_layout, m->wlr_output); set_root_geom(m->root, m->geom); - struct workspace *ws = monitor_get_active_workspace(m); - struct layout *lt = ws->layout; + struct tagset *tagset = monitor_get_active_tagset(m); + struct layout *lt = tagset_get_layout(tagset); container_surround_gaps(&m->root->geom, lt->options.outer_gap); - update_layout_counters(ws); - call_update_function(<->options.event_handler, lt->n_area); + update_layout_counters(tagset); + call_update_function(lt->options.event_handler, lt->n_area); - struct wlr_list *visible_container_lists = get_visible_lists(ws); - struct wlr_list *tiled_containers = get_tiled_list(ws); - struct wlr_list *hidden_containers = get_hidden_list(ws); + GPtrArray *visible_container_lists = tagset_get_visible_lists(tagset); + GPtrArray *tiled_containers = tagset_get_tiled_list(tagset); + GPtrArray *hidden_containers = tagset_get_hidden_list(tagset); update_hidden_status_of_containers(m, visible_container_lists, tiled_containers, hidden_containers); if (!lt->options.arrange_by_focus) { - for (int i = 0; i < ws->floating_containers.length; i++) { - struct container *con = ws->floating_containers.items[i]; + for (int i = 0; i < tagset->list_set->floating_containers->len; i++) { + struct container *con = g_ptr_array_index(tagset->list_set->floating_containers, i); if (con->geom_was_changed) { resize(con, con->prev_floating_geom); con->geom_was_changed = false; @@ -215,15 +221,15 @@ void arrange_monitor(struct monitor *m) } } - arrange_containers(ws, m->root->geom, tiled_containers); + arrange_containers(tagset, m->root->geom, tiled_containers); root_damage_whole(m->root); } -void arrange_containers(struct workspace *ws, struct wlr_box root_geom, - struct wlr_list *tiled_containers) +void arrange_containers(struct tagset *tagset, struct wlr_box root_geom, + GPtrArray *tiled_containers) { - struct layout *lt = ws->layout; + struct layout *lt = tagset_get_layout(tagset); /* each container will get an inner_gap. If two containers are adjacent the * inner_gap is applied twice. To counter this effect we divide the @@ -236,7 +242,7 @@ void arrange_containers(struct workspace *ws, struct wlr_box root_geom, container_surround_gaps(&root_geom, -actual_inner_gap); if (lt->options.smart_hidden_edges) { - if (tiled_containers->length <= 1) { + if (tiled_containers->len <= 1) { container_add_gaps(&root_geom, -lt->options.tile_border_px, lt->options.hidden_edges); } @@ -245,8 +251,14 @@ void arrange_containers(struct workspace *ws, struct wlr_box root_geom, lt->options.hidden_edges); } - for (int i = 0; i < tiled_containers->length; i++) { - struct container *con = tiled_containers->items[i]; + for (int i = 0; i < tiled_containers->len; i++) { + struct container *con = g_ptr_array_index(tiled_containers, i); + + /* // the monitor must be on the same monitor as it is tiled on else it is */ + /* // a bug */ + /* printf("con: %i ws: %i monitor: %p\n", i, con->client->ws_id, container_get_monitor(con)); */ + /* printf("tagset: %p ->ws: %i ->m: %p\n", tagset, tagset->selected_ws_id, tagset->m); */ + /* assert(container_get_monitor(con) == tagset->m); */ arrange_container(con, i, root_geom, actual_inner_gap); } @@ -258,9 +270,8 @@ static void arrange_container(struct container *con, int arrange_position, if (con->hidden) return; - struct monitor *m = con->m; - struct workspace *ws = monitor_get_active_workspace(m); - struct layout *lt = ws->layout; + struct monitor *m = container_get_monitor(con); + struct layout *lt = get_layout_in_monitor(m); struct wlr_box geom = get_nth_geom_in_layout(L, lt, root_geom, arrange_position); container_surround_gaps(&geom, inner_gap); @@ -276,7 +287,7 @@ static void arrange_container(struct container *con, int arrange_position, static void set_container_geom(struct container *con, struct wlr_box geom) { - struct monitor *m = con->m; + struct monitor *m = container_get_monitor(con); struct layout *lt = get_layout_in_monitor(m); if (con->floating && !lt->options.arrange_by_focus) @@ -325,9 +336,18 @@ void resize(struct container *con, struct wlr_box geom) } break; case LAYER_SHELL: - wlr_layer_surface_v1_configure(con->client->surface.layer, - con->m->geom.width, - con->m->geom.height); + { + struct monitor *m = container_get_monitor(con); + int width = m->geom.width; + int height = m->geom.height; + if (con->client->surface.layer->current.desired_width > 0) + width = con->client->surface.layer->current.desired_width; + if (con->client->surface.layer->current.desired_height > 0) + height = con->client->surface.layer->current.desired_width; + wlr_layer_surface_v1_configure(con->client->surface.layer, + width, + height); + } break; case X11_UNMANAGED: case X11_MANAGED: @@ -338,28 +358,25 @@ void resize(struct container *con, struct wlr_box geom) } void update_hidden_status_of_containers(struct monitor *m, - struct wlr_list *visible_container_lists, struct wlr_list *tiled_containers, - struct wlr_list *hidden_containers) + GPtrArray2D *visible_container_lists, GPtrArray *tiled_containers, + GPtrArray *hidden_containers) { - // because the master are is included in n aswell as nmaster we have to - // subtract the solution by one to count struct layout *lt = get_layout_in_monitor(m); - if (lt->n_tiled > tiled_containers->length) { - int n_missing = MIN(lt->n_tiled - tiled_containers->length, hidden_containers->length); + if (lt->n_tiled > tiled_containers->len) { + int n_missing = MIN(lt->n_tiled - tiled_containers->len, hidden_containers->len); for (int i = 0; i < n_missing; i++) { - struct container *con = hidden_containers->items[0]; + struct container *con = g_ptr_array_index(hidden_containers, 0); con->hidden = false; - wlr_list_del(hidden_containers, 0); - wlr_list_push(tiled_containers, con); + g_ptr_array_remove_index(hidden_containers, 0); + g_ptr_array_add(tiled_containers, con); } } else { - int tile_containers_length = tiled_containers->length; - for (int i = lt->n_tiled; i < tile_containers_length; i++) { - struct container *con = wlr_list_pop(tiled_containers); + for (int i = tiled_containers->len-1; i >= lt->n_tiled; i--) { + struct container *con = g_ptr_array_steal_index(tiled_containers, i); con->hidden = true; - wlr_list_insert(hidden_containers, 0, con); + g_ptr_array_insert(hidden_containers, 0, con); } } @@ -367,23 +384,23 @@ void update_hidden_status_of_containers(struct monitor *m, struct container *con = get_in_composed_list(visible_container_lists, i); con->hidden = false; } - for (int i = 0; i < hidden_containers->length; i++) { - struct container *con = hidden_containers->items[i]; + for (int i = 0; i < hidden_containers->len; i++) { + struct container *con = g_ptr_array_index(hidden_containers, i); con->hidden = true; } } -int get_container_count(struct workspace *ws) +int get_container_count(struct tagset *tagset) { - return length_of_composed_list(&ws->container_lists); + return length_of_composed_list(tagset->list_set->container_lists); } -int get_tiled_container_count(struct workspace *ws) +int get_tiled_container_count(struct tagset *tagset) { int n = 0; - struct wlr_list *tiled_containers = get_tiled_list(ws); - struct wlr_list *hidden_containers = get_hidden_list(ws); + GPtrArray *tiled_containers = tagset_get_tiled_list(tagset); + GPtrArray *hidden_containers = tagset_get_hidden_list(tagset); - n = tiled_containers->length + hidden_containers->length; + n = tiled_containers->len + hidden_containers->len; return n; } diff --git a/src/translationLayer.c b/src/translationLayer.c index c0cf9a26..459bb204 100644 --- a/src/translationLayer.c +++ b/src/translationLayer.c @@ -1,22 +1,24 @@ #include "translationLayer.h" + +#include +#include +#include + #include "cursor.h" -#include "lib/actions/actions.h" -#include "lib/actions/libcontainer.h" -#include "lib/config/config.h" -#include "lib/config/localconfig.h" +#include "lib/actions/lib_actions.h" +#include "lib/actions/lib_container.h" +#include "lib/config/lib_config.h" +#include "lib/config/local_config.h" #include "lib/event_handler/lib_event_handler.h" #include "lib/layout/lib_layout.h" -#include "lib/info/info.h" +#include "lib/info/lib_info.h" #include "lib/event_handler/local_event_handler.h" #include "lib/monitor/lib_monitor.h" -#include "tile/tile.h" -#include -#include -#include static const struct luaL_Reg action[] = { {"arrange", lib_arrange}, + {"create_output", lib_create_output}, {"decrease_nmaster", lib_decrease_nmaster}, {"exec", lib_exec}, {"focus_container", lib_focus_container}, @@ -38,6 +40,7 @@ static const struct luaL_Reg action[] = {"set_nmaster", lib_set_nmaster}, {"show_scratchpad", lib_show_scratchpad}, {"swap_workspace", lib_swap_workspace}, + {"tag_view", lib_tag_view}, {"toggle_bars", lib_toggle_bars}, {"toggle_floating", lib_toggle_floating}, {"toggle_layout", lib_toggle_layout}, @@ -58,17 +61,13 @@ static const struct luaL_Reg container[] = static const struct luaL_Reg event[] = { - {"set_create_container_function", lib_set_create_container_function}, - {"set_on_focus_function", lib_set_on_focus_function}, - {"set_on_start_function", lib_set_on_start_function}, - {"set_update_function", lib_set_update_function}, + {"add_listener", lib_add_listener}, {NULL, NULL}, }; static const struct luaL_Reg localevent[] = { - {"set_update_function", local_set_update_function}, - {"set_create_container_function", local_set_create_container_function}, + {"add_listener", local_add_listener}, {NULL, NULL}, }; @@ -77,6 +76,7 @@ static const struct luaL_Reg info[] = {"get_container_under_cursor", lib_get_container_under_cursor}, {"get_next_empty_workspace", lib_get_next_empty_workspace}, {"get_nmaster", lib_get_nmaster}, + {"get_root_area", lib_get_root_area}, {"get_this_container_count", lib_get_this_container_count}, {"get_workspace", lib_get_workspace}, {"is_container_not_in_limit", lib_is_container_not_in_limit}, @@ -87,6 +87,9 @@ static const struct luaL_Reg info[] = static const struct luaL_Reg config[] = { + {"add_mon_rule", lib_add_mon_rule}, + {"add_rule", lib_add_rule}, + {"bind_key", lib_bind_key}, {"create_layout_set", lib_create_layout_set}, {"create_workspaces", lib_create_workspaces}, {"reload", lib_reload}, @@ -97,19 +100,17 @@ static const struct luaL_Reg config[] = {"set_focus_color", lib_set_focus_color}, {"set_hidden_edges", lib_set_hidden_edges}, {"set_inner_gaps", lib_set_inner_gaps}, - {"set_keybinds", lib_set_keybinds}, {"set_layout_constraints", lib_set_layout_constraints}, {"set_master_constraints", lib_set_master_constraints}, {"set_master_layout_data", lib_set_master_layout_data}, {"set_mod", lib_set_mod}, - {"set_monrules", lib_set_monrules}, {"set_outer_gaps", lib_set_outer_gaps}, {"set_repeat_delay", lib_set_repeat_delay}, {"set_repeat_rate", lib_set_repeat_rate}, {"set_resize_data", lib_set_resize_data}, {"set_resize_direction", lib_set_resize_direction}, + {"set_resize_function", lib_set_resize_function}, {"set_root_color", lib_set_root_color}, - {"set_rules", lib_set_rules}, {"set_sloppy_focus", lib_set_sloppy_focus}, {"set_smart_hidden_edges", lib_set_smart_hidden_edges}, {"set_tile_borderpx", lib_set_tile_borderpx}, @@ -130,6 +131,7 @@ static const struct luaL_Reg localconfig[] = {"set_outer_gaps", local_set_outer_gaps}, {"set_resize_data", local_set_resize_data}, {"set_resize_direction", local_set_resize_direction}, + {"set_resize_function", local_set_resize_function}, {"set_sloppy_focus", local_set_sloppy_focus}, {"set_smart_hidden_edges", local_set_smart_hidden_edges}, {"set_tile_borderpx", local_set_tile_borderpx}, @@ -226,8 +228,10 @@ static void load_info() lua_setglobal(L, "info"); } -void load_libs(lua_State *L) +void load_lua_api(lua_State *L) { + luaL_openlibs(L); + luaL_newlib(L, action); lua_setglobal(L, "action"); diff --git a/src/utils/coreUtils.c b/src/utils/coreUtils.c index 2cb4f95b..c6504d73 100644 --- a/src/utils/coreUtils.c +++ b/src/utils/coreUtils.c @@ -1,4 +1,6 @@ #include "utils/coreUtils.h" + +#include #include #include #include @@ -10,6 +12,8 @@ #include #include +#include "utils/parseConfigUtils.h" + struct lua_State *L; bool dir_exists(const char *path) @@ -27,48 +31,56 @@ bool file_exists(const char *path) return access(path, R_OK) != -1; } -void wlr_list_clear(struct wlr_list *list, void (*destroy_func)(void *)) +void wlr_list_cat(GPtrArray *dest, GPtrArray *src) +{ + for (int i = 0; i < src->len; i++) { + void *item = g_ptr_array_index(src, i); + g_ptr_array_add(dest, item); + } +} + +void list_clear(GPtrArray *array, void (*destroy_func)(void *)) { - int length = list->length; - for (int i = 0; i < length; i++) { - void *item = list->items[0]; + for (int i = array->len-1; i >= 0; i--) { + void *item = g_ptr_array_index(array, i); if (destroy_func) { destroy_func(item); } - wlr_list_del(list, 0); + g_ptr_array_remove_index_fast(array, i); } - - wlr_list_init(list); } -bool wlr_list_empty(struct wlr_list *list) +int cross_sum(int n, int base) { - return list->length <= 0; + int sum = 0; + while (n > 0) { + sum += n % base; + n /= base; + } + return sum; } -int wlr_list_remove(struct wlr_list *list, - int (*compare)(const void *, const void *), const void *cmp_to) +bool list_remove(GPtrArray *array, int (*compare)(const void *, const void *), const void *cmp_to) { - for (int i = 0; i < list->length; i++) { - void *item = list->items[i]; + for (int i = 0; i < array->len; i++) { + void *item = g_ptr_array_index(array, i); if (compare(item, cmp_to) == 0) { - wlr_list_del(list, i); - return 0; + g_ptr_array_remove_index(array, i); + return true; } } - return 1; + return false; } -int remove_in_composed_list(struct wlr_list *lists, - int (*compare)(const void *, const void *), const void *cmp_to) +bool remove_in_composed_list(GPtrArray *array, int (*compare)(const void *, const void *), void *cmp_to) { - for (int i = 0; i < lists->length; i++) { - struct wlr_list *list = lists->items[i]; - if (wlr_list_remove(list, compare, cmp_to) == 0) { - return 0; + for (int i = 0; i < array->len; i++) { + GPtrArray *list = g_ptr_array_index(array, i); + if (list_remove(list, compare, cmp_to)) { + return true; } } - return 1; + return false; } int cmp_ptr(const void *ptr1, const void *ptr2) @@ -76,14 +88,19 @@ int cmp_ptr(const void *ptr1, const void *ptr2) return ptr1 == ptr2 ? 0 : 1; } -int find_in_composed_list(struct wlr_list *lists, +int cmp_str(const void *s1, const void *s2) +{ + return strcmp(s1, s2); +} + +int find_in_composed_list(GPtrArray *lists, int (*compare)(const void *, const void *), const void *cmp_to) { int position = 0; - for (int i = 0; i < lists->length; i++) { - struct wlr_list *list = lists->items[i]; - for (int j = 0; j < list->length; j++) { - void *item = list->items[j]; + for (int i = 0; i < lists->len; i++) { + GPtrArray *list = g_ptr_array_index(lists, i); + for (int j = 0; j < list->len; j++) { + void *item = g_ptr_array_index(list, j); if (compare(item, cmp_to) == 0) { return position; } @@ -93,15 +110,15 @@ int find_in_composed_list(struct wlr_list *lists, return -1; } -struct wlr_list *find_list_in_composed_list(struct wlr_list *lists, +GPtrArray *find_list_in_composed_list(GPtrArray *arrays, int (*compare)(const void *, const void *), const void *cmp_to) { - for (int i = 0; i < lists->length; i++) { - struct wlr_list *list = lists->items[i]; - for (int j = 0; j < list->length; j++) { - void *item = list->items[j]; + for (int i = 0; i < arrays->len; i++) { + GPtrArray *array = g_ptr_array_index(arrays, i); + for (int j = 0; j < array->len; j++) { + void *item = g_ptr_array_index(array, j); if (compare(item, cmp_to) == 0) { - return list; + return array; } } } @@ -110,6 +127,8 @@ struct wlr_list *find_list_in_composed_list(struct wlr_list *lists, char last_char(const char *str) { + if (strlen(str) == 0) + return '\0'; return str[strlen(str)-1]; } @@ -131,18 +150,34 @@ int path_compare(const char *path1, const char *path2) return ret; } -void join_path(char *base, const char *file) +void join_path(char **base, const char *file) { - if (last_char(base) != '/' && file[0] != '/') { - strcat(base, "/"); - } else if (last_char(base) == '/' && file[0] == ' ') { - base[strlen(base)-1] = '\0'; + *base = realloc(*base, strlen(*base) + 1 + strlen(file) + 1); + + if (last_char(*base) != '/' && file[0] != '/') { + strcat(*base, "/"); + } else if (last_char(*base) == '/' && file[0] == ' ') { + *base[strlen(*base)-1] = '\0'; } - strcat(base, file); + strcat(*base, file); +} + +void debug_print(const char *fmt, ...) +{ +#if DEBUG + va_list args; + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +#endif } void lua_ref_safe(lua_State *L, int t, int *ref) { + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + return; + } if (*ref > 0) { luaL_unref(L, t, *ref); } @@ -183,6 +218,11 @@ void lua_get_default_layout_data(lua_State *L) } } +void lua_get_default_resize_function(lua_State *L) +{ + lua_getglobal_safe(L, "Resize_main_all"); +} + void lua_get_default_master_layout_data(lua_State *L) { lua_createtable(L, 1, 0); @@ -316,25 +356,25 @@ bool is_approx_equal(double a, double b, double error_range) return fabs(a - b) < error_range; } -void *get_on_list(struct wlr_list *list, int i) +void *get_on_list(GPtrArray *list, int i) { - if (i >= list->length) + if (i >= list->len) return NULL; if (i == INVALID_POSITION) return NULL; - return list->items[i]; + return g_ptr_array_index(list, i); } -void *get_in_composed_list(struct wlr_list *lists, int i) +void *get_in_composed_list(GPtrArray *arrays, int i) { - for (int j = 0; j < lists->length; j++) { - struct wlr_list *list = lists->items[j]; - if (i >= 0 && i < list->length) { - void *item = list->items[i]; + for (int j = 0; j < arrays->len; j++) { + GPtrArray *array = g_ptr_array_index(arrays, j); + if (i >= 0 && i < array->len) { + void *item = g_ptr_array_index(array, i); return item; } - i -= list->length; + i -= array->len; if (i < 0) break; @@ -344,14 +384,14 @@ void *get_in_composed_list(struct wlr_list *lists, int i) return NULL; } -struct wlr_list *get_list_at_i_in_composed_list(struct wlr_list *lists, int i) +GPtrArray *get_list_at_i_in_composed_list(GPtrArray *arrays, int i) { - for (int j = 0; j < lists->length; j++) { - struct wlr_list *list = lists->items[j]; - if (i >= 0 && i < list->length) { + for (int j = 0; j < arrays->len; j++) { + GPtrArray *list = g_ptr_array_index(arrays, j); + if (i >= 0 && i < list->len) { return list; } - i -= list->length; + i -= list->len; if (i < 0) break; @@ -373,34 +413,34 @@ int relative_index_to_absolute_index(int i, int j, int length) return new_position; } -static void *get_relative_item(struct wlr_list *list, - void *(*get_item)(struct wlr_list *, int i), - int (*length_of)(struct wlr_list *), int i, int j) +static void *get_relative_item(GPtrArray *list, + void *(*get_item)(GPtrArray *, int i), + int (*length_of)(GPtrArray *), int i, int j) { int new_position = relative_index_to_absolute_index(i, j, length_of(list)); return get_item(list, new_position); } -void *get_relative_item_in_list(struct wlr_list *list, int i, int j) +void *get_relative_item_in_list(GPtrArray *array, int i, int j) { - return get_relative_item(list, get_on_list, length_of_list, i, j); + return get_relative_item(array, get_on_list, length_of_list, i, j); } -void *get_relative_item_in_composed_list(struct wlr_list *lists, int i, int j) +void *get_relative_item_in_composed_list(GPtrArray *arrays, int i, int j) { - return get_relative_item(lists, get_in_composed_list, length_of_composed_list, + return get_relative_item(arrays, get_in_composed_list, length_of_composed_list, i, j); } -void delete_from_composed_list(struct wlr_list *lists, int i) +void delete_from_composed_list(GPtrArray *arrays, int i) { - for (int j = 0; j < lists->length; j++) { - struct wlr_list *list = lists->items[j]; - if (i >= 0 && i < list->length) { - wlr_list_del(list, i); + for (int j = 0; j < arrays->len; j++) { + GPtrArray *list = g_ptr_array_index(arrays, j); + if (i >= 0 && i < list->len) { + g_ptr_array_remove_index(list, i); return; } - i -= list->length; + i -= list->len; if (i < 0) break; @@ -410,17 +450,17 @@ void delete_from_composed_list(struct wlr_list *lists, int i) return; } -int length_of_list(struct wlr_list *list) +int length_of_list(GPtrArray *array) { - return list->length; + return array->len; } -int length_of_composed_list(struct wlr_list *lists) +int length_of_composed_list(GPtrArray *array) { int length = 0; - for (int i = 0; i < lists->length; i++) { - struct wlr_list *list = lists->items[i]; - length += list->length; + for (int i = 0; i < array->len; i++) { + GPtrArray *list = g_ptr_array_index(array, i); + length += list->len; } return length; } diff --git a/src/utils/parseConfigUtils.c b/src/utils/parseConfigUtils.c index bdcf1c71..89ad3f62 100644 --- a/src/utils/parseConfigUtils.c +++ b/src/utils/parseConfigUtils.c @@ -1,7 +1,6 @@ #include "utils/parseConfigUtils.h" -#include "options.h" -#include "server.h" -#include "utils/writeFile.h" + +#include #include #include #include @@ -11,38 +10,41 @@ #include #include #include +#include +#include +#include + +#include "options.h" +#include "server.h" +#include "utils/writeFile.h" #include "stringop.h" #include "utils/coreUtils.h" +#include "rules/mon_rule.h" -static const char *config_paths[] = { - "$HOME/.config/japokwm/", - "$XDG_CONFIG_HOME/japokwm/", - "/etc/japokwm/", +static const char *plugin_relative_paths[] = { + "autoload", + "plugins", }; static const char *config_file = "init.lua"; static const char *error_file = "init.err"; static int error_fd = -1; -static int load_file(lua_State *L, const char *path, const char *file); +static int load_file(lua_State *L, const char *file); // returns 0 upon success and 1 upon failure -static int load_file(lua_State *L, const char *path, const char *file) +static int load_file(lua_State *L, const char *file) { - char config_file[strlen(path)+strlen(file)]; - strcpy(config_file, ""); - join_path(config_file, path); - join_path(config_file, file); - - if (!file_exists(config_file)) { - return 1; + printf("load file: %s\n", file); + if (!file_exists(file)) { + return EXIT_FAILURE; } - if (luaL_loadfile(L, config_file)) { + if (luaL_loadfile(L, file)) { const char *errmsg = luaL_checkstring(L, -1); lua_pop(L, 1); handle_error(errmsg); - return 1; + return EXIT_FAILURE; } int ret = lua_call_safe(L, 0, 0, 0); @@ -50,13 +52,23 @@ static int load_file(lua_State *L, const char *path, const char *file) return ret; } +GPtrArray *create_default_config_paths() +{ + GPtrArray *config_paths = g_ptr_array_new(); + g_ptr_array_add(config_paths, "$HOME/.config/japokwm/"); + g_ptr_array_add(config_paths, "$XDG_CONFIG_HOME/japokwm/"); + g_ptr_array_add(config_paths, "/etc/japokwm/"); + return config_paths; +} + char *get_config_file(const char *file) { - for (size_t i = 0; i < LENGTH(config_paths); ++i) { - char *path = strdup(config_paths[i]); + debug_print("get config file: %s\n", file); + for (size_t i = 0; i < server.config_paths->len; ++i) { + char *path = strdup(g_ptr_array_index(server.config_paths, i)); + debug_print("path: %s\n", path); + join_path(&path, file); expand_path(&path); - path = realloc(path, strlen(path) + strlen(file)); - join_path(path, file); if (file_exists(path)) return path; free(path); @@ -71,10 +83,6 @@ char *get_config_layout_path() char *get_config_dir(const char *file) { - if (strcmp(server.config_dir, "") != 0 && dir_exists(server.config_dir)) { - return strdup(server.config_dir); - } - char *abs_file = get_config_file(file); if (!abs_file) return NULL; @@ -86,79 +94,46 @@ void append_to_lua_path(lua_State *L, const char *path) { lua_getglobal(L, "package"); lua_getfield(L, -1, "path"); - const char * curr_path = luaL_checkstring(L, -1); + const char *curr_path = luaL_checkstring(L, -1); lua_pop(L, 1); - char path_var[strlen(curr_path) + 1 + strlen(path) + strlen("/?.lua")]; + char *path_var = malloc(strlen(curr_path) + strlen(path) + 2); strcpy(path_var, curr_path); strcat(path_var, ";"); strcat(path_var, path); - join_path(path_var, "/?.lua"); lua_pushstring(L, path_var); lua_setfield(L, -2, "path"); lua_pop(L, 1); + free(path_var); } -// returns 0 upon success and 1 upon failure -static int load_default_config(lua_State *L) +static int load_plugin_paths(lua_State *L) { - char *config_path = get_config_dir(config_file); - printf("config_dir: %s\n", config_path); - - // get the index of the config file in config_paths array - int default_id = -1; - for (int i = 0; i < LENGTH(config_paths); i++) { - char *path = strdup(config_paths[i]); - expand_path(&path); - if (path_compare(path, config_path) == 0) { - default_id = i; - free(path); - break; - } - free(path); + char *base_path = get_config_dir(config_file); + for (int i = 0; i < LENGTH(plugin_relative_paths); i++) { + char *base = strdup(base_path); + const char *path = plugin_relative_paths[i]; + join_path(&base, path); + join_path(&base, "?"); + join_path(&base, "init.lua"); + append_to_lua_path(L, base); + free(base); } + free(base_path); + return 1; +} - bool loaded_custom_path = false; - if (default_id == -1) { - default_id = 0; - // try to load the file given by config_path - char *path = strdup(config_path); - expand_path(&path); - - append_to_lua_path(L, config_path); - - loaded_custom_path = (load_file(L, path, config_file) == EXIT_SUCCESS); - free(path); - } - - if (config_path) - free(config_path); - - if (loaded_custom_path) - return EXIT_SUCCESS; - - int success = EXIT_SUCCESS; - // repeat loop until the first config file was loaded successfully - for (int i = 0; i < LENGTH(config_paths); i++) { - if (i < default_id) - continue; - - char *path = strdup(config_paths[i]); - expand_path(&path); - - append_to_lua_path(L, config_paths[i]); +// returns 0 upon success and 1 upon failure +static int load_default_config(lua_State *L) +{ + char *file_path = get_config_file(config_file); + if (!file_path) + return EXIT_FAILURE; - if (load_file(L, path, config_file) == EXIT_FAILURE) { - success = EXIT_FAILURE; - free(path); - continue; - } + int success = load_file(L, file_path); + free(file_path); - // when config loaded successfully break; - free(path); - break; - } return success; } @@ -166,10 +141,11 @@ static int load_default_config(lua_State *L) int load_config(lua_State *L) { int success = 0; - if (strcmp(server.config_file, "") != 0) { - printf("load custom config file: %s\n", server.config_file); - success = load_file(L, "", server.config_file); + if (server.config_file != NULL && strcmp(server.config_file, "") != 0) { + debug_print("load file\n"); + success = load_file(L, server.config_file); } else { + debug_print("load_default_config\n"); success = load_default_config(L); } return success; @@ -178,51 +154,31 @@ int load_config(lua_State *L) // returns 0 upon success and 1 upon failure int init_utils(lua_State *L) { + load_plugin_paths(L); const char *tile_file = "tile.lua"; char *config_dir = get_config_dir(tile_file); if (!config_dir) - return 1; - - // get the value of the - int default_id = 0; - for (int i = 0; i < LENGTH(config_paths); i++) { - char *path = strdup(config_paths[default_id]); - expand_path(&path); - if (path_compare(path, config_dir) == 0) { - default_id = i; - break; - } - free(path); - } - free(config_dir); - - bool success = true; - // repeat loop until the first config file was loaded successfully - for (int i = 0; i < LENGTH(config_paths); i++) { - if (i < default_id) - continue; + return EXIT_FAILURE; - char *path = strdup(config_paths[i]); - expand_path(&path); + char *dir = strdup(config_dir); + join_path(&dir, "?.lua"); + append_to_lua_path(L, dir); + free(dir); - append_to_lua_path(L, config_paths[i]); + char *file_path = strdup(config_dir); + join_path(&file_path, tile_file); + int success = load_file(L, file_path); - if (load_file(L, path, tile_file)) - continue; + free(config_dir); + free(file_path); - // when config loaded successfully break; - success = false; - break; - } return success; } void init_error_file() { - char *ef = get_config_file(""); - ef = realloc(ef, strlen(ef)+strlen(error_file)); - join_path(ef, error_file); + char *ef = get_config_file(error_file); error_fd = open(ef, O_WRONLY | O_CREAT | O_TRUNC, 0644); free(ef); } @@ -257,9 +213,33 @@ int lua_getglobal_safe(lua_State *L, const char *name) return LUA_OK; } +static void *_notify_msg(void *arg) +{ + char *msg = arg; + notify_init(msg); + NotifyNotification* n = notify_notification_new ("Error in config file", + msg, + 0); + notify_notification_set_timeout(n, 10000); // 10 seconds + + if (!notify_notification_show(n, 0)) + { + printf("showing notification failed!\n"); + } + return NULL; +} + +void notify_msg(const char *msg) +{ + pthread_t thread; + pthread_create(&thread, NULL, _notify_msg, (char *)msg); + pthread_detach(thread); +} + void handle_error(const char *msg) { - wlr_log(WLR_ERROR, "%s", msg); + notify_msg(msg); + printf("%s\n", msg); // if error file not initialized if (error_fd < 0) @@ -269,54 +249,27 @@ void handle_error(const char *msg) write_to_file(error_fd, "\n"); } -char *get_config_array_str(lua_State *L, const char *name, size_t i) +const char *get_config_str(lua_State *L, int idx) { - lua_rawgeti(L, -1, i); - if (!lua_isstring(L, -1)) { - char c[NUM_CHARS] = ""; - handle_error(c); - return NULL; - } - const char *str = luaL_checkstring(L, -1); - lua_pop(L, 1); - - char *termcmd = strdup(str); - return termcmd; -} - -char *get_config_str(lua_State *L, char *name) -{ - lua_getglobal_safe(L, name); - if (!lua_isstring(L, -1)) { - char c[NUM_CHARS] = ""; - handle_error(c); + if (!lua_isstring(L, idx)) { return ""; } - const char *str = luaL_checkstring(L, -1); - char *termcmd = calloc(strlen(str), sizeof(char)); - strcpy(termcmd, str); - lua_pop(L, 1); - return termcmd; + const char *str = luaL_checkstring(L, idx); + return str; } -static float get_config_array_float(lua_State *L, const char *name, size_t i) +const char *get_config_array_str(lua_State *L, const char *name, size_t i) { lua_rawgeti(L, -1, i); - if (!lua_isnumber(L, -1)) { - char c[NUM_CHARS] = ""; - handle_error(c); - return 0; - } - float f = luaL_checknumber(L, -1); + const char *str = get_config_str(L, -1); lua_pop(L, 1); - return f; + return str; } -float get_config_float(lua_State *L, char *name) +static float get_config_array_float(lua_State *L, const char *name, size_t i) { - lua_getglobal_safe(L, name); + lua_rawgeti(L, -1, i); if (!lua_isnumber(L, -1)) { - /* write_to_file(fd, "ERROR: %s is not a number\n"); */ char c[NUM_CHARS] = ""; handle_error(c); return 0; @@ -339,44 +292,7 @@ static int get_config_array_int(lua_State *L, const char *name, size_t i) return f; } -int get_config_int(lua_State *L, char *name) -{ - lua_getglobal_safe(L, name); - if (!lua_isinteger(L, -1)) { - char c[NUM_CHARS] = ""; - handle_error(c); - return 0; - } - int i = luaL_checkinteger(L, -1); - lua_pop(L, 1); - return i; -} - -/* static bool get_config_array_bool(lua_State *L, const char *name, size_t i) */ -/* { */ -/* lua_rawgeti(L, -1, i); */ -/* if (!lua_isboolean(L, -1)) { */ -/* return false; */ -/* } */ -/* bool f = lua_toboolean(L, -1); */ -/* lua_pop(L, 1); */ -/* return f; */ -/* } */ - -bool get_config_bool(lua_State *L, char *name) -{ - lua_getglobal_safe(L, name); - if (!lua_isboolean(L, -1)) { - char c[NUM_CHARS] = ""; - handle_error(c); - return false; - } - bool b = lua_toboolean(L, -1); - lua_pop(L, 1); - return b; -} - -static int get_config_array_func_id(lua_State *L, const char *name, int i) +static int get_config_array_func(lua_State *L, const char *name, int i) { lua_rawgeti(L, -1, i); if (!lua_isfunction(L, -1)) { @@ -390,145 +306,33 @@ static int get_config_array_func_id(lua_State *L, const char *name, int i) return f; } -void call_arrange_func(lua_State *L, int funcId, int n) -{ - lua_rawgeti(L, LUA_REGISTRYINDEX, funcId); - lua_pushinteger(L, n); - lua_call_safe(L, 1, 0, 0); -} - -void call_function(lua_State *L, struct layout lt) -{ - lua_rawgeti(L, LUA_REGISTRYINDEX, lt.lua_layout_ref); - lua_pushinteger(L, lt.n_area); - lua_call_safe(L, 1, 0, 0); -} - -struct layout get_config_layout(lua_State *L, char *name) -{ - lua_getglobal_safe(L, name); - struct layout layout = { - .symbol = get_config_array_str(L, name, 1), - .name = get_config_array_str(L, name, 2), - .n_area = 1, - .nmaster = 1, - .lua_layout_ref = 0, - .lua_layout_copy_data_ref = 0, - .lua_layout_original_copy_data_ref = 0, - .options = get_default_options(), - }; - lua_pop(L, 1); - return layout; -} - -struct rule get_config_array_rule(lua_State *L, const char* name, size_t i) -{ - struct rule rule; - lua_rawgeti(L, -1, i); - - rule.id = get_config_array_str(L, name, 1); - rule.title = get_config_array_str(L, name, 2); - rule.lua_func_ref = get_config_array_func_id(L, name, 3); - - lua_pop(L, 1); - return rule; -} - -struct rule get_config_rule(lua_State *L, char *name) -{ - struct rule rule; - rule.id = get_config_array_str(L, name, 1); - rule.title = get_config_array_str(L, name, 2); - rule.lua_func_ref = get_config_array_func_id(L, name, 3); - return rule; -} - -struct monrule get_config_array_monrule(lua_State *L, const char* name, size_t i) -{ - struct monrule monrule; - lua_rawgeti(L, -1, i); - - monrule.name = get_config_array_str(L, name, 1); - monrule.lua_func_ref = get_config_array_func_id(L, name, 2); - return monrule; -} - -struct monrule get_config_monrule(lua_State *L, char *name) +struct rule *get_config_rule(lua_State *L) { - struct monrule monrule; - lua_getglobal_safe(L, name); - - monrule.name = get_config_array_str(L, name, 1); - monrule.lua_func_ref = get_config_array_func_id(L, name, 2); - + lua_getfield(L, -1, "class"); + const char *id = get_config_str(L, -1); lua_pop(L, 1); - return monrule; -} - -struct layout get_config_key(lua_State *L, char *name) -{ - struct layout key = (struct layout)get_config_layout(L, name); - return key; -} - -void get_config_str_arr(lua_State *L, struct wlr_list *resArr, char *name) -{ - lua_getglobal_safe(L, name); - size_t len = lua_rawlen(L, -1); - - for (int i = 0; i < len; i++) - wlr_list_push(resArr, get_config_array_str(L, name, i+1)); + lua_getfield(L, -1, "name"); + const char *title = get_config_str(L, -1); lua_pop(L, 1); -} - -void get_config_int_arr(lua_State *L, int resArr[], char *name) -{ - lua_getglobal_safe(L, name); - size_t len = lua_rawlen(L, -1); - - for (int i = 0; i < len; i++) - resArr[i] = get_config_array_int(L, name, i); -} + lua_getfield(L, -1, "callback"); + int lua_func_ref = 0; + lua_ref_safe(L, LUA_REGISTRYINDEX, &lua_func_ref); -void get_config_float_arr(lua_State *L, float resArr[], char *name) -{ - lua_getglobal_safe(L, name); - size_t len = lua_rawlen(L, -1); - - for (int i = 0; i < len; i++) - resArr[i] = get_config_array_float(L, name, i+1); lua_pop(L, 1); -} - -void get_config_rule_arr(lua_State *L, struct rule **rules, size_t *rule_count, char *name) -{ - lua_getglobal_safe(L, name); - size_t len = lua_rawlen(L, -1); - *rule_count = len; - *rules = calloc(len, sizeof(struct rule)); - - for (int i = 0; i < len; i++) - *rules[i] = get_config_array_rule(L, name, i+1); - lua_pop(L, 1); + struct rule *rule = create_rule(id, title, lua_func_ref); + return rule; } -void get_config_mon_rule_arr(lua_State *L, struct monrule **monrules, size_t *monrule_count, char *name) +struct mon_rule *get_config_mon_rule(lua_State *L) { - lua_getglobal_safe(L, name); - size_t len = lua_rawlen(L, -1); - *monrule_count = len; - *monrules = calloc(len, sizeof(struct monrule)); - - for (int i = 0; i < len; i++) { - *monrules[i-1] = get_config_array_monrule(L, name, i+1); - } - + lua_getfield(L, -1, "output"); + const char *output_name = get_config_str(L, -1); lua_pop(L, 1); -} + lua_getfield(L, -1, "callback"); + int lua_func_ref = 0; + lua_ref_safe(L, LUA_REGISTRYINDEX, &lua_func_ref); -void call_func(int funcid) -{ - lua_rawgeti(L, LUA_REGISTRYINDEX, funcid); - lua_call_safe(L, 0, 0, 0); + struct mon_rule *mon_rule = create_mon_rule(output_name, lua_func_ref); + return mon_rule; } diff --git a/src/utils/vector.c b/src/utils/vector.c new file mode 100644 index 00000000..190ddd62 --- /dev/null +++ b/src/utils/vector.c @@ -0,0 +1,478 @@ +#define __STDC_WANT_LIB_EXT1__ 1 + +#include +#include +#include + +#include "utils/vector.h" +#include "utils/coreUtils.h" + +Vector* vector_create(size_t capacity, size_t element_size) { + Vector *vector = malloc(sizeof(Vector)); + + vector->size = 0; + vector->capacity = MAX(VECTOR_MINIMUM_CAPACITY, capacity); + vector->element_size = element_size; + vector->data = malloc(vector->capacity * element_size); + + return vector; +} + +Vector *vector_copy(Vector* source) { + assert(source != NULL); + assert(vector_is_initialized(source)); + + Vector *destination = malloc(sizeof(Vector)); + + /* Copy ALL the data */ + destination->size = source->size; + destination->capacity = source->size * 2; + destination->element_size = source->element_size; + + /* Note that we are not necessarily allocating the same capacity */ + destination->data = malloc(destination->capacity * source->element_size); + + memcpy(destination->data, source->data, vector_byte_size(source)); + + return destination; +} + +int vector_move(Vector* destination, Vector* source) { + assert(destination != NULL); + assert(source != NULL); + + if (destination == NULL) return VECTOR_ERROR; + if (source == NULL) return VECTOR_ERROR; + + *destination = *source; + source->data = NULL; + + return VECTOR_SUCCESS; +} + +void vector_move_assign(Vector* destination, Vector* source) { + vector_swap(destination, source); + vector_destroy(source); +} + +int vector_swap(Vector* destination, Vector* source) { + void* temp; + + assert(destination != NULL); + assert(source != NULL); + assert(vector_is_initialized(source)); + assert(vector_is_initialized(destination)); + + if (destination == NULL) return VECTOR_ERROR; + if (source == NULL) return VECTOR_ERROR; + if (!vector_is_initialized(destination)) return VECTOR_ERROR; + if (!vector_is_initialized(source)) return VECTOR_ERROR; + + _vector_swap(&destination->size, &source->size); + _vector_swap(&destination->capacity, &source->capacity); + _vector_swap(&destination->element_size, &source->element_size); + + temp = destination->data; + destination->data = source->data; + source->data = temp; + + return VECTOR_SUCCESS; +} + +void vector_destroy(Vector* vector) { + assert(vector != NULL); + + free(vector->data); + vector->data = NULL; + + free(vector); +} + +/* Insertion */ +int vector_push_back(Vector* vector, void* element) { + assert(vector != NULL); + assert(element != NULL); + + if (_vector_should_grow(vector)) { + if (_vector_adjust_capacity(vector) == VECTOR_ERROR) { + return VECTOR_ERROR; + } + } + + _vector_assign(vector, vector->size, element); + + ++vector->size; + + return VECTOR_SUCCESS; +} + +int vector_push_front(Vector* vector, void* element) { + return vector_insert(vector, 0, element); +} + +int vector_insert(Vector* vector, size_t index, void* element) { + void* offset; + + assert(vector != NULL); + assert(element != NULL); + assert(index <= vector->size); + + if (vector == NULL) return VECTOR_ERROR; + if (element == NULL) return VECTOR_ERROR; + if (vector->element_size == 0) return VECTOR_ERROR; + if (index > vector->size) return VECTOR_ERROR; + + if (_vector_should_grow(vector)) { + if (_vector_adjust_capacity(vector) == VECTOR_ERROR) { + return VECTOR_ERROR; + } + } + + /* Move other elements to the right */ + if (_vector_move_right(vector, index) == VECTOR_ERROR) { + return VECTOR_ERROR; + } + + /* Insert the element */ + offset = _vector_offset(vector, index); + memcpy(offset, element, vector->element_size); + ++vector->size; + + return VECTOR_SUCCESS; +} + +int vector_assign(Vector* vector, size_t index, void* element) { + assert(vector != NULL); + assert(element != NULL); + assert(index < vector->size); + + if (vector == NULL) return VECTOR_ERROR; + if (element == NULL) return VECTOR_ERROR; + if (vector->element_size == 0) return VECTOR_ERROR; + if (index >= vector->size) return VECTOR_ERROR; + + _vector_assign(vector, index, element); + + return VECTOR_SUCCESS; +} + +/* Deletion */ +int vector_pop_back(Vector* vector) { + assert(vector != NULL); + assert(vector->size > 0); + + if (vector == NULL) return VECTOR_ERROR; + if (vector->element_size == 0) return VECTOR_ERROR; + + --vector->size; + +#ifndef VECTOR_NO_SHRINK + if (_vector_should_shrink(vector)) { + _vector_adjust_capacity(vector); + } +#endif + + return VECTOR_SUCCESS; +} + +int vector_pop_front(Vector* vector) { + return vector_erase(vector, 0); +} + +int vector_erase(Vector* vector, size_t index) { + assert(vector != NULL); + assert(index < vector->size); + + if (vector == NULL) return VECTOR_ERROR; + if (vector->element_size == 0) return VECTOR_ERROR; + if (index >= vector->size) return VECTOR_ERROR; + + /* Just overwrite */ + _vector_move_left(vector, index); + +#ifndef VECTOR_NO_SHRINK + if (--vector->size == vector->capacity / 4) { + _vector_adjust_capacity(vector); + } +#endif + + return VECTOR_SUCCESS; +} + +int vector_clear(Vector* vector) { + return vector_resize(vector, 0); +} + +/* Lookup */ +void* vector_get(Vector* vector, size_t index) { + assert(vector != NULL); + assert(index < vector->size); + + if (vector == NULL) return NULL; + if (vector->element_size == 0) return NULL; + if (index >= vector->size) return NULL; + + return _vector_offset(vector, index); +} + +const void* vector_const_get(const Vector* vector, size_t index) { + assert(vector != NULL); + assert(index < vector->size); + + if (vector == NULL) return NULL; + if (vector->element_size == 0) return NULL; + if (index >= vector->size) return NULL; + + return _vector_const_offset(vector, index); +} + +void* vector_front(Vector* vector) { + return vector_get(vector, 0); +} + +void* vector_back(Vector* vector) { + return vector_get(vector, vector->size - 1); +} + +/* Information */ + +bool vector_is_initialized(const Vector* vector) { + return vector->data != NULL; +} + +size_t vector_byte_size(const Vector* vector) { + return vector->size * vector->element_size; +} + +size_t vector_free_space(const Vector* vector) { + return vector->capacity - vector->size; +} + +bool vector_is_empty(const Vector* vector) { + return vector->size == 0; +} + +/* Memory management */ +int vector_resize(Vector* vector, size_t new_size) { + if (new_size <= vector->capacity * VECTOR_SHRINK_THRESHOLD) { + vector->size = new_size; + if (_vector_reallocate(vector, new_size * VECTOR_GROWTH_FACTOR) == -1) { + return VECTOR_ERROR; + } + } else if (new_size > vector->capacity) { + if (_vector_reallocate(vector, new_size * VECTOR_GROWTH_FACTOR) == -1) { + return VECTOR_ERROR; + } + } + + vector->size = new_size; + + return VECTOR_SUCCESS; +} + +int vector_reserve(Vector* vector, size_t minimum_capacity) { + if (minimum_capacity > vector->capacity) { + if (_vector_reallocate(vector, minimum_capacity) == VECTOR_ERROR) { + return VECTOR_ERROR; + } + } + + return VECTOR_SUCCESS; +} + +int vector_shrink_to_fit(Vector* vector) { + return _vector_reallocate(vector, vector->size); +} + +/* Iterators */ +Iterator vector_begin(Vector* vector) { + return vector_iterator(vector, 0); +} + +Iterator vector_end(Vector* vector) { + return vector_iterator(vector, vector->size); +} + +Iterator vector_iterator(Vector* vector, size_t index) { + Iterator iterator = {NULL, 0}; + + assert(vector != NULL); + assert(index <= vector->size); + + if (vector == NULL) return iterator; + if (index > vector->size) return iterator; + if (vector->element_size == 0) return iterator; + + iterator.pointer = _vector_offset(vector, index); + iterator.element_size = vector->element_size; + + return iterator; +} + +void* iterator_get(Iterator* iterator) { + return iterator->pointer; +} + +void iterator_increment(Iterator* iterator) { + assert(iterator != NULL); + iterator->pointer += iterator->element_size; +} + +void iterator_decrement(Iterator* iterator) { + assert(iterator != NULL); + iterator->pointer -= iterator->element_size; +} + +void* iterator_next(Iterator* iterator) { + void* current = iterator->pointer; + iterator_increment(iterator); + + return current; +} + +void* iterator_previous(Iterator* iterator) { + void* current = iterator->pointer; + iterator_decrement(iterator); + + return current; +} + +bool iterator_equals(Iterator* first, Iterator* second) { + assert(first->element_size == second->element_size); + return first->pointer == second->pointer; +} + +bool iterator_is_before(Iterator* first, Iterator* second) { + assert(first->element_size == second->element_size); + return first->pointer < second->pointer; +} + +bool iterator_is_after(Iterator* first, Iterator* second) { + assert(first->element_size == second->element_size); + return first->pointer > second->pointer; +} + +/***** PRIVATE *****/ + +bool _vector_should_grow(Vector* vector) { + assert(vector->size <= vector->capacity); + return vector->size == vector->capacity; +} + +bool _vector_should_shrink(Vector* vector) { + assert(vector->size <= vector->capacity); + return vector->size == vector->capacity * VECTOR_SHRINK_THRESHOLD; +} + +size_t _vector_free_bytes(const Vector* vector) { + return vector_free_space(vector) * vector->element_size; +} + +void* _vector_offset(Vector* vector, size_t index) { + return vector->data + (index * vector->element_size); +} + +const void* _vector_const_offset(const Vector* vector, size_t index) { + return vector->data + (index * vector->element_size); +} + +void _vector_assign(Vector* vector, size_t index, void* element) { + /* Insert the element */ + void* offset = _vector_offset(vector, index); + memcpy(offset, element, vector->element_size); +} + +int _vector_move_right(Vector* vector, size_t index) { + assert(vector->size < vector->capacity); + + /* The location where to start to move from. */ + void* offset = _vector_offset(vector, index); + + /* How many to move to the right. */ + size_t elements_in_bytes = (vector->size - index) * vector->element_size; + +#ifdef __STDC_LIB_EXT1__ + size_t right_capacity_in_bytes = + (vector->capacity - (index + 1)) * vector->element_size; + + /* clang-format off */ + int return_code = memmove_s( + offset + vector->element_size, + right_capacity_in_bytes, + offset, + elements_in_bytes + ); + /* clang-format on */ + + return return_code == 0 ? VECTOR_SUCCESS : VECTOR_ERROR; + +#else + memmove(offset + vector->element_size, offset, elements_in_bytes); + return VECTOR_SUCCESS; +#endif +} + +void _vector_move_left(Vector* vector, size_t index) { + size_t right_elements_in_bytes; + void* offset; + + /* The offset into the memory */ + offset = _vector_offset(vector, index); + + /* How many to move to the left */ + right_elements_in_bytes = (vector->size - index - 1) * vector->element_size; + + memmove(offset, offset + vector->element_size, right_elements_in_bytes); +} + +int _vector_adjust_capacity(Vector* vector) { + return _vector_reallocate(vector, + MAX(1, vector->size * VECTOR_GROWTH_FACTOR)); +} + +int _vector_reallocate(Vector* vector, size_t new_capacity) { + size_t new_capacity_in_bytes; + void* old; + assert(vector != NULL); + + if (new_capacity < VECTOR_MINIMUM_CAPACITY) { + if (vector->capacity > VECTOR_MINIMUM_CAPACITY) { + new_capacity = VECTOR_MINIMUM_CAPACITY; + } else { + /* NO-OP */ + return VECTOR_SUCCESS; + } + } + + new_capacity_in_bytes = new_capacity * vector->element_size; + old = vector->data; + + if ((vector->data = malloc(new_capacity_in_bytes)) == NULL) { + return VECTOR_ERROR; + } + +#ifdef __STDC_LIB_EXT1__ + /* clang-format off */ + if (memcpy_s(vector->data, + new_capacity_in_bytes, + old, + vector_byte_size(vector)) != 0) { + return VECTOR_ERROR; + } +/* clang-format on */ +#else + memcpy(vector->data, old, vector_byte_size(vector)); +#endif + + vector->capacity = new_capacity; + + free(old); + + return VECTOR_SUCCESS; +} + +void _vector_swap(size_t* first, size_t* second) { + size_t temp = *first; + *first = *second; + *second = temp; +} diff --git a/src/utils/writeFile.c b/src/utils/writeFile.c index 9bd01081..0a65e5e9 100644 --- a/src/utils/writeFile.c +++ b/src/utils/writeFile.c @@ -7,12 +7,12 @@ int write_to_file(int fd, const char *content) { if (fd < 0) { - wlr_log(WLR_ERROR, "ERROR: file didn't open correctly"); + debug_print("ERROR: file didn't open correctly\n"); return -1; } if (write(fd, content, strlen(content)) != strlen(content)) { - wlr_log(WLR_ERROR, "ERROR: failed to write content to file\n"); + debug_print("ERROR: failed to write content to file\n"); return -1; } return 0; @@ -27,7 +27,6 @@ static int write_double_to_file(int fd, double d) void write_container_to_file(int fd, struct wlr_fbox box) { - wlr_log(WLR_DEBUG, "write container"); write_double_to_file(fd, box.x); write_to_file(fd, " "); write_double_to_file(fd, box.y); diff --git a/src/wlr_signal.c b/src/wlr_signal.c new file mode 100644 index 00000000..0f3815b3 --- /dev/null +++ b/src/wlr_signal.c @@ -0,0 +1 @@ +#include "wlr_signal.h" diff --git a/src/workspace.c b/src/workspace.c index 4b56481d..6868d238 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -13,223 +13,148 @@ #include "utils/parseConfigUtils.h" #include "container.h" -static void update_workspaces_id(struct wlr_list *workspaces) +static void update_workspaces_id(GPtrArray *workspaces) { - int id = 0; - for (int i = 0; i < workspaces->length; i++) { - struct workspace *ws = workspaces->items[i]; + for (int id = 0; id < workspaces->len; id++) { + struct workspace *ws = g_ptr_array_index(workspaces, id); ws->id = id; - id++; } } -static void setup_lists(struct workspace *ws) +GPtrArray *create_workspaces(GPtrArray *tag_names) { - wlr_list_init(&ws->loaded_layouts); - - wlr_list_init(&ws->container_lists); - wlr_list_init(&ws->visible_container_lists); - - wlr_list_init(&ws->independent_containers); - wlr_list_init(&ws->tiled_containers); - wlr_list_init(&ws->hidden_containers); - wlr_list_init(&ws->floating_containers); - - wlr_list_push(&ws->container_lists, &ws->tiled_containers); - wlr_list_push(&ws->container_lists, &ws->floating_containers); - wlr_list_push(&ws->container_lists, &ws->hidden_containers); - - wlr_list_push(&ws->visible_container_lists, &ws->tiled_containers); - wlr_list_push(&ws->visible_container_lists, &ws->floating_containers); - - wlr_list_init(&ws->focus_stack_lists); - wlr_list_init(&ws->focus_stack_visible_lists); - wlr_list_init(&ws->focus_stack_lists_with_layer_shell); - - wlr_list_init(&ws->focus_stack_layer_background); - wlr_list_init(&ws->focus_stack_layer_bottom); - wlr_list_init(&ws->focus_stack_layer_top); - wlr_list_init(&ws->focus_stack_layer_overlay); - wlr_list_init(&ws->focus_stack_layer_bottom); - wlr_list_init(&ws->focus_stack_on_top); - wlr_list_init(&ws->focus_stack_normal); - wlr_list_init(&ws->focus_stack_hidden); - wlr_list_init(&ws->focus_stack_not_focusable); - - wlr_list_push(&ws->focus_stack_lists, &ws->focus_stack_layer_top); - wlr_list_push(&ws->focus_stack_lists, &ws->focus_stack_on_top); - wlr_list_push(&ws->focus_stack_lists, &ws->focus_stack_normal); - wlr_list_push(&ws->focus_stack_lists, &ws->focus_stack_not_focusable); - wlr_list_push(&ws->focus_stack_lists, &ws->focus_stack_hidden); - - wlr_list_push(&ws->focus_stack_lists_with_layer_shell, &ws->focus_stack_layer_overlay); - wlr_list_push(&ws->focus_stack_lists_with_layer_shell, &ws->focus_stack_layer_top); - wlr_list_push(&ws->focus_stack_lists_with_layer_shell, &ws->focus_stack_on_top); - wlr_list_push(&ws->focus_stack_lists_with_layer_shell, &ws->focus_stack_normal); - wlr_list_push(&ws->focus_stack_lists_with_layer_shell, &ws->focus_stack_not_focusable); - wlr_list_push(&ws->focus_stack_lists_with_layer_shell, &ws->focus_stack_layer_bottom); - wlr_list_push(&ws->focus_stack_lists_with_layer_shell, &ws->focus_stack_layer_background); - - wlr_list_push(&ws->focus_stack_visible_lists, &ws->focus_stack_on_top); - wlr_list_push(&ws->focus_stack_visible_lists, &ws->focus_stack_normal); - wlr_list_push(&ws->focus_stack_visible_lists, &ws->focus_stack_not_focusable); + GPtrArray *workspaces = g_ptr_array_new(); + for (int i = 0; i < tag_names->len; i++) { + const char *name = g_ptr_array_index(tag_names, i); + struct workspace *ws = create_workspace(name, i, server.default_layout); + g_ptr_array_add(workspaces, ws); + } + return workspaces; } struct workspace *create_workspace(const char *name, size_t id, struct layout *lt) { struct workspace *ws = calloc(1, sizeof(struct workspace)); - ws->name = name; + ws->name = strdup(name); ws->id = id; - setup_lists(ws); - + ws->loaded_layouts = g_ptr_array_new(); // fill layout stack with reasonable values push_layout(ws, lt); push_layout(ws, lt); + + ws->list_set = create_list_set(); + ws->subscribed_tagsets = g_ptr_array_new(); return ws; } -void update_workspaces(struct wlr_list *workspaces, struct wlr_list *tag_names) +void copy_layout_from_selected_workspace(GPtrArray *workspaces) +{ + struct layout *src_lt = get_layout_in_monitor(selected_monitor); + + for (int i = 0; i < workspaces->len; i++) { + struct workspace *ws = g_ptr_array_index(workspaces, i); + struct layout *dest_lt = ws->layout; + struct layout *dest_prev_lt = ws->previous_layout; + + if (dest_lt == src_lt) + continue; + + copy_layout(dest_lt, src_lt); + copy_layout(dest_prev_lt, src_lt); + } +} + +void update_workspaces(GPtrArray *workspaces, GPtrArray *tag_names) { - if (tag_names->length > server.workspaces.length) { - for (int i = server.workspaces.length-1; i < tag_names->length; i++) { - const char *name = tag_names->items[0]; + if (tag_names->len > server.workspaces->len) { + for (int i = server.workspaces->len-1; i < tag_names->len; i++) { + const char *name = g_ptr_array_index(tag_names, 0); struct workspace *ws = create_workspace(name, i, server.default_layout); - wlr_list_push(&server.workspaces, ws); + g_ptr_array_add(server.workspaces, ws); } } else { - int tile_containers_length = server.workspaces.length; - for (int i = tag_names->length; i < tile_containers_length; i++) { - struct workspace *ws = wlr_list_pop(&server.workspaces); + int tile_containers_length = server.workspaces->len; + for (int i = tag_names->len; i < tile_containers_length; i++) { + struct workspace *ws = g_ptr_array_steal_index( + server.workspaces, + server.workspaces->len); destroy_workspace(ws); } } - for (int i = 0; i < server.workspaces.length; i++) { - struct workspace *ws = server.workspaces.items[i]; - rename_workspace(ws, tag_names->items[i]); + for (int i = 0; i < server.workspaces->len; i++) { + struct workspace *ws = g_ptr_array_index(server.workspaces, i); + const char *name = g_ptr_array_index(tag_names, i); + rename_workspace(ws, name); } } void destroy_workspace(struct workspace *ws) { - for (int i = 0; i < length_of_composed_list(&ws->container_lists); i++) { - struct container *con = get_in_composed_list(&ws->container_lists, i); + g_ptr_array_free(ws->subscribed_tagsets, TRUE); + for (int i = 0; i < length_of_composed_list(ws->list_set->container_lists); i++) { + struct container *con = get_in_composed_list(ws->list_set->container_lists, i); struct client *c = con->client; kill_client(c); } + destroy_list_set(ws->list_set); + free(ws->name); free(ws); } -void update_workspace_ids(struct wlr_list *workspaces) +void update_workspace_ids(GPtrArray *workspaces) { - for (int i = 0; i < workspaces->length; i++) { - struct workspace *ws = workspaces->items[i]; + for (int i = 0; i < workspaces->len; i++) { + struct workspace *ws = g_ptr_array_index(workspaces, i); ws->id = i; } } -void create_workspaces(struct wlr_list *workspaces, struct wlr_list *tag_names, - struct layout *default_layout) +void destroy_workspaces(GPtrArray *workspaces) { - wlr_list_init(workspaces); - for (int i = 0; i < tag_names->length; i++) { - struct workspace *ws = create_workspace(tag_names->items[i], i, default_layout); - wlr_list_push(workspaces, ws); + for (int i = 0; i < workspaces->len; i++) { + struct workspace *ws = g_ptr_array_index(workspaces, 0); + destroy_workspace(ws); } -} - -void destroy_workspaces(struct wlr_list *workspaces) -{ - for (int i = 0; i < workspaces->length; i++) - destroy_workspace(wlr_list_pop(workspaces)); - wlr_list_finish(workspaces); + g_ptr_array_free(workspaces, true); } bool is_workspace_occupied(struct workspace *ws) { assert(ws); - return ws->m ? true : false; -} - -static bool container_intersects_with_monitor(struct container *con, struct monitor *m) -{ - if (!con) - return false; - if (!m) - return false; - - struct wlr_box tmp_geom; - return wlr_box_intersection(&tmp_geom, &con->geom, &m->geom); + struct monitor *m = workspace_get_monitor(ws); + bool is_occupied = (m != NULL) && workspace_is_visible(ws); + return is_occupied; } -bool exist_on(struct container *con, struct workspace *ws) +bool workspace_is_visible(struct workspace *ws) { - if (!con || !ws) - return false; - if (con->m != ws->m) { - if (con->floating) - return container_intersects_with_monitor(con, ws->m) - && workspace_contains_client(get_workspace(con->m->ws_id), con->client); - else - return false; - } - - struct client *c = con->client; - - if (!c) - return false; + assert(ws != NULL); - if (c->type == LAYER_SHELL) { + if (ws->prev_m && !is_workspace_empty(ws)) { return true; } - if (c->sticky) { - return true; + if (ws->tagset) { + return tagset_is_visible(ws->tagset); } - - return workspace_contains_client(ws, c); -} - -bool workspace_contains_client(struct workspace *ws, struct client *c) -{ - if (!ws) - return false; - if (!c) - return false; - - return ws->id == c->ws_id; -} - -bool workspace_has_clients(struct workspace *ws) -{ - if (!ws) - return false; - - for (int i = 0; i < server.normal_clients.length; i++) { - struct client *c = server.normal_clients.items[i]; - - if (workspace_contains_client(ws, c)) - return true; + if (ws->selected_tagset) { + return tagset_is_visible(ws->selected_tagset); } - return false; } -bool hidden_on(struct container *con, struct workspace *ws) +bool workspace_is_active(struct workspace *ws) { - return !visible_on(con, ws) && exist_on(con, ws); -} + struct monitor *m = workspace_get_monitor(ws); -bool visible_on(struct container *con, struct workspace *ws) -{ - if (!con) - return false; - if (con->hidden) + if (!m) return false; - return exist_on(con, ws); + struct tagset *tagset = monitor_get_active_tagset(m); + return bitset_test(tagset->loaded_workspaces, ws->id); } int get_workspace_container_count(struct workspace *ws) @@ -237,22 +162,7 @@ int get_workspace_container_count(struct workspace *ws) if (!ws) return -1; - int i = 0; - for (int i = 0; i < ws->tiled_containers.length; i++) { - struct container *con = get_container(ws, i); - - if (visible_on(con, ws)) - i++; - } - return i; -} - -struct container *get_container(struct workspace *ws, int i) -{ - if (!ws) - return NULL; - - return get_in_composed_list(&ws->container_lists, i); + return length_of_composed_list(ws->list_set->visible_container_lists); } bool is_workspace_empty(struct workspace *ws) @@ -260,10 +170,10 @@ bool is_workspace_empty(struct workspace *ws) return get_workspace_container_count(ws) == 0; } -struct workspace *find_next_unoccupied_workspace(struct wlr_list *workspaces, struct workspace *ws) +struct workspace *find_next_unoccupied_workspace(GPtrArray *workspaces, struct workspace *ws) { - for (size_t i = ws ? ws->id : 0; i < workspaces->length; i++) { - struct workspace *w = workspaces->items[i]; + for (size_t i = ws ? ws->id : 0; i < workspaces->len; i++) { + struct workspace *w = g_ptr_array_index(workspaces, i); if (!w) break; if (!is_workspace_occupied(w)) @@ -276,17 +186,17 @@ struct workspace *get_workspace(int id) { if (id < 0) return NULL; - if (id >= server.workspaces.length) + if (id >= server.workspaces->len) return NULL; - return server.workspaces.items[id]; + return g_ptr_array_index(server.workspaces, id); } -struct workspace *get_next_empty_workspace(struct wlr_list *workspaces, size_t i) +struct workspace *get_next_empty_workspace(GPtrArray *workspaces, size_t i) { struct workspace *ws = NULL; - for (int j = i; j < workspaces->length; j++) { - struct workspace *ws = workspaces->items[j]; + for (int j = i; j < workspaces->len; j++) { + struct workspace *ws = g_ptr_array_index(workspaces, j); if (is_workspace_empty(ws)) break; } @@ -294,14 +204,14 @@ struct workspace *get_next_empty_workspace(struct wlr_list *workspaces, size_t i return ws; } -struct workspace *get_prev_empty_workspace(struct wlr_list *workspaces, size_t i) +struct workspace *get_prev_empty_workspace(GPtrArray *workspaces, size_t i) { - if (i >= workspaces->length) + if (i >= workspaces->len) return NULL; struct workspace *ws = NULL; for (int j = i; j >= 0; j--) { - struct workspace *ws = workspaces->items[j]; + struct workspace *ws = g_ptr_array_index(workspaces, j); if (is_workspace_empty(ws)) break; } @@ -309,130 +219,174 @@ struct workspace *get_prev_empty_workspace(struct wlr_list *workspaces, size_t i return ws; } -struct wlr_list *get_visible_lists(struct workspace *ws) +struct tagset *workspace_get_selected_tagset(struct workspace *ws) { - struct layout *lt = ws->layout; - - if (lt->options.arrange_by_focus) - return &ws->focus_stack_visible_lists; - else - return &ws->visible_container_lists; + struct tagset *tagset = ws->selected_tagset; + return tagset; } -struct wlr_list *get_tiled_list(struct workspace *ws) +struct tagset *workspace_get_tagset(struct workspace *ws) { - struct layout *lt = ws->layout; + return ws->tagset; +} - if (lt->options.arrange_by_focus) - return &ws->focus_stack_normal; - else - return &ws->tiled_containers; +struct monitor *workspace_get_selected_monitor(struct workspace *ws) +{ + assert(ws != NULL); + if (!ws->selected_tagset) + return NULL; + return ws->selected_tagset->m; } -struct wlr_list *get_floating_list(struct workspace *ws) +struct monitor *workspace_get_monitor(struct workspace *ws) { - struct layout *lt = ws->layout; + assert(ws != NULL); - if (lt->options.arrange_by_focus) - return &ws->focus_stack_normal; - else - return &ws->floating_containers; + if (ws->tagset) { + return ws->tagset->m; + } + if (ws->selected_tagset) { + return ws->selected_tagset->m; + } + if (ws->prev_m && !is_workspace_empty(ws)) { + return ws->prev_m; + } + return NULL; } -struct wlr_list *get_hidden_list(struct workspace *ws) +void push_layout(struct workspace *ws, struct layout *lt) { - struct layout *lt = ws->layout; + lt->ws_id = ws->id; + ws->previous_layout = ws->layout; + ws->layout = lt; +} - if (lt->options.arrange_by_focus) - return &ws->focus_stack_hidden; - else - return &ws->hidden_containers; +void load_default_layout(lua_State *L) +{ + load_layout(L, server.default_layout->name); } -void workspace_assign_monitor(struct workspace *ws, struct monitor *m) +void load_layout(lua_State *L, const char *name) { - ws->m = m; + char *config_path = get_config_file("layouts"); + char *file = strdup(""); + join_path(&file, config_path); + join_path(&file, name); + join_path(&file, "init.lua"); + if (config_path) + free(config_path); + + if (!file_exists(file)) + goto cleanup; + + if (luaL_loadfile(L, file)) { + lua_pop(L, 1); + goto cleanup; + } + lua_call_safe(L, 0, 0, 0); + +cleanup: + free(file); } -void set_selected_layout(struct workspace *ws, struct layout *layout) +void reset_loaded_layout(struct workspace *ws) { - if (!ws) - return; + int length = ws->loaded_layouts->len; + for (int i = 0; i < length; i++) { + struct layout *lt = g_ptr_array_index(ws->loaded_layouts, 0); + destroy_layout(lt); + g_ptr_array_remove_index(ws->loaded_layouts, 0); + } +} - if (strcmp(ws->name, "") == 0) { - wlr_log(WLR_ERROR, "ERROR: tag not initialized"); - return; +void remove_loaded_layouts(GPtrArray *workspaces) +{ + for (int i = 0; i < workspaces->len; i++) { + struct workspace *ws = get_workspace(i); + list_clear(ws->loaded_layouts, (void (*)(void *))destroy_layout); } - push_layout(ws, layout); } -void move_container_to_workspace(struct container *con, struct workspace *ws) +void focus_next_unoccupied_workspace(struct monitor *m, GPtrArray *workspaces, struct workspace *ws) { - if (!ws) - return; - if (!con) - return; - if (con->client->type == LAYER_SHELL) - return; + struct workspace *w = find_next_unoccupied_workspace(workspaces, ws); - set_container_workspace(con, ws); - con->client->moved_workspace = true; - container_damage_whole(con); + if (!w) + return; - arrange(); - struct workspace *selected_workspace = monitor_get_active_workspace(con->m); - focus_most_recent_container(selected_workspace, FOCUS_NOOP); + BitSet *bitset = bitset_create(server.workspaces->len); + bitset_set(bitset, w->id); - ipc_event_workspace(); + struct tagset *tagset = create_tagset(m, w->id, bitset); + push_tagset_no_ref(tagset); } -void add_container_to_containers(struct container *con, struct workspace *ws, int i) +void rename_workspace(struct workspace *ws, const char *name) { - if (!con) - return; if (!ws) return; + free(ws->name); + ws->name = strdup(name); +} - if (con->floating) { - wlr_list_insert(&ws->floating_containers, i, con); - return; - } - if (con->hidden) { - wlr_list_insert(&ws->hidden_containers, i, con); - return; - } - wlr_list_insert(&ws->tiled_containers, i, con); +void workspace_add_container_to_containers(struct workspace *ws, struct container *con, int i) +{ + assert(con != NULL); + + DO_ACTION(ws, + if (con->floating) { + g_ptr_array_insert(list_set->floating_containers, i, con); + continue; + } + if (con->hidden) { + g_ptr_array_insert(list_set->hidden_containers, i, con); + continue; + } + assert(list_set->tiled_containers->len >= i); + if (list_set->tiled_containers->len <= 0) { + g_ptr_array_add(list_set->tiled_containers, con); + } else { + g_ptr_array_insert(list_set->tiled_containers, i, con); + } + ); } -void add_container_to_focus_stack(struct container *con, struct workspace *ws) +void list_set_add_container_to_focus_stack(struct list_set *list_set, struct container *con) { if (con->client->type == LAYER_SHELL) { switch (con->client->surface.layer->current.layer) { case ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND: - wlr_list_insert(&ws->focus_stack_layer_background, 0, con); + g_ptr_array_insert(list_set->focus_stack_layer_background, 0, con); break; case ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM: - wlr_list_insert(&ws->focus_stack_layer_bottom, 0, con); + g_ptr_array_insert(list_set->focus_stack_layer_bottom, 0, con); break; case ZWLR_LAYER_SHELL_V1_LAYER_TOP: - wlr_list_insert(&ws->focus_stack_layer_top, 0, con); + g_ptr_array_insert(list_set->focus_stack_layer_top, 0, con); break; case ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY: - wlr_list_insert(&ws->focus_stack_layer_overlay, 0, con); + g_ptr_array_insert(list_set->focus_stack_layer_overlay, 0, con); break; } return; } if (con->on_top) { - wlr_list_insert(&ws->focus_stack_on_top, 0, con); + g_ptr_array_insert(list_set->focus_stack_on_top, 0, con); return; } if (!con->focusable) { - wlr_list_insert(&ws->focus_stack_not_focusable, 0, con); + g_ptr_array_insert(list_set->focus_stack_not_focusable, 0, con); return; } - wlr_list_insert(&ws->focus_stack_normal, 0, con); + g_ptr_array_insert(list_set->focus_stack_normal, 0, con); +} + +void workspace_add_container_to_focus_stack(struct workspace *ws, struct container *con) +{ + DO_ACTION(ws, + list_set_add_container_to_focus_stack(list_set, con); + ); } void add_container_to_stack(struct container *con) @@ -443,154 +397,48 @@ void add_container_to_stack(struct container *con) if (con->client->type == LAYER_SHELL) { switch (con->client->surface.layer->current.layer) { case ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND: - wlr_list_insert(&server.layer_visual_stack_background, 0, con); + g_ptr_array_insert(server.layer_visual_stack_background, 0, con); break; case ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM: - wlr_list_insert(&server.layer_visual_stack_bottom, 0, con); + g_ptr_array_insert(server.layer_visual_stack_bottom, 0, con); break; case ZWLR_LAYER_SHELL_V1_LAYER_TOP: - wlr_list_insert(&server.layer_visual_stack_top, 0, con); + g_ptr_array_insert(server.layer_visual_stack_top, 0, con); break; case ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY: - wlr_list_insert(&server.layer_visual_stack_overlay, 0, con); + g_ptr_array_insert(server.layer_visual_stack_overlay, 0, con); break; } return; } if (con->floating) { - wlr_list_insert(&server.floating_visual_stack, 0, con); - return; - } - - wlr_list_insert(&server.tiled_visual_stack, 0, con); -} - -void focus_most_recent_container(struct workspace *ws, enum focus_actions a) -{ - struct container *con = get_in_composed_list(&ws->focus_stack_lists, 0); - - if (!con) { - con = get_container(ws, 0); - if (!con) { - ipc_event_window(); - return; - } - } - - focus_container(con, a); -} - -void focus_next_unoccupied_workspace(struct monitor *m, struct wlr_list *workspaces, struct workspace *ws) -{ - struct workspace *w = find_next_unoccupied_workspace(workspaces, ws); - - if (!w) - return; - - focus_workspace(m, w); -} - -void rename_workspace(struct workspace *ws, const char *name) -{ - if (!ws) - return; - ws->name = name; -} - -void focus_workspace(struct monitor *m, struct workspace *ws) -{ - if (!m || !ws) - return; - assert(m->damage != NULL); - - // focus the workspace in the monitor it appears in if such a monitor exist - // and is not the selected one - if (is_workspace_occupied(ws) && ws->m != selected_monitor) { - struct workspace *wss = monitor_get_active_workspace(m); - for (int i = 0; i < wss->floating_containers.length; i++) { - struct container *con = wss->floating_containers.items[i]; - move_container_to_workspace(con, ws); - } - - center_mouse_in_monitor(ws->m); - selected_monitor = ws->m; - focus_workspace(ws->m, ws); + g_ptr_array_insert(server.floating_visual_stack, 0, con); return; } - struct container *con; - wl_list_for_each(con, &sticky_stack, stlink) { - con->client->ws_id = ws->id; - } - - ipc_event_workspace(); - - struct workspace *old_ws = monitor_get_active_workspace(m); - // unset old workspace - if (old_ws && !workspace_has_clients(old_ws)) { - struct workspace *old_ws = monitor_get_active_workspace(m); - old_ws->m = NULL; - } - - m->ws_id = ws->id; - ws->m = m; - - arrange(); - focus_most_recent_container(ws, FOCUS_NOOP); - root_damage_whole(m->root); + g_ptr_array_insert(server.tiled_visual_stack, 0, con); } -void copy_layout_from_selected_workspace(struct wlr_list *workspaces) +void workspace_remove_container(struct workspace *ws, struct container *con) { - struct layout *src_lt = get_layout_in_monitor(selected_monitor); - - for (int i = 0; i < workspaces->length; i++) { - struct workspace *ws = workspaces->items[i]; - struct layout *dest_lt = ws->layout; - struct layout *dest_prev_lt = ws->previous_layout; - - if (dest_lt == src_lt) - continue; - - copy_layout(dest_lt, src_lt); - copy_layout(dest_prev_lt, src_lt); - } + DO_ACTION(ws, + remove_in_composed_list(list_set->container_lists, cmp_ptr, con); + ); } -void load_default_layout(lua_State *L, struct workspace *ws) +void workspace_remove_container_from_focus_stack(struct workspace *ws, struct container *con) { - load_layout(L, server.default_layout->name); + DO_ACTION(ws, + remove_in_composed_list(list_set->focus_stack_lists, cmp_ptr, con); + ); } -void set_container_workspace(struct container *con, struct workspace *ws) +void workspace_remove_independent_container(struct workspace *ws, struct container *con) { - if (!con) - return; - if (!ws) - return; - if (con->m->ws_id == ws->id) - return; - - struct workspace *sel_ws = monitor_get_active_workspace(con->m); - - if (!ws->m) { - ws->m = con->m; - } else { - set_container_monitor(con, ws->m); - } - con->client->ws_id = ws->id; - - remove_in_composed_list(&sel_ws->container_lists, cmp_ptr, con); - add_container_to_containers(con, ws, 0); - - remove_in_composed_list(&sel_ws->focus_stack_lists, cmp_ptr, con); - add_container_to_focus_stack(con, ws); - - if (con->floating) - con->client->bw = ws->layout->options.float_border_px; - else - con->client->bw = ws->layout->options.tile_border_px; + DO_ACTION(ws, + g_ptr_array_remove(list_set->independent_containers, con); + ); } // TODO refactor this function @@ -617,65 +465,3 @@ void layout_set_set_layout(lua_State *L) load_layout(L, layout_name); } - -void load_layout(lua_State *L, const char *name) -{ - char *config_path = get_config_file("layouts"); - char file[NUM_CHARS] = ""; - strcpy(file, ""); - join_path(file, config_path); - join_path(file, name); - join_path(file, "init.lua"); - if (config_path) - free(config_path); - - if (!file_exists(file)) - return; - - if (luaL_loadfile(L, file)) { - lua_pop(L, 1); - return; - } - lua_call_safe(L, 0, 0, 0); -} - -void reset_loaded_layout(struct workspace *ws) -{ - int length = ws->loaded_layouts.length; - for (int i = 0; i < length; i++) { - struct layout *lt = ws->loaded_layouts.items[0]; - destroy_layout(lt); - wlr_list_del(&ws->loaded_layouts, 0); - } -} - -void remove_loaded_layouts(struct wlr_list *workspaces) -{ - for (int i = 0; i < workspaces->length; i++) { - struct workspace *ws = workspaces->items[i]; - wlr_list_clear(&ws->loaded_layouts, (void (*)(void *))destroy_layout); - } -} - -void push_workspace(struct monitor *m, struct workspace *ws) -{ - if (!m) - return; - if (!ws) - return; - - if (m->ws_id == ws->id) - return; - - if (m->ws_id != server.previous_workspace_id) - server.previous_workspace_id = m->ws_id; - - focus_workspace(m, ws); -} - -void push_layout(struct workspace *ws, struct layout *lt) -{ - lt->ws_id = ws->id; - ws->previous_layout = ws->layout; - ws->layout = lt; -} diff --git a/src/xdg_shell.c b/src/xdg_shell.c index e76a82bd..8e0c66e9 100644 --- a/src/xdg_shell.c +++ b/src/xdg_shell.c @@ -6,11 +6,17 @@ #include "client.h" #include "popup.h" +#include "server.h" +#include "utils/coreUtils.h" +#include "container.h" +#include "tile/tileUtils.h" +#include "workspace.h" +#include "rules/rule.h" static void destroyxdeco(struct wl_listener *listener, void *data); static void getxdecomode(struct wl_listener *listener, void *data); -void destroyxdeco(struct wl_listener *listener, void *data) +static void destroyxdeco(struct wl_listener *listener, void *data) { struct wlr_xdg_toplevel_decoration_v1 *wlr_deco = data; Decoration *d = wlr_deco->data; @@ -33,35 +39,80 @@ void create_notify_xdg(struct wl_listener *listener, void *data) * client, either a toplevel (application window) or popup. */ struct wlr_xdg_surface *xdg_surface = data; - if (xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) return; - union surface_t surface; surface.xdg = xdg_surface; /* Allocate a Client for this surface */ struct client *c = xdg_surface->data = create_client(XDG_SHELL, surface); - /* Tell the client not to try anything fancy */ wlr_xdg_toplevel_set_tiled(c->surface.xdg, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT); /* Listen to the various events it can emit */ - c->map.notify = maprequest; - wl_signal_add(&xdg_surface->events.map, &c->map); - c->unmap.notify = unmap_notify; - wl_signal_add(&xdg_surface->events.unmap, &c->unmap); - c->destroy.notify = destroy_notify; - wl_signal_add(&xdg_surface->events.destroy, &c->destroy); - /* popups */ - c->new_popup.notify = popup_handle_new_popup; - wl_signal_add(&xdg_surface->events.new_popup, &c->new_popup); - c->set_title.notify = client_handle_set_title; - wl_signal_add(&xdg_surface->toplevel->events.set_title, &c->set_title); - c->set_app_id.notify = client_handle_set_app_id; - wl_signal_add(&xdg_surface->toplevel->events.set_app_id, &c->set_app_id); + LISTEN(&xdg_surface->events.map, &c->map, map_request); + LISTEN(&xdg_surface->surface->events.commit, &c->commit, commit_notify); + LISTEN(&xdg_surface->events.unmap, &c->unmap, unmap_notify); + LISTEN(&xdg_surface->events.destroy, &c->destroy, destroy_notify); + + LISTEN(&xdg_surface->toplevel->events.set_title, &c->set_title, client_handle_set_title); + LISTEN(&xdg_surface->toplevel->events.set_app_id, &c->set_app_id, client_handle_set_app_id); + + LISTEN(&xdg_surface->events.new_popup, &c->new_popup, popup_handle_new_popup); + + create_container(c, selected_monitor, true); +} + +void destroy_notify(struct wl_listener *listener, void *data) +{ + struct client *c = wl_container_of(listener, c, destroy); + + wl_list_remove(&c->map.link); + wl_list_remove(&c->commit.link); + wl_list_remove(&c->unmap.link); + wl_list_remove(&c->destroy.link); + + wl_list_remove(&c->set_title.link); + wl_list_remove(&c->set_app_id.link); + + wl_list_remove(&c->new_popup.link); + + destroy_container(c->con); + destroy_client(c); +} + +void map_request(struct wl_listener *listener, void *data) +{ + /* Called when the surface is mapped, or ready to display on-screen. */ + struct client *c = wl_container_of(listener, c, map); + struct workspace *ws = get_workspace(c->ws_id); + c->bw = ws->layout->options.tile_border_px; + + g_ptr_array_add(server.normal_clients, c); + + struct container *con = c->con; + add_container_to_tile(con); + arrange(); + struct monitor *m = container_get_monitor(con); + focus_most_recent_container(m->tagset); +} + +void unmap_notify(struct wl_listener *listener, void *data) +{ + /* Called when the surface is unmapped, and should no longer be shown. */ + struct client *c = wl_container_of(listener, c, unmap); + + struct container *con = c->con; + container_damage_whole(c->con); + remove_container_from_tile(con); + + remove_in_composed_list(server.client_lists, cmp_ptr, c); + + arrange(); + struct monitor *m = selected_monitor; + focus_most_recent_container(m->tagset); } void createxdeco(struct wl_listener *listener, void *data) diff --git a/src/xwayland.c b/src/xwayland.c index 674e7fae..d6953703 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -3,11 +3,13 @@ #include #include -#include "clipboard.h" #include "container.h" #include "server.h" #include "tile/tileUtils.h" +#include "seat.h" +#include "workspace.h" +#if JAPOKWM_HAS_XWAYLAND static const char *atom_map[ATOM_LAST] = { "_NET_WM_WINDOW_TYPE_NORMAL", "_NET_WM_WINDOW_TYPE_DIALOG", @@ -22,6 +24,16 @@ static const char *atom_map[ATOM_LAST] = { "_NET_WM_WINDOW_TYPE_NOTIFICATION", "_NET_WM_STATE_MODAL", }; +#endif + +static void activatex11(struct wl_listener *listener, void *data) +{ + struct client *c = wl_container_of(listener, c, activate); + + /* Only "managed" windows can be activated */ + if (c->type == X11_MANAGED) + wlr_xwayland_surface_activate(c->surface.xwayland, true); +} void create_notifyx11(struct wl_listener *listener, void *data) { @@ -34,18 +46,37 @@ void create_notifyx11(struct wl_listener *listener, void *data) // set default value will be overriden on maprequest /* Listen to the various events it can emit */ - c->map.notify = maprequestx11; - wl_signal_add(&xwayland_surface->events.map, &c->map); - c->unmap.notify = unmap_notify; - wl_signal_add(&xwayland_surface->events.unmap, &c->unmap); - c->destroy.notify = destroy_notify; - wl_signal_add(&xwayland_surface->events.destroy, &c->destroy); - c->set_title.notify = client_handle_set_title; - wl_signal_add(&xwayland_surface->events.set_title, &c->set_title); + LISTEN(&xwayland_surface->events.map, &c->map, maprequestx11); + LISTEN(&xwayland_surface->events.unmap, &c->unmap, unmap_notifyx11); + LISTEN(&xwayland_surface->events.destroy, &c->destroy, destroy_notifyx11); + LISTEN(&xwayland_surface->events.set_title, &c->set_title, client_handle_set_title); + LISTEN(&xwayland_surface->events.request_activate, &c->activate, activatex11); + + create_container(c, selected_monitor, true); +} + +void destroy_notifyx11(struct wl_listener *listener, void *data) +{ + struct client *c = wl_container_of(listener, c, destroy); + + + struct container *con = c->con; + if (con->is_xwayland_popup) { + g_ptr_array_remove(server.xwayland_popups, con); + } + destroy_container(c->con); + + wl_list_remove(&c->map.link); + wl_list_remove(&c->unmap.link); + wl_list_remove(&c->destroy.link); + wl_list_remove(&c->set_title.link); + + destroy_client(c); } void handle_xwayland_ready(struct wl_listener *listener, void *data) { +#if JAPOKWM_HAS_XWAYLAND struct server *server = wl_container_of(listener, server, xwayland_ready); struct xwayland *xwayland = &server->xwayland; @@ -53,7 +84,7 @@ void handle_xwayland_ready(struct wl_listener *listener, void *data) xcb_connection_t *xcb_conn = xcb_connect(NULL, NULL); int err = xcb_connection_has_error(xcb_conn); if (err) { - wlr_log(WLR_ERROR, "XCB connect failed: %d", err); + debug_print("XCB connect failed: %d\n", err); return; } @@ -72,7 +103,7 @@ void handle_xwayland_ready(struct wl_listener *listener, void *data) free(reply); if (error != NULL) { - wlr_log(WLR_ERROR, "could not resolve atom %s, X11 error code %d", + debug_print("could not resolve atom %s, X11 error code %d\n", atom_map[i], error->error_code); free(error); break; @@ -80,6 +111,25 @@ void handle_xwayland_ready(struct wl_listener *listener, void *data) } xcb_disconnect(xcb_conn); +#endif +} + +void unmap_notifyx11(struct wl_listener *listener, void *data) +{ + /* Called when the surface is unmapped, and should no longer be shown. */ + struct client *c = wl_container_of(listener, c, unmap); + + wl_list_remove(&c->commit.link); + + struct container *con = c->con; + container_damage_whole(c->con); + remove_container_from_tile(con); + + remove_in_composed_list(server.client_lists, cmp_ptr, c); + + arrange(); + struct monitor *m = selected_monitor; + focus_most_recent_container(m->tagset); } void maprequestx11(struct wl_listener *listener, void *data) @@ -91,10 +141,12 @@ void maprequestx11(struct wl_listener *listener, void *data) struct layout *lt = get_layout_in_monitor(m); c->type = xwayland_surface->override_redirect ? X11_UNMANAGED : X11_MANAGED; - c->ws_id = m->ws_id; + c->ws_id = m->tagset->selected_ws_id; c->bw = lt->options.tile_border_px; - struct container *con = create_container(c, m, true); + struct container *con = c->con; + add_container_to_tile(con); + LISTEN(&xwayland_surface->surface->events.commit, &c->commit, commit_notify); struct wlr_box prefered_geom = (struct wlr_box) { .x = c->surface.xwayland->x, @@ -128,26 +180,29 @@ void maprequestx11(struct wl_listener *listener, void *data) switch (c->type) { case X11_MANAGED: { - wlr_list_push(&server.normal_clients, c); + g_ptr_array_add(server.normal_clients, c); con->on_top = false; - if (wants_floating(con->client)) { - set_container_floating(con, fix_position, true); + if (x11_wants_floating(con->client)) { + set_container_floating(con, container_fix_position, true); resize(con, prefered_geom); } break; } case X11_UNMANAGED: { - wlr_list_push(&server.independent_clients, c); + g_ptr_array_add(server.independent_clients, c); struct workspace *ws = monitor_get_active_workspace(m); - if (is_popup_menu(c) || xwayland_surface->parent) { - remove_in_composed_list(&ws->focus_stack_lists, cmp_ptr, con); - wlr_list_insert(&ws->focus_stack_normal, 1, con); + if (x11_is_popup_menu(c) || xwayland_surface->parent) { + remove_in_composed_list(ws->list_set->focus_stack_lists, cmp_ptr, con); + g_ptr_array_insert(ws->list_set->focus_stack_normal, 0, con); + + con->is_xwayland_popup = true; + g_ptr_array_add(server.xwayland_popups, con); } else { con->on_top = true; - focus_container(con, FOCUS_NOOP); + focus_container(con); } con->has_border = false; @@ -161,7 +216,91 @@ void maprequestx11(struct wl_listener *listener, void *data) } arrange(); struct container *sel = get_focused_container(m); - focus_container(sel, FOCUS_NOOP); - wlr_xcursor_manager_set_cursor_image(server.cursor_mgr, - "left_ptr", server.cursor.wlr_cursor); + focus_container(sel); + struct seat *seat = input_manager_get_default_seat(); + wlr_xcursor_manager_set_cursor_image(seat->cursor->xcursor_mgr, + "left_ptr", seat->cursor->wlr_cursor); +} + +bool xwayland_popups_exist() +{ + return server.xwayland_popups->len > 0; +} + +bool x11_wants_floating(struct client *c) +{ + if (c->type != X11_MANAGED && c->type != X11_UNMANAGED) + return false; + + struct wlr_xwayland_surface *surface = c->surface.xwayland; + if (surface->modal) + return true; + +#if JAPOKWM_HAS_XWAYLAND + struct xwayland xwayland = server.xwayland; + for (size_t i = 0; i < surface->window_type_len; ++i) { + xcb_atom_t type = surface->window_type[i]; + if (type == xwayland.atoms[NET_WM_WINDOW_TYPE_DIALOG] || + type == xwayland.atoms[NET_WM_WINDOW_TYPE_UTILITY] || + type == xwayland.atoms[NET_WM_WINDOW_TYPE_TOOLBAR] || + type == xwayland.atoms[NET_WM_WINDOW_TYPE_POPUP_MENU] || + type == xwayland.atoms[NET_WM_WINDOW_TYPE_SPLASH]) { + return true; + } + } +#endif + + struct wlr_xwayland_surface_size_hints *size_hints = surface->size_hints; + if (size_hints != NULL && + size_hints->min_width > 0 && size_hints->min_height > 0 && + (size_hints->max_width == size_hints->min_width || + size_hints->max_height == size_hints->min_height)) { + return true; + } + + return false; +} + +bool x11_is_popup_menu(struct client *c) +{ +#if JAPOKWM_HAS_XWAYLAND + struct wlr_xwayland_surface *surface = c->surface.xwayland; + struct xwayland xwayland = server.xwayland; + for (size_t i = 0; i < surface->window_type_len; ++i) { + xcb_atom_t type = surface->window_type[i]; + if (type == xwayland.atoms[NET_WM_WINDOW_TYPE_POPUP_MENU] || + type == xwayland.atoms[NET_WM_WINDOW_TYPE_POPUP] || + type == xwayland.atoms[NET_WM_WINDOW_TYPE_MENU] || + type == xwayland.atoms[NET_WM_WINDOW_TYPE_NORMAL]) { + return true; + } + } +#endif + return false; +} + + +void init_xwayland(struct wl_display *display, struct seat *seat) +{ +#if JAPOKWM_HAS_XWAYLAND + /* + * Initialise the XWayland X server. + * It will be started when the first X client is started. + */ + server.xwayland.wlr_xwayland = wlr_xwayland_create(server.wl_display, + server.compositor, true); + if (server.xwayland.wlr_xwayland) { + server.xwayland_ready.notify = handle_xwayland_ready; + wl_signal_add(&server.xwayland.wlr_xwayland->events.ready, + &server.xwayland_ready); + wl_signal_add(&server.xwayland.wlr_xwayland->events.new_surface, + &server.new_xwayland_surface); + wlr_xwayland_set_seat(server.xwayland.wlr_xwayland, seat->wlr_seat); + + setenv("DISPLAY", server.xwayland.wlr_xwayland->display_name, true); + } else { + printf("failed to setup XWayland X server, continuing without it"); + unsetenv("DISPLAY"); + } +#endif } diff --git a/test/bitset_test.c b/test/bitset_test.c new file mode 100644 index 00000000..af74d0e6 --- /dev/null +++ b/test/bitset_test.c @@ -0,0 +1,63 @@ +#include +#include + +#include "bitset/bitset.h" + +void test_bitset() +{ + BitSet *bitset = bitset_create(8); + int bit = bitset_test(bitset, 0); + g_assert_cmpint(bit, ==, 0); +} + +void test_move_bitset() +{ + BitSet *bitset1 = bitset_create(8); + BitSet *bitset2 = bitset_create(8); + bitset_move(bitset1, bitset2); + int bit = bitset_test(bitset1, 0); + g_assert_cmpint(bit, ==, 0); +} + +void test_copy_bitset() +{ + BitSet *bitset1 = bitset_create(8); + BitSet *bitset2 = bitset_copy(bitset1); + int bit = bitset_test(bitset2, 0); + g_assert_cmpint(bit, ==, 0); +} + +void test_bitset_assign() +{ + BitSet *bitset1 = bitset_create(8); + bitset_assign(bitset1, 0, 1); + g_assert_cmpint(bitset_test(bitset1, 0), ==, 1); +} + +void test_from_value() +{ + /* // 42 == 0b101010 */ + /* BitSet *bitset1 = bitset_from_value_reversed(42, 6); */ + /* g_assert_cmpint(bitset_test(bitset1, 0), ==, 1); */ + /* g_assert_cmpint(bitset_test(bitset1, 1), ==, 0); */ + /* g_assert_cmpint(bitset_test(bitset1, 2), ==, 1); */ + /* g_assert_cmpint(bitset_test(bitset1, 3), ==, 0); */ + /* g_assert_cmpint(bitset_test(bitset1, 4), ==, 1); */ + /* g_assert_cmpint(bitset_test(bitset1, 5), ==, 0); */ +} + +#define PREFIX "bitset" +#define add_test(func) g_test_add_func("/"PREFIX"/"#func, func) +int main(int argc, char** argv) +{ + setbuf(stdout, NULL); + g_test_init(&argc, &argv, NULL); + + add_test(test_bitset); + add_test(test_move_bitset); + add_test(test_copy_bitset); + add_test(test_bitset_assign); + /* add_test(test_from_value); */ + + return g_test_run(); +} diff --git a/test/container_test.c b/test/container_test.c index 196f026b..a72a0a84 100644 --- a/test/container_test.c +++ b/test/container_test.c @@ -1,4 +1,4 @@ -#include +#include #include "monitor.h" #include "container.h" @@ -6,163 +6,137 @@ #include "utils/coreUtils.h" #include "server.h" -START_TEST(test_visible_on) +void test_visible_on() { - struct wlr_list tag_names; - wlr_list_init(&tag_names); - wlr_list_push(&tag_names, "1"); - wlr_list_push(&tag_names, "2"); - wlr_list_push(&tag_names, "3"); - wlr_list_push(&tag_names, "4"); - - struct layout lt; - lt.options.arrange_by_focus = false; - struct wlr_list workspaces; - create_workspaces(&workspaces, &tag_names, <); - - struct monitor m0; - struct monitor m1; - - struct workspace *ws0 = workspaces.items[0]; - struct workspace *ws1 = workspaces.items[1]; - ws0->m = &m0; - ws1->m = &m1; - - struct client c; - struct container con = { - .client = &c - }; - - con.m = &m0; - con.client->ws_id = ws0->id; - con.hidden = false; - ck_assert_int_eq(visible_on(&con, ws0), true); - - con.m = &m1; - con.client->ws_id = ws0->id; - con.hidden = false; - ck_assert_int_eq(visible_on(&con, ws0), false); - - con.m = &m0; - con.client->ws_id = ws1->id; - con.hidden = false; - ck_assert_int_eq(visible_on(&con, ws1), false); - - con.m = &m1; - con.client->ws_id = ws1->id; - con.hidden = true; - ck_assert_int_eq(visible_on(&con, ws1), false); -} END_TEST - -START_TEST(test_exist_on) + /* struct wlr_list tag_names; */ + /* wlr_list_init(&tag_names); */ + /* wlr_list_push(&tag_names, "1"); */ + /* wlr_list_push(&tag_names, "2"); */ + /* wlr_list_push(&tag_names, "3"); */ + /* wlr_list_push(&tag_names, "4"); */ + + /* struct layout lt; */ + /* lt.options.arrange_by_focus = false; */ + /* struct wlr_list workspaces; */ + /* create_workspaces(&workspaces, &tag_names); */ + + /* struct monitor m0; */ + /* struct monitor m1; */ + + /* struct workspace *ws0 = workspaces.items[0]; */ + /* struct workspace *ws1 = workspaces.items[1]; */ + /* ws0->m = &m0; */ + /* ws1->m = &m1; */ + + /* struct client c; */ + /* struct container con = { */ + /* .client = &c */ + /* }; */ + + /* con.m = &m0; */ + /* con.client->ws_id = ws0->id; */ + /* con.hidden = false; */ + /* ck_assert_int_eq(visible_on(&con, ws0), true); */ + + /* con.m = &m1; */ + /* con.client->ws_id = ws0->id; */ + /* con.hidden = false; */ + /* ck_assert_int_eq(visible_on(&con, ws0), false); */ + + /* con.m = &m0; */ + /* con.client->ws_id = ws1->id; */ + /* con.hidden = false; */ + /* ck_assert_int_eq(visible_on(&con, ws1), false); */ + + /* con.m = &m1; */ + /* con.client->ws_id = ws1->id; */ + /* con.hidden = true; */ + /* ck_assert_int_eq(visible_on(&con, ws1), false); */ +} + +void test_exist_on() { - struct wlr_list tag_names; - wlr_list_init(&tag_names); - wlr_list_push(&tag_names, "1"); - wlr_list_push(&tag_names, "2"); - wlr_list_push(&tag_names, "3"); - wlr_list_push(&tag_names, "4"); - - struct layout lt; - lt.options.arrange_by_focus = false; - struct wlr_list workspaces; - create_workspaces(&workspaces, &tag_names, <); - - struct monitor m0; - struct monitor m1; - - struct workspace *ws0 = workspaces.items[0]; - struct workspace *ws1 = workspaces.items[1]; - - ws0->m = &m0; - ws1->m = &m1; - - struct client c; - struct container con = { - .client = &c - }; - - con.m = &m0; - con.client->ws_id = ws0->id; - con.hidden = true; - ck_assert_int_eq(exist_on(&con, ws0), true); - - con.m = &m1; - con.client->ws_id = ws0->id; - con.hidden = false; - ck_assert_int_eq(exist_on(&con, ws0), false); - - con.m = &m0; - con.client->ws_id = ws1->id; - con.hidden = false; - ck_assert_int_eq(exist_on(&con, ws1), false); - - con.m = &m1; - con.client->ws_id = ws1->id; - con.hidden = false; - ck_assert_int_eq(exist_on(&con, ws1), true); -} END_TEST - -START_TEST(focus_on_hidden_stack_test) + /* struct wlr_list tag_names; */ + /* wlr_list_init(&tag_names); */ + /* wlr_list_push(&tag_names, "1"); */ + /* wlr_list_push(&tag_names, "2"); */ + /* wlr_list_push(&tag_names, "3"); */ + /* wlr_list_push(&tag_names, "4"); */ + + /* struct layout lt; */ + /* lt.options.arrange_by_focus = false; */ + /* struct wlr_list workspaces; */ + /* create_workspaces(&workspaces, &tag_names, <); */ + + /* struct monitor m0; */ + /* struct monitor m1; */ + + /* struct workspace *ws0 = workspaces.items[0]; */ + /* struct workspace *ws1 = workspaces.items[1]; */ + + /* ws0->m = &m0; */ + /* ws1->m = &m1; */ + + /* struct client c; */ + /* struct container con = { */ + /* .client = &c */ + /* }; */ + + /* con.m = &m0; */ + /* con.client->ws_id = ws0->id; */ + /* con.hidden = true; */ + /* ck_assert_int_eq(exist_on(&con, ws0), true); */ + + /* con.m = &m1; */ + /* con.client->ws_id = ws0->id; */ + /* con.hidden = false; */ + /* ck_assert_int_eq(exist_on(&con, ws0), false); */ + + /* con.m = &m0; */ + /* con.client->ws_id = ws1->id; */ + /* con.hidden = false; */ + /* ck_assert_int_eq(exist_on(&con, ws1), false); */ + + /* con.m = &m1; */ + /* con.client->ws_id = ws1->id; */ + /* con.hidden = false; */ + /* ck_assert_int_eq(exist_on(&con, ws1), true); */ +} + +void focus_on_hidden_stack_test() { // TODO fix this unittest -} END_TEST +} -START_TEST(focus_container_test) +void focus_container_test() { -} END_TEST +} -START_TEST(get_position_in_container_stack_crash_test) +void get_position_in_container_stack_crash_test() { get_position_in_container_stack(NULL); -} END_TEST - -START_TEST(get_focused_container_crash_test) -{ - get_focused_container(NULL); - - struct monitor m; - m.ws_id = -1; - get_focused_container(&m); - - m.ws_id = 700; - get_focused_container(&m); -} END_TEST +} -Suite *suite() +void get_focused_container_crash_test() { - Suite *s; - TCase *tc; + /* get_focused_container(NULL); */ - s = suite_create("container"); - tc = tcase_create("core"); + /* struct monitor m; */ + /* m.tagset = -1; */ + /* get_focused_container(&m); */ - tcase_add_test(tc, test_visible_on); - tcase_add_test(tc, test_exist_on); - tcase_add_test(tc, focus_on_hidden_stack_test); - tcase_add_test(tc, focus_container_test); - tcase_add_test(tc, get_position_in_container_stack_crash_test); - tcase_add_test(tc, get_focused_container_crash_test); - suite_add_tcase(s, tc); - - return s; + /* m.tagset = 700; */ + /* get_focused_container(&m); */ } -int main() +#define PREFIX "container" +#define add_test(func) g_test_add_func("/"PREFIX"/"#func, func) +int main(int argc, char **argv) { setbuf(stdout, NULL); + g_test_init(&argc, &argv, NULL); - int number_failed; - Suite *s; - SRunner *sr; - - s = suite(); - sr = srunner_create(s); - - srunner_run_all(sr, CK_NORMAL); - srunner_ntests_run(sr); - number_failed = srunner_ntests_failed(sr); - srunner_free(sr); + add_test(test_visible_on); - return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; + return g_test_run(); } diff --git a/test/meson.build b/test/meson.build index babc3955..30e9fbfe 100644 --- a/test/meson.build +++ b/test/meson.build @@ -1,8 +1,8 @@ # unit test setup -extraLibs = [dependency('check')] c_args = ['-I../include'] test_files = files( + 'tagset_test.c', 'container_test.c', 'tile/tileUtils_test.c', 'utils/coreUtils_test.c', @@ -10,6 +10,7 @@ test_files = files( 'utils/stringUtils_test.c', 'workspace_test.c', 'scratchpad_test.c', + 'bitset_test.c' ) foreach test_file: test_files @@ -21,8 +22,8 @@ foreach test_file: test_files t = executable(test_file_name, [test_file], c_args: c_args, - dependencies: [deps, extraLibs], - include_directories: inc, + dependencies: [deps], + include_directories: include_dirs, link_with: [wmlib], ) diff --git a/test/scratchpad_test.c b/test/scratchpad_test.c index 90ea500a..b563820a 100644 --- a/test/scratchpad_test.c +++ b/test/scratchpad_test.c @@ -1,41 +1,21 @@ #include -#include +#include #include "scratchpad.h" -START_TEST(show_scratchpad_crash_test) +void tagset_connect_workspace_testestace() { - show_scratchpad(); -} END_TEST - -Suite *suite() -{ - Suite *s; - TCase *tc; - - s = suite_create("scratchpad"); - tc = tcase_create("Core"); - - tcase_add_test(tc, show_scratchpad_crash_test); - - suite_add_tcase(s, tc); - - return s; + /* show_scratchpad(); */ } -int main() +#define PREFIX "scratchpad" +#define add_test(func) g_test_add_func("/"PREFIX"/"#func, func) +int main(int argc, char **argv) { - int numberFailed; - Suite *s; - SRunner *sr; - - s = suite(); - sr = srunner_create(s); + setbuf(stdout, NULL); + g_test_init(&argc, &argv, NULL); - srunner_run_all(sr, CK_NORMAL); - srunner_ntests_run(sr); - numberFailed = srunner_ntests_failed(sr); - srunner_free(sr); + add_test(tagset_connect_workspace_testestace); - return (numberFailed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; + return g_test_run(); } diff --git a/test/tagset_test.c b/test/tagset_test.c new file mode 100644 index 00000000..16fdee2f --- /dev/null +++ b/test/tagset_test.c @@ -0,0 +1,37 @@ +#include +#include + +#include "monitor.h" +#include "tagset.h" +#include "workspace.h" + +#define WORKSPACE_COUNT 4 + +void tagset_connect_workspace_test() +{ + struct tagset tagset1; + /* struct workspace ws[WORKSPACE_COUNT]; */ + +/* ws[0].tagset = NULL; */ +/* ws[0].selected_tagset = NULL; */ + + tagset1.workspaces = bitset_create(WORKSPACE_COUNT); + tagset1.loaded_workspaces = bitset_create(WORKSPACE_COUNT); + + bitset_set(tagset1.workspaces, 0); + + bitset_destroy(tagset1.workspaces); + bitset_destroy(tagset1.loaded_workspaces); +} + +#define PREFIX "tagset" +#define add_test(func) g_test_add_func("/"PREFIX"/"#func, func) +int main(int argc, char **argv) +{ + setbuf(stdout, NULL); + g_test_init(&argc, &argv, NULL); + + add_test(tagset_connect_workspace_test); + + return g_test_run(); +} diff --git a/test/tile/tileUtils_test.c b/test/tile/tileUtils_test.c index 5b943a77..d46b4bf5 100644 --- a/test/tile/tileUtils_test.c +++ b/test/tile/tileUtils_test.c @@ -1,110 +1,89 @@ #include -#include +#include #include "server.h" #include "tile/tileUtils.h" -START_TEST(get_container_count_test) +void get_container_count_test() { - struct wlr_list tag_names; - wlr_list_init(&tag_names); - wlr_list_push(&tag_names, "1"); - wlr_list_push(&tag_names, "2"); - struct layout lt; - create_workspaces(&server.workspaces, &tag_names, <); - - struct monitor m0, m1; - struct workspace *ws0 = get_workspace(0); - ws0->m = &m0; - struct workspace *ws1 = get_workspace(1); - ws1->m = &m1; - - const int container_count = 3; - struct client clients[container_count]; - for (int i = 0; i < container_count; i++) { - clients[0].type = XDG_SHELL; - clients[0].sticky = false; - } - - struct container cons[container_count]; - for (int i = 0; i < container_count; i++) { - cons[i].client = &clients[i]; - cons[i].floating = false; - cons[i].m = &m0; - } - - clients[0].ws_id = 0; - wlr_list_push(&ws0->tiled_containers, &cons[0]); - clients[1].ws_id = 0; - wlr_list_push(&ws0->tiled_containers, &cons[1]); - clients[2].ws_id = 1; - wlr_list_push(&ws1->tiled_containers, &cons[2]); - ck_assert_int_eq(get_container_count(ws0), 2); -} END_TEST - -START_TEST(get_relative_item_test) -{ - struct wlr_list lists; - - struct wlr_list list1; - struct wlr_list list2; - struct wlr_list list3; - - wlr_list_init(&lists); - - wlr_list_init(&list1); - wlr_list_init(&list2); - wlr_list_init(&list3); - - wlr_list_push(&lists, &list1); - wlr_list_push(&lists, &list2); - wlr_list_push(&lists, &list3); + /* struct wlr_list tag_names; */ + /* wlr_list_init(&tag_names); */ + /* wlr_list_push(&tag_names, "1"); */ + /* wlr_list_push(&tag_names, "2"); */ + /* create_workspaces(&server.workspaces, &tag_names); */ + + /* struct monitor m0, m1; */ + /* struct tagset *tagset0 = get_tagset_from_workspace_id(&server.workspaces, 0); */ + /* tagset0->m = &m0; */ + /* struct tagset *tagset1 = get_tagset_from_workspace_id(&server.workspaces, 1); */ + /* tagset1->m = &m1; */ + + /* const int container_count = 3; */ + /* struct client clients[container_count]; */ + /* for (int i = 0; i < container_count; i++) { */ + /* clients[0].type = XDG_SHELL; */ + /* clients[0].sticky = false; */ + /* } */ + + /* struct container cons[container_count]; */ + /* for (int i = 0; i < container_count; i++) { */ + /* cons[i].client = &clients[i]; */ + /* cons[i].floating = false; */ + /* cons[i].m = &m0; */ + /* } */ + + /* clients[0].ws_id = 0; */ + /* wlr_list_push(&tagset0->list_set.tiled_containers, &cons[0]); */ + /* clients[1].ws_id = 0; */ + /* wlr_list_push(&tagset0->list_set.tiled_containers, &cons[1]); */ + /* clients[2].ws_id = 1; */ + /* wlr_list_push(&tagset1->list_set.tiled_containers, &cons[2]); */ + /* ck_assert_int_eq(get_container_count(tagset0), 2); */ +} - wlr_list_push(&list1, "0"); - wlr_list_push(&list1, "1"); +void get_relative_item_test() +{ + GPtrArray *lists; - wlr_list_push(&list1, "2"); - wlr_list_push(&list2, "3"); - wlr_list_push(&list2, "4"); - wlr_list_push(&list2, "5"); + GPtrArray *list1; + GPtrArray *list2; + GPtrArray *list3; - wlr_list_push(&list3, "6"); - wlr_list_push(&list3, "7"); - wlr_list_push(&list3, "8"); + lists = g_ptr_array_new(); - ck_assert_str_eq(get_relative_item_in_composed_list(&lists, 4, 1), "5"); -} END_TEST + list1 = g_ptr_array_new(); + list2 = g_ptr_array_new(); + list3 = g_ptr_array_new(); + g_ptr_array_add(lists, list1); + g_ptr_array_add(lists, list2); + g_ptr_array_add(lists, list3); -Suite *suite() -{ - Suite *s; - TCase *tc; + g_ptr_array_add(list1, "0"); + g_ptr_array_add(list1, "1"); - s = suite_create("tileUtils"); - tc = tcase_create("core"); + g_ptr_array_add(list1, "2"); + g_ptr_array_add(list2, "3"); + g_ptr_array_add(list2, "4"); + g_ptr_array_add(list2, "5"); - tcase_add_test(tc, get_container_count_test); - tcase_add_test(tc, get_relative_item_test); - suite_add_tcase(s, tc); + g_ptr_array_add(list3, "6"); + g_ptr_array_add(list3, "7"); + g_ptr_array_add(list3, "8"); - return s; + g_assert_cmpstr(get_relative_item_in_composed_list(lists, 4, 1), ==, "5"); } -int main() +#define PREFIX "tileUtils" +#define add_test(func) g_test_add_func("/"PREFIX"/"#func, func) +int main(int argc, char **argv) { setbuf(stdout, NULL); - int numberFailed; - Suite *s; - SRunner *sr; + g_test_init(&argc, &argv, NULL); - s = suite(); - sr = srunner_create(s); - srunner_run_all(sr, CK_NORMAL); - srunner_ntests_run(sr); - numberFailed = srunner_ntests_failed(sr); - srunner_free(sr); + add_test(get_container_count_test); + add_test(get_relative_item_test); - return (numberFailed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; + return g_test_run(); } diff --git a/test/utils/coreUtils_test.c b/test/utils/coreUtils_test.c index f28671be..1d624281 100644 --- a/test/utils/coreUtils_test.c +++ b/test/utils/coreUtils_test.c @@ -1,259 +1,244 @@ -#include +#include #include #include "utils/coreUtils.h" -START_TEST(dir_exists_test) +void dir_exists_test() { - ck_assert_int_eq(dir_exists("/"), true); - ck_assert_int_eq(dir_exists(""), false); -} END_TEST + g_assert_cmpint(dir_exists("/"), ==, true); + g_assert_cmpint(dir_exists(""), ==, false); +} -START_TEST(get_on_composed_list_test) +void get_on_composed_list_test() { - struct wlr_list lists; - - struct wlr_list list1; - struct wlr_list list2; - struct wlr_list list3; - - wlr_list_init(&lists); - - wlr_list_init(&list1); - wlr_list_init(&list2); - wlr_list_init(&list3); - - wlr_list_push(&lists, &list1); - wlr_list_push(&lists, &list2); - wlr_list_push(&lists, &list3); - - wlr_list_push(&list1, "0"); - wlr_list_push(&list1, "1"); - - wlr_list_push(&list1, "2"); - wlr_list_push(&list2, "3"); - wlr_list_push(&list2, "4"); - wlr_list_push(&list2, "5"); - - wlr_list_push(&list3, "6"); - wlr_list_push(&list3, "7"); - wlr_list_push(&list3, "8"); - - ck_assert_str_eq(get_in_composed_list(&lists, 0), "0"); - ck_assert_str_eq(get_in_composed_list(&lists, 1), "1"); - ck_assert_str_eq(get_in_composed_list(&lists, 2), "2"); - ck_assert_str_eq(get_in_composed_list(&lists, 3), "3"); - ck_assert_str_eq(get_in_composed_list(&lists, 4), "4"); - ck_assert_str_eq(get_in_composed_list(&lists, 5), "5"); - ck_assert_str_eq(get_in_composed_list(&lists, 6), "6"); - ck_assert_str_eq(get_in_composed_list(&lists, 7), "7"); - ck_assert_str_eq(get_in_composed_list(&lists, 8), "8"); -} END_TEST - -START_TEST(remove_from_composed_list_test) + GPtrArray *lists; + + GPtrArray *list1; + GPtrArray *list2; + GPtrArray *list3; + + lists = g_ptr_array_new(); + + list1 = g_ptr_array_new(); + list2 = g_ptr_array_new(); + list3 = g_ptr_array_new(); + + g_ptr_array_add(lists, list1); + g_ptr_array_add(lists, list2); + g_ptr_array_add(lists, list3); + + g_ptr_array_add(list1, "0"); + g_ptr_array_add(list1, "1"); + + g_ptr_array_add(list1, "2"); + g_ptr_array_add(list2, "3"); + g_ptr_array_add(list2, "4"); + g_ptr_array_add(list2, "5"); + + g_ptr_array_add(list3, "6"); + g_ptr_array_add(list3, "7"); + g_ptr_array_add(list3, "8"); + + g_assert_cmpstr(get_in_composed_list(lists, 0), ==, "0"); + g_assert_cmpstr(get_in_composed_list(lists, 1), ==, "1"); + g_assert_cmpstr(get_in_composed_list(lists, 2), ==, "2"); + g_assert_cmpstr(get_in_composed_list(lists, 3), ==, "3"); + g_assert_cmpstr(get_in_composed_list(lists, 4), ==, "4"); + g_assert_cmpstr(get_in_composed_list(lists, 5), ==, "5"); + g_assert_cmpstr(get_in_composed_list(lists, 6), ==, "6"); + g_assert_cmpstr(get_in_composed_list(lists, 7), ==, "7"); + g_assert_cmpstr(get_in_composed_list(lists, 8), ==, "8"); +} + +void remove_from_composed_list_test() { - struct wlr_list lists; - - struct wlr_list list1; - struct wlr_list list2; - struct wlr_list list3; - - wlr_list_init(&lists); - - wlr_list_init(&list1); - wlr_list_init(&list2); - wlr_list_init(&list3); - - wlr_list_push(&lists, &list1); - wlr_list_push(&lists, &list2); - wlr_list_push(&lists, &list3); - - wlr_list_push(&list1, "0"); - wlr_list_push(&list1, "1"); - - wlr_list_push(&list2, "2"); - wlr_list_push(&list2, "3"); - wlr_list_push(&list2, "4"); - wlr_list_push(&list2, "5"); - - wlr_list_push(&list3, "6"); - wlr_list_push(&list3, "7"); - wlr_list_push(&list3, "8"); - wlr_list_push(&list3, "9"); - - delete_from_composed_list(&lists, 0); - ck_assert_str_eq(get_in_composed_list(&lists, 0), "1"); - ck_assert_str_eq(get_in_composed_list(&lists, 1), "2"); - wlr_list_insert(&list1, 0, "0"); - - delete_from_composed_list(&lists, 1); - ck_assert_str_eq(get_in_composed_list(&lists, 0), "0"); - ck_assert_str_eq(get_in_composed_list(&lists, 1), "2"); - wlr_list_insert(&list1, 1, "1"); - - delete_from_composed_list(&lists, 2); - ck_assert_str_eq(get_in_composed_list(&lists, 1), "1"); - ck_assert_str_eq(get_in_composed_list(&lists, 2), "3"); - wlr_list_insert(&list2, 0, "2"); - - delete_from_composed_list(&lists, 3); - ck_assert_str_eq(get_in_composed_list(&lists, 2), "2"); - ck_assert_str_eq(get_in_composed_list(&lists, 3), "4"); - wlr_list_insert(&list2, 1, "3"); - - delete_from_composed_list(&lists, 7); - ck_assert_str_eq(get_in_composed_list(&lists, 6), "6"); - ck_assert_str_eq(get_in_composed_list(&lists, 7), "8"); - wlr_list_insert(&list3, 1, "7"); - - delete_from_composed_list(&lists, 8); - ck_assert_str_eq(get_in_composed_list(&lists, 7), "7"); - ck_assert_str_eq(get_in_composed_list(&lists, 8), "9"); - wlr_list_insert(&list3, 2, "8"); -} END_TEST - -START_TEST(wlr_list_remove_in_composed_list_test) + GPtrArray *lists; + + GPtrArray *list1; + GPtrArray *list2; + GPtrArray *list3; + + lists = g_ptr_array_new(); + + list1 = g_ptr_array_new(); + list2 = g_ptr_array_new(); + list3 = g_ptr_array_new(); + + g_ptr_array_add(lists, list1); + g_ptr_array_add(lists, list2); + g_ptr_array_add(lists, list3); + + g_ptr_array_add(list1, "0"); + g_ptr_array_add(list1, "1"); + + g_ptr_array_add(list2, "2"); + g_ptr_array_add(list2, "3"); + g_ptr_array_add(list2, "4"); + g_ptr_array_add(list2, "5"); + + g_ptr_array_add(list3, "6"); + g_ptr_array_add(list3, "7"); + g_ptr_array_add(list3, "8"); + g_ptr_array_add(list3, "9"); + + delete_from_composed_list(lists, 0); + g_assert_cmpstr(get_in_composed_list(lists, 0), ==, "1"); + g_assert_cmpstr(get_in_composed_list(lists, 1), ==, "2"); + g_ptr_array_insert(list1, 0, "0"); + + delete_from_composed_list(lists, 1); + g_assert_cmpstr(get_in_composed_list(lists, 0), ==, "0"); + g_assert_cmpstr(get_in_composed_list(lists, 1), ==, "2"); + g_ptr_array_insert(list1, 1, "1"); + + delete_from_composed_list(lists, 2); + g_assert_cmpstr(get_in_composed_list(lists, 1), ==, "1"); + g_assert_cmpstr(get_in_composed_list(lists, 2), ==, "3"); + g_ptr_array_insert(list2, 0, "2"); + + delete_from_composed_list(lists, 3); + g_assert_cmpstr(get_in_composed_list(lists, 2), ==, "2"); + g_assert_cmpstr(get_in_composed_list(lists, 3), ==, "4"); + g_ptr_array_insert(list2, 1, "3"); + + delete_from_composed_list(lists, 7); + g_assert_cmpstr(get_in_composed_list(lists, 6), ==, "6"); + g_assert_cmpstr(get_in_composed_list(lists, 7), ==, "8"); + g_ptr_array_insert(list3, 1, "7"); + + delete_from_composed_list(lists, 8); + g_assert_cmpstr(get_in_composed_list(lists, 7), ==, "7"); + g_assert_cmpstr(get_in_composed_list(lists, 8), ==, "9"); + g_ptr_array_insert(list3, 2, "8"); +} + +void wlr_list_remove_in_composed_list_test() { - struct wlr_list lists; + GPtrArray *lists; - struct wlr_list list1; - struct wlr_list list2; - struct wlr_list list3; + GPtrArray *list1; + GPtrArray *list2; + GPtrArray *list3; - wlr_list_init(&lists); + lists = g_ptr_array_new(); - wlr_list_init(&list1); - wlr_list_init(&list2); - wlr_list_init(&list3); + list1 = g_ptr_array_new(); + list2 = g_ptr_array_new(); + list3 = g_ptr_array_new(); - wlr_list_push(&lists, &list1); - wlr_list_push(&lists, &list2); - wlr_list_push(&lists, &list3); + g_ptr_array_add(lists, list1); + g_ptr_array_add(lists, list2); + g_ptr_array_add(lists, list3); - wlr_list_push(&list1, "0"); - wlr_list_push(&list1, "1"); + g_ptr_array_add(list1, "0"); + g_ptr_array_add(list1, "1"); - wlr_list_push(&list1, "2"); - wlr_list_push(&list2, "3"); - wlr_list_push(&list2, "4"); - wlr_list_push(&list2, "5"); + g_ptr_array_add(list1, "2"); + g_ptr_array_add(list2, "3"); + g_ptr_array_add(list2, "4"); + g_ptr_array_add(list2, "5"); - wlr_list_push(&list3, "6"); - wlr_list_push(&list3, "7"); - wlr_list_push(&list3, "8"); + g_ptr_array_add(list3, "6"); + g_ptr_array_add(list3, "7"); + g_ptr_array_add(list3, "8"); - remove_in_composed_list(&lists, (int (*)(const void *, const void *))strcmp, "1"); - ck_assert_str_eq(get_in_composed_list(&lists, 1), "2"); -} END_TEST + remove_in_composed_list(lists, (int (*)(const void *, const void *))strcmp, "1"); + g_assert_cmpstr(get_in_composed_list(lists, 1), ==, "2"); +} -START_TEST(wlr_list_find_in_composed_list_test) +void wlr_list_find_in_composed_list_test() { - struct wlr_list lists; + GPtrArray *lists; - struct wlr_list list1; - struct wlr_list list2; - struct wlr_list list3; + GPtrArray *list1; + GPtrArray *list2; + GPtrArray *list3; - wlr_list_init(&lists); + lists = g_ptr_array_new(); - wlr_list_init(&list1); - wlr_list_init(&list2); - wlr_list_init(&list3); + list1 = g_ptr_array_new(); + list2 = g_ptr_array_new(); + list3 = g_ptr_array_new(); - wlr_list_push(&lists, &list1); - wlr_list_push(&lists, &list2); - wlr_list_push(&lists, &list3); + g_ptr_array_add(lists, list1); + g_ptr_array_add(lists, list2); + g_ptr_array_add(lists, list3); - wlr_list_push(&list1, "0"); - wlr_list_push(&list1, "1"); + g_ptr_array_add(list1, "0"); + g_ptr_array_add(list1, "1"); - wlr_list_push(&list2, "2"); - wlr_list_push(&list2, "3"); - wlr_list_push(&list2, "4"); - wlr_list_push(&list2, "5"); + g_ptr_array_add(list2, "2"); + g_ptr_array_add(list2, "3"); + g_ptr_array_add(list2, "4"); + g_ptr_array_add(list2, "5"); - wlr_list_push(&list3, "6"); - wlr_list_push(&list3, "7"); - wlr_list_push(&list3, "8"); + g_ptr_array_add(list3, "6"); + g_ptr_array_add(list3, "7"); + g_ptr_array_add(list3, "8"); - int position = find_in_composed_list(&lists, (int (*)(const void *, const void *))strcmp, "1"); - ck_assert_int_eq(position, 1); -} END_TEST + int position = find_in_composed_list(lists, cmp_str, "1"); + g_assert_cmpint(position, ==, 1); +} -START_TEST(get_relative_item_in_list_test) +void cross_sum_test() { - struct wlr_list lists; - - struct wlr_list list1; - struct wlr_list list2; - struct wlr_list list3; - - wlr_list_init(&lists); - - wlr_list_init(&list1); - wlr_list_init(&list2); - wlr_list_init(&list3); + g_assert_cmpint(cross_sum(0b111, 2), ==, 3); + g_assert_cmpint(cross_sum(100, 10), ==, 1); +} - wlr_list_push(&lists, &list1); - wlr_list_push(&lists, &list2); - wlr_list_push(&lists, &list3); +void get_relative_item_in_list_test() +{ + GPtrArray *lists; - wlr_list_push(&list1, "0"); - wlr_list_push(&list1, "1"); + GPtrArray *list1; + GPtrArray *list2; + GPtrArray *list3; - wlr_list_push(&list2, "2"); - wlr_list_push(&list2, "3"); - wlr_list_push(&list2, "4"); - wlr_list_push(&list2, "5"); + lists = g_ptr_array_new(); - wlr_list_push(&list3, "6"); - wlr_list_push(&list3, "7"); - wlr_list_push(&list3, "8"); + list1 = g_ptr_array_new(); + list2 = g_ptr_array_new(); + list3 = g_ptr_array_new(); - ck_assert_str_eq(get_relative_item_in_list(&list1, 0, -1), "1"); - ck_assert_str_eq(get_relative_item_in_list(&list2, 0, -1), "5"); - ck_assert_str_eq(get_relative_item_in_list(&list3, 0, -1), "8"); + g_ptr_array_add(lists, list1); + g_ptr_array_add(lists, list2); + g_ptr_array_add(lists, list3); - ck_assert_str_eq(get_relative_item_in_composed_list(&lists, 0, 1), "1"); - ck_assert_str_eq(get_relative_item_in_composed_list(&lists, 3, 4), "7"); - ck_assert_str_eq(get_relative_item_in_composed_list(&lists, 3, -2), "1"); -} END_TEST + g_ptr_array_add(list1, "0"); + g_ptr_array_add(list1, "1"); -Suite *suite() -{ - Suite *s; - TCase *tc; + g_ptr_array_add(list2, "2"); + g_ptr_array_add(list2, "3"); + g_ptr_array_add(list2, "4"); + g_ptr_array_add(list2, "5"); - s = suite_create("coreUtils"); - tc = tcase_create("core"); + g_ptr_array_add(list3, "6"); + g_ptr_array_add(list3, "7"); + g_ptr_array_add(list3, "8"); - tcase_add_test(tc, dir_exists_test); - tcase_add_test(tc, get_on_composed_list_test); - tcase_add_test(tc, remove_from_composed_list_test); - tcase_add_test(tc, wlr_list_remove_in_composed_list_test); - tcase_add_test(tc, wlr_list_find_in_composed_list_test); - tcase_add_test(tc, get_relative_item_in_list_test); - suite_add_tcase(s, tc); + g_assert_cmpstr(get_relative_item_in_list(list1, 0, -1), ==, "1"); + g_assert_cmpstr(get_relative_item_in_list(list2, 0, -1), ==, "5"); + g_assert_cmpstr(get_relative_item_in_list(list3, 0, -1), ==, "8"); - return s; + g_assert_cmpstr(get_relative_item_in_composed_list(lists, 0, 1), ==, "1"); + g_assert_cmpstr(get_relative_item_in_composed_list(lists, 3, 4), ==, "7"); + g_assert_cmpstr(get_relative_item_in_composed_list(lists, 3, -2), ==, "1"); } -int main() +#define PREFIX "coreUtils" +#define add_test(func) g_test_add_func("/"PREFIX"/"#func, func) +int main(int argc, char **argv) { setbuf(stdout, NULL); + g_test_init(&argc, &argv, NULL); - int number_failed; - Suite *s; - SRunner *sr; - - s = suite(); - sr = srunner_create(s); - - srunner_run_all(sr, CK_NORMAL); - - srunner_ntests_run(sr); - number_failed = srunner_ntests_failed(sr); - srunner_free(sr); + add_test(dir_exists_test); + add_test(get_on_composed_list_test); + add_test(remove_from_composed_list_test); + add_test(wlr_list_remove_in_composed_list_test); + add_test(wlr_list_find_in_composed_list_test); + add_test(cross_sum_test); + add_test(get_relative_item_in_list_test); - return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; + return g_test_run(); } diff --git a/test/utils/gapUtils_test.c b/test/utils/gapUtils_test.c index 926b1b45..2fed63da 100644 --- a/test/utils/gapUtils_test.c +++ b/test/utils/gapUtils_test.c @@ -1,12 +1,12 @@ #include -#include +#include #include #include #include "utils/coreUtils.h" #include "utils/gapUtils.h" -START_TEST(containerAddGapsTest) +void containerAddGapsTest() { struct wlr_box con; con.x = 50; @@ -18,29 +18,29 @@ START_TEST(containerAddGapsTest) int gap = 5; container_add_gaps(&con2, gap, WLR_EDGE_LEFT | WLR_EDGE_RIGHT); - ck_assert_double_eq(con2.x, 55); - ck_assert_double_eq(con2.y, 50); - ck_assert_double_eq(con2.width, 90); - ck_assert_double_eq(con2.height, 100); + g_assert_cmpint(con2.x, ==, 55); + g_assert_cmpint(con2.y, ==,50); + g_assert_cmpint(con2.width, ==, 90); + g_assert_cmpint(con2.height, ==, 100); con2 = con; container_add_gaps(&con2, gap, WLR_EDGE_TOP); - ck_assert_double_eq(con2.x, 50); - ck_assert_double_eq(con2.y, 55); - ck_assert_double_eq(con2.width, 100); - ck_assert_double_eq(con2.height, 95); + g_assert_cmpint(con2.x, ==, 50); + g_assert_cmpint(con2.y, ==, 55); + g_assert_cmpint(con2.width, ==, 100); + g_assert_cmpint(con2.height, ==, 95); con2 = con; container_add_gaps(&con2, gap, WLR_EDGE_BOTTOM); - ck_assert_double_eq(con2.x, 50); - ck_assert_double_eq(con2.y, 50); - ck_assert_double_eq(con2.width, 100); - ck_assert_double_eq(con2.height, 95); -} END_TEST + g_assert_cmpint(con2.x, ==, 50); + g_assert_cmpint(con2.y, ==, 50); + g_assert_cmpint(con2.width, ==, 100); + g_assert_cmpint(con2.height, ==, 95); +} -START_TEST(containerSurroundGapsTest) +void containerSurroundGapsTest() { struct wlr_box con; con.x = 50; @@ -48,40 +48,21 @@ START_TEST(containerSurroundGapsTest) con.width = 100; con.height = 100; container_surround_gaps(&con, 4.0); - ck_assert_double_eq(con.x, 52); - ck_assert_double_eq(con.y, 52); - ck_assert_double_eq(con.width, 96); - ck_assert_double_eq(con.height, 96); -} END_TEST - -Suite *suite() -{ - Suite *s; - TCase *tc; - - s = suite_create("gapUtils"); - tc = tcase_create("Core"); - tcase_add_test(tc, containerAddGapsTest); - tcase_add_test(tc, containerSurroundGapsTest); - - suite_add_tcase(s, tc); - - return s; + g_assert_cmpint(con.x, ==, 52); + g_assert_cmpint(con.y, ==, 52); + g_assert_cmpint(con.width, ==, 96); + g_assert_cmpint(con.height, ==, 96); } -int main() +#define PREFIX "gapUtils" +#define add_test(func) g_test_add_func("/"PREFIX"/"#func, func) +int main(int argc, char **argv) { - int numberFailed; - Suite *s; - SRunner *sr; - - s = suite(); - sr = srunner_create(s); + setbuf(stdout, NULL); + g_test_init(&argc, &argv, NULL); - srunner_run_all(sr, CK_NORMAL); - srunner_ntests_run(sr); - numberFailed = srunner_ntests_failed(sr); - srunner_free(sr); + add_test(containerAddGapsTest); + add_test(containerSurroundGapsTest); - return (numberFailed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; + return g_test_run(); } diff --git a/test/utils/stringUtils_test.c b/test/utils/stringUtils_test.c index b96f2401..9f2fb243 100644 --- a/test/utils/stringUtils_test.c +++ b/test/utils/stringUtils_test.c @@ -1,60 +1,41 @@ #include -#include +#include #include #include "utils/stringUtils.h" -START_TEST(intToStringTest) +void intToStringTest() { char res[NUM_DIGITS]; int_to_string(res, 30); - ck_assert_str_eq("30", res); -} END_TEST + g_assert_cmpstr("30", ==, res); +} -START_TEST(doubleToStringTest) +void doubleToStringTest() { char res[MAXLEN]; double_to_string(res, 3.141592); - ck_assert_str_eq("3.142", res); -} END_TEST + g_assert_cmpstr("3.142", ==, res); +} -START_TEST(repeatStringTest) +void repeatStringTest() { char res[NUM_CHARS]; strcpy(res, "g"); repeat_string(res, 4); - ck_assert_str_eq("gggg", res); -} END_TEST - -Suite *suite() -{ - Suite *s; - TCase *tc; - - s = suite_create("stringUtils"); - tc = tcase_create("Core"); - - tcase_add_test(tc, doubleToStringTest); - tcase_add_test(tc, intToStringTest); - tcase_add_test(tc, repeatStringTest); - suite_add_tcase(s, tc); - - return s; + g_assert_cmpstr("gggg", ==, res); } -int main() +#define PREFIX "stringUtils" +#define add_test(func) g_test_add_func("/"PREFIX"/"#func, func) +int main(int argc, char **argv) { - int numberFailed; - Suite *s; - SRunner *sr; - - s = suite(); - sr = srunner_create(s); + setbuf(stdout, NULL); + g_test_init(&argc, &argv, NULL); - srunner_run_all(sr, CK_NORMAL); - srunner_ntests_run(sr); - numberFailed = srunner_ntests_failed(sr); - srunner_free(sr); + add_test(doubleToStringTest); + add_test(intToStringTest); + add_test(repeatStringTest); - return (numberFailed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; + return g_test_run(); } diff --git a/test/workspace_test.c b/test/workspace_test.c index 241c6138..5471bd4d 100644 --- a/test/workspace_test.c +++ b/test/workspace_test.c @@ -1,82 +1,27 @@ -#include #include #include "workspace.h" -START_TEST(push_workspace_crash_test) +void workspace_contains_client_crash_test() { - push_workspace(NULL, NULL); -} END_TEST - -START_TEST(workspace_contains_client_crash_test) -{ - workspace_contains_client(NULL, NULL); -} END_TEST - -START_TEST(workspace_has_clients_test) -{ - struct layout lt; - struct workspace *ws = create_workspace("test", 3, <); - ck_assert_int_eq(workspace_has_clients(ws), false); -} END_TEST - -START_TEST(reset_loaded_layouts_test) -{ - struct wlr_list tag_names; - wlr_list_init(&tag_names); - wlr_list_push(&tag_names, "0"); - wlr_list_push(&tag_names, "1"); - wlr_list_push(&tag_names, "2"); - wlr_list_push(&tag_names, "3"); - - lua_State *L = luaL_newstate(); - struct layout *lt0 = create_layout(L); - struct layout *lt1 = create_layout(L); - struct layout *lt2 = create_layout(L); - struct wlr_list workspaces; - create_workspaces(&workspaces, &tag_names, lt0); - - struct workspace *ws0 = workspaces.items[0]; - wlr_list_push(&ws0->loaded_layouts, lt0); - wlr_list_push(&ws0->loaded_layouts, lt1); - wlr_list_push(&ws0->loaded_layouts, lt2); - - remove_loaded_layouts(&workspaces); - ck_assert_int_eq(ws0->loaded_layouts.length, 0); -} END_TEST + /* workspace_contains_client(NULL, NULL); */ +} -Suite *suite() +void workspace_has_clients_test() { - Suite *s; - TCase *tc; - - s = suite_create("workspace"); - tc = tcase_create("core"); - - tcase_add_test(tc, push_workspace_crash_test); - tcase_add_test(tc, workspace_has_clients_test); - tcase_add_test(tc, workspace_contains_client_crash_test); - tcase_add_test(tc, reset_loaded_layouts_test); - suite_add_tcase(s, tc); - - return s; + /* struct workspace *ws = create_workspace("test", 3); */ + /* ck_assert_int_eq(workspace_has_clients(ws), false); */ } -int main() +#define PREFIX "workspace" +#define add_test(func) g_test_add_func("/"PREFIX"/"#func, func) +int main(int argc, char **argv) { setbuf(stdout, NULL); + g_test_init(&argc, &argv, NULL); - int number_failed; - Suite *s; - SRunner *sr; - - s = suite(); - sr = srunner_create(s); - - srunner_run_all(sr, CK_NORMAL); - srunner_ntests_run(sr); - number_failed = srunner_ntests_failed(sr); - srunner_free(sr); + add_test(workspace_has_clients_test); + add_test(workspace_contains_client_crash_test); - return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; + return g_test_run(); }