├── .clang-format
├── .editorconfig
├── .gitignore
├── COPYING
├── LICENSE.md
├── Makefile
├── README.md
├── cwc-portals.conf
├── cwc.desktop
├── cwctl
├── cmd-screen.c
├── cwctl.h
├── gen.py
├── ipc-common.c
├── main.c
├── meson.build
└── script
│ ├── binds.lua
│ ├── client.lua
│ └── screen.lua
├── defconfig
├── keybind.lua
├── oneshot.lua
└── rc.lua
├── docs
├── 00-getting-started.md
├── 30-custom-layout.md
├── 80-c-plugin.md
├── config.ld
├── ldoc.css
└── ldoc.ltp
├── include
├── cwc
│ ├── config.h
│ ├── desktop
│ │ ├── idle.h
│ │ ├── layer_shell.h
│ │ ├── output.h
│ │ ├── session_lock.h
│ │ ├── toplevel.h
│ │ └── transaction.h
│ ├── input
│ │ ├── cursor.h
│ │ ├── keyboard.h
│ │ ├── manager.h
│ │ ├── seat.h
│ │ ├── switch.h
│ │ ├── tablet.h
│ │ ├── text_input.h
│ │ └── touch.h
│ ├── ipc.h
│ ├── layout
│ │ ├── bsp.h
│ │ ├── container.h
│ │ └── master.h
│ ├── luac.h
│ ├── luaclass.h
│ ├── luaobject.h
│ ├── plugin.h
│ ├── server.h
│ ├── signal.h
│ ├── types.h
│ └── util.h
└── private
│ ├── callback.h
│ ├── luac.h
│ └── server.h
├── lib
├── cuteful
│ ├── client.lua
│ ├── enum.lua
│ ├── init.lua
│ ├── screen.lua
│ └── tag.lua
├── gears
│ ├── cache.lua
│ ├── color.lua
│ ├── debug.lua
│ ├── filesystem.lua
│ ├── geometry.lua
│ ├── init.lua
│ ├── matcher.lua
│ ├── math.lua
│ ├── matrix.lua
│ ├── object.lua
│ ├── object
│ │ └── properties.lua
│ ├── protected_call.lua
│ ├── shape.lua
│ ├── sort
│ │ ├── init.lua
│ │ └── topological.lua
│ ├── string.lua
│ ├── surface.lua
│ ├── table.lua
│ ├── timer.lua
│ └── wallpaper.lua
└── wibox
│ ├── hierarchy.lua
│ ├── init.lua
│ └── widget
│ └── base.lua
├── meson.build
├── meson_options.txt
├── plugins
├── cwcle.c
├── flayout.c
└── meson.build
├── protocol
├── meson.build
├── wlr-layer-shell-unstable-v1.xml
└── wlr-output-power-management-unstable-v1.xml
├── src
├── config.c
├── desktop
│ ├── idle.c
│ ├── layer_shell.c
│ ├── output.c
│ ├── session_lock.c
│ ├── toplevel.c
│ └── transaction.c
├── input
│ ├── cursor.c
│ ├── keybinding.c
│ ├── keyboard.c
│ ├── manager.c
│ ├── seat.c
│ ├── switch.c
│ ├── tablet.c
│ ├── text_input.c
│ └── touch.c
├── ipc
│ ├── common.c
│ └── server.c
├── layout
│ ├── bsp.c
│ ├── container.c
│ └── master.c
├── luac.c
├── luaclass.c
├── luaobject.c
├── main.c
├── meson.build
├── objects
│ ├── client.c
│ ├── container.c
│ ├── input.c
│ ├── kbind.c
│ ├── kbindmap.c
│ ├── layer_shell.c
│ ├── screen.c
│ └── tag.c
├── plugin.c
├── server.c
├── signal.c
├── util-map.c
└── util.c
└── tests
├── capi
├── signal.c
└── signal.lua
├── hash.c
├── hash.cpp
├── luapi
├── client.lua
├── container.lua
├── kbinding.lua
├── layer_shell.lua
├── screen.lua
├── signal.lua
└── tag.lua
├── meson.build
└── rc.lua
/.clang-format:
--------------------------------------------------------------------------------
1 | BasedOnStyle: LLVM
2 | ColumnLimit: 80
3 | IndentWidth: 4
4 | AlignAfterOpenBracket: Align
5 | AlignConsecutiveAssignments: true
6 | AlignArrayOfStructures: Left
7 | AlignConsecutiveBitFields: true
8 | AlignConsecutiveMacros: true
9 | AlignEscapedNewlines: Left
10 | AllowShortFunctionsOnASingleLine: Empty
11 | AlwaysBreakBeforeMultilineStrings: false
12 | BinPackParameters: false
13 | BreakBeforeBinaryOperators: NonAssignment
14 | BreakBeforeBraces: Linux
15 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # see https://github.com/CppCXY/EmmyLuaCodeStyle
2 | [*.lua]
3 | # [basic]
4 |
5 | # optional space/tab
6 | indent_style = space
7 | # if indent_style is space, this is valid
8 | indent_size = 4
9 | # none/single/double
10 | quote_style = double
11 |
12 | continuation_indent = 4
13 |
14 | # this mean utf8 length , if this is 'unset' then the line width is no longer checked
15 | # this option decides when to chopdown the code
16 | max_line_length = 100
17 |
18 | # optional crlf/lf/cr/auto, if it is 'auto', in windows it is crlf other platforms are lf
19 | # in neovim the value 'auto' is not a valid option, please use 'unset'
20 | end_of_line = lf
21 |
22 | #optional keep/never/always/smart
23 | trailing_table_separator = smart
24 |
25 | # keep/remove/remove_table_only/remove_string_only
26 | call_arg_parentheses = remove_table_only
27 |
28 | detect_end_of_line = true
29 |
30 | # this will check text end with new line
31 | insert_final_newline = true
32 |
33 | # [space]
34 | space_around_table_field_list = true
35 |
36 | space_before_attribute = false
37 |
38 | space_before_function_open_parenthesis = false
39 |
40 | space_before_function_call_open_parenthesis = false
41 |
42 | space_before_closure_open_parenthesis = true
43 |
44 | # optional always/only_string/only_table/none
45 | # or true/false
46 | space_before_function_call_single_arg = always
47 |
48 | space_before_open_square_bracket = false
49 |
50 | space_inside_function_call_parentheses = false
51 |
52 | space_inside_function_param_list_parentheses = false
53 |
54 | space_inside_square_brackets = false
55 |
56 | # like t[#t+1] = 1
57 | space_around_table_append_operator = true
58 |
59 | ignore_spaces_inside_function_call = false
60 |
61 | space_before_inline_comment = 1
62 |
63 | # [operator space]
64 | space_around_math_operator = true
65 |
66 | space_after_comma = true
67 |
68 | space_after_comma_in_for_statement = true
69 |
70 | space_around_concat_operator = true
71 |
72 | # [align]
73 |
74 | align_call_args = false
75 |
76 | align_function_params = true
77 |
78 | align_continuous_assign_statement = true
79 |
80 | align_continuous_rect_table_field = true
81 |
82 | align_continuous_line_space = 2
83 |
84 | align_if_branch = false
85 |
86 | # option none / always / contain_curly/
87 | align_array_table = true
88 |
89 | align_continuous_inline_comment = true
90 | # option none / always / only_call_stmt
91 | align_chain_expr = none
92 |
93 | # [indent]
94 |
95 | never_indent_before_if_condition = false
96 |
97 | never_indent_comment_on_if_branch = false
98 |
99 | # [line space]
100 |
101 | # The following configuration supports four expressions
102 | # keep
103 | # fixed(n)
104 | # min(n)
105 | # max(n)
106 | # for eg. min(2)
107 |
108 | line_space_after_if_statement = keep
109 |
110 | line_space_after_do_statement = keep
111 |
112 | line_space_after_while_statement = keep
113 |
114 | line_space_after_repeat_statement = keep
115 |
116 | line_space_after_for_statement = keep
117 |
118 | line_space_after_local_or_assign_statement = keep
119 |
120 | line_space_after_function_statement = fixed(2)
121 |
122 | line_space_after_expression_statement = keep
123 |
124 | line_space_after_comment = keep
125 |
126 | # [line break]
127 | break_all_list_when_line_exceed = false
128 |
129 | auto_collapse_lines = false
130 |
131 | # [preference]
132 | ignore_space_after_colon = false
133 |
134 | remove_call_expression_list_finish_comma = false
135 |
136 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # dev
2 | build/
3 | .cache/
4 | core.cwc*
5 | subprojects/
6 |
7 | # tests
8 | .gdb_history
9 | *.log
10 |
11 | # generated docs
12 | doc/
13 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | SHELL := /bin/sh
2 | BUILDDIR := build
3 |
4 | all:
5 | @if [ ! -d "$(BUILDDIR)" ]; then meson setup $(BUILDDIR) -Dplugins=true -Dtests=true; fi
6 | @ninja -C $(BUILDDIR)/
7 |
8 | all-release: docs
9 | @if [ ! -d "$(BUILDDIR)" ]; then meson setup $(BUILDDIR) -Dplugins=true -Dtests=true --buildtype=release; fi
10 | @ninja -C $(BUILDDIR)/
11 |
12 | all-debugrelease:
13 | @if [ ! -d "$(BUILDDIR)" ]; then meson setup $(BUILDDIR) -Dplugins=true -Dtests=true --buildtype=debugoptimized; make docs; fi
14 | @ninja -C $(BUILDDIR)/
15 |
16 | cwc:
17 | @if [ ! -d "$(BUILDDIR)" ]; then meson setup $(BUILDDIR); fi
18 |
19 | clean:
20 | rm -rf ./$(BUILDDIR) ./doc
21 |
22 | install:
23 | ninja -C $(BUILDDIR) install
24 |
25 | uninstall:
26 | ninja -C $(BUILDDIR) uninstall
27 |
28 | format:
29 | @find src plugins tests include cwctl -type f -name "*.[ch]" -print0 | xargs -0 clang-format -i --verbose
30 | @CodeFormat format -w .
31 | @CodeFormat format -f ./docs/config.ld --overwrite
32 |
33 | docs:
34 | rm -rf doc
35 | cd docs && ldoc .
36 |
37 | .PHONY: cwc release clean install uninstall header format docs
38 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # README
2 |
3 | ## About CwC
4 |
5 | CwC is an extensible Wayland compositor with dynamic window management based
6 | on wlroots. Highly influenced by Awesome window manager, cwc use Lua for its
7 | configuration and C plugin for extension.
8 |
9 | For new users, you may want to check out [getting started][getting_started] page.
10 |
11 | ## Stability state
12 |
13 | Crash may happen so daily driving isn't recommended unless you're okay with it
14 | and if you encounter one please report by [creating issue][github-issue] with steps to
15 | reproduce. I will fix it as quickly as possible because I also daily drive it and
16 | I want my setup to be super stable.
17 |
18 | API breaking change is documented with exclamation mark (`!`) in the commit
19 | message as per [conventional commits specification][conventional-commits].
20 | APIs that derived from AwesomeWM are unlikely to get a change therefore
21 | guaranteed not to break your configuration.
22 |
23 | ## Features
24 |
25 | - Very configurable, fast, and lightweight.
26 | - Hot reload configuration support.
27 | - Can be used as floating/tiling window manager.
28 | - Tabbed windows.
29 | - Tags instead of workspaces.
30 | - Documented Lua API.
31 | - wlr protocols support.
32 | - Multihead support with hotplugging and restore.
33 |
34 | ## Building and installation
35 |
36 | Required dependencies:
37 |
38 | - wayland
39 | - wlroots 0.19
40 | - hyprcursor
41 | - cairo
42 | - xkbcommon
43 | - libinput
44 | - xxhash
45 | - LuaJIT
46 | - Xwayland
47 | - xcb
48 |
49 | Lua library dependencies:
50 |
51 | - LGI
52 | - cairo with support for GObject introspection
53 | - Pango with support for GObject introspection
54 |
55 | Dev dependencies:
56 |
57 | - meson
58 | - ninja
59 | - wayland-protocols
60 | - clang-format & EmmyLuaCodeStyle (formatting)
61 |
62 | ### Manual
63 |
64 | ```console
65 | $ make all-release
66 | $ sudo make install
67 | ```
68 |
69 | CwC now should be available in the display manager or execute `cwc` in the tty.
70 |
71 | To clear the installation and build artifacts, you can execute this command:
72 |
73 | ```console
74 | $ sudo make uninstall
75 | $ make clean
76 | ```
77 |
78 | ### AUR
79 |
80 | AUR package is available under the package name [cwc-git][cwc-git].
81 |
82 | ```console
83 | $ yay -S cwc-git
84 | ```
85 |
86 |
87 |
Screenshot
88 |

89 |
90 |
91 | ## Credits
92 |
93 | CwC contains verbatim or modified works from these awesome projects:
94 |
95 | - [Awesome](https://github.com/awesomeWM/awesome)
96 | - [dwl](https://codeberg.org/dwl/dwl)
97 | - [Hikari](https://hub.darcs.net/raichoo/hikari)
98 | - [Hyprland](https://github.com/hyprwm/Hyprland)
99 | - [Sway](https://github.com/swaywm/sway)
100 | - [TinyWL](https://gitlab.freedesktop.org/wlroots/wlroots)
101 |
102 | See [LICENSE.md](LICENSE.md) for license details.
103 |
104 |
105 |
106 | [getting_started]: https://cudiph.github.io/cwc/apidoc/documentation/00-getting-started.md.html
107 | [github-issue]: https://github.com/Cudiph/cwcwm/issues
108 | [conventional-commits]: https://www.conventionalcommits.org/en/v1.0.0/#commit-message-with--to-draw-attention-to-breaking-change
109 | [cwc-git]: https://aur.archlinux.org/packages/cwc-git
110 |
--------------------------------------------------------------------------------
/cwc-portals.conf:
--------------------------------------------------------------------------------
1 | [preferred]
2 | default=gtk
3 | org.freedesktop.impl.portal.ScreenCast=wlr
4 | org.freedesktop.impl.portal.Screenshot=wlr
5 |
--------------------------------------------------------------------------------
/cwc.desktop:
--------------------------------------------------------------------------------
1 | [Desktop Entry]
2 | Name=CwC
3 | Comment=An extensible and dynamic Wayland compositor
4 | Exec=cwc
5 | Type=Application
6 |
--------------------------------------------------------------------------------
/cwctl/cmd-screen.c:
--------------------------------------------------------------------------------
1 | /* cmd-screen.c - cwctl screen command
2 | *
3 | * Copyright (C) 2025 Dwi Asmoro Bangun
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #include
20 | #include
21 | #include
22 | #include
23 |
24 | #include "cwctl.h"
25 | #include "script-asset.h"
26 |
27 | static char *screen_help =
28 | "Manage screen object\n"
29 | "\n"
30 | "Usage\n"
31 | " cwctl screen [options] [COMMAND [ARG...]]\n"
32 | "\n"
33 | "Available Commands:\n"
34 | " list\n"
35 | " list all screen information\n"
36 | "\n"
37 | " toggle \n"
38 | " toggle screen property with boolean data type\n"
39 | "\n"
40 | " set \n"
41 | " set the property of a screen\n"
42 | "\n"
43 | " get \n"
44 | " get the property of a screen\n"
45 | "\n"
46 | "Options:\n"
47 | " -h, --help get the property of a screen\n"
48 | " -f, --filter specify which output name for get,set,toggle to apply. "
49 | "default is 'focused' output, use '*' for every output";
50 |
51 | static struct option screen_long_opt[] = {
52 | {"help", NO_ARG, NULL, 'h'},
53 | {"filter", ARG, NULL, 'f'},
54 | {NULL, 0, NULL, 0 },
55 | };
56 |
57 | static char *filter = "focused";
58 | static char formatted[100] = {0};
59 |
60 | static void handle_list(char *script)
61 | {
62 | strcat(script, "return scr_list()");
63 | repl(script);
64 | }
65 |
66 | static void handle_toggle(int cmd_argcount, int argc, char **argv, char *script)
67 | {
68 | if (cmd_argcount < 1) {
69 | fprintf(stderr, "missing property argument\n");
70 | return;
71 | }
72 |
73 | snprintf(formatted, 99, "return scr_set('%s', '%s', 'toggle')\n", filter,
74 | argv[optind + 1]);
75 | strcat(script, formatted);
76 | repl(script);
77 | }
78 |
79 | static void handle_set(int cmd_argcount, int argc, char **argv, char *script)
80 | {
81 | if (cmd_argcount < 2) {
82 | fprintf(stderr, "not enough argument\n");
83 | return;
84 | }
85 |
86 | snprintf(formatted, 99, "return scr_set('%s', '%s', %s)\n", filter,
87 | argv[optind + 1], argv[optind + 2]);
88 | strcat(script, formatted);
89 | repl(script);
90 | }
91 |
92 | static void handle_get(int cmd_argcount, int argc, char **argv, char *script)
93 | {
94 | if (cmd_argcount < 1) {
95 | fprintf(stderr, "missing property argument\n");
96 | return;
97 | }
98 |
99 | snprintf(formatted, 99, "return scr_get('%s', '%s')\n", filter,
100 | argv[optind + 1]);
101 | strcat(script, formatted);
102 | repl(script);
103 | }
104 |
105 | int screen_cmd(int argc, char **argv)
106 | {
107 | char *script = calloc(1, _cwctl_script_screen_lua_len + 100);
108 | strcpy(script, (char *)_cwctl_script_screen_lua);
109 |
110 | int c;
111 | while ((c = getopt_long(argc, argv, "hf:", screen_long_opt, NULL)) != -1)
112 | switch (c) {
113 | case 'h':
114 | puts(screen_help);
115 | goto cleanup;
116 | case 'f':
117 | filter = optarg;
118 | break;
119 | default:
120 | puts(screen_help);
121 | goto cleanup;
122 | }
123 |
124 | if ((argc - optind) == 0) {
125 | handle_list(script);
126 | goto cleanup;
127 | }
128 |
129 | char *command = argv[optind];
130 | int cmd_argcount = argc - optind - 1;
131 |
132 | if (strcmp(command, "list") == 0) {
133 | handle_list(script);
134 | } else if (strcmp(command, "toggle") == 0) {
135 | handle_toggle(cmd_argcount, argc, argv, script);
136 | } else if (strcmp(command, "set") == 0) {
137 | handle_set(cmd_argcount, argc, argv, script);
138 | } else if (strcmp(command, "get") == 0) {
139 | handle_get(cmd_argcount, argc, argv, script);
140 | } else if (strcmp(command, "help") == 0) {
141 | puts(screen_help);
142 | } else {
143 | fprintf(stderr,
144 | "command %s not found, run 'cwctl screen --help' to show all "
145 | "command\n",
146 | command);
147 | return 1;
148 | }
149 |
150 | cleanup:
151 | free(script);
152 | return 0;
153 | }
154 |
--------------------------------------------------------------------------------
/cwctl/cwctl.h:
--------------------------------------------------------------------------------
1 | #ifndef _CWCTL_H
2 | #define _CWCTL_H
3 |
4 | #define ARG 1
5 | #define NO_ARG 0
6 | #define BUFFER_SIZE 1e6
7 |
8 | void repl(char *cmd);
9 |
10 | int screen_cmd(int argc, char **argv);
11 |
12 | #endif // !_CWCTL_H
13 |
--------------------------------------------------------------------------------
/cwctl/gen.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import random
3 | import re
4 | from string import ascii_uppercase
5 |
6 | if len(sys.argv) < 3:
7 | print("Usage: python gen.py FILE... OUTPUT")
8 | exit(0)
9 |
10 | content = id = ""
11 | for i in range(12):
12 | id += random.choice(ascii_uppercase)
13 |
14 |
15 | for p in sys.argv[1:-1]:
16 | path = re.sub("[^0-9a-zA-Z]+", "_", p)
17 |
18 | with open(p, "rb") as f:
19 | data = f.read()
20 | f.close()
21 |
22 | hex_list = ""
23 | for i, byte in enumerate(data):
24 | if hex_list:
25 | hex_list += ", "
26 | if i % 12 == 0:
27 | hex_list += "\n "
28 | hex_list += f"{byte:#0{4}x}"
29 |
30 | hex_list += ", 0x00"
31 | code = f"""\
32 | // {p}
33 | static unsigned char {path}[] = {{\
34 | {hex_list}
35 | }};
36 | static unsigned int {path}_len = {len(data) + 1};
37 | \n"""
38 |
39 | content += code
40 |
41 | template = f"""\
42 | // Generated by cwctl/gen.py, change will be overridden.
43 |
44 | #ifndef _EMBED_GENERATED_{id}
45 | #define _EMBED_GENERATED_{id}
46 |
47 | {content}
48 |
49 | #endif
50 | """
51 |
52 | with open(sys.argv[-1], 'w') as f:
53 | f.write(template);
54 | f.close()
55 |
--------------------------------------------------------------------------------
/cwctl/ipc-common.c:
--------------------------------------------------------------------------------
1 | ../src/ipc/common.c
--------------------------------------------------------------------------------
/cwctl/meson.build:
--------------------------------------------------------------------------------
1 | assets = {
2 | 'client': 'script/client.lua',
3 | 'screen': 'script/screen.lua',
4 | 'binds': 'script/binds.lua',
5 | }
6 |
7 | script_assets = []
8 | foreach _, path : assets
9 | script_assets += path
10 | endforeach
11 |
12 | script_header = custom_target(
13 | name.underscorify() + '_h',
14 | input: script_assets,
15 | output: 'script-asset.h',
16 | command: ['python', meson.current_source_dir() / 'gen.py', '@INPUT@', '@OUTPUT@'],
17 | )
18 |
19 | srcs = [
20 | 'main.c',
21 | 'ipc-common.c',
22 | 'cmd-screen.c',
23 |
24 | script_header,
25 | ]
26 |
27 | executable(
28 | 'cwctl',
29 | srcs,
30 | include_directories : cwc_inc,
31 | install: true,
32 | install_dir: '/usr/bin',
33 | )
34 |
--------------------------------------------------------------------------------
/cwctl/script/binds.lua:
--------------------------------------------------------------------------------
1 | local cwc = cwc
2 | local kbd = cwc.kbd
3 |
4 | local function binds_list()
5 | local binds = {}
6 | local out = ""
7 |
8 | for _, bind in pairs(kbd.get_default_member()) do
9 | table.insert(binds, bind)
10 | end
11 |
12 | for _, kbind in pairs(kbd.get_bindmap()) do
13 | if kbind.active then
14 | for _, bind in pairs(kbind.member) do
15 | table.insert(binds, bind)
16 | end
17 | end
18 | end
19 |
20 | for idx, bind in pairs(binds) do
21 | if out then out = out .. "\n" end
22 | local mod_and_keyname = ""
23 |
24 | for _, modname in pairs(bind.modifier_name) do
25 | if #mod_and_keyname > 0 then
26 | mod_and_keyname = mod_and_keyname .. " + "
27 | end
28 |
29 | mod_and_keyname = mod_and_keyname .. modname
30 | end
31 |
32 | if #mod_and_keyname > 0 then
33 | mod_and_keyname = mod_and_keyname .. " + " .. bind.keyname
34 | else
35 | mod_and_keyname = bind.keyname
36 | end
37 |
38 |
39 | local modnum_list = ''
40 | for _, mod_enum in pairs(bind.modifier) do
41 | if #modnum_list > 0 then modnum_list = modnum_list .. ", " end
42 | modnum_list = modnum_list .. mod_enum
43 | end
44 |
45 | local template =
46 | "[%d] %s (%s):\n" ..
47 | "\tGroup: %s\n" ..
48 | "\tDescription: %s\n" ..
49 | "\tExclusive: %s\n" ..
50 | "\tRepeat: %s\n" ..
51 | "\tModifier: %s\n" ..
52 | "\tKeysym: 0x%x\n" ..
53 | ""
54 |
55 | out = out .. string.format(template,
56 | idx, mod_and_keyname, bind,
57 | bind.group or "-",
58 | bind.description or "-",
59 | bind.exclusive,
60 | bind.repeated,
61 | #modnum_list > 0 and modnum_list or '-',
62 | bind.keysym
63 | )
64 | end
65 |
66 | return out
67 | end
68 |
69 | return binds_list()
70 |
--------------------------------------------------------------------------------
/cwctl/script/client.lua:
--------------------------------------------------------------------------------
1 | local bit = require("bit")
2 |
3 | local cwc = cwc
4 |
5 | local function c_list()
6 | local cls = cwc.client.get()
7 | local out = ""
8 |
9 | for c_idx, c in pairs(cls) do
10 | if out then out = out .. "\n" end
11 | local tag = ""
12 |
13 | for i = 1, c.screen.max_general_workspace do
14 | if bit.band(c.tag, bit.lshift(1, i - 1)) ~= 0 then
15 | if #tag ~= 0 then tag = tag .. ", " end
16 | tag = tag .. i
17 | end
18 | end
19 |
20 |
21 | local template =
22 | "[%d] %s (%s):\n" ..
23 | "\tApp ID: %s\n" ..
24 | "\tPID: %s\n" ..
25 | "\tXwayland: %s\n" ..
26 | "\tUnmanaged: %s\n" ..
27 | "\tVisible: %s\n" ..
28 | "\tMapped: %s\n" ..
29 | "\tFullscreen: %s\n" ..
30 | "\tMaximized: %s\n" ..
31 | "\tFloating: %s\n" ..
32 | "\tMinimized: %s\n" ..
33 | "\tSticky: %s\n" ..
34 | "\tOntop: %s\n" ..
35 | "\tAbove: %s\n" ..
36 | "\tBelow: %s\n" ..
37 | "\tTearing Allowed: %s\n" ..
38 | "\tUrgent: %s\n" ..
39 | "\tTag: %s\n" ..
40 | "\tOpacity: %f\n" ..
41 | "\tBorder: %s, %d px, %d deg\n" ..
42 | "\tParent: %s\n" ..
43 | "\tScreen: %s\n" ..
44 | "\tContainer: %s\n" ..
45 | "\tGeometry:\n" ..
46 | "\t\tx: %d\n" ..
47 | "\t\ty: %d\n" ..
48 | "\t\tw: %d\n" ..
49 | "\t\th: %d\n" ..
50 | ""
51 |
52 | out = out .. string.format(template,
53 | c_idx, c.title, c,
54 | c.appid,
55 | c.pid,
56 | c.x11,
57 | c.unmanaged,
58 | c.visible,
59 | c.mapped,
60 | c.fullscreen,
61 | c.maximized,
62 | c.floating,
63 | c.minimized,
64 | c.sticky,
65 | c.ontop,
66 | c.above,
67 | c.below,
68 | c.allow_tearing,
69 | c.urgent,
70 | tag,
71 | c.opacity,
72 | c.border_enabled, c.border_width, c.border_rotation,
73 | c.parent,
74 | c.screen,
75 | c.container,
76 | c.geometry.x, c.geometry.y, c.geometry.width, c.geometry.height
77 | )
78 | end
79 |
80 | return out
81 | end
82 |
83 | return c_list()
84 |
--------------------------------------------------------------------------------
/cwctl/script/screen.lua:
--------------------------------------------------------------------------------
1 | local bit = require("bit")
2 | local gears = require("gears")
3 |
4 | local cwc = cwc
5 |
6 | local function scr_list()
7 | local scrs = cwc.screen.get()
8 | local out = ""
9 |
10 | for s_idx, s in pairs(scrs) do
11 | if out then out = out .. "\n" end
12 | local active_tags = ""
13 |
14 | for i = 1, s.max_general_workspace do
15 | if bit.band(s.active_tag, bit.lshift(1, i - 1)) ~= 0 then
16 | if #active_tags ~= 0 then active_tags = active_tags .. ", " end
17 | active_tags = active_tags .. i
18 | end
19 | end
20 |
21 | local template =
22 | "[%d] %s (%s):\n" ..
23 | "\tDescription: %s\n" ..
24 | "\tEnabled: %s\n" ..
25 | "\tDPMS: %s\n" ..
26 | "\tMake: %s\n" ..
27 | "\tModel: %s\n" ..
28 | "\tSerial: %s\n" ..
29 | "\tMode: %sx%s@%s\n" ..
30 | "\tPosition: %s,%s\n" ..
31 | "\tPhysical size: %dx%d mm\n" ..
32 | "\tScale: %f\n" ..
33 | "\tScreen geometry:\n" ..
34 | "\t\tx: %d\n" ..
35 | "\t\ty: %d\n" ..
36 | "\t\tw: %d\n" ..
37 | "\t\th: %d\n" ..
38 | "\tScreen work area:\n" ..
39 | "\t\tx: %d\n" ..
40 | "\t\ty: %d\n" ..
41 | "\t\tw: %d\n" ..
42 | "\t\th: %d\n" ..
43 | "\tNon desktop: %s\n" ..
44 | "\tRestored: %s\n" ..
45 | "\tSelected tag: %d\n" ..
46 | "\tActive tags: %s\n" ..
47 | "\tTearing allowed: %s\n" ..
48 | "\tHas focus: %s\n" ..
49 | ""
50 |
51 | out = out .. string.format(template,
52 | s_idx, s.name, s,
53 | s.description,
54 | s.enabled,
55 | s.dpms and "on" or "off",
56 | s.make,
57 | s.model,
58 | s.serial,
59 | s.width, s.height, s.refresh / 1000,
60 | s.geometry.x, s.geometry.y,
61 | s.phys_width, s.phys_height,
62 | s.scale,
63 | s.geometry.x, s.geometry.y, s.geometry.width, s.geometry.height,
64 | s.workarea.x, s.workarea.y, s.workarea.width, s.workarea.height,
65 | s.non_desktop,
66 | s.restored,
67 | s.selected_tag.index,
68 | active_tags,
69 | s.allow_tearing,
70 | cwc.screen.focused() == s
71 | )
72 | end
73 |
74 | return out
75 | end
76 |
77 | local function scr_set(filter, key, value)
78 | local scrs = cwc.screen.get()
79 | local focused = cwc.screen.focused()
80 |
81 | if scrs[1][key] == nil then
82 | return "error: screen `" .. key .. "` property is read only/doesn't exist"
83 | end
84 |
85 | for _, s in pairs(scrs) do
86 | if value == "toggle" then value = not s[key] end
87 |
88 | if filter == "*" then
89 | s[key] = value
90 | elseif filter == "focused" and s == focused then
91 | s[key] = value
92 | break
93 | elseif filter == s.name then
94 | s[key] = value
95 | break
96 | end
97 | end
98 |
99 | return "OK"
100 | end
101 |
102 | local function scr_get(filter, key)
103 | local scrs = cwc.screen.get()
104 | local focused = cwc.screen.focused()
105 |
106 | if scrs[1][key] == nil then
107 | return "error: screen `" .. key .. "` property is empty or doesn't exist"
108 | end
109 |
110 | local out = ""
111 | for i, s in pairs(scrs) do
112 | local formatted = string.format('\n[%d] "%s".%s = %s \n', i, s.name, key,
113 | gears.debug.dump_return(s[key]))
114 |
115 | if filter == "*" then
116 | out = out .. formatted
117 | elseif filter == "focused" and s == focused then
118 | out = out .. formatted
119 | break
120 | elseif filter == s.name then
121 | out = out .. formatted
122 | break
123 | end
124 | end
125 |
126 | return out
127 | end
128 |
--------------------------------------------------------------------------------
/defconfig/oneshot.lua:
--------------------------------------------------------------------------------
1 | -- script which better run once on startup
2 |
3 | local cwc = cwc
4 |
5 | -- autostart app
6 | cwc.spawn_with_shell("swaybg --output '*' --color '#222222'")
7 | cwc.spawn_with_shell("waybar")
8 | cwc.spawn_with_shell("playerctld daemon")
9 |
10 | local idle_cmd = "playerctl pause; cwctl screen --filter '*' set dpms false"
11 | local resume_cmd = "playerctl play; cwctl screen --filter='*' set dpms true"
12 | local swayidle_cmd = string.format('swayidle -w timeout 3600 "%s" resume "%s"', idle_cmd, resume_cmd)
13 | cwc.spawn_with_shell(swayidle_cmd)
14 |
15 | -- for app that use tray better to wait for the bar to load
16 | cwc.spawn_with_shell("sleep 3 && copyq")
17 | cwc.spawn_with_shell("sleep 3 && aria2tray --hide-window")
18 |
19 | -- env var
20 | cwc.setenv("HYPRCURSOR_THEME", "Bibata-Modern-Classic")
21 |
22 | -- xdg-desktop-portal-wlr
23 | cwc.spawn_with_shell(
24 | "dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP")
25 |
26 | -- load builtin cwc C plugin
27 | local plugins_folder = cwc.is_nested() and "./build/plugins" or cwc.get_datadir() .. "/plugins"
28 | cwc.plugin.load(plugins_folder .. "/cwcle.so")
29 | cwc.plugin.load(plugins_folder .. "/flayout.so")
30 |
--------------------------------------------------------------------------------
/docs/30-custom-layout.md:
--------------------------------------------------------------------------------
1 | # Custom Layout
2 |
3 | Custom layout only applicable for master/stack layout mode, for others layout is need to be
4 | integrated to the compositor itself.
5 |
6 | ## Custom layout in C
7 |
8 | To create a custom layout in C is by implementing the `struct layout_interface` the structure is as following.
9 |
10 | ```C
11 | struct layout_interface {
12 | char *name;
13 |
14 | /* toplevels doesn't include maximized/fullscreen or not tiled client, so
15 | * the implementation can focus arranging the given toplevels.
16 | */
17 | void (*arrange)(struct cwc_toplevel **toplevels,
18 | int len,
19 | struct cwc_output *output,
20 | struct master_state *master_state);
21 |
22 | // private...
23 | };
24 | ```
25 |
26 | the field `name` is the name of the layout and the `arrange` is function to arrange
27 | the layout.
28 |
29 | - `toplevels` - array of toplevels that can be arranged.
30 | - `len` - length of the toplevel array
31 | - `output` - screen where all the toplevel placed.
32 | - `master_state` - info such as such as master width factor, master_count, etc.
33 |
34 | Let's take a look at `monocle` layout for the simplest implementation how to arrange the toplevels.
35 | The monocle layout is just set all the tileable toplevel to the size of the workarea/usable area.
36 |
37 | ```C
38 | static void arrange_monocle(struct cwc_toplevel **toplevels,
39 | int len,
40 | struct cwc_output *output,
41 | struct master_state *master_state)
42 | {
43 | int i = 0;
44 | struct cwc_toplevel *toplevel = toplevels[i];
45 |
46 | // You can also loop through the array by checking if the element is NULL.
47 | // The end of the array is guaranteed to be NULL.
48 | while (toplevel) {
49 | cwc_container_set_position_gap(
50 | toplevel->container, output->usable_area.x, output->usable_area.y);
51 | cwc_container_set_size(toplevel->container, output->usable_area.width,
52 | output->usable_area.height);
53 |
54 | toplevel = toplevels[++i];
55 | }
56 | }
57 | ```
58 |
59 | One thing to keep in mind is to set the toplevel size and position we use `cwc_container_set_xxx`
60 | instead of `cwc_toplevel_set_xxx` because the toplevel decoration need to accounted. Also
61 | you don't need to worry about the gap as it's already taken care by both `set_position_gap` and
62 | `set_size`.
63 |
64 | Once the interface is implemented, now register it by using `master_register_layout`.
65 | To create a layout as a plugin see @{80-c-plugin.md}.
66 |
67 | ```C
68 | static void master_register_monocle()
69 | {
70 | struct layout_interface *monocle_impl = calloc(1, sizeof(*monocle_impl));
71 | monocle_impl->name = "monocle";
72 | monocle_impl->arrange = arrange_monocle;
73 |
74 | master_register_layout(monocle_impl);
75 | }
76 | ```
77 |
78 | To remove it use `master_unregister_layout`.
79 |
80 | ```C
81 | master_unregister_layout(&monocle_impl);
82 | free(monocle_impl);
83 | ```
84 |
85 | ## Custom layout in Lua
86 |
87 | Planned.
88 |
--------------------------------------------------------------------------------
/docs/80-c-plugin.md:
--------------------------------------------------------------------------------
1 | # C Plugin
2 |
3 | DISCLAIMER: By writing or loading C plugin writer assume you already know what you're doing and
4 | writer is not responsible for segfault, loss of data, kernel panic, bad API design,
5 | thermonuclear war, or the current economic crisis. Always make sure the plugin you want to load
6 | doesn't do anything malicious.
7 |
8 | ## Loading C Plugin
9 |
10 | Loading C plugin can be done by using `cwc.plugin.load` API and to unload it using
11 | `cwc.plugin.unload_byname`. If the plugin already exist `cwc.plugin.load` won't reload it,
12 | if the plugin doesn't support unloading `unload_byname` is doing nothing.
13 |
14 | ## Writing C Plugin
15 |
16 | You may be want to write C Plugin because you need full access to cwc internals and perhaps you
17 | want to add amazing feature so that CwC become more awesome (pun intended). Knowledge of the CwC
18 | internal code and wlroots may be necessary to start hacking.
19 |
20 | The API for writing the C plugin is very similar to writing linux kernel module in fact some of the code
21 | is from linux `module.h`, the difference is it's just start with `PLUGIN` instead of `MODULE`.
22 |
23 | What it does under the hood is just loading the shared object using dlopen.
24 | Let's take a look at `flayout.c` plugin for example.
25 |
26 | ```C
27 | #include
28 |
29 | #include "cwc/desktop/output.h"
30 | #include "cwc/desktop/toplevel.h"
31 | #include "cwc/layout/master.h"
32 | #include "cwc/plugin.h"
33 | #include "cwc/server.h"
34 |
35 | // function arrange_flayout...
36 |
37 | struct layout_interface fullscreen_impl = {.name = "fullscreen",
38 | .arrange = arrange_flayout};
39 |
40 | static int init()
41 | {
42 | master_register_layout(&fullscreen_impl);
43 |
44 | return 0;
45 | }
46 |
47 | static void fini()
48 | {
49 | master_unregister_layout(&fullscreen_impl);
50 | }
51 |
52 | plugin_init(init);
53 | plugin_exit(fini);
54 |
55 | PLUGIN_NAME("flayout");
56 | PLUGIN_VERSION("0.1.0");
57 | PLUGIN_DESCRIPTION("f layout we go f screen");
58 | PLUGIN_LICENSE("MIT");
59 | PLUGIN_AUTHOR("Dwi Asmoro Bangun ");
60 | ```
61 |
62 | First we includes CwC header and other library header that we need. All the public CwC header
63 | is commonly located at `/usr/include/cwc`, with that information you may need to adjust compiler flag.
64 |
65 | ```C
66 | #include
67 |
68 | #include "cwc/desktop/output.h"
69 | #include "cwc/desktop/toplevel.h"
70 | #include "cwc/layout/master.h"
71 | #include "cwc/plugin.h"
72 | #include "cwc/server.h"
73 | ```
74 |
75 | Then create an entry point for the plugin load to call. The function name can be anything
76 | as long as you call `plugin_init` macro with the function name as argument.
77 | The function signature is `int (*name)(void)`, the return value is actually doesn't decide anything
78 | but it'll follow linux convention with zero for success and non-zero for error in case
79 | something change in the future.
80 |
81 | ```C
82 | static int init_can_be_any_name()
83 | {
84 | master_register_layout(&fullscreen_impl);
85 |
86 | return 0;
87 | }
88 |
89 | plugin_init(init_can_be_any_name);
90 | ```
91 |
92 | If the plugin support unloading, `plugin_exit` macro can be used to mark a function to call
93 | before the plugin is unloaded. If the plugin doesn't support unloading you may omit it.
94 | Or if you support unloading but doesn't need a cleanup just pass an empty function.
95 | The function signature is `void (*name)(void)`.
96 |
97 | ```C
98 | static void fini()
99 | {
100 | master_unregister_layout(&fullscreen_impl);
101 | }
102 |
103 | plugin_exit(fini);
104 | ```
105 |
106 | Last is plugin metadata, `PLUGIN_NAME` and `PLUGIN_VERSION` is mandatory so that the loader can
107 | manage it.
108 |
109 | ```C
110 | PLUGIN_NAME("flayout");
111 | PLUGIN_VERSION("0.1.0");
112 | PLUGIN_DESCRIPTION("f layout we go f screen");
113 | PLUGIN_LICENSE("MIT");
114 | PLUGIN_AUTHOR("Dwi Asmoro Bangun ");
115 | ```
116 |
117 | After writing the plugin now it's time for the compilation. The plugin need to be compiled
118 | to shared object file (.so) with linker flag
119 | `--allow-shlib-undefined`, `-shared`, and `--as-needed`
120 |
121 | If you want to create a lua API in the plugin the convention used is `cwc.`. For example
122 | in `cwcle` the `PLUGIN_NAME` is `cwcle` so in the lua side it should be accessed with `cwc.cwcle`.
123 |
--------------------------------------------------------------------------------
/include/cwc/config.h:
--------------------------------------------------------------------------------
1 | #ifndef _CWC_CONFIG_H
2 | #define _CWC_CONFIG_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | struct cwc_config {
10 | // cwc
11 | bool warp_cursor_to_edge_on_resize; // TODO
12 | bool move_cursor_on_focus; // TODO
13 |
14 | // client
15 | int border_color_rotation_degree;
16 | int border_width;
17 | struct _cairo_pattern *border_color_focus; // USE SETTER
18 | struct _cairo_pattern *border_color_normal; // USE SETTER
19 |
20 | // screen
21 | int useless_gaps;
22 |
23 | // pointer device
24 | int cursor_size;
25 | int cursor_inactive_timeout_ms;
26 | int cursor_edge_threshold;
27 | float cursor_edge_snapping_overlay_color[4];
28 |
29 | // kbd
30 | int repeat_rate;
31 | int repeat_delay;
32 |
33 | // the one and only lua_State
34 | struct lua_State *_L_but_better_to_use_function_than_directly;
35 |
36 | struct {
37 | /* data is a struct cwc_config which is the older config for comparison
38 | */
39 | struct wl_signal commit;
40 | } events;
41 |
42 | struct {
43 | struct cwc_config *old_config;
44 | } CWC_PRIVATE;
45 | };
46 |
47 | extern struct cwc_config g_config;
48 |
49 | static inline struct lua_State *g_config_get_lua_State()
50 | {
51 | return g_config._L_but_better_to_use_function_than_directly;
52 | }
53 |
54 | void cwc_config_init();
55 |
56 | void cwc_config_commit();
57 |
58 | void cwc_config_set_default();
59 |
60 | void cwc_config_set_cairo_pattern(struct _cairo_pattern **dest,
61 | struct _cairo_pattern *src);
62 |
63 | void cwc_config_set_number_positive(int *dest, int src);
64 |
65 | #endif // !_CWC_CONFIG_H
66 |
--------------------------------------------------------------------------------
/include/cwc/desktop/idle.h:
--------------------------------------------------------------------------------
1 | #ifndef _CWC_IDLE_H
2 | #define _CWC_IDLE_H
3 |
4 | #include
5 | #include
6 |
7 | struct cwc_idle {
8 | struct cwc_server *server;
9 | struct wlr_idle_notifier_v1 *idle_notifier;
10 | struct wlr_idle_inhibit_manager_v1 *inhibit_manager;
11 |
12 | struct wl_listener new_inhibitor_l;
13 | };
14 |
15 | void update_idle_inhibitor(void *data);
16 |
17 | #endif // !_CWC_IDLE_H
18 |
--------------------------------------------------------------------------------
/include/cwc/desktop/layer_shell.h:
--------------------------------------------------------------------------------
1 | #ifndef _CWC_LAYER_SHELL_H
2 | #define _CWC_LAYER_SHELL_H
3 |
4 | #include
5 | #include
6 |
7 | #include "cwc/types.h"
8 |
9 | struct cwc_server;
10 |
11 | /* node.data == cwc_layer_surface */
12 | struct cwc_layer_surface {
13 | enum cwc_data_type type;
14 | struct wl_list link; // struct cwc_server.layer_shells
15 | struct wlr_layer_surface_v1 *wlr_layer_surface;
16 | struct wlr_scene_layer_surface_v1 *scene_layer;
17 | struct cwc_output *output;
18 | bool mapped;
19 | struct wlr_scene_tree *popup_tree;
20 |
21 | struct wl_listener new_popup_l;
22 | struct wl_listener destroy_l;
23 |
24 | struct wl_listener map_l;
25 | struct wl_listener unmap_l;
26 | struct wl_listener commit_l;
27 | };
28 |
29 | void arrange_layers(struct cwc_output *output);
30 |
31 | #endif // !_CWC_LAYER_SHELL_H
32 |
--------------------------------------------------------------------------------
/include/cwc/desktop/session_lock.h:
--------------------------------------------------------------------------------
1 | #ifndef _CWC_SESSION_LOCK
2 | #define _CWC_SESSION_LOCK
3 |
4 | #include
5 | #include
6 |
7 | struct cwc_session_locker;
8 |
9 | struct cwc_session_lock_manager {
10 | struct cwc_server *server;
11 | struct wlr_session_lock_manager_v1 *manager;
12 | bool locked;
13 | struct cwc_session_locker *locker; // available when locked = true
14 |
15 | struct wl_listener new_lock_l;
16 | struct wl_listener destroy_l;
17 | };
18 |
19 | struct cwc_session_locker {
20 | struct wlr_session_lock_v1 *locker;
21 | struct cwc_session_lock_manager *manager;
22 |
23 | struct wl_listener unlock_l;
24 | struct wl_listener new_surface_l;
25 | struct wl_listener destroy_l;
26 | };
27 |
28 | #endif // !_CWC_SESSION_LOCK
29 |
--------------------------------------------------------------------------------
/include/cwc/desktop/transaction.h:
--------------------------------------------------------------------------------
1 | #ifndef _CWC_TRANSACTION_H
2 | #define _CWC_TRANSACTION_H
3 |
4 | #include "cwc/desktop/output.h"
5 |
6 | void transaction_schedule_output(struct cwc_output *output);
7 | void transaction_schedule_tag(struct cwc_tag_info *tag);
8 |
9 | void transaction_pause();
10 | void transaction_resume();
11 |
12 | #endif // !_CWC_TRANSACTION_H
13 |
--------------------------------------------------------------------------------
/include/cwc/input/cursor.h:
--------------------------------------------------------------------------------
1 | #ifndef _CWC_INPUT_CURSOR_H
2 | #define _CWC_INPUT_CURSOR_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | struct cwc_server;
11 |
12 | enum cwc_cursor_state {
13 | CWC_CURSOR_STATE_NORMAL,
14 |
15 | CWC_CURSOR_STATE_MOVE,
16 | CWC_CURSOR_STATE_RESIZE,
17 |
18 | CWC_CURSOR_STATE_MOVE_BSP,
19 | CWC_CURSOR_STATE_RESIZE_BSP,
20 |
21 | CWC_CURSOR_STATE_MOVE_MASTER,
22 | CWC_CURSOR_STATE_RESIZE_MASTER,
23 | };
24 |
25 | struct hyprcursor_buffer {
26 | struct wlr_buffer base;
27 | cairo_surface_t *surface;
28 | };
29 |
30 | struct bsp_grab {
31 | struct bsp_node *horizontal;
32 | struct bsp_node *vertical;
33 | double wfact_horizontal;
34 | double wfact_vertical;
35 | };
36 |
37 | struct cwc_cursor {
38 | struct wlr_seat *seat;
39 | struct wlr_cursor *wlr_cursor;
40 | struct wlr_xcursor_manager *xcursor_mgr;
41 | struct hyprcursor_manager_t *hyprcursor_mgr;
42 | const char *current_name;
43 |
44 | // interactive
45 | enum cwc_cursor_state state;
46 | uint32_t resize_edges;
47 | double grab_x, grab_y;
48 | union {
49 | struct wlr_box grab_float;
50 | struct bsp_grab grab_bsp;
51 | };
52 | struct cwc_toplevel *grabbed_toplevel;
53 | const char *name_before_interactive;
54 | struct wlr_scene_rect *snap_overlay;
55 |
56 | // resize scheduling
57 | uint64_t last_resize_time_msec;
58 | struct wlr_box pending_box;
59 |
60 | // hyprcursor
61 | struct hyprcursor_cursor_style_info info;
62 | hyprcursor_cursor_image_data **images;
63 | int images_count;
64 | int frame_index; // point to animation frame in cursor_buffers
65 | struct wl_array cursor_buffers; // struct hyprcursor_buffer *
66 | struct wl_event_source *animation_timer;
67 | float scale;
68 |
69 | struct wlr_pointer_constraint_v1 *active_constraint;
70 | bool dont_emit_signal;
71 | struct cwc_output *last_output;
72 |
73 | // cursor inactive timeout
74 | bool hidden;
75 | const char *name_before_hidden;
76 | struct wl_event_source *inactive_timer;
77 | struct wlr_surface *client_surface;
78 | int hotspot_x, hotspot_y;
79 | struct wl_listener client_side_surface_destroy_l;
80 |
81 | struct wl_listener cursor_motion_l;
82 | struct wl_listener cursor_motion_abs_l;
83 | struct wl_listener cursor_axis_l;
84 | struct wl_listener cursor_button_l;
85 | struct wl_listener cursor_frame_l;
86 |
87 | struct wl_listener swipe_begin_l;
88 | struct wl_listener swipe_update_l;
89 | struct wl_listener swipe_end_l;
90 |
91 | struct wl_listener pinch_begin_l;
92 | struct wl_listener pinch_update_l;
93 | struct wl_listener pinch_end_l;
94 |
95 | struct wl_listener hold_begin_l;
96 | struct wl_listener hold_end_l;
97 |
98 | struct wl_listener config_commit_l;
99 | struct wl_listener destroy_l;
100 | };
101 |
102 | void process_cursor_motion(struct cwc_cursor *cursor,
103 | uint32_t time_msec,
104 | struct wlr_input_device *device,
105 | double dx,
106 | double dy,
107 | double dx_unaccel,
108 | double dy_unaccel);
109 |
110 | struct cwc_cursor *cwc_cursor_create(struct wlr_seat *seat);
111 |
112 | void cwc_cursor_destroy(struct cwc_cursor *cursor);
113 |
114 | /* name should follow shape in cursor shape protocol */
115 | void cwc_cursor_set_image_by_name(struct cwc_cursor *cursor, const char *name);
116 |
117 | void cwc_cursor_set_surface(struct cwc_cursor *cursor,
118 | struct wlr_surface *surface,
119 | int32_t hotspot_x,
120 | int32_t hotspot_y);
121 |
122 | void cwc_cursor_hide_cursor(struct cwc_cursor *cursor);
123 |
124 | void cwc_cursor_update_scale(struct cwc_cursor *cursor);
125 |
126 | /* change style (mainly size)
127 | *
128 | * return true if success
129 | */
130 | bool cwc_cursor_hyprcursor_change_style(
131 | struct cwc_cursor *cursor, struct hyprcursor_cursor_style_info info);
132 |
133 | struct cwc_pointer_constraint {
134 | struct wlr_pointer_constraint_v1 *constraint;
135 | struct cwc_cursor *cursor;
136 |
137 | struct wl_listener destroy_l;
138 | };
139 |
140 | /* passing NULL will try to find toplevel below the cursor */
141 | void start_interactive_move(struct cwc_toplevel *toplevel);
142 | void start_interactive_resize(struct cwc_toplevel *toplevel, uint32_t edges);
143 |
144 | /* no op when is not from interactive */
145 | void stop_interactive(struct cwc_cursor *cursor);
146 |
147 | #endif // !_CWC_INPUT_CURSOR_H
148 |
--------------------------------------------------------------------------------
/include/cwc/input/keyboard.h:
--------------------------------------------------------------------------------
1 | #ifndef _CWC_INPUT_KEYBOARD_H
2 | #define _CWC_INPUT_KEYBOARD_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | #include "cwc/util.h"
9 |
10 | struct cwc_server;
11 |
12 | struct cwc_keyboard_group {
13 | struct cwc_seat *seat;
14 | struct wlr_keyboard_group *wlr_kbd_group;
15 |
16 | struct wl_listener modifiers_l;
17 | struct wl_listener key_l;
18 |
19 | struct wl_listener config_commit_l; // for native
20 | };
21 |
22 | struct cwc_keyboard {
23 | struct cwc_seat *seat;
24 | struct wlr_keyboard *wlr;
25 |
26 | struct wl_listener modifiers_l;
27 | struct wl_listener key_l;
28 | };
29 |
30 | struct cwc_virtual_keyboard {
31 | struct cwc_keyboard *base;
32 |
33 | struct wl_listener destroy_l;
34 | };
35 |
36 | struct cwc_keyboard_group *cwc_keyboard_group_create(struct cwc_seat *seat,
37 | bool virtual);
38 |
39 | void cwc_keyboard_group_destroy(struct cwc_keyboard_group *kbd_group);
40 |
41 | void cwc_keyboard_group_add_device(struct cwc_keyboard_group *kbd_group,
42 | struct wlr_input_device *device);
43 |
44 | struct wlr_surface;
45 | void keyboard_focus_surface(struct cwc_seat *seat, struct wlr_surface *surface);
46 |
47 | //================== KEYBINDING ====================
48 | enum cwc_keybind_type {
49 | CWC_KEYBIND_TYPE_LUA,
50 | CWC_KEYBIND_TYPE_C,
51 | };
52 |
53 | struct cwc_keybind_info {
54 | enum cwc_keybind_type type;
55 | uint64_t key; // upper 32 bit is modifier and lower is the xkb_keysym_t
56 | char *description;
57 | char *group;
58 | union {
59 | void (*on_press)(void *args);
60 | int luaref_press;
61 | };
62 | union {
63 | void (*on_release)(void *args);
64 | int luaref_release;
65 | };
66 | void *args;
67 | bool exclusive; // execute keybind even when locked or inhibited
68 | bool repeat;
69 | };
70 |
71 | struct cwc_keybind_map {
72 | struct wl_list link;
73 | struct cwc_hhmap *map; // struct cwc_keybind_info
74 | bool active;
75 | struct wl_event_source *repeat_timer;
76 | struct cwc_keybind_info *repeated_bind;
77 | };
78 |
79 | static inline uint32_t kbindinfo_key_get_modifier(uint64_t genkey)
80 | {
81 | return genkey >> 32;
82 | }
83 |
84 | static inline uint32_t kbindinfo_key_get_keysym(uint64_t genkey)
85 | {
86 | return genkey & 0xffffffff;
87 | }
88 |
89 | /* factory */
90 | struct cwc_keybind_map *cwc_keybind_map_create(struct wl_list *list);
91 | void cwc_keybind_map_destroy(struct cwc_keybind_map *kmap);
92 | void cwc_keybind_map_clear(struct cwc_keybind_map *kmap);
93 |
94 | struct lua_State;
95 | int cwc_keybind_map_register_bind_from_lua(struct lua_State *L,
96 | struct cwc_keybind_map *kmap);
97 |
98 | /* function which start with double underscore is the low level function.
99 | * the function without underscore prefix is just a wrapper around the
100 | * low level function that point to the default map on the global server struct.
101 | */
102 |
103 | /* the key is 8 bytes length generated by combining the 4 bytes of modifiers and
104 | * 4 bytes of the keysym */
105 | uint64_t keybind_generate_key(uint32_t modifiers, uint32_t key);
106 |
107 | /* register a keybind to the map */
108 | void keybind_kbd_register(struct cwc_keybind_map *kmap,
109 | uint32_t modifiers,
110 | xkb_keysym_t key,
111 | struct cwc_keybind_info info);
112 | void keybind_mouse_register(struct cwc_keybind_map *kmap,
113 | uint32_t modifiers,
114 | uint32_t button,
115 | struct cwc_keybind_info info);
116 |
117 | /* remove a keybind from a map */
118 | void keybind_kbd_remove(struct cwc_keybind_map *kmap,
119 | uint32_t modifiers,
120 | xkb_keysym_t key);
121 | void keybind_mouse_remove(struct cwc_keybind_map *kmap,
122 | uint32_t modifiers,
123 | uint32_t button);
124 |
125 | /* true if a keybind entry found/processed */
126 | bool keybind_kbd_execute(struct cwc_keybind_map *kmap,
127 | struct cwc_seat *seat,
128 | uint32_t modifiers,
129 | xkb_keysym_t key,
130 | bool press);
131 | bool keybind_mouse_execute(struct cwc_keybind_map *kmap,
132 | uint32_t modifiers,
133 | uint32_t button,
134 | bool press);
135 |
136 | void keybind_register_common_key();
137 |
138 | #endif // !_CWC_INPUT_KEYBOARD_H
139 |
--------------------------------------------------------------------------------
/include/cwc/input/manager.h:
--------------------------------------------------------------------------------
1 | #ifndef _CWC_INPUT_MANAGER_H
2 | #define _CWC_INPUT_MANAGER_H
3 |
4 | #include
5 |
6 | struct cwc_libinput_device {
7 | struct wl_list link; // cwc_input_manager.devices
8 | struct libinput_device *device;
9 | struct wlr_input_device *wlr_input_dev;
10 |
11 | struct wl_listener destroy_l;
12 | };
13 |
14 | struct cwc_input_manager {
15 | struct wl_list devices; // cwc_libinput_device.link
16 | struct wl_list seats; // cwc_seat.link
17 |
18 | // pointer
19 | struct wlr_relative_pointer_manager_v1 *relative_pointer_manager;
20 | struct wlr_pointer_gestures_v1 *pointer_gestures;
21 |
22 | struct wlr_cursor_shape_manager_v1 *cursor_shape_manager;
23 | struct wl_listener request_set_shape_l;
24 |
25 | struct wlr_pointer_constraints_v1 *pointer_constraints;
26 | struct wl_listener new_pointer_constraint_l;
27 |
28 | struct wlr_virtual_pointer_manager_v1 *virtual_pointer_manager;
29 | struct wl_listener new_vpointer_l;
30 |
31 | // kbd
32 | struct wlr_virtual_keyboard_manager_v1 *virtual_kbd_manager;
33 | struct wl_listener new_vkbd_l;
34 |
35 | struct wlr_keyboard_shortcuts_inhibit_manager_v1 *kbd_inhibit_manager;
36 | struct wl_listener new_keyboard_inhibitor_l;
37 |
38 | // extra
39 | struct wlr_transient_seat_manager_v1 *transient_seat_manager;
40 | struct wl_listener create_seat_l;
41 |
42 | struct wlr_tablet_manager_v2 *tablet_manager;
43 |
44 | // backend
45 | struct wl_listener new_input_l;
46 | };
47 |
48 | struct cwc_input_manager *cwc_input_manager_get();
49 |
50 | void cwc_input_manager_destroy();
51 |
52 | void cwc_input_manager_update_cursor_scale();
53 |
54 | #endif // !_CWC_INPUT_MANAGER_H
55 |
--------------------------------------------------------------------------------
/include/cwc/input/seat.h:
--------------------------------------------------------------------------------
1 | #ifndef _CWC_SEAT_H
2 | #define _CWC_SEAT_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | struct cwc_server;
9 | struct cwc_keyboard_group;
10 | struct cwc_input_manager;
11 |
12 | /* wlr_seat.data == cwc_seat */
13 | struct cwc_seat {
14 | struct wl_list link; // struct cwc_input_manager.seats
15 | struct wlr_seat *wlr_seat;
16 |
17 | struct cwc_cursor *cursor;
18 | struct cwc_keyboard_group *kbd_group;
19 |
20 | struct cwc_layer_surface *exclusive_kbd_interactive;
21 | struct wlr_keyboard_shortcuts_inhibitor_v1 *kbd_inhibitor;
22 |
23 | struct cwc_input_method *input_method;
24 | struct cwc_text_input *focused_text_input;
25 | struct wlr_input_method_keyboard_grab_v2 *kbd_grab;
26 |
27 | struct wl_list switch_devs; // struct cwc_switch.link
28 | struct wl_list tablet_devs; // struct cwc_tablet.link
29 | struct wl_list tablet_pad_devs; // struct cwc_tablet_pad.link
30 | struct wl_list touch_devs; // struct cwc_touch.link
31 | struct wl_list text_inputs; // struct cwc_text_input.link
32 |
33 | struct wl_listener request_set_cursor_l;
34 | struct wl_listener pointer_focus_change_l;
35 | struct wl_listener keyboard_focus_change_l;
36 |
37 | struct wl_listener request_selection_l;
38 | struct wl_listener request_primary_selection_l;
39 | struct wl_listener request_start_drag_l;
40 | struct wl_listener start_drag_l;
41 | struct wl_listener destroy_l;
42 |
43 | struct wl_listener kbd_grab_destroy_l;
44 | };
45 |
46 | struct cwc_drag {
47 | struct wlr_drag *wlr_drag;
48 | struct wlr_scene_tree *scene_tree;
49 |
50 | struct wl_listener on_drag_motion_l;
51 | struct wl_listener on_drag_destroy_l;
52 | };
53 |
54 | struct cwc_seat *cwc_seat_create(struct cwc_input_manager *manager,
55 | const char *name);
56 |
57 | void cwc_seat_destroy(struct cwc_seat *seat);
58 |
59 | void cwc_seat_add_keyboard_device(struct cwc_seat *seat,
60 | struct wlr_input_device *dev);
61 |
62 | void cwc_seat_add_pointer_device(struct cwc_seat *seat,
63 | struct wlr_input_device *dev);
64 |
65 | void cwc_seat_add_switch_device(struct cwc_seat *seat,
66 | struct wlr_input_device *dev);
67 |
68 | void cwc_seat_add_tablet_device(struct cwc_seat *seat,
69 | struct wlr_input_device *dev);
70 |
71 | void cwc_seat_add_tablet_pad_device(struct cwc_seat *seat,
72 | struct wlr_input_device *dev);
73 |
74 | void cwc_seat_add_touch_device(struct cwc_seat *seat,
75 | struct wlr_input_device *dev);
76 |
77 | #endif // !_CWC_SEAT_H
78 |
--------------------------------------------------------------------------------
/include/cwc/input/switch.h:
--------------------------------------------------------------------------------
1 | #ifndef _CWC_INPUT_SWITCH_H
2 | #define _CWC_INPUT_SWITCH_H
3 |
4 | #include
5 | #include
6 |
7 | struct cwc_switch {
8 | struct wl_list link; // cwc_seat.switch_devs
9 | struct cwc_seat *seat;
10 | struct wlr_switch *wlr_switch;
11 |
12 | struct wl_listener toggle_l;
13 | struct wl_listener destroy_l;
14 | };
15 |
16 | struct cwc_switch *cwc_switch_create(struct cwc_seat *seat,
17 | struct wlr_input_device *dev);
18 |
19 | void cwc_switch_destroy(struct cwc_switch *switch_dev);
20 |
21 | #endif // !_CWC_INPUT_SWITCH_H
22 |
--------------------------------------------------------------------------------
/include/cwc/input/tablet.h:
--------------------------------------------------------------------------------
1 | #ifndef _CWC_INPUT_TABLET_H
2 | #define _CWC_INPUT_TABLET_H
3 |
4 | #include
5 | #include
6 |
7 | struct cwc_tablet {
8 | struct wl_list link;
9 | struct cwc_seat *seat;
10 | struct wlr_tablet_v2_tablet *tablet;
11 |
12 | struct wl_listener axis_l;
13 | struct wl_listener proximity_l;
14 | struct wl_listener tip_l;
15 | struct wl_listener button_l;
16 | };
17 |
18 | struct cwc_tablet *cwc_tablet_create(struct cwc_seat *seat,
19 | struct wlr_input_device *dev);
20 |
21 | void cwc_tablet_destroy(struct cwc_tablet *tablet);
22 |
23 | struct cwc_tablet_pad {
24 | struct wl_list link;
25 | struct cwc_seat *seat;
26 | struct wlr_tablet_v2_tablet_pad *tablet_pad;
27 |
28 | struct wl_listener button_l;
29 | struct wl_listener ring_l;
30 | struct wl_listener strip_l;
31 | struct wl_listener attach_tablet_l;
32 | };
33 |
34 | struct cwc_tablet_pad *cwc_tablet_pad_create(struct cwc_seat *seat,
35 | struct wlr_input_device *dev);
36 |
37 | void cwc_tablet_pad_destroy(struct cwc_tablet_pad *tpad);
38 |
39 | #endif // !_CWC_INPUT_TABLET_H
40 |
--------------------------------------------------------------------------------
/include/cwc/input/text_input.h:
--------------------------------------------------------------------------------
1 | #ifndef _CWC_TEXT_INPUT_H
2 | #define _CWC_TEXT_INPUT_H
3 |
4 | #include
5 | #include
6 |
7 | #include "cwc/input/seat.h"
8 |
9 | struct cwc_text_input {
10 | struct wl_list link; // struct cwc_seat.text_inputs
11 | struct wlr_text_input_v3 *wlr;
12 |
13 | struct wl_listener enable_l;
14 | struct wl_listener commit_l;
15 | struct wl_listener disable_l;
16 | struct wl_listener destroy_l;
17 | };
18 |
19 | struct cwc_input_method {
20 | struct wlr_input_method_v2 *wlr;
21 |
22 | struct wl_listener commit_l;
23 | struct wl_listener new_popup_l;
24 | struct wl_listener grab_keyboard_l;
25 | struct wl_listener destroy_l;
26 | };
27 |
28 | struct cwc_im_popup {
29 | struct cwc_input_method *im;
30 | struct wlr_input_popup_surface_v2 *popup;
31 | struct wlr_scene_tree *tree;
32 |
33 | struct wl_listener commit_l;
34 | struct wl_listener destroy_l;
35 | };
36 |
37 | void text_input_try_focus_surface(struct cwc_seat *seat,
38 | struct wlr_surface *surface);
39 |
40 | #endif // !_CWC_TEXT_INPUT_H
41 |
--------------------------------------------------------------------------------
/include/cwc/input/touch.h:
--------------------------------------------------------------------------------
1 | #ifndef _CWC_INPUT_TOUCH_H
2 | #define _CWC_INPUT_TOUCH_H
3 |
4 | #include
5 | #include
6 |
7 | struct cwc_touch {
8 | struct wl_list link;
9 | struct cwc_seat *seat;
10 | struct wlr_touch *wlr_touch;
11 |
12 | struct wl_listener down_l;
13 | struct wl_listener up_l;
14 | struct wl_listener motion_l;
15 | struct wl_listener cancel_l;
16 | struct wl_listener frame_l;
17 |
18 | struct wl_listener destroy_l;
19 | };
20 |
21 | struct cwc_touch *cwc_touch_create(struct cwc_seat *seat,
22 | struct wlr_input_device *dev);
23 |
24 | void cwc_touch_destroy(struct cwc_touch *touch);
25 |
26 | #endif // !_CWC_INPUT_TOUCH_H
27 |
--------------------------------------------------------------------------------
/include/cwc/ipc.h:
--------------------------------------------------------------------------------
1 | #ifndef _CWC_IPC_H
2 | #define _CWC_IPC_H
3 |
4 | /* The ipc messaging format is:
5 | *
6 | * ```
7 | * cwc-ipc\n
8 | * \n
9 | * body...
10 | * ```
11 | *
12 | * The first line contain the signature which is "cwc-ipc", the second line
13 | * contain a byte of opcode, and the rest is the message body.
14 | */
15 |
16 | #define IPC_HEADER "cwc-ipc"
17 | #define HEADER_SIZE (sizeof(IPC_HEADER) + 2)
18 |
19 | /* Opcode is 1 byte after header */
20 | enum cwc_ipc_opcode {
21 | /* the compositor will evaluate received string and send returned value */
22 | IPC_EVAL = 1,
23 | /* the compositor response of IPC_EVAL */
24 | IPC_EVAL_RESPONSE,
25 |
26 | /* compositor object signal such client, screen, etc. */
27 | IPC_SIGNAL,
28 | };
29 |
30 | /* check if msg header is valid */
31 | bool check_header(const char *msg);
32 |
33 | /* create message with known string length */
34 | int ipc_create_message_n(char *dest,
35 | int maxlen,
36 | enum cwc_ipc_opcode opcode,
37 | const char *body,
38 | int n);
39 |
40 | /* create message with c string */
41 | int ipc_create_message(char *dest,
42 | int maxlen,
43 | enum cwc_ipc_opcode opcode,
44 | const char *body);
45 |
46 | /* return a pointer to the message body (a slice) in msg. */
47 | const char *ipc_get_body(const char *msg, enum cwc_ipc_opcode *opcode);
48 |
49 | #endif // !_CWC_IPC_H
50 |
--------------------------------------------------------------------------------
/include/cwc/layout/bsp.h:
--------------------------------------------------------------------------------
1 | #ifndef _CWC_BSP_H
2 | #define _CWC_BSP_H
3 |
4 | #include
5 |
6 | struct cwc_output;
7 | struct cwc_container;
8 |
9 | enum bsp_node_type {
10 | BSP_NODE_INTERNAL,
11 | BSP_NODE_LEAF,
12 | };
13 |
14 | enum bsp_split_type {
15 | BSP_SPLIT_AUTO, // leaf only
16 | BSP_SPLIT_HORIZONTAL,
17 | BSP_SPLIT_VERTICAL,
18 | };
19 |
20 | enum Position { LEFT, RIGHT, ROOT };
21 |
22 | struct bsp_node {
23 | enum bsp_node_type type;
24 |
25 | struct cwc_container *container; // leaf
26 |
27 | struct bsp_node *parent; // NULL parent indicates root
28 | struct bsp_node *left, *right; // leaf doesn't have child
29 |
30 | bool enabled;
31 |
32 | enum bsp_split_type split_type;
33 |
34 | /* factor of the left leaf node */
35 | double left_wfact;
36 |
37 | /* for internal node, node origin in local layout coordinate */
38 | int x, y;
39 |
40 | /* for internal node, this represent width and height of area occupied by
41 | * this node.
42 | */
43 | int width, height;
44 | };
45 |
46 | /* insert container to the bsp tree, container must not be already in a tree.
47 | * The new container is always inserted to the right node
48 | */
49 | void bsp_insert_container(struct cwc_container *new, int workspace);
50 |
51 | /* insert container to bsp tree with explicit position */
52 | void bsp_insert_container_pos(struct cwc_container *new,
53 | int workspace,
54 | enum Position pos);
55 |
56 | /* remove client from the bsp tree */
57 | void bsp_remove_container(struct cwc_container *container, bool update);
58 |
59 | void bsp_toggle_split(struct bsp_node *node);
60 |
61 | void bsp_node_enable(struct bsp_node *node);
62 | void bsp_node_disable(struct bsp_node *node);
63 |
64 | struct bsp_node *bsp_get_root(struct bsp_node *node);
65 |
66 | /* update the bsp in O(n) complexity */
67 | void bsp_update_root(struct cwc_output *output, int workspace);
68 |
69 | void bsp_last_focused_update(struct cwc_container *container);
70 |
71 | struct bsp_root_entry *
72 | bsp_entry_init(struct cwc_output *output, int workspace, struct bsp_node *root);
73 |
74 | /* return NULL if not initialized */
75 | struct bsp_root_entry *bsp_entry_get(struct cwc_output *output, int workspace);
76 |
77 | /* deinitialize */
78 | void bsp_entry_fini(struct cwc_output *output, int workspace);
79 |
80 | /* decide with node position should be inserted.
81 | * It create an origin in the middle of region and compare the point if
82 | * the box is wide and the point on the right of the origin, it return RIGHT.
83 | * If the box is tall and it below the origin then it will return RIGHT.
84 | */
85 | enum Position
86 | wlr_box_bsp_should_insert_at_position(struct wlr_box *region, int x, int y);
87 |
88 | /* return the vertical split node or horizontal split node if find any.
89 | * Return pointer cannot be NULL.
90 | */
91 | void bsp_find_resize_fence(struct bsp_node *me,
92 | uint32_t edges,
93 | struct bsp_node **vertical,
94 | struct bsp_node **horizontal);
95 |
96 | #endif // !_CWC_BSP_H
97 |
--------------------------------------------------------------------------------
/include/cwc/layout/master.h:
--------------------------------------------------------------------------------
1 | #ifndef _CWC_MASTER_H
2 | #define _CWC_MASTER_H
3 |
4 | #include "cwc/types.h"
5 |
6 | struct cwc_toplevel;
7 | struct cwc_output;
8 | struct cwc_cursor;
9 |
10 | struct master_state;
11 |
12 | typedef void (*resize_func_t)(struct cwc_toplevel **toplevels,
13 | int len,
14 | struct cwc_cursor *cursor,
15 | struct master_state *master_state);
16 |
17 | struct layout_interface {
18 | char *name;
19 |
20 | /* toplevels doesn't include maximized/fullscreen or not tiled client, so
21 | * the implementation can focus arranging the given toplevels.
22 | */
23 | void (*arrange)(struct cwc_toplevel **toplevels,
24 | int len,
25 | struct cwc_output *output,
26 | struct master_state *master_state);
27 |
28 | resize_func_t resize_start;
29 | resize_func_t resize_update;
30 | resize_func_t resize_end;
31 |
32 | // private
33 |
34 | // circular, intrusive, no head for easier navigation.
35 | struct layout_interface *next;
36 | struct layout_interface *prev;
37 | };
38 |
39 | void master_register_layout(struct layout_interface *impl);
40 | void master_unregister_layout(struct layout_interface *impl);
41 |
42 | struct layout_interface *get_default_master_layout();
43 |
44 | void master_arrange_update(struct cwc_output *output);
45 |
46 | void master_resize_start(struct cwc_output *output, struct cwc_cursor *cursor);
47 | void master_resize_update(struct cwc_output *output, struct cwc_cursor *cursor);
48 | void master_resize_end(struct cwc_output *output, struct cwc_cursor *cursor);
49 |
50 | struct cwc_toplevel *master_get_master(struct cwc_output *output);
51 |
52 | void master_set_master(struct cwc_toplevel *toplevel);
53 |
54 | #endif // !_CWC_MASTER_H
55 |
--------------------------------------------------------------------------------
/include/cwc/luac.h:
--------------------------------------------------------------------------------
1 | /*
2 | * luac.h - Lua configuration management header
3 | *
4 | * Copyright © 2008-2009 Julien Danjou
5 | *
6 | * This program is free software; you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation; either version 2 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License along
17 | * with this program; if not, write to the Free Software Foundation, Inc.,
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 | *
20 | */
21 |
22 | #ifndef _CWC_LUAC_H
23 | #define _CWC_LUAC_H
24 |
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 |
32 | extern bool lua_initial_load;
33 | extern bool luacheck;
34 | extern char *config_path;
35 | extern char *library_path;
36 |
37 | int luaC_init();
38 | void luaC_fini();
39 |
40 | //========== MACRO =============
41 |
42 | static inline void luaC_dumpstack(lua_State *L)
43 | {
44 | fprintf(stderr, "-------- Lua stack dump ---------\n");
45 | for (int i = lua_gettop(L); i; i--) {
46 | int t = lua_type(L, i);
47 | switch (t) {
48 | case LUA_TSTRING:
49 | fprintf(stderr, "%d: string\t\t`%s'\n", i, lua_tostring(L, i));
50 | break;
51 | case LUA_TBOOLEAN:
52 | fprintf(stderr, "%d: bool\t\t%s\n", i,
53 | lua_toboolean(L, i) ? "true" : "false");
54 | break;
55 | case LUA_TNUMBER:
56 | fprintf(stderr, "%d: number\t\t%g\n", i, lua_tonumber(L, i));
57 | break;
58 | case LUA_TNIL:
59 | fprintf(stderr, "%d:\t\t nil\n", i);
60 | break;
61 | default:
62 | fprintf(stderr, "%d: %s\t#%d\t%p\n", i, lua_typename(L, t),
63 | (int)lua_objlen(L, i), lua_topointer(L, i));
64 | break;
65 | }
66 | }
67 | fprintf(stderr, "------- Lua stack dump end ------\n");
68 | }
69 |
70 | /* push a table with the structure of wlr_box to the stack */
71 | static inline int luaC_pushbox(lua_State *L, struct wlr_box box)
72 | {
73 | lua_createtable(L, 0, 4);
74 | lua_pushnumber(L, box.x);
75 | lua_setfield(L, -2, "x");
76 | lua_pushnumber(L, box.y);
77 | lua_setfield(L, -2, "y");
78 | lua_pushnumber(L, box.width);
79 | lua_setfield(L, -2, "width");
80 | lua_pushnumber(L, box.height);
81 | lua_setfield(L, -2, "height");
82 |
83 | return 1;
84 | }
85 |
86 | /* check if the color is a cairo pattern */
87 | static inline cairo_pattern_t *luaC_checkcolor(lua_State *L, int idx)
88 | {
89 | if (idx < 0)
90 | idx = lua_gettop(L) + idx + 1;
91 |
92 | luaL_checktype(L, idx, LUA_TUSERDATA);
93 | int last_stack_size = lua_gettop(L);
94 |
95 | // tostring(arg1)
96 | lua_getglobal(L, "tostring");
97 | lua_pushvalue(L, idx);
98 | lua_pcall(L, 1, 1, 0);
99 |
100 | // check if returned string has word cairo
101 | if (!strstr(lua_tostring(L, -1), "cairo")) {
102 | luaL_error(L, "color need to be created from gears.color");
103 | return NULL;
104 | }
105 |
106 | cairo_pattern_t **pattern = lua_touserdata(L, idx);
107 |
108 | lua_settop(L, last_stack_size);
109 | return *pattern;
110 | }
111 |
112 | #endif // !_CWC_LUAC_H
113 |
--------------------------------------------------------------------------------
/include/cwc/luaobject.h:
--------------------------------------------------------------------------------
1 | /*
2 | * luaobject.h - useful functions for handling Lua objects
3 | *
4 | * Copyright © 2009 Julien Danjou
5 | *
6 | * This program is free software; you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation; either version 2 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License along
17 | * with this program; if not, write to the Free Software Foundation, Inc.,
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 | *
20 | */
21 |
22 | #ifndef _CWC_LUAOBJECT_H
23 | #define _CWC_LUAOBJECT_H
24 |
25 | #include
26 | #include
27 |
28 | extern const char *const LUAC_OBJECT_REGISTRY_KEY;
29 |
30 | /* get the object registry table */
31 | static inline void luaC_object_registry_push(lua_State *L)
32 | {
33 | lua_pushstring(L, LUAC_OBJECT_REGISTRY_KEY);
34 | lua_rawget(L, LUA_REGISTRYINDEX);
35 | }
36 |
37 | /* put a lua object at idx to the object registry with the pointer as key */
38 | static inline int
39 | luaC_object_register(lua_State *L, int idx, const void *pointer)
40 | {
41 | lua_pushvalue(L, idx);
42 | luaC_object_registry_push(L);
43 |
44 | lua_pushlightuserdata(L, (void *)pointer);
45 | lua_pushvalue(L, -3);
46 | lua_rawset(L, -3);
47 |
48 | lua_pop(L, 2);
49 |
50 | return 0;
51 | }
52 |
53 | /* remove a referenced object from the object registry */
54 | static inline int luaC_object_unregister(lua_State *L, const void *pointer)
55 | {
56 | luaC_object_registry_push(L);
57 |
58 | lua_pushlightuserdata(L, (void *)pointer);
59 | lua_pushnil(L);
60 | lua_rawset(L, -3);
61 |
62 | lua_pop(L, 1);
63 |
64 | return 0;
65 | }
66 |
67 | /** Push a referenced object onto the stack.
68 | * \param L The Lua VM state.
69 | * \param pointer The object to push.
70 | * \return The number of element pushed on stack.
71 | */
72 | static inline int luaC_object_push(lua_State *L, const void *pointer)
73 | {
74 | luaC_object_registry_push(L);
75 | lua_pushlightuserdata(L, (void *)pointer);
76 | lua_rawget(L, -2);
77 | lua_remove(L, -2);
78 | return 1;
79 | }
80 |
81 | /** Check if object exist in the registry table.
82 | * \param L The Lua VM state.
83 | * \param pointer The object to check.
84 | * \return Existence of the object.
85 | */
86 | static inline bool luaC_object_valid(lua_State *L, const void *pointer)
87 | {
88 | luaC_object_push(L, pointer);
89 | bool valid = !lua_isnil(L, -1);
90 | lua_pop(L, 1);
91 | return valid;
92 | }
93 |
94 | #endif // !_CWC_LUAOBJECT_H
95 |
--------------------------------------------------------------------------------
/include/cwc/plugin.h:
--------------------------------------------------------------------------------
1 | #ifndef _CWC_PLUGIN_H
2 | #define _CWC_PLUGIN_H
3 |
4 | #include
5 | #include
6 |
7 | struct cwc_server;
8 |
9 | typedef int (*initcall_t)(void);
10 | typedef void (*exitcall_t)(void);
11 |
12 | struct cwc_plugin {
13 | struct wl_list link; // cwc_server.plugins
14 |
15 | // metadata
16 | char *name;
17 | char *description;
18 | char *version;
19 | char *author;
20 | char *license;
21 | char *filename;
22 |
23 | void *handle;
24 | initcall_t init_fn;
25 | };
26 |
27 | #define __PASTE(a, b) a##b
28 |
29 | #define __stringify_1(s...) #s
30 |
31 | #define __stringify(s...) __stringify_1(s)
32 |
33 | #define __section(name) __attribute__((__section__(name)))
34 |
35 | #define __aligned(x) __attribute__((__aligned__(x)))
36 |
37 | #define __used __attribute__((__used__))
38 |
39 | #define __PLUGIN_TAG_SYMBOL(tag) _pluginfo_##tag
40 |
41 | #define PLUGIN_TAG_SYMBOL(tag) __stringify(__PLUGIN_TAG_SYMBOL(tag))
42 |
43 | #define PLUGIN_INFO(tag, value) \
44 | const char __PLUGIN_TAG_SYMBOL(tag)[] __used __aligned(1) \
45 | __section(".pluginfo") = #tag "=" value
46 |
47 | /* A wrapper to mark function as plugin entry point */
48 | #define plugin_init(fn) \
49 | int __cwc_init_plugin() \
50 | { \
51 | return fn(); \
52 | }
53 |
54 | #define plugin_init_global(fn) \
55 | int __cwc_init_plugin_global() \
56 | { \
57 | return fn(); \
58 | }
59 |
60 | /* invoked when plugin removed */
61 | #define plugin_exit(fn) \
62 | void __cwc_cleanup_plugin() \
63 | { \
64 | return fn(); \
65 | }
66 |
67 | /* set plugin name (required) */
68 | #define PLUGIN_NAME(_name) PLUGIN_INFO(name, _name)
69 |
70 | /* Set plugin version (required) */
71 | #define PLUGIN_VERSION(_version) PLUGIN_INFO(version, _version)
72 |
73 | /* Set plugin license */
74 | #define PLUGIN_LICENSE(_license) PLUGIN_INFO(license, _license)
75 |
76 | /* Set plugin author */
77 | #define PLUGIN_AUTHOR(_author) PLUGIN_INFO(author, _author)
78 |
79 | /* Set plugin description */
80 | #define PLUGIN_DESCRIPTION(_description) PLUGIN_INFO(description, _description)
81 |
82 | struct cwc_plugin *__load_plugin(const char *pathname, int __mode);
83 |
84 | /* load plugin */
85 | struct cwc_plugin *load_plugin(const char *pathname);
86 |
87 | /* load plugin as library (the symbol exposed globally to other plugin) */
88 | struct cwc_plugin *load_plugin_library(const char *pathname);
89 |
90 | /* start plugin */
91 | void cwc_plugin_start(struct cwc_plugin *plugin);
92 |
93 | /* run the cleanup function and destroy it */
94 | void cwc_plugin_unload(struct cwc_plugin *plugin);
95 |
96 | /* return true if found */
97 | bool cwc_plugin_stop_by_name(const char *name);
98 |
99 | bool cwc_plugin_is_exist(const char *name);
100 |
101 | /* stop all plugin in the list */
102 | void cwc_plugin_stop_plugins(struct wl_list *head);
103 |
104 | #endif // !_CWC_PLUGIN_H
105 |
--------------------------------------------------------------------------------
/include/cwc/server.h:
--------------------------------------------------------------------------------
1 | #ifndef _CWC_SERVER_H
2 | #define _CWC_SERVER_H
3 |
4 | #include
5 |
6 | enum server_init_return_code {
7 | SERVER_INIT_SUCCESS = 0,
8 | SERVER_INIT_FAILED = 1,
9 |
10 | LUACHECK_OK = 10,
11 | LUACHECK_ERROR = 11,
12 | };
13 |
14 | struct cwc_server {
15 | struct wl_display *wl_display;
16 | struct wl_event_loop *wl_event_loop;
17 |
18 | struct wlr_backend *backend;
19 | struct wlr_backend *headless_backend;
20 | struct wlr_renderer *renderer;
21 | struct wlr_allocator *allocator;
22 | struct wlr_compositor *compositor;
23 | struct wlr_scene *scene;
24 | struct wlr_scene_output_layout *scene_layout;
25 | struct wlr_session *session;
26 |
27 | struct wlr_security_context_manager_v1 *security_context_manager;
28 | struct wlr_export_dmabuf_manager_v1 *export_dmabuf_manager;
29 | struct wlr_screencopy_manager_v1 *screencopy_manager;
30 | struct wlr_ext_image_copy_capture_manager_v1 *copy_capture_manager;
31 | struct wlr_data_control_manager_v1 *wlr_data_control_manager;
32 | struct wlr_ext_data_control_manager_v1 *ext_data_control_manager;
33 | struct wlr_gamma_control_manager_v1 *gamma_control_manager;
34 | struct wlr_xdg_output_manager_v1 *xdg_output_manager;
35 |
36 | // desktop
37 | struct wlr_output_layout *output_layout;
38 | struct wl_listener output_layout_change_l;
39 | struct wl_listener new_output_l;
40 | struct cwc_output *fallback_output;
41 |
42 | struct wlr_output_manager_v1 *output_manager;
43 | struct wl_listener output_manager_apply_l;
44 | struct wl_listener output_manager_test_l;
45 |
46 | struct wlr_output_power_manager_v1 *output_power_manager;
47 | struct wl_listener opm_set_mode_l;
48 |
49 | struct wlr_xdg_shell *xdg_shell;
50 | struct wl_listener new_xdg_toplevel_l;
51 | struct wl_listener new_xdg_popup_l;
52 |
53 | struct wlr_xwayland *xwayland;
54 | struct wl_listener xw_ready_l;
55 | struct wl_listener xw_new_surface_l;
56 |
57 | struct wlr_xdg_decoration_manager_v1 *xdg_decoration_manager;
58 | struct wl_listener new_decoration_l;
59 |
60 | struct wlr_tearing_control_manager_v1 *tearing_manager;
61 | struct wl_listener new_tearing_object_l;
62 |
63 | struct cwc_session_lock_manager *session_lock;
64 | struct cwc_idle *idle;
65 |
66 | struct wlr_xdg_activation_v1 *xdg_activation;
67 | struct wl_listener request_activate_l;
68 |
69 | struct wlr_ext_foreign_toplevel_list_v1 *foreign_toplevel_list;
70 | struct wlr_foreign_toplevel_manager_v1 *foreign_toplevel_manager;
71 |
72 | struct wlr_scene_tree *main_tree;
73 | struct wlr_scene_tree *temporary_tree;
74 | // sorted from back to front
75 | struct {
76 | struct wlr_scene_tree *background; // layer_shell
77 | struct wlr_scene_tree *bottom; // layer_shell
78 | struct wlr_scene_tree *below; // toplevel below normal toplevel
79 | struct wlr_scene_tree *toplevel; // regular toplevel belong here
80 | struct wlr_scene_tree *above; // toplevel above normal toplevel
81 | struct wlr_scene_tree *top; // layer_shell
82 | struct wlr_scene_tree *overlay; // layer_shell
83 | struct wlr_scene_tree *session_lock; // session_lock
84 | } root;
85 | struct wlr_layer_shell_v1 *layer_shell;
86 | struct wl_listener layer_shell_surface_l;
87 |
88 | // inputs
89 | struct cwc_input_manager *input;
90 | struct cwc_seat *seat;
91 |
92 | struct wlr_input_method_manager_v2 *input_method_manager;
93 | struct wl_listener new_input_method_l;
94 |
95 | struct wlr_text_input_manager_v3 *text_input_manager;
96 | struct wl_listener new_text_input_l;
97 |
98 | // ipc
99 | int socket_fd;
100 | char *socket_path;
101 |
102 | // list
103 | struct wl_list plugins; // cwc_plugin.link
104 | struct wl_list outputs; // cwc_output.link
105 | struct wl_list toplevels; // cwc_toplevel.link
106 | struct wl_list containers; // cwc_container.link
107 | struct wl_list layer_shells; // cwc_layer_surface.link
108 | struct wl_list kbd_kmaps; // cwc_keybind_map
109 |
110 | // maps
111 | struct cwc_hhmap *output_state_cache; // struct cwc_output_state
112 | struct cwc_hhmap *signal_map; // struct cwc_signal_entry
113 | struct cwc_keybind_map *main_kbd_kmap; // struct cwc_keybind_info
114 | struct cwc_keybind_map *main_mouse_kmap; // struct cwc_keybind_info
115 |
116 | // server wide state
117 | struct cwc_container *insert_marked; // managed by container.c
118 | struct cwc_output *focused_output; // managed by output.c
119 | int resize_count; // frame synchronization
120 | };
121 |
122 | /* global server instance from main */
123 | extern struct cwc_server server;
124 |
125 | enum server_init_return_code
126 | server_init(struct cwc_server *s, char *config_path, char *library_path);
127 | void server_fini(struct cwc_server *s);
128 |
129 | void spawn(char **argv);
130 | void spawn_with_shell(const char *const command);
131 |
132 | #endif // !_CWC_SERVER_H
133 |
--------------------------------------------------------------------------------
/include/cwc/signal.h:
--------------------------------------------------------------------------------
1 | #ifndef _CWC_SIGNAL_H
2 | #define _CWC_SIGNAL_H
3 |
4 | #include
5 | #include
6 |
7 | #include "cwc/luaobject.h"
8 |
9 | typedef void (*signal_callback_t)(void *data);
10 |
11 | struct signal_c_callback {
12 | struct wl_list link;
13 | signal_callback_t callback;
14 | };
15 |
16 | struct signal_lua_callback {
17 | struct wl_list link; // struct cwc_signal_entry.lua_callback
18 | int luaref;
19 | };
20 |
21 | struct cwc_signal_entry {
22 | struct wl_list c_callbacks; // struct signal_c_callback.link
23 | struct wl_list lua_callbacks; // struct signal_lua_callback.link
24 | };
25 |
26 | /* Register a listener for C function */
27 | void cwc_signal_connect(const char *name, signal_callback_t callback);
28 |
29 | /* Register a listener for a lua function at idx index */
30 | void cwc_signal_connect_lua(const char *name, lua_State *L, int idx);
31 |
32 | /* Unregister a listener */
33 | void cwc_signal_disconnect(const char *name, signal_callback_t callback);
34 |
35 | /* Unregister a listener for lua */
36 | void cwc_signal_disconnect_lua(const char *name, lua_State *L, int idx);
37 |
38 | /* Notify signal for C listener only */
39 | void cwc_signal_emit_c(const char *name, void *data);
40 |
41 | /* notify for lua listener only */
42 | void cwc_signal_emit_lua(const char *name, lua_State *L, int nargs);
43 |
44 | /** Notify listener for both C and lua side
45 | *
46 | * \param name Signal name
47 | * \param data Data to pass for C callback
48 | * \param L Lua VM state
49 | * \param n Length of element to pass to the lua callback
50 | */
51 | void cwc_signal_emit(const char *name, void *data, lua_State *L, int nargs);
52 |
53 | /* only for reloading lua config */
54 | struct cwc_hhmap;
55 | void cwc_lua_signal_clear(struct cwc_hhmap *map);
56 |
57 | //====================== MACRO ===================
58 |
59 | /* emit signal name with an object pointed as pointer as the only argument
60 | * passed
61 | */
62 | int cwc_object_emit_signal_simple(const char *name,
63 | lua_State *L,
64 | void *pointer);
65 |
66 | /* the C listener data arg is a list of what is passed as by the varargs ... */
67 | void cwc_object_emit_signal_varr(const char *name,
68 | lua_State *L,
69 | int nargs,
70 | ...);
71 |
72 | #endif // !_CWC_SIGNAL_H
73 |
--------------------------------------------------------------------------------
/include/cwc/types.h:
--------------------------------------------------------------------------------
1 | /* general types */
2 |
3 | #ifndef _CWC_TYPES_H
4 | #define _CWC_TYPES_H
5 |
6 | #include
7 | #include
8 |
9 | // hard limit of minimum toplevel width
10 | #define MIN_WIDTH 20
11 |
12 | // max 30 workspace/tags because the tag is using uint32_t
13 | #define MAX_WORKSPACE 30
14 | typedef uint32_t tag_bitfield_t;
15 | typedef uint32_t container_state_bitfield_t;
16 |
17 | /* Documented data pointer
18 | *
19 | * wlr_surface->data == wlr_scene_tree
20 | *
21 | * node.data == cwc_data_interface_t
22 | *
23 | * wlr_xwayland_surface.data == toplevel
24 | *
25 | * wlr_xdg_surface.data == (toplevel || popup)
26 | */
27 |
28 | /* data type should be the first entry so that it act like union.
29 | *
30 | * The use case is for object which are stored in data that the type
31 | * cannot be sure like data in the `scene_node.data`. For data that is already
32 | * obvious such as in `wlr_seat.data`, it already obvious enough that it point
33 | * to cwc_seat hence it's unnecessary to put the data type.
34 | */
35 | enum cwc_data_type {
36 | CWC_DATA_EMPTY = 0,
37 | CWC_DATA_RESERVED,
38 |
39 | DATA_TYPE_OUTPUT,
40 |
41 | DATA_TYPE_XDG_SHELL,
42 | DATA_TYPE_XWAYLAND,
43 | DATA_TYPE_LAYER_SHELL,
44 | DATA_TYPE_POPUP,
45 |
46 | DATA_TYPE_BORDER,
47 | DATA_TYPE_CONTAINER,
48 | };
49 |
50 | /* common interface, better use this instead of the object to check the type */
51 | typedef struct cwc_data_interface {
52 | enum cwc_data_type type;
53 | } cwc_data_interface_t;
54 |
55 | static inline struct cwc_container *
56 | cwc_container_try_from_data_descriptor(void *data)
57 | {
58 | cwc_data_interface_t *cwc_data = data;
59 | if (cwc_data->type == DATA_TYPE_CONTAINER)
60 | return data;
61 |
62 | return NULL;
63 | }
64 |
65 | /* layout stuff */
66 | enum cwc_layout_mode {
67 | CWC_LAYOUT_FLOATING,
68 | CWC_LAYOUT_MASTER,
69 | CWC_LAYOUT_BSP,
70 | CWC_LAYOUT_LENGTH,
71 | };
72 |
73 | struct bsp_root_entry {
74 | struct bsp_node *root; // NULL indicate empty bsp
75 | struct cwc_container *last_focused;
76 | };
77 |
78 | struct master_state {
79 | int master_count;
80 | int column_count;
81 | double mwfact;
82 | struct layout_interface *current_layout;
83 | };
84 |
85 | /* contains information of a single view only tag or a traditional workspace */
86 | struct cwc_tag_info {
87 | char *label;
88 | enum cwc_layout_mode layout_mode;
89 | int index; // index in output_state array
90 | bool pending_transaction;
91 |
92 | int useless_gaps;
93 | struct bsp_root_entry bsp_root_entry;
94 | struct master_state master_state;
95 | };
96 |
97 | #endif // !_CWC_TYPES_H
98 |
--------------------------------------------------------------------------------
/include/private/callback.h:
--------------------------------------------------------------------------------
1 | struct wl_listener;
2 |
3 | void on_keyboard_focus_change(struct wl_listener *listener, void *data);
4 |
5 | void on_pointer_focus_change(struct wl_listener *listener, void *data);
6 | void on_request_set_cursor(struct wl_listener *listener, void *data);
7 |
--------------------------------------------------------------------------------
/include/private/luac.h:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | extern void luaC_object_setup(lua_State *L);
4 | extern void luaC_client_setup(lua_State *L);
5 | extern void luaC_container_setup(lua_State *L);
6 | extern void luaC_screen_setup(lua_State *L);
7 | extern void luaC_tag_setup(lua_State *L);
8 | extern void luaC_pointer_setup(lua_State *L);
9 | extern void luaC_plugin_setup(lua_State *L);
10 | extern void luaC_kbd_setup(lua_State *L);
11 | extern void luaC_input_setup(lua_State *L);
12 | extern void luaC_layer_shell_setup(lua_State *L);
13 | extern void luaC_kbindmap_setup(lua_State *L);
14 | extern void luaC_kbind_setup(lua_State *L);
15 |
--------------------------------------------------------------------------------
/include/private/server.h:
--------------------------------------------------------------------------------
1 | struct cwc_server;
2 | struct cwc_input_manager;
3 |
4 | extern void setup_output(struct cwc_server *s);
5 | extern void cleanup_output(struct cwc_server *s);
6 |
7 | extern void setup_xdg_shell(struct cwc_server *s);
8 | extern void cleanup_xdg_shell(struct cwc_server *s);
9 |
10 | extern void setup_decoration_manager(struct cwc_server *s);
11 | extern void cleanup_decoration_manager(struct cwc_server *s);
12 |
13 | extern void setup_layer_shell(struct cwc_server *s);
14 | extern void cleanup_layer_shell(struct cwc_server *s);
15 |
16 | extern void setup_cwc_session_lock(struct cwc_server *s);
17 | extern void cleanup_cwc_session_lock(struct cwc_server *s);
18 |
19 | extern void setup_pointer(struct cwc_input_manager *input_mgr);
20 | extern void cleanup_pointer(struct cwc_input_manager *input_mgr);
21 |
22 | extern void setup_keyboard(struct cwc_input_manager *input_mgr);
23 | extern void cleanup_keyboard(struct cwc_input_manager *input_mgr);
24 |
25 | extern void setup_text_input(struct cwc_server *s);
26 | extern void cleanup_text_input(struct cwc_server *s);
27 |
28 | void setup_seat(struct cwc_input_manager *input_mgr);
29 | void cleanup_seat(struct cwc_input_manager *input_mgr);
30 |
31 | extern void cwc_idle_init(struct cwc_server *s);
32 | extern void cwc_idle_fini(struct cwc_server *s);
33 | extern void xwayland_init(struct cwc_server *s);
34 | extern void xwayland_fini(struct cwc_server *s);
35 |
36 | extern void setup_transaction(struct cwc_server *s);
37 |
38 | extern void setup_ipc(struct cwc_server *s);
39 | extern void cleanup_ipc(struct cwc_server *s);
40 |
--------------------------------------------------------------------------------
/lib/cuteful/client.lua:
--------------------------------------------------------------------------------
1 | -------------------------------------------------------------------------------
2 | --- Useful functions for client manipulation.
3 | --
4 | -- @author Dwi Asmoro Bangun
5 | -- @copyright 2024
6 | -- @license GPLv3
7 | -- @module cuteful.client
8 | -------------------------------------------------------------------------------
9 |
10 | local cwc = cwc
11 |
12 | local client = {}
13 |
14 | --- Get a client by its relative index.
15 | --
16 | -- @staticfct getidx
17 | -- @tparam number idx Index relative to the this client
18 | -- @tparam[opt] cwc_client client Base client (index 0).
19 | -- @treturn cwc_client
20 | function client.getidx(idx, _client)
21 | local c = _client or cwc.client.focused()
22 | local containers = c.screen:get_containers(true)
23 | if (#containers <= 1) then return end
24 |
25 | -- find the client index
26 | local hop_distance = idx % #containers
27 | local focused_idx = 0
28 | for i = 1, #containers do
29 | if c.container == containers[i] then
30 | focused_idx = i - 1
31 | break
32 | end
33 | end
34 | local nextidx = (focused_idx + hop_distance) % #containers
35 | return containers[nextidx + 1].front
36 | end
37 |
38 | --- Focus a client by its relative index.
39 | --
40 | -- @staticfct cuteful.client.focusidx
41 | -- @tparam number idx Index relative to the this client
42 | -- @tparam[opt] cwc_client client Base client (index 0).
43 | -- @noreturn
44 | function client.focusidx(idx, _client)
45 | local c = client.getidx(idx, _client)
46 | if c then c:focus() end
47 | end
48 |
49 | --- Swap a client by its relative index.
50 | --
51 | -- @staticfct swapidx
52 | -- @tparam number idx Index relative to the this client
53 | -- @tparam[opt] cwc_client client Base client (index 0).
54 | -- @noreturn
55 | function client.swapidx(idx, _client)
56 | _client = _client or cwc.client.focused()
57 | local c = client.getidx(idx, _client)
58 | if c then c.container:swap(_client.container) end
59 | end
60 |
61 | --- Maximize client horizontally.
62 | --
63 | -- @staticfct maximize_horizontal
64 | -- @tparam[opt] cwc_client client The client
65 | -- @noreturn
66 | function client.maximize_horizontal(_c)
67 | local c = _c or cwc.client.focused()
68 |
69 | if c.maximized then
70 | c.maximized = false
71 | return
72 | end
73 |
74 | local workarea = c.screen.workarea
75 | local new_geom = c.geometry
76 | new_geom.x = workarea.x
77 | new_geom.width = workarea.width
78 |
79 | c.maximized = true
80 | c.geometry = new_geom
81 | end
82 |
83 | --- Maximize client vertically.
84 | --
85 | -- @staticfct maximize_vertical
86 | -- @tparam[opt] cwc_client client The client
87 | -- @noreturn
88 | function client.maximize_vertical(_c)
89 | local c = _c or cwc.client.focused()
90 |
91 | if c.maximized then
92 | c.maximized = false
93 | return
94 | end
95 |
96 | local workarea = c.screen.workarea
97 | local new_geom = c.geometry
98 | new_geom.y = workarea.y
99 | new_geom.height = workarea.height
100 | c.maximized = true
101 | c.geometry = new_geom
102 | end
103 |
104 | --- Restore (=unminimize) a client in the screen.
105 | --
106 | -- @staticfct restore
107 | -- @tparam boolean active_tag Unminimize client in the active tag only.
108 | -- @tparam[opt] cwc_screen s The screen to use.
109 | -- @treturn cwc_client The restored client.
110 | function client.restore(active_tag, s)
111 | s = s or cwc.screen.focused()
112 | local cls = s:get_minimized(active_tag)
113 | local c = cls[1]
114 | if c then
115 | c.minimized = false
116 | return c
117 | end
118 | end
119 |
120 | --- Get the master client.
121 | --
122 | -- @staticfct get_master
123 | -- @tparam[opt] cwc_screen s Screen to use
124 | -- @treturn cwc_client The master client
125 | function client.get_master(s)
126 | s = s or cwc.screen.focused()
127 | local cts = s:get_containers(true)
128 | return cts[1].front
129 | end
130 |
131 | --- Set the master client or container.
132 | --
133 | -- @staticfct set_master
134 | -- @tparam cwc_client c New master client.
135 | -- @tparam[opt=false] boolean swap_container Whether to swap the container.
136 | -- @noreturn
137 | function client.set_master(c, swap_container)
138 | local master = client.get_master()
139 | if swap_container then
140 | c.container:swap(master.container)
141 | else
142 | c:swap(master)
143 | end
144 | end
145 |
146 | return client
147 |
--------------------------------------------------------------------------------
/lib/cuteful/enum.lua:
--------------------------------------------------------------------------------
1 | ---------------------------------------------------------------------------
2 | --- Constants extracted from C code.
3 | --
4 | -- @author Dwi Asmoro Bangun
5 | -- @copyright 2024
6 | -- @license GPLv3
7 | -- @module cuteful.enum
8 | ---------------------------------------------------------------------------
9 |
10 | local bit = require("bit")
11 |
12 | local enum = {
13 |
14 | --- Keyboard modifier constant mapped from `wlr_keyboard.h`
15 | --
16 | -- @table modifier
17 | modifier = {
18 | NONE = 0,
19 | SHIFT = bit.lshift(1, 0),
20 | CAPS = bit.lshift(1, 1),
21 | CTRL = bit.lshift(1, 2),
22 | ALT = bit.lshift(1, 3),
23 | MOD2 = bit.lshift(1, 4),
24 | MOD3 = bit.lshift(1, 5),
25 | LOGO = bit.lshift(1, 6), -- Aka super/mod4/window key
26 | MOD5 = bit.lshift(1, 7),
27 | },
28 |
29 | --- Extracted from Linux `input-event-codes.h`
30 | --
31 | -- @table mouse_btn
32 | mouse_btn = {
33 | LEFT = 0x110,
34 | RIGHT = 0x111,
35 | MIDDLE = 0x112,
36 | SIDE = 0x113,
37 | EXTRA = 0x114,
38 | FORWARD = 0x115,
39 | BACK = 0x116,
40 | TASK = 0x117,
41 | },
42 |
43 | --- Extracted from wlr_direction `wlr_output_layout.h`
44 | --
45 | -- @table direction
46 | direction = {
47 | UP = bit.lshift(1, 0),
48 | DOWN = bit.lshift(1, 1),
49 | LEFT = bit.lshift(1, 2),
50 | RIGHT = bit.lshift(1, 3),
51 | },
52 |
53 | --- Extracted from `wayland-server-protocol.h`
54 | --
55 | -- @table output_transform
56 | output_transform = {
57 | TRANSFORM_NORMAL = 0,
58 | TRANSFORM_90 = 1,
59 | TRANSFORM_180 = 2,
60 | TRANSFORM_270 = 3,
61 | TRANSFORM_FLIPPED = 4,
62 | TRANSFORM_FLIPPED_90 = 5,
63 | TRANSFORM_FLIPPED_180 = 6,
64 | TRANSFORM_FLIPPED_270 = 7,
65 | },
66 |
67 | --- Input device type extracted from `wlr-input-device.h`
68 | --
69 | -- @table device_type
70 | device_type = {
71 | KEYBOARD = 0,
72 | POINTER = 1,
73 | TOUCH = 2,
74 | TABLET = 3,
75 | TABLET_PAD = 4,
76 | SWITCH = 5,
77 | },
78 |
79 | --- Pointer constant used for configuring pointer device from `libinput.h`
80 | -- Ref:
81 | --
82 | --@table libinput
83 | libinput = {
84 | SCROLL_NO_SCROLL = 0,
85 | SCROLL_2FG = bit.lshift(1, 0),
86 | SCROLL_EDGE = bit.lshift(1, 1),
87 | SCROLL_ON_BUTTON_DOWN = bit.lshift(1, 2),
88 |
89 | CLICK_METHOD_NONE = 0,
90 | CLICK_METHOD_BUTTON_AREAS = bit.lshift(1, 0),
91 | CLICK_METHOD_CLICKFINGER = bit.lshift(1, 1),
92 |
93 | SEND_EVENTS_ENABLED = 0,
94 | SEND_EVENTS_DISABLED = bit.lshift(1, 0),
95 | SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE = bit.lshift(1, 1),
96 |
97 | ACCEL_PROFILE_FLAT = bit.lshift(1, 0),
98 | ACCEL_PROFILE_ADAPTIVE = bit.lshift(1, 1),
99 | ACCEL_PROFILE_CUSTOM = bit.lshift(1, 2),
100 |
101 | TAP_MAP_LRM = 0,
102 | TAP_MAP_LMR = 1,
103 |
104 | DRAG_LOCK_DISABLED = 0,
105 | DRAG_LOCK_ENABLED_TIMEOUT = 1,
106 | DRAG_LOCK_ENABLED_STICKY = 2,
107 |
108 | },
109 |
110 | --- layout_mode enum extracted from cwc `types.h`.
111 | --
112 | -- @table layout_mode
113 | layout_mode = {
114 | FLOATING = 0,
115 | MASTER = 1,
116 | BSP = 2,
117 | LENGTH = 3,
118 | },
119 |
120 | }
121 |
122 | return enum
123 |
--------------------------------------------------------------------------------
/lib/cuteful/init.lua:
--------------------------------------------------------------------------------
1 | ---------------------------------------------------------------------------
2 | --- CwC UTility library hopEfully useFUL
3 | --
4 | -- @author Dwi Asmoro Bangun
5 | -- @copyright 2024
6 | -- @license GPLv3
7 | -- @module cuteful
8 | ---------------------------------------------------------------------------
9 |
10 | local cuteful = {
11 | enum = require("cuteful.enum"),
12 | tag = require("cuteful.tag"),
13 | client = require("cuteful.client"),
14 | screen = require("cuteful.screen"),
15 | }
16 |
17 | return cuteful
18 |
--------------------------------------------------------------------------------
/lib/cuteful/screen.lua:
--------------------------------------------------------------------------------
1 | -------------------------------------------------------------------------------
2 | --- Useful functions for screen object.
3 | --
4 | -- @author Dwi Asmoro Bangun
5 | -- @copyright 2025
6 | -- @license GPLv3
7 | -- @module cuteful.screen
8 | -------------------------------------------------------------------------------
9 |
10 | local cwc = cwc
11 |
12 | local screen = {}
13 |
14 | --- Get a screen by its relative index.
15 | --
16 | -- @staticfct idx
17 | -- @tparam number offset Offset index starting from the pivot.
18 | -- @tparam[opt=nil] cwc_screen s Screen as pivot offset.
19 | -- @treturn cwc_screen The screen object
20 | function screen.idx(offset, reference)
21 | reference = reference or cwc.screen.focused()
22 | local scrs = cwc.screen.get()
23 |
24 | local idx = 0
25 | for _, s in pairs(scrs) do
26 | if s == reference then break end
27 | idx = idx + 1
28 | end
29 |
30 | idx = (idx + offset) % #scrs + 1
31 | return scrs[idx]
32 | end
33 |
34 | --- Move the focus to a screen relative to the current one.
35 | --
36 | -- @staticfct focus_relative
37 | -- @tparam number offset Value to add to the current focused screen index. 1 to focus the next one, -1 to focus the previous one.
38 | -- @noreturn
39 | function screen.focus_relative(offset)
40 | screen.idx(offset):focus()
41 | end
42 |
43 | --- Move the focus to a screen in a specific direction.
44 | --
45 | -- @staticfct focus_bydirection
46 | -- @tparam number dir The direction enum.
47 | -- @tparam[opt=nil] cwc_screen s Screen.
48 | -- @noreturn
49 | -- @see cuteful.enum.direction
50 | function screen.focus_bydirection(dir, s)
51 | s = s or cwc.screen.focused()
52 | s:get_nearest(dir):focus()
53 | end
54 |
55 | return screen
56 |
--------------------------------------------------------------------------------
/lib/cuteful/tag.lua:
--------------------------------------------------------------------------------
1 | ---------------------------------------------------------------------------
2 | --- Useful functions for tag operation.
3 | --
4 | -- @author Dwi Asmoro Bangun
5 | -- @copyright 2024
6 | -- @license GPLv3
7 | -- @module cuteful.tag
8 | ---------------------------------------------------------------------------
9 |
10 | local gears = require("gears")
11 | local gtable = gears.table
12 | local cwc = cwc
13 |
14 | local tag = {
15 | history = {},
16 | }
17 |
18 | --- Select a tag relative to the currently selected one will cycle between the general workspace range.
19 | --
20 | -- @staticfct cuteful.tag.viewidx
21 | -- @tparam number idx View index relative to the current tag
22 | -- @tparam[opt] cwc_screen screen The screen
23 | -- @noreturn
24 | function tag.viewidx(idx, screen)
25 | local s = screen or cwc.screen.focused()
26 |
27 | local active_ws = s.active_workspace - 1
28 | active_ws = (active_ws + idx) % s.max_general_workspace
29 | s.active_workspace = active_ws + 1
30 | end
31 |
32 | --- View next tag
33 | --
34 | -- @staticfct cuteful.tag.viewnext
35 | -- @tparam[opt] cwc_screen screen
36 | -- @noreturn
37 | function tag.viewnext(screen)
38 | tag.viewidx(1, screen)
39 | end
40 |
41 | --- View previous tag
42 | --
43 | -- @staticfct cuteful.tag.viewprev
44 | -- @tparam[opt] cwc_screen screen
45 | -- @noreturn
46 | function tag.viewprev(screen)
47 | tag.viewidx(-1, screen)
48 | end
49 |
50 | --- View no tag.
51 | --
52 | -- @staticfct viewnone
53 | -- @tparam[opt] cwc_screen screen
54 | -- @noreturn
55 | function tag.viewnone(screen)
56 | local s = screen or cwc.screen.focused()
57 | s.active_workspace = 0;
58 | end
59 |
60 | --- Set layout_mode for tag n.
61 | --
62 | -- @staticfct layout_mode
63 | -- @tparam integer n
64 | -- @tparam integer layout_mode
65 | -- @tparam[opt] cwc_screen screen
66 | -- @noreturn
67 | -- @see cuteful.enum.layout_mode
68 | function tag.layout_mode(idx, layout_mode, screen)
69 | local s = screen or cwc.screen.focused()
70 | local t = s:get_tag(idx)
71 |
72 | t.layout_mode = layout_mode
73 | end
74 |
75 | --- Increase gap.
76 | --
77 | -- @staticfct incgap
78 | -- @tparam integer add
79 | -- @tparam[opt] cwc_tag tag
80 | -- @noreturn
81 | function tag.incgap(add, t)
82 | t = t or cwc.screen.focused().selected_tag
83 | t.gap = t.gap + add
84 | end
85 |
86 | --- Increase master width factor.
87 | --
88 | -- @staticfct incmwfact
89 | -- @tparam number add
90 | -- @tparam[opt] cwc_tag tag
91 | -- @noreturn
92 | function tag.incmwfact(add, t)
93 | t = t or cwc.screen.focused().selected_tag
94 | t.mwfact = t.mwfact + add
95 | end
96 |
97 | --- Increase the number of master windows.
98 | --
99 | -- @staticfct incnmaster
100 | -- @tparam number add
101 | -- @tparam[opt] cwc_tag tag
102 | -- @noreturn
103 | function tag.incnmaster(add, t)
104 | t = t or cwc.screen.focused().selected_tag
105 | t.master_count = t.master_count + add
106 | end
107 |
108 | --- Increase number of column windows.
109 | --
110 | -- @staticfct incncol
111 | -- @tparam number add
112 | -- @tparam[opt] cwc_tag tag
113 | -- @noreturn
114 | function tag.incncol(add, t)
115 | t = t or cwc.screen.focused().selected_tag
116 | t.column_count = t.column_count + add
117 | end
118 |
119 | ------------------ HISTORY -------------------
120 |
121 | -- the end of the array is the top of the stack
122 | local stacks = {}
123 |
124 | cwc.connect_signal("screen::new", function(s)
125 | if not stacks[s.name] then
126 | stacks[s.name] = { 1 }
127 | end
128 | end)
129 |
130 | cwc.connect_signal("screen::prop::active_tag", function(s)
131 | local sel_stack = stacks[s.name]
132 | local k = gtable.hasitem(sel_stack, s.active_tag)
133 |
134 | if k then
135 | table.remove(sel_stack, k)
136 | end
137 |
138 | table.insert(sel_stack, s.active_tag)
139 | end)
140 |
141 | --- Revert tag history.
142 | --
143 | -- @staticfct cuteful.tag.history.restore
144 | -- @tparam[opt] cwc_screen screen The screen.
145 | -- @tparam[opt] integer idx Index in the history stack. Default to previous (idx number 1).
146 | -- @noreturn
147 | function tag.history.restore(screen, idx)
148 | local s = screen or cwc.screen.focused()
149 | idx = idx or 1
150 | idx = idx + 1
151 |
152 | local sel_stack = stacks[s.name]
153 | local selected = sel_stack[idx]
154 | if not selected then return end
155 |
156 | -- make the newest at the start of the array
157 | local reversed = gtable.reverse(sel_stack)
158 | s.active_tag = reversed[idx]
159 | end
160 |
161 | return tag
162 |
--------------------------------------------------------------------------------
/lib/gears/cache.lua:
--------------------------------------------------------------------------------
1 | ---------------------------------------------------------------------------
2 | --- Cache object with data that can be garbage-collected.
3 | --
4 | -- Here is an example with a basic cache:
5 | --
6 | --@DOC_text_gears_cache_cache_EXAMPLE@
7 | --
8 | -- The example below demonstrates how the garbage collector will clear the
9 | -- cache:
10 | --
11 | --@DOC_text_gears_cache_another_cache_EXAMPLE@
12 | --
13 | --
14 | -- @author Uli Schlachter
15 | -- @copyright 2015 Uli Schlachter
16 | -- @classmod gears.cache
17 | ---------------------------------------------------------------------------
18 |
19 | local select = select
20 | local setmetatable = setmetatable
21 | local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1)
22 |
23 | local cache = {}
24 |
25 | --- Get an entry from the cache, creating it if it's missing.
26 | -- @param ... Arguments for the creation callback. These are checked against the
27 | -- cache contents for equality.
28 | -- @return The entry from the cache
29 | function cache:get(...)
30 | local result = self._cache
31 | for i = 1, select("#", ...) do
32 | local arg = select(i, ...)
33 | local next = result[arg]
34 | if not next then
35 | next = {}
36 | result[arg] = next
37 | end
38 | result = next
39 | end
40 | local ret = result._entry
41 | if not ret then
42 | ret = { self._creation_cb(...) }
43 | result._entry = ret
44 | end
45 | return unpack(ret)
46 | end
47 |
48 | --- Create a new cache object. A cache keeps some data that can be
49 | -- garbage-collected at any time, but might be useful to keep.
50 | -- @param creation_cb Callback that is used for creating missing cache entries.
51 | -- @return A new cache object.
52 | -- @constructorfct gears.cache
53 | function cache.new(creation_cb)
54 | return setmetatable({
55 | _cache = setmetatable({}, { __mode = "v" }),
56 | _creation_cb = creation_cb
57 | }, {
58 | __index = cache
59 | })
60 | end
61 |
62 | return setmetatable(cache, { __call = function(_, ...) return cache.new(...) end })
63 |
64 | -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
65 |
--------------------------------------------------------------------------------
/lib/gears/init.lua:
--------------------------------------------------------------------------------
1 | ---------------------------------------------------------------------------
2 | -- @author Uli Schlachter
3 | -- @copyright 2010 Uli Schlachter
4 | -- @module gears
5 | ---------------------------------------------------------------------------
6 |
7 |
8 | return
9 | {
10 | color = require("gears.color"),
11 | debug = require("gears.debug"),
12 | object = require("gears.object"),
13 | surface = require("gears.surface"),
14 | wallpaper = require("gears.wallpaper"),
15 | timer = require("gears.timer"),
16 | cache = require("gears.cache"),
17 | matrix = require("gears.matrix"),
18 | shape = require("gears.shape"),
19 | protected_call = require("gears.protected_call"),
20 | geometry = require("gears.geometry"),
21 | math = require("gears.math"),
22 | table = require("gears.table"),
23 | string = require("gears.string"),
24 | sort = require("gears.sort"),
25 | filesystem = require("gears.filesystem"),
26 | }
27 |
28 | -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
29 |
--------------------------------------------------------------------------------
/lib/gears/math.lua:
--------------------------------------------------------------------------------
1 | ---------------------------------------------------------------------------
2 | -- Various math related functions.
3 | --
4 | --- Math module for gears.
5 | --
6 | -- @utillib gears.math
7 | ---------------------------------------------------------------------------
8 |
9 | local rtable = table
10 |
11 | local gmath = {}
12 |
13 | local function subset_mask_apply(mask, set)
14 | local ret = {}
15 | for i = 1, #set do
16 | if mask[i] then
17 | rtable.insert(ret, set[i])
18 | end
19 | end
20 | return ret
21 | end
22 |
23 | local function subset_next(mask)
24 | local i = 1
25 | while i <= #mask and mask[i] do
26 | mask[i] = false
27 | i = i + 1
28 | end
29 |
30 | if i <= #mask then
31 | mask[i] = 1
32 | return true
33 | end
34 | return false
35 | end
36 |
37 | --- Return all subsets of a specific set.
38 | -- This function, giving a set, will return all subset it.
39 | -- For example, if we consider a set with value { 10, 15, 34 },
40 | -- it will return a table containing 2^n set:
41 | -- { }, { 10 }, { 15 }, { 34 }, { 10, 15 }, { 10, 34 }, etc.
42 | -- @param set A set.
43 | -- @return A table with all subset.
44 | -- @staticfct gears.math.subsets
45 | function gmath.subsets(set)
46 | local mask = {}
47 | local ret = {}
48 | for i = 1, #set do mask[i] = false end
49 |
50 | -- Insert the empty one
51 | rtable.insert(ret, {})
52 |
53 | while subset_next(mask) do
54 | rtable.insert(ret, subset_mask_apply(mask, set))
55 | end
56 | return ret
57 | end
58 |
59 | --- Make i cycle.
60 | -- @param t A length. Must be greater than zero.
61 | -- @param i An absolute index to fit into #t.
62 | -- @return An integer in (1, t) or nil if t is less than or equal to zero.
63 | -- @staticfct gears.math.cycle
64 | function gmath.cycle(t, i)
65 | if t < 1 then return end
66 | i = i % t
67 | if i == 0 then
68 | i = t
69 | end
70 | return i
71 | end
72 |
73 | --- Round a number to an integer.
74 | -- @tparam number x
75 | -- @treturn integer
76 | -- @staticfct gears.math.round
77 | function gmath.round(x)
78 | return math.floor(x + 0.5)
79 | end
80 |
81 | --- Return the sign of the number x
82 | -- return 1 if x is positive, -1 if negative and 0 if x is 0
83 | -- @tparam number x
84 | -- @treturn integer
85 | -- @staticfct gears.math.sign
86 | function gmath.sign(x)
87 | if x > 0 then
88 | return 1
89 | elseif x < 0 then
90 | return -1
91 | else
92 | return 0
93 | end
94 | end
95 |
96 | return gmath
97 |
--------------------------------------------------------------------------------
/lib/gears/protected_call.lua:
--------------------------------------------------------------------------------
1 | ---------------------------------------------------------------------------
2 | -- Safely call a function and handle errors using `gears.debug`.
3 | --
4 | -- This is a `pcall`/`xpcall` wrapper. All it does is to integrate into the
5 | -- AwesomeWM-wide error handling and logging.
6 | --
7 | -- @author Uli Schlachter
8 | -- @copyright 2016 Uli Schlachter
9 | -- @utillib gears.protected_call
10 | ---------------------------------------------------------------------------
11 |
12 | local gdebug = require("gears.debug")
13 | local tostring = tostring
14 | local traceback = debug.traceback
15 | local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1)
16 | local xpcall = xpcall
17 |
18 | local protected_call = {}
19 |
20 | function protected_call._error_handler(err)
21 | gdebug.print_error(traceback("Error during a protected call: " .. tostring(err), 2))
22 | end
23 |
24 | function protected_call._handle_result(success, ...)
25 | if success then
26 | return ...
27 | end
28 | end
29 |
30 | local do_pcall
31 | if not select(2, xpcall(function(a) return a end, error, true)) then
32 | -- Lua 5.1 doesn't support arguments in xpcall :-(
33 | do_pcall = function(func, ...)
34 | local args = { ... }
35 | return protected_call._handle_result(xpcall(function()
36 | return func(unpack(args))
37 | end, protected_call._error_handler))
38 | end
39 | else
40 | do_pcall = function(func, ...)
41 | return protected_call._handle_result(xpcall(func, protected_call._error_handler, ...))
42 | end
43 | end
44 |
45 | --- Call a function in protected mode and handle error-reporting.
46 | -- If the function call succeeds, all results of the function are returned.
47 | -- Otherwise, an error message is printed and nothing is returned.
48 | -- @tparam function func The function to call
49 | -- @param ... Arguments to the function
50 | -- @return The result of the given function, or nothing if an error occurred.
51 | -- @staticfct gears.protected_call
52 | function protected_call.call(func, ...)
53 | return do_pcall(func, ...)
54 | end
55 |
56 | local pcall_mt = {}
57 | function pcall_mt:__call(...)
58 | return do_pcall(...)
59 | end
60 |
61 | return setmetatable(protected_call, pcall_mt)
62 |
63 | -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
64 |
--------------------------------------------------------------------------------
/lib/gears/sort/init.lua:
--------------------------------------------------------------------------------
1 | ---------------------------------------------------------------------------
2 | --- Utilities to sort and arrange data.
3 | --
4 | -- @utillib gears.sort
5 | ---------------------------------------------------------------------------
6 |
7 | return {
8 | topological = require("gears.sort.topological")
9 | }
10 |
--------------------------------------------------------------------------------
/lib/gears/sort/topological.lua:
--------------------------------------------------------------------------------
1 | ---------------------------------------------------------------------------
2 | --- Extra sorting algorithms.
3 | --
4 | -- @submodule gears.sort
5 | ---------------------------------------------------------------------------
6 |
7 | local tsort = {}
8 | local gtable = require("gears.table")
9 |
10 | local mt = { __index = tsort }
11 |
12 | local function add_node(self, node)
13 | if not self._edges[node] then
14 | self._edges[node] = {}
15 | end
16 | end
17 |
18 | --- Ensure that `node` appears after all `dependencies`.
19 | -- @param node The node that edges are added to.
20 | -- @tparam table dependencies List of nodes that have to appear before `node`.
21 | -- @noreturn
22 | -- @method append
23 | function tsort:append(node, dependencies)
24 | add_node(self, node)
25 | for _, dep in ipairs(dependencies) do
26 | add_node(self, dep)
27 | self._edges[node][dep] = true
28 | end
29 | end
30 |
31 | --- Ensure that `node` appears before all `subordinates`.
32 | -- @param node The node that edges are added to.
33 | -- @tparam table subordinates List of nodes that have to appear after `node`.
34 | -- @noreturn
35 | -- @method prepend
36 | function tsort:prepend(node, subordinates)
37 | for _, dep in ipairs(subordinates) do
38 | self:append(dep, { node })
39 | end
40 | end
41 |
42 | local HANDLING, DONE = 1, 2
43 |
44 | local function visit(result, self, state, node)
45 | if state[node] == DONE then
46 | -- This node is already in the output
47 | return
48 | end
49 | if state[node] == HANDLING then
50 | -- We are handling this node already and managed to visit it again
51 | -- from itself. Thus, there must be a loop.
52 | result.BAD = node
53 | return true
54 | end
55 |
56 | state[node] = HANDLING
57 | -- Before this node, all nodes that it depends on must appear
58 | for dep in pairs(self._edges[node]) do
59 | if visit(result, self, state, dep) then
60 | return true
61 | end
62 | end
63 | state[node] = DONE
64 | table.insert(result, node)
65 | end
66 |
67 | --- Create a copy of this topological sort.
68 | -- This is useful to backup it before adding elements that can potentially
69 | -- have circular dependencies and thus render the original useless.
70 | -- @treturn gears.sort.topological The cloned sorter object.
71 | -- @method clone
72 | function tsort:clone()
73 | local new = tsort.topological()
74 |
75 | -- Disable deep copy as the sorted values may be objects or tables
76 | new._edges = gtable.clone(self._edges, false)
77 |
78 | return new
79 | end
80 |
81 | --- Remove a node from the topological map.
82 | --
83 | -- @param node The node
84 | -- @noreturn
85 | -- @method remove
86 | function tsort:remove(node)
87 | self._edges[node] = nil
88 | for _, deps in pairs(self._edges) do
89 | deps[node] = nil
90 | end
91 | end
92 |
93 | --- Try to sort the nodes.
94 | -- @treturn[1] table A sorted list of nodes
95 | -- @treturn[2] nil
96 | -- @return[2] A node around which a loop exists
97 | -- @method sort
98 | function tsort:sort()
99 | local result, state = {}, {}
100 | for node in pairs(self._edges) do
101 | if visit(result, self, state, node) then
102 | return nil, result.BAD
103 | end
104 | end
105 | return result
106 | end
107 |
108 | --- A topological sorting class.
109 | --
110 | -- The object returned by this function allows to create simple dependency
111 | -- graphs. It can be used for decision making or ordering of complex sequences.
112 | --
113 | --
114 | --@DOC_text_gears_sort_topological_EXAMPLE@
115 | --
116 | -- @constructorfct gears.sort.topological
117 |
118 | function tsort.topological()
119 | return setmetatable({
120 | _edges = {},
121 | }, mt)
122 | end
123 |
124 | return setmetatable(tsort, {
125 | __call = function(_, ...)
126 | return tsort.topological(...)
127 | end
128 | })
129 |
--------------------------------------------------------------------------------
/meson.build:
--------------------------------------------------------------------------------
1 | project('cwcwm', 'c', 'cpp', default_options: ['c_std=gnu23,gnu17,gnu11','b_ndebug=if-release'])
2 |
3 | add_project_arguments(['-DWLR_USE_UNSTABLE'], language: 'c')
4 | add_project_arguments(['-DHHMAP_AUTOMATIC_REHASH'], language: 'c')
5 |
6 | # versioning
7 | git_version_hash = run_command(['git', 'rev-parse', '--short', 'HEAD'], capture: true)
8 | add_project_arguments(['-DCWC_VERSION="0.0.1"', '-DCWC_GITHASH="'+git_version_hash.stdout().strip()+'"'], language: 'c')
9 |
10 | cwc_datadir = '/usr/share/cwc'
11 | add_project_arguments(['-DCWC_DATADIR="' + cwc_datadir + '"'], language: 'c')
12 |
13 | cc = meson.get_compiler('c')
14 | proj_dir = meson.current_source_dir()
15 |
16 | # dependencies (required)
17 | wayland_server = dependency('wayland-server')
18 | hyprcursor = dependency('hyprcursor')
19 | cairo = dependency('cairo')
20 | xkbcommon = dependency('xkbcommon')
21 | libinput = dependency('libinput')
22 | xxhash = dependency('libxxhash')
23 | lua = dependency('luajit')
24 | xcb = dependency('xcb')
25 | libm = cc.find_library('m', required : true)
26 | libdl = cc.find_library('dl', required : true)
27 |
28 | # wlroots as subproject, if any
29 | wlroots_version = ['>=0.19.0', '<0.20.0']
30 | subproject(
31 | 'wlroots',
32 | default_options: ['examples=false'],
33 | required: false,
34 | version: wlroots_version,
35 | )
36 | wlr = dependency('wlroots-0.19', version: wlroots_version, fallback: 'wlroots')
37 |
38 | all_deps = [
39 | wayland_server,
40 | wlr,
41 | hyprcursor,
42 | cairo,
43 | xkbcommon,
44 | libinput,
45 | xxhash,
46 | lua,
47 | xcb,
48 | libm,
49 | libdl,
50 | ]
51 |
52 | cwc_inc = include_directories('include')
53 |
54 | # protocols
55 | wlr_files = []
56 | subdir('protocol')
57 |
58 | subdir('src')
59 | subdir('cwctl')
60 |
61 | if get_option('plugins')
62 | subdir('plugins')
63 | endif
64 |
65 | if get_option('tests')
66 | subdir('tests')
67 | endif
68 |
69 | # install script
70 | install_subdir('lib', install_dir: cwc_datadir)
71 | install_subdir('defconfig', install_dir: cwc_datadir)
72 | install_subdir('doc', install_dir: '/usr/share/doc/cwc')
73 | install_subdir('include/cwc', install_dir: '/usr/include')
74 | install_data('cwc.desktop', install_dir: '/usr/share/wayland-sessions')
75 | install_data('cwc-portals.conf', install_dir: '/usr/share/xdg-desktop-portal')
76 |
--------------------------------------------------------------------------------
/meson_options.txt:
--------------------------------------------------------------------------------
1 | option('plugins', type: 'boolean', value: true, description: 'build plugins')
2 | option('tests', type: 'boolean', value: false, description: 'build tests')
3 |
4 | ### planned
5 | # option('xwayland', type: 'boolean', value: true, description: 'build with xwayland support')
6 |
--------------------------------------------------------------------------------
/plugins/flayout.c:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include "cwc/desktop/output.h"
4 | #include "cwc/desktop/toplevel.h"
5 | #include "cwc/layout/container.h"
6 | #include "cwc/layout/master.h"
7 | #include "cwc/plugin.h"
8 |
9 | static void arrange_flayout(struct cwc_toplevel **toplevels,
10 | int len,
11 | struct cwc_output *output,
12 | struct master_state *master_state)
13 | {
14 | int i = 0;
15 | struct cwc_toplevel *toplevel = toplevels[i];
16 |
17 | while (toplevel) {
18 | cwc_container_set_position_gap(toplevel->container, 0, 0);
19 | cwc_container_set_size(toplevel->container,
20 | output->output_layout_box.width,
21 | output->output_layout_box.height);
22 |
23 | toplevel = toplevels[++i];
24 | }
25 | }
26 |
27 | struct layout_interface fullscreen_impl = {.name = "fullscreen",
28 | .arrange = arrange_flayout};
29 |
30 | static int init()
31 | {
32 | master_register_layout(&fullscreen_impl);
33 |
34 | return 0;
35 | }
36 |
37 | /* crash when removing layout while its the current layout
38 | * TODO: refactor layout list to array instead of linked list.
39 | */
40 | static void fini()
41 | {
42 | master_unregister_layout(&fullscreen_impl);
43 | }
44 |
45 | plugin_init(init);
46 | plugin_exit(fini);
47 |
48 | PLUGIN_NAME("flayout");
49 | PLUGIN_VERSION("0.1.0");
50 | PLUGIN_DESCRIPTION("f layout we go f screen");
51 | PLUGIN_LICENSE("MIT");
52 | PLUGIN_AUTHOR("Dwi Asmoro Bangun ");
53 |
--------------------------------------------------------------------------------
/plugins/meson.build:
--------------------------------------------------------------------------------
1 | plugdir = cwc_datadir / 'plugins'
2 |
3 | shared_module(
4 | 'cwcle', 'cwcle.c',
5 | protocols_server_header['xdg-shell'],
6 | include_directories : cwc_inc,
7 | dependencies: [wlr, cairo, lua],
8 | name_prefix: '',
9 | install: true,
10 | install_dir: plugdir
11 | )
12 |
13 | shared_module(
14 | 'flayout', 'flayout.c',
15 | protocols_server_header['xdg-shell'],
16 | include_directories : cwc_inc,
17 | dependencies: [wlr],
18 | name_prefix: '',
19 | install: true,
20 | install_dir: plugdir
21 | )
22 |
--------------------------------------------------------------------------------
/protocol/meson.build:
--------------------------------------------------------------------------------
1 | wayland_protos = dependency('wayland-protocols',
2 | version: '>=1.35',
3 | fallback: 'wayland-protocols',
4 | default_options: ['tests=false'],
5 | )
6 | wl_protocol_dir = wayland_protos.get_variable('pkgdatadir')
7 |
8 | wayland_scanner_dep = dependency('wayland-scanner', native: true)
9 | wayland_scanner = find_program(
10 | wayland_scanner_dep.get_variable('wayland_scanner'),
11 | native: true,
12 | )
13 |
14 | protocols = {
15 | # Stable upstream protocols
16 | 'linux-dmabuf-v1': wl_protocol_dir / 'stable/linux-dmabuf/linux-dmabuf-v1.xml',
17 | 'presentation-time': wl_protocol_dir / 'stable/presentation-time/presentation-time.xml',
18 | 'tablet-v2': wl_protocol_dir / 'stable/tablet/tablet-v2.xml',
19 | 'viewporter': wl_protocol_dir / 'stable/viewporter/viewporter.xml',
20 | 'xdg-shell': wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml',
21 |
22 | # Staging upstream protocols
23 | 'alpha-modifier-v1': wl_protocol_dir / 'staging/alpha-modifier/alpha-modifier-v1.xml',
24 | 'content-type-v1': wl_protocol_dir / 'staging/content-type/content-type-v1.xml',
25 | 'cursor-shape-v1': wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml',
26 | 'drm-lease-v1': wl_protocol_dir / 'staging/drm-lease/drm-lease-v1.xml',
27 | 'ext-foreign-toplevel-list-v1': wl_protocol_dir / 'staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-v1.xml',
28 | 'ext-idle-notify-v1': wl_protocol_dir / 'staging/ext-idle-notify/ext-idle-notify-v1.xml',
29 | 'ext-image-capture-source-v1': wl_protocol_dir / 'staging/ext-image-capture-source/ext-image-capture-source-v1.xml',
30 | 'ext-image-copy-capture-v1': wl_protocol_dir / 'staging/ext-image-copy-capture/ext-image-copy-capture-v1.xml',
31 | 'ext-session-lock-v1': wl_protocol_dir / 'staging/ext-session-lock/ext-session-lock-v1.xml',
32 | 'fractional-scale-v1': wl_protocol_dir / 'staging/fractional-scale/fractional-scale-v1.xml',
33 | 'linux-drm-syncobj-v1': wl_protocol_dir / 'staging/linux-drm-syncobj/linux-drm-syncobj-v1.xml',
34 | 'security-context-v1': wl_protocol_dir / 'staging/security-context/security-context-v1.xml',
35 | 'single-pixel-buffer-v1': wl_protocol_dir / 'staging/single-pixel-buffer/single-pixel-buffer-v1.xml',
36 | 'xdg-activation-v1': wl_protocol_dir / 'staging/xdg-activation/xdg-activation-v1.xml',
37 | 'xwayland-shell-v1': wl_protocol_dir / 'staging/xwayland-shell/xwayland-shell-v1.xml',
38 | 'tearing-control-v1': wl_protocol_dir / 'staging/tearing-control/tearing-control-v1.xml',
39 |
40 | # Unstable upstream protocols
41 | 'fullscreen-shell-unstable-v1': wl_protocol_dir / 'unstable/fullscreen-shell/fullscreen-shell-unstable-v1.xml',
42 | 'idle-inhibit-unstable-v1': wl_protocol_dir / 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml',
43 | 'keyboard-shortcuts-inhibit-unstable-v1': wl_protocol_dir / 'unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml',
44 | 'pointer-constraints-unstable-v1': wl_protocol_dir / 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml',
45 | 'pointer-gestures-unstable-v1': wl_protocol_dir / 'unstable/pointer-gestures/pointer-gestures-unstable-v1.xml',
46 | 'primary-selection-unstable-v1': wl_protocol_dir / 'unstable/primary-selection/primary-selection-unstable-v1.xml',
47 | 'relative-pointer-unstable-v1': wl_protocol_dir / 'unstable/relative-pointer/relative-pointer-unstable-v1.xml',
48 | 'text-input-unstable-v3': wl_protocol_dir / 'unstable/text-input/text-input-unstable-v3.xml',
49 | 'xdg-decoration-unstable-v1': wl_protocol_dir / 'unstable/xdg-decoration/xdg-decoration-unstable-v1.xml',
50 | 'xdg-foreign-unstable-v1': wl_protocol_dir / 'unstable/xdg-foreign/xdg-foreign-unstable-v1.xml',
51 | 'xdg-foreign-unstable-v2': wl_protocol_dir / 'unstable/xdg-foreign/xdg-foreign-unstable-v2.xml',
52 | 'xdg-output-unstable-v1': wl_protocol_dir / 'unstable/xdg-output/xdg-output-unstable-v1.xml',
53 | 'ext-transient-seat-v1': wl_protocol_dir / 'staging/ext-transient-seat/ext-transient-seat-v1.xml',
54 |
55 | # Other protocols
56 | 'wlr-layer-shell-unstable-v1': 'wlr-layer-shell-unstable-v1.xml',
57 | 'wlr-output-power-management-unstable-v1': 'wlr-output-power-management-unstable-v1.xml',
58 |
59 | }
60 |
61 | protocols_code = {}
62 | protocols_server_header = {}
63 | protocols_client_header = {}
64 | foreach name, path : protocols
65 | code = custom_target(
66 | name.underscorify() + '_c',
67 | input: path,
68 | output: '@BASENAME@-protocol.c',
69 | command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'],
70 | )
71 | wlr_files += code
72 |
73 | server_header = custom_target(
74 | name.underscorify() + '_server_h',
75 | input: path,
76 | output: '@BASENAME@-protocol.h',
77 | command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'],
78 | )
79 | wlr_files += server_header
80 |
81 | client_header = custom_target(
82 | name.underscorify() + '_client_h',
83 | input: path,
84 | output: '@BASENAME@-client-protocol.h',
85 | command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'],
86 | build_by_default: false,
87 | )
88 |
89 | protocols_code += { name: code }
90 | protocols_server_header += { name: server_header }
91 | protocols_client_header += { name: client_header }
92 | endforeach
93 |
94 |
--------------------------------------------------------------------------------
/protocol/wlr-output-power-management-unstable-v1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Copyright © 2019 Purism SPC
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a
7 | copy of this software and associated documentation files (the "Software"),
8 | to deal in the Software without restriction, including without limitation
9 | the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 | and/or sell copies of the Software, and to permit persons to whom the
11 | Software is furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice (including the next
14 | paragraph) shall be included in all copies or substantial portions of the
15 | Software.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 | DEALINGS IN THE SOFTWARE.
24 |
25 |
26 |
27 | This protocol allows clients to control power management modes
28 | of outputs that are currently part of the compositor space. The
29 | intent is to allow special clients like desktop shells to power
30 | down outputs when the system is idle.
31 |
32 | To modify outputs not currently part of the compositor space see
33 | wlr-output-management.
34 |
35 | Warning! The protocol described in this file is experimental and
36 | backward incompatible changes may be made. Backward compatible changes
37 | may be added together with the corresponding interface version bump.
38 | Backward incompatible changes are done by bumping the version number in
39 | the protocol and interface names and resetting the interface version.
40 | Once the protocol is to be declared stable, the 'z' prefix and the
41 | version number in the protocol and interface names are removed and the
42 | interface version number is reset.
43 |
44 |
45 |
46 |
47 | This interface is a manager that allows creating per-output power
48 | management mode controls.
49 |
50 |
51 |
52 |
53 | Create a output power management mode control that can be used to
54 | adjust the power management mode for a given output.
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | All objects created by the manager will still remain valid, until their
63 | appropriate destroy request has been called.
64 |
65 |
66 |
67 |
68 |
69 |
70 | This object offers requests to set the power management mode of
71 | an output.
72 |
73 |
74 |
75 |
77 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 | Set an output's power save mode to the given mode. The mode change
88 | is effective immediately. If the output does not support the given
89 | mode a failed event is sent.
90 |
91 |
92 |
93 |
94 |
95 |
96 | Report the power management mode change of an output.
97 |
98 | The mode event is sent after an output changed its power
99 | management mode. The reason can be a client using set_mode or the
100 | compositor deciding to change an output's mode.
101 | This event is also sent immediately when the object is created
102 | so the client is informed about the current power management mode.
103 |
104 |
106 |
107 |
108 |
109 |
110 | This event indicates that the output power management mode control
111 | is no longer valid. This can happen for a number of reasons,
112 | including:
113 | - The output doesn't support power management
114 | - Another client already has exclusive power management mode control
115 | for this output
116 | - The output disappeared
117 |
118 | Upon receiving this event, the client should destroy this object.
119 |
120 |
121 |
122 |
123 |
124 | Destroys the output power management mode control object.
125 |
126 |
127 |
128 |
129 |
--------------------------------------------------------------------------------
/src/config.c:
--------------------------------------------------------------------------------
1 | /* config.c - configuration management
2 | *
3 | * Copyright (C) 2024 Dwi Asmoro Bangun
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 |
25 | #include "cwc/config.h"
26 | #include "cwc/util.h"
27 |
28 | void cwc_config_init()
29 | {
30 | cwc_config_set_default();
31 | g_config.CWC_PRIVATE.old_config = malloc(sizeof(struct cwc_config));
32 | memcpy(g_config.CWC_PRIVATE.old_config, &g_config, sizeof(g_config));
33 | wl_signal_init(&g_config.events.commit);
34 | }
35 |
36 | void cwc_config_commit()
37 | {
38 | cwc_log(CWC_INFO, "config committed");
39 | wl_signal_emit(&g_config.events.commit, g_config.CWC_PRIVATE.old_config);
40 | memcpy(g_config.CWC_PRIVATE.old_config, &g_config, sizeof(g_config));
41 | }
42 |
43 | void cwc_config_set_default()
44 | {
45 | g_config.border_color_rotation_degree = 0;
46 | g_config.border_color_focus = NULL;
47 | g_config.border_color_normal =
48 | cairo_pattern_create_rgba(127 / 255.0, 127 / 255.0, 127 / 255.0, 1);
49 | g_config.useless_gaps = 0;
50 | g_config.border_width = 1;
51 |
52 | g_config.warp_cursor_to_edge_on_resize = false;
53 | g_config.move_cursor_on_focus = false;
54 |
55 | g_config.cursor_size = 24;
56 | g_config.cursor_inactive_timeout_ms = 5000;
57 | g_config.cursor_edge_threshold = 16;
58 | g_config.cursor_edge_snapping_overlay_color[0] = 0.1;
59 | g_config.cursor_edge_snapping_overlay_color[1] = 0.2;
60 | g_config.cursor_edge_snapping_overlay_color[2] = 0.4;
61 | g_config.cursor_edge_snapping_overlay_color[3] = 0.1;
62 |
63 | g_config.repeat_rate = 30;
64 | g_config.repeat_delay = 400;
65 | }
66 |
67 | void cwc_config_set_cairo_pattern(cairo_pattern_t **dst, cairo_pattern_t *src)
68 | {
69 | if (g_config.border_color_focus)
70 | cairo_pattern_destroy(*dst);
71 |
72 | *dst = cairo_pattern_reference(src);
73 | }
74 |
75 | void cwc_config_set_number_positive(int *dest, int src)
76 | {
77 | *dest = MAX(0, src);
78 | }
79 |
--------------------------------------------------------------------------------
/src/desktop/idle.c:
--------------------------------------------------------------------------------
1 | /* idle.c - define idle behavior
2 | *
3 | * Copyright (C) 2024 Dwi Asmoro Bangun
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #include
20 | #include
21 | #include
22 | #include
23 |
24 | #include "cwc/desktop/idle.h"
25 | #include "cwc/desktop/toplevel.h"
26 | #include "cwc/server.h"
27 | #include "cwc/util.h"
28 |
29 | /* return the inhibitor count that visible */
30 | static int get_valid_idle_inhibitor_count()
31 | {
32 | int count = 0;
33 |
34 | struct wlr_idle_inhibitor_v1 *inhibitor;
35 | wl_list_for_each(inhibitor, &server.idle->inhibit_manager->inhibitors, link)
36 | {
37 | struct cwc_toplevel *toplevel =
38 | cwc_toplevel_try_from_wlr_surface(inhibitor->surface);
39 |
40 | if (toplevel && !cwc_toplevel_is_visible(toplevel))
41 | continue;
42 |
43 | count++;
44 | }
45 |
46 | return count;
47 | }
48 |
49 | void update_idle_inhibitor(void *data)
50 | {
51 | if (get_valid_idle_inhibitor_count() < 1)
52 | wlr_idle_notifier_v1_set_inhibited(server.idle->idle_notifier, false);
53 | else
54 | wlr_idle_notifier_v1_set_inhibited(server.idle->idle_notifier, true);
55 | }
56 |
57 | static void on_destroy_inhibitor(struct wl_listener *listener, void *data)
58 | {
59 | LISTEN_DESTROY(listener);
60 | cwc_log(CWC_DEBUG, "idle inhibitor destroyed: %p", data);
61 |
62 | /* let wlroots remove the inhibitors in the list first before updating */
63 | wl_event_loop_add_idle(server.wl_event_loop, update_idle_inhibitor, data);
64 | }
65 |
66 | static void on_new_inhibitor(struct wl_listener *listener, void *data)
67 | {
68 | struct cwc_idle *idle = wl_container_of(listener, idle, new_inhibitor_l);
69 | struct wlr_idle_inhibitor_v1 *wlr_inhibitor = data;
70 | wlr_inhibitor->data = idle;
71 |
72 | cwc_log(CWC_DEBUG, "idle inhibitor created: %p %p", wlr_inhibitor,
73 | wlr_inhibitor->surface);
74 |
75 | LISTEN_CREATE(&wlr_inhibitor->events.destroy, on_destroy_inhibitor);
76 |
77 | update_idle_inhibitor(NULL);
78 | }
79 |
80 | void cwc_idle_init(struct cwc_server *s)
81 | {
82 | struct cwc_idle *idle = calloc(1, sizeof(*idle));
83 | s->idle = idle;
84 | idle->server = s;
85 |
86 | idle->inhibit_manager = wlr_idle_inhibit_v1_create(s->wl_display);
87 | idle->idle_notifier = wlr_idle_notifier_v1_create(s->wl_display);
88 | idle->new_inhibitor_l.notify = on_new_inhibitor;
89 | wl_signal_add(&idle->inhibit_manager->events.new_inhibitor,
90 | &idle->new_inhibitor_l);
91 | }
92 |
93 | void cwc_idle_fini(struct cwc_server *s)
94 | {
95 | wl_list_remove(&s->idle->new_inhibitor_l.link);
96 | free(s->idle);
97 | s->idle = NULL;
98 | }
99 |
--------------------------------------------------------------------------------
/src/desktop/session_lock.c:
--------------------------------------------------------------------------------
1 | /* session_lock.c - session lock protocol implementation
2 | *
3 | * Copyright (C) 2024 Dwi Asmoro Bangun
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 |
25 | #include "cwc/desktop/output.h"
26 | #include "cwc/desktop/session_lock.h"
27 | #include "cwc/input/keyboard.h"
28 | #include "cwc/server.h"
29 | #include "cwc/util.h"
30 |
31 | static void on_surface_map(struct wl_listener *listener, void *data)
32 | {
33 | struct cwc_output *output =
34 | wl_container_of(listener, output, surface_map_l);
35 |
36 | keyboard_focus_surface(server.seat, output->lock_surface->surface);
37 | }
38 |
39 | static void on_surface_destroy(struct wl_listener *listener, void *data)
40 | {
41 | struct cwc_output *output =
42 | wl_container_of(listener, output, surface_destroy_l);
43 |
44 | wl_list_remove(&output->surface_map_l.link);
45 | wl_list_remove(&output->surface_destroy_l.link);
46 |
47 | output->lock_surface = NULL;
48 | }
49 |
50 | static void on_unlock(struct wl_listener *listener, void *data)
51 | {
52 | struct cwc_session_locker *locker =
53 | wl_container_of(listener, locker, unlock_l);
54 | struct cwc_session_lock_manager *mgr = locker->manager;
55 |
56 | cwc_log(CWC_DEBUG, "unlocking session lock: %p", locker);
57 |
58 | // unset state
59 | mgr->locked = false;
60 | mgr->locker = NULL;
61 | cwc_output_focus_newest_focus_visible_toplevel(server.focused_output);
62 | }
63 |
64 | static void on_new_surface(struct wl_listener *listener, void *data)
65 | {
66 | struct cwc_session_locker *locker =
67 | wl_container_of(listener, locker, new_surface_l);
68 | struct wlr_session_lock_surface_v1 *lock_surface = data;
69 |
70 | struct cwc_output *output = lock_surface->output->data;
71 | output->lock_surface = lock_surface;
72 |
73 | wlr_scene_subsurface_tree_create(output->layers.session_lock,
74 | lock_surface->surface);
75 |
76 | output->surface_map_l.notify = on_surface_map;
77 | output->surface_destroy_l.notify = on_surface_destroy;
78 | wl_signal_add(&lock_surface->surface->events.map, &output->surface_map_l);
79 | wl_signal_add(&lock_surface->surface->events.destroy,
80 | &output->surface_destroy_l);
81 |
82 | wlr_session_lock_surface_v1_configure(lock_surface,
83 | output->output_layout_box.width,
84 | output->output_layout_box.height);
85 | }
86 |
87 | static void on_lock_destroy(struct wl_listener *listener, void *data)
88 | {
89 | struct cwc_session_locker *locker =
90 | wl_container_of(listener, locker, destroy_l);
91 |
92 | cwc_log(CWC_DEBUG, "destroying session lock: %p", locker);
93 |
94 | wl_list_remove(&locker->unlock_l.link);
95 | wl_list_remove(&locker->new_surface_l.link);
96 | wl_list_remove(&locker->destroy_l.link);
97 | free(locker);
98 | }
99 |
100 | static void on_new_lock(struct wl_listener *listener, void *data)
101 | {
102 | struct cwc_session_lock_manager *mgr =
103 | wl_container_of(listener, mgr, new_lock_l);
104 | struct wlr_session_lock_v1 *wlr_sesslock = data;
105 |
106 | if (mgr->locked) {
107 | wlr_session_lock_v1_destroy(wlr_sesslock);
108 | cwc_log(CWC_ERROR, "attempt to lock an already locked session");
109 | return;
110 | }
111 |
112 | struct cwc_session_locker *locker = calloc(1, sizeof(*locker));
113 | locker->locker = wlr_sesslock;
114 | locker->manager = mgr;
115 |
116 | locker->unlock_l.notify = on_unlock;
117 | locker->new_surface_l.notify = on_new_surface;
118 | locker->destroy_l.notify = on_lock_destroy;
119 | wl_signal_add(&wlr_sesslock->events.unlock, &locker->unlock_l);
120 | wl_signal_add(&wlr_sesslock->events.new_surface, &locker->new_surface_l);
121 | wl_signal_add(&wlr_sesslock->events.destroy, &locker->destroy_l);
122 |
123 | cwc_log(CWC_DEBUG, "locking session: %p", locker);
124 |
125 | wlr_session_lock_v1_send_locked(locker->locker);
126 | mgr->locked = true;
127 | mgr->locker = locker;
128 | }
129 |
130 | static void on_session_lock_destroy(struct wl_listener *listener, void *data)
131 | {
132 | struct cwc_session_lock_manager *mgr =
133 | wl_container_of(listener, mgr, destroy_l);
134 |
135 | wl_list_remove(&mgr->new_lock_l.link);
136 | wl_list_remove(&mgr->destroy_l.link);
137 | mgr->manager = NULL;
138 | mgr->server->session_lock = NULL;
139 |
140 | free(mgr);
141 | }
142 |
143 | void setup_cwc_session_lock(struct cwc_server *s)
144 | {
145 | struct cwc_session_lock_manager *mgr = calloc(1, sizeof(*mgr));
146 | s->session_lock = mgr;
147 | mgr->manager = wlr_session_lock_manager_v1_create(s->wl_display);
148 | mgr->server = s;
149 |
150 | mgr->new_lock_l.notify = on_new_lock;
151 | mgr->destroy_l.notify = on_session_lock_destroy;
152 | wl_signal_add(&mgr->manager->events.new_lock, &mgr->new_lock_l);
153 | wl_signal_add(&mgr->manager->events.destroy, &mgr->destroy_l);
154 | }
155 |
156 | void cleanup_cwc_session_lock(struct cwc_server *s)
157 | {
158 | ;
159 | }
160 |
--------------------------------------------------------------------------------
/src/desktop/transaction.c:
--------------------------------------------------------------------------------
1 | /* transaction.c - actually just a simple scheduler
2 | *
3 | * Copyright (C) 2025 Dwi Asmoro Bangun
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #include
20 | #include
21 | #include
22 |
23 | #include "cwc/desktop/layer_shell.h"
24 | #include "cwc/desktop/transaction.h"
25 | #include "cwc/server.h"
26 |
27 | static struct transaction {
28 | struct wl_event_source *idle_source;
29 |
30 | struct wl_array tags; // struct cwc_tag_info*
31 |
32 | bool output_pending;
33 | bool paused;
34 | bool processing; // prevent scheduling loop
35 | } T = {0};
36 |
37 | static inline void _process_pending_outputs(struct cwc_output *output)
38 | {
39 |
40 | if (!cwc_output_is_exist(output) || !output->pending_transaction)
41 | return;
42 |
43 | if (output->pending.committed) {
44 | wlr_output_commit_state(output->wlr_output, &output->pending);
45 | wlr_output_state_finish(&output->pending);
46 | wlr_output_state_init(&output->pending);
47 | }
48 |
49 | arrange_layers(output);
50 | cwc_output_update_visible(output);
51 | output->pending_transaction = false;
52 | }
53 |
54 | static inline void _process_pending_tag(struct cwc_tag_info *tag)
55 | {
56 | struct cwc_output *output = cwc_output_from_tag_info(tag);
57 | if (!cwc_output_is_exist(output))
58 | return;
59 |
60 | cwc_output_tiling_layout_update(output, tag->index);
61 | tag->pending_transaction = false;
62 | }
63 |
64 | static void process_pending(void *data)
65 | {
66 | T.processing = true;
67 |
68 | if (T.output_pending) {
69 | struct cwc_output *output;
70 | wl_list_for_each(output, &server.outputs, link)
71 | {
72 | _process_pending_outputs(output);
73 | }
74 |
75 | cwc_output_update_outputs_state();
76 | T.output_pending = false;
77 | }
78 |
79 | if (T.tags.size) {
80 | struct cwc_tag_info **tag;
81 | wl_array_for_each(tag, &T.tags)
82 | {
83 | _process_pending_tag(*tag);
84 | }
85 | wl_array_release(&T.tags);
86 | wl_array_init(&T.tags);
87 | }
88 |
89 | T.idle_source = NULL;
90 | T.processing = false;
91 | }
92 |
93 | static void transaction_start()
94 | {
95 | if (T.idle_source || T.paused)
96 | return;
97 |
98 | T.idle_source =
99 | wl_event_loop_add_idle(server.wl_event_loop, process_pending, NULL);
100 | }
101 |
102 | void transaction_pause()
103 | {
104 | if (T.idle_source)
105 | wl_event_source_remove(T.idle_source);
106 |
107 | T.idle_source = NULL;
108 | T.paused = true;
109 | }
110 |
111 | void transaction_resume()
112 | {
113 | T.paused = false;
114 | transaction_start();
115 | }
116 |
117 | void transaction_schedule_output(struct cwc_output *output)
118 | {
119 | if (T.processing)
120 | return;
121 |
122 | transaction_start();
123 | output->pending_transaction = true;
124 | T.output_pending = true;
125 | }
126 |
127 | void transaction_schedule_tag(struct cwc_tag_info *tag)
128 | {
129 | if (tag->pending_transaction || T.processing)
130 | return;
131 |
132 | struct cwc_tag_info **elem = wl_array_add(&T.tags, sizeof(&tag));
133 | *elem = tag;
134 | tag->pending_transaction = true;
135 |
136 | transaction_start();
137 | }
138 |
139 | void setup_transaction(struct cwc_server *s)
140 | {
141 | wl_array_init(&T.tags);
142 | }
143 |
--------------------------------------------------------------------------------
/src/input/manager.c:
--------------------------------------------------------------------------------
1 | /* manager.c - input device management
2 | *
3 | * Copyright (C) 2025 Dwi Asmoro Bangun
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 |
31 | #include "cwc/config.h"
32 | #include "cwc/desktop/output.h"
33 | #include "cwc/input/cursor.h"
34 | #include "cwc/input/keyboard.h"
35 | #include "cwc/input/manager.h"
36 | #include "cwc/input/seat.h"
37 | #include "cwc/luaclass.h"
38 | #include "cwc/luaobject.h"
39 | #include "cwc/server.h"
40 | #include "cwc/signal.h"
41 |
42 | // input manager singleton
43 | static struct cwc_input_manager *input_manager = NULL;
44 |
45 | static void on_libinput_device_destroy(struct wl_listener *listener, void *data)
46 | {
47 | struct cwc_libinput_device *dev = wl_container_of(listener, dev, destroy_l);
48 |
49 | lua_State *L = g_config_get_lua_State();
50 | cwc_object_emit_signal_simple("input::destroy", L, dev);
51 | luaC_object_unregister(L, dev);
52 |
53 | wl_list_remove(&dev->link);
54 | wl_list_remove(&dev->destroy_l.link);
55 |
56 | free(dev);
57 | }
58 |
59 | static void on_new_input(struct wl_listener *listener, void *data)
60 | {
61 | struct cwc_input_manager *mgr = wl_container_of(listener, mgr, new_input_l);
62 | struct wlr_input_device *device = data;
63 |
64 | switch (device->type) {
65 | case WLR_INPUT_DEVICE_POINTER:
66 | cwc_seat_add_pointer_device(server.seat, device);
67 | break;
68 | case WLR_INPUT_DEVICE_KEYBOARD:
69 | cwc_seat_add_keyboard_device(server.seat, device);
70 | break;
71 | case WLR_INPUT_DEVICE_SWITCH:
72 | // cwc_seat_add_switch_device(server.seat, device);
73 | break;
74 | case WLR_INPUT_DEVICE_TABLET:
75 | // cwc_seat_add_tablet_device(server.seat, device);
76 | break;
77 | case WLR_INPUT_DEVICE_TABLET_PAD:
78 | // cwc_seat_add_tablet_pad_device(server.seat, device);
79 | break;
80 | case WLR_INPUT_DEVICE_TOUCH:
81 | // cwc_seat_add_touch_device(server.seat, device);
82 | break;
83 | }
84 |
85 | if (wlr_input_device_is_libinput(device)) {
86 | struct cwc_libinput_device *libinput_dev =
87 | calloc(1, sizeof(*libinput_dev));
88 | libinput_dev->device = wlr_libinput_get_device_handle(device);
89 | libinput_dev->wlr_input_dev = device;
90 |
91 | libinput_dev->destroy_l.notify = on_libinput_device_destroy;
92 | wl_signal_add(&device->events.destroy, &libinput_dev->destroy_l);
93 |
94 | wl_list_insert(server.input->devices.prev, &libinput_dev->link);
95 |
96 | lua_State *L = g_config_get_lua_State();
97 | luaC_object_input_register(L, libinput_dev);
98 | cwc_object_emit_signal_simple("input::new", L, libinput_dev);
99 | }
100 | }
101 |
102 | struct cwc_input_manager *cwc_input_manager_get()
103 | {
104 | if (input_manager)
105 | return input_manager;
106 |
107 | input_manager = calloc(1, sizeof(*input_manager));
108 | if (!input_manager)
109 | exit(EXIT_FAILURE);
110 |
111 | // input_manager->tablet_manager = wlr_tablet_v2_create(server.wl_display);
112 |
113 | wl_list_init(&input_manager->devices);
114 | wl_list_init(&input_manager->seats);
115 |
116 | input_manager->new_input_l.notify = on_new_input;
117 | wl_signal_add(&server.backend->events.new_input,
118 | &input_manager->new_input_l);
119 |
120 | input_manager->relative_pointer_manager =
121 | wlr_relative_pointer_manager_v1_create(server.wl_display);
122 |
123 | return input_manager;
124 | }
125 |
126 | void cwc_input_manager_destroy()
127 | {
128 | wl_list_remove(&input_manager->new_input_l.link);
129 |
130 | free(input_manager);
131 | input_manager = NULL;
132 | }
133 |
134 | void cwc_input_manager_update_cursor_scale()
135 | {
136 | struct cwc_seat *seat;
137 | wl_list_for_each(seat, &server.input->seats, link)
138 | {
139 | cwc_cursor_update_scale(seat->cursor);
140 | wlr_cursor_move(seat->cursor->wlr_cursor, NULL, 0, 0);
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/src/input/switch.c:
--------------------------------------------------------------------------------
1 | /* switch.c - switch input device
2 | *
3 | * Copyright (C) 2025 Dwi Asmoro Bangun
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #include
20 | #include
21 | #include
22 | #include
23 |
24 | #include "cwc/input/seat.h"
25 | #include "cwc/input/switch.h"
26 |
27 | static void on_toggle(struct wl_listener *listener, void *data)
28 | {
29 | struct cwc_switch *swt = wl_container_of(listener, swt, toggle_l);
30 |
31 | // TODO: xxx
32 | }
33 |
34 | static void on_destroy(struct wl_listener *listener, void *data)
35 | {
36 | struct cwc_switch *swt = wl_container_of(listener, swt, destroy_l);
37 |
38 | cwc_switch_destroy(swt);
39 | }
40 |
41 | struct cwc_switch *cwc_switch_create(struct cwc_seat *seat,
42 | struct wlr_input_device *dev)
43 | {
44 | struct cwc_switch *swt = calloc(1, sizeof(*swt));
45 | if (!swt)
46 | return NULL;
47 |
48 | swt->seat = seat;
49 | swt->wlr_switch = wlr_switch_from_input_device(dev);
50 | swt->wlr_switch->data = swt;
51 |
52 | swt->toggle_l.notify = on_toggle;
53 | wl_signal_add(&swt->wlr_switch->events.toggle, &swt->toggle_l);
54 |
55 | swt->destroy_l.notify = on_destroy;
56 | wl_signal_add(&swt->wlr_switch->base.events.destroy, &swt->destroy_l);
57 |
58 | wl_list_insert(&seat->switch_devs, &swt->link);
59 |
60 | return swt;
61 | }
62 |
63 | void cwc_switch_destroy(struct cwc_switch *switch_dev)
64 | {
65 | wl_list_remove(&switch_dev->toggle_l.link);
66 | wl_list_remove(&switch_dev->destroy_l.link);
67 |
68 | wl_list_remove(&switch_dev->link);
69 |
70 | free(switch_dev);
71 | }
72 |
--------------------------------------------------------------------------------
/src/input/tablet.c:
--------------------------------------------------------------------------------
1 | /* tablet.c - tablet input device
2 | *
3 | * Copyright (C) 2025 Dwi Asmoro Bangun
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 |
26 | #include "cwc/input/manager.h"
27 | #include "cwc/input/seat.h"
28 | #include "cwc/input/tablet.h"
29 | #include "cwc/server.h"
30 |
31 | static void on_axis(struct wl_listener *listener, void *data)
32 | {
33 | struct wlr_tablet_tool_axis_event *event = data;
34 | }
35 |
36 | static void on_proximity(struct wl_listener *listener, void *data)
37 | {
38 | struct wlr_tablet_tool_proximity_event *event = data;
39 | }
40 |
41 | static void on_tip(struct wl_listener *listener, void *data)
42 | {
43 | struct wlr_tablet_tool_tip_event *event = data;
44 | }
45 |
46 | static void on_button(struct wl_listener *listener, void *data)
47 | {
48 | struct wlr_tablet_tool_button_event *event = data;
49 | }
50 |
51 | struct cwc_tablet *cwc_tablet_create(struct cwc_seat *seat,
52 | struct wlr_input_device *dev)
53 | {
54 | struct cwc_tablet *tablet = calloc(1, sizeof(*tablet));
55 | if (!tablet)
56 | return NULL;
57 |
58 | tablet->seat = seat;
59 | tablet->tablet =
60 | wlr_tablet_create(server.input->tablet_manager, seat->wlr_seat, dev);
61 |
62 | tablet->axis_l.notify = on_axis;
63 | tablet->proximity_l.notify = on_proximity;
64 | tablet->tip_l.notify = on_tip;
65 | tablet->button_l.notify = on_button;
66 | wl_signal_add(&tablet->tablet->wlr_tablet->events.axis, &tablet->axis_l);
67 | wl_signal_add(&tablet->tablet->wlr_tablet->events.proximity,
68 | &tablet->proximity_l);
69 | wl_signal_add(&tablet->tablet->wlr_tablet->events.tip, &tablet->tip_l);
70 | wl_signal_add(&tablet->tablet->wlr_tablet->events.button,
71 | &tablet->button_l);
72 |
73 | wl_list_insert(&seat->tablet_devs, &tablet->link);
74 |
75 | return tablet;
76 | }
77 |
78 | void cwc_tablet_destroy(struct cwc_tablet *tablet)
79 | {
80 | wl_list_remove(&tablet->axis_l.link);
81 | wl_list_remove(&tablet->proximity_l.link);
82 | wl_list_remove(&tablet->tip_l.link);
83 | wl_list_remove(&tablet->button_l.link);
84 |
85 | wl_list_remove(&tablet->link);
86 |
87 | free(tablet);
88 | }
89 |
90 | static void on_tpad_button(struct wl_listener *listener, void *data)
91 | {
92 | struct wlr_tablet_pad_button_event *event = data;
93 | }
94 |
95 | static void on_tpad_ring(struct wl_listener *listener, void *data)
96 | {
97 | struct wlr_tablet_pad_ring_event *event = data;
98 | }
99 |
100 | static void on_tpad_strip(struct wl_listener *listener, void *data)
101 | {
102 | struct wlr_tablet_pad_strip_event *event = data;
103 | }
104 |
105 | static void on_tpad_attach_tablet(struct wl_listener *listener, void *data)
106 | {
107 | struct wlr_tablet_tool *tablet = data;
108 | }
109 |
110 | struct cwc_tablet_pad *cwc_tablet_pad_create(struct cwc_seat *seat,
111 | struct wlr_input_device *dev)
112 | {
113 | struct cwc_tablet_pad *tpad = calloc(1, sizeof(*tpad));
114 | if (!tpad)
115 | return NULL;
116 |
117 | tpad->seat = seat;
118 | tpad->tablet_pad = wlr_tablet_pad_create(server.input->tablet_manager,
119 | seat->wlr_seat, dev);
120 |
121 | tpad->button_l.notify = on_tpad_button;
122 | tpad->ring_l.notify = on_tpad_ring;
123 | tpad->strip_l.notify = on_tpad_strip;
124 | tpad->attach_tablet_l.notify = on_tpad_attach_tablet;
125 | wl_signal_add(&tpad->tablet_pad->wlr_pad->events.button, &tpad->button_l);
126 | wl_signal_add(&tpad->tablet_pad->wlr_pad->events.ring, &tpad->ring_l);
127 | wl_signal_add(&tpad->tablet_pad->wlr_pad->events.strip, &tpad->strip_l);
128 | wl_signal_add(&tpad->tablet_pad->wlr_pad->events.attach_tablet,
129 | &tpad->attach_tablet_l);
130 |
131 | wl_list_insert(&seat->tablet_pad_devs, &tpad->link);
132 |
133 | return tpad;
134 | }
135 |
136 | void cwc_tablet_pad_destroy(struct cwc_tablet_pad *tpad)
137 | {
138 | wl_list_remove(&tpad->button_l.link);
139 | wl_list_remove(&tpad->ring_l.link);
140 | wl_list_remove(&tpad->strip_l.link);
141 | wl_list_remove(&tpad->attach_tablet_l.link);
142 |
143 | wl_list_remove(&tpad->link);
144 |
145 | free(tpad);
146 | }
147 |
--------------------------------------------------------------------------------
/src/input/touch.c:
--------------------------------------------------------------------------------
1 | /* touch.c - touch input device
2 | *
3 | * Copyright (C) 2025 Dwi Asmoro Bangun
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #include
20 | #include
21 | #include
22 | #include
23 |
24 | #include "cwc/desktop/toplevel.h"
25 | #include "cwc/input/seat.h"
26 | #include "cwc/input/touch.h"
27 |
28 | static void on_down(struct wl_listener *listener, void *data)
29 | {
30 | struct cwc_touch *touch = wl_container_of(listener, touch, down_l);
31 | struct wlr_touch_down_event *event = data;
32 |
33 | double sx, sy;
34 | struct wlr_surface *surface =
35 | scene_surface_at(event->x, event->y, &sx, &sy);
36 |
37 | if (surface)
38 | wlr_seat_touch_notify_down(touch->seat->wlr_seat, surface,
39 | event->time_msec, event->touch_id, sx, sy);
40 | }
41 |
42 | static void on_up(struct wl_listener *listener, void *data)
43 | {
44 | struct cwc_touch *touch = wl_container_of(listener, touch, up_l);
45 | struct wlr_touch_up_event *event = data;
46 |
47 | wlr_seat_touch_notify_up(touch->seat->wlr_seat, event->time_msec,
48 | event->touch_id);
49 | }
50 |
51 | static void on_motion(struct wl_listener *listener, void *data)
52 | {
53 | struct cwc_touch *touch = wl_container_of(listener, touch, motion_l);
54 | struct wlr_touch_motion_event *event = data;
55 |
56 | double sx, sy;
57 | struct wlr_surface *surface =
58 | scene_surface_at(event->x, event->y, &sx, &sy);
59 |
60 | if (surface)
61 | wlr_seat_touch_notify_motion(touch->seat->wlr_seat, event->time_msec,
62 | event->touch_id, sx, sy);
63 | }
64 |
65 | static void on_cancel(struct wl_listener *listener, void *data)
66 | {
67 | struct cwc_touch *touch = wl_container_of(listener, touch, cancel_l);
68 | struct wlr_touch_cancel_event *event = data;
69 |
70 | // wlr_seat_touch_notify_cancel();
71 | }
72 |
73 | static void on_frame(struct wl_listener *listener, void *data)
74 | {
75 | struct cwc_touch *touch = wl_container_of(listener, touch, frame_l);
76 | wlr_seat_touch_notify_frame(touch->seat->wlr_seat);
77 | }
78 |
79 | static void on_destroy(struct wl_listener *listener, void *data)
80 | {
81 | struct cwc_touch *touch = wl_container_of(listener, touch, destroy_l);
82 |
83 | cwc_touch_destroy(touch);
84 | }
85 |
86 | struct cwc_touch *cwc_touch_create(struct cwc_seat *seat,
87 | struct wlr_input_device *dev)
88 | {
89 | struct cwc_touch *touch = calloc(1, sizeof(*touch));
90 | if (!touch)
91 | return NULL;
92 |
93 | touch->seat = seat;
94 | touch->wlr_touch = wlr_touch_from_input_device(dev);
95 | touch->wlr_touch->data = touch;
96 |
97 | touch->down_l.notify = on_down;
98 | touch->up_l.notify = on_up;
99 | touch->motion_l.notify = on_motion;
100 | touch->cancel_l.notify = on_cancel;
101 | touch->frame_l.notify = on_frame;
102 | wl_signal_add(&touch->wlr_touch->events.down, &touch->down_l);
103 | wl_signal_add(&touch->wlr_touch->events.up, &touch->up_l);
104 | wl_signal_add(&touch->wlr_touch->events.motion, &touch->motion_l);
105 | wl_signal_add(&touch->wlr_touch->events.cancel, &touch->cancel_l);
106 | wl_signal_add(&touch->wlr_touch->events.frame, &touch->frame_l);
107 |
108 | touch->destroy_l.notify = on_destroy;
109 | wl_signal_add(&dev->events.destroy, &touch->destroy_l);
110 |
111 | wl_list_insert(&seat->touch_devs, &touch->link);
112 |
113 | return touch;
114 | }
115 |
116 | void cwc_touch_destroy(struct cwc_touch *touch)
117 | {
118 | wl_list_remove(&touch->up_l.link);
119 | wl_list_remove(&touch->down_l.link);
120 | wl_list_remove(&touch->motion_l.link);
121 | wl_list_remove(&touch->cancel_l.link);
122 | wl_list_remove(&touch->frame_l.link);
123 |
124 | wl_list_remove(&touch->destroy_l.link);
125 |
126 | wl_list_remove(&touch->link);
127 |
128 | free(touch);
129 | }
130 |
--------------------------------------------------------------------------------
/src/ipc/common.c:
--------------------------------------------------------------------------------
1 | /* ipc/common.c - common functions for ipc message handling
2 | *
3 | * Copyright (C) 2025 Dwi Asmoro Bangun
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #include
20 |
21 | #include "cwc/ipc.h"
22 |
23 | int ipc_create_message_n(
24 | char *dest, int maxlen, enum cwc_ipc_opcode opcode, const char *body, int n)
25 | {
26 | int num_written = HEADER_SIZE + n;
27 |
28 | if (num_written > maxlen)
29 | return -1;
30 |
31 | strcpy(dest, IPC_HEADER);
32 | char opcstr[4] = {'\n', opcode, '\n', '\x00'};
33 | strcat(dest, opcstr);
34 | strncat(dest, body, n);
35 |
36 | return num_written;
37 | }
38 |
39 | int ipc_create_message(char *dest,
40 | int maxlen,
41 | enum cwc_ipc_opcode opcode,
42 | const char *body)
43 | {
44 | return ipc_create_message_n(dest, maxlen, opcode, body, strlen(body));
45 | }
46 |
47 | bool check_header(const char *msg)
48 | {
49 | int hlen = sizeof(IPC_HEADER);
50 |
51 | if (strncmp(IPC_HEADER, msg, hlen - 1) != 0)
52 | return false;
53 |
54 | if (msg[hlen - 1] == '\n' && msg[hlen + 1] == '\n')
55 | return true;
56 |
57 | return false;
58 | }
59 |
60 | const char *ipc_get_body(const char *msg, enum cwc_ipc_opcode *opcode)
61 | {
62 | if (!check_header(msg))
63 | return NULL;
64 |
65 | int hlen = sizeof(IPC_HEADER);
66 | if (opcode) {
67 | *opcode = msg[hlen];
68 | }
69 |
70 | return &msg[hlen + 2];
71 | }
72 |
--------------------------------------------------------------------------------
/src/luaclass.c:
--------------------------------------------------------------------------------
1 | /* luaclass.c - lua classified object management
2 | *
3 | * Copyright (C) 2024 Dwi Asmoro Bangun
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #include
20 | #include
21 |
22 | const char *const client_classname = "cwc_client";
23 | const char *const container_classname = "cwc_container";
24 | const char *const screen_classname = "cwc_screen";
25 | const char *const tag_classname = "cwc_tag";
26 | const char *const input_classname = "cwc_input";
27 | const char *const layer_shell_classname = "cwc_layer_shell";
28 | const char *const kbindmap_classname = "cwc_kbindmap";
29 | const char *const kbind_classname = "cwc_kbind";
30 |
31 | /* equivalent lua code:
32 | * function(t, k)
33 | *
34 | * local mt = getmetatable(t)
35 | * local index = mt.__cwcindex
36 | *
37 | * if index["get_" .. k] then return index["get_" .. k]() end
38 | *
39 | * return index[k]
40 | *
41 | * end
42 | */
43 | static int luaC_getter(lua_State *L)
44 | {
45 | if (!lua_getmetatable(L, 1))
46 | return 0;
47 |
48 | lua_getfield(L, -1, "__cwcindex");
49 |
50 | lua_pushstring(L, "get_");
51 | lua_pushvalue(L, 2);
52 | lua_concat(L, 2);
53 |
54 | lua_rawget(L, -2);
55 |
56 | if (lua_isfunction(L, -1)) {
57 | lua_pushvalue(L, 1);
58 | lua_call(L, 1, 1);
59 | return 1;
60 | }
61 |
62 | lua_pop(L, 1);
63 | lua_pushvalue(L, 2);
64 | lua_gettable(L, -2);
65 |
66 | return 1;
67 | }
68 |
69 | /* equivalent lua code:
70 | * function(t, k, v)
71 | *
72 | * local mt = getmetatable(t)
73 | * local index = mt.__cwcindex
74 | *
75 | * if not index["set_" .. k] then return end
76 | *
77 | * mt.[k](t, v)
78 | *
79 | * end
80 | */
81 | static int luaC_setter(lua_State *L)
82 | {
83 | if (!lua_getmetatable(L, 1))
84 | return 0;
85 |
86 | lua_getfield(L, -1, "__cwcindex");
87 |
88 | lua_pushstring(L, "set_");
89 | lua_pushvalue(L, 2);
90 | lua_concat(L, 2);
91 |
92 | lua_rawget(L, -2);
93 |
94 | if (lua_isnil(L, -1))
95 | return 0;
96 |
97 | lua_pushvalue(L, 1);
98 | lua_pushvalue(L, 3);
99 |
100 | lua_call(L, 2, 0);
101 |
102 | return 1;
103 | }
104 |
105 | /* methods that start with `get_` can be accessed without the prefix,
106 | * for example c:get_fullscreen() is the same as c.fullscreen
107 | *
108 | * [-0, +0, -]
109 | */
110 | void luaC_register_class(lua_State *L,
111 | const char *classname,
112 | luaL_Reg methods[],
113 | luaL_Reg metamethods[])
114 | {
115 | // create the metatable and register the metamethods other than
116 | // index and newindex
117 | luaL_newmetatable(L, classname);
118 | luaL_register(L, NULL, metamethods);
119 |
120 | lua_newtable(L);
121 | luaL_register(L, NULL, methods);
122 | lua_setfield(L, -2, "__cwcindex");
123 |
124 | lua_pushcfunction(L, luaC_getter);
125 | lua_setfield(L, -2, "__index");
126 |
127 | lua_pushcfunction(L, luaC_setter);
128 | lua_setfield(L, -2, "__newindex");
129 |
130 | // pop metatable
131 | lua_pop(L, 1);
132 | }
133 |
--------------------------------------------------------------------------------
/src/luaobject.c:
--------------------------------------------------------------------------------
1 | /* luaobject.c - lua object management
2 | *
3 | * Copyright (C) 2024 Dwi Asmoro Bangun
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #include
20 | #include
21 |
22 | #include "cwc/luaobject.h"
23 |
24 | const char *const LUAC_OBJECT_REGISTRY_KEY = "cwc.object.registry";
25 |
26 | /** Setup the object system at startup.
27 | * \param L The Lua VM state.
28 | */
29 | void luaC_object_setup(lua_State *L)
30 | {
31 | lua_pushstring(L, LUAC_OBJECT_REGISTRY_KEY);
32 | lua_newtable(L);
33 | lua_rawset(L, LUA_REGISTRYINDEX);
34 | }
35 |
--------------------------------------------------------------------------------
/src/main.c:
--------------------------------------------------------------------------------
1 | /* main.c - cwc entry point
2 | *
3 | * Copyright (C) 2024 Dwi Asmoro Bangun
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 |
28 | #include "cwc/config.h"
29 | #include "cwc/luac.h"
30 | #include "cwc/server.h"
31 |
32 | static char *help_txt = "Usage:\n"
33 | " cwc [options]\n"
34 | "\n"
35 | "Options:\n"
36 | " -h, --help show this message\n"
37 | " -v, --version show version\n"
38 | " -c, --config lua configuration file to use\n"
39 | " -k, --check check configuration file syntax\n"
40 | " -s, --startup startup command\n"
41 | " -l, --library library directory search path\n"
42 | " -d, --debug +increase debug verbosity level\n"
43 | "\n"
44 | "Example:\n"
45 | " cwc -c ~/test/rc.lua -dd";
46 |
47 | #define ARG 1
48 | #define NO_ARG 0
49 | static struct option long_options[] = {
50 | {"help", NO_ARG, NULL, 'h'},
51 | {"version", NO_ARG, NULL, 'v'},
52 | {"config", ARG, NULL, 'c'},
53 | {"check", NO_ARG, NULL, 'k'},
54 | {"startup", ARG, NULL, 's'},
55 | {"library", ARG, NULL, 'l'},
56 | {"debug", NO_ARG, NULL, 'd'},
57 | };
58 |
59 | // globals
60 | struct cwc_server server = {0};
61 | struct cwc_config g_config = {0};
62 | bool lua_initial_load = true;
63 | bool luacheck = false;
64 | char *config_path = NULL;
65 | char *library_path = NULL;
66 |
67 | static void sig_handler(int signal)
68 | {
69 | wl_display_terminate(server.wl_display);
70 | }
71 |
72 | /* entry point */
73 | int main(int argc, char **argv)
74 | {
75 | char *startup_cmd = NULL;
76 | int exit_value = 0;
77 | char log_level = WLR_ERROR;
78 |
79 | setenv("XDG_CURRENT_DESKTOP", "cwc", true);
80 | setenv("_JAVA_AWT_WM_NONREPARENTING", "1", true);
81 |
82 | int c;
83 | while ((c = getopt_long(argc, argv, "hvc:s:l:p:d", long_options, NULL))
84 | != -1)
85 | switch (c) {
86 | case 'd':
87 | log_level++;
88 | if (log_level > 3)
89 | log_level = 3;
90 | break;
91 | case 'v':
92 | printf("cwc v%s-%s\n", CWC_VERSION, CWC_GITHASH);
93 | return 0;
94 | case 'c':
95 | config_path = optarg;
96 | break;
97 | case 'k':
98 | luacheck = true;
99 | break;
100 | case 's':
101 | startup_cmd = optarg;
102 | break;
103 | case 'l':
104 | library_path = optarg;
105 | break;
106 | case 'h':
107 | puts(help_txt);
108 | return 0;
109 | default:
110 | puts(help_txt);
111 | return 1;
112 | }
113 |
114 | wlr_log_init(log_level, NULL);
115 | cwc_config_init();
116 |
117 | if ((exit_value = server_init(&server, config_path, library_path))) {
118 | goto shutdown;
119 | }
120 |
121 | signal(SIGINT, sig_handler);
122 | signal(SIGTERM, sig_handler);
123 |
124 | if (startup_cmd)
125 | spawn_with_shell(startup_cmd);
126 |
127 | wl_display_run(server.wl_display);
128 |
129 | shutdown:
130 | server_fini(&server);
131 | luaC_fini();
132 |
133 | switch (exit_value) {
134 | case LUACHECK_OK:
135 | return 0;
136 | case LUACHECK_ERROR:
137 | case SERVER_INIT_SUCCESS:
138 | case SERVER_INIT_FAILED:
139 | default:
140 | return exit_value;
141 | break;
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/src/meson.build:
--------------------------------------------------------------------------------
1 | srcs = [
2 | 'main.c',
3 | 'server.c',
4 |
5 | 'config.c',
6 | 'plugin.c',
7 | 'signal.c',
8 | 'util.c',
9 | 'util-map.c',
10 |
11 | 'luac.c',
12 | 'luaclass.c',
13 | 'luaobject.c',
14 |
15 | 'desktop/idle.c',
16 | 'desktop/layer_shell.c',
17 | 'desktop/output.c',
18 | 'desktop/session_lock.c',
19 | 'desktop/toplevel.c',
20 | 'desktop/transaction.c',
21 |
22 | 'input/cursor.c',
23 | 'input/manager.c',
24 | 'input/keybinding.c',
25 | 'input/keyboard.c',
26 | 'input/seat.c',
27 | 'input/switch.c',
28 | 'input/tablet.c',
29 | 'input/text_input.c',
30 | 'input/touch.c',
31 |
32 | 'ipc/server.c',
33 | 'ipc/common.c',
34 |
35 | 'layout/bsp.c',
36 | 'layout/master.c',
37 | 'layout/container.c',
38 |
39 | 'objects/client.c',
40 | 'objects/container.c',
41 | 'objects/kbind.c',
42 | 'objects/kbindmap.c',
43 | 'objects/screen.c',
44 | 'objects/tag.c',
45 | 'objects/input.c',
46 | 'objects/layer_shell.c',
47 |
48 | # Stable upstream protocols
49 | protocols_server_header['xdg-shell'],
50 | protocols_server_header['tablet-v2'],
51 |
52 | # Staging upstream protocols
53 | protocols_server_header['cursor-shape-v1'],
54 | protocols_server_header['ext-image-capture-source-v1'],
55 | protocols_server_header['ext-image-copy-capture-v1'],
56 | protocols_server_header['tearing-control-v1'],
57 |
58 | # Unstable upstream protocols
59 | protocols_server_header['pointer-constraints-unstable-v1'],
60 |
61 | # Other protocols
62 | protocols_server_header['wlr-layer-shell-unstable-v1'],
63 | protocols_server_header['wlr-output-power-management-unstable-v1'],
64 | ]
65 |
66 | executable(
67 | 'cwc',
68 | srcs,
69 | install: true,
70 | install_dir: '/usr/bin',
71 | dependencies: all_deps,
72 | include_directories : cwc_inc,
73 | link_args : ['-Wl,--export-dynamic'],
74 | )
75 |
--------------------------------------------------------------------------------
/src/objects/kbindmap.c:
--------------------------------------------------------------------------------
1 | /* kbindmap.c - keybinding map object
2 | *
3 | * Copyright (C) 2025 Dwi Asmoro Bangun
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | /** Lua object for keybindings map.
20 | *
21 | * @author Dwi Asmoro Bangun
22 | * @copyright 2025
23 | * @license GPLv3
24 | * @coreclassmod cwc_kbindmap
25 | */
26 |
27 | #include
28 | #include
29 | #include
30 | #include
31 |
32 | #include "cwc/desktop/output.h"
33 | #include "cwc/desktop/toplevel.h"
34 | #include "cwc/input/keyboard.h"
35 | #include "cwc/luaclass.h"
36 | #include "cwc/server.h"
37 |
38 | /** Active state of the keybind map.
39 | *
40 | * @property active
41 | * @tparam[opt=true] boolean active
42 | */
43 | static int luaC_kbindmap_get_active(lua_State *L)
44 | {
45 | struct cwc_keybind_map *kmap = luaC_kbindmap_checkudata(L, 1);
46 |
47 | lua_pushboolean(L, kmap->active);
48 |
49 | return 1;
50 | }
51 |
52 | static int luaC_kbindmap_set_active(lua_State *L)
53 | {
54 | luaL_checktype(L, 2, LUA_TBOOLEAN);
55 | struct cwc_keybind_map *kmap = luaC_kbindmap_checkudata(L, 1);
56 |
57 | kmap->active = lua_toboolean(L, 2);
58 |
59 | return 0;
60 | }
61 |
62 | /** Get list of all binding in the map.
63 | *
64 | * @property member
65 | * @readonly
66 | * @tparam[opt={}] cwc_kbind[] member List of the bindings.
67 | * @see cwc_kbind
68 | * @see cwc.kbd.get_default_member
69 | */
70 | static int luaC_kbindmap_get_member(lua_State *L)
71 | {
72 | struct cwc_keybind_map *kmap = luaC_kbindmap_checkudata(L, 1);
73 |
74 | lua_newtable(L);
75 | int j = 1;
76 | for (size_t i = 0; i < kmap->map->alloc; i++) {
77 | struct hhash_entry *elem = &kmap->map->table[i];
78 | if (!elem->hash)
79 | continue;
80 | struct cwc_keybind_info *kbind = elem->data;
81 |
82 | luaC_object_push(L, kbind);
83 | lua_rawseti(L, -2, j++);
84 | }
85 |
86 | return 1;
87 | }
88 |
89 | /** Register a keyboard binding.
90 | *
91 | * @method bind
92 | * @tparam table|number modifier Table of modifier or modifier bitfield
93 | * @tparam string keyname Keyname from `xkbcommon-keysyms.h`
94 | * @tparam func on_press Function to execute when pressed
95 | * @tparam[opt] func on_release Function to execute when released
96 | * @tparam[opt] table data Additional data
97 | * @tparam[opt] string data.group Keybinding group
98 | * @tparam[opt] string data.description Keybinding description
99 | * @tparam[opt] string data.exclusive Allow keybind to be executed even in
100 | * lockscreen and shortcut inhibit
101 | * @tparam[opt] string data.repeated Repeat keybind when hold (only on_press
102 | * will be executed)
103 | * @noreturn
104 | * @see cuteful.enum.modifier
105 | * @see cwc.pointer.bind
106 | * @see cwc.kbd.bind
107 | */
108 | static int luaC_kbindmap_bind(lua_State *L)
109 | {
110 | struct cwc_keybind_map *kmap = luaC_kbindmap_checkudata(L, 1);
111 |
112 | /* remove the kmap object so that the argument is equal to in cwc.kbd.bind
113 | */
114 | lua_remove(L, 1);
115 |
116 | cwc_keybind_map_register_bind_from_lua(L, kmap);
117 |
118 | return 0;
119 | }
120 |
121 | /** Set this map as the only active one disabling all others map.
122 | *
123 | * @method active_only
124 | * @noreturn
125 | */
126 | static int luaC_kbindmap_active_only(lua_State *L)
127 | {
128 | struct cwc_keybind_map *kmap = luaC_kbindmap_checkudata(L, 1);
129 |
130 | struct cwc_keybind_map *input_dev;
131 | wl_list_for_each(input_dev, &server.kbd_kmaps, link)
132 | {
133 | input_dev->active = false;
134 | }
135 |
136 | kmap->active = true;
137 |
138 | return 0;
139 | }
140 |
141 | /** Clear all keybinding (emptying the map).
142 | *
143 | * @method clear
144 | * @noreturn
145 | */
146 | static int luaC_kbindmap_clear(lua_State *L)
147 | {
148 | struct cwc_keybind_map *kmap = luaC_kbindmap_checkudata(L, 1);
149 |
150 | cwc_keybind_map_clear(kmap);
151 |
152 | return 0;
153 | }
154 |
155 | /** Destroy this map freeing it from memory.
156 | *
157 | * @method destroy
158 | * @noreturn
159 | */
160 | static int luaC_kbindmap_destroy(lua_State *L)
161 | {
162 | struct cwc_keybind_map *kmap = luaC_kbindmap_checkudata(L, 1);
163 |
164 | cwc_keybind_map_destroy(kmap);
165 |
166 | return 0;
167 | }
168 |
169 | #define REG_METHOD(name) {#name, luaC_kbindmap_##name}
170 | #define REG_READ_ONLY(name) {"get_" #name, luaC_kbindmap_get_##name}
171 | #define REG_SETTER(name) {"set_" #name, luaC_kbindmap_set_##name}
172 | #define REG_PROPERTY(name) REG_READ_ONLY(name), REG_SETTER(name)
173 |
174 | void luaC_kbindmap_setup(lua_State *L)
175 | {
176 | luaL_Reg kbindmap_metamethods[] = {
177 | {"__eq", luaC_kbindmap_eq },
178 | {"__tostring", luaC_kbindmap_tostring},
179 | {NULL, NULL },
180 | };
181 |
182 | luaL_Reg kbindmap_methods[] = {
183 | REG_METHOD(bind), REG_METHOD(active_only),
184 | REG_METHOD(clear), REG_METHOD(destroy),
185 |
186 | REG_READ_ONLY(member),
187 |
188 | REG_PROPERTY(active),
189 |
190 | {NULL, NULL},
191 | };
192 |
193 | luaC_register_class(L, kbindmap_classname, kbindmap_methods,
194 | kbindmap_metamethods);
195 | }
196 |
--------------------------------------------------------------------------------
/src/objects/layer_shell.c:
--------------------------------------------------------------------------------
1 | /* layer_shell.c - lua cwc_layer_shell object
2 | *
3 | * Copyright (C) 2025 Dwi Asmoro Bangun
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | /** Layer shell object info and events.
20 | *
21 | * @author Dwi Asmoro Bangun
22 | * @copyright 2025
23 | * @license GPLv3
24 | * @coreclassmod cwc.layer_shell
25 | */
26 |
27 | #include
28 | #include
29 | #include
30 |
31 | #include "cwc/desktop/layer_shell.h"
32 | #include "cwc/desktop/output.h"
33 | #include "cwc/luaclass.h"
34 | #include "cwc/luaobject.h"
35 | #include "cwc/server.h"
36 |
37 | /** Emitted when a layer shell surface is created.
38 | *
39 | * @signal layer_shell::new
40 | * @tparam cwc_layer_shell layer_shell The layer shell object.
41 | */
42 |
43 | /** Emitted when layer shell surface is about to be destroyed.
44 | *
45 | * @signal layer_shell::destroy
46 | * @tparam cwc_layer_shell layer_shell The layer shell object.
47 | */
48 |
49 | //================== CODE ====================
50 |
51 | /** Get all input device in the server.
52 | *
53 | * @staticfct get
54 | * @treturn cwc_layer_shell[]
55 | */
56 | static int luaC_layer_shell_get(lua_State *L)
57 | {
58 | lua_newtable(L);
59 |
60 | int i = 1;
61 | struct cwc_layer_surface *lshell;
62 | wl_list_for_each(lshell, &server.layer_shells, link)
63 | {
64 | luaC_object_push(L, lshell);
65 | lua_rawseti(L, -2, i++);
66 | }
67 |
68 | return 1;
69 | }
70 |
71 | /** Kill a layer shell client without any question asked.
72 | *
73 | * @method kill
74 | * @noreturn
75 | */
76 | static int luaC_layer_shell_kill(lua_State *L)
77 | {
78 | struct cwc_layer_surface *layer_shell = luaC_layer_shell_checkudata(L, 1);
79 | wl_client_destroy(layer_shell->wlr_layer_surface->resource->client);
80 | return 0;
81 | }
82 |
83 | /** The screen where the layer shell visible.
84 | *
85 | * @property screen
86 | * @tparam cwc_screen screen
87 | * @readonly
88 | * @propertydefault Up to the client.
89 | */
90 | static int luaC_layer_shell_get_screen(lua_State *L)
91 | {
92 | struct cwc_layer_surface *layer_shell = luaC_layer_shell_checkudata(L, 1);
93 | luaC_object_push(L, layer_shell->output);
94 | return 1;
95 | }
96 |
97 | /** The namespace of the layer shell.
98 | *
99 | * @property namespace
100 | * @tparam string namespace
101 | * @readonly
102 | * @propertydefault Extracted from wlr_layer_surface.
103 | */
104 | static int luaC_layer_shell_get_namespace(lua_State *L)
105 | {
106 | struct cwc_layer_surface *layer_shell = luaC_layer_shell_checkudata(L, 1);
107 | lua_pushstring(L, layer_shell->wlr_layer_surface->namespace);
108 | return 1;
109 | }
110 |
111 | /** The pid of the layer shell.
112 | *
113 | * @property pid
114 | * @tparam number pid
115 | * @readonly
116 | * @negativeallowed false
117 | * @propertydefault Provided by the OS.
118 | */
119 | static int luaC_layer_shell_get_pid(lua_State *L)
120 | {
121 | struct cwc_layer_surface *layer_shell = luaC_layer_shell_checkudata(L, 1);
122 | pid_t pid = 0;
123 |
124 | wl_client_get_credentials(layer_shell->wlr_layer_surface->resource->client,
125 | &pid, NULL, NULL);
126 |
127 | lua_pushnumber(L, pid);
128 | return 1;
129 | }
130 |
131 | #define REG_METHOD(name) {#name, luaC_layer_shell_##name}
132 | #define REG_READ_ONLY(name) {"get_" #name, luaC_layer_shell_get_##name}
133 |
134 | void luaC_layer_shell_setup(lua_State *L)
135 | {
136 | luaL_Reg layer_shell_metamethods[] = {
137 | {"__eq", luaC_layer_shell_eq },
138 | {"__tostring", luaC_layer_shell_tostring},
139 | {NULL, NULL },
140 | };
141 |
142 | luaL_Reg layer_shell_methods[] = {
143 | REG_METHOD(kill),
144 |
145 | REG_READ_ONLY(screen), REG_READ_ONLY(namespace), REG_READ_ONLY(pid),
146 |
147 | {NULL, NULL},
148 | };
149 |
150 | luaC_register_class(L, layer_shell_classname, layer_shell_methods,
151 | layer_shell_metamethods);
152 | luaL_Reg layer_shell_staticlibs[] = {
153 | {"get", luaC_layer_shell_get},
154 | {NULL, NULL },
155 | };
156 |
157 | lua_newtable(L);
158 | luaL_register(L, NULL, layer_shell_staticlibs);
159 | lua_setfield(L, -2, "layer_shell");
160 | }
161 |
--------------------------------------------------------------------------------
/src/util.c:
--------------------------------------------------------------------------------
1 | /* util.c - utility functions and data structure
2 | *
3 | * Copyright (C) 2024 Dwi Asmoro Bangun
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 |
26 | #include "cwc/util.h"
27 |
28 | bool wl_list_length_at_least(struct wl_list *list, int more_than_or_equal_to)
29 | {
30 | int count = 0;
31 | struct wl_list *e = list->next;
32 | while (e != list) {
33 | e = e->next;
34 | if (++count >= more_than_or_equal_to)
35 | return true;
36 | }
37 |
38 | return false;
39 | }
40 |
41 | void wl_list_swap(struct wl_list *x, struct wl_list *y)
42 | {
43 | if (x == y)
44 | return;
45 |
46 | if (x->next == y) {
47 | wl_list_remove(x);
48 | wl_list_insert(y, x);
49 | return;
50 | }
51 |
52 | if (x->prev == y) {
53 | wl_list_remove(y);
54 | wl_list_insert(x, y);
55 | return;
56 | }
57 |
58 | struct wl_list *x_prev = x->prev;
59 | wl_list_remove(x);
60 | wl_list_insert(y, x);
61 | wl_list_remove(y);
62 | wl_list_insert(x_prev, y);
63 | }
64 |
65 | void wl_list_reattach(struct wl_list *older_sibling, struct wl_list *elm)
66 | {
67 | wl_list_remove(elm);
68 | wl_list_insert(older_sibling, elm);
69 | }
70 |
71 | bool _cwc_assert(bool condition, const char *format, ...)
72 | {
73 | if (condition)
74 | return true;
75 |
76 | va_list args;
77 | va_start(args, format);
78 | _wlr_vlog(WLR_ERROR, format, args);
79 | vfprintf(stderr, format, args);
80 | va_end(args);
81 |
82 | #ifndef NDEBUG
83 | raise(SIGABRT);
84 | #endif
85 |
86 | return false;
87 | }
88 |
89 | void normalized_region_at(
90 | struct wlr_box *region, double x, double y, double *nx, double *ny)
91 | {
92 | if (nx)
93 | *nx = (x - (double)region->x) / region->width;
94 |
95 | if (ny)
96 | *ny = (y - (double)region->y) / region->height;
97 | }
98 |
99 | double distance(int lx, int ly, int lx2, int ly2)
100 | {
101 | int x_diff = abs(lx2 - lx);
102 | int y_diff = abs(ly2 - ly);
103 |
104 | return sqrt(pow(x_diff, 2) + pow(y_diff, 2));
105 | }
106 |
107 | bool is_direction_match(enum wlr_direction dir, int x, int y)
108 | {
109 | cwc_assert(x || y, "both x and y cannot be zero");
110 |
111 | double angle = atan2(y, x) * (180 / M_PI);
112 |
113 | switch (dir) {
114 | case WLR_DIRECTION_UP:
115 | if (angle > -45 || angle < -135)
116 | return false;
117 | break;
118 | case WLR_DIRECTION_RIGHT:
119 | if (angle > -45 && angle < 45)
120 | break;
121 | else
122 | return false;
123 | case WLR_DIRECTION_DOWN:
124 | if (angle < 45 || angle > 135)
125 | return false;
126 | break;
127 | case WLR_DIRECTION_LEFT:
128 | if (angle > 135 || angle < -135)
129 | break;
130 | else
131 | return false;
132 | }
133 |
134 | return true;
135 | }
136 |
137 | uint32_t
138 | get_snap_edges(struct wlr_box *output_box, int cx, int cy, int threshold)
139 | {
140 | uint32_t edges = 0;
141 | int output_right_edge_diff = output_box->x + output_box->width - cx;
142 | int output_left_edge_diff = cx - output_box->x;
143 | int output_bottom_edge_diff = output_box->y + output_box->height - cy;
144 | int output_top_edge_diff = cy - output_box->y;
145 |
146 | if (output_right_edge_diff >= 0 && output_right_edge_diff < threshold) {
147 | edges |= WLR_EDGE_RIGHT;
148 | } else if (output_left_edge_diff >= 0
149 | && output_left_edge_diff < threshold) {
150 | edges |= WLR_EDGE_LEFT;
151 | }
152 |
153 | if (output_bottom_edge_diff >= 0 && output_bottom_edge_diff < threshold) {
154 | edges |= WLR_EDGE_BOTTOM;
155 | } else if (output_top_edge_diff >= 0 && output_top_edge_diff < threshold) {
156 | edges |= WLR_EDGE_TOP;
157 | }
158 |
159 | return edges;
160 | }
161 |
--------------------------------------------------------------------------------
/tests/capi/signal.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include "cwc/desktop/toplevel.h"
5 | #include "cwc/plugin.h"
6 | #include "cwc/signal.h"
7 | #include "cwc/util.h"
8 |
9 | static int call_counter = 0;
10 | static bool already_called = false;
11 |
12 | static void on_toplevel_map(void *data)
13 | {
14 | struct cwc_toplevel *toplevel = data;
15 | call_counter++;
16 | cwc_toplevel_set_fullscreen(toplevel, true);
17 |
18 | assert(toplevel);
19 | assert(call_counter == 1);
20 | assert(cwc_toplevel_is_fullscreen(toplevel));
21 |
22 | cwc_log(CWC_INFO, "C SIGNAL TEST OK");
23 | }
24 |
25 | static void on_custom_signal(void *data)
26 | {
27 | assert(strcmp("testvalue", (char *)data) == 0);
28 | assert(!already_called);
29 |
30 | already_called = true;
31 | cwc_log(CWC_INFO, "C CUSTOM SIGNAL TEST OK");
32 | }
33 |
34 | static int signal_init()
35 | {
36 | cwc_signal_connect("client::map", on_toplevel_map);
37 | cwc_signal_disconnect("client::map", on_toplevel_map);
38 | cwc_signal_connect("client::map", on_toplevel_map);
39 |
40 | cwc_signal_connect("custom::signal", on_custom_signal);
41 | cwc_signal_emit_c("custom::signal", "testvalue");
42 | cwc_signal_disconnect("custom::signal", on_custom_signal);
43 | cwc_signal_emit_c("custom::signal", "testvalue");
44 |
45 | return 0;
46 | }
47 |
48 | static void signal_test_eval()
49 | {
50 | cwc_log(CWC_INFO, "UNLOADING C SIGNAL TEST PLUGIN OK");
51 | }
52 |
53 | plugin_init(signal_init);
54 | plugin_exit(signal_test_eval);
55 |
56 | PLUGIN_NAME("signal_test");
57 | PLUGIN_VERSION("0.1.0");
58 | PLUGIN_DESCRIPTION("signal test");
59 | PLUGIN_LICENSE("0BSD");
60 | PLUGIN_AUTHOR("Antoni Morbell");
61 |
--------------------------------------------------------------------------------
/tests/capi/signal.lua:
--------------------------------------------------------------------------------
1 | local cwc = cwc
2 |
3 | local function test()
4 | cwc.plugin.load("./build/tests/csignal_test.so")
5 | cwc.spawn_with_shell("kitty")
6 | end
7 |
8 | return test
9 |
--------------------------------------------------------------------------------
/tests/hash.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 |
6 | #include "cwc/util.h"
7 |
8 | #define KEY_LEN 10
9 |
10 | static int TABLE_SIZE;
11 |
12 | char *number_to_string(int n)
13 | {
14 | char *container = calloc(1, KEY_LEN);
15 | sprintf(container, "%d", n);
16 |
17 | return container;
18 | }
19 |
20 | // setup data for repeated_read
21 | void setup_data(struct cwc_hhmap *m)
22 | {
23 | srand(69);
24 | for (int i = 0; i < TABLE_SIZE; i++) {
25 | // char key[KEY_LEN];
26 | // sprintf(key, "%d", i);
27 | char *key = number_to_string(i);
28 | cwc_hhmap_ninsert(m, key, KEY_LEN, key);
29 | }
30 | }
31 |
32 | void destroy_data(struct cwc_hhmap *m)
33 | {
34 | for (int i = 0; i < TABLE_SIZE; i++) {
35 | char key[KEY_LEN];
36 | memset(key, 0, KEY_LEN);
37 | sprintf(key, "%d", i);
38 | cwc_hhmap_nremove(m, key, KEY_LEN);
39 | }
40 |
41 | for (int i = 0; i < TABLE_SIZE; i++) {
42 | char key[KEY_LEN];
43 | memset(key, 0, KEY_LEN);
44 | sprintf(key, "%d", i);
45 | char *val = cwc_hhmap_nget(m, key, KEY_LEN);
46 | assert(val == NULL);
47 | }
48 | }
49 |
50 | void basic_perf(struct cwc_hhmap *m)
51 | {
52 | // basic performance test
53 | for (int i = 0; i < TABLE_SIZE; i++) {
54 | char key[KEY_LEN];
55 | memset(key, 0, KEY_LEN);
56 | sprintf(key, "%d", i);
57 | cwc_hhmap_ninsert(m, key, KEY_LEN, (void *)(long)i);
58 | }
59 | for (int i = 0; i < TABLE_SIZE; i++) {
60 | char key[KEY_LEN];
61 | memset(key, 0, KEY_LEN);
62 | sprintf(key, "%d", i);
63 | char *value = cwc_hhmap_nget(m, key, KEY_LEN);
64 | // printf("%d %ld %ld\n", i, m->size, m->alloc);
65 | assert(value == i);
66 | }
67 |
68 | for (int i = 0; i < TABLE_SIZE; i++) {
69 | char key[KEY_LEN];
70 | memset(key, 0, KEY_LEN);
71 | sprintf(key, "%d", i);
72 | cwc_hhmap_nremove(m, key, KEY_LEN);
73 | // printf("%d %ld %ld\n", i, m->size, m->alloc);
74 | }
75 |
76 | for (int i = 0; i < TABLE_SIZE; i++) {
77 | char key[KEY_LEN];
78 | memset(key, 0, KEY_LEN);
79 | sprintf(key, "%d", i);
80 | char *value = cwc_hhmap_nget(m, key, KEY_LEN);
81 | assert(value == NULL);
82 | }
83 | }
84 |
85 | void basic_operation(struct cwc_hhmap *m)
86 | {
87 | char key[10] = "key";
88 | cwc_hhmap_insert(m, "key0", "value0");
89 | cwc_hhmap_insert(m, "key1", "value1");
90 | cwc_hhmap_insert(m, "key2", "value2");
91 | cwc_hhmap_insert(m, "key3", "value3");
92 | cwc_hhmap_insert(m, "key4", "value4");
93 | cwc_hhmap_insert(m, "key5", "value5");
94 |
95 | for (int i = 0; i < 8; i++) {
96 | sprintf(key + 3, "%d", i);
97 | char *value = cwc_hhmap_get(m, key);
98 | printf("%p\n", value);
99 | }
100 |
101 | for (int i = 0; i < 8; i++) {
102 | sprintf(key + 3, "%d", i);
103 | cwc_hhmap_remove(m, key);
104 | char *value = cwc_hhmap_get(m, key);
105 | printf("%p\n", value);
106 | }
107 | }
108 |
109 | void repeated_read(struct cwc_hhmap *m)
110 | {
111 | // seq read
112 | for (int i = 0; i < TABLE_SIZE; i++) {
113 | char key[KEY_LEN];
114 | memset(key, 0, KEY_LEN);
115 | sprintf(key, "%d", i);
116 | char *val = cwc_hhmap_nget(m, key, KEY_LEN);
117 | // assert(strcmp(val, key) == 0);
118 | }
119 |
120 | // rand read
121 | for (int i = 0; i < TABLE_SIZE; i++) {
122 | char key[KEY_LEN];
123 | memset(key, 0, KEY_LEN);
124 | sprintf(key, "%d", rand() % TABLE_SIZE);
125 | char *val = cwc_hhmap_nget(m, key, KEY_LEN);
126 | // assert(strcmp(val, key) == 0);
127 | }
128 | }
129 |
130 | int main()
131 | {
132 | TABLE_SIZE = 1e7;
133 | struct cwc_hhmap *m = cwc_hhmap_create(0);
134 | setup_data(m);
135 |
136 | for (int i = 0; i < 5; i++) {
137 | // basic_perf(m);
138 | repeated_read(m);
139 | }
140 |
141 | destroy_data(m);
142 |
143 | cwc_hhmap_destroy(m);
144 | return 0;
145 | }
146 |
--------------------------------------------------------------------------------
/tests/hash.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | using namespace std;
7 |
8 | static int TABLE_SIZE;
9 |
10 | // boost::unordered::unordered_flat_map umap;
11 | unordered_map umap;
12 |
13 | void setup_data()
14 | {
15 | srand(69);
16 |
17 | for (int i = 0; i < TABLE_SIZE; i++) {
18 | string j = to_string(i);
19 | umap[j] = nullptr;
20 | }
21 | }
22 |
23 | void destroy_data()
24 | {
25 | for (int i = 0; i < TABLE_SIZE; ++i) {
26 | string key = to_string(i);
27 | umap.erase(key);
28 | }
29 |
30 | for (int i = 0; i < TABLE_SIZE; ++i) {
31 | string key = to_string(i);
32 | assert(umap[key] == NULL);
33 | }
34 | }
35 |
36 | void basic_perf()
37 | {
38 | // string cache[MAX_LOOP];
39 | for (int i = 0; i < TABLE_SIZE; ++i) {
40 | // cache[i] = to_string(i);
41 | // umap[cache[i]] = nullptr;
42 |
43 | string j = to_string(i);
44 | umap[j] = (char *)(long)i;
45 | }
46 |
47 | for (int i = 0; i < TABLE_SIZE; ++i) {
48 | // string elem = cache[i];
49 | // char* value = umap[cache[i]];
50 | // assert(value == nullptr);
51 |
52 | string elem = to_string(i);
53 | char* value = umap[elem];
54 | assert(value == (char *)i);
55 | }
56 |
57 | for (int i = 0; i < TABLE_SIZE; ++i) {
58 | // umap.erase(cache[i]);
59 |
60 | string elem = to_string(i);
61 | umap.erase(elem);
62 | }
63 |
64 | for (int i = 0; i < TABLE_SIZE; ++i) {
65 | // string elem = cache[i];
66 | // char* value = umap[cache[i]];
67 | // assert(value == nullptr);
68 |
69 | string elem = to_string(i);
70 | char* value = umap[elem];
71 | assert(value == nullptr);
72 | }
73 | }
74 |
75 | void repeated_read()
76 | {
77 |
78 | // seq
79 | for (int i = 0; i < TABLE_SIZE; i++) {
80 | string j = to_string(i);
81 | umap[j] = nullptr;
82 | }
83 |
84 | // rand
85 | for (int i = 0; i < TABLE_SIZE; i++) {
86 | string j = to_string(rand() % TABLE_SIZE);
87 | char* val = umap[j];
88 | }
89 | }
90 |
91 | int main()
92 | {
93 | TABLE_SIZE = 1e7;
94 | setup_data();
95 | for (int i = 0; i < 5; i++) {
96 | // basic_perf();
97 | repeated_read();
98 | }
99 |
100 | destroy_data();
101 | return 0;
102 | }
103 |
--------------------------------------------------------------------------------
/tests/luapi/client.lua:
--------------------------------------------------------------------------------
1 | -- Test the cwc_client property
2 | local bit = require("bit")
3 | local enum = require("cuteful.enum")
4 | local gears = require("gears")
5 |
6 | local cwc = cwc
7 |
8 | local signal_list = {
9 | "client::new",
10 | "client::destroy",
11 | "client::map",
12 | "client::unmap",
13 | "client::focus",
14 | "client::unfocus",
15 | "client::swap",
16 | -- "client::mouse_enter", -- require interaction
17 | -- "client::mouse_leave", -- require interaction
18 | "client::raised",
19 | "client::lowered",
20 | "client::property::fullscreen",
21 | "client::property::maximized",
22 | "client::property::minimized",
23 | "client::property::floating",
24 | "client::property::urgent",
25 | }
26 |
27 | local triggered_list = {}
28 |
29 | for _, signame in pairs(signal_list) do
30 | cwc.connect_signal(signame, function()
31 | triggered_list[signame] = true
32 | end)
33 | end
34 |
35 | local function signal_check()
36 | local count = 0
37 | for _, signame in pairs(signal_list) do
38 | if not triggered_list[signame] then
39 | local template = string.format("signal %s is not triggered", signame)
40 | print(template)
41 | count = count + 1
42 | end
43 | end
44 |
45 | if count > 0 then
46 | print(string.format("%d of %d cwc_client signal test FAILED", count, #signal_list))
47 | else
48 | print("cwc_client signal test PASSED")
49 | end
50 | end
51 |
52 |
53 |
54 | local function static_test()
55 | local cls = cwc.client.get()
56 | assert(#cls == 20)
57 | end
58 |
59 | local function readonly_test(c)
60 | assert(type(c.mapped) == "boolean")
61 | assert(type(c.visible) == "boolean")
62 | assert(type(c.x11) == "boolean")
63 |
64 | -- assert(string.find(tostring(c.parent), "cwc_client"))
65 | assert(string.find(tostring(c.screen), "cwc_screen"))
66 | assert(type(c.pid) == "number")
67 | assert(type(c.title) == "string")
68 | assert(type(c.appid) == "string")
69 | assert(string.find(tostring(c.container), "cwc_container"))
70 | end
71 |
72 | local function property_test(c)
73 | assert(c.fullscreen == false)
74 | c.fullscreen = true
75 | assert(c.fullscreen)
76 | c.fullscreen = false
77 | assert(not c.fullscreen)
78 |
79 | assert(c.maximized == false)
80 | c.maximized = true
81 | assert(c.maximized)
82 | c.maximized = false
83 | assert(not c.maximized)
84 |
85 | assert(type(c.floating) == "boolean")
86 | c.floating = true
87 | assert(c.floating)
88 |
89 | assert(c.minimized == false)
90 | c.minimized = true
91 | assert(c.minimized)
92 | c.minimized = false
93 | assert(not c.minimized)
94 |
95 | assert(c.sticky == false)
96 | assert(c.ontop == false)
97 | assert(c.above == false)
98 | assert(c.below == false)
99 | c.ontop = true
100 | c.below = true
101 | c.above = true
102 | assert(c.above)
103 | assert(c.below == false)
104 | assert(c.ontop == false)
105 |
106 | local geom = c.geometry
107 | assert(type(geom) == "table")
108 | assert(type(geom.x) == "number")
109 | assert(type(geom.y) == "number")
110 | assert(type(geom.width) == "number")
111 | assert(type(geom.height) == "number")
112 |
113 | assert(c.tag > 0)
114 | assert(c.workspace > 0)
115 | c.workspace = 5
116 | assert(c.tag == bit.lshift(1, 4))
117 | assert(c.workspace == 5)
118 |
119 | assert(c.opacity >= 0 and c.opacity <= 1)
120 | c.opacity = 0.5
121 | assert(c.opacity == 0.5)
122 |
123 | assert(not c.allow_tearing)
124 | c.allow_tearing = true
125 | assert(c.allow_tearing)
126 |
127 | assert(type(c.border_width) == "number")
128 | c.border_width = 2
129 | assert(c.border_width == 2)
130 |
131 | assert(type(c.border_rotation) == "number")
132 | c.border_rotation = 90
133 | assert(c.border_rotation == 90)
134 | end
135 |
136 | local function method_test(c)
137 | local cls = cwc.client.get()
138 | local rand = cls[math.random(#cls)]
139 | c:set_border_color(gears.color("#ffffff"))
140 | c:raise()
141 | c:lower()
142 | c:focus()
143 | c:jump_to(true)
144 | c:jump_to()
145 | c:swap(rand)
146 | c:center()
147 | c:move_to_tag(c.workspace)
148 | c:toggle_split()
149 | c:toggle_tag(c.workspace + 1)
150 | c:close()
151 | c:move_to_screen(cwc.screen.focused())
152 | rand:kill()
153 | end
154 |
155 | local function test()
156 | static_test()
157 |
158 | local c = cwc.client.focused()
159 | readonly_test(c)
160 | property_test(c)
161 | method_test(c)
162 |
163 | print("cwc_client test PASSED")
164 | end
165 |
166 |
167 | return {
168 | api = test,
169 | signal = signal_check,
170 | }
171 |
--------------------------------------------------------------------------------
/tests/luapi/container.lua:
--------------------------------------------------------------------------------
1 | local cwc = cwc
2 |
3 | local signal_list = {
4 | "container::new",
5 | "container::destroy",
6 | "container::insert",
7 | "container::remove",
8 | "container::swap",
9 | }
10 |
11 | local triggered_list = {}
12 |
13 | for _, signame in pairs(signal_list) do
14 | cwc.connect_signal(signame, function()
15 | triggered_list[signame] = true
16 | end)
17 | end
18 |
19 | local function signal_check()
20 | local count = 0
21 | for _, signame in pairs(signal_list) do
22 | if not triggered_list[signame] then
23 | local template = string.format("signal %s is not triggered", signame)
24 | print(template)
25 | count = count + 1
26 | end
27 | end
28 |
29 | if count > 0 then
30 | print(string.format("%d of %d cwc_container signal test FAILED", count, #signal_list))
31 | else
32 | print("cwc_container signal test PASSED")
33 | end
34 | end
35 |
36 | local function property_test(cont, c)
37 | assert(type(cont.clients) == "table")
38 | assert(string.find(tostring(cont.clients[1]), "cwc_client"))
39 | assert(cont.front == c)
40 |
41 | local geom = cont.geometry
42 | assert(type(cont.geometry) == "table")
43 | assert(type(geom) == "table")
44 | assert(type(geom.x) == "number")
45 | assert(type(geom.y) == "number")
46 | assert(type(geom.width) == "number")
47 | assert(type(geom.height) == "number")
48 |
49 | assert(cont.insert_mark == false)
50 | cont.insert_mark = true
51 | assert(cont.insert_mark == true)
52 | end
53 |
54 | local function method_test(cont)
55 | local cts = cwc.container.get()
56 | local cls = cwc.client.get()
57 | local rand_cont = cts[math.random(#cts)]
58 | local rand_client = cls[math.random(#cls)]
59 | cont:focusidx(1)
60 | cont:swap(rand_cont)
61 | cont:insert_client(rand_client)
62 | assert(#cont.client_stack == #cont:get_client_stack(true))
63 | end
64 |
65 | local function test()
66 | local c = cwc.client.focused()
67 | local cont = c.container
68 |
69 | property_test(cont, c)
70 | method_test(cont)
71 |
72 | print("cwc_container test PASSED")
73 | end
74 |
75 | return {
76 | api = test,
77 | signal = signal_check,
78 | }
79 |
--------------------------------------------------------------------------------
/tests/luapi/kbinding.lua:
--------------------------------------------------------------------------------
1 | -- Test the cwc_kbindmap and cwc_kbind property
2 |
3 | local enum = require("cuteful.enum")
4 |
5 | local cwc = cwc
6 | local kbd = cwc.kbd
7 |
8 | local function test()
9 | local kmap = kbd.create_bindmap()
10 | local kmap2 = kbd.create_bindmap()
11 |
12 | assert(kmap.active)
13 | kmap.active = false
14 | assert(not kmap.active)
15 | kmap.active = true
16 | assert(kmap.active)
17 | kmap2:active_only()
18 | assert(not kmap.active)
19 |
20 | kmap:bind({ enum.modifier.LOGO }, "a", function()
21 | print("test")
22 | end, { description = "b", group = "c", exclusive = true, repeated = true })
23 |
24 | assert(#kmap.member)
25 |
26 | ---- kbind -----
27 | local bind_a = kmap.member[1]
28 | assert(bind_a.description == "b")
29 | assert(bind_a.group == "c")
30 |
31 | bind_a.description = "d"
32 | assert(bind_a.description == "d")
33 | bind_a.group = "e"
34 | assert(bind_a.group == "e")
35 |
36 | assert(bind_a.repeated)
37 | assert(bind_a.exclusive)
38 |
39 | bind_a.repeated = false
40 | assert(not bind_a.repeated)
41 | bind_a.exclusive = false
42 | assert(not bind_a.exclusive)
43 |
44 | assert(#bind_a.modifier_name == 1)
45 | assert(#bind_a.modifier == 1)
46 | print(bind_a.modifier[1])
47 | print(bind_a.modifier_name[1])
48 | assert(bind_a.modifier[1] == enum.modifier.LOGO)
49 | assert(bind_a.modifier_name[1] == "LOGO")
50 |
51 | assert(bind_a.keyname == "a")
52 | assert(bind_a.keysym == 0x61)
53 |
54 | kmap:clear()
55 | assert(#kmap.member == 0)
56 |
57 | kmap:destroy()
58 | local success = pcall(function()
59 | print(kmap.active)
60 | end)
61 | assert(not success)
62 |
63 | print("cwc_kbindmap & cwc_kbind test PASSED")
64 | end
65 |
66 | return test
67 |
--------------------------------------------------------------------------------
/tests/luapi/layer_shell.lua:
--------------------------------------------------------------------------------
1 | local cwc = cwc
2 |
3 | local signal_list = {
4 | "layer_shell::new",
5 | "layer_shell::destroy",
6 | }
7 |
8 | local triggered_list = {}
9 |
10 | for _, signame in pairs(signal_list) do
11 | cwc.connect_signal(signame, function()
12 | triggered_list[signame] = true
13 | end)
14 | end
15 |
16 | local function signal_check()
17 | local count = 0
18 | for _, signame in pairs(signal_list) do
19 | if not triggered_list[signame] then
20 | local template = string.format("signal %s is not triggered", signame)
21 | print(template)
22 | count = count + 1
23 | end
24 | end
25 |
26 | if count > 0 then
27 | print(string.format("%d of %d cwc_layer_shell signal test FAILED", count, #signal_list))
28 | else
29 | print("cwc_layer_shell signal test PASSED")
30 | end
31 | end
32 |
33 | local function readonly_test(ls)
34 | assert(ls.screen)
35 | assert(ls.namespace)
36 | assert(ls.pid)
37 | end
38 |
39 | local function method_test(ls)
40 | ls:kill()
41 | end
42 |
43 | local function test()
44 | local ls = cwc.layer_shell.get()[1]
45 | readonly_test(ls)
46 | method_test(ls)
47 |
48 | print("cwc_layer_shell test PASSED")
49 | end
50 |
51 |
52 | return {
53 | api = test,
54 | signal = signal_check,
55 | }
56 |
--------------------------------------------------------------------------------
/tests/luapi/screen.lua:
--------------------------------------------------------------------------------
1 | -- Test the cwc_screen property
2 |
3 | local bit = require("bit")
4 | local enum = require('cuteful.enum')
5 |
6 | local cwc = cwc
7 |
8 | local signal_list = {
9 | "screen::new",
10 | -- "screen::destroy", -- currently there's no way to destroy screen programmatically
11 | "screen::prop::active_tag",
12 | "screen::prop::active_workspace",
13 | "screen::prop::selected_tag",
14 | "screen::focus",
15 | "screen::unfocus",
16 | "screen::mouse_enter",
17 | "screen::mouse_leave",
18 | }
19 |
20 | local triggered_list = {}
21 |
22 | for _, signame in pairs(signal_list) do
23 | cwc.connect_signal(signame, function()
24 | triggered_list[signame] = true
25 | end)
26 | end
27 |
28 | local function signal_check()
29 | local count = 0
30 | for _, signame in pairs(signal_list) do
31 | if not triggered_list[signame] then
32 | local template = string.format("signal %s is not triggered", signame)
33 | print(template)
34 | count = count + 1
35 | end
36 | end
37 |
38 | if count > 0 then
39 | print(string.format("%d of %d cwc_screen signal test FAILED", count, #signal_list))
40 | else
41 | print("cwc_screen signal test PASSED")
42 | end
43 | end
44 |
45 | local function ro_test(s)
46 | assert(s.width > 0)
47 | assert(s.height > 0)
48 | assert(type(s.refresh) == "number")
49 | assert(s.phys_width >= 0)
50 | assert(s.phys_height >= 0)
51 | assert(s.scale >= 0)
52 | assert(#s.name > 0)
53 | assert(type(s.description) == "string")
54 | -- assert(type(s.make) == "string")
55 | -- assert(type(s.model) == "string")
56 | -- assert(type(s.serial) == "string")
57 | assert(type(s.enabled) == "boolean")
58 | assert(type(s.non_desktop) == "boolean")
59 | assert(type(s.restored) == "boolean")
60 |
61 | assert(type(s.workarea) == "table")
62 | assert(s.workarea.x >= 0)
63 | assert(s.workarea.y >= 0)
64 | assert(s.workarea.width >= 0)
65 | assert(s.workarea.height >= 0)
66 | end
67 |
68 | local function prop_test(s)
69 | -- when cwc start and the output is created, it always start at view/workspace 1
70 | assert(s.active_workspace == 1)
71 | assert(s.active_tag == 1)
72 |
73 | s:get_tag(5):view_only()
74 | assert(s:get_active_workspace() == 5)
75 | assert(s:get_active_tag() == bit.lshift(1, 5 - 1))
76 |
77 | s.active_workspace = 8
78 | assert(s.active_workspace == 8)
79 | assert(s.active_tag == bit.lshift(1, 8 - 1))
80 |
81 | s.active_tag = bit.bor(bit.lshift(1, 5 - 1), bit.lshift(1, 3 - 1))
82 |
83 | s:get_tag(4):toggle()
84 | assert(s.active_tag == bit.bor(bit.lshift(1, 5 - 1), bit.lshift(1, 3 - 1), bit.lshift(1, 4 - 1)))
85 |
86 | assert(s.max_general_workspace == 9)
87 | s.max_general_workspace = 3
88 | assert(s:get_max_general_workspace() == 3)
89 | s.max_general_workspace = -1
90 | assert(s.max_general_workspace == 1)
91 | s:set_max_general_workspace(10000)
92 | assert(s.max_general_workspace == cwc.screen.get_max_workspace())
93 |
94 | assert(not s.allow_tearing)
95 | s.allow_tearing = true
96 | assert(s.allow_tearing)
97 | end
98 |
99 | local function method_test(s)
100 | assert(#s.focus_stack == #s.containers)
101 | assert(#s.clients == #s:get_clients())
102 | assert(#s.containers == #s:get_containers())
103 | assert(#s.minimized == #s:get_minimized())
104 | s:get_nearest(enum.direction.LEFT)
105 | s:focus()
106 | end
107 |
108 | local function test()
109 | local s = cwc.screen.focused()
110 |
111 | s:get_tag(1):view_only()
112 |
113 | ro_test(s)
114 | prop_test(s)
115 | method_test(s)
116 |
117 | print("cwc_screen test PASSED")
118 | end
119 |
120 | return {
121 | api = test,
122 | signal = signal_check,
123 | }
124 |
--------------------------------------------------------------------------------
/tests/luapi/signal.lua:
--------------------------------------------------------------------------------
1 | local gstring = require("gears.string")
2 | local cwc = cwc
3 |
4 | local function on_client_custom(c, testname, optarg)
5 | assert(gstring.startswith(tostring(c), "cwc_client"))
6 | assert(testname == "sig1" or testname == "sig2")
7 | assert(100 or nil)
8 |
9 | print(string.format("lua signal test %s PASSED with received value:", testname), c, testname,
10 | optarg)
11 | end
12 |
13 | local function on_client_map(c)
14 | assert(gstring.startswith(tostring(c), "cwc_client"))
15 | print("lua client map signal PASSED", c)
16 | end
17 |
18 | local function on_client_unmap(c)
19 | assert(gstring.startswith(tostring(c), "cwc_client"))
20 | print("lua client unmap signal PASSED", c)
21 | end
22 |
23 | local function test()
24 | cwc.connect_signal("client::map", on_client_map)
25 | cwc.connect_signal("client::unmap", on_client_unmap)
26 |
27 | local clients = cwc.screen.focused():get_clients()
28 | cwc.connect_signal("client::custom", on_client_custom)
29 | cwc.emit_signal("client::custom", clients[1], "sig1")
30 | cwc.emit_signal("client::custom", clients[1], "sig2", 100)
31 | end
32 |
33 | return test
34 |
--------------------------------------------------------------------------------
/tests/luapi/tag.lua:
--------------------------------------------------------------------------------
1 | -- Test the cwc_tag property
2 |
3 | local enum = require("cuteful.enum")
4 |
5 | local cwc = cwc
6 |
7 | local function test()
8 | local s = cwc.screen.focused()
9 | local tag = s:get_tag(3)
10 |
11 | tag:strategy_idx(1)
12 | tag:view_only()
13 |
14 | assert(tag.index == 3)
15 |
16 | assert(tag.selected)
17 | tag.selected = not tag.selected
18 | assert(tag.selected == false)
19 | tag:toggle()
20 | assert(tag.selected)
21 |
22 | assert(tag.gap >= 0)
23 | tag.gap = 3
24 | assert(tag.gap == 3)
25 |
26 | assert(tag.layout_mode >= 0 and tag.layout_mode < enum.layout_mode.LENGTH)
27 | tag.layout_mode = enum.layout_mode.BSP
28 | assert(tag.layout_mode == enum.layout_mode.BSP)
29 |
30 | assert(tag.mwfact == 0.5)
31 | tag.mwfact = 99999
32 | assert(tag.mwfact == 0.9)
33 |
34 | assert(tostring(tag.screen):match("cwc_screen"))
35 |
36 | assert(tag.master_count == 1)
37 | tag.master_count = 2
38 | assert(tag.master_count == 2)
39 |
40 | assert(tag.column_count == 1)
41 | tag.column_count = 3
42 | assert(tag.column_count == 3)
43 |
44 | print("cwc_tag test PASSED")
45 | end
46 |
47 | return test
48 |
--------------------------------------------------------------------------------
/tests/meson.build:
--------------------------------------------------------------------------------
1 | executable(
2 | 'hashc',
3 | ['hash.c', '../src/util-map.c'],
4 | dependencies: [xxhash, wlr, wayland_server],
5 | include_directories : cwc_inc,
6 | )
7 |
8 | executable(
9 | 'hashcpp',
10 | ['hash.cpp'],
11 | )
12 |
13 | shared_module(
14 | 'csignal_test', 'capi/signal.c',
15 | protocols_server_header['xdg-shell'],
16 | include_directories : cwc_inc,
17 | dependencies: [wlr, lua],
18 | name_prefix: '',
19 | )
20 |
--------------------------------------------------------------------------------
/tests/rc.lua:
--------------------------------------------------------------------------------
1 | local cful = require("cuteful")
2 |
3 | local screen_test = require("luapi.screen")
4 | local client_test = require("luapi.client")
5 | local capi_signal = require("capi.signal")
6 | local lua_signal = require("luapi.signal")
7 | local container_test = require("luapi.container")
8 | local tag_test = require("luapi.tag")
9 | local layershell_test = require("luapi.layer_shell")
10 | local kbinding_test = require("luapi.kbinding")
11 |
12 | local cwc = cwc
13 |
14 | local mod = cful.enum.modifier
15 |
16 | local MODKEY = mod.LOGO
17 | if cwc.is_nested() then
18 | MODKEY = mod.ALT
19 | end
20 | print(MODKEY == mod.ALT)
21 |
22 | -- 2 client at each tag
23 | local tagidx = 1
24 | local counter = 0
25 | cwc.connect_signal("client::map", function(c)
26 | c:focus()
27 | c:move_to_tag(tagidx)
28 | counter = counter + 1
29 | if counter == 2 then
30 | counter = 0
31 | tagidx = tagidx + 1
32 | end
33 | end)
34 |
35 | -- make a client available on all tags in case the test need client
36 | for _ = 1, 20 do
37 | cwc.spawn_with_shell("kitty")
38 | end
39 |
40 | -- spawn waybar for layer shell testing
41 | cwc.spawn({ "waybar" })
42 |
43 | local kbd = cwc.kbd
44 | for i = 1, 9 do
45 | local i_str = tostring(i)
46 | kbd.bind(MODKEY, i_str, function()
47 | local t = cwc.screen.focused():get_tag(i)
48 | t:view_only()
49 | end, { description = "view tag #" .. i_str, group = "tag" })
50 |
51 | kbd.bind({ MODKEY, mod.CTRL }, i_str, function()
52 | local t = cwc.screen.focused():get_tag(i)
53 | t:toggle()
54 | end, { description = "toggle tag #" .. i_str, group = "tag" })
55 |
56 | kbd.bind({ MODKEY, mod.SHIFT }, i_str, function()
57 | local c = cwc.client.focused()
58 | if not c then return end
59 |
60 | c:move_to_tag(i_str)
61 | end, { description = "move focused client to tag #" .. i_str, group = "tag" })
62 |
63 | kbd.bind({ MODKEY, mod.SHIFT, mod.CTRL }, i_str, function()
64 | local c = cwc.client.focused()
65 | if not c then return end
66 |
67 | c:toggle_tag(i_str)
68 | end, { description = "toggle focused client on tag #" .. i_str, group = "tag" })
69 | end
70 |
71 | -- start API test by pressing F12
72 | cwc.kbd.bind({}, "F12", function()
73 | print("\n--------------------------------- API TEST START ------------------------------------")
74 | client_test.api()
75 | screen_test.api()
76 | layershell_test.api()
77 | capi_signal()
78 | lua_signal()
79 | tag_test()
80 | kbinding_test()
81 |
82 | cwc.screen.focused():get_tag(2):view_only()
83 | container_test.api()
84 | print("--------------------------------- API TEST END ------------------------------------")
85 | end)
86 |
87 | -- signal test by pressing F11 must execute after API test
88 | cwc.kbd.bind({}, "F11", function()
89 | print(
90 | "\n--------------------------------- SIGNAL TEST START ------------------------------------")
91 | client_test.signal()
92 | screen_test.signal()
93 | container_test.signal()
94 | layershell_test.signal()
95 | print("--------------------------------- SIGNAL TEST END ------------------------------------")
96 | end)
97 |
98 | cwc.kbd.bind({ MODKEY, mod.CTRL }, "r", cwc.reload, { description = "reload configuration" })
99 |
--------------------------------------------------------------------------------