main neuswc / libswc / panel.c
  1/* swc: libswc/panel.c
  2 *
  3 * Copyright (c) 2013, 2014 Michael Forney
  4 *
  5 * Permission is hereby granted, free of charge, to any person obtaining a copy
  6 * of this software and associated documentation files (the "Software"), to deal
  7 * in the Software without restriction, including without limitation the rights
  8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9 * copies of the Software, and to permit persons to whom the Software is
 10 * furnished to do so, subject to the following conditions:
 11 *
 12 * The above copyright notice and this permission notice shall be included in
 13 * all copies or substantial portions of the Software.
 14 *
 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 21 * SOFTWARE.
 22 */
 23
 24#include "panel.h"
 25#include "compositor.h"
 26#include "internal.h"
 27#include "keyboard.h"
 28#include "output.h"
 29#include "screen.h"
 30#include "seat.h"
 31#include "surface.h"
 32#include "util.h"
 33#include "view.h"
 34
 35#include "swc-server-protocol.h"
 36#include <assert.h>
 37#include <stdlib.h>
 38
 39struct panel {
 40	struct wl_resource *resource;
 41
 42	struct wl_listener surface_destroy_listener;
 43	struct compositor_view *view;
 44	struct view_handler view_handler;
 45	struct screen *screen;
 46	struct screen_modifier modifier;
 47	uint32_t edge;
 48	uint32_t offset, strut_size;
 49	uint32_t y_offset;
 50	bool docked;
 51};
 52
 53static void
 54update_position(struct panel *panel)
 55{
 56	int32_t x, y;
 57	struct swc_rectangle *screen = &panel->screen->base.geometry,
 58	                     *view = &panel->view->base.geometry;
 59
 60	switch (panel->edge) {
 61	case SWC_PANEL_EDGE_TOP:
 62		x = screen->x + panel->offset;
 63		y = screen->y + panel->y_offset;
 64		break;
 65	case SWC_PANEL_EDGE_BOTTOM:
 66		x = screen->x + panel->offset;
 67		y = screen->y + screen->height - view->height - panel->y_offset;
 68		break;
 69	case SWC_PANEL_EDGE_LEFT:
 70		x = screen->x;
 71		y = screen->y + screen->height - view->height - panel->offset;
 72		break;
 73	case SWC_PANEL_EDGE_RIGHT:
 74		x = screen->x + screen->width - view->width;
 75		y = screen->y + panel->offset;
 76		break;
 77	default:
 78		return;
 79	}
 80
 81	view_move(&panel->view->base, x, y);
 82}
 83
 84static void
 85dock(struct wl_client *client, struct wl_resource *resource, uint32_t edge,
 86     struct wl_resource *screen_resource, uint32_t focus)
 87{
 88	struct panel *panel = wl_resource_get_user_data(resource);
 89	struct screen *screen;
 90	uint32_t length;
 91
 92	if (screen_resource) {
 93		screen = wl_resource_get_user_data(screen_resource);
 94	} else {
 95		screen = wl_container_of(swc.screens.next, screen, link);
 96	}
 97
 98	switch (edge) {
 99	case SWC_PANEL_EDGE_TOP:
100	case SWC_PANEL_EDGE_BOTTOM:
101		length = screen->base.geometry.width;
102		break;
103	case SWC_PANEL_EDGE_LEFT:
104	case SWC_PANEL_EDGE_RIGHT:
105		length = screen->base.geometry.height;
106		break;
107	default:
108		return;
109	}
110
111	if (panel->screen && screen != panel->screen) {
112		wl_list_remove(&panel->modifier.link);
113		screen_update_usable_geometry(panel->screen);
114	}
115
116	panel->screen = screen;
117	panel->edge = edge;
118	panel->docked = true;
119
120	update_position(panel);
121	compositor_view_show(panel->view);
122	raise_window_top(panel->view);
123	wl_list_insert(&screen->modifiers, &panel->modifier.link);
124
125	if (focus) {
126		keyboard_set_focus(swc.seat->keyboard, panel->view);
127	}
128
129	swc_panel_send_docked(resource, length);
130}
131
132static void
133set_offset(struct wl_client *client, struct wl_resource *resource,
134           uint32_t offset)
135{
136	struct panel *panel = wl_resource_get_user_data(resource);
137
138	panel->offset = offset;
139	if (panel->docked) {
140		update_position(panel);
141	}
142}
143
144static void
145set_y_offset(struct wl_client *client, struct wl_resource *resource,
146             uint32_t offset)
147{
148	struct panel *panel = wl_resource_get_user_data(resource);
149
150	panel->y_offset = offset;
151	if (panel->docked) {
152		update_position(panel);
153	}
154}
155
156static void
157set_strut(struct wl_client *client, struct wl_resource *resource, uint32_t size,
158          uint32_t begin, uint32_t end)
159{
160	struct panel *panel = wl_resource_get_user_data(resource);
161
162	panel->strut_size = size;
163	if (panel->docked) {
164		screen_update_usable_geometry(panel->screen);
165	}
166}
167
168static const struct swc_panel_interface panel_impl = {
169    .dock = dock,
170    .set_offset = set_offset,
171    .set_y_offset = set_y_offset,
172    .set_strut = set_strut,
173};
174
175static void
176handle_resize(struct view_handler *handler, uint32_t old_width,
177              uint32_t old_height)
178{
179	struct panel *panel = wl_container_of(handler, panel, view_handler);
180	update_position(panel);
181}
182
183static const struct view_handler_impl view_handler_impl = {
184    .resize = handle_resize,
185};
186
187static void
188modify(struct screen_modifier *modifier, const struct swc_rectangle *geom,
189       pixman_region32_t *usable)
190{
191	struct panel *panel = wl_container_of(modifier, panel, modifier);
192	pixman_box32_t box = {.x1 = geom->x,
193	                      .y1 = geom->y,
194	                      .x2 = geom->x + geom->width,
195	                      .y2 = geom->y + geom->height};
196
197	assert(panel->docked);
198
199	DEBUG("Original geometry { x1: %d, y1: %d, x2: %d, y2: %d }\n", box.x1,
200	      box.y1, box.x2, box.y2);
201
202	switch (panel->edge) {
203	case SWC_PANEL_EDGE_TOP:
204		box.y1 = MAX(box.y1, geom->y + panel->strut_size);
205		break;
206	case SWC_PANEL_EDGE_BOTTOM:
207		box.y2 = MIN(box.y2, geom->y + geom->height - panel->strut_size);
208		break;
209	case SWC_PANEL_EDGE_LEFT:
210		box.x1 = MAX(box.x1, geom->x + panel->strut_size);
211		break;
212	case SWC_PANEL_EDGE_RIGHT:
213		box.x2 = MIN(box.x2, geom->x + geom->width - panel->strut_size);
214		break;
215	}
216
217	DEBUG("Usable region { x1: %d, y1: %d, x2: %d, y2: %d }\n", box.x1, box.y1,
218	      box.x2, box.y2);
219
220	pixman_region32_reset(usable, &box);
221}
222
223static void
224destroy_panel(struct wl_resource *resource)
225{
226	struct panel *panel = wl_resource_get_user_data(resource);
227
228	if (panel->docked) {
229		wl_list_remove(&panel->modifier.link);
230		screen_update_usable_geometry(panel->screen);
231	}
232
233	compositor_view_destroy(panel->view);
234	free(panel);
235}
236
237static void
238handle_surface_destroy(struct wl_listener *listener, void *data)
239{
240	struct panel *panel =
241	    wl_container_of(listener, panel, surface_destroy_listener);
242	wl_resource_destroy(panel->resource);
243}
244
245struct panel *
246panel_new(struct wl_client *client, uint32_t version, uint32_t id,
247          struct surface *surface)
248{
249	struct panel *panel;
250
251	panel = malloc(sizeof(*panel));
252
253	if (!panel) {
254		goto error0;
255	}
256
257	panel->resource =
258	    wl_resource_create(client, &swc_panel_interface, version, id);
259
260	if (!panel->resource) {
261		goto error0;
262	}
263
264	if (!surface_set_role(surface, panel->resource)) {
265		goto error2;
266	}
267
268	if (!(panel->view = compositor_create_view(surface))) {
269		goto error3;
270	}
271
272	wl_resource_set_implementation(panel->resource, &panel_impl, panel,
273	                               &destroy_panel);
274	panel->surface_destroy_listener.notify = &handle_surface_destroy;
275	panel->view_handler.impl = &view_handler_impl;
276	panel->view->always_top = true;
277	panel->modifier.modify = &modify;
278	panel->screen = NULL;
279	panel->offset = 0;
280	panel->y_offset = 0;
281	panel->strut_size = 0;
282	panel->docked = false;
283	wl_list_insert(&panel->view->base.handlers, &panel->view_handler.link);
284	wl_resource_add_destroy_listener(surface->resource,
285	                                 &panel->surface_destroy_listener);
286
287	return panel;
288
289error3:
290	wl_resource_destroy(panel->resource);
291error2:
292	free(panel);
293error0:
294	return NULL;
295}