Add a window client, first implementation of surface resizing.
[profile/ivi/wayland.git] / window.c
1 #include <stdint.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <i915_drm.h>
6 #include <sys/ioctl.h>
7 #include <sys/poll.h>
8 #include <fcntl.h>
9 #include <unistd.h>
10 #include <math.h>
11 #include <time.h>
12 #include <cairo.h>
13
14 #include "wayland-client.h"
15
16 static const char gem_device[] = "/dev/dri/card0";
17 static const char socket_name[] = "\0wayland";
18
19 static void
20 unpremultiply_data(uint8_t *data, int width, int height, int stride)
21 {
22         unsigned int i, j;
23         uint8_t *row;
24
25         for (j = 0; j < height; j++) {
26                 row = data + j * stride;
27
28                 for (i = 0; i < width; i++) {
29                         uint8_t *b = &row[i * 4];
30                         uint32_t pixel;
31                         uint8_t  alpha;
32
33                         memcpy (&pixel, b, sizeof (uint32_t));
34                         alpha = (pixel & 0xff000000) >> 24;
35                         if (alpha == 0) {
36                                 b[0] = b[1] = b[2] = b[3] = 0;
37                         } else {
38                                 b[0] = (((pixel & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
39                                 b[1] = (((pixel & 0x00ff00) >>  8) * 255 + alpha / 2) / alpha;
40                                 b[2] = (((pixel & 0x0000ff) >>  0) * 255 + alpha / 2) / alpha;
41                                 b[3] = alpha;
42                         }
43                 }
44         }
45 }
46
47 static uint32_t name_cairo_surface(int fd, cairo_surface_t *surface)
48 {
49         struct drm_i915_gem_create create;
50         struct drm_gem_flink flink;
51         struct drm_i915_gem_pwrite pwrite;
52         int32_t width, height, stride;
53         void *data;
54
55         width = cairo_image_surface_get_width(surface);
56         height = cairo_image_surface_get_height(surface);
57         stride = cairo_image_surface_get_stride(surface);
58         data = cairo_image_surface_get_data(surface);
59
60         unpremultiply_data(data, width, height, stride);
61
62         memset(&create, 0, sizeof(create));
63         create.size = height * stride;
64
65         if (ioctl(fd, DRM_IOCTL_I915_GEM_CREATE, &create) != 0) {
66                 fprintf(stderr, "gem create failed: %m\n");
67                 return 0;
68         }
69
70         pwrite.handle = create.handle;
71         pwrite.offset = 0;
72         pwrite.size = height * stride;
73         pwrite.data_ptr = (uint64_t) (uintptr_t) data;
74         if (ioctl(fd, DRM_IOCTL_I915_GEM_PWRITE, &pwrite) < 0) {
75                 fprintf(stderr, "gem pwrite failed: %m\n");
76                 return 0;
77         }
78
79         flink.handle = create.handle;
80         if (ioctl(fd, DRM_IOCTL_GEM_FLINK, &flink) != 0) {
81                 fprintf(stderr, "gem flink failed: %m\n");
82                 return 0;
83         }
84
85 #if 0
86         /* We need to hold on to the handle until the server has received
87          * the attach request... we probably need a confirmation event.
88          * I guess the breadcrumb idea will suffice. */
89         struct drm_gem_close close;
90         close.handle = create.handle;
91         if (ioctl(fd, DRM_IOCTL_GEM_CLOSE, &close) < 0) {
92                 fprintf(stderr, "gem close failed: %m\n");
93                 return 0;
94         }
95 #endif
96
97         return flink.name;
98 }
99
100 struct window {
101         struct wl_surface *surface;
102         int x, y, width, height, stride;
103         int drag_x, drag_y, last_x, last_y;
104         int state;
105         uint32_t name;
106         int fd;
107 };
108
109 static void *
110 draw_window(struct window *window)
111 {
112         cairo_surface_t *surface;
113         cairo_t *cr;
114         int border = 4;
115         int half = (border + 1) / 2;
116
117         surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24,
118                                              window->width,
119                                              window->height);
120
121         cr = cairo_create(surface);
122         cairo_set_line_width (cr, border);
123         cairo_move_to(cr, half, half);
124         cairo_line_to(cr, window->width - half, half);
125         cairo_line_to(cr, window->width - half,
126                       window->height - half);
127         cairo_line_to(cr, half, window->height - half);
128         cairo_close_path(cr);
129         cairo_set_source_rgba(cr, 0, 0, 0, 0.85);
130         cairo_fill_preserve(cr);
131         cairo_set_source_rgba(cr, 0, 0, 0, 1);
132         cairo_stroke(cr);
133         cairo_destroy(cr);
134
135         window->stride = cairo_image_surface_get_stride(surface);
136
137         window->name = name_cairo_surface(window->fd, surface);
138         cairo_surface_destroy(surface);
139
140         wl_surface_attach(window->surface, window->name,
141                           window->width, window->height, window->stride);
142                           
143         wl_surface_map(window->surface, 
144                        window->x, window->y,
145                        window->width, window->height);
146
147         return surface;
148 }
149
150 static int
151 connection_update(struct wl_connection *connection,
152                   uint32_t mask, void *data)
153 {
154         struct pollfd *p = data;
155
156         p->events = 0;
157         if (mask & WL_CONNECTION_READABLE)
158                 p->events |= POLLIN;
159         if (mask & WL_CONNECTION_WRITABLE)
160                 p->events |= POLLOUT;
161
162         return 0;
163 }
164
165 enum window_state {
166         WINDOW_STABLE,
167         WINDOW_MOVING,
168         WINDOW_RESIZING_UPPER_LEFT,
169         WINDOW_RESIZING_UPPER_RIGHT,
170         WINDOW_RESIZING_LOWER_LEFT,
171         WINDOW_RESIZING_LOWER_RIGHT
172 };
173
174 enum location {
175         LOCATION_INTERIOR,
176         LOCATION_UPPER_LEFT,
177         LOCATION_UPPER_RIGHT,
178         LOCATION_LOWER_LEFT,
179         LOCATION_LOWER_RIGHT,
180         LOCATION_OUTSIDE
181 };
182
183 void event_handler(struct wl_display *display,
184                    uint32_t opcode,
185                    uint32_t arg1, uint32_t arg2, void *data)
186 {
187         struct window *window = data;
188         int location, border = 4;
189         int grip_size = 16;
190
191         if (opcode == 0) {
192                 window->last_x = arg1;
193                 window->last_y = arg2;
194                 switch (window->state) {
195                 case WINDOW_MOVING:
196                         window->x = window->drag_x + arg1;
197                         window->y = window->drag_y + arg2;
198                         wl_surface_map(window->surface, window->x, window->y,
199                                        window->width, window->height);
200                         break;
201                 case WINDOW_RESIZING_LOWER_RIGHT:
202                         window->width = window->drag_x + arg1;
203                         window->height = window->drag_y + arg2;
204                         draw_window(window);
205                         break;
206                 }
207         }
208
209         if (window->x + border <= window->last_x &&
210             window->last_x < window->x + window->width - border &&
211             window->y + border <= window->last_y &&
212             window->last_y < window->y + window->height - border) {
213                 location = LOCATION_INTERIOR;
214         } else if (window->x + window->width - grip_size <= window->last_x &&
215                    window->last_x < window->x + window->width &&
216                    window->y + window->height - grip_size <= window->last_y &&
217                    window->last_y < window->y + window->height) {
218                 location = LOCATION_LOWER_RIGHT;
219         } else {
220                 location = LOCATION_OUTSIDE;
221         }
222
223         if (opcode == 1 && arg1 == 0 && arg2 == 1) {
224                 switch (location) {
225                 case LOCATION_INTERIOR:
226                         window->drag_x = window->x - window->last_x;
227                         window->drag_y = window->y - window->last_y;
228                         window->state = WINDOW_MOVING;
229                         break;
230                 case LOCATION_LOWER_RIGHT:
231                         window->drag_x = window->width - window->last_x;
232                         window->drag_y = window->height - window->last_y;
233                         window->state = WINDOW_RESIZING_LOWER_RIGHT;
234                         break;
235                 default:
236                         window->state = WINDOW_STABLE;
237                         break;
238                 }
239         } else if (opcode == 1 && arg1 == 0 && arg2 == 0) {
240                 window->state = WINDOW_STABLE;
241         }
242 }
243
244 int main(int argc, char *argv[])
245 {
246         struct wl_display *display;
247         int fd, ret;
248         uint32_t mask;
249         cairo_surface_t *s;
250         struct pollfd p[1];
251         struct window window;
252
253         fd = open(gem_device, O_RDWR);
254         if (fd < 0) {
255                 fprintf(stderr, "drm open failed: %m\n");
256                 return -1;
257         }
258
259         display = wl_display_create(socket_name,
260                                     connection_update, &p[0]);
261         if (display == NULL) {
262                 fprintf(stderr, "failed to create display: %m\n");
263                 return -1;
264         }
265         p[0].fd = wl_display_get_fd(display);
266
267         window.surface = wl_display_create_surface(display);
268         window.x = 200;
269         window.y = 200;
270         window.width = 350;
271         window.height = 200;
272         window.state = WINDOW_STABLE;
273         window.fd = fd;
274
275         s = draw_window(&window);
276
277         wl_display_set_event_handler(display, event_handler, &window);
278
279         while (ret = poll(p, 1, -1), ret >= 0) {
280                 mask = 0;
281                 if (p[0].revents & POLLIN)
282                         mask |= WL_CONNECTION_READABLE;
283                 if (p[0].revents & POLLOUT)
284                         mask |= WL_CONNECTION_WRITABLE;
285                 if (mask)
286                         wl_display_iterate(display, mask);
287         }
288
289         return 0;
290 }