• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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