1/* swc: example/wm.c
2 *
3 * Copyright (c) 2014 Michael Forney
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 */
23
24#include <stdlib.h>
25#include <swc.h>
26#include <unistd.h>
27#include <wayland-server.h>
28#include <xkbcommon/xkbcommon.h>
29
30struct screen {
31 struct swc_screen *swc;
32 struct wl_list windows;
33 unsigned num_windows;
34};
35
36struct window {
37 struct swc_window *swc;
38 struct screen *screen;
39 struct wl_list link;
40};
41
42static const char *terminal_command[] = {"st-wl", NULL};
43static const char *dmenu_command[] = {"dmenu_run-wl", NULL};
44static const uint32_t border_width = 1;
45static const uint32_t border_color_active = 0xff333388;
46static const uint32_t border_color_normal = 0xff888888;
47
48static struct screen *active_screen;
49static struct window *focused_window;
50static struct wl_display *display;
51static struct wl_event_loop *event_loop;
52
53/* This is a basic grid arrange function that tries to give each window an
54 * equal space. */
55static void
56arrange(struct screen *screen)
57{
58 struct window *window = NULL;
59 unsigned num_columns, num_rows, column_index, row_index;
60 struct swc_rectangle geometry;
61 struct swc_rectangle *screen_geometry = &screen->swc->usable_geometry;
62
63 if (screen->num_windows == 0) {
64 return;
65 }
66
67 num_columns = ceil(sqrt(screen->num_windows));
68 num_rows = screen->num_windows / num_columns + 1;
69 window = wl_container_of(screen->windows.next, window, link);
70
71 for (column_index = 0; &window->link != &screen->windows; ++column_index) {
72 geometry.x = screen_geometry->x + border_width +
73 screen_geometry->width * column_index / num_columns;
74 geometry.width =
75 screen_geometry->width / num_columns - 2 * border_width;
76
77 if (column_index == screen->num_windows % num_columns) {
78 --num_rows;
79 }
80
81 for (row_index = 0; row_index < num_rows; ++row_index) {
82 geometry.y = screen_geometry->y + border_width +
83 screen_geometry->height * row_index / num_rows;
84 geometry.height =
85 screen_geometry->height / num_rows - 2 * border_width;
86
87 swc_window_set_geometry(window->swc, &geometry);
88 window = wl_container_of(window->link.next, window, link);
89 }
90 }
91}
92
93static void
94screen_add_window(struct screen *screen, struct window *window)
95{
96 window->screen = screen;
97 wl_list_insert(&screen->windows, &window->link);
98 ++screen->num_windows;
99 swc_window_show(window->swc);
100 arrange(screen);
101}
102
103static void
104screen_remove_window(struct screen *screen, struct window *window)
105{
106 window->screen = NULL;
107 wl_list_remove(&window->link);
108 --screen->num_windows;
109 swc_window_hide(window->swc);
110 arrange(screen);
111}
112
113static void
114focus(struct window *window)
115{
116 if (focused_window) {
117 swc_window_set_border(focused_window->swc, border_color_normal,
118 border_width, 0, 0);
119 }
120
121 if (window) {
122 swc_window_set_border(window->swc, border_color_active, border_width, 0, 0);
123 swc_window_focus(window->swc);
124 } else {
125 swc_window_focus(NULL);
126 }
127
128 focused_window = window;
129}
130
131static void
132screen_usable_geometry_changed(void *data)
133{
134 struct screen *screen = data;
135
136 /* If the usable geometry of the screen changes, for example when a panel is
137 * docked to the edge of the screen, we need to rearrange the windows to
138 * ensure they are all within the new usable geometry. */
139 arrange(screen);
140}
141
142static void
143screen_entered(void *data)
144{
145 struct screen *screen = data;
146
147 active_screen = screen;
148}
149
150static const struct swc_screen_handler screen_handler = {
151 .usable_geometry_changed = &screen_usable_geometry_changed,
152 .entered = &screen_entered,
153};
154
155static void
156window_destroy(void *data)
157{
158 struct window *window = data, *next_focus;
159
160 if (focused_window == window) {
161 /* Try to find a new focus nearby the old one. */
162 next_focus = wl_container_of(window->link.next, window, link);
163
164 if (&next_focus->link == &window->screen->windows) {
165 next_focus = wl_container_of(window->link.prev, window, link);
166
167 if (&next_focus->link == &window->screen->windows) {
168 next_focus = NULL;
169 }
170 }
171
172 focus(next_focus);
173 }
174
175 screen_remove_window(window->screen, window);
176 free(window);
177}
178
179static void
180window_entered(void *data)
181{
182 struct window *window = data;
183
184 focus(window);
185}
186
187static const struct swc_window_handler window_handler = {
188 .destroy = &window_destroy,
189 .entered = &window_entered,
190};
191
192static void
193new_screen(struct swc_screen *swc)
194{
195 struct screen *screen;
196
197 screen = malloc(sizeof(*screen));
198
199 if (!screen) {
200 return;
201 }
202
203 screen->swc = swc;
204 screen->num_windows = 0;
205 wl_list_init(&screen->windows);
206 swc_screen_set_handler(swc, &screen_handler, screen);
207 active_screen = screen;
208}
209
210static void
211new_window(struct swc_window *swc)
212{
213 struct window *window;
214
215 window = malloc(sizeof(*window));
216
217 if (!window) {
218 return;
219 }
220
221 window->swc = swc;
222 window->screen = NULL;
223 swc_window_set_handler(swc, &window_handler, window);
224 swc_window_set_tiled(swc);
225 screen_add_window(active_screen, window);
226 focus(window);
227}
228
229const struct swc_manager manager = {&new_screen, &new_window};
230
231static void
232spawn(void *data, uint32_t time, uint32_t value, uint32_t state)
233{
234 char *const *command = data;
235
236 if (state != WL_KEYBOARD_KEY_STATE_PRESSED) {
237 return;
238 }
239
240 if (fork() == 0) {
241 execvp(command[0], command);
242 exit(EXIT_FAILURE);
243 }
244}
245
246static void
247quit(void *data, uint32_t time, uint32_t value, uint32_t state)
248{
249 if (state != WL_KEYBOARD_KEY_STATE_PRESSED) {
250 return;
251 }
252
253 wl_display_terminate(display);
254}
255
256int
257main(int argc, char *argv[])
258{
259 const char *socket;
260
261 display = wl_display_create();
262 if (!display) {
263 return EXIT_FAILURE;
264 }
265
266 socket = wl_display_add_socket_auto(display);
267 if (!socket) {
268 return EXIT_FAILURE;
269 }
270 setenv("WAYLAND_DISPLAY", socket, 1);
271
272 if (!swc_initialize(display, NULL, &manager)) {
273 return EXIT_FAILURE;
274 }
275
276 swc_add_binding(SWC_BINDING_KEY, SWC_MOD_LOGO, XKB_KEY_Return, &spawn,
277 terminal_command);
278 swc_add_binding(SWC_BINDING_KEY, SWC_MOD_LOGO, XKB_KEY_r, &spawn,
279 dmenu_command);
280 swc_add_binding(SWC_BINDING_KEY, SWC_MOD_LOGO, XKB_KEY_q, &quit, NULL);
281
282 event_loop = wl_display_get_event_loop(display);
283 wl_display_run(display);
284 wl_display_destroy(display);
285
286 return EXIT_SUCCESS;
287}