1 /*
2  * Copyright © 2011 Benjamin Franzke
3  * Copyright © 2010 Intel Corporation
4  * Copyright © 2018-2021 Jonas Ådahl
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice (including the
15  * next paragraph) shall be included in all copies or substantial
16  * portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21  * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
22  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
23  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25  * SOFTWARE.
26  */
27 
28 #include "config.h"
29 
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <linux/input.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/mman.h>
37 #include <unistd.h>
38 #include <wayland-cursor.h>
39 #include <xkbcommon/xkbcommon.h>
40 #include <iostream>
41 
42 #include "libdecor.h"
43 #include "utils.h"
44 #include "cursor-settings.h"
45 extern "C" {
46 #include "os-compatibility.h"
47 }
48 
49 #include "xdg-shell-client-protocol.h"
50 
51 static const int DEFAULT_WIDTH = 400;
52 static const int DEFAULT_HEIGHT = 400;
53 
54 static struct wl_compositor *wl_compositor;
55 static struct wl_shm *wl_shm;
56 
57 static bool has_xrgb = false;
58 
59 using std::cerr;
60 using std::endl;
61 
62 class Buffer {
63 public:
Buffer(struct wl_buffer * wl_buffer,int width,int height,void * data,size_t data_size)64 	Buffer(struct wl_buffer *wl_buffer,
65 	       int width,
66 	       int height,
67 	       void *data,
68 	       size_t data_size) :
69 		wl_buffer(wl_buffer),
70 		width(width),
71 		height(height),
72 		data(data),
73 		data_size(data_size)
74 	{
75 		wl_buffer_add_listener(this->wl_buffer, &this->buffer_listener, this);
76 		this->buffer_listener.release = buffer_release;
77 	}
78 
~Buffer()79 	virtual ~Buffer()
80 	{
81 		wl_buffer_destroy(this->wl_buffer);
82 		munmap(this->data, this->data_size);
83 	}
84 
create_shm_buffer(int width,int height,uint32_t format)85 	static Buffer * create_shm_buffer(int width,
86 					  int height,
87 					  uint32_t format)
88 	{
89 		struct wl_shm_pool *pool;
90 		int fd, size, stride;
91 		void *data;
92 		struct wl_buffer *wl_buffer;
93 
94 		stride = width * 4;
95 		size = stride * height;
96 
97 		fd = os_create_anonymous_file(size);
98 		if (fd < 0) {
99 			cerr << "Creating a buffer file for " << size <<
100 				" B failed: " << strerror(errno) << endl;
101 			return NULL;
102 		}
103 
104 		data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
105 		if (data == MAP_FAILED) {
106 			cerr << "mmap failed: " << strerror(errno) << endl;
107 			close(fd);
108 			return NULL;
109 		}
110 
111 		pool = wl_shm_create_pool(wl_shm, fd, size);
112 
113 
114 		wl_buffer = wl_shm_pool_create_buffer(pool, 0,
115 						      width, height,
116 						      stride, format);
117 		wl_shm_pool_destroy(pool);
118 		close(fd);
119 
120 		return new Buffer(wl_buffer, width, height, data, size);
121 	}
122 
paint_buffer(enum libdecor_window_state window_state)123 	void paint_buffer(enum libdecor_window_state window_state)
124 	{
125 		uint32_t *pixels = reinterpret_cast<uint32_t *>(this->data);
126 		uint32_t color;
127 		int y, x;
128 		size_t off;
129 
130 		if (window_state & LIBDECOR_WINDOW_STATE_ACTIVE) {
131 			color = 0xffbcbcbc;
132 		} else {
133 			color = 0xff8e8e8e;
134 		}
135 
136 		for (y = 0; y < this->height; y++) {
137 			for (x = 0; x < this->width; x++) {
138 				off = x + y * this->width;
139 				pixels[off] = color;
140 			}
141 		}
142 	}
143 
get_buffer()144 	struct wl_buffer * get_buffer()
145 	{
146 		return this->wl_buffer;
147 	}
148 
149 private:
buffer_release(void * user_data,struct wl_buffer * wl_buffer)150 	static void buffer_release(void *user_data,
151 				   struct wl_buffer *wl_buffer)
152 	{
153 		Buffer *buffer = reinterpret_cast<Buffer *>(user_data);
154 
155 		delete buffer;
156 	}
157 
158 	struct wl_buffer *wl_buffer;
159 	struct wl_buffer_listener buffer_listener;
160 	int width;
161 	int height;
162 	void *data;
163 	size_t data_size;
164 };
165 
166 class Window {
167 public:
Window(struct libdecor * context,struct wl_compositor * wl_compositor)168 	Window(struct libdecor *context,
169 	       struct wl_compositor *wl_compositor)
170 	{
171 		this->wl_surface = wl_compositor_create_surface(wl_compositor);
172 
173 		this->libdecor_frame_iface.configure = handle_configure;
174 		this->libdecor_frame_iface.close = handle_close;
175 		this->libdecor_frame_iface.commit = handle_commit;
176 		this->libdecor_frame_iface.dismiss_popup = handle_dismiss_popup;
177 
178 		this->frame = libdecor_decorate(context, this->wl_surface,
179 						&libdecor_frame_iface, this);
180 		libdecor_frame_set_app_id(this->frame, "libdecor-c++-demo");
181 		libdecor_frame_set_title(this->frame, "libdecor C++ demo");
182 		libdecor_frame_map(this->frame);
183  	}
184 
redraw()185 	void redraw()
186 	{
187 		Buffer *buffer;
188 
189 		buffer = Buffer::create_shm_buffer(this->configured_width,
190 						   this->configured_height,
191 						   WL_SHM_FORMAT_XRGB8888);
192 		buffer->paint_buffer(this->window_state);
193 
194 		wl_surface_attach(this->wl_surface, buffer->get_buffer(), 0, 0);
195 		wl_surface_damage_buffer(this->wl_surface, 0, 0,
196 					 this->configured_width,
197 					 this->configured_height);
198 		wl_surface_commit(this->wl_surface);
199 	}
200 
201 private:
configure(struct libdecor_frame * frame,struct libdecor_configuration * configuration)202 	void configure(struct libdecor_frame *frame,
203 		       struct libdecor_configuration *configuration)
204 	{
205 		int width = 0, height = 0;
206 		enum libdecor_window_state window_state;
207 		struct libdecor_state *state;
208 
209 		libdecor_configuration_get_content_size(configuration, frame,
210 							&width, &height);
211 
212 		width = (width == 0) ? DEFAULT_WIDTH : width;
213 		height = (height == 0) ? DEFAULT_HEIGHT : height;
214 
215 		this->configured_width = width;
216 		this->configured_height = height;
217 
218 		if (!libdecor_configuration_get_window_state(configuration,
219 							     &window_state))
220 			window_state = LIBDECOR_WINDOW_STATE_NONE;
221 
222 		this->window_state = window_state;
223 
224 		state = libdecor_state_new(width, height);
225 		libdecor_frame_commit(frame, state, configuration);
226 		libdecor_state_free(state);
227 
228 		this->redraw();
229 	}
230 
handle_configure(struct libdecor_frame * frame,struct libdecor_configuration * configuration,void * user_data)231 	static void handle_configure(struct libdecor_frame *frame,
232 				     struct libdecor_configuration *configuration,
233 				     void *user_data)
234 	{
235 		Window *window = reinterpret_cast<Window *>(user_data);
236 
237 		window->configure(frame, configuration);
238 	}
239 
handle_close(struct libdecor_frame * frame,void * user_data)240 	static void handle_close(struct libdecor_frame *frame,
241 				 void *user_data)
242 	{
243 		exit(EXIT_SUCCESS);
244 	}
245 
commit()246 	void commit()
247 	{
248 		wl_surface_commit(this->wl_surface);
249 	}
250 
handle_commit(struct libdecor_frame * frame,void * user_data)251 	static void handle_commit(struct libdecor_frame *frame,
252 			          void *user_data)
253 	{
254 		Window *window = reinterpret_cast<Window *>(user_data);
255 
256 		window->commit();
257 	}
258 
handle_dismiss_popup(struct libdecor_frame * frame,const char * seat_name,void * user_data)259 	static void handle_dismiss_popup(struct libdecor_frame *frame,
260 					 const char *seat_name,
261 					 void *user_data)
262 	{
263 	}
264 
265 	struct wl_surface *wl_surface;
266 	struct libdecor_frame *frame;
267 	struct libdecor_frame_interface libdecor_frame_iface;
268 
269 	Buffer *buffer;
270 
271 	int configured_width;
272 	int configured_height;
273 	enum libdecor_window_state window_state;
274 };
275 
276 static Window *window;
277 
278 static void
shm_format(void * data,struct wl_shm * wl_shm,uint32_t format)279 shm_format(void *data,
280 	   struct wl_shm *wl_shm,
281 	   uint32_t format)
282 {
283 	if (format == WL_SHM_FORMAT_XRGB8888)
284 		has_xrgb = true;
285 }
286 
287 static struct wl_shm_listener shm_listener = {
288 	shm_format
289 };
290 
291 static void
registry_handle_global(void * user_data,struct wl_registry * wl_registry,uint32_t id,const char * interface,uint32_t version)292 registry_handle_global(void *user_data,
293 		       struct wl_registry *wl_registry,
294 		       uint32_t id,
295 		       const char *interface,
296 		       uint32_t version)
297 {
298 	if (strcmp(interface, "wl_compositor") == 0) {
299 		if (version < 4) {
300 			cerr << "wl_compositor version >= 4 required" << endl;
301 			exit(EXIT_FAILURE);
302 		}
303 		wl_compositor =
304 			reinterpret_cast<struct wl_compositor *>(
305 				wl_registry_bind(wl_registry,
306 						 id, &wl_compositor_interface, 4));
307 	} else if (strcmp(interface, "wl_shm") == 0) {
308 		wl_shm = reinterpret_cast<struct wl_shm *>(
309 			wl_registry_bind(wl_registry,
310 					 id, &wl_shm_interface, 1));
311 		wl_shm_add_listener(wl_shm, &shm_listener, NULL);
312 	}
313 }
314 
315 static void
registry_handle_global_remove(void * data,struct wl_registry * registry,uint32_t name)316 registry_handle_global_remove(void *data, struct wl_registry *registry,
317 			      uint32_t name)
318 {
319 }
320 
321 static const struct wl_registry_listener registry_listener = {
322 	registry_handle_global,
323 	registry_handle_global_remove
324 };
325 
326 static void
handle_error(struct libdecor * context,enum libdecor_error error,const char * message)327 handle_error(struct libdecor *context,
328 	     enum libdecor_error error,
329 	     const char *message)
330 {
331 	cerr << "Caught error (" << error << "): " << message << endl;
332 	exit(EXIT_FAILURE);
333 }
334 
335 static struct libdecor_interface libdecor_iface = {
336 	.error = handle_error,
337 };
338 
339 int
main(int argc,char ** argv)340 main(int argc,
341      char **argv)
342 {
343 	struct wl_display *wl_display;
344 	struct wl_registry *wl_registry;
345 	struct libdecor *context;
346 
347 	wl_display = wl_display_connect(NULL);
348 	if (!wl_display) {
349 		cerr << "No Wayland connection" << endl;
350 		return EXIT_FAILURE;
351 	}
352 
353 	wl_registry = wl_display_get_registry(wl_display);
354 	wl_registry_add_listener(wl_registry,
355 				 &registry_listener,
356 				 NULL);
357 	wl_display_roundtrip(wl_display);
358 	wl_display_roundtrip(wl_display);
359 	if (!has_xrgb) {
360 		cerr << "No XRGB shm format" << endl;
361 		return EXIT_FAILURE;
362 	}
363 
364 	context = libdecor_new(wl_display, &libdecor_iface);
365 	window = new Window(context, wl_compositor);
366 
367 	while (libdecor_dispatch(context, -1) >= 0);
368 
369 	delete window;
370 
371 	return EXIT_SUCCESS;
372 }
373