main neuswc / example / wm.c
  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}