1 /*
2 * Copyright © 2010-2012 Intel Corporation
3 * Copyright © 2011-2012 Collabora, Ltd.
4 * Copyright © 2013 Raspberry Pi Foundation
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the next
14 * paragraph) shall be included in all copies or substantial portions of the
15 * Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 * DEALINGS IN THE SOFTWARE.
24 */
25
26 #include "config.h"
27
28 #include <stdlib.h>
29 #include <stdint.h>
30 #include <stdio.h>
31 #include <string.h>
32
33 #include "shell.h"
34 #include "input-method-unstable-v1-server-protocol.h"
35 #include "shared/helpers.h"
36
37 struct input_panel_surface {
38 struct wl_resource *resource;
39 struct wl_signal destroy_signal;
40
41 struct desktop_shell *shell;
42
43 struct wl_list link;
44 struct weston_surface *surface;
45 struct weston_view *view;
46 struct wl_listener surface_destroy_listener;
47
48 struct weston_view_animation *anim;
49
50 struct weston_output *output;
51 uint32_t panel;
52 };
53
54 static void
input_panel_slide_done(struct weston_view_animation * animation,void * data)55 input_panel_slide_done(struct weston_view_animation *animation, void *data)
56 {
57 struct input_panel_surface *ipsurf = data;
58
59 ipsurf->anim = NULL;
60 }
61
62 static void
show_input_panel_surface(struct input_panel_surface * ipsurf)63 show_input_panel_surface(struct input_panel_surface *ipsurf)
64 {
65 struct desktop_shell *shell = ipsurf->shell;
66 struct weston_seat *seat;
67 struct weston_surface *focus;
68 float x, y;
69
70 wl_list_for_each(seat, &shell->compositor->seat_list, link) {
71 struct weston_keyboard *keyboard =
72 weston_seat_get_keyboard(seat);
73
74 if (!keyboard || !keyboard->focus)
75 continue;
76 focus = weston_surface_get_main_surface(keyboard->focus);
77 if (!focus)
78 continue;
79 ipsurf->output = focus->output;
80 x = ipsurf->output->x + (ipsurf->output->width - ipsurf->surface->width) / 2;
81 y = ipsurf->output->y + ipsurf->output->height - ipsurf->surface->height;
82 weston_view_set_position(ipsurf->view, x, y);
83 }
84
85 weston_layer_entry_insert(&shell->input_panel_layer.view_list,
86 &ipsurf->view->layer_link);
87 weston_view_geometry_dirty(ipsurf->view);
88 weston_view_update_transform(ipsurf->view);
89 ipsurf->surface->is_mapped = true;
90 ipsurf->view->is_mapped = true;
91 weston_surface_damage(ipsurf->surface);
92
93 if (ipsurf->anim)
94 weston_view_animation_destroy(ipsurf->anim);
95
96 ipsurf->anim =
97 weston_slide_run(ipsurf->view,
98 ipsurf->surface->height * 0.9, 0,
99 input_panel_slide_done, ipsurf);
100 }
101
102 static void
show_input_panels(struct wl_listener * listener,void * data)103 show_input_panels(struct wl_listener *listener, void *data)
104 {
105 struct desktop_shell *shell =
106 container_of(listener, struct desktop_shell,
107 show_input_panel_listener);
108 struct input_panel_surface *ipsurf, *next;
109
110 shell->text_input.surface = (struct weston_surface*)data;
111
112 if (shell->showing_input_panels)
113 return;
114
115 shell->showing_input_panels = true;
116
117 if (!shell->locked)
118 weston_layer_set_position(&shell->input_panel_layer,
119 WESTON_LAYER_POSITION_TOP_UI);
120
121 wl_list_for_each_safe(ipsurf, next,
122 &shell->input_panel.surfaces, link) {
123 if (ipsurf->surface->width == 0)
124 continue;
125
126 show_input_panel_surface(ipsurf);
127 }
128 }
129
130 static void
hide_input_panels(struct wl_listener * listener,void * data)131 hide_input_panels(struct wl_listener *listener, void *data)
132 {
133 struct desktop_shell *shell =
134 container_of(listener, struct desktop_shell,
135 hide_input_panel_listener);
136 struct weston_view *view, *next;
137
138 if (!shell->showing_input_panels)
139 return;
140
141 shell->showing_input_panels = false;
142
143 if (!shell->locked)
144 weston_layer_unset_position(&shell->input_panel_layer);
145
146 wl_list_for_each_safe(view, next,
147 &shell->input_panel_layer.view_list.link,
148 layer_link.link)
149 weston_view_unmap(view);
150 }
151
152 static void
update_input_panels(struct wl_listener * listener,void * data)153 update_input_panels(struct wl_listener *listener, void *data)
154 {
155 struct desktop_shell *shell =
156 container_of(listener, struct desktop_shell,
157 update_input_panel_listener);
158
159 memcpy(&shell->text_input.cursor_rectangle, data, sizeof(pixman_box32_t));
160 }
161
162 static int
input_panel_get_label(struct weston_surface * surface,char * buf,size_t len)163 input_panel_get_label(struct weston_surface *surface, char *buf, size_t len)
164 {
165 return snprintf(buf, len, "input panel");
166 }
167
168 static void
input_panel_committed(struct weston_surface * surface,int32_t sx,int32_t sy)169 input_panel_committed(struct weston_surface *surface, int32_t sx, int32_t sy)
170 {
171 struct input_panel_surface *ip_surface = surface->committed_private;
172 struct desktop_shell *shell = ip_surface->shell;
173 struct weston_view *view;
174 float x, y;
175
176 if (surface->width == 0)
177 return;
178
179 if (ip_surface->panel) {
180 view = get_default_view(shell->text_input.surface);
181 if (view == NULL)
182 return;
183 x = view->geometry.x + shell->text_input.cursor_rectangle.x2;
184 y = view->geometry.y + shell->text_input.cursor_rectangle.y2;
185 } else {
186 x = ip_surface->output->x + (ip_surface->output->width - surface->width) / 2;
187 y = ip_surface->output->y + ip_surface->output->height - surface->height;
188 }
189
190 weston_view_set_position(ip_surface->view, x, y);
191
192 if (!weston_surface_is_mapped(surface) && shell->showing_input_panels)
193 show_input_panel_surface(ip_surface);
194 }
195
196 static void
destroy_input_panel_surface(struct input_panel_surface * input_panel_surface)197 destroy_input_panel_surface(struct input_panel_surface *input_panel_surface)
198 {
199 wl_signal_emit(&input_panel_surface->destroy_signal, input_panel_surface);
200
201 wl_list_remove(&input_panel_surface->surface_destroy_listener.link);
202 wl_list_remove(&input_panel_surface->link);
203
204 input_panel_surface->surface->committed = NULL;
205 weston_surface_set_label_func(input_panel_surface->surface, NULL);
206 weston_view_destroy(input_panel_surface->view);
207
208 free(input_panel_surface);
209 }
210
211 static struct input_panel_surface *
get_input_panel_surface(struct weston_surface * surface)212 get_input_panel_surface(struct weston_surface *surface)
213 {
214 if (surface->committed == input_panel_committed) {
215 return surface->committed_private;
216 } else {
217 return NULL;
218 }
219 }
220
221 static void
input_panel_handle_surface_destroy(struct wl_listener * listener,void * data)222 input_panel_handle_surface_destroy(struct wl_listener *listener, void *data)
223 {
224 struct input_panel_surface *ipsurface = container_of(listener,
225 struct input_panel_surface,
226 surface_destroy_listener);
227
228 if (ipsurface->resource) {
229 wl_resource_destroy(ipsurface->resource);
230 } else {
231 destroy_input_panel_surface(ipsurface);
232 }
233 }
234
235 static struct input_panel_surface *
create_input_panel_surface(struct desktop_shell * shell,struct weston_surface * surface)236 create_input_panel_surface(struct desktop_shell *shell,
237 struct weston_surface *surface)
238 {
239 struct input_panel_surface *input_panel_surface;
240
241 input_panel_surface = calloc(1, sizeof *input_panel_surface);
242 if (!input_panel_surface)
243 return NULL;
244
245 surface->committed = input_panel_committed;
246 surface->committed_private = input_panel_surface;
247 weston_surface_set_label_func(surface, input_panel_get_label);
248
249 input_panel_surface->shell = shell;
250
251 input_panel_surface->surface = surface;
252 input_panel_surface->view = weston_view_create(surface);
253
254 wl_signal_init(&input_panel_surface->destroy_signal);
255 input_panel_surface->surface_destroy_listener.notify = input_panel_handle_surface_destroy;
256 wl_signal_add(&surface->destroy_signal,
257 &input_panel_surface->surface_destroy_listener);
258
259 wl_list_init(&input_panel_surface->link);
260
261 return input_panel_surface;
262 }
263
264 static void
input_panel_surface_set_toplevel(struct wl_client * client,struct wl_resource * resource,struct wl_resource * output_resource,uint32_t position)265 input_panel_surface_set_toplevel(struct wl_client *client,
266 struct wl_resource *resource,
267 struct wl_resource *output_resource,
268 uint32_t position)
269 {
270 struct input_panel_surface *input_panel_surface =
271 wl_resource_get_user_data(resource);
272 struct desktop_shell *shell = input_panel_surface->shell;
273 struct weston_head *head;
274
275 wl_list_insert(&shell->input_panel.surfaces,
276 &input_panel_surface->link);
277
278 head = weston_head_from_resource(output_resource);
279 input_panel_surface->output = head->output;
280 input_panel_surface->panel = 0;
281 }
282
283 static void
input_panel_surface_set_overlay_panel(struct wl_client * client,struct wl_resource * resource)284 input_panel_surface_set_overlay_panel(struct wl_client *client,
285 struct wl_resource *resource)
286 {
287 struct input_panel_surface *input_panel_surface =
288 wl_resource_get_user_data(resource);
289 struct desktop_shell *shell = input_panel_surface->shell;
290
291 wl_list_insert(&shell->input_panel.surfaces,
292 &input_panel_surface->link);
293
294 input_panel_surface->panel = 1;
295 }
296
297 static const struct zwp_input_panel_surface_v1_interface input_panel_surface_implementation = {
298 input_panel_surface_set_toplevel,
299 input_panel_surface_set_overlay_panel
300 };
301
302 static void
destroy_input_panel_surface_resource(struct wl_resource * resource)303 destroy_input_panel_surface_resource(struct wl_resource *resource)
304 {
305 struct input_panel_surface *ipsurf =
306 wl_resource_get_user_data(resource);
307
308 destroy_input_panel_surface(ipsurf);
309 }
310
311 static void
input_panel_get_input_panel_surface(struct wl_client * client,struct wl_resource * resource,uint32_t id,struct wl_resource * surface_resource)312 input_panel_get_input_panel_surface(struct wl_client *client,
313 struct wl_resource *resource,
314 uint32_t id,
315 struct wl_resource *surface_resource)
316 {
317 struct weston_surface *surface =
318 wl_resource_get_user_data(surface_resource);
319 struct desktop_shell *shell = wl_resource_get_user_data(resource);
320 struct input_panel_surface *ipsurf;
321
322 if (get_input_panel_surface(surface)) {
323 wl_resource_post_error(surface_resource,
324 WL_DISPLAY_ERROR_INVALID_OBJECT,
325 "wl_input_panel::get_input_panel_surface already requested");
326 return;
327 }
328
329 ipsurf = create_input_panel_surface(shell, surface);
330 if (!ipsurf) {
331 wl_resource_post_error(surface_resource,
332 WL_DISPLAY_ERROR_INVALID_OBJECT,
333 "surface->committed already set");
334 return;
335 }
336
337 ipsurf->resource =
338 wl_resource_create(client,
339 &zwp_input_panel_surface_v1_interface,
340 1,
341 id);
342 wl_resource_set_implementation(ipsurf->resource,
343 &input_panel_surface_implementation,
344 ipsurf,
345 destroy_input_panel_surface_resource);
346 }
347
348 static const struct zwp_input_panel_v1_interface input_panel_implementation = {
349 input_panel_get_input_panel_surface
350 };
351
352 static void
unbind_input_panel(struct wl_resource * resource)353 unbind_input_panel(struct wl_resource *resource)
354 {
355 struct desktop_shell *shell = wl_resource_get_user_data(resource);
356
357 shell->input_panel.binding = NULL;
358 }
359
360 static void
bind_input_panel(struct wl_client * client,void * data,uint32_t version,uint32_t id)361 bind_input_panel(struct wl_client *client,
362 void *data, uint32_t version, uint32_t id)
363 {
364 struct desktop_shell *shell = data;
365 struct wl_resource *resource;
366
367 resource = wl_resource_create(client,
368 &zwp_input_panel_v1_interface, 1, id);
369
370 if (shell->input_panel.binding == NULL) {
371 wl_resource_set_implementation(resource,
372 &input_panel_implementation,
373 shell, unbind_input_panel);
374 shell->input_panel.binding = resource;
375 return;
376 }
377
378 wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
379 "interface object already bound");
380 }
381
382 void
input_panel_destroy(struct desktop_shell * shell)383 input_panel_destroy(struct desktop_shell *shell)
384 {
385 wl_list_remove(&shell->show_input_panel_listener.link);
386 wl_list_remove(&shell->hide_input_panel_listener.link);
387 }
388
389 int
input_panel_setup(struct desktop_shell * shell)390 input_panel_setup(struct desktop_shell *shell)
391 {
392 struct weston_compositor *ec = shell->compositor;
393
394 shell->show_input_panel_listener.notify = show_input_panels;
395 wl_signal_add(&ec->show_input_panel_signal,
396 &shell->show_input_panel_listener);
397 shell->hide_input_panel_listener.notify = hide_input_panels;
398 wl_signal_add(&ec->hide_input_panel_signal,
399 &shell->hide_input_panel_listener);
400 shell->update_input_panel_listener.notify = update_input_panels;
401 wl_signal_add(&ec->update_input_panel_signal,
402 &shell->update_input_panel_listener);
403
404 wl_list_init(&shell->input_panel.surfaces);
405
406 if (wl_global_create(shell->compositor->wl_display,
407 &zwp_input_panel_v1_interface, 1,
408 shell, bind_input_panel) == NULL)
409 return -1;
410
411 return 0;
412 }
413