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 <stdio.h>
30 #include <string.h>
31 
32 #include "shell.h"
33 #include "desktop-shell-server-protocol.h"
34 #include "input-method-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 		ipsurf->output = focus->output;
78 		x = ipsurf->output->x + (ipsurf->output->width - ipsurf->surface->width) / 2;
79 		y = ipsurf->output->y + ipsurf->output->height - ipsurf->surface->height;
80 		weston_view_set_position(ipsurf->view, x, y);
81 	}
82 
83 	weston_layer_entry_insert(&shell->input_panel_layer.view_list,
84 	                          &ipsurf->view->layer_link);
85 	weston_view_geometry_dirty(ipsurf->view);
86 	weston_view_update_transform(ipsurf->view);
87 	weston_surface_damage(ipsurf->surface);
88 
89 	if (ipsurf->anim)
90 		weston_view_animation_destroy(ipsurf->anim);
91 
92 	ipsurf->anim =
93 		weston_slide_run(ipsurf->view,
94 				 ipsurf->surface->height * 0.9, 0,
95 				 input_panel_slide_done, ipsurf);
96 }
97 
98 static void
show_input_panels(struct wl_listener * listener,void * data)99 show_input_panels(struct wl_listener *listener, void *data)
100 {
101 	struct desktop_shell *shell =
102 		container_of(listener, struct desktop_shell,
103 			     show_input_panel_listener);
104 	struct input_panel_surface *ipsurf, *next;
105 
106 	shell->text_input.surface = (struct weston_surface*)data;
107 
108 	if (shell->showing_input_panels)
109 		return;
110 
111 	shell->showing_input_panels = true;
112 
113 	if (!shell->locked)
114 		wl_list_insert(&shell->compositor->cursor_layer.link,
115 			       &shell->input_panel_layer.link);
116 
117 	wl_list_for_each_safe(ipsurf, next,
118 			      &shell->input_panel.surfaces, link) {
119 		if (ipsurf->surface->width == 0)
120 			continue;
121 
122 		show_input_panel_surface(ipsurf);
123 	}
124 }
125 
126 static void
hide_input_panels(struct wl_listener * listener,void * data)127 hide_input_panels(struct wl_listener *listener, void *data)
128 {
129 	struct desktop_shell *shell =
130 		container_of(listener, struct desktop_shell,
131 			     hide_input_panel_listener);
132 	struct weston_view *view, *next;
133 
134 	if (!shell->showing_input_panels)
135 		return;
136 
137 	shell->showing_input_panels = false;
138 
139 	if (!shell->locked)
140 		wl_list_remove(&shell->input_panel_layer.link);
141 
142 	wl_list_for_each_safe(view, next,
143 			      &shell->input_panel_layer.view_list.link,
144 			      layer_link.link)
145 		weston_view_unmap(view);
146 }
147 
148 static void
update_input_panels(struct wl_listener * listener,void * data)149 update_input_panels(struct wl_listener *listener, void *data)
150 {
151 	struct desktop_shell *shell =
152 		container_of(listener, struct desktop_shell,
153 			     update_input_panel_listener);
154 
155 	memcpy(&shell->text_input.cursor_rectangle, data, sizeof(pixman_box32_t));
156 }
157 
158 static int
input_panel_get_label(struct weston_surface * surface,char * buf,size_t len)159 input_panel_get_label(struct weston_surface *surface, char *buf, size_t len)
160 {
161 	return snprintf(buf, len, "input panel");
162 }
163 
164 static void
input_panel_configure(struct weston_surface * surface,int32_t sx,int32_t sy)165 input_panel_configure(struct weston_surface *surface, int32_t sx, int32_t sy)
166 {
167 	struct input_panel_surface *ip_surface = surface->configure_private;
168 	struct desktop_shell *shell = ip_surface->shell;
169 	struct weston_view *view;
170 	float x, y;
171 
172 	if (surface->width == 0)
173 		return;
174 
175 	if (ip_surface->panel) {
176 		view = get_default_view(shell->text_input.surface);
177 		if (view == NULL)
178 			return;
179 		x = view->geometry.x + shell->text_input.cursor_rectangle.x2;
180 		y = view->geometry.y + shell->text_input.cursor_rectangle.y2;
181 	} else {
182 		x = ip_surface->output->x + (ip_surface->output->width - surface->width) / 2;
183 		y = ip_surface->output->y + ip_surface->output->height - surface->height;
184 	}
185 
186 	weston_view_set_position(ip_surface->view, x, y);
187 
188 	if (!weston_surface_is_mapped(surface) && shell->showing_input_panels)
189 		show_input_panel_surface(ip_surface);
190 }
191 
192 static void
destroy_input_panel_surface(struct input_panel_surface * input_panel_surface)193 destroy_input_panel_surface(struct input_panel_surface *input_panel_surface)
194 {
195 	wl_signal_emit(&input_panel_surface->destroy_signal, input_panel_surface);
196 
197 	wl_list_remove(&input_panel_surface->surface_destroy_listener.link);
198 	wl_list_remove(&input_panel_surface->link);
199 
200 	input_panel_surface->surface->configure = NULL;
201 	weston_surface_set_label_func(input_panel_surface->surface, NULL);
202 	weston_view_destroy(input_panel_surface->view);
203 
204 	free(input_panel_surface);
205 }
206 
207 static struct input_panel_surface *
get_input_panel_surface(struct weston_surface * surface)208 get_input_panel_surface(struct weston_surface *surface)
209 {
210 	if (surface->configure == input_panel_configure) {
211 		return surface->configure_private;
212 	} else {
213 		return NULL;
214 	}
215 }
216 
217 static void
input_panel_handle_surface_destroy(struct wl_listener * listener,void * data)218 input_panel_handle_surface_destroy(struct wl_listener *listener, void *data)
219 {
220 	struct input_panel_surface *ipsurface = container_of(listener,
221 							     struct input_panel_surface,
222 							     surface_destroy_listener);
223 
224 	if (ipsurface->resource) {
225 		wl_resource_destroy(ipsurface->resource);
226 	} else {
227 		destroy_input_panel_surface(ipsurface);
228 	}
229 }
230 
231 static struct input_panel_surface *
create_input_panel_surface(struct desktop_shell * shell,struct weston_surface * surface)232 create_input_panel_surface(struct desktop_shell *shell,
233 			   struct weston_surface *surface)
234 {
235 	struct input_panel_surface *input_panel_surface;
236 
237 	input_panel_surface = calloc(1, sizeof *input_panel_surface);
238 	if (!input_panel_surface)
239 		return NULL;
240 
241 	surface->configure = input_panel_configure;
242 	surface->configure_private = input_panel_surface;
243 	weston_surface_set_label_func(surface, input_panel_get_label);
244 
245 	input_panel_surface->shell = shell;
246 
247 	input_panel_surface->surface = surface;
248 	input_panel_surface->view = weston_view_create(surface);
249 
250 	wl_signal_init(&input_panel_surface->destroy_signal);
251 	input_panel_surface->surface_destroy_listener.notify = input_panel_handle_surface_destroy;
252 	wl_signal_add(&surface->destroy_signal,
253 		      &input_panel_surface->surface_destroy_listener);
254 
255 	wl_list_init(&input_panel_surface->link);
256 
257 	return input_panel_surface;
258 }
259 
260 static void
input_panel_surface_set_toplevel(struct wl_client * client,struct wl_resource * resource,struct wl_resource * output_resource,uint32_t position)261 input_panel_surface_set_toplevel(struct wl_client *client,
262 				 struct wl_resource *resource,
263 				 struct wl_resource *output_resource,
264 				 uint32_t position)
265 {
266 	struct input_panel_surface *input_panel_surface =
267 		wl_resource_get_user_data(resource);
268 	struct desktop_shell *shell = input_panel_surface->shell;
269 
270 	wl_list_insert(&shell->input_panel.surfaces,
271 		       &input_panel_surface->link);
272 
273 	input_panel_surface->output = wl_resource_get_user_data(output_resource);
274 	input_panel_surface->panel = 0;
275 }
276 
277 static void
input_panel_surface_set_overlay_panel(struct wl_client * client,struct wl_resource * resource)278 input_panel_surface_set_overlay_panel(struct wl_client *client,
279 				      struct wl_resource *resource)
280 {
281 	struct input_panel_surface *input_panel_surface =
282 		wl_resource_get_user_data(resource);
283 	struct desktop_shell *shell = input_panel_surface->shell;
284 
285 	wl_list_insert(&shell->input_panel.surfaces,
286 		       &input_panel_surface->link);
287 
288 	input_panel_surface->panel = 1;
289 }
290 
291 static const struct wl_input_panel_surface_interface input_panel_surface_implementation = {
292 	input_panel_surface_set_toplevel,
293 	input_panel_surface_set_overlay_panel
294 };
295 
296 static void
destroy_input_panel_surface_resource(struct wl_resource * resource)297 destroy_input_panel_surface_resource(struct wl_resource *resource)
298 {
299 	struct input_panel_surface *ipsurf =
300 		wl_resource_get_user_data(resource);
301 
302 	destroy_input_panel_surface(ipsurf);
303 }
304 
305 static void
input_panel_get_input_panel_surface(struct wl_client * client,struct wl_resource * resource,uint32_t id,struct wl_resource * surface_resource)306 input_panel_get_input_panel_surface(struct wl_client *client,
307 				    struct wl_resource *resource,
308 				    uint32_t id,
309 				    struct wl_resource *surface_resource)
310 {
311 	struct weston_surface *surface =
312 		wl_resource_get_user_data(surface_resource);
313 	struct desktop_shell *shell = wl_resource_get_user_data(resource);
314 	struct input_panel_surface *ipsurf;
315 
316 	if (get_input_panel_surface(surface)) {
317 		wl_resource_post_error(surface_resource,
318 				       WL_DISPLAY_ERROR_INVALID_OBJECT,
319 				       "wl_input_panel::get_input_panel_surface already requested");
320 		return;
321 	}
322 
323 	ipsurf = create_input_panel_surface(shell, surface);
324 	if (!ipsurf) {
325 		wl_resource_post_error(surface_resource,
326 				       WL_DISPLAY_ERROR_INVALID_OBJECT,
327 				       "surface->configure already set");
328 		return;
329 	}
330 
331 	ipsurf->resource =
332 		wl_resource_create(client,
333 				   &wl_input_panel_surface_interface, 1, id);
334 	wl_resource_set_implementation(ipsurf->resource,
335 				       &input_panel_surface_implementation,
336 				       ipsurf,
337 				       destroy_input_panel_surface_resource);
338 }
339 
340 static const struct wl_input_panel_interface input_panel_implementation = {
341 	input_panel_get_input_panel_surface
342 };
343 
344 static void
unbind_input_panel(struct wl_resource * resource)345 unbind_input_panel(struct wl_resource *resource)
346 {
347 	struct desktop_shell *shell = wl_resource_get_user_data(resource);
348 
349 	shell->input_panel.binding = NULL;
350 }
351 
352 static void
bind_input_panel(struct wl_client * client,void * data,uint32_t version,uint32_t id)353 bind_input_panel(struct wl_client *client,
354 	      void *data, uint32_t version, uint32_t id)
355 {
356 	struct desktop_shell *shell = data;
357 	struct wl_resource *resource;
358 
359 	resource = wl_resource_create(client,
360 				      &wl_input_panel_interface, 1, id);
361 
362 	if (shell->input_panel.binding == NULL) {
363 		wl_resource_set_implementation(resource,
364 					       &input_panel_implementation,
365 					       shell, unbind_input_panel);
366 		shell->input_panel.binding = resource;
367 		return;
368 	}
369 
370 	wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
371 			       "interface object already bound");
372 }
373 
374 void
input_panel_destroy(struct desktop_shell * shell)375 input_panel_destroy(struct desktop_shell *shell)
376 {
377 	wl_list_remove(&shell->show_input_panel_listener.link);
378 	wl_list_remove(&shell->hide_input_panel_listener.link);
379 }
380 
381 int
input_panel_setup(struct desktop_shell * shell)382 input_panel_setup(struct desktop_shell *shell)
383 {
384 	struct weston_compositor *ec = shell->compositor;
385 
386 	shell->show_input_panel_listener.notify = show_input_panels;
387 	wl_signal_add(&ec->show_input_panel_signal,
388 		      &shell->show_input_panel_listener);
389 	shell->hide_input_panel_listener.notify = hide_input_panels;
390 	wl_signal_add(&ec->hide_input_panel_signal,
391 		      &shell->hide_input_panel_listener);
392 	shell->update_input_panel_listener.notify = update_input_panels;
393 	wl_signal_add(&ec->update_input_panel_signal,
394 		      &shell->update_input_panel_listener);
395 
396 	wl_list_init(&shell->input_panel.surfaces);
397 
398 	if (wl_global_create(shell->compositor->wl_display,
399 			     &wl_input_panel_interface, 1,
400 			     shell, bind_input_panel) == NULL)
401 		return -1;
402 
403 	return 0;
404 }
405