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 ®istry_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