blob: ffa3ade17a3d24b649700f9c6d46b5848665337e [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;
Manuel Bachmann5082ad62014-04-17 14:04:32 +020064 struct weston_seat *seat;
65 struct weston_surface *focus;
66 float x, y;
67
68 wl_list_for_each(seat, &shell->compositor->seat_list, link) {
Pekka Paalanena4bac9e2014-11-20 10:15:07 +020069 if (!seat->keyboard || !seat->keyboard->focus)
Manuel Bachmann5082ad62014-04-17 14:04:32 +020070 continue;
71 focus = weston_surface_get_main_surface(seat->keyboard->focus);
72 ipsurf->output = focus->output;
73 x = ipsurf->output->x + (ipsurf->output->width - ipsurf->surface->width) / 2;
74 y = ipsurf->output->y + ipsurf->output->height - ipsurf->surface->height;
75 weston_view_set_position(ipsurf->view, x, y);
76 }
Ander Conselvan de Oliveira75c373c2014-04-14 15:48:07 +030077
Giulio Camuffo412e6a52014-07-09 22:12:56 +030078 weston_layer_entry_insert(&shell->input_panel_layer.view_list,
79 &ipsurf->view->layer_link);
Ander Conselvan de Oliveira75c373c2014-04-14 15:48:07 +030080 weston_view_geometry_dirty(ipsurf->view);
81 weston_view_update_transform(ipsurf->view);
82 weston_surface_damage(ipsurf->surface);
83
84 if (ipsurf->anim)
85 weston_view_animation_destroy(ipsurf->anim);
86
87 ipsurf->anim =
88 weston_slide_run(ipsurf->view,
89 ipsurf->surface->height * 0.9, 0,
90 input_panel_slide_done, ipsurf);
91}
92
93static void
Kristian Høgsberg677a5f52013-12-04 11:00:19 -080094show_input_panels(struct wl_listener *listener, void *data)
95{
96 struct desktop_shell *shell =
97 container_of(listener, struct desktop_shell,
98 show_input_panel_listener);
99 struct input_panel_surface *ipsurf, *next;
100
101 shell->text_input.surface = (struct weston_surface*)data;
102
103 if (shell->showing_input_panels)
104 return;
105
106 shell->showing_input_panels = true;
107
108 if (!shell->locked)
Manuel Bachmann805d2f52014-03-05 12:21:34 +0100109 wl_list_insert(&shell->compositor->cursor_layer.link,
Kristian Høgsberg677a5f52013-12-04 11:00:19 -0800110 &shell->input_panel_layer.link);
111
112 wl_list_for_each_safe(ipsurf, next,
113 &shell->input_panel.surfaces, link) {
Kristian Høgsberg2eebcd32014-01-02 01:27:06 -0800114 if (ipsurf->surface->width == 0)
Kristian Høgsberg677a5f52013-12-04 11:00:19 -0800115 continue;
Ander Conselvan de Oliveira75c373c2014-04-14 15:48:07 +0300116
117 show_input_panel_surface(ipsurf);
Kristian Høgsberg677a5f52013-12-04 11:00:19 -0800118 }
119}
120
121static void
122hide_input_panels(struct wl_listener *listener, void *data)
123{
124 struct desktop_shell *shell =
125 container_of(listener, struct desktop_shell,
126 hide_input_panel_listener);
127 struct weston_view *view, *next;
128
129 if (!shell->showing_input_panels)
130 return;
131
132 shell->showing_input_panels = false;
133
134 if (!shell->locked)
135 wl_list_remove(&shell->input_panel_layer.link);
136
137 wl_list_for_each_safe(view, next,
Giulio Camuffo412e6a52014-07-09 22:12:56 +0300138 &shell->input_panel_layer.view_list.link,
139 layer_link.link)
Kristian Høgsberg677a5f52013-12-04 11:00:19 -0800140 weston_view_unmap(view);
141}
142
143static void
144update_input_panels(struct wl_listener *listener, void *data)
145{
146 struct desktop_shell *shell =
147 container_of(listener, struct desktop_shell,
148 update_input_panel_listener);
149
150 memcpy(&shell->text_input.cursor_rectangle, data, sizeof(pixman_box32_t));
151}
152
Pekka Paalanen8274d902014-08-06 19:36:51 +0300153static int
154input_panel_get_label(struct weston_surface *surface, char *buf, size_t len)
155{
156 return snprintf(buf, len, "input panel");
157}
158
Kristian Høgsberg677a5f52013-12-04 11:00:19 -0800159static void
160input_panel_configure(struct weston_surface *surface, int32_t sx, int32_t sy)
161{
162 struct input_panel_surface *ip_surface = surface->configure_private;
163 struct desktop_shell *shell = ip_surface->shell;
U. Artie Eoffc4c7a4f2014-01-15 14:03:56 -0800164 struct weston_view *view;
Kristian Høgsberg677a5f52013-12-04 11:00:19 -0800165 float x, y;
Kristian Høgsberg677a5f52013-12-04 11:00:19 -0800166
167 if (surface->width == 0)
168 return;
169
Kristian Høgsberg677a5f52013-12-04 11:00:19 -0800170 if (ip_surface->panel) {
U. Artie Eoffc4c7a4f2014-01-15 14:03:56 -0800171 view = get_default_view(shell->text_input.surface);
172 if (view == NULL)
173 return;
174 x = view->geometry.x + shell->text_input.cursor_rectangle.x2;
175 y = view->geometry.y + shell->text_input.cursor_rectangle.y2;
Kristian Høgsberg677a5f52013-12-04 11:00:19 -0800176 } else {
177 x = ip_surface->output->x + (ip_surface->output->width - surface->width) / 2;
178 y = ip_surface->output->y + ip_surface->output->height - surface->height;
179 }
180
181 weston_view_set_position(ip_surface->view, x, y);
182
Ander Conselvan de Oliveira75c373c2014-04-14 15:48:07 +0300183 if (!weston_surface_is_mapped(surface) && shell->showing_input_panels)
184 show_input_panel_surface(ip_surface);
Kristian Høgsberg677a5f52013-12-04 11:00:19 -0800185}
186
187static void
188destroy_input_panel_surface(struct input_panel_surface *input_panel_surface)
189{
190 wl_signal_emit(&input_panel_surface->destroy_signal, input_panel_surface);
191
192 wl_list_remove(&input_panel_surface->surface_destroy_listener.link);
193 wl_list_remove(&input_panel_surface->link);
194
195 input_panel_surface->surface->configure = NULL;
Pekka Paalanen8274d902014-08-06 19:36:51 +0300196 weston_surface_set_label_func(input_panel_surface->surface, NULL);
Kristian Høgsberg677a5f52013-12-04 11:00:19 -0800197 weston_view_destroy(input_panel_surface->view);
198
199 free(input_panel_surface);
200}
201
202static struct input_panel_surface *
203get_input_panel_surface(struct weston_surface *surface)
204{
205 if (surface->configure == input_panel_configure) {
206 return surface->configure_private;
207 } else {
208 return NULL;
209 }
210}
211
212static void
213input_panel_handle_surface_destroy(struct wl_listener *listener, void *data)
214{
215 struct input_panel_surface *ipsurface = container_of(listener,
216 struct input_panel_surface,
217 surface_destroy_listener);
218
219 if (ipsurface->resource) {
220 wl_resource_destroy(ipsurface->resource);
221 } else {
222 destroy_input_panel_surface(ipsurface);
223 }
224}
225
226static struct input_panel_surface *
227create_input_panel_surface(struct desktop_shell *shell,
228 struct weston_surface *surface)
229{
230 struct input_panel_surface *input_panel_surface;
231
232 input_panel_surface = calloc(1, sizeof *input_panel_surface);
233 if (!input_panel_surface)
234 return NULL;
235
236 surface->configure = input_panel_configure;
237 surface->configure_private = input_panel_surface;
Pekka Paalanen8274d902014-08-06 19:36:51 +0300238 weston_surface_set_label_func(surface, input_panel_get_label);
Kristian Høgsberg677a5f52013-12-04 11:00:19 -0800239
240 input_panel_surface->shell = shell;
241
242 input_panel_surface->surface = surface;
243 input_panel_surface->view = weston_view_create(surface);
244
245 wl_signal_init(&input_panel_surface->destroy_signal);
246 input_panel_surface->surface_destroy_listener.notify = input_panel_handle_surface_destroy;
247 wl_signal_add(&surface->destroy_signal,
248 &input_panel_surface->surface_destroy_listener);
249
250 wl_list_init(&input_panel_surface->link);
251
252 return input_panel_surface;
253}
254
255static void
256input_panel_surface_set_toplevel(struct wl_client *client,
257 struct wl_resource *resource,
258 struct wl_resource *output_resource,
259 uint32_t position)
260{
261 struct input_panel_surface *input_panel_surface =
262 wl_resource_get_user_data(resource);
263 struct desktop_shell *shell = input_panel_surface->shell;
264
265 wl_list_insert(&shell->input_panel.surfaces,
266 &input_panel_surface->link);
267
268 input_panel_surface->output = wl_resource_get_user_data(output_resource);
269 input_panel_surface->panel = 0;
270}
271
272static void
273input_panel_surface_set_overlay_panel(struct wl_client *client,
274 struct wl_resource *resource)
275{
276 struct input_panel_surface *input_panel_surface =
277 wl_resource_get_user_data(resource);
278 struct desktop_shell *shell = input_panel_surface->shell;
279
280 wl_list_insert(&shell->input_panel.surfaces,
281 &input_panel_surface->link);
282
283 input_panel_surface->panel = 1;
284}
285
286static const struct wl_input_panel_surface_interface input_panel_surface_implementation = {
287 input_panel_surface_set_toplevel,
288 input_panel_surface_set_overlay_panel
289};
290
291static void
292destroy_input_panel_surface_resource(struct wl_resource *resource)
293{
294 struct input_panel_surface *ipsurf =
295 wl_resource_get_user_data(resource);
296
297 destroy_input_panel_surface(ipsurf);
298}
299
300static void
301input_panel_get_input_panel_surface(struct wl_client *client,
302 struct wl_resource *resource,
303 uint32_t id,
304 struct wl_resource *surface_resource)
305{
306 struct weston_surface *surface =
307 wl_resource_get_user_data(surface_resource);
308 struct desktop_shell *shell = wl_resource_get_user_data(resource);
309 struct input_panel_surface *ipsurf;
310
311 if (get_input_panel_surface(surface)) {
312 wl_resource_post_error(surface_resource,
313 WL_DISPLAY_ERROR_INVALID_OBJECT,
314 "wl_input_panel::get_input_panel_surface already requested");
315 return;
316 }
317
318 ipsurf = create_input_panel_surface(shell, surface);
319 if (!ipsurf) {
320 wl_resource_post_error(surface_resource,
321 WL_DISPLAY_ERROR_INVALID_OBJECT,
322 "surface->configure already set");
323 return;
324 }
325
326 ipsurf->resource =
327 wl_resource_create(client,
328 &wl_input_panel_surface_interface, 1, id);
329 wl_resource_set_implementation(ipsurf->resource,
330 &input_panel_surface_implementation,
331 ipsurf,
332 destroy_input_panel_surface_resource);
333}
334
335static const struct wl_input_panel_interface input_panel_implementation = {
336 input_panel_get_input_panel_surface
337};
338
339static void
340unbind_input_panel(struct wl_resource *resource)
341{
342 struct desktop_shell *shell = wl_resource_get_user_data(resource);
343
344 shell->input_panel.binding = NULL;
345}
346
347static void
348bind_input_panel(struct wl_client *client,
349 void *data, uint32_t version, uint32_t id)
350{
351 struct desktop_shell *shell = data;
352 struct wl_resource *resource;
353
354 resource = wl_resource_create(client,
355 &wl_input_panel_interface, 1, id);
356
357 if (shell->input_panel.binding == NULL) {
358 wl_resource_set_implementation(resource,
359 &input_panel_implementation,
360 shell, unbind_input_panel);
361 shell->input_panel.binding = resource;
362 return;
363 }
364
365 wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
366 "interface object already bound");
Kristian Høgsberg677a5f52013-12-04 11:00:19 -0800367}
368
369void
370input_panel_destroy(struct desktop_shell *shell)
371{
372 wl_list_remove(&shell->show_input_panel_listener.link);
373 wl_list_remove(&shell->hide_input_panel_listener.link);
374}
375
376int
377input_panel_setup(struct desktop_shell *shell)
378{
379 struct weston_compositor *ec = shell->compositor;
380
381 shell->show_input_panel_listener.notify = show_input_panels;
382 wl_signal_add(&ec->show_input_panel_signal,
383 &shell->show_input_panel_listener);
384 shell->hide_input_panel_listener.notify = hide_input_panels;
385 wl_signal_add(&ec->hide_input_panel_signal,
386 &shell->hide_input_panel_listener);
387 shell->update_input_panel_listener.notify = update_input_panels;
388 wl_signal_add(&ec->update_input_panel_signal,
389 &shell->update_input_panel_listener);
390
391 wl_list_init(&shell->input_panel.surfaces);
392
393 if (wl_global_create(shell->compositor->wl_display,
394 &wl_input_panel_interface, 1,
395 shell, bind_input_panel) == NULL)
396 return -1;
397
398 return 0;
399}