window.c 128 KB
Newer Older
1
2
/*
 * Copyright © 2008 Kristian Høgsberg
3
 * Copyright © 2012-2013 Collabora, Ltd.
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that copyright
 * notice and this permission notice appear in supporting documentation, and
 * that the name of the copyright holders not be used in advertising or
 * publicity pertaining to distribution of the software without specific,
 * written prior permission.  The copyright holders make no representations
 * about the suitability of this software for any purpose.  It is provided "as
 * is" without express or implied warranty.
 *
 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
 * OF THIS SOFTWARE.
 */

24
#include "config.h"
25

26
27
28
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
29
#include <stdarg.h>
30
31
32
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
33
#include <errno.h>
34
#include <math.h>
35
#include <assert.h>
36
37
#include <time.h>
#include <cairo.h>
38
#include <sys/mman.h>
39
#include <sys/epoll.h>
40
#include <sys/timerfd.h>
Kristian Høgsberg's avatar
Kristian Høgsberg committed
41

42
#ifdef HAVE_CAIRO_EGL
43
44
#include <wayland-egl.h>

45
46
47
48
#ifdef USE_CAIRO_GLESV2
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#else
Kristian Høgsberg's avatar
Kristian Høgsberg committed
49
#include <GL/gl.h>
50
#endif
Kristian Høgsberg's avatar
Kristian Høgsberg committed
51
52
#include <EGL/egl.h>
#include <EGL/eglext.h>
53

Kristian Høgsberg's avatar
Kristian Høgsberg committed
54
#include <cairo-gl.h>
55
56
57
58
59
60
#else /* HAVE_CAIRO_EGL */
typedef void *EGLDisplay;
typedef void *EGLConfig;
typedef void *EGLContext;
#define EGL_NO_DISPLAY ((EGLDisplay)0)
#endif /* no HAVE_CAIRO_EGL */
61

62
#include <xkbcommon/xkbcommon.h>
63
#include <wayland-cursor.h>
64

65
#include <linux/input.h>
66
#include <wayland-client.h>
67
#include "../shared/cairo-util.h"
68
#include "text-cursor-position-client-protocol.h"
69
#include "workspaces-client-protocol.h"
70
#include "../shared/os-compatibility.h"
71

72
#include "window.h"
73

74
struct shm_pool;
75

76
77
78
79
80
81
82
struct global {
	uint32_t name;
	char *interface;
	uint32_t version;
	struct wl_list link;
};

83
struct display {
84
	struct wl_display *display;
85
	struct wl_registry *registry;
86
	struct wl_compositor *compositor;
Pekka Paalanen's avatar
Pekka Paalanen committed
87
	struct wl_subcompositor *subcompositor;
88
	struct wl_shell *shell;
89
	struct wl_shm *shm;
90
	struct wl_data_device_manager *data_device_manager;
91
	struct text_cursor_position *text_cursor_position;
92
	struct workspace_manager *workspace_manager;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
93
	EGLDisplay dpy;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
94
	EGLConfig argb_config;
95
96
	EGLContext argb_ctx;
	cairo_device_t *argb_device;
97
	uint32_t serial;
98
99

	int display_fd;
100
	uint32_t display_fd_events;
101
102
103
104
105
	struct task display_task;

	int epoll_fd;
	struct wl_list deferred_list;

Pekka Paalanen's avatar
Pekka Paalanen committed
106
107
	int running;

108
	struct wl_list global_list;
109
	struct wl_list window_list;
110
	struct wl_list input_list;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
111
	struct wl_list output_list;
112

113
	struct theme *theme;
114

115
	struct wl_cursor_theme *cursor_theme;
116
	struct wl_cursor **cursors;
117

118
	display_output_handler_t output_configure_handler;
119
	display_global_handler_t global_handler;
120
	display_global_handler_t global_handler_remove;
121
122

	void *user_data;
123
124

	struct xkb_context *xkb_context;
125
126
127

	uint32_t workspace;
	uint32_t workspace_count;
128
129
130
131

	/* A hack to get text extents for tooltips */
	cairo_surface_t *dummy_surface;
	void *dummy_surface_data;
132
133

	int has_rgb565;
134
	int seat_version;
135
136
};

137
enum {
138
	TYPE_NONE,
139
	TYPE_TOPLEVEL,
140
	TYPE_FULLSCREEN,
141
	TYPE_MAXIMIZED,
142
	TYPE_TRANSIENT,
143
	TYPE_MENU,
144
145
	TYPE_CUSTOM
};
146
147
148
149
150
151

struct window_output {
	struct output *output;
	struct wl_list link;
};

152
153
154
155
156
struct toysurface {
	/*
	 * Prepare the surface for drawing. Makes sure there is a surface
	 * of the right size available for rendering, and returns it.
	 * dx,dy are the x,y of wl_surface.attach.
157
	 * width,height are the new buffer size.
158
159
	 * If flags has SURFACE_HINT_RESIZE set, the user is
	 * doing continuous resizing.
160
161
162
	 * Returns the Cairo surface to draw to.
	 */
	cairo_surface_t *(*prepare)(struct toysurface *base, int dx, int dy,
163
				    int32_t width, int32_t height, uint32_t flags,
164
				    enum wl_output_transform buffer_transform, int32_t buffer_scale);
165
166
167
168
169
170
171

	/*
	 * Post the surface to the server, returning the server allocation
	 * rectangle. The Cairo surface from prepare() must be destroyed
	 * after calling this.
	 */
	void (*swap)(struct toysurface *base,
172
		     enum wl_output_transform buffer_transform, int32_t buffer_scale,
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
		     struct rectangle *server_allocation);

	/*
	 * Make the toysurface current with the given EGL context.
	 * Returns 0 on success, and negative of failure.
	 */
	int (*acquire)(struct toysurface *base, EGLContext ctx);

	/*
	 * Release the toysurface from the EGL context, returning control
	 * to Cairo.
	 */
	void (*release)(struct toysurface *base);

	/*
	 * Destroy the toysurface, including the Cairo surface, any
	 * backing storage, and the Wayland protocol objects.
	 */
	void (*destroy)(struct toysurface *base);
};

194
195
196
197
struct surface {
	struct window *window;

	struct wl_surface *surface;
Pekka Paalanen's avatar
Pekka Paalanen committed
198
199
200
	struct wl_subsurface *subsurface;
	int synchronized;
	int synchronized_default;
201
	struct toysurface *toysurface;
202
	struct widget *widget;
203
204
	int redraw_needed;
	struct wl_callback *frame_cb;
Pekka Paalanen's avatar
Pekka Paalanen committed
205
	uint32_t last_time;
206
207
208

	struct rectangle allocation;
	struct rectangle server_allocation;
209

210
211
212
	struct wl_region *input_region;
	struct wl_region *opaque_region;

213
214
	enum window_buffer_type buffer_type;
	enum wl_output_transform buffer_transform;
215
	int32_t buffer_scale;
216
217

	cairo_surface_t *cairo_surface;
Pekka Paalanen's avatar
Pekka Paalanen committed
218
219

	struct wl_list link;
220
221
};

222
223
struct window {
	struct display *display;
224
	struct wl_list window_output_list;
225
	char *title;
226
	struct rectangle saved_allocation;
227
	struct rectangle min_allocation;
228
	struct rectangle pending_allocation;
229
	int x, y;
230
	int resize_edges;
231
	int redraw_needed;
232
	int redraw_task_scheduled;
233
	struct task redraw_task;
234
	int resize_needed;
235
	int saved_type;
236
	int type;
237
238
	int focus_count;

239
	int resizing;
240
	int fullscreen_method;
241
	int configure_requests;
242

243
244
	enum preferred_format preferred_format;

245
	window_key_handler_t key_handler;
246
	window_keyboard_focus_handler_t keyboard_focus_handler;
247
248
	window_data_handler_t data_handler;
	window_drop_handler_t drop_handler;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
249
	window_close_handler_t close_handler;
250
	window_fullscreen_handler_t fullscreen_handler;
251
	window_output_handler_t output_handler;
252

253
254
	struct surface *main_surface;
	struct wl_shell_surface *shell_surface;
255

256
	struct window_frame *frame;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
257

Pekka Paalanen's avatar
Pekka Paalanen committed
258
259
260
	/* struct surface::link, contains also main_surface */
	struct wl_list subsurface_list;

261
	void *user_data;
262
	struct wl_list link;
263
264
};

265
struct widget {
266
	struct window *window;
267
	struct surface *surface;
268
	struct tooltip *tooltip;
269
	struct wl_list child_list;
270
271
	struct wl_list link;
	struct rectangle allocation;
272
273
	widget_resize_handler_t resize_handler;
	widget_redraw_handler_t redraw_handler;
274
275
	widget_enter_handler_t enter_handler;
	widget_leave_handler_t leave_handler;
276
	widget_motion_handler_t motion_handler;
277
	widget_button_handler_t button_handler;
Rusty Lynch's avatar
Rusty Lynch committed
278
279
280
281
282
	widget_touch_down_handler_t touch_down_handler;
	widget_touch_up_handler_t touch_up_handler;
	widget_touch_motion_handler_t touch_motion_handler;
	widget_touch_frame_handler_t touch_frame_handler;
	widget_touch_cancel_handler_t touch_cancel_handler;
283
	widget_axis_handler_t axis_handler;
284
	void *user_data;
285
	int opaque;
286
	int tooltip_count;
287
	int default_cursor;
288
289
290
291
292
	/* If this is set to false then no cairo surface will be
	 * created before redrawing the surface. This is useful if the
	 * redraw handler is going to do completely custom rendering
	 * such as using EGL directly */
	int use_cairo;
293
294
};

Rusty Lynch's avatar
Rusty Lynch committed
295
296
struct touch_point {
	int32_t id;
297
	float x, y;
Rusty Lynch's avatar
Rusty Lynch committed
298
299
300
301
	struct widget *widget;
	struct wl_list link;
};

302
303
struct input {
	struct display *display;
304
305
306
	struct wl_seat *seat;
	struct wl_pointer *pointer;
	struct wl_keyboard *keyboard;
Rusty Lynch's avatar
Rusty Lynch committed
307
308
	struct wl_touch *touch;
	struct wl_list touch_point_list;
309
310
	struct window *pointer_focus;
	struct window *keyboard_focus;
Rusty Lynch's avatar
Rusty Lynch committed
311
	struct window *touch_focus;
312
	int current_cursor;
313
314
	uint32_t cursor_anim_start;
	struct wl_callback *cursor_frame_cb;
315
	struct wl_surface *pointer_surface;
316
	uint32_t modifiers;
317
	uint32_t pointer_enter_serial;
318
	uint32_t cursor_serial;
319
	float sx, sy;
320
	struct wl_list link;
321

322
	struct widget *focus_widget;
323
324
325
	struct widget *grab;
	uint32_t grab_button;

326
327
328
	struct wl_data_device *data_device;
	struct data_offer *drag_offer;
	struct data_offer *selection_offer;
329
330
331
332
333
	uint32_t touch_grab;
	int32_t touch_grab_id;
	float drag_x, drag_y;
	struct window *drag_focus;
	uint32_t drag_enter_serial;
334
335
336
337
338
339
340
341

	struct {
		struct xkb_keymap *keymap;
		struct xkb_state *state;
		xkb_mod_mask_t control_mask;
		xkb_mod_mask_t alt_mask;
		xkb_mod_mask_t shift_mask;
	} xkb;
342
343
344
345
346
347

	struct task repeat_task;
	int repeat_timer_fd;
	uint32_t repeat_sym;
	uint32_t repeat_key;
	uint32_t repeat_time;
348
349
};

Kristian Høgsberg's avatar
Kristian Høgsberg committed
350
351
352
struct output {
	struct display *display;
	struct wl_output *output;
353
	uint32_t server_output_id;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
354
355
	struct rectangle allocation;
	struct wl_list link;
356
	int transform;
357
	int scale;
358
359
360

	display_output_handler_t destroy_handler;
	void *user_data;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
361
362
};

363
struct window_frame {
364
365
	struct widget *widget;
	struct widget *child;
366
	struct frame *frame;
367
368
};

Kristian Høgsberg's avatar
Kristian Høgsberg committed
369
370
struct menu {
	struct window *window;
371
	struct window *parent;
372
	struct widget *widget;
373
	struct input *input;
374
	struct frame *frame;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
375
376
377
378
	const char **entries;
	uint32_t time;
	int current;
	int count;
379
	int release_count;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
380
381
382
	menu_func_t func;
};

383
384
385
386
387
388
389
390
391
392
struct tooltip {
	struct widget *parent;
	struct window *window;
	struct widget *widget;
	char *entry;
	struct task tooltip_task;
	int tooltip_fd;
	float x, y;
};

393
394
395
396
397
398
399
struct shm_pool {
	struct wl_shm_pool *pool;
	size_t size;
	size_t used;
	void *data;
};

400
enum {
401
402
	CURSOR_DEFAULT = 100,
	CURSOR_UNSET
403
404
};

405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
enum window_location {
	WINDOW_INTERIOR = 0,
	WINDOW_RESIZING_TOP = 1,
	WINDOW_RESIZING_BOTTOM = 2,
	WINDOW_RESIZING_LEFT = 4,
	WINDOW_RESIZING_TOP_LEFT = 5,
	WINDOW_RESIZING_BOTTOM_LEFT = 6,
	WINDOW_RESIZING_RIGHT = 8,
	WINDOW_RESIZING_TOP_RIGHT = 9,
	WINDOW_RESIZING_BOTTOM_RIGHT = 10,
	WINDOW_RESIZING_MASK = 15,
	WINDOW_EXTERIOR = 16,
	WINDOW_TITLEBAR = 17,
	WINDOW_CLIENT_AREA = 18,
};

421
static const cairo_user_data_key_t shm_surface_data_key;
422

423
424
425
/* #define DEBUG */

#ifdef DEBUG
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465

static void
debug_print(void *proxy, int line, const char *func, const char *fmt, ...)
__attribute__ ((format (printf, 4, 5)));

static void
debug_print(void *proxy, int line, const char *func, const char *fmt, ...)
{
	va_list ap;
	struct timeval tv;

	gettimeofday(&tv, NULL);
	fprintf(stderr, "%8ld.%03ld ",
		(long)tv.tv_sec & 0xffff, (long)tv.tv_usec / 1000);

	if (proxy)
		fprintf(stderr, "%s@%d ",
			wl_proxy_get_class(proxy), wl_proxy_get_id(proxy));

	/*fprintf(stderr, __FILE__ ":%d:%s ", line, func);*/
	fprintf(stderr, "%s ", func);

	va_start(ap, fmt);
	vfprintf(stderr, fmt, ap);
	va_end(ap);
}

#define DBG(fmt, ...) \
	debug_print(NULL, __LINE__, __func__, fmt, ##__VA_ARGS__)

#define DBG_OBJ(obj, fmt, ...) \
	debug_print(obj, __LINE__, __func__, fmt, ##__VA_ARGS__)

#else

#define DBG(...) do {} while (0)
#define DBG_OBJ(...) do {} while (0)

#endif

466
static void
467
surface_to_buffer_size (enum wl_output_transform buffer_transform, int32_t buffer_scale, int32_t *width, int32_t *height)
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
{
	int32_t tmp;

	switch (buffer_transform) {
	case WL_OUTPUT_TRANSFORM_90:
	case WL_OUTPUT_TRANSFORM_270:
	case WL_OUTPUT_TRANSFORM_FLIPPED_90:
	case WL_OUTPUT_TRANSFORM_FLIPPED_270:
		tmp = *width;
		*width = *height;
		*height = tmp;
		break;
	default:
		break;
	}

	*width *= buffer_scale;
	*height *= buffer_scale;
}

static void
489
buffer_to_surface_size (enum wl_output_transform buffer_transform, int32_t buffer_scale, int32_t *width, int32_t *height)
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
{
	int32_t tmp;

	switch (buffer_transform) {
	case WL_OUTPUT_TRANSFORM_90:
	case WL_OUTPUT_TRANSFORM_270:
	case WL_OUTPUT_TRANSFORM_FLIPPED_90:
	case WL_OUTPUT_TRANSFORM_FLIPPED_270:
		tmp = *width;
		*width = *height;
		*height = tmp;
		break;
	default:
		break;
	}

	*width /= buffer_scale;
	*height /= buffer_scale;
}

510
#ifdef HAVE_CAIRO_EGL
511

512
513
514
struct egl_window_surface {
	struct toysurface base;
	cairo_surface_t *cairo_surface;
515
516
	struct display *display;
	struct wl_surface *surface;
517
518
	struct wl_egl_window *egl_window;
	EGLSurface egl_surface;
519
520
};

521
522
523
524
525
526
527
528
static struct egl_window_surface *
to_egl_window_surface(struct toysurface *base)
{
	return container_of(base, struct egl_window_surface, base);
}

static cairo_surface_t *
egl_window_surface_prepare(struct toysurface *base, int dx, int dy,
529
			   int32_t width, int32_t height, uint32_t flags,
530
			   enum wl_output_transform buffer_transform, int32_t buffer_scale)
531
532
533
{
	struct egl_window_surface *surface = to_egl_window_surface(base);

534
535
	surface_to_buffer_size (buffer_transform, buffer_scale, &width, &height);

536
537
538
539
540
541
	wl_egl_window_resize(surface->egl_window, width, height, dx, dy);
	cairo_gl_surface_set_size(surface->cairo_surface, width, height);

	return cairo_surface_reference(surface->cairo_surface);
}

542
static void
543
egl_window_surface_swap(struct toysurface *base,
544
			enum wl_output_transform buffer_transform, int32_t buffer_scale,
545
546
547
548
549
550
551
552
			struct rectangle *server_allocation)
{
	struct egl_window_surface *surface = to_egl_window_surface(base);

	cairo_gl_surface_swapbuffers(surface->cairo_surface);
	wl_egl_window_get_attached_size(surface->egl_window,
					&server_allocation->width,
					&server_allocation->height);
553
554
555
556

	buffer_to_surface_size (buffer_transform, buffer_scale,
				&server_allocation->width,
				&server_allocation->height);
557
558
559
560
}

static int
egl_window_surface_acquire(struct toysurface *base, EGLContext ctx)
561
{
562
563
	struct egl_window_surface *surface = to_egl_window_surface(base);
	cairo_device_t *device;
564

565
566
567
	device = cairo_surface_get_device(surface->cairo_surface);
	if (!device)
		return -1;
568

569
570
571
572
573
574
575
576
577
578
579
580
581
582
	if (!ctx) {
		if (device == surface->display->argb_device)
			ctx = surface->display->argb_ctx;
		else
			assert(0);
	}

	cairo_device_flush(device);
	cairo_device_acquire(device);
	if (!eglMakeCurrent(surface->display->dpy, surface->egl_surface,
			    surface->egl_surface, ctx))
		fprintf(stderr, "failed to make surface current\n");

	return 0;
583
584
}

585
586
static void
egl_window_surface_release(struct toysurface *base)
587
{
588
	struct egl_window_surface *surface = to_egl_window_surface(base);
589
	cairo_device_t *device;
590

591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
	device = cairo_surface_get_device(surface->cairo_surface);
	if (!device)
		return;

	if (!eglMakeCurrent(surface->display->dpy, NULL, NULL,
			    surface->display->argb_ctx))
		fprintf(stderr, "failed to make context current\n");

	cairo_device_release(device);
}

static void
egl_window_surface_destroy(struct toysurface *base)
{
	struct egl_window_surface *surface = to_egl_window_surface(base);
	struct display *d = surface->display;

	cairo_surface_destroy(surface->cairo_surface);
	eglDestroySurface(d->dpy, surface->egl_surface);
	wl_egl_window_destroy(surface->egl_window);
	surface->surface = NULL;

	free(surface);
}

static struct toysurface *
egl_window_surface_create(struct display *display,
			  struct wl_surface *wl_surface,
			  uint32_t flags,
			  struct rectangle *rectangle)
{
	struct egl_window_surface *surface;
623

624
625
626
	if (display->dpy == EGL_NO_DISPLAY)
		return NULL;

627
628
629
	surface = calloc(1, sizeof *surface);
	if (!surface)
		return NULL;
630

631
632
633
634
635
	surface->base.prepare = egl_window_surface_prepare;
	surface->base.swap = egl_window_surface_swap;
	surface->base.acquire = egl_window_surface_acquire;
	surface->base.release = egl_window_surface_release;
	surface->base.destroy = egl_window_surface_destroy;
636

637
638
	surface->display = display;
	surface->surface = wl_surface;
639

640
641
642
	surface->egl_window = wl_egl_window_create(surface->surface,
						   rectangle->width,
						   rectangle->height);
643

644
645
646
647
	surface->egl_surface = eglCreateWindowSurface(display->dpy,
						      display->argb_config,
						      surface->egl_window,
						      NULL);
648

649
650
651
652
653
	surface->cairo_surface =
		cairo_gl_surface_create_for_egl(display->argb_device,
						surface->egl_surface,
						rectangle->width,
						rectangle->height);
654

655
	return &surface->base;
656
657
}

658
659
660
661
662
663
664
665
666
667
668
#else

static struct toysurface *
egl_window_surface_create(struct display *display,
			  struct wl_surface *wl_surface,
			  uint32_t flags,
			  struct rectangle *rectangle)
{
	return NULL;
}

669
670
#endif

671
672
673
674
675
struct shm_surface_data {
	struct wl_buffer *buffer;
	struct shm_pool *pool;
};

676
677
678
679
struct wl_buffer *
display_get_buffer_for_surface(struct display *display,
			       cairo_surface_t *surface)
{
680
	struct shm_surface_data *data;
681

682
	data = cairo_surface_get_user_data(surface, &shm_surface_data_key);
683
684
685
686

	return data->buffer;
}

687
688
689
static void
shm_pool_destroy(struct shm_pool *pool);

690
static void
691
692
693
694
shm_surface_data_destroy(void *p)
{
	struct shm_surface_data *data = p;

695
	wl_buffer_destroy(data->buffer);
696
697
	if (data->pool)
		shm_pool_destroy(data->pool);
698
699

	free(data);
700
701
}

702
703
static struct wl_shm_pool *
make_shm_pool(struct display *display, int size, void **data)
704
{
705
706
	struct wl_shm_pool *pool;
	int fd;
707

708
	fd = os_create_anonymous_file(size);
709
	if (fd < 0) {
710
711
		fprintf(stderr, "creating a buffer file for %d B failed: %m\n",
			size);
712
713
714
		return NULL;
	}

715
716
	*data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
	if (*data == MAP_FAILED) {
717
		fprintf(stderr, "mmap failed: %m\n");
718
719
720
721
		close(fd);
		return NULL;
	}

722
723
724
725
726
727
728
	pool = wl_shm_create_pool(display->shm, fd, size);

	close(fd);

	return pool;
}

729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
static struct shm_pool *
shm_pool_create(struct display *display, size_t size)
{
	struct shm_pool *pool = malloc(sizeof *pool);

	if (!pool)
		return NULL;

	pool->pool = make_shm_pool(display, size, &pool->data);
	if (!pool->pool) {
		free(pool);
		return NULL;
	}

	pool->size = size;
	pool->used = 0;

	return pool;
}

static void *
shm_pool_allocate(struct shm_pool *pool, size_t size, int *offset)
{
	if (pool->used + size > pool->size)
		return NULL;

	*offset = pool->used;
	pool->used += size;

	return (char *) pool->data + *offset;
}

/* destroy the pool. this does not unmap the memory though */
static void
shm_pool_destroy(struct shm_pool *pool)
{
	munmap(pool->data, pool->size);
	wl_shm_pool_destroy(pool->pool);
	free(pool);
}

/* Start allocating from the beginning of the pool again */
static void
shm_pool_reset(struct shm_pool *pool)
{
	pool->used = 0;
}

static int
data_length_for_shm_surface(struct rectangle *rect)
{
	int stride;

	stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32,
						rect->width);
	return stride * rect->height;
}

787
static cairo_surface_t *
788
789
790
display_create_shm_surface_from_pool(struct display *display,
				     struct rectangle *rectangle,
				     uint32_t flags, struct shm_pool *pool)
791
792
793
794
{
	struct shm_surface_data *data;
	uint32_t format;
	cairo_surface_t *surface;
795
	cairo_format_t cairo_format;
796
	int stride, length, offset;
797
	void *map;
798
799
800
801
802

	data = malloc(sizeof *data);
	if (data == NULL)
		return NULL;

803
804
805
806
807
808
	if (flags & SURFACE_HINT_RGB565 && display->has_rgb565)
		cairo_format = CAIRO_FORMAT_RGB16_565;
	else
		cairo_format = CAIRO_FORMAT_ARGB32;

	stride = cairo_format_stride_for_width (cairo_format, rectangle->width);
809
810
811
812
813
814
815
	length = stride * rectangle->height;
	data->pool = NULL;
	map = shm_pool_allocate(pool, length, &offset);

	if (!map) {
		free(data);
		return NULL;
816
	}
817

818
	surface = cairo_image_surface_create_for_data (map,
819
						       cairo_format,
820
821
822
823
						       rectangle->width,
						       rectangle->height,
						       stride);

824
825
	cairo_surface_set_user_data(surface, &shm_surface_data_key,
				    data, shm_surface_data_destroy);
826

827
828
829
830
831
832
833
834
	if (flags & SURFACE_HINT_RGB565 && display->has_rgb565)
		format = WL_SHM_FORMAT_RGB565;
	else {
		if (flags & SURFACE_OPAQUE)
			format = WL_SHM_FORMAT_XRGB8888;
		else
			format = WL_SHM_FORMAT_ARGB8888;
	}
835

836
837
838
839
	data->buffer = wl_shm_pool_create_buffer(pool->pool, offset,
						 rectangle->width,
						 rectangle->height,
						 stride, format);
840

841
842
843
844
845
846
	return surface;
}

static cairo_surface_t *
display_create_shm_surface(struct display *display,
			   struct rectangle *rectangle, uint32_t flags,
847
848
			   struct shm_pool *alternate_pool,
			   struct shm_surface_data **data_ret)
849
850
851
852
853
{
	struct shm_surface_data *data;
	struct shm_pool *pool;
	cairo_surface_t *surface;

854
855
	if (alternate_pool) {
		shm_pool_reset(alternate_pool);
856
857
858
		surface = display_create_shm_surface_from_pool(display,
							       rectangle,
							       flags,
859
860
861
862
863
864
							       alternate_pool);
		if (surface) {
			data = cairo_surface_get_user_data(surface,
							   &shm_surface_data_key);
			goto out;
		}
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
	}

	pool = shm_pool_create(display,
			       data_length_for_shm_surface(rectangle));
	if (!pool)
		return NULL;

	surface =
		display_create_shm_surface_from_pool(display, rectangle,
						     flags, pool);

	if (!surface) {
		shm_pool_destroy(pool);
		return NULL;
	}

	/* make sure we destroy the pool when the surface is destroyed */
882
	data = cairo_surface_get_user_data(surface, &shm_surface_data_key);
883
	data->pool = pool;
884

885
886
887
888
out:
	if (data_ret)
		*data_ret = data;

889
890
891
	return surface;
}

Dylan Noblesmith's avatar
Dylan Noblesmith committed
892
893
894
895
896
897
898
899
900
901
902
static int
check_size(struct rectangle *rect)
{
	if (rect->width && rect->height)
		return 0;

	fprintf(stderr, "tried to create surface of "
		"width: %d, height: %d\n", rect->width, rect->height);
	return -1;
}

903
904
cairo_surface_t *
display_create_surface(struct display *display,
905
		       struct wl_surface *surface,
906
907
		       struct rectangle *rectangle,
		       uint32_t flags)
908
{
Dylan Noblesmith's avatar
Dylan Noblesmith committed
909
910
	if (check_size(rectangle) < 0)
		return NULL;
911
912

	assert(flags & SURFACE_SHM);
913
914
915
916
	return display_create_shm_surface(display, rectangle, flags,
					  NULL, NULL);
}

917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
struct shm_surface_leaf {
	cairo_surface_t *cairo_surface;
	/* 'data' is automatically destroyed, when 'cairo_surface' is */
	struct shm_surface_data *data;

	struct shm_pool *resize_pool;
	int busy;
};

static void
shm_surface_leaf_release(struct shm_surface_leaf *leaf)
{
	if (leaf->cairo_surface)
		cairo_surface_destroy(leaf->cairo_surface);
	/* leaf->data already destroyed via cairo private */

	if (leaf->resize_pool)
		shm_pool_destroy(leaf->resize_pool);
935
936

	memset(leaf, 0, sizeof *leaf);
937
938
}

939
940
#define MAX_LEAVES 3

941
942
943
944
945
946
947
struct shm_surface {
	struct toysurface base;
	struct display *display;
	struct wl_surface *surface;
	uint32_t flags;
	int dx, dy;

948
	struct shm_surface_leaf leaf[MAX_LEAVES];
949
	struct shm_surface_leaf *current;
950
951
952
953
954
955
956
957
};

static struct shm_surface *
to_shm_surface(struct toysurface *base)
{
	return container_of(base, struct shm_surface, base);
}

958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
static void
shm_surface_buffer_state_debug(struct shm_surface *surface, const char *msg)
{
#ifdef DEBUG
	struct shm_surface_leaf *leaf;
	char bufs[MAX_LEAVES + 1];
	int i;

	for (i = 0; i < MAX_LEAVES; i++) {
		leaf = &surface->leaf[i];

		if (leaf->busy)
			bufs[i] = 'b';
		else if (leaf->cairo_surface)
			bufs[i] = 'a';
		else
			bufs[i] = ' ';
	}

	bufs[MAX_LEAVES] = '\0';
	DBG_OBJ(surface->surface, "%s, leaves [%s]\n", msg, bufs);
#endif
}

982
983
984
static void
shm_surface_buffer_release(void *data, struct wl_buffer *buffer)
{
985
	struct shm_surface *surface = data;
986
987
988
	struct shm_surface_leaf *leaf;
	int i;
	int free_found;
989
990

	shm_surface_buffer_state_debug(surface, "buffer_release before");
991
992
993
994
995
996
997
998
999

	for (i = 0; i < MAX_LEAVES; i++) {
		leaf = &surface->leaf[i];
		if (leaf->data && leaf->data->buffer == buffer) {
			leaf->busy = 0;
			break;
		}
	}
	assert(i < MAX_LEAVES && "unknown buffer released");
1000

For faster browsing, not all history is shown. View entire blame