commit daa8175
wf
·
2026-04-04 08:49:42 +0000 UTC
parent 7a39712
More IPC commands, clean up code, add documentation
+0,
-1
1@@ -1,3 +1,2 @@
2 howl
3 howlc
4-test
M
Makefile
+8,
-2
1@@ -22,13 +22,19 @@ $(howlc): $(howlc_src)
2 @echo "Building howlc..."
3 $(CC) $(CFLAGS) $(LDFLAGS) -o $(howlc) $(howlc_src) $(LDLIBS)
4
5-install: $(howl) $(howlc)
6+install: $(howl) $(howlc) install-man
7 install -Dm755 $(howl) $(DESTDIR)$(PREFIX)/bin/$(howl)
8 install -Dm755 $(howlc) $(DESTDIR)$(PREFIX)/bin/$(howlc)
9
10-uninstall:
11+uninstall: uninstall-man
12 rm -f $(DESTDIR)$(PREFIX)/bin/$(howl)
13 rm -f $(DESTDIR)$(PREFIX)/bin/$(howlc)
14
15+install-man:
16+ install -Dm644 doc/howlc.1 $(DESTDIR)$(PREFIX)/share/man/man1/howlc.1
17+
18+uninstall-man:
19+ rm -f $(DESTDIR)$(PREFIX)/share/man/man1/howlc.1
20+
21 clean:
22 rm -f $(howl) $(howlc)
+13,
-26
1@@ -1,35 +1,22 @@
2 howl
3 ====
4
5-howl is a floating wayland compositor written in neuswc[^1] that implements an IPC interface, allowing for scriptability and advanced usage. howl's code is largely based on that of tohu[^2]'s and berry[^3]'s.
6-to build howl, use any POSIX-compliant make. the repo provides a Makefile compatible with most implementations, including bmake.
7+howl is a small wayland compositor written with the neuswc library.
8
9-dependencies
10-------------
11+It is controlled through an external client, which allows for scriptability.
12
13- - neuswc[^1]
14- - everything that neuswc depends on
15+See the `howlc` manual page for more information on usage.
16
17-todo
18-----
19+To do
20+-----
21
22- - titlebars(?)
23- - unbinding keys
24- - allow for more scriptability, assign pseudorandom IDs to windows etc.
25- - documentation
26+ * Expose compositor internals through the client
27+ * Eyecandy: titlebars and double borders
28+ * Clean up the codebase
29
30-acknowledgements
31-----------------
32+Credits
33+-------
34
35-the name howl doesn't carry any meaning, aside from being a nice coincidence that its last 2 letters are 'wl'.
36-howl is currently in a moreso experimental state rather than being a full-fledged compositor. the end goal is to develop an alternative to berrywm, my favorite x11 window manager, for wayland. however, the structural differences and my lack of competence do not allow it to be as high-quality as berry, though i am working towards making it at least comparable!
37-
38-obviously, i would like to thank the following:
39- - neulibs developers for creating neuswc/neuwld
40- - shrub900/uint23 for their amazing work on tohu/wsxwm respectively
41- - jlervin for their amazing work on berrywm
42- - many, many others
43-
44-[^1]: <https://git.sr.ht/~shrub900/neuswc>
45-[^2]: <https://git.sr.ht/~shrub900/tohu>
46-[^3]: <https://github.com/JLErvin/berry>
47+ * neuswc authors
48+ * uint23, shrub: For developing wsxwm / tohu
49+ * JLErvin: For developing berry, which is the inspiration and reference for this project
M
client.c
+22,
-17
1@@ -17,23 +17,28 @@ struct command {
2 };
3
4 static const struct command commands[] = {
5- { "move", cmd_move, 2, false },
6- { "resize", cmd_resize, 2, false },
7- { "teleport", cmd_teleport, 4, false },
8- { "center", cmd_center, 0, false },
9- { "hide", cmd_hide, 0, false },
10- { "show", cmd_show, 0, false },
11- { "close", cmd_close, 0, false },
12- { "geometry", cmd_geometry, 0, false },
13- { "pid", cmd_pid, 0, false },
14- { "title", cmd_title, 0, false },
15- { "appid", cmd_app_id, 0, false },
16-
17- { "bind", cmd_bind, 2, false },
18- { "modkey", cmd_modkey, 1, true },
19- { "focus_color", cmd_focus_color, 1, true },
20- { "unfocus_color", cmd_unfocus_color, 1, true },
21- { "border_width", cmd_border_width, 1, true },
22+ { "move", cmd_move, 2, false },
23+ { "move_absolute", cmd_move_absolute, 2, false },
24+ { "resize", cmd_resize, 2, false },
25+ { "resize_absolute", cmd_resize, 2, false },
26+ { "teleport", cmd_teleport, 4, false },
27+ { "center", cmd_center, 0, false },
28+ { "fullscreen", cmd_fullscreen, 0, false },
29+ { "hide", cmd_hide, 0, false },
30+ { "show", cmd_show, 0, false },
31+ { "close", cmd_close, 0, false },
32+ { "workspace", cmd_workspace, 1, false },
33+ { "move_workspace", cmd_move_workspace, 1, false },
34+ { "geometry", cmd_geometry, 0, false },
35+ { "pid", cmd_pid, 0, false },
36+ { "title", cmd_title, 0, false },
37+ { "appid", cmd_app_id, 0, false },
38+ { "bind", cmd_bind, 2, false },
39+ { "modkey", cmd_modkey, 1, true },
40+ { "focus_color", cmd_focus_color, 1, true },
41+ { "unfocus_color", cmd_unfocus_color, 1, true },
42+ { "border_width", cmd_border_width, 1, true },
43+ { "quit", cmd_quit, 0, false }
44 };
45
46 static void
+206,
-0
1@@ -0,0 +1,206 @@
2+.\" generated with Ronn-NG/v0.10.1
3+.\" http://github.com/apjanke/ronn-ng/tree/0.10.1
4+.TH "HOWLC" "1" "April 2026" ""
5+.SH "NAME"
6+\fBhowlc\fR \- client to control the howl compositor
7+.SH "SYNOPSIS"
8+\fBhowlc\fR \fIcmd\fR [\fIargs\|\.\|\.\|\.\fR]
9+.SH "DESCRIPTION"
10+\fBhowlc\fR is a client that controls the howl compositor\.
11+.SH "COMMANDS"
12+.TP
13+\fBmove\fR, \fBmove_absolute\fR
14+Will move the window relative to either its current position or to the top left corner of the screen\. Arguments are in the form of pairs of x and y integer coordinates\.
15+.IP
16+Example:
17+.IP "" 4
18+.nf
19+$ howlc move 20 \-40 # Shift the window 20px forward and 40px up relative to its position
20+$ howlc move_absolute 300 300 # Move the window to absolute coordinates [300, 300]
21+.fi
22+.IP "" 0
23+
24+.TP
25+\fBresize\fR, \fBresize_absolute\fR
26+Will resize the window relative to either its current position or the top left corner of the screen\. Arguments are in the form of pairs of w and h integers\.
27+.IP
28+Example:
29+.IP "" 4
30+.nf
31+$ howlc resize 20 \-40 # Grow the window 20px forward and shrink it 40px up relative to its size
32+$ howlc resize_absolute 300 300 # Resize the window to the absolute size [300, 300]
33+.fi
34+.IP "" 0
35+
36+.TP
37+\fBteleport\fR
38+Will "teleport" (move and resize) the window to the coordinates specified, in the format of x, y, w and h\.
39+.IP
40+Example:
41+.IP "" 4
42+.nf
43+$ howlc teleport 50 25 200 250 # Teleport the window to [50, 25] and resize it to [200, 250]
44+.fi
45+.IP "" 0
46+
47+.TP
48+\fBcenter\fR
49+Will position the window in the center of the screen\.
50+.IP
51+Example:
52+.IP "" 4
53+.nf
54+$ howlc center # Move the window to the center of the screen
55+.fi
56+.IP "" 0
57+
58+.TP
59+\fBfullscreen\fR
60+Will resize the window to the screen size\.
61+.IP
62+Example:
63+.IP "" 4
64+.nf
65+$ howlc fullscreen # Put the window in fullscreen mode
66+.fi
67+.IP "" 0
68+
69+.TP
70+\fBhide\fR, \fBshow\fR
71+Hide/show the current window\. Currently these don't have much use\.
72+.IP
73+Example:
74+.IP "" 4
75+.nf
76+$ howlc hide # Hide (unmap) the current window
77+$ howlc show # And now show (map) it
78+.fi
79+.IP "" 0
80+
81+.TP
82+\fBclose\fR
83+Closes the window\.
84+.IP
85+Example:
86+.IP "" 4
87+.nf
88+$ howlc close # Close the current window
89+.fi
90+.IP "" 0
91+
92+.TP
93+\fBworkspace\fR, \fBmove_workspace\fR
94+Switch, or move the current window, to the specified workspace\. Argument is a single integer representing the workspace number\.
95+.IP
96+Example:
97+.IP "" 4
98+.nf
99+$ howlc workspace 3 # Switch to the 3rd workspace
100+$ howlc move_workspace 6 # Move the current window to the 6th workspace
101+.fi
102+.IP "" 0
103+
104+.TP
105+\fBget_geometry\fR, \fBget_pid\fR, \fBget_title\fR, \fBget_app_id\fR
106+Print the current window's geometry (in x, y, w and h), process ID, title or app ID\. These are practically non\-operational right now\.
107+.IP
108+Example:
109+.IP "" 4
110+.nf
111+$ howlc get_geometry # Get the current window's geometry
112+327 148 620 620
113+$ howlc get_pid # Get the current window's process ID
114+12345
115+$ howlc get_title # Get the current window's title
116+Example
117+$ howlc get_app_id # Get the current window's app ID
118+example
119+.fi
120+.IP "" 0
121+
122+.TP
123+\fBbind\fR
124+Create a key binding\. Arguments are in the form of a list of keys and the command to execute, see the BINDINGS section for more info\.
125+.IP
126+Example:
127+.IP "" 4
128+.nf
129+$ howlc bind mod+shift+g "example_command" # Bind modkey+Shift+g to execute "example_command"
130+$ howlc bind win+4 "howlc workspace 4" # Bind Win+4 to switch to the 4th workspace
131+.fi
132+.IP "" 0
133+
134+.TP
135+\fBmodkey\fR
136+Configure the modifier key\. This can be then used as an alias in keybindings, like so: \fBmod+key\fR\. See the BINDINGS section for allowed values\.
137+.IP
138+Example:
139+.IP "" 4
140+.nf
141+$ howlc modkey alt # Configure the modifier key to Alt
142+$ howlc modkey win+ctrl # Configure the modifier key to Win+Ctrl
143+.fi
144+.IP "" 0
145+
146+.TP
147+\fBfocus_color\fR, \fBunfocus_color\fR
148+Set the focused and unfocused colors of the window borders\. Arguments are in the format of #RRGGBB, where the leading pound sign is optional\. Note that in the format of #RRGGBB, the argument needs to be quoted, since the pound sign (#) is a special one in most shells\.
149+.IP
150+Example:
151+.IP "" 4
152+.nf
153+$ howlc focus_color "#f03937" # Set the focused color to a shade of red
154+$ howlc unfocus_color eeeeee # Set the unfocused color to a bright white
155+.fi
156+.IP "" 0
157+
158+.TP
159+\fBborder_width\fR
160+Set the window's border width\. Argument is a single integer representing the new border width\.
161+.IP
162+Examples:
163+.IP "" 4
164+.nf
165+$ howlc border_width 4 # Set the border width to 4px
166+$ howlc border_width 0 # Set the border width to 0px, essentially disabling borders
167+.fi
168+.IP "" 0
169+
170+.TP
171+\fBquit\fR
172+Quit the compositor\.
173+.IP
174+Example:
175+.IP "" 4
176+.nf
177+$ howlc quit # Terminate the Wayland display, cleanup and exit
178+.fi
179+.IP "" 0
180+
181+.SH "BINDINGS"
182+The format for keybindings is the following:
183+.P
184+Each key/modifier in the binding string is separated by a plus (+) sign\. There can be multiple modifiers, but only one key\. The accepted modifiers are:
185+.IP "\(bu" 4
186+\fBmod\fR: Corresponds to the modifier key
187+.IP "\(bu" 4
188+\fBalt\fR: The Alt key
189+.IP "\(bu" 4
190+\fBwin\fR: The Win key, sometimes the logo
191+.IP "\(bu" 4
192+\fBctrl\fR: The Ctrl key
193+.IP "\(bu" 4
194+\fBshift\fR: The Shift key
195+.IP "\(bu" 4
196+\fBany\fR: Any, (and) no modifiers
197+.IP "" 0
198+.P
199+The accepted key strings are as specified in \fB<xkbcommon/xkbcommon\-keysyms\.h>\fR\.
200+.SH "BUGS"
201+The \fBget_*\fR functions are useless since they print the needed information to the compositor's controlling terminal\.
202+.P
203+The manual does not specify the names for modifiers on other platforms\.
204+.SH "COPYRIGHT"
205+\fBhowl\fR and \fBhowlc\fR are (C) wf 2026 \fIhttps://codeberg\.org/wf\fR\.
206+.SH "SEE ALSO"
207+The README\.
+185,
-0
1@@ -0,0 +1,185 @@
2+howlc(1) - client to control the howl compositor
3+================================================
4+
5+## SYNOPSIS
6+
7+`howlc` *cmd* [*args...*]
8+
9+## DESCRIPTION
10+
11+**howlc** is a client that controls the howl compositor.
12+
13+## COMMANDS
14+
15+ * `move`, `move_absolute`:
16+ Will move the window relative to either its current position or to the top
17+ left corner of the screen. Arguments are in the form of pairs of x and y
18+ integer coordinates.
19+
20+ Example:
21+
22+ ```
23+ $ howlc move 20 -40 # Shift the window 20px forward and 40px up relative to its position
24+ $ howlc move_absolute 300 300 # Move the window to absolute coordinates [300, 300]
25+ ```
26+
27+
28+ * `resize`, `resize_absolute`:
29+ Will resize the window relative to either its current position or the top
30+ left corner of the screen. Arguments are in the form of pairs of w and h
31+ integers.
32+
33+ Example:
34+ ```
35+ $ howlc resize 20 -40 # Grow the window 20px forward and shrink it 40px up relative to its size
36+ $ howlc resize_absolute 300 300 # Resize the window to the absolute size [300, 300]
37+ ```
38+
39+ * `teleport`:
40+ Will "teleport" (move and resize) the window to the coordinates specified, in
41+ the format of x, y, w and h.
42+
43+ Example:
44+ ```
45+ $ howlc teleport 50 25 200 250 # Teleport the window to [50, 25] and resize it to [200, 250]
46+ ```
47+
48+ * `center`:
49+ Will position the window in the center of the screen.
50+
51+ Example:
52+ ```
53+ $ howlc center # Move the window to the center of the screen
54+ ```
55+
56+ * `fullscreen`:
57+ Will resize the window to the screen size.
58+
59+ Example:
60+ ```
61+ $ howlc fullscreen # Put the window in fullscreen mode
62+ ```
63+
64+ * `hide`, `show`:
65+ Hide/show the current window. Currently these don't have much use.
66+
67+ Example:
68+ ```
69+ $ howlc hide # Hide (unmap) the current window
70+ $ howlc show # And now show (map) it
71+ ```
72+
73+ * `close`:
74+ Closes the window.
75+
76+ Example:
77+ ```
78+ $ howlc close # Close the current window
79+ ```
80+
81+ * `workspace`, `move_workspace`:
82+ Switch, or move the current window, to the specified workspace. Argument is a single
83+ integer representing the workspace number.
84+
85+ Example:
86+ ```
87+ $ howlc workspace 3 # Switch to the 3rd workspace
88+ $ howlc move_workspace 6 # Move the current window to the 6th workspace
89+ ```
90+
91+ * `get_geometry`, `get_pid`, `get_title`, `get_app_id`:
92+ Print the current window's geometry (in x, y, w and h), process ID, title or app ID.
93+ These are practically non-operational right now.
94+
95+ Example:
96+ ```
97+ $ howlc get_geometry # Get the current window's geometry
98+ 327 148 620 620
99+ $ howlc get_pid # Get the current window's process ID
100+ 12345
101+ $ howlc get_title # Get the current window's title
102+ Example
103+ $ howlc get_app_id # Get the current window's app ID
104+ example
105+ ```
106+
107+ * `bind`:
108+ Create a key binding. Arguments are in the form of a list of keys and the command to execute,
109+ see the BINDINGS section for more info.
110+
111+ Example:
112+ ```
113+ $ howlc bind mod+shift+g "example_command" # Bind modkey+Shift+g to execute "example_command"
114+ $ howlc bind win+4 "howlc workspace 4" # Bind Win+4 to switch to the 4th workspace
115+ ```
116+
117+ * `modkey`:
118+ Configure the modifier key. This can be then used as an alias in keybindings, like so: `mod+key`.
119+ See the BINDINGS section for allowed values.
120+
121+ Example:
122+ ```
123+ $ howlc modkey alt # Configure the modifier key to Alt
124+ $ howlc modkey win+ctrl # Configure the modifier key to Win+Ctrl
125+ ```
126+
127+ * `focus_color`, `unfocus_color`:
128+ Set the focused and unfocused colors of the window borders. Arguments are in the format of #RRGGBB,
129+ where the leading pound sign is optional. Note that in the format of #RRGGBB, the argument needs to
130+ be quoted, since the pound sign (#) is a special one in most shells.
131+
132+ Example:
133+ ```
134+ $ howlc focus_color "#f03937" # Set the focused color to a shade of red
135+ $ howlc unfocus_color eeeeee # Set the unfocused color to a bright white
136+ ```
137+
138+ * `border_width`:
139+ Set the window's border width. Argument is a single integer representing the new border width.
140+
141+ Examples:
142+ ```
143+ $ howlc border_width 4 # Set the border width to 4px
144+ $ howlc border_width 0 # Set the border width to 0px, essentially disabling borders
145+ ```
146+
147+ * `quit`:
148+ Quit the compositor.
149+
150+ Example:
151+ ```
152+ $ howlc quit # Terminate the Wayland display, cleanup and exit
153+ ```
154+
155+## BINDINGS
156+
157+The format for keybindings is the following:
158+
159+Each key/modifier in the binding string is separated by a plus (+) sign. There can be multiple modifiers,
160+but only one key. The accepted modifiers are:
161+
162+ * `mod`: Corresponds to the modifier key
163+ * `alt`: The Alt key
164+ * `win`: The Win key, sometimes the logo
165+ * `ctrl`: The Ctrl key
166+ * `shift`: The Shift key
167+ * `any`: Any, (and) no modifiers
168+
169+The accepted key strings are as specified in `<xkbcommon/xkbcommon-keysyms.h>`.
170+
171+## BUGS
172+
173+The `get_*` functions are useless since they print the needed information to the compositor's controlling
174+terminal.
175+
176+The manual does not specify the names for modifiers on other platforms.
177+
178+There is no way to unbind keys. This is an swc problem, but still deserves to be mentioned here.
179+
180+## COPYRIGHT
181+
182+`howl` and `howlc` are (C) wf 2026 <https://codeberg.org/wf>.
183+
184+## SEE ALSO
185+
186+The README.
M
howl.c
+159,
-24
1@@ -4,8 +4,6 @@
2 #include <errno.h>
3 #include <signal.h>
4 #include <unistd.h>
5-#include <fcntl.h>
6-#include <getopt.h>
7 #include <sys/socket.h>
8 #include <sys/un.h>
9
10@@ -22,19 +20,27 @@ static void on_win_destroy(void *);
11 static void on_win_entered(void *);
12 static void on_scr_destroy(void *);
13 static bool is_ws_client(const struct client *, const struct screen *);
14+static void mouse_move(void *, uint32_t, uint32_t, uint32_t);
15+static void mouse_resize(void *, uint32_t, uint32_t, uint32_t);
16
17 extern void ipc_move(char **);
18+extern void ipc_move_absolute(char **);
19 extern void ipc_resize(char **);
20+extern void ipc_resize_absolute(char **);
21 extern void ipc_teleport(char **);
22 extern void ipc_center(char **);
23+extern void ipc_fullscreen(char **);
24 extern void ipc_hide(char **);
25 extern void ipc_show(char **);
26 extern void ipc_close(char **);
27+extern void ipc_workspace(char **);
28+extern void ipc_move_workspace(char **);
29 extern void ipc_get_geometry(char **);
30 extern void ipc_get_pid(char **);
31 extern void ipc_get_title(char **);
32 extern void ipc_get_app_id(char **);
33 extern void ipc_bind(char **);
34+extern void ipc_quit(char **);
35 extern void ipc_config(char **);
36
37 bool have_config = true;
38@@ -57,19 +63,25 @@ static struct swc_screen_handler scr_handler = {
39
40 typedef void (*cmd_handler_t)(char **);
41 static const cmd_handler_t cmd_handler [cmd_last] = {
42- [cmd_move] = ipc_move,
43- [cmd_resize] = ipc_resize,
44- [cmd_teleport] = ipc_teleport,
45- [cmd_center] = ipc_center,
46- [cmd_hide] = ipc_hide,
47- [cmd_show] = ipc_show,
48- [cmd_close] = ipc_close,
49- [cmd_geometry] = ipc_get_geometry,
50- [cmd_pid] = ipc_get_pid,
51- [cmd_title] = ipc_get_title,
52- [cmd_app_id] = ipc_get_app_id,
53- [cmd_bind] = ipc_bind,
54- [cmd_config] = ipc_config
55+ [cmd_move] = ipc_move,
56+ [cmd_move_absolute] = ipc_move,
57+ [cmd_resize] = ipc_resize,
58+ [cmd_resize_absolute] = ipc_resize,
59+ [cmd_teleport] = ipc_teleport,
60+ [cmd_center] = ipc_center,
61+ [cmd_fullscreen] = ipc_fullscreen,
62+ [cmd_hide] = ipc_hide,
63+ [cmd_show] = ipc_show,
64+ [cmd_close] = ipc_close,
65+ [cmd_workspace] = ipc_workspace,
66+ [cmd_move_workspace] = ipc_move_workspace,
67+ [cmd_geometry] = ipc_get_geometry,
68+ [cmd_pid] = ipc_get_pid,
69+ [cmd_title] = ipc_get_title,
70+ [cmd_app_id] = ipc_get_app_id,
71+ [cmd_bind] = ipc_bind,
72+ [cmd_quit] = ipc_quit,
73+ [cmd_config] = ipc_config
74 };
75
76 static int
77@@ -130,7 +142,8 @@ ipc_handler(int fd, uint32_t mask, void *data) {
78
79 int cmd = atoi(argv[0]);
80 void *fn = cmd_handler[cmd];
81- if (!fn) _wrn("no such command #%s", argv[0]);
82+ /* TODO: this is hard to debug */
83+ if (!fn) _wrn("no such command #%d", cmd);
84
85 cmd_handler[cmd](argv);
86 }
87@@ -212,9 +225,10 @@ setup(void) {
88 wm.scr = NULL;
89 wm.grab.active = false;
90 wm.grab.resize = false;
91- wm.grab.c = NULL;
92+ wm.grab.client = NULL;
93 wm.ws = 1;
94
95+ config.modkey = SWC_MOD_LOGO;
96 config.focus_color = 0xFFE16C87;
97 config.unfocus_color = 0xFF444059;
98 config.border_width = 2;
99@@ -249,6 +263,22 @@ setup(void) {
100
101 if (have_config) load_config(conf_path);
102
103+ /* FIXME: these don't get enough time to inherit the modkey that the user configured */
104+ swc_add_binding(
105+ SWC_BINDING_BUTTON,
106+ config.modkey,
107+ 0x110, /* left mouse button */
108+ mouse_move,
109+ NULL
110+ );
111+ swc_add_binding(
112+ SWC_BINDING_BUTTON,
113+ config.modkey,
114+ 0x111, /* right mouse button */
115+ mouse_resize,
116+ NULL
117+ );
118+
119 signal(SIGINT, sig_handler);
120 signal(SIGTERM, sig_handler);
121 signal(SIGQUIT, sig_handler);
122@@ -256,10 +286,9 @@ setup(void) {
123 free(conf_path);
124 }
125
126-static void
127+void
128 cleanup(void) {
129- swc_finalize();
130- wl_display_destroy(wm.dpy);
131+ wl_display_terminate(wm.dpy);
132 close(sfd);
133 unlink(SOCK_PATH);
134 }
135@@ -300,6 +329,112 @@ is_ws_client(const struct client *c, const struct screen *s) {
136 return c && c->ws == wm.ws && (!s || c->scr == s);
137 }
138
139+static void
140+sync_windows(void) {
141+ struct client *c;
142+
143+ wl_list_for_each(c, &wm.clients, link) {
144+ if (c->ws == wm.ws)
145+ swc_window_show(c->win);
146+ else
147+ swc_window_hide(c->win);
148+ }
149+}
150+
151+void
152+ws_go_to(uint8_t ws) {
153+ struct client *c;
154+
155+ if (ws < 1 || ws > 9 || ws == wm.ws)
156+ return;
157+
158+ wm.ws = ws;
159+ sync_windows();
160+
161+ c = first_client(wm.scr);
162+ if (!c) c = first_client(NULL);
163+ focus(c);
164+}
165+
166+void
167+ws_move_to(uint8_t ws) {
168+ struct client *c, *next;
169+
170+ if (!wm.cur || ws < 1 || ws > 9) return;
171+ c = wm.cur;
172+ if (c->ws == ws)
173+ return;
174+
175+ c->ws = ws;
176+ if (c->ws == wm.ws)
177+ swc_window_show(c->win);
178+ else
179+ swc_window_hide(c->win);
180+
181+ next = first_client(wm.scr);
182+ if (!next) next = first_client(NULL);
183+ focus(next);
184+}
185+
186+static void
187+mouse_move(void *data, uint32_t time, uint32_t value, uint32_t state) {
188+ UNUSED(data);
189+ UNUSED(time);
190+ UNUSED(value);
191+
192+ if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
193+ if (!wm.cur)
194+ return;
195+
196+ _inf("I am moving with my mouse");
197+
198+ wm.grab.active = true;
199+ wm.grab.resize = false;
200+ wm.grab.client = wm.cur;
201+
202+ swc_window_begin_move(wm.grab.client->win);
203+ } else {
204+ if (!wm.grab.active || wm.grab.resize || !wm.grab.client)
205+ return;
206+
207+ _inf("I am no longer moving with my mouse.");
208+
209+ swc_window_end_move(wm.grab.client->win);
210+ wm.grab.active = false;
211+ wm.grab.client = NULL;
212+ }
213+}
214+
215+static void
216+mouse_resize(void *data, uint32_t time, uint32_t value, uint32_t state) {
217+ UNUSED(data);
218+ UNUSED(time);
219+ UNUSED(value);
220+
221+ if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
222+ if (!wm.cur)
223+ return;
224+
225+ wm.grab.active = true;
226+ wm.grab.resize = true;
227+ wm.grab.client = wm.cur;
228+
229+ swc_window_begin_resize(
230+ wm.grab.client->win,
231+ SWC_WINDOW_EDGE_RIGHT | SWC_WINDOW_EDGE_BOTTOM
232+ );
233+ } else {
234+ if (!wm.grab.active || !wm.grab.resize || !wm.grab.client)
235+ return;
236+
237+ swc_window_end_resize(wm.grab.client->win);
238+
239+ wm.grab.active = false;
240+ wm.grab.resize = false;
241+ wm.grab.client = NULL;
242+ }
243+}
244+
245 static void
246 new_screen(struct swc_screen *scr) {
247 struct screen *s;
248@@ -332,8 +467,8 @@ new_window(struct swc_window *win) {
249
250 c->win = win;
251 c->scr = wm.scr;
252- c->visible = 0;
253- c->fullscreen = 0;
254+ c->visible = false;
255+ c->fullscreen = false;
256 c->ws = wm.ws;
257 c->x = 0;
258 c->y = 0;
259@@ -370,9 +505,9 @@ on_win_destroy(void *data) {
260 struct client *c = data, *next;
261
262 if (!c) return;
263- if (wm.grab.active && wm.grab.c == c) {
264+ if (wm.grab.active && wm.grab.client == c) {
265 wm.grab.active = false;
266- wm.grab.c = NULL;
267+ wm.grab.client = NULL;
268 }
269
270 wl_list_remove(&c->link);
+6,
-0
1@@ -3,12 +3,17 @@
2
3 enum cmd {
4 cmd_move,
5+ cmd_move_absolute,
6 cmd_resize,
7+ cmd_resize_absolute,
8 cmd_teleport,
9 cmd_center,
10+ cmd_fullscreen,
11 cmd_hide,
12 cmd_show,
13 cmd_close,
14+ cmd_workspace,
15+ cmd_move_workspace,
16 cmd_geometry,
17 cmd_pid,
18 cmd_title,
19@@ -18,6 +23,7 @@ enum cmd {
20 cmd_focus_color,
21 cmd_unfocus_color,
22 cmd_border_width,
23+ cmd_quit,
24 cmd_config,
25 cmd_last
26 };
+1,
-8
1@@ -7,13 +7,6 @@
2 #include <wayland-server.h>
3 #include <swc.h>
4
5-union arg {
6- int i;
7- uint32_t ui;
8- float f;
9- const void *v;
10-};
11-
12 struct config {
13 uint32_t modkey;
14 uint32_t focus_color;
15@@ -43,7 +36,7 @@ struct screen {
16
17 struct grab {
18 bool active, resize;
19- struct client *c;
20+ struct client *client;
21 };
22
23 struct wm {
M
ipc.c
+75,
-1
1@@ -38,7 +38,7 @@ fn_bind(char *s) {
2 char *tok = strtok(s, "+");
3 while (tok != NULL) {
4 uint32_t tmp = 0;
5-
6+
7 if (strcmp(tok, "mod") == 0) tmp = config.modkey;
8 else if (strcmp(tok, "alt") == 0) tmp = SWC_MOD_ALT;
9 else if (strcmp(tok, "win") == 0) tmp = SWC_MOD_LOGO;
10@@ -65,6 +65,9 @@ fn_bind(char *s) {
11 }
12
13 extern void bind_handler(void *, uint32_t, uint32_t, uint32_t);
14+extern void ws_go_to(int8_t);
15+extern void ws_move_to(int8_t);
16+extern void cleanup(void);
17
18 /*********/
19
20@@ -84,6 +87,14 @@ ipc_move(char **arg) {
21 swc_window_set_position(wm.cur->win, wm.cur->x + atoi(arg[1]), wm.cur->y + atoi(arg[2]));
22 }
23
24+void
25+ipc_move_absolute(char **arg) {
26+ if (arg[1] == NULL || arg[2] == NULL || !wm.cur)
27+ return;
28+
29+ swc_window_set_position(wm.cur->win, atoi(arg[1]), atoi(arg[2]));
30+}
31+
32 void
33 ipc_resize(char **arg) {
34 if (arg[1] == NULL || arg[2] == NULL || !wm.cur) return;
35@@ -99,6 +110,14 @@ ipc_resize(char **arg) {
36 swc_window_set_size(wm.cur->win, wm.cur->width + strtoul(arg[1], NULL, 0), wm.cur->height + strtoul(arg[2], NULL, 0));
37 }
38
39+void
40+ipc_resize_absolute(char **arg) {
41+ if (arg[1] == NULL || arg[2] == NULL || !wm.cur)
42+ return;
43+
44+ swc_window_set_size(wm.cur->win, strtoul(arg[1], NULL, 0), strtoul(arg[2], NULL, 0));
45+}
46+
47 void
48 ipc_teleport(char **arg) {
49 if (arg[1] == NULL || arg[2] == NULL ||
50@@ -135,6 +154,41 @@ ipc_center(char **arg) {
51 );
52 }
53
54+void
55+ipc_fullscreen(char **arg) {
56+ UNUSED(arg);
57+ if (!wm.cur) return;
58+
59+ struct swc_rectangle geom;
60+
61+ if (wm.cur->fullscreen) {
62+ wm.cur->fullscreen = false;
63+ swc_window_set_stacked(wm.cur->win);
64+
65+ if (wm.cur->width > 0 && wm.cur->height > 0) {
66+ geom.x = wm.cur->x;
67+ geom.y = wm.cur->y;
68+ geom.width = wm.cur->width;
69+ geom.height = wm.cur->height;
70+ swc_window_set_geometry(wm.cur->win, &geom);
71+ }
72+ return;
73+ }
74+
75+ if (!wm.cur->scr || !wm.cur->scr->scr)
76+ return;
77+
78+ if (swc_window_get_geometry(wm.cur->win, &geom)) {
79+ wm.cur->x = geom.x;
80+ wm.cur->y = geom.y;
81+ wm.cur->width = geom.width;
82+ wm.cur->height = geom.height;
83+ }
84+
85+ wm.cur->fullscreen = true;
86+ swc_window_set_fullscreen(wm.cur->win, wm.scr->scr);
87+}
88+
89 void
90 ipc_hide(char **arg) {
91 UNUSED(arg);
92@@ -159,6 +213,20 @@ ipc_close(char **arg) {
93 swc_window_close(wm.cur->win);
94 }
95
96+void
97+ipc_workspace(char **arg) {
98+ if (arg[1] == NULL) return;
99+
100+ ws_go_to(atoi(arg[1]));
101+}
102+
103+void
104+ipc_move_workspace(char **arg) {
105+ if (arg[1] == NULL || !wm.cur) return;
106+
107+ ws_move_to(atoi(arg[1]));
108+}
109+
110 /*********/
111
112 void
113@@ -267,3 +335,9 @@ ipc_config(char **arg) {
114 );
115 }
116 }
117+
118+void
119+ipc_quit(char **arg) {
120+ UNUSED(arg);
121+ cleanup();
122+}
M
log.c
+3,
-0
1@@ -4,6 +4,8 @@
2
3 #include "howl.h"
4
5+extern void cleanup();
6+
7 void
8 _inf(const char *msg, ...) {
9 va_list list;
10@@ -45,5 +47,6 @@ _err(int ret, const char *msg, ...) {
11 fputc('\n', stderr);
12 fflush(stderr);
13
14+ cleanup();
15 exit(ret);
16 }