blob: 601b289a51fb8759f6eb5feed6df9ea77cd87ac8 [file] [log] [blame]
Kristian Høgsberg677a5f52013-12-04 11:00:19 -08001/*
2 * Copyright © 2010-2012 Intel Corporation
3 * Copyright © 2011-2012 Collabora, Ltd.
4 * Copyright © 2013 Raspberry Pi Foundation
5 *
6 * Permission to use, copy, modify, distribute, and sell this software and
7 * its documentation for any purpose is hereby granted without fee, provided
8 * that the above copyright notice appear in all copies and that both that
9 * copyright notice and this permission notice appear in supporting
10 * documentation, and that the name of the copyright holders not be used in
11 * advertising or publicity pertaining to distribution of the software
12 * without specific, written prior permission. The copyright holders make
13 * no representations about the suitability of this software for any
14 * purpose. It is provided "as is" without express or implied warranty.
15 *
16 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
17 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
18 * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
20 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
21 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
22 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 */
24
25#include "config.h"
26
27#include <stdlib.h>
28#include <stdio.h>
29#include <string.h>
30
31#include "shell.h"
32#include "desktop-shell-server-protocol.h"
33#include "input-method-server-protocol.h"
34
35struct input_panel_surface {
36 struct wl_resource *resource;
37 struct wl_signal destroy_signal;
38
39 struct desktop_shell *shell;
40
41 struct wl_list link;
42 struct weston_surface *surface;
43 struct weston_view *view;
44 struct wl_listener surface_destroy_listener;
45
Ander Conselvan de Oliveira75c373c2014-04-14 15:48:07 +030046 struct weston_view_animation *anim;
47
Kristian Høgsberg677a5f52013-12-04 11:00:19 -080048 struct weston_output *output;
49 uint32_t panel;
50};
51
52static void
Ander Conselvan de Oliveira75c373c2014-04-14 15:48:07 +030053input_panel_slide_done(struct weston_view_animation *animation, void *data)
54{
55 struct input_panel_surface *ipsurf = data;
56
57 ipsurf->anim = NULL;
58}
59
60static void
61show_input_panel_surface(struct input_panel_surface *ipsurf)
62{
63 struct desktop_shell *shell = ipsurf->shell;
64
65 wl_list_insert(&shell->input_panel_layer.view_list,
66 &ipsurf->view->layer_link);
67 weston_view_geometry_dirty(ipsurf->view);
68 weston_view_update_transform(ipsurf->view);
69 weston_surface_damage(ipsurf->surface);
70
71 if (ipsurf->anim)
72 weston_view_animation_destroy(ipsurf->anim);
73
74 ipsurf->anim =
75 weston_slide_run(ipsurf->view,
76 ipsurf->surface->height * 0.9, 0,
77 input_panel_slide_done, ipsurf);
78}
79
80static void
Kristian Høgsberg677a5f52013-12-04 11:00:19 -080081show_input_panels(struct wl_listener *listener, void *data)
82{
83 struct desktop_shell *shell =
84 container_of(listener, struct desktop_shell,
85 show_input_panel_listener);
86 struct input_panel_surface *ipsurf, *next;
87
88 shell->text_input.surface = (struct weston_surface*)data;
89
90 if (shell->showing_input_panels)
91 return;
92
93 shell->showing_input_panels = true;
94
95 if (!shell->locked)
Manuel Bachmann805d2f52014-03-05 12:21:34 +010096 wl_list_insert(&shell->compositor->cursor_layer.link,
Kristian Høgsberg677a5f52013-12-04 11:00:19 -080097 &shell->input_panel_layer.link);
98
99 wl_list_for_each_safe(ipsurf, next,
100 &shell->input_panel.surfaces, link) {
Kristian Høgsberg2eebcd32014-01-02 01:27:06 -0800101 if (ipsurf->surface->width == 0)
Kristian Høgsberg677a5f52013-12-04 11:00:19 -0800102 continue;
Ander Conselvan de Oliveira75c373c2014-04-14 15:48:07 +0300103
104 show_input_panel_surface(ipsurf);
Kristian Høgsberg677a5f52013-12-04 11:00:19 -0800105 }
106}
107
108static void
109hide_input_panels(struct wl_listener *listener, void *data)
110{
111 struct desktop_shell *shell =
112 container_of(listener, struct desktop_shell,
113 hide_input_panel_listener);
114 struct weston_view *view, *next;
115
116 if (!shell->showing_input_panels)
117 return;
118
119 shell->showing_input_panels = false;
120
121 if (!shell->locked)
122 wl_list_remove(&shell->input_panel_layer.link);
123
124 wl_list_for_each_safe(view, next,
125 &shell->input_panel_layer.view_list, layer_link)
126 weston_view_unmap(view);
127}
128
129static void
130update_input_panels(struct wl_listener *listener, void *data)
131{
132 struct desktop_shell *shell =
133 container_of(listener, struct desktop_shell,
134 update_input_panel_listener);
135
136 memcpy(&shell->text_input.cursor_rectangle, data, sizeof(pixman_box32_t));
137}
138
139static void
140input_panel_configure(struct weston_surface *surface, int32_t sx, int32_t sy)
141{
142 struct input_panel_surface *ip_surface = surface->configure_private;
143 struct desktop_shell *shell = ip_surface->shell;
U. Artie Eoffc4c7a4f2014-01-15 14:03:56 -0800144 struct weston_view *view;
Kristian Høgsberg677a5f52013-12-04 11:00:19 -0800145 float x, y;
Kristian Høgsberg677a5f52013-12-04 11:00:19 -0800146
147 if (surface->width == 0)
148 return;
149
Kristian Høgsberg677a5f52013-12-04 11:00:19 -0800150 if (ip_surface->panel) {
U. Artie Eoffc4c7a4f2014-01-15 14:03:56 -0800151 view = get_default_view(shell->text_input.surface);
152 if (view == NULL)
153 return;
154 x = view->geometry.x + shell->text_input.cursor_rectangle.x2;
155 y = view->geometry.y + shell->text_input.cursor_rectangle.y2;
Kristian Høgsberg677a5f52013-12-04 11:00:19 -0800156 } else {
157 x = ip_surface->output->x + (ip_surface->output->width - surface->width) / 2;
158 y = ip_surface->output->y + ip_surface->output->height - surface->height;
159 }
160
161 weston_view_set_position(ip_surface->view, x, y);
162
Ander Conselvan de Oliveira75c373c2014-04-14 15:48:07 +0300163 if (!weston_surface_is_mapped(surface) && shell->showing_input_panels)
164 show_input_panel_surface(ip_surface);
Kristian Høgsberg677a5f52013-12-04 11:00:19 -0800165}
166
167static void
168destroy_input_panel_surface(struct input_panel_surface *input_panel_surface)
169{
170 wl_signal_emit(&input_panel_surface->destroy_signal, input_panel_surface);
171
172 wl_list_remove(&input_panel_surface->surface_destroy_listener.link);
173 wl_list_remove(&input_panel_surface->link);
174
175 input_panel_surface->surface->configure = NULL;
176 weston_view_destroy(input_panel_surface->view);
177
178 free(input_panel_surface);
179}
180
181static struct input_panel_surface *
182get_input_panel_surface(struct weston_surface *surface)
183{
184 if (surface->configure == input_panel_configure) {
185 return surface->configure_private;
186 } else {
187 return NULL;
188 }
189}
190
191static void
192input_panel_handle_surface_destroy(struct wl_listener *listener, void *data)
193{
194 struct input_panel_surface *ipsurface = container_of(listener,
195 struct input_panel_surface,
196 surface_destroy_listener);
197
198 if (ipsurface->resource) {
199 wl_resource_destroy(ipsurface->resource);
200 } else {
201 destroy_input_panel_surface(ipsurface);
202 }
203}
204
205static struct input_panel_surface *
206create_input_panel_surface(struct desktop_shell *shell,
207 struct weston_surface *surface)
208{
209 struct input_panel_surface *input_panel_surface;
210
211 input_panel_surface = calloc(1, sizeof *input_panel_surface);
212 if (!input_panel_surface)
213 return NULL;
214
215 surface->configure = input_panel_configure;
216 surface->configure_private = input_panel_surface;
217
218 input_panel_surface->shell = shell;
219
220 input_panel_surface->surface = surface;
221 input_panel_surface->view = weston_view_create(surface);
222
223 wl_signal_init(&input_panel_surface->destroy_signal);
224 input_panel_surface->surface_destroy_listener.notify = input_panel_handle_surface_destroy;
225 wl_signal_add(&surface->destroy_signal,
226 &input_panel_surface->surface_destroy_listener);
227
228 wl_list_init(&input_panel_surface->link);
229
230 return input_panel_surface;
231}
232
233static void
234input_panel_surface_set_toplevel(struct wl_client *client,
235 struct wl_resource *resource,
236 struct wl_resource *output_resource,
237 uint32_t position)
238{
239 struct input_panel_surface *input_panel_surface =
240 wl_resource_get_user_data(resource);
241 struct desktop_shell *shell = input_panel_surface->shell;
242
243 wl_list_insert(&shell->input_panel.surfaces,
244 &input_panel_surface->link);
245
246 input_panel_surface->output = wl_resource_get_user_data(output_resource);
247 input_panel_surface->panel = 0;
248}
249
250static void
251input_panel_surface_set_overlay_panel(struct wl_client *client,
252 struct wl_resource *resource)
253{
254 struct input_panel_surface *input_panel_surface =
255 wl_resource_get_user_data(resource);
256 struct desktop_shell *shell = input_panel_surface->shell;
257
258 wl_list_insert(&shell->input_panel.surfaces,
259 &input_panel_surface->link);
260
261 input_panel_surface->panel = 1;
262}
263
264static const struct wl_input_panel_surface_interface input_panel_surface_implementation = {
265 input_panel_surface_set_toplevel,
266 input_panel_surface_set_overlay_panel
267};
268
269static void
270destroy_input_panel_surface_resource(struct wl_resource *resource)
271{
272 struct input_panel_surface *ipsurf =
273 wl_resource_get_user_data(resource);
274
275 destroy_input_panel_surface(ipsurf);
276}
277
278static void
279input_panel_get_input_panel_surface(struct wl_client *client,
280 struct wl_resource *resource,
281 uint32_t id,
282 struct wl_resource *surface_resource)
283{
284 struct weston_surface *surface =
285 wl_resource_get_user_data(surface_resource);
286 struct desktop_shell *shell = wl_resource_get_user_data(resource);
287 struct input_panel_surface *ipsurf;
288
289 if (get_input_panel_surface(surface)) {
290 wl_resource_post_error(surface_resource,
291 WL_DISPLAY_ERROR_INVALID_OBJECT,
292 "wl_input_panel::get_input_panel_surface already requested");
293 return;
294 }
295
296 ipsurf = create_input_panel_surface(shell, surface);
297 if (!ipsurf) {
298 wl_resource_post_error(surface_resource,
299 WL_DISPLAY_ERROR_INVALID_OBJECT,
300 "surface->configure already set");
301 return;
302 }
303
304 ipsurf->resource =
305 wl_resource_create(client,
306 &wl_input_panel_surface_interface, 1, id);
307 wl_resource_set_implementation(ipsurf->resource,
308 &input_panel_surface_implementation,
309 ipsurf,
310 destroy_input_panel_surface_resource);
311}
312
313static const struct wl_input_panel_interface input_panel_implementation = {
314 input_panel_get_input_panel_surface
315};
316
317static void
318unbind_input_panel(struct wl_resource *resource)
319{
320 struct desktop_shell *shell = wl_resource_get_user_data(resource);
321
322 shell->input_panel.binding = NULL;
323}
324
325static void
326bind_input_panel(struct wl_client *client,
327 void *data, uint32_t version, uint32_t id)
328{
329 struct desktop_shell *shell = data;
330 struct wl_resource *resource;
331
332 resource = wl_resource_create(client,
333 &wl_input_panel_interface, 1, id);
334
335 if (shell->input_panel.binding == NULL) {
336 wl_resource_set_implementation(resource,
337 &input_panel_implementation,
338 shell, unbind_input_panel);
339 shell->input_panel.binding = resource;
340 return;
341 }
342
343 wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
344 "interface object already bound");
345 wl_resource_destroy(resource);
346}
347
348void
349input_panel_destroy(struct desktop_shell *shell)
350{
351 wl_list_remove(&shell->show_input_panel_listener.link);
352 wl_list_remove(&shell->hide_input_panel_listener.link);
353}
354
355int
356input_panel_setup(struct desktop_shell *shell)
357{
358 struct weston_compositor *ec = shell->compositor;
359
360 shell->show_input_panel_listener.notify = show_input_panels;
361 wl_signal_add(&ec->show_input_panel_signal,
362 &shell->show_input_panel_listener);
363 shell->hide_input_panel_listener.notify = hide_input_panels;
364 wl_signal_add(&ec->hide_input_panel_signal,
365 &shell->hide_input_panel_listener);
366 shell->update_input_panel_listener.notify = update_input_panels;
367 wl_signal_add(&ec->update_input_panel_signal,
368 &shell->update_input_panel_listener);
369
370 wl_list_init(&shell->input_panel.surfaces);
371
372 if (wl_global_create(shell->compositor->wl_display,
373 &wl_input_panel_interface, 1,
374 shell, bind_input_panel) == NULL)
375 return -1;
376
377 return 0;
378}