├── .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 | screenshot 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 | --------------------------------------------------------------------------------