diff options
Diffstat (limited to 'shared')
-rw-r--r-- | shared/Makefile.am | 32 | ||||
-rw-r--r-- | shared/cairo-util.c | 511 | ||||
-rw-r--r-- | shared/cairo-util.h | 89 | ||||
-rw-r--r-- | shared/config-parser.c | 434 | ||||
-rw-r--r-- | shared/config-parser.h | 114 | ||||
-rw-r--r-- | shared/image-loader.c | 405 | ||||
-rw-r--r-- | shared/image-loader.h | 31 | ||||
-rw-r--r-- | shared/matrix.c | 273 | ||||
-rw-r--r-- | shared/matrix.h | 82 | ||||
-rw-r--r-- | shared/option-parser.c | 85 | ||||
-rw-r--r-- | shared/os-compatibility.c | 180 | ||||
-rw-r--r-- | shared/os-compatibility.h | 54 | ||||
-rw-r--r-- | shared/zalloc.h | 42 |
13 files changed, 2332 insertions, 0 deletions
diff --git a/shared/Makefile.am b/shared/Makefile.am new file mode 100644 index 00000000..2fcff7bb --- /dev/null +++ b/shared/Makefile.am @@ -0,0 +1,32 @@ +noinst_LTLIBRARIES = libshared.la libshared-cairo.la + +libshared_la_CFLAGS = $(GCC_CFLAGS) $(COMPOSITOR_CFLAGS) + +libshared_la_SOURCES = \ + config-parser.c \ + option-parser.c \ + config-parser.h \ + os-compatibility.c \ + os-compatibility.h + +libshared_cairo_la_CFLAGS = \ + $(GCC_CFLAGS) \ + $(COMPOSITOR_CFLAGS) \ + $(PIXMAN_CFLAGS) \ + $(CAIRO_CFLAGS) \ + $(PNG_CFLAGS) \ + $(WEBP_CFLAGS) + +libshared_cairo_la_LIBADD = \ + $(PIXMAN_LIBS) \ + $(CAIRO_LIBS) \ + $(PNG_LIBS) \ + $(WEBP_LIBS) \ + $(JPEG_LIBS) + +libshared_cairo_la_SOURCES = \ + $(libshared_la_SOURCES) \ + image-loader.c \ + image-loader.h \ + cairo-util.c \ + cairo-util.h diff --git a/shared/cairo-util.c b/shared/cairo-util.c new file mode 100644 index 00000000..4305ba69 --- /dev/null +++ b/shared/cairo-util.c @@ -0,0 +1,511 @@ +/* + * Copyright © 2008 Kristian Høgsberg + * Copyright © 2012 Intel Corporation + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include "config.h" + +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <math.h> +#include <cairo.h> +#include "cairo-util.h" + +#include "image-loader.h" +#include "config-parser.h" + +#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0]) + +void +surface_flush_device(cairo_surface_t *surface) +{ + cairo_device_t *device; + + device = cairo_surface_get_device(surface); + if (device) + cairo_device_flush(device); +} + +static int +blur_surface(cairo_surface_t *surface, int margin) +{ + int32_t width, height, stride, x, y, z, w; + uint8_t *src, *dst; + uint32_t *s, *d, a, p; + int i, j, k, size, half; + uint32_t kernel[71]; + double f; + + size = ARRAY_LENGTH(kernel); + width = cairo_image_surface_get_width(surface); + height = cairo_image_surface_get_height(surface); + stride = cairo_image_surface_get_stride(surface); + src = cairo_image_surface_get_data(surface); + + dst = malloc(height * stride); + if (dst == NULL) + return -1; + + half = size / 2; + a = 0; + for (i = 0; i < size; i++) { + f = (i - half); + kernel[i] = exp(- f * f / ARRAY_LENGTH(kernel)) * 10000; + a += kernel[i]; + } + + for (i = 0; i < height; i++) { + s = (uint32_t *) (src + i * stride); + d = (uint32_t *) (dst + i * stride); + for (j = 0; j < width; j++) { + if (margin < j && j < width - margin) { + d[j] = s[j]; + continue; + } + + x = 0; + y = 0; + z = 0; + w = 0; + for (k = 0; k < size; k++) { + if (j - half + k < 0 || j - half + k >= width) + continue; + p = s[j - half + k]; + + x += (p >> 24) * kernel[k]; + y += ((p >> 16) & 0xff) * kernel[k]; + z += ((p >> 8) & 0xff) * kernel[k]; + w += (p & 0xff) * kernel[k]; + } + d[j] = (x / a << 24) | (y / a << 16) | (z / a << 8) | w / a; + } + } + + for (i = 0; i < height; i++) { + s = (uint32_t *) (dst + i * stride); + d = (uint32_t *) (src + i * stride); + for (j = 0; j < width; j++) { + if (margin <= i && i < height - margin) { + d[j] = s[j]; + continue; + } + + x = 0; + y = 0; + z = 0; + w = 0; + for (k = 0; k < size; k++) { + if (i - half + k < 0 || i - half + k >= height) + continue; + s = (uint32_t *) (dst + (i - half + k) * stride); + p = s[j]; + + x += (p >> 24) * kernel[k]; + y += ((p >> 16) & 0xff) * kernel[k]; + z += ((p >> 8) & 0xff) * kernel[k]; + w += (p & 0xff) * kernel[k]; + } + d[j] = (x / a << 24) | (y / a << 16) | (z / a << 8) | w / a; + } + } + + free(dst); + cairo_surface_mark_dirty(surface); + + return 0; +} + +void +tile_mask(cairo_t *cr, cairo_surface_t *surface, + int x, int y, int width, int height, int margin, int top_margin) +{ + cairo_pattern_t *pattern; + cairo_matrix_t matrix; + int i, fx, fy, vmargin; + + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + pattern = cairo_pattern_create_for_surface (surface); + cairo_pattern_set_filter(pattern, CAIRO_FILTER_NEAREST); + + for (i = 0; i < 4; i++) { + fx = i & 1; + fy = i >> 1; + + cairo_matrix_init_translate(&matrix, + -x + fx * (128 - width), + -y + fy * (128 - height)); + cairo_pattern_set_matrix(pattern, &matrix); + + if (fy) + vmargin = margin; + else + vmargin = top_margin; + + cairo_reset_clip(cr); + cairo_rectangle(cr, + x + fx * (width - margin), + y + fy * (height - vmargin), + margin, vmargin); + cairo_clip (cr); + cairo_mask(cr, pattern); + } + + /* Top stretch */ + cairo_matrix_init_translate(&matrix, 60, 0); + cairo_matrix_scale(&matrix, 8.0 / width, 1); + cairo_matrix_translate(&matrix, -x - width / 2, -y); + cairo_pattern_set_matrix(pattern, &matrix); + cairo_rectangle(cr, x + margin, y, width - 2 * margin, margin); + + cairo_reset_clip(cr); + cairo_rectangle(cr, + x + margin, + y, + width - 2 * margin, margin); + cairo_clip (cr); + cairo_mask(cr, pattern); + + /* Bottom stretch */ + cairo_matrix_translate(&matrix, 0, -height + 128); + cairo_pattern_set_matrix(pattern, &matrix); + + cairo_reset_clip(cr); + cairo_rectangle(cr, x + margin, y + height - margin, + width - 2 * margin, margin); + cairo_clip (cr); + cairo_mask(cr, pattern); + + /* Left stretch */ + cairo_matrix_init_translate(&matrix, 0, 60); + cairo_matrix_scale(&matrix, 1, 8.0 / height); + cairo_matrix_translate(&matrix, -x, -y - height / 2); + cairo_pattern_set_matrix(pattern, &matrix); + cairo_reset_clip(cr); + cairo_rectangle(cr, x, y + margin, margin, height - 2 * margin); + cairo_clip (cr); + cairo_mask(cr, pattern); + + /* Right stretch */ + cairo_matrix_translate(&matrix, -width + 128, 0); + cairo_pattern_set_matrix(pattern, &matrix); + cairo_rectangle(cr, x + width - margin, y + margin, + margin, height - 2 * margin); + cairo_reset_clip(cr); + cairo_clip (cr); + cairo_mask(cr, pattern); + + cairo_pattern_destroy(pattern); + cairo_reset_clip(cr); +} + +void +tile_source(cairo_t *cr, cairo_surface_t *surface, + int x, int y, int width, int height, int margin, int top_margin) +{ + cairo_pattern_t *pattern; + cairo_matrix_t matrix; + int i, fx, fy, vmargin; + + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + pattern = cairo_pattern_create_for_surface (surface); + cairo_pattern_set_filter(pattern, CAIRO_FILTER_NEAREST); + cairo_set_source(cr, pattern); + cairo_pattern_destroy(pattern); + + for (i = 0; i < 4; i++) { + fx = i & 1; + fy = i >> 1; + + cairo_matrix_init_translate(&matrix, + -x + fx * (128 - width), + -y + fy * (128 - height)); + cairo_pattern_set_matrix(pattern, &matrix); + + if (fy) + vmargin = margin; + else + vmargin = top_margin; + + cairo_rectangle(cr, + x + fx * (width - margin), + y + fy * (height - vmargin), + margin, vmargin); + cairo_fill(cr); + } + + /* Top stretch */ + cairo_matrix_init_translate(&matrix, 60, 0); + cairo_matrix_scale(&matrix, 8.0 / (width - 2 * margin), 1); + cairo_matrix_translate(&matrix, -x - width / 2, -y); + cairo_pattern_set_matrix(pattern, &matrix); + cairo_rectangle(cr, x + margin, y, width - 2 * margin, top_margin); + cairo_fill(cr); + + /* Bottom stretch */ + cairo_matrix_translate(&matrix, 0, -height + 128); + cairo_pattern_set_matrix(pattern, &matrix); + cairo_rectangle(cr, x + margin, y + height - margin, + width - 2 * margin, margin); + cairo_fill(cr); + + /* Left stretch */ + cairo_matrix_init_translate(&matrix, 0, 60); + cairo_matrix_scale(&matrix, 1, 8.0 / (height - margin - top_margin)); + cairo_matrix_translate(&matrix, -x, -y - height / 2); + cairo_pattern_set_matrix(pattern, &matrix); + cairo_rectangle(cr, x, y + top_margin, + margin, height - margin - top_margin); + cairo_fill(cr); + + /* Right stretch */ + cairo_matrix_translate(&matrix, -width + 128, 0); + cairo_pattern_set_matrix(pattern, &matrix); + cairo_rectangle(cr, x + width - margin, y + top_margin, + margin, height - margin - top_margin); + cairo_fill(cr); +} + +void +rounded_rect(cairo_t *cr, int x0, int y0, int x1, int y1, int radius) +{ + cairo_move_to(cr, x0, y0 + radius); + cairo_arc(cr, x0 + radius, y0 + radius, radius, M_PI, 3 * M_PI / 2); + cairo_line_to(cr, x1 - radius, y0); + cairo_arc(cr, x1 - radius, y0 + radius, radius, 3 * M_PI / 2, 2 * M_PI); + cairo_line_to(cr, x1, y1 - radius); + cairo_arc(cr, x1 - radius, y1 - radius, radius, 0, M_PI / 2); + cairo_line_to(cr, x0 + radius, y1); + cairo_arc(cr, x0 + radius, y1 - radius, radius, M_PI / 2, M_PI); + cairo_close_path(cr); +} + +cairo_surface_t * +load_cairo_surface(const char *filename) +{ + pixman_image_t *image; + int width, height, stride; + void *data; + + image = load_image(filename); + if (image == NULL) { + return NULL; + } + + data = pixman_image_get_data(image); + width = pixman_image_get_width(image); + height = pixman_image_get_height(image); + stride = pixman_image_get_stride(image); + + return cairo_image_surface_create_for_data(data, CAIRO_FORMAT_ARGB32, + width, height, stride); +} + +struct theme * +theme_create(void) +{ + struct theme *t; + cairo_t *cr; + cairo_pattern_t *pattern; + + t = malloc(sizeof *t); + if (t == NULL) + return NULL; + + t->margin = 32; + t->width = 6; + t->titlebar_height = 27; + t->frame_radius = 3; + t->shadow = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 128, 128); + cr = cairo_create(t->shadow); + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + cairo_set_source_rgba(cr, 0, 0, 0, 1); + rounded_rect(cr, 32, 32, 96, 96, t->frame_radius); + cairo_fill(cr); + if (cairo_status (cr) != CAIRO_STATUS_SUCCESS) + goto err_shadow; + cairo_destroy(cr); + if (blur_surface(t->shadow, 64) == -1) + goto err_shadow; + + t->active_frame = + cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 128, 128); + cr = cairo_create(t->active_frame); + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + + pattern = cairo_pattern_create_linear(16, 16, 16, 112); + cairo_pattern_add_color_stop_rgb(pattern, 0.0, 1.0, 1.0, 1.0); + cairo_pattern_add_color_stop_rgb(pattern, 0.2, 0.8, 0.8, 0.8); + cairo_set_source(cr, pattern); + cairo_pattern_destroy(pattern); + + rounded_rect(cr, 0, 0, 128, 128, t->frame_radius); + cairo_fill(cr); + + if (cairo_status(cr) != CAIRO_STATUS_SUCCESS) + goto err_active_frame; + + cairo_destroy(cr); + + t->inactive_frame = + cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 128, 128); + cr = cairo_create(t->inactive_frame); + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + cairo_set_source_rgba(cr, 0.75, 0.75, 0.75, 1); + rounded_rect(cr, 0, 0, 128, 128, t->frame_radius); + cairo_fill(cr); + + if (cairo_status (cr) != CAIRO_STATUS_SUCCESS) + goto err_inactive_frame; + + cairo_destroy(cr); + + return t; + + err_inactive_frame: + cairo_surface_destroy(t->inactive_frame); + err_active_frame: + cairo_surface_destroy(t->active_frame); + err_shadow: + cairo_surface_destroy(t->shadow); + free(t); + return NULL; +} + +void +theme_destroy(struct theme *t) +{ + cairo_surface_destroy(t->active_frame); + cairo_surface_destroy(t->inactive_frame); + cairo_surface_destroy(t->shadow); + free(t); +} + +void +theme_render_frame(struct theme *t, + cairo_t *cr, int width, int height, + const char *title, uint32_t flags) +{ + cairo_text_extents_t extents; + cairo_font_extents_t font_extents; + cairo_surface_t *source; + int x, y, margin; + + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_set_source_rgba(cr, 0, 0, 0, 0); + cairo_paint(cr); + + if (flags & THEME_FRAME_MAXIMIZED) + margin = 0; + else { + cairo_set_source_rgba(cr, 0, 0, 0, 0.45); + tile_mask(cr, t->shadow, + 2, 2, width + 8, height + 8, + 64, 64); + margin = t->margin; + } + + if (flags & THEME_FRAME_ACTIVE) + source = t->active_frame; + else + source = t->inactive_frame; + + tile_source(cr, source, + margin, margin, + width - margin * 2, height - margin * 2, + t->width, t->titlebar_height); + + cairo_rectangle (cr, margin + t->width, margin, + width - (margin + t->width) * 2, + t->titlebar_height - t->width); + cairo_clip(cr); + + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + cairo_select_font_face(cr, "sans", + CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_BOLD); + cairo_set_font_size(cr, 14); + cairo_text_extents(cr, title, &extents); + cairo_font_extents (cr, &font_extents); + x = (width - extents.width) / 2; + y = margin + + (t->titlebar_height - + font_extents.ascent - font_extents.descent) / 2 + + font_extents.ascent; + + if (flags & THEME_FRAME_ACTIVE) { + cairo_move_to(cr, x + 1, y + 1); + cairo_set_source_rgb(cr, 1, 1, 1); + cairo_show_text(cr, title); + cairo_move_to(cr, x, y); + cairo_set_source_rgb(cr, 0, 0, 0); + cairo_show_text(cr, title); + } else { + cairo_move_to(cr, x, y); + cairo_set_source_rgb(cr, 0.4, 0.4, 0.4); + cairo_show_text(cr, title); + } +} + +enum theme_location +theme_get_location(struct theme *t, int x, int y, + int width, int height, int flags) +{ + int vlocation, hlocation, location; + const int grip_size = 8; + int margin; + + margin = (flags & THEME_FRAME_MAXIMIZED) ? 0 : t->margin; + + if (x < margin) + hlocation = THEME_LOCATION_EXTERIOR; + else if (margin <= x && x < margin + grip_size) + hlocation = THEME_LOCATION_RESIZING_LEFT; + else if (x < width - margin - grip_size) + hlocation = THEME_LOCATION_INTERIOR; + else if (x < width - margin) + hlocation = THEME_LOCATION_RESIZING_RIGHT; + else + hlocation = THEME_LOCATION_EXTERIOR; + + if (y < margin) + vlocation = THEME_LOCATION_EXTERIOR; + else if (margin <= y && y < margin + grip_size) + vlocation = THEME_LOCATION_RESIZING_TOP; + else if (y < height - margin - grip_size) + vlocation = THEME_LOCATION_INTERIOR; + else if (y < height - margin) + vlocation = THEME_LOCATION_RESIZING_BOTTOM; + else + vlocation = THEME_LOCATION_EXTERIOR; + + location = vlocation | hlocation; + if (location & THEME_LOCATION_EXTERIOR) + location = THEME_LOCATION_EXTERIOR; + if (location == THEME_LOCATION_INTERIOR && + y < margin + t->titlebar_height) + location = THEME_LOCATION_TITLEBAR; + else if (location == THEME_LOCATION_INTERIOR) + location = THEME_LOCATION_CLIENT_AREA; + + return location; +} diff --git a/shared/cairo-util.h b/shared/cairo-util.h new file mode 100644 index 00000000..7b403944 --- /dev/null +++ b/shared/cairo-util.h @@ -0,0 +1,89 @@ +/* + * Copyright © 2008 Kristian Høgsberg + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef _CAIRO_UTIL_H +#define _CAIRO_UTIL_H + +#include <cairo.h> + +void +surface_flush_device(cairo_surface_t *surface); + +void +tile_mask(cairo_t *cr, cairo_surface_t *surface, + int x, int y, int width, int height, int margin, int top_margin); + +void +tile_source(cairo_t *cr, cairo_surface_t *surface, + int x, int y, int width, int height, int margin, int top_margin); + +void +rounded_rect(cairo_t *cr, int x0, int y0, int x1, int y1, int radius); + +cairo_surface_t * +load_cairo_surface(const char *filename); + +struct theme { + cairo_surface_t *active_frame; + cairo_surface_t *inactive_frame; + cairo_surface_t *shadow; + int frame_radius; + int margin; + int width; + int titlebar_height; +}; + +struct theme * +theme_create(void); +void +theme_destroy(struct theme *t); + +enum { + THEME_FRAME_ACTIVE = 1, + THEME_FRAME_MAXIMIZED, +}; + +void +theme_render_frame(struct theme *t, + cairo_t *cr, int width, int height, + const char *title, uint32_t flags); + +enum theme_location { + THEME_LOCATION_INTERIOR = 0, + THEME_LOCATION_RESIZING_TOP = 1, + THEME_LOCATION_RESIZING_BOTTOM = 2, + THEME_LOCATION_RESIZING_LEFT = 4, + THEME_LOCATION_RESIZING_TOP_LEFT = 5, + THEME_LOCATION_RESIZING_BOTTOM_LEFT = 6, + THEME_LOCATION_RESIZING_RIGHT = 8, + THEME_LOCATION_RESIZING_TOP_RIGHT = 9, + THEME_LOCATION_RESIZING_BOTTOM_RIGHT = 10, + THEME_LOCATION_RESIZING_MASK = 15, + THEME_LOCATION_EXTERIOR = 16, + THEME_LOCATION_TITLEBAR = 17, + THEME_LOCATION_CLIENT_AREA = 18, +}; + +enum theme_location +theme_get_location(struct theme *t, int x, int y, int width, int height, int flags); + +#endif diff --git a/shared/config-parser.c b/shared/config-parser.c new file mode 100644 index 00000000..8defbbb4 --- /dev/null +++ b/shared/config-parser.c @@ -0,0 +1,434 @@ +/* + * Copyright © 2011 Intel Corporation + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include "config.h" + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <ctype.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> + +#include <wayland-util.h> +#include "config-parser.h" + +#define container_of(ptr, type, member) ({ \ + const __typeof__( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +struct weston_config_entry { + char *key; + char *value; + struct wl_list link; +}; + +struct weston_config_section { + char *name; + struct wl_list entry_list; + struct wl_list link; +}; + +struct weston_config { + struct wl_list section_list; + char path[PATH_MAX]; +}; + +static int +open_config_file(struct weston_config *c, const char *name) +{ + const char *config_dir = getenv("XDG_CONFIG_HOME"); + const char *home_dir = getenv("HOME"); + const char *config_dirs = getenv("XDG_CONFIG_DIRS"); + const char *p, *next; + int fd; + + if (name[0] == '/') { + snprintf(c->path, sizeof c->path, "%s", name); + return open(name, O_RDONLY | O_CLOEXEC); + } + + /* Precedence is given to config files in the home directory, + * and then to directories listed in XDG_CONFIG_DIRS and + * finally to the current working directory. */ + + /* $XDG_CONFIG_HOME */ + if (config_dir) { + snprintf(c->path, sizeof c->path, "%s/%s", config_dir, name); + fd = open(c->path, O_RDONLY | O_CLOEXEC); + if (fd >= 0) + return fd; + } + + /* $HOME/.config */ + if (home_dir) { + snprintf(c->path, sizeof c->path, + "%s/.config/%s", home_dir, name); + fd = open(c->path, O_RDONLY | O_CLOEXEC); + if (fd >= 0) + return fd; + } + + /* For each $XDG_CONFIG_DIRS: weston/<config_file> */ + if (!config_dirs) + config_dirs = "/etc/xdg"; /* See XDG base dir spec. */ + + for (p = config_dirs; *p != '\0'; p = next) { + next = strchrnul(p, ':'); + snprintf(c->path, sizeof c->path, + "%.*s/weston/%s", (int)(next - p), p, name); + fd = open(c->path, O_RDONLY | O_CLOEXEC); + if (fd >= 0) + return fd; + + if (*next == ':') + next++; + } + + /* Current working directory. */ + snprintf(c->path, sizeof c->path, "./%s", name); + + return open(c->path, O_RDONLY | O_CLOEXEC); +} + +static struct weston_config_entry * +config_section_get_entry(struct weston_config_section *section, + const char *key) +{ + struct weston_config_entry *e; + + if (section == NULL) + return NULL; + wl_list_for_each(e, §ion->entry_list, link) + if (strcmp(e->key, key) == 0) + return e; + + return NULL; +} + +WL_EXPORT +struct weston_config_section * +weston_config_get_section(struct weston_config *config, const char *section, + const char *key, const char *value) +{ + struct weston_config_section *s; + struct weston_config_entry *e; + + if (config == NULL) + return NULL; + wl_list_for_each(s, &config->section_list, link) { + if (strcmp(s->name, section) != 0) + continue; + if (key == NULL) + return s; + e = config_section_get_entry(s, key); + if (e && strcmp(e->value, value) == 0) + return s; + } + + return NULL; +} + +WL_EXPORT +int +weston_config_section_get_int(struct weston_config_section *section, + const char *key, + int32_t *value, int32_t default_value) +{ + struct weston_config_entry *entry; + char *end; + + entry = config_section_get_entry(section, key); + if (entry == NULL) { + *value = default_value; + errno = ENOENT; + return -1; + } + + *value = strtol(entry->value, &end, 0); + if (*end != '\0') { + *value = default_value; + errno = EINVAL; + return -1; + } + + return 0; +} + +WL_EXPORT +int +weston_config_section_get_uint(struct weston_config_section *section, + const char *key, + uint32_t *value, uint32_t default_value) +{ + struct weston_config_entry *entry; + char *end; + + entry = config_section_get_entry(section, key); + if (entry == NULL) { + *value = default_value; + errno = ENOENT; + return -1; + } + + *value = strtoul(entry->value, &end, 0); + if (*end != '\0') { + *value = default_value; + errno = EINVAL; + return -1; + } + + return 0; +} + +WL_EXPORT +int +weston_config_section_get_double(struct weston_config_section *section, + const char *key, + double *value, double default_value) +{ + struct weston_config_entry *entry; + char *end; + + entry = config_section_get_entry(section, key); + if (entry == NULL) { + *value = default_value; + errno = ENOENT; + return -1; + } + + *value = strtod(entry->value, &end); + if (*end != '\0') { + *value = default_value; + errno = EINVAL; + return -1; + } + + return 0; +} + +WL_EXPORT +int +weston_config_section_get_string(struct weston_config_section *section, + const char *key, + char **value, const char *default_value) +{ + struct weston_config_entry *entry; + + entry = config_section_get_entry(section, key); + if (entry == NULL) { + if (default_value) + *value = strdup(default_value); + else + *value = NULL; + errno = ENOENT; + return -1; + } + + *value = strdup(entry->value); + + return 0; +} + +WL_EXPORT +int +weston_config_section_get_bool(struct weston_config_section *section, + const char *key, + int *value, int default_value) +{ + struct weston_config_entry *entry; + + entry = config_section_get_entry(section, key); + if (entry == NULL) { + *value = default_value; + errno = ENOENT; + return -1; + } + + if (strcmp(entry->value, "false") == 0) + *value = 0; + else if (strcmp(entry->value, "true") == 0) + *value = 1; + else { + *value = default_value; + errno = EINVAL; + return -1; + } + + return 0; +} + +static struct weston_config_section * +config_add_section(struct weston_config *config, const char *name) +{ + struct weston_config_section *section; + + section = malloc(sizeof *section); + section->name = strdup(name); + wl_list_init(§ion->entry_list); + wl_list_insert(config->section_list.prev, §ion->link); + + return section; +} + +static struct weston_config_entry * +section_add_entry(struct weston_config_section *section, + const char *key, const char *value) +{ + struct weston_config_entry *entry; + + entry = malloc(sizeof *entry); + entry->key = strdup(key); + entry->value = strdup(value); + wl_list_insert(section->entry_list.prev, &entry->link); + + return entry; +} + +struct weston_config * +weston_config_parse(const char *name) +{ + FILE *fp; + char line[512], *p; + struct weston_config *config; + struct weston_config_section *section = NULL; + int i, fd; + + config = malloc(sizeof *config); + if (config == NULL) + return NULL; + + wl_list_init(&config->section_list); + + fd = open_config_file(config, name); + if (fd == -1) { + free(config); + return NULL; + } + + fp = fdopen(fd, "r"); + if (fp == NULL) { + free(config); + return NULL; + } + + while (fgets(line, sizeof line, fp)) { + switch (line[0]) { + case '#': + case '\n': + continue; + case '[': + p = strchr(&line[1], ']'); + if (!p || p[1] != '\n') { + fprintf(stderr, "malformed " + "section header: %s\n", line); + fclose(fp); + weston_config_destroy(config); + return NULL; + } + p[0] = '\0'; + section = config_add_section(config, &line[1]); + continue; + default: + p = strchr(line, '='); + if (!p || p == line || !section) { + fprintf(stderr, "malformed " + "config line: %s\n", line); + fclose(fp); + weston_config_destroy(config); + return NULL; + } + + p[0] = '\0'; + p++; + while (isspace(*p)) + p++; + i = strlen(p); + while (i > 0 && isspace(p[i - 1])) { + p[i - 1] = '\0'; + i--; + } + section_add_entry(section, line, p); + continue; + } + } + + fclose(fp); + + return config; +} + +const char * +weston_config_get_full_path(struct weston_config *config) +{ + return config == NULL ? NULL : config->path; +} + +int +weston_config_next_section(struct weston_config *config, + struct weston_config_section **section, + const char **name) +{ + if (config == NULL) + return 0; + + if (*section == NULL) + *section = container_of(config->section_list.next, + struct weston_config_section, link); + else + *section = container_of((*section)->link.next, + struct weston_config_section, link); + + if (&(*section)->link == &config->section_list) + return 0; + + *name = (*section)->name; + + return 1; +} + +void +weston_config_destroy(struct weston_config *config) +{ + struct weston_config_section *s, *next_s; + struct weston_config_entry *e, *next_e; + + if (config == NULL) + return; + + wl_list_for_each_safe(s, next_s, &config->section_list, link) { + wl_list_for_each_safe(e, next_e, &s->entry_list, link) { + free(e->key); + free(e->value); + free(e); + } + free(s->name); + free(s); + } + + free(config); +} diff --git a/shared/config-parser.h b/shared/config-parser.h new file mode 100644 index 00000000..745562bc --- /dev/null +++ b/shared/config-parser.h @@ -0,0 +1,114 @@ +/* + * Copyright © 2008 Kristian Høgsberg + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef CONFIGPARSER_H +#define CONFIGPARSER_H + +#ifdef __cplusplus +extern "C" { +#endif + +enum config_key_type { + CONFIG_KEY_INTEGER, /* typeof data = int */ + CONFIG_KEY_UNSIGNED_INTEGER, /* typeof data = unsigned int */ + CONFIG_KEY_STRING, /* typeof data = char* */ + CONFIG_KEY_BOOLEAN /* typeof data = int */ +}; + +struct config_key { + const char *name; + enum config_key_type type; + void *data; +}; + +struct config_section { + const char *name; + const struct config_key *keys; + int num_keys; + void (*done)(void *data); +}; + +enum weston_option_type { + WESTON_OPTION_INTEGER, + WESTON_OPTION_UNSIGNED_INTEGER, + WESTON_OPTION_STRING, + WESTON_OPTION_BOOLEAN +}; + +struct weston_option { + enum weston_option_type type; + const char *name; + int short_name; + void *data; +}; + +int +parse_options(const struct weston_option *options, + int count, int *argc, char *argv[]); + +struct weston_config_section; +struct weston_config; + +struct weston_config_section * +weston_config_get_section(struct weston_config *config, const char *section, + const char *key, const char *value); +int +weston_config_section_get_int(struct weston_config_section *section, + const char *key, + int32_t *value, int32_t default_value); +int +weston_config_section_get_uint(struct weston_config_section *section, + const char *key, + uint32_t *value, uint32_t default_value); +int +weston_config_section_get_double(struct weston_config_section *section, + const char *key, + double *value, double default_value); +int +weston_config_section_get_string(struct weston_config_section *section, + const char *key, + char **value, + const char *default_value); +int +weston_config_section_get_bool(struct weston_config_section *section, + const char *key, + int *value, int default_value); +struct weston_config * +weston_config_parse(const char *name); + +const char * +weston_config_get_full_path(struct weston_config *config); + +void +weston_config_destroy(struct weston_config *config); + +int weston_config_next_section(struct weston_config *config, + struct weston_config_section **section, + const char **name); + + +#ifdef __cplusplus +} +#endif + +#endif /* CONFIGPARSER_H */ + diff --git a/shared/image-loader.c b/shared/image-loader.c new file mode 100644 index 00000000..35dadd3d --- /dev/null +++ b/shared/image-loader.c @@ -0,0 +1,405 @@ +/* + * Copyright © 2008-2012 Kristian Høgsberg + * Copyright © 2012 Intel Corporation + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <jpeglib.h> +#include <png.h> +#include <pixman.h> + +#include "image-loader.h" + +#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0]) + +#ifdef HAVE_WEBP +#include <webp/decode.h> +#endif + +static int +stride_for_width(int width) +{ + return width * 4; +} + +static void +swizzle_row(JSAMPLE *row, JDIMENSION width) +{ + JSAMPLE *s; + uint32_t *d; + + s = row + (width - 1) * 3; + d = (uint32_t *) (row + (width - 1) * 4); + while (s >= row) { + *d = 0xff000000 | (s[0] << 16) | (s[1] << 8) | (s[2] << 0); + s -= 3; + d--; + } +} + +static void +error_exit(j_common_ptr cinfo) +{ + longjmp(cinfo->client_data, 1); +} + +static void +pixman_image_destroy_func(pixman_image_t *image, void *data) +{ + free(data); +} + +static pixman_image_t * +load_jpeg(FILE *fp) +{ + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + pixman_image_t *pixman_image = NULL; + unsigned int i; + int stride, first; + JSAMPLE *data, *rows[4]; + jmp_buf env; + + cinfo.err = jpeg_std_error(&jerr); + jerr.error_exit = error_exit; + cinfo.client_data = env; + if (setjmp(env)) + return NULL; + + jpeg_create_decompress(&cinfo); + + jpeg_stdio_src(&cinfo, fp); + + jpeg_read_header(&cinfo, TRUE); + + cinfo.out_color_space = JCS_RGB; + jpeg_start_decompress(&cinfo); + + stride = cinfo.output_width * 4; + data = malloc(stride * cinfo.output_height); + if (data == NULL) { + fprintf(stderr, "couldn't allocate image data\n"); + return NULL; + } + + while (cinfo.output_scanline < cinfo.output_height) { + first = cinfo.output_scanline; + for (i = 0; i < ARRAY_LENGTH(rows); i++) + rows[i] = data + (first + i) * stride; + + jpeg_read_scanlines(&cinfo, rows, ARRAY_LENGTH(rows)); + for (i = 0; first + i < cinfo.output_scanline; i++) + swizzle_row(rows[i], cinfo.output_width); + } + + jpeg_finish_decompress(&cinfo); + + jpeg_destroy_decompress(&cinfo); + + pixman_image = pixman_image_create_bits(PIXMAN_a8r8g8b8, + cinfo.output_width, + cinfo.output_height, + (uint32_t *) data, stride); + + pixman_image_set_destroy_function(pixman_image, + pixman_image_destroy_func, data); + + return pixman_image; +} + +static inline int +multiply_alpha(int alpha, int color) +{ + int temp = (alpha * color) + 0x80; + + return ((temp + (temp >> 8)) >> 8); +} + +static void +premultiply_data(png_structp png, + png_row_infop row_info, + png_bytep data) +{ + unsigned int i; + png_bytep p; + + for (i = 0, p = data; i < row_info->rowbytes; i += 4, p += 4) { + png_byte alpha = p[3]; + uint32_t w; + + if (alpha == 0) { + w = 0; + } else { + png_byte red = p[0]; + png_byte green = p[1]; + png_byte blue = p[2]; + + if (alpha != 0xff) { + red = multiply_alpha(alpha, red); + green = multiply_alpha(alpha, green); + blue = multiply_alpha(alpha, blue); + } + w = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0); + } + + * (uint32_t *) p = w; + } +} + +static void +read_func(png_structp png, png_bytep data, png_size_t size) +{ + FILE *fp = png_get_io_ptr(png); + + if (fread(data, 1, size, fp) != size) + png_error(png, NULL); +} + +static void +png_error_callback(png_structp png, png_const_charp error_msg) +{ + longjmp (png_jmpbuf (png), 1); +} + +static pixman_image_t * +load_png(FILE *fp) +{ + png_struct *png; + png_info *info; + png_byte *data = NULL; + png_byte **row_pointers = NULL; + png_uint_32 width, height; + int depth, color_type, interlace, stride; + unsigned int i; + pixman_image_t *pixman_image = NULL; + + png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, + png_error_callback, NULL); + if (!png) + return NULL; + + info = png_create_info_struct(png); + if (!info) { + png_destroy_read_struct(&png, &info, NULL); + return NULL; + } + + if (setjmp(png_jmpbuf(png))) { + if (data) + free(data); + if (row_pointers) + free(row_pointers); + png_destroy_read_struct(&png, &info, NULL); + return NULL; + } + + png_set_read_fn(png, fp, read_func); + png_read_info(png, info); + png_get_IHDR(png, info, + &width, &height, &depth, + &color_type, &interlace, NULL, NULL); + + if (color_type == PNG_COLOR_TYPE_PALETTE) + png_set_palette_to_rgb(png); + + if (color_type == PNG_COLOR_TYPE_GRAY) + png_set_expand_gray_1_2_4_to_8(png); + + if (png_get_valid(png, info, PNG_INFO_tRNS)) + png_set_tRNS_to_alpha(png); + + if (depth == 16) + png_set_strip_16(png); + + if (depth < 8) + png_set_packing(png); + + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb(png); + + if (interlace != PNG_INTERLACE_NONE) + png_set_interlace_handling(png); + + png_set_filler(png, 0xff, PNG_FILLER_AFTER); + png_set_read_user_transform_fn(png, premultiply_data); + png_read_update_info(png, info); + png_get_IHDR(png, info, + &width, &height, &depth, + &color_type, &interlace, NULL, NULL); + + + stride = stride_for_width(width); + data = malloc(stride * height); + if (!data) { + png_destroy_read_struct(&png, &info, NULL); + return NULL; + } + + row_pointers = malloc(height * sizeof row_pointers[0]); + if (row_pointers == NULL) { + free(data); + png_destroy_read_struct(&png, &info, NULL); + return NULL; + } + + for (i = 0; i < height; i++) + row_pointers[i] = &data[i * stride]; + + png_read_image(png, row_pointers); + png_read_end(png, info); + + free(row_pointers); + png_destroy_read_struct(&png, &info, NULL); + + pixman_image = pixman_image_create_bits(PIXMAN_a8r8g8b8, + width, height, (uint32_t *) data, stride); + + pixman_image_set_destroy_function(pixman_image, + pixman_image_destroy_func, data); + + return pixman_image; +} + +#ifdef HAVE_WEBP + +static pixman_image_t * +load_webp(FILE *fp) +{ + WebPDecoderConfig config; + uint8_t buffer[16 * 1024]; + int len; + VP8StatusCode status; + WebPIDecoder *idec; + + if (!WebPInitDecoderConfig(&config)) { + fprintf(stderr, "Library version mismatch!\n"); + return NULL; + } + + /* webp decoding api doesn't seem to specify a min size that's + usable for GetFeatures, but 256 works... */ + len = fread(buffer, 1, 256, fp); + status = WebPGetFeatures(buffer, len, &config.input); + if (status != VP8_STATUS_OK) { + fprintf(stderr, "failed to parse webp header\n"); + WebPFreeDecBuffer(&config.output); + return NULL; + } + + config.output.colorspace = MODE_BGRA; + config.output.u.RGBA.stride = stride_for_width(config.input.width); + config.output.u.RGBA.size = + config.output.u.RGBA.stride * config.input.height; + config.output.u.RGBA.rgba = + malloc(config.output.u.RGBA.stride * config.input.height); + config.output.is_external_memory = 1; + if (!config.output.u.RGBA.rgba) { + WebPFreeDecBuffer(&config.output); + return NULL; + } + + rewind(fp); + idec = WebPINewDecoder(&config.output); + if (!idec) { + WebPFreeDecBuffer(&config.output); + return NULL; + } + + while (!feof(fp)) { + len = fread(buffer, 1, sizeof buffer, fp); + status = WebPIAppend(idec, buffer, len); + if (status != VP8_STATUS_OK) { + fprintf(stderr, "webp decode status %d\n", status); + WebPIDelete(idec); + WebPFreeDecBuffer(&config.output); + return NULL; + } + } + + WebPIDelete(idec); + WebPFreeDecBuffer(&config.output); + + return pixman_image_create_bits(PIXMAN_a8r8g8b8, + config.input.width, + config.input.height, + (uint32_t *) config.output.u.RGBA.rgba, + config.output.u.RGBA.stride); +} + +#endif + + +struct image_loader { + unsigned char header[4]; + int header_size; + pixman_image_t *(*load)(FILE *fp); +}; + +static const struct image_loader loaders[] = { + { { 0x89, 'P', 'N', 'G' }, 4, load_png }, + { { 0xff, 0xd8 }, 2, load_jpeg }, +#ifdef HAVE_WEBP + { { 'R', 'I', 'F', 'F' }, 4, load_webp } +#endif +}; + +pixman_image_t * +load_image(const char *filename) +{ + pixman_image_t *image; + unsigned char header[4]; + FILE *fp; + unsigned int i; + + fp = fopen(filename, "rb"); + if (fp == NULL) + return NULL; + + if (fread(header, sizeof header, 1, fp) != 1) { + fclose(fp); + return NULL; + } + + rewind(fp); + for (i = 0; i < ARRAY_LENGTH(loaders); i++) { + if (memcmp(header, loaders[i].header, + loaders[i].header_size) == 0) { + image = loaders[i].load(fp); + break; + } + } + + fclose(fp); + + if (i == ARRAY_LENGTH(loaders)) { + fprintf(stderr, "unrecognized file header for %s: " + "0x%02x 0x%02x 0x%02x 0x%02x\n", + filename, header[0], header[1], header[2], header[3]); + image = NULL; + } + + return image; +} diff --git a/shared/image-loader.h b/shared/image-loader.h new file mode 100644 index 00000000..445e651e --- /dev/null +++ b/shared/image-loader.h @@ -0,0 +1,31 @@ +/* + * Copyright © 2013 Intel Corporation + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef _IMAGE_LOADER_H +#define _IMAGE_LOADER_H + +#include <pixman.h> + +pixman_image_t * +load_image(const char *filename); + +#endif diff --git a/shared/matrix.c b/shared/matrix.c new file mode 100644 index 00000000..4f0b6b79 --- /dev/null +++ b/shared/matrix.c @@ -0,0 +1,273 @@ +/* + * Copyright © 2011 Intel Corporation + * Copyright © 2012 Collabora, Ltd. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "config.h" + +#include <float.h> +#include <string.h> +#include <stdlib.h> +#include <math.h> + +#ifdef IN_WESTON +#include <wayland-server.h> +#else +#define WL_EXPORT +#endif + +#include "matrix.h" + + +/* + * Matrices are stored in column-major order, that is the array indices are: + * 0 4 8 12 + * 1 5 9 13 + * 2 6 10 14 + * 3 7 11 15 + */ + +WL_EXPORT void +weston_matrix_init(struct weston_matrix *matrix) +{ + static const struct weston_matrix identity = { + .d = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }, + .type = 0, + }; + + memcpy(matrix, &identity, sizeof identity); +} + +/* m <- n * m, that is, m is multiplied on the LEFT. */ +WL_EXPORT void +weston_matrix_multiply(struct weston_matrix *m, const struct weston_matrix *n) +{ + struct weston_matrix tmp; + const float *row, *column; + div_t d; + int i, j; + + for (i = 0; i < 16; i++) { + tmp.d[i] = 0; + d = div(i, 4); + row = m->d + d.quot * 4; + column = n->d + d.rem; + for (j = 0; j < 4; j++) + tmp.d[i] += row[j] * column[j * 4]; + } + tmp.type = m->type | n->type; + memcpy(m, &tmp, sizeof tmp); +} + +WL_EXPORT void +weston_matrix_translate(struct weston_matrix *matrix, float x, float y, float z) +{ + struct weston_matrix translate = { + .d = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1 }, + .type = WESTON_MATRIX_TRANSFORM_TRANSLATE, + }; + + weston_matrix_multiply(matrix, &translate); +} + +WL_EXPORT void +weston_matrix_scale(struct weston_matrix *matrix, float x, float y,float z) +{ + struct weston_matrix scale = { + .d = { x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1 }, + .type = WESTON_MATRIX_TRANSFORM_SCALE, + }; + + weston_matrix_multiply(matrix, &scale); +} + +WL_EXPORT void +weston_matrix_rotate_xy(struct weston_matrix *matrix, float cos, float sin) +{ + struct weston_matrix translate = { + .d = { cos, sin, 0, 0, -sin, cos, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }, + .type = WESTON_MATRIX_TRANSFORM_ROTATE, + }; + + weston_matrix_multiply(matrix, &translate); +} + +/* v <- m * v */ +WL_EXPORT void +weston_matrix_transform(struct weston_matrix *matrix, struct weston_vector *v) +{ + int i, j; + struct weston_vector t; + + for (i = 0; i < 4; i++) { + t.f[i] = 0; + for (j = 0; j < 4; j++) + t.f[i] += v->f[j] * matrix->d[i + j * 4]; + } + + *v = t; +} + +static inline void +swap_rows(double *a, double *b) +{ + unsigned k; + double tmp; + + for (k = 0; k < 13; k += 4) { + tmp = a[k]; + a[k] = b[k]; + b[k] = tmp; + } +} + +static inline void +swap_unsigned(unsigned *a, unsigned *b) +{ + unsigned tmp; + + tmp = *a; + *a = *b; + *b = tmp; +} + +static inline unsigned +find_pivot(double *column, unsigned k) +{ + unsigned p = k; + for (++k; k < 4; ++k) + if (fabs(column[p]) < fabs(column[k])) + p = k; + + return p; +} + +/* + * reference: Gene H. Golub and Charles F. van Loan. Matrix computations. + * 3rd ed. The Johns Hopkins University Press. 1996. + * LU decomposition, forward and back substitution: Chapter 3. + */ + +MATRIX_TEST_EXPORT inline int +matrix_invert(double *A, unsigned *p, const struct weston_matrix *matrix) +{ + unsigned i, j, k; + unsigned pivot; + double pv; + + for (i = 0; i < 4; ++i) + p[i] = i; + for (i = 16; i--; ) + A[i] = matrix->d[i]; + + /* LU decomposition with partial pivoting */ + for (k = 0; k < 4; ++k) { + pivot = find_pivot(&A[k * 4], k); + if (pivot != k) { + swap_unsigned(&p[k], &p[pivot]); + swap_rows(&A[k], &A[pivot]); + } + + pv = A[k * 4 + k]; + if (fabs(pv) < 1e-9) + return -1; /* zero pivot, not invertible */ + + for (i = k + 1; i < 4; ++i) { + A[i + k * 4] /= pv; + + for (j = k + 1; j < 4; ++j) + A[i + j * 4] -= A[i + k * 4] * A[k + j * 4]; + } + } + + return 0; +} + +MATRIX_TEST_EXPORT inline void +inverse_transform(const double *LU, const unsigned *p, float *v) +{ + /* Solve A * x = v, when we have P * A = L * U. + * P * A * x = P * v => L * U * x = P * v + * Let U * x = b, then L * b = P * v. + */ + double b[4]; + unsigned j; + + /* Forward substitution, column version, solves L * b = P * v */ + /* The diagonal of L is all ones, and not explicitly stored. */ + b[0] = v[p[0]]; + b[1] = (double)v[p[1]] - b[0] * LU[1 + 0 * 4]; + b[2] = (double)v[p[2]] - b[0] * LU[2 + 0 * 4]; + b[3] = (double)v[p[3]] - b[0] * LU[3 + 0 * 4]; + b[2] -= b[1] * LU[2 + 1 * 4]; + b[3] -= b[1] * LU[3 + 1 * 4]; + b[3] -= b[2] * LU[3 + 2 * 4]; + + /* backward substitution, column version, solves U * y = b */ +#if 1 + /* hand-unrolled, 25% faster for whole function */ + b[3] /= LU[3 + 3 * 4]; + b[0] -= b[3] * LU[0 + 3 * 4]; + b[1] -= b[3] * LU[1 + 3 * 4]; + b[2] -= b[3] * LU[2 + 3 * 4]; + + b[2] /= LU[2 + 2 * 4]; + b[0] -= b[2] * LU[0 + 2 * 4]; + b[1] -= b[2] * LU[1 + 2 * 4]; + + b[1] /= LU[1 + 1 * 4]; + b[0] -= b[1] * LU[0 + 1 * 4]; + + b[0] /= LU[0 + 0 * 4]; +#else + for (j = 3; j > 0; --j) { + unsigned k; + b[j] /= LU[j + j * 4]; + for (k = 0; k < j; ++k) + b[k] -= b[j] * LU[k + j * 4]; + } + + b[0] /= LU[0 + 0 * 4]; +#endif + + /* the result */ + for (j = 0; j < 4; ++j) + v[j] = b[j]; +} + +WL_EXPORT int +weston_matrix_invert(struct weston_matrix *inverse, + const struct weston_matrix *matrix) +{ + double LU[16]; /* column-major */ + unsigned perm[4]; /* permutation */ + unsigned c; + + if (matrix_invert(LU, perm, matrix) < 0) + return -1; + + weston_matrix_init(inverse); + for (c = 0; c < 4; ++c) + inverse_transform(LU, perm, &inverse->d[c * 4]); + inverse->type = matrix->type; + + return 0; +} diff --git a/shared/matrix.h b/shared/matrix.h new file mode 100644 index 00000000..e5cf636b --- /dev/null +++ b/shared/matrix.h @@ -0,0 +1,82 @@ +/* + * Copyright © 2008-2011 Kristian Høgsberg + * Copyright © 2012 Collabora, Ltd. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WESTON_MATRIX_H +#define WESTON_MATRIX_H + +#ifdef __cplusplus +extern "C" { +#endif + +enum weston_matrix_transform_type { + WESTON_MATRIX_TRANSFORM_TRANSLATE = (1 << 0), + WESTON_MATRIX_TRANSFORM_SCALE = (1 << 1), + WESTON_MATRIX_TRANSFORM_ROTATE = (1 << 2), + WESTON_MATRIX_TRANSFORM_OTHER = (1 << 3), +}; + +struct weston_matrix { + float d[16]; + unsigned int type; +}; + +struct weston_vector { + float f[4]; +}; + +void +weston_matrix_init(struct weston_matrix *matrix); +void +weston_matrix_multiply(struct weston_matrix *m, const struct weston_matrix *n); +void +weston_matrix_scale(struct weston_matrix *matrix, float x, float y, float z); +void +weston_matrix_translate(struct weston_matrix *matrix, + float x, float y, float z); +void +weston_matrix_rotate_xy(struct weston_matrix *matrix, float cos, float sin); +void +weston_matrix_transform(struct weston_matrix *matrix, struct weston_vector *v); + +int +weston_matrix_invert(struct weston_matrix *inverse, + const struct weston_matrix *matrix); + +#ifdef UNIT_TEST +# define MATRIX_TEST_EXPORT WL_EXPORT + +int +matrix_invert(double *A, unsigned *p, const struct weston_matrix *matrix); + +void +inverse_transform(const double *LU, const unsigned *p, float *v); + +#else +# define MATRIX_TEST_EXPORT static +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* WESTON_MATRIX_H */ diff --git a/shared/option-parser.c b/shared/option-parser.c new file mode 100644 index 00000000..c00349a8 --- /dev/null +++ b/shared/option-parser.c @@ -0,0 +1,85 @@ +/* + * Copyright © 2012 Kristian Høgsberg + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include "config.h" + +#include <stdlib.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> + +#include "config-parser.h" + +static void +handle_option(const struct weston_option *option, char *value) +{ + switch (option->type) { + case WESTON_OPTION_INTEGER: + * (int32_t *) option->data = strtol(value, NULL, 0); + return; + case WESTON_OPTION_UNSIGNED_INTEGER: + * (uint32_t *) option->data = strtoul(value, NULL, 0); + return; + case WESTON_OPTION_STRING: + * (char **) option->data = strdup(value); + return; + case WESTON_OPTION_BOOLEAN: + * (int32_t *) option->data = 1; + return; + default: + assert(0); + } +} + +int +parse_options(const struct weston_option *options, + int count, int *argc, char *argv[]) +{ + int i, j, k, len = 0; + + for (i = 1, j = 1; i < *argc; i++) { + for (k = 0; k < count; k++) { + if (options[k].name) + len = strlen(options[k].name); + if (options[k].name && + argv[i][0] == '-' && + argv[i][1] == '-' && + strncmp(options[k].name, &argv[i][2], len) == 0 && + (argv[i][len + 2] == '=' || argv[i][len + 2] == '\0')) { + handle_option(&options[k], &argv[i][len + 3]); + break; + } else if (options[k].short_name && + argv[i][0] == '-' && + options[k].short_name == argv[i][1]) { + handle_option(&options[k], &argv[i][2]); + break; + } + } + if (k == count) + argv[j++] = argv[i]; + } + argv[j] = NULL; + *argc = j; + + return j; +} diff --git a/shared/os-compatibility.c b/shared/os-compatibility.c new file mode 100644 index 00000000..4f96dd4c --- /dev/null +++ b/shared/os-compatibility.c @@ -0,0 +1,180 @@ +/* + * Copyright © 2012 Collabora, Ltd. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include "config.h" + +#include <sys/types.h> +#include <sys/socket.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/epoll.h> +#include <string.h> +#include <stdlib.h> + +#include "os-compatibility.h" + +static int +set_cloexec_or_close(int fd) +{ + long flags; + + if (fd == -1) + return -1; + + flags = fcntl(fd, F_GETFD); + if (flags == -1) + goto err; + + if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) + goto err; + + return fd; + +err: + close(fd); + return -1; +} + +int +os_socketpair_cloexec(int domain, int type, int protocol, int *sv) +{ + int ret; + +#ifdef SOCK_CLOEXEC + ret = socketpair(domain, type | SOCK_CLOEXEC, protocol, sv); + if (ret == 0 || errno != EINVAL) + return ret; +#endif + + ret = socketpair(domain, type, protocol, sv); + if (ret < 0) + return ret; + + sv[0] = set_cloexec_or_close(sv[0]); + sv[1] = set_cloexec_or_close(sv[1]); + + if (sv[0] != -1 && sv[1] != -1) + return 0; + + close(sv[0]); + close(sv[1]); + return -1; +} + +int +os_epoll_create_cloexec(void) +{ + int fd; + +#ifdef EPOLL_CLOEXEC + fd = epoll_create1(EPOLL_CLOEXEC); + if (fd >= 0) + return fd; + if (errno != EINVAL) + return -1; +#endif + + fd = epoll_create(1); + return set_cloexec_or_close(fd); +} + +static int +create_tmpfile_cloexec(char *tmpname) +{ + int fd; + +#ifdef HAVE_MKOSTEMP + fd = mkostemp(tmpname, O_CLOEXEC); + if (fd >= 0) + unlink(tmpname); +#else + fd = mkstemp(tmpname); + if (fd >= 0) { + fd = set_cloexec_or_close(fd); + unlink(tmpname); + } +#endif + + return fd; +} + +/* + * Create a new, unique, anonymous file of the given size, and + * return the file descriptor for it. The file descriptor is set + * CLOEXEC. The file is immediately suitable for mmap()'ing + * the given size at offset zero. + * + * The file should not have a permanent backing store like a disk, + * but may have if XDG_RUNTIME_DIR is not properly implemented in OS. + * + * The file name is deleted from the file system. + * + * The file is suitable for buffer sharing between processes by + * transmitting the file descriptor over Unix sockets using the + * SCM_RIGHTS methods. + */ +int +os_create_anonymous_file(off_t size) +{ + static const char template[] = "/weston-shared-XXXXXX"; + const char *path; + char *name; + int fd; + + path = getenv("XDG_RUNTIME_DIR"); + if (!path) { + errno = ENOENT; + return -1; + } + + name = malloc(strlen(path) + sizeof(template)); + if (!name) + return -1; + + strcpy(name, path); + strcat(name, template); + + fd = create_tmpfile_cloexec(name); + + free(name); + + if (fd < 0) + return -1; + + if (ftruncate(fd, size) < 0) { + close(fd); + return -1; + } + + return fd; +} + +#ifndef HAVE_STRCHRNUL +char * +strchrnul(const char *s, int c) +{ + while (*s && *s != c) + s++; + return (char *)s; +} +#endif diff --git a/shared/os-compatibility.h b/shared/os-compatibility.h new file mode 100644 index 00000000..c1edcfbd --- /dev/null +++ b/shared/os-compatibility.h @@ -0,0 +1,54 @@ +/* + * Copyright © 2012 Collabora, Ltd. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef OS_COMPATIBILITY_H +#define OS_COMPATIBILITY_H + +#include <sys/types.h> + +#include "../config.h" + +#ifdef HAVE_EXECINFO_H +#include <execinfo.h> +#else +static inline int +backtrace(void **buffer, int size) +{ + return 0; +} +#endif + +int +os_socketpair_cloexec(int domain, int type, int protocol, int *sv); + +int +os_epoll_create_cloexec(void); + +int +os_create_anonymous_file(off_t size); + +#ifndef HAVE_STRCHRNUL +char * +strchrnul(const char *s, int c); +#endif + +#endif /* OS_COMPATIBILITY_H */ diff --git a/shared/zalloc.h b/shared/zalloc.h new file mode 100644 index 00000000..29da1190 --- /dev/null +++ b/shared/zalloc.h @@ -0,0 +1,42 @@ +/* + * Copyright © 2013 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WESTON_ZALLOC_H +#define WESTON_ZALLOC_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdlib.h> + +static inline void * +zalloc(size_t size) +{ + return calloc(1, size); +} + +#ifdef __cplusplus +} +#endif + +#endif /* WESTON_ZALLOC_H */ |