keyrouter: Fix wrong return value
[platform/core/uifw/libds-tizen.git] / src / libds / shell_surface.c
1 #include <assert.h>
2 #include <stdlib.h>
3 #include <string.h>
4
5 #include "libds/log.h"
6
7 #include "shell.h"
8
9 static const struct wl_shell_surface_interface shell_surface_impl;
10
11 static void reset_shell_surface(struct ds_shell_surface *shell_surface);
12 static void shell_surface_handle_surface_destroy(struct wl_listener *listener,
13         void *data);
14 static void shell_surface_handle_surface_commit(struct wl_listener *listener,
15         void *data);
16 static void shell_surface_handle_resource_destroy(struct wl_resource *resource);
17 static void shell_surface_configure_destroy(struct ds_shell_surface_configure *configure);
18 static void surface_send_configure(void *user_data);
19 static void handle_shell_surface_commit(struct ds_surface *surface);
20
21 WL_EXPORT void
22 ds_shell_surface_add_destroy_listener(struct ds_shell_surface *shell_surface,
23         struct wl_listener *listener)
24 {
25     wl_signal_add(&shell_surface->events.destroy, listener);
26 }
27
28 WL_EXPORT void
29 ds_shell_surface_add_map_listener(struct ds_shell_surface *shell_surface,
30         struct wl_listener *listener)
31 {
32     wl_signal_add(&shell_surface->events.map, listener);
33 }
34
35 WL_EXPORT void
36 ds_shell_surface_add_unmap_listener(struct ds_shell_surface *shell_surface,
37         struct wl_listener *listener)
38 {
39     wl_signal_add(&shell_surface->events.unmap, listener);
40 }
41
42 WL_EXPORT struct ds_surface *
43 ds_shell_surface_get_surface(struct ds_shell_surface *shell_surface)
44 {
45     return shell_surface->surface;
46 }
47
48 static const struct ds_surface_role shell_surface_role =
49 {
50     .name = "shell_surface",
51     .commit = handle_shell_surface_commit,
52 };
53
54 struct ds_shell_surface *
55 create_shell_surface(struct ds_shell_client *client, struct ds_surface *surface,
56         uint32_t id)
57 {
58     struct ds_shell_surface *shell_surface;
59
60     shell_surface = calloc(1, sizeof *shell_surface);
61     if (!shell_surface) {
62         wl_client_post_no_memory(client->wl_client);
63         return NULL;
64     }
65
66     shell_surface->client = client;
67     shell_surface->role = DS_SHELL_SURFACE_ROLE_NONE;
68     shell_surface->surface = surface;
69     shell_surface->resource = wl_resource_create(client->wl_client,
70             &wl_shell_surface_interface, wl_resource_get_version(client->resource),
71             id);
72     if (!shell_surface->resource) {
73         free(shell_surface);
74         wl_client_post_no_memory(client->wl_client);
75         return NULL;
76     }
77
78     if (!ds_surface_set_role(shell_surface->surface, &shell_surface_role,
79                 shell_surface, shell_surface->resource, -1)) {
80         free(shell_surface);
81         return NULL;
82     }
83
84     wl_list_init(&shell_surface->configure_list);
85
86     wl_signal_init(&shell_surface->events.destroy);
87     wl_signal_init(&shell_surface->events.ping_timeout);
88     wl_signal_init(&shell_surface->events.new_popup);
89     wl_signal_init(&shell_surface->events.map);
90     wl_signal_init(&shell_surface->events.unmap);
91     wl_signal_init(&shell_surface->events.configure);
92
93     shell_surface->listener.surface_destroy.notify =
94         shell_surface_handle_surface_destroy;
95     ds_surface_add_destroy_listener(surface,
96             &shell_surface->listener.surface_destroy);
97
98     shell_surface->listener.surface_commit.notify =
99         shell_surface_handle_surface_commit;
100     ds_surface_add_commit_listener(surface,
101             &shell_surface->listener.surface_commit);
102
103     wl_resource_set_implementation(shell_surface->resource, &shell_surface_impl,
104             shell_surface, shell_surface_handle_resource_destroy);
105
106     wl_list_insert(&client->shell_surfaces, &shell_surface->link);
107
108     ds_inf("New shell_surface %p (res %p)", shell_surface, shell_surface->resource);
109
110     return shell_surface;
111 }
112
113 void
114 destroy_shell_surface(struct ds_shell_surface *shell_surface)
115 {
116     reset_shell_surface(shell_surface);
117
118     wl_resource_set_user_data(shell_surface->resource, NULL);
119
120     ds_surface_reset_role_data(shell_surface->surface);
121
122     wl_list_remove(&shell_surface->link);
123     wl_list_remove(&shell_surface->listener.surface_destroy.link);
124     wl_list_remove(&shell_surface->listener.surface_commit.link);
125
126     free(shell_surface);
127 }
128
129 static void
130 unmap_shell_surface(struct ds_shell_surface *shell_surface)
131 {
132     struct ds_shell_surface_configure *configure, *tmp;
133
134     // TODO handle popup
135
136     if (shell_surface->mapped)
137         wl_signal_emit(&shell_surface->events.unmap, shell_surface);
138
139     switch (shell_surface->role) {
140         case DS_SHELL_SURFACE_ROLE_TOPLEVEL:
141             if (shell_surface->toplevel->parent) {
142                 wl_list_remove(&shell_surface->toplevel->parent_unmap.link);
143                 shell_surface->toplevel->parent = NULL;
144             }
145             free(shell_surface->toplevel->title);
146             shell_surface->toplevel->title = NULL;
147             free(shell_surface->toplevel->app_id);
148             shell_surface->toplevel->app_id = NULL;
149             break;
150         case DS_SHELL_SURFACE_ROLE_POPUP:
151             // TODO
152             break;
153         case DS_SHELL_SURFACE_ROLE_NONE:
154             assert(false && "not reached");
155     }
156
157     wl_list_for_each_safe(configure, tmp, &shell_surface->configure_list, link)
158         shell_surface_configure_destroy(configure);
159
160     if (shell_surface->configure_idle) {
161         wl_event_source_remove(shell_surface->configure_idle);
162         shell_surface->configure_idle = NULL;
163     }
164
165     shell_surface->configured = false;
166     shell_surface->mapped = false;
167 }
168
169 static void
170 reset_shell_surface(struct ds_shell_surface *shell_surface)
171 {
172     struct ds_shell_toplevel_requested *req;
173
174     if (shell_surface->role != DS_SHELL_SURFACE_ROLE_NONE)
175         unmap_shell_surface(shell_surface);
176
177     if (shell_surface->added) {
178         wl_signal_emit(&shell_surface->events.destroy, shell_surface);
179         shell_surface->added = false;
180     }
181
182     switch (shell_surface->role) {
183         case DS_SHELL_SURFACE_ROLE_TOPLEVEL:
184             req = &shell_surface->toplevel->requested;
185             if (req->fullscreen_output)
186                 wl_list_remove(&req->fullscreen_output_destroy.link);
187             free(shell_surface->toplevel);
188             shell_surface->toplevel = NULL;
189             break;
190         case DS_SHELL_SURFACE_ROLE_POPUP:
191             // TODO
192             break;
193         case DS_SHELL_SURFACE_ROLE_NONE:
194             // This space is intentionally left blank
195             break;
196     }
197
198     shell_surface->role = DS_SHELL_SURFACE_ROLE_NONE;
199 }
200
201 static uint32_t
202 ds_shell_surface_schedule_configure(struct ds_shell_surface *shell_surface)
203 {
204     struct wl_display *display;
205     struct wl_event_loop *loop;
206
207     display = wl_client_get_display(shell_surface->client->wl_client);
208     loop = wl_display_get_event_loop(display);
209
210     if (!shell_surface->configure_idle) {
211         shell_surface->scheduled_serial = wl_display_next_serial(display);
212         shell_surface->configure_idle = wl_event_loop_add_idle(loop,
213                 surface_send_configure, shell_surface);
214         if (!shell_surface->configure_idle)
215             wl_client_post_no_memory(shell_surface->client->wl_client);
216     }
217
218     return shell_surface->scheduled_serial;
219 }
220
221 static void
222 handle_shell_surface_commit(struct ds_surface *surface)
223 {
224     struct ds_shell_surface *shell_surface;
225
226     shell_surface = ds_surface_get_role_data(surface);
227     shell_surface->current = shell_surface->pending;
228
229     switch (shell_surface->role) {
230         case DS_SHELL_SURFACE_ROLE_NONE:
231             // inert toplevel or popup
232             break;
233         case DS_SHELL_SURFACE_ROLE_TOPLEVEL:
234             if (!shell_surface->toplevel->added) {
235                 ds_shell_surface_schedule_configure(shell_surface);
236                 shell_surface->toplevel->added = true;
237             }
238             // TODO
239             break;
240         case DS_SHELL_SURFACE_ROLE_POPUP:
241             // TODO
242             break;
243     }
244
245     if (!shell_surface->added) {
246         shell_surface->added = true;
247         wl_signal_emit(&shell_surface->client->shell->events.new_surface, shell_surface);
248     }
249
250     if (shell_surface->configured &&
251             ds_surface_has_buffer(shell_surface->surface) &&
252             !shell_surface->mapped) {
253         shell_surface->mapped = true;
254         wl_signal_emit(&shell_surface->events.map, shell_surface);
255     }
256 }
257
258 void handle_shell_surface_precommit(struct ds_surface *surface)
259 {
260     struct ds_shell_surface *shell_surface;
261
262     shell_surface = ds_surface_get_role_data(surface);
263
264     // TODO
265     (void)shell_surface;
266 }
267
268 void
269 ds_shell_surface_ping(struct ds_shell_surface *shell_surface)
270 {
271 }
272
273 static void
274 shell_surface_handle_surface_destroy(struct wl_listener *listener, void *data)
275 {
276     struct ds_shell_surface *shell_surface;
277
278     shell_surface = wl_container_of(listener, shell_surface, listener.surface_destroy);
279     destroy_shell_surface(shell_surface);
280 }
281
282 static void
283 shell_surface_handle_surface_commit(struct wl_listener *listener, void *data)
284 {
285     struct ds_shell_surface *shell_surface;
286
287     shell_surface = wl_container_of(listener, shell_surface, listener.surface_commit);
288
289     if (ds_surface_has_buffer(shell_surface->surface) &&
290             !shell_surface->configured) {
291         wl_resource_post_error(shell_surface->resource,
292                 -1,
293                 "shell_surface has never been configured");
294         return;
295     }
296
297     if (!ds_surface_get_role(shell_surface->surface)) {
298         wl_resource_post_error(shell_surface->resource,
299                 -1,
300                 "shell_surface must have a role");
301         return;
302     }
303 }
304
305 static void
306 shell_surface_handle_resource_destroy(struct wl_resource *resource)
307 {
308     struct ds_shell_surface *shell_surface;
309
310     shell_surface = wl_resource_get_user_data(resource);
311     if (!shell_surface)
312         return;
313
314     destroy_shell_surface(shell_surface);
315 }
316
317 static void
318 shell_surface_configure_destroy(struct ds_shell_surface_configure *configure)
319 {
320     wl_list_remove(&configure->link);
321     free(configure);
322 }
323
324 static void
325 create_shell_surface_toplevel(struct ds_shell_surface *shell_surface)
326 {
327     assert(shell_surface->toplevel == NULL);
328
329     shell_surface->toplevel = calloc(1, sizeof *shell_surface->toplevel);
330     if (!shell_surface->toplevel) {
331         wl_resource_post_no_memory(shell_surface->resource);
332         return;
333     }
334
335     shell_surface->toplevel->base = shell_surface;
336
337     wl_signal_init(&shell_surface->toplevel->events.request_maximize);
338     wl_signal_init(&shell_surface->toplevel->events.request_fullscreen);
339     wl_signal_init(&shell_surface->toplevel->events.request_minimize);
340     wl_signal_init(&shell_surface->toplevel->events.request_move);
341     wl_signal_init(&shell_surface->toplevel->events.request_resize);
342     wl_signal_init(&shell_surface->toplevel->events.request_show_window_menu);
343     wl_signal_init(&shell_surface->toplevel->events.set_parent);
344     wl_signal_init(&shell_surface->toplevel->events.set_title);
345     wl_signal_init(&shell_surface->toplevel->events.set_app_id);
346
347     shell_surface->role = DS_SHELL_SURFACE_ROLE_TOPLEVEL;
348 }
349
350 static void
351 shell_surface_handle_pong(struct wl_client *wl_client,
352         struct wl_resource *resource, uint32_t serial)
353 {
354     struct ds_shell_surface *shell_surface;
355     struct ds_shell_client *client;
356
357     shell_surface = wl_resource_get_user_data(resource);
358
359     client = shell_surface->client;
360     if (client->ping_serial != serial)
361         return;
362
363     wl_event_source_timer_update(client->ping_timer, 0);
364     client->ping_serial = 0;
365 }
366
367 static void
368 shell_surface_handle_move(struct wl_client *client,
369         struct wl_resource *resource, struct wl_resource *seat_resource,
370         uint32_t serial)
371 {
372     struct ds_shell_surface *shell_surface;
373
374     shell_surface = wl_resource_get_user_data(resource);
375
376     // TODO
377     (void)shell_surface;
378 }
379
380 static void
381 shell_surface_handle_resize(struct wl_client *client,
382         struct wl_resource *resource, struct wl_resource *seat_resource,
383         uint32_t serial, uint32_t edges)
384 {
385     struct ds_shell_surface *shell_surface;
386
387     shell_surface = wl_resource_get_user_data(resource);
388
389     // TODO
390     (void)shell_surface;
391 }
392
393 static void
394 shell_surface_handle_set_toplevel(struct wl_client *client, struct wl_resource *resource)
395 {
396     struct ds_shell_surface *shell_surface;
397
398     shell_surface = wl_resource_get_user_data(resource);
399     create_shell_surface_toplevel(shell_surface);
400 }
401
402 static void
403 shell_surface_handle_set_transient(struct wl_client *client,
404         struct wl_resource *resource, struct wl_resource *parent_resource,
405         int32_t x, int32_t y, uint32_t flags)
406 {
407     struct ds_shell_surface *shell_surface;
408
409     shell_surface = wl_resource_get_user_data(resource);
410
411     // TODO
412     (void)shell_surface;
413     ds_err("Not implemented yet.");
414 }
415
416 static void
417 shell_surface_handle_set_fullscreen(struct wl_client *client,
418         struct wl_resource *resource, uint32_t method, uint32_t framerate,
419         struct wl_resource *output_resource)
420 {
421     struct ds_shell_surface *shell_surface;
422
423     shell_surface = wl_resource_get_user_data(resource);
424
425     // TODO
426     (void)shell_surface;
427     ds_err("Not implemented yet.");
428 }
429
430 static void
431 shell_surface_handle_set_popup(struct wl_client *client,
432         struct wl_resource *resource, struct wl_resource *seat_resource,
433         uint32_t serial, struct wl_resource *parent_resource,
434         int32_t x, int32_t y, uint32_t flags)
435 {
436     struct ds_shell_surface *shell_surface;
437
438     shell_surface = wl_resource_get_user_data(resource);
439
440     // TODO
441     (void)shell_surface;
442     ds_err("Not implemented yet.");
443 }
444
445 static void
446 shell_surface_handle_set_maximized(struct wl_client *client,
447         struct wl_resource *resource, struct wl_resource *output_resource)
448 {
449     struct ds_shell_surface *shell_surface;
450
451     shell_surface = wl_resource_get_user_data(resource);
452
453     shell_surface->toplevel->requested.maximized = true;
454     wl_signal_emit(&shell_surface->toplevel->events.request_maximize, shell_surface);
455     ds_shell_surface_schedule_configure(shell_surface);
456 }
457
458 static void
459 shell_surface_handle_set_title(struct wl_client *client,
460         struct wl_resource *resource, const char *title)
461 {
462     struct ds_shell_surface *shell_surface;
463     char *tmp;
464
465     shell_surface = wl_resource_get_user_data(resource);
466     tmp = strdup(title);
467     if (!tmp) {
468         wl_resource_post_no_memory(resource);
469         return;
470     }
471
472     if (shell_surface->toplevel->title)
473         free(shell_surface->toplevel->title);
474
475     shell_surface->toplevel->title = tmp;
476     wl_signal_emit(&shell_surface->toplevel->events.set_title, shell_surface);
477 }
478
479 static void
480 shell_surface_handle_set_class(struct wl_client *client,
481         struct wl_resource *resource, const char *clas)
482 {
483     struct ds_shell_surface *shell_surface;
484
485     shell_surface = wl_resource_get_user_data(resource);
486
487     // TODO
488     (void)shell_surface;
489     ds_err("Not implemented yet.");
490 }
491
492 static const struct wl_shell_surface_interface shell_surface_impl =
493 {
494     .pong = shell_surface_handle_pong,
495     .move = shell_surface_handle_move,
496     .resize = shell_surface_handle_resize,
497     .set_toplevel = shell_surface_handle_set_toplevel,
498     .set_transient = shell_surface_handle_set_transient,
499     .set_fullscreen = shell_surface_handle_set_fullscreen,
500     .set_popup = shell_surface_handle_set_popup,
501     .set_maximized = shell_surface_handle_set_maximized,
502     .set_title = shell_surface_handle_set_title,
503     .set_class = shell_surface_handle_set_class,
504 };
505
506 static void
507 surface_send_configure(void *user_data)
508 {
509     struct ds_shell_surface *shell_surface;
510     struct ds_shell_surface_configure *configure;
511     uint32_t width, height;
512     uint32_t edges = 0;
513
514     shell_surface = user_data;
515     shell_surface->configure_idle = NULL;
516
517     // TDOO: Not sure if shell needs the struct ds_shell_surface_configure.
518     configure = calloc(1, sizeof *configure);
519     if (!configure) {
520         wl_client_post_no_memory(shell_surface->client->wl_client);
521         return;
522     }
523
524     wl_list_insert(shell_surface->configure_list.prev, &configure->link);
525     configure->serial = shell_surface->scheduled_serial;
526     configure->shell_surface = shell_surface;
527
528     wl_signal_emit(&shell_surface->events.configure, configure);
529
530     edges = (WL_SHELL_SURFACE_RESIZE_TOP | WL_SHELL_SURFACE_RESIZE_LEFT); // fixed default value
531     width = shell_surface->current.geometry.width;
532     height = shell_surface->current.geometry.height;
533
534     wl_shell_surface_send_configure(shell_surface->resource, edges, width, height);
535
536     shell_surface->configured = true;
537
538     // TDOO: Not sure if shell needs the struct ds_shell_surface_configure.
539     shell_surface_configure_destroy(configure);
540 }