diff options
author | Aaron Plattner <aplattner@nvidia.com> | 2013-04-09 09:00:10 -0700 |
---|---|---|
committer | Aaron Plattner <aplattner@nvidia.com> | 2013-04-09 09:00:10 -0700 |
commit | 8600b2a00d39fef61123f5eec972d8508740e460 (patch) | |
tree | d024db1b882cf4a8e714a1392347df694dc5b675 | |
parent | 031b1b3c7e9e555420a48e9817abf06d26d5ebf8 (diff) |
319.12319.12
104 files changed, 20293 insertions, 6224 deletions
diff --git a/doc/nvidia-settings.1.m4 b/doc/nvidia-settings.1.m4 index b98da21..790ded8 100644 --- a/doc/nvidia-settings.1.m4 +++ b/doc/nvidia-settings.1.m4 @@ -222,32 +222,23 @@ for a complete list of available attributes, what the current value is, what val Additionally, individual attributes may be specified like this: .nf - nvidia-settings --query CursorShadow + nvidia-settings --query Overlay .fi -Attributes that may differ per display device (for example, DigitalVibrance can be set independently on each display device when in TwinView) can be appended with a "display device name" within brackets; e.g.: +An attribute name may be prepended with an X Display name and a forward slash to indicate a different X Display; e.g.: .nf - nvidia-settings --query DigitalVibrance[CRT-0] - -.fi -If an attribute is display device specific, but the query does not specify a display device, then the attribute value for all display devices will be queried. -.PP -An attribute name may be prepended with an X Display name and a forward slash -to indicate a different X Display; e.g.: -.nf - - nvidia-settings --query localhost:0.0/DigitalVibrance[DFP-1] + nvidia-settings --query localhost:0.0/Overlay .fi An attribute name may also just be prepended with the screen number and a forward slash: .nf - nvidia-settings --query 0/DigitalVibrance[DFP-1] + nvidia-settings --query 0/Overlay .fi in which case the default X Display will be used, but you can indicate to which X screen to direct the query (if your X server has multiple X screens). -If no X screen is specified, then the attribute value will be queried for all X screens. +If no X screen is specified, then the attribute value will be queried for all valid targets of the attribute (eg GPUs, Displays X screens, etc). .PP Attributes can be addressed through "target types". A target type indicates the object that is queried when you query an attribute. @@ -256,15 +247,18 @@ The default target type is an X screen, but other possible target types are GPUs Target types give you different granularities with which to perform queries and assignments. Since X screens can span multiple GPUs (in the case of Xinerama, or SLI), and multiple X screens can exist on the same GPU, it is sometimes useful to address attributes by GPU rather than X screen. .PP -A target specification is contained within brackets and consists of a target type name, a colon, and the target id. +A target specification is contained within brackets and may consist of a target type name, a colon, and the target id. The target type name can be one of .B screen, .B gpu, .B framelock, .B vcs, .B gvi, +.B fan, +.B thermalsensor, +.B svp, or -.B fan; +.B dpy; the target id is the index into the list of targets (for that target type). Target specifications can be used wherever an X screen is used in query and assignment commands; the target specification can be used either by itself on the left side of the forward slash, or as part of an X Display name. .PP @@ -284,6 +278,53 @@ To address GPU 0 instead, you can use either of: nvidia-settings --query localhost:0[gpu:0]/VideoRam .fi +Note that if a target specification is present, it will override any X screen specified in the display name as the target to process. +For example, the following query would address GPU 0, and not X screen 1: +.nf + + nvidia-settings --query localhost:0.1[gpu:0]/VideoRam + +.fi +.PP +A target name may be used instead of a target id, in which case all targets with matching names are processed. +.PP +For example, querying the DigitalVibrance of display device DVI-I-1 may be done like so: +.nf + + nvidia-settings --query [dpy:DVI-I-1]/DigitalVibrance + +.fi +When a target name is specified, the target type name may be omitted, though this should be used with caution since the name will be matched across all target types. The above example could be written as: +.nf + + nvidia-settings --query [DVI-I-1]/DigitalVibrance + +.fi +The target name may also simply be a target type name, in which case all targets of that type will be queried. +.PP +For exmple, querying the BusRate of all GPUs may be done like so: +.nf + + nvidia-settings --query [gpu]/BusRate + +.fi +.PP +The target specification may also include a target qualifier. This is useful to limit processing to a subset of targets, based on an existing relationship(s) to other targets. +The target qualifier is specified by prepending a target type name, a colon, the target id, and a period to the existing specification. Only one qualitfer may be specified. +.PP +For example, querying the RefreshRate of all DFP devices on GPU 1 may be done like so: +.nf + + nvidia-settings --query [GPU:1.DPY:DFP]/RefreshRate + +.fi +Likewise, a simple target name (or target type name) may be used as the qualifier. For example, to query the BusType of all GPUs that have DFPs can be done like so: +.nf + + nvidia-settings --query [DFP.GPU]/BusType + +.fi +.PP See the output of .nf @@ -312,7 +353,7 @@ For example: .nf nvidia-settings --assign FSAA=2 - nvidia-settings --assign 0/DigitalVibrance[CRT-1]=9 + nvidia-settings --assign [CRT-1]/DigitalVibrance=9 nvidia-settings --assign [gpu:0]/DigitalVibrance=0 .fi .PP diff --git a/doc/version.mk b/doc/version.mk index d8ad3ea..69efa2d 100644 --- a/doc/version.mk +++ b/doc/version.mk @@ -1 +1 @@ -NVIDIA_VERSION = 313.30 +NVIDIA_VERSION = 319.12 diff --git a/samples/nv-control-events.c b/samples/nv-control-events.c index ffaee69..1b34b44 100644 --- a/samples/nv-control-events.c +++ b/samples/nv-control-events.c @@ -529,13 +529,6 @@ static AttrEntry attr_table[] = { MAKE_ENTRY(NV_CTRL_FLIPPING_ALLOWED), MAKE_ENTRY(NV_CTRL_ARCHITECTURE), MAKE_ENTRY(NV_CTRL_TEXTURE_CLAMPING), - MAKE_ENTRY(NV_CTRL_CURSOR_SHADOW), - MAKE_ENTRY(NV_CTRL_CURSOR_SHADOW_ALPHA), - MAKE_ENTRY(NV_CTRL_CURSOR_SHADOW_RED), - MAKE_ENTRY(NV_CTRL_CURSOR_SHADOW_GREEN), - MAKE_ENTRY(NV_CTRL_CURSOR_SHADOW_BLUE), - MAKE_ENTRY(NV_CTRL_CURSOR_SHADOW_X_OFFSET), - MAKE_ENTRY(NV_CTRL_CURSOR_SHADOW_Y_OFFSET), MAKE_ENTRY(NV_CTRL_FSAA_APPLICATION_CONTROLLED), MAKE_ENTRY(NV_CTRL_LOG_ANISO_APPLICATION_CONTROLLED), MAKE_ENTRY(NV_CTRL_IMAGE_SHARPENING), diff --git a/samples/nv-control-info.c b/samples/nv-control-info.c index 4fba32a..913c246 100644 --- a/samples/nv-control-info.c +++ b/samples/nv-control-info.c @@ -112,13 +112,6 @@ static AttrEntry attr_int_table[] = { MAKE_ENTRY(NV_CTRL_FLIPPING_ALLOWED), MAKE_ENTRY(NV_CTRL_ARCHITECTURE), MAKE_ENTRY(NV_CTRL_TEXTURE_CLAMPING), - MAKE_ENTRY(NV_CTRL_CURSOR_SHADOW), - MAKE_ENTRY(NV_CTRL_CURSOR_SHADOW_ALPHA), - MAKE_ENTRY(NV_CTRL_CURSOR_SHADOW_RED), - MAKE_ENTRY(NV_CTRL_CURSOR_SHADOW_GREEN), - MAKE_ENTRY(NV_CTRL_CURSOR_SHADOW_BLUE), - MAKE_ENTRY(NV_CTRL_CURSOR_SHADOW_X_OFFSET), - MAKE_ENTRY(NV_CTRL_CURSOR_SHADOW_Y_OFFSET), MAKE_ENTRY(NV_CTRL_FSAA_APPLICATION_CONTROLLED), MAKE_ENTRY(NV_CTRL_LOG_ANISO_APPLICATION_CONTROLLED), MAKE_ENTRY(NV_CTRL_IMAGE_SHARPENING), diff --git a/samples/version.mk b/samples/version.mk index d8ad3ea..69efa2d 100644 --- a/samples/version.mk +++ b/samples/version.mk @@ -1 +1 @@ -NVIDIA_VERSION = 313.30 +NVIDIA_VERSION = 319.12 diff --git a/src/Makefile b/src/Makefile index 8a2ff04..c5da53a 100644 --- a/src/Makefile +++ b/src/Makefile @@ -42,8 +42,6 @@ endif X_CFLAGS ?= -GL_INCLUDE_PATH ?= /usr/include - PKG_CONFIG ?= pkg-config ifndef GTK_CFLAGS @@ -54,22 +52,37 @@ ifndef GTK_LDFLAGS GTK_LDFLAGS := $(shell $(PKG_CONFIG) --libs gtk+-2.0) endif +ifndef VDPAU_CFLAGS + VDPAU_CFLAGS := $(shell $(PKG_CONFIG) --cflags vdpau 2> /dev/null) + ifeq ($(VDPAU_CFLAGS),) + VDPAU_CFLAGS = -I /usr/include + endif +endif + +ifndef JANSSON_CFLAGS + JANSSON_CFLAGS = -Wno-cast-qual + ifeq ($(TARGET_ARCH),armv7l) + JANSSON_CFLAGS += -Wno-unused-but-set-variable + endif +endif ############################################################################## # The XF86Config-parser, libXNVCtrl, and common-utils directories may # be in one of two places: either elsewhere in the driver source tree # when building nvidia-settings as part of the NVIDIA driver build (in -# which case, XNVCTRL_DIR, XNVCTRL_ARCHIVE, XCONFIG_PARSER_DIR and -# COMMON_UTILS_DIR should be defined by the calling makefile), or -# directly in the source directory when building from the +# which case, XNVCTRL_DIR, XNVCTRL_ARCHIVE, XCONFIG_PARSER_DIR, +# COMMON_UTILS_DIR and COMMON_UNIX_DIR should be defined by the calling +# makefile), or directly in the source directory when building from the # nvidia-settings source tarball (in which case, the below conditional # assignments should be used) ############################################################################## -XNVCTRL_DIR ?= libXNVCtrl -XNVCTRL_ARCHIVE ?= $(XNVCTRL_DIR)/libXNVCtrl.a -XCONFIG_PARSER_DIR ?= XF86Config-parser -COMMON_UTILS_DIR ?= common-utils +XNVCTRL_DIR ?= libXNVCtrl +XNVCTRL_ARCHIVE ?= $(XNVCTRL_DIR)/libXNVCtrl.a +XCONFIG_PARSER_DIR ?= XF86Config-parser +COMMON_UTILS_DIR ?= common-utils +COMMON_UNIX_DIR ?= common-unix +VIRTUAL_RESOLUTIONS_DIR ?= $(COMMON_UNIX_DIR)/virtual-resolutions ############################################################################## # assign variables @@ -85,6 +98,7 @@ CFLAGS += $(X_CFLAGS) ifeq ($(TARGET_OS),SunOS) LDFLAGS += -Wl,-rpath=/usr/X11R6/lib + LIBS += -lscf endif LDFLAGS += $(X_LDFLAGS) @@ -125,6 +139,9 @@ SRC += $(addprefix $(XCONFIG_PARSER_DIR)/,$(XCONFIG_PARSER_SRC)) include $(COMMON_UTILS_DIR)/src.mk SRC += $(addprefix $(COMMON_UTILS_DIR)/,$(COMMON_UTILS_SRC)) +include $(VIRTUAL_RESOLUTIONS_DIR)/src.mk +SRC += $(addprefix $(VIRTUAL_RESOLUTIONS_DIR)/,$(VIRTUAL_RESOLUTIONS_SRC)) + SRC += $(STAMP_C) OBJS = $(call BUILD_OBJECT_LIST,$(SRC)) @@ -136,12 +153,17 @@ CFLAGS += -I $(XCONFIG_PARSER_DIR)/.. CFLAGS += -I libXNVCtrlAttributes CFLAGS += -I xpm_data CFLAGS += -I gtk+-2.x +CFLAGS += -I jansson CFLAGS += -I $(COMMON_UTILS_DIR) +CFLAGS += -I $(VIRTUAL_RESOLUTIONS_DIR) CFLAGS += -I $(OUTPUTDIR) +CFLAGS += $(VDPAU_CFLAGS) CFLAGS += -DPROGRAM_NAME=\"nvidia-setttings\" $(call BUILD_OBJECT_LIST,$(GTK_SRC)): CFLAGS += $(GTK_CFLAGS) +$(call BUILD_OBJECT_LIST,$(JANSSON_SRC)): CFLAGS += $(JANSSON_CFLAGS) + ############################################################################## # build rules diff --git a/src/app-profiles.c b/src/app-profiles.c new file mode 100644 index 0000000..4b37006 --- /dev/null +++ b/src/app-profiles.c @@ -0,0 +1,2188 @@ +/* + * nvidia-settings: A tool for configuring the NVIDIA X driver on Unix + * and Linux systems. + * + * Copyright (C) 2013 NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses>. + */ + +/* + * app-profiles.c - this source file contains functions for querying and + * assigning application profile settings, as well as parsing and saving + * application profile configuration files. + */ + +#define _GNU_SOURCE + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> +#include <dirent.h> +#include <ctype.h> +#include <time.h> +#include "common-utils.h" +#include "app-profiles.h" +#include "msg.h" + +static char *slurp(FILE *fp) +{ + int eof = FALSE; + char *text = strdup(""); + char *new_text; + char *line = NULL; + + while (text && !eof) { + line = fget_next_line(fp, &eof); + if (!eof) { + new_text = nvstrcat(text, "\n", line, NULL); + free(text); + text = new_text; + } + } + + free(line); + + return text; +} + +static void splice_string(char **s, size_t b, size_t e, const char *replace) +{ + char *tail = strdup(*s + e); + *s = realloc(*s, b + strlen(replace) + strlen(tail) + 1); + if (!*s) { + return; + } + sprintf(*s + b, "%s%s", replace, tail); + free(tail); +} + +#define HEX_DIGITS "0123456789abcdefABCDEF" + +char *nv_app_profile_cfg_file_syntax_to_json(const char *orig_s) +{ + char *s = strdup(orig_s); + + int quoted = FALSE; + char *tok; + size_t start, end, size; + unsigned long long val; + char *old_substr = NULL; + char *endptr; + char *new_substr = NULL; + + tok = s; + while ((tok = strpbrk(tok, "\\\"#" HEX_DIGITS))) { + switch (*tok) { + case '\"': + // Quotation mark + quoted = !quoted; + tok++; + break; + case '\\': + // Escaped character + tok++; + if (*tok) { + tok++; + } + break; + case '#': + // Comment + if (!quoted) { + char *end_tok = nvstrchrnul(tok, '\n'); + start = tok - s; + end = end_tok - s; + splice_string(&s, start, end, ""); + if (!s) { + goto fail; + } + tok = s + start; + } else { + tok++; + } + break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + // Numeric value + size = strspn(tok, "Xx." HEX_DIGITS); + if ((tok[0] == '0') && + (tok[1] == 'x' || tok[1] == 'X' || isdigit(tok[1])) && + !quoted) { + old_substr = nvstrndup(tok, size); + if (!old_substr) { + goto fail; + } + errno = 0; + val = strtoull(old_substr, &endptr, 0); + if (errno || (endptr - old_substr != strlen(old_substr))) { + // Invalid conversion, skip this string + tok += size; + free(old_substr); old_substr = NULL; + } else { + new_substr = nvasprintf("%llu", val); + if (!new_substr) { + goto fail; + } else { + start = tok - s; + end = tok - s + size; + splice_string(&s, start, end, new_substr); + free(new_substr); new_substr = NULL; + free(old_substr); old_substr = NULL; + tok = s + start; + } + } + } else { + // Not hex or octal; let the JSON parser deal with it + tok += size; + } + break; + default: + assert(!"Unhandled character"); + break; + } + } + + assert(!new_substr); + assert(!old_substr); + return s; + +fail: + free(old_substr); + free(new_substr); + free(s); + return NULL; +} + +static int open_and_stat(const char *filename, const char *perms, FILE **fp, struct stat *stat_buf) +{ + int ret; + *fp = fopen(filename, perms); + if (!*fp) { + if (errno != ENOENT) { + nv_error_msg("Could not open file %s (%s)", filename, strerror(errno)); + } + return -1; + } + + ret = fstat(fileno(*fp), stat_buf); + if (ret == -1) { + nv_error_msg("Could not stat file %s (%s)", filename, strerror(errno)); + fclose(*fp); + } + return ret; +} + +static char *nv_dirname(const char *path) +{ + char *last_slash = strrchr(path, '/'); + if (last_slash) { + return nvstrndup(path, last_slash - path); + } else { + return nvstrdup("."); + } +} + +static char *nv_basename(const char *path) +{ + char *last_slash = strrchr(path, '/'); + if (last_slash) { + return strdup(last_slash+1); + } else { + return strdup(path); + } +} + +static json_t *app_profile_config_insert_file_object(AppProfileConfig *config, json_t *new_file) +{ + json_t *json_filename, *json_new_filename; + char *dirname; + const char *filename, *new_filename; + json_t *file; + json_t *order, *file_order; + size_t new_file_major, new_file_minor, file_order_major, file_order_minor; + size_t i; + size_t num_files; + + json_new_filename = json_object_get(new_file, "filename"); + + assert(json_new_filename); + new_filename = json_string_value(json_new_filename); + + assert(nv_app_profile_config_check_valid_source_file(config, + new_filename, + NULL)); + + // Determine the correct location of the file in the search path + dirname = NULL; + + new_file_major = -1; + for (i = 0; i < config->search_path_count; i++) { + if (!strcmp(new_filename, config->search_path[i])) { + new_file_major = i; + break; + } else { + if (!dirname) { + dirname = nv_dirname(new_filename); + } + if (!strcmp(dirname, config->search_path[i])) { + new_file_major = i; + break; + } + } + } + free(dirname); + + new_file_minor = 0; + num_files = json_array_size(config->parsed_files); + + for (i = 0; i < num_files; i++) { + file = json_array_get(config->parsed_files, i); + file_order = json_object_get(file, "order"); + file_order_major = json_integer_value(json_object_get(file_order, "major")); + file_order_minor = json_integer_value(json_object_get(file_order, "minor")); + json_filename = json_object_get(file, "filename"); + assert(json_filename); + filename = json_string_value(json_filename); + if (file_order_major < new_file_major) { + } else if (file_order_major == new_file_major) { + if (strcoll(filename, new_filename) > 0) { + break; + } + new_file_minor++; + } else { + break; + } + } + + // Mark the order of the file + order = json_object_get(new_file, "order"); + json_object_set_new(order, "major", json_integer(new_file_major)); + json_object_set_new(order, "minor", json_integer(new_file_minor)); + + // Add the new file + json_array_insert(config->parsed_files, i, new_file); + + // Bump up minor for files after this one with the same major + num_files = json_array_size(config->parsed_files); + + for ( ; i < num_files; i++) { + file = json_array_get(config->parsed_files, i); + file_order = json_object_get(file, "order"); + file_order_major = json_integer_value(json_object_get(order, "major")); + file_order_minor = json_integer_value(json_object_get(order, "minor")); + if (file_order_major > new_file_major) { + break; + } + json_object_set_new(file_order, "minor", json_integer(file_order_minor+1)); + } + + return new_file; +} + + +/* + * Create a new empty file object and adds it to the configuration. + */ +static json_t *app_profile_config_new_file(AppProfileConfig *config, + const char *filename) +{ + json_t *new_file = json_object(); + + json_object_set_new(new_file, "filename", json_string(filename)); + json_object_set_new(new_file, "rules", json_array()); + json_object_set_new(new_file, "profiles", json_object()); + json_object_set_new(new_file, "dirty", json_false()); + json_object_set_new(new_file, "new", json_true()); + // order is set by app_profile_config_insert_file_object() below + + new_file = app_profile_config_insert_file_object(config, new_file); + + return new_file; +} + +static char *rule_id_to_key_string(int id) +{ + char *key; + key = nvasprintf("%d", id); + return key; +} + +/* + * Constructs a profile name that is guaranteed to be unique to this + * configuration. This is used to handle the case where there are multiple + * profiles with the same name (an invalid configuration). + */ +static char *app_profile_config_unique_profile_name(AppProfileConfig *config, + const char *orig_name, + const char *filename, + int do_warn, + int *needs_dirty) +{ + json_t *json_gold_filename = json_object_get(config->profile_locations, orig_name); + + if (json_gold_filename) { + int i = 0; + char *new_name = NULL; + do { + free(new_name); + new_name = nvasprintf("%s_duplicate_%d", orig_name, i++); + } while (new_name && json_object_get(config->profile_locations, new_name)); + if (do_warn) { + nv_error_msg("The profile \"%s\" in the file \"%s\" has the same name " + "as a profile defined in the file \"%s\", and will be renamed to \"%s\".", + orig_name, filename, json_string_value(json_gold_filename), new_name); + } + if (needs_dirty) { + *needs_dirty = TRUE; + } + return new_name; + } else { + return strdup(orig_name); + } +} + + +char *nv_app_profile_config_get_unused_profile_name(AppProfileConfig *config) +{ + char *temp_name, *unique_name; + int salt = rand(); + + temp_name = nvasprintf("profile_%x", salt); + unique_name = app_profile_config_unique_profile_name(config, + temp_name, + NULL, + FALSE, + NULL); + + free(temp_name); + return unique_name; +} + +static json_t *json_settings_parse(json_t *old_settings, const char *filename) +{ + int uses_setting_objects; + size_t i, size; + json_t *old_setting; + json_t *new_settings, *new_setting; + json_t *json_key, *json_value; + + if (!json_is_array(old_settings)) { + return NULL; + } + + new_settings = json_array(); + + uses_setting_objects = json_array_size(old_settings) && + json_is_object(json_array_get(old_settings, 0)); + + for (i = 0, size = json_array_size(old_settings); i < size; ) { + old_setting = json_array_get(old_settings, i++); + if (uses_setting_objects) { + json_key = json_object_get(old_setting, "key"); + if (!json_key) { + json_key = json_object_get(old_setting, "k"); + } + json_value = json_object_get(old_setting, "value"); + if (!json_value) { + json_value = json_object_get(old_setting, "v"); + } + } else { + if (i >= size) { + nv_error_msg("App profile parse error in %s: Key/value array of odd length\n", filename); + json_decref(new_settings); + return NULL; + } + json_key = old_setting; + json_value = json_array_get(old_settings, i++); + } + + if (!json_is_string(json_key)) { + nv_error_msg("App profile parse error in %s: Invalid key detected in settings array\n", filename); + json_decref(new_settings); + return NULL; + } + + if (!json_is_integer(json_value) && + !json_is_real(json_value) && + !json_is_true(json_value) && + !json_is_false(json_value) && + !json_is_string(json_value)) { + nv_error_msg("App profile parse error in %s: Invalid value detected in settings array\n", filename); + json_decref(new_settings); + return NULL; + } + new_setting = json_object(); + json_object_set(new_setting, "key", json_key); + json_object_set(new_setting, "value", json_value); + json_array_append_new(new_settings, new_setting); + } + + return new_settings; +} + +/* + * Load app profile settings from an already-open file. This operation is + * atomic: either all of the settings from the file are added to the + * configuration, or none are. + */ +static void app_profile_config_load_file(AppProfileConfig *config, + const char *filename, + struct stat *stat_buf, + FILE *fp) +{ + char *json_text = NULL; + char *orig_text = NULL; + size_t i, size; + json_error_t error; + json_t *orig_file = NULL; + json_t *orig_json_profiles, *orig_json_rules; + int next_free_rule_id = config->next_free_rule_id; + int dirty = FALSE; + json_t *new_file = NULL; + json_t *new_json_profiles = NULL; + json_t *new_json_rules = NULL; + + if (!S_ISREG(stat_buf->st_mode)) { + // Silently ignore all but regular files + goto done; + } + + orig_text = slurp(fp); + + if (!orig_text) { + nv_error_msg("Could not read from file %s", filename); + goto done; + } + + // Convert the file contents to JSON + json_text = nv_app_profile_cfg_file_syntax_to_json(orig_text); + + if (!json_text) { + nv_error_msg("App profile parse error in %s: text is not valid app profile configuration syntax", filename); + goto done; + } + + new_file = json_object(); + + json_object_set_new(new_file, "dirty", json_false()); + json_object_set_new(new_file, "filename", json_string(filename)); + + new_json_profiles = json_object(); + new_json_rules = json_array(); + + // Parse the resulting JSON + orig_file = json_loads(json_text, 0, &error); + + if (!orig_file) { + nv_error_msg("App profile parse error in %s: %s on %s, line %d\n", + filename, error.text, error.source, error.line); + goto done; + } + + if (!json_is_object(orig_file)) { + nv_error_msg("App profile parse error in %s: top-level config not an object!\n", filename); + goto done; + } + + orig_json_profiles = json_object_get(orig_file, "profiles"); + + if (orig_json_profiles) { + /* + * Note: we store profiles internally as members of an object, but the + * config file syntax uses an array to store profiles. + */ + if (!json_is_array(orig_json_profiles)) { + nv_error_msg("App profile parse error in %s: profiles value is not an array\n", filename); + goto done; + } + + size = json_array_size(orig_json_profiles); + for (i = 0; i < size; i++) { + const char *new_name; + json_t *orig_json_profile, *orig_json_name, *orig_json_settings; + json_t *new_json_profile, *new_json_settings; + + new_json_profile = json_object(); + + orig_json_profile = json_array_get(orig_json_profiles, i); + if (!json_is_object(orig_json_profile)) { + goto done; + } + + orig_json_name = json_object_get(orig_json_profile, "name"); + if (!json_is_string(orig_json_name)) { + goto done; + } + + orig_json_settings = json_object_get(orig_json_profile, "settings"); + new_json_settings = json_settings_parse(orig_json_settings, filename); + if (!new_json_settings) { + goto done; + } + + new_name = app_profile_config_unique_profile_name(config, + json_string_value(orig_json_name), + filename, + TRUE, + &dirty); + json_object_set_new(new_json_profile, "settings", new_json_settings); + + json_object_set_new(new_json_profiles, new_name, new_json_profile); + } + } + + orig_json_rules = json_object_get(orig_file, "rules"); + + if (orig_json_rules) { + if (!json_is_array(orig_json_rules)) { + nv_error_msg("App profile parse error in %s: rules value is not an array\n", filename); + goto done; + } + + size = json_array_size(orig_json_rules); + for (i = 0; i < size; i++) { + int new_id; + char *profile_name; + json_t *orig_json_rule, *orig_json_pattern, *orig_json_profile; + json_t *new_json_rule, *new_json_pattern; + orig_json_rule = json_array_get(orig_json_rules, i); + + if (!json_is_object(orig_json_rule)) { + goto done; + } + + new_id = next_free_rule_id++; + + new_json_rule = json_object(); + new_json_pattern = json_object(); + + orig_json_pattern = json_object_get(orig_json_rule, "pattern"); + if (json_is_object(orig_json_pattern)) { + // pattern object + json_t *orig_json_feature, *orig_json_matches; + orig_json_feature = json_object_get(orig_json_pattern, "feature"); + if (!json_is_string(orig_json_feature)) { + json_decref(new_json_rule); + json_decref(new_json_pattern); + goto done; + } + orig_json_matches = json_object_get(orig_json_pattern, "matches"); + if (!json_is_string(orig_json_matches)) { + json_decref(new_json_rule); + json_decref(new_json_pattern); + goto done; + } + json_object_set(new_json_pattern, "feature", orig_json_feature); + json_object_set(new_json_pattern, "matches", orig_json_matches); + } else if (json_is_string(orig_json_pattern)) { + // procname + json_object_set_new(new_json_pattern, "feature", json_string("procname")); + json_object_set(new_json_pattern, "matches", orig_json_pattern); + } else { + json_decref(new_json_rule); + json_decref(new_json_pattern); + goto done; + } + + json_object_set_new(new_json_rule, "pattern", new_json_pattern); + + orig_json_profile = json_object_get(orig_json_rule, "profile"); + if (json_is_object(orig_json_profile) || json_is_array(orig_json_profile)) { + // inline profile object + json_t *new_json_profile; + json_t *orig_json_settings, *orig_json_name; + json_t *new_json_settings; + + if (json_is_object(orig_json_profile)) { + orig_json_settings = json_object_get(orig_json_profile, "settings"); + orig_json_name = json_object_get(orig_json_profile, "name"); + } else { + // must be array + orig_json_settings = orig_json_profile; + orig_json_name = NULL; + } + + if (json_is_string(orig_json_name)) { + profile_name = app_profile_config_unique_profile_name(config, + json_string_value(orig_json_name), + filename, + TRUE, + &dirty); + } else if (!orig_json_name) { + char *profile_name_template; + profile_name_template = nvasprintf("inline_%d", new_id); + profile_name = app_profile_config_unique_profile_name(config, + profile_name_template, + filename, + FALSE, + &dirty); + free(profile_name_template); + } else { + json_decref(new_json_rule); + goto done; + } + + new_json_settings = json_settings_parse(orig_json_settings, filename); + + if (!profile_name || !new_json_settings) { + free(profile_name); + json_decref(new_json_settings); + json_decref(new_json_rule); + goto done; + } + + new_json_profile = json_object(); + json_object_set_new(new_json_profile, "settings", new_json_settings); + + json_object_set_new(new_json_profiles, profile_name, new_json_profile); + } else if (json_is_string(orig_json_profile)) { + // named profile + profile_name = strdup(json_string_value(orig_json_profile)); + } else { + json_decref(new_json_rule); + goto done; + } + + json_object_set_new(new_json_rule, "profile", json_string(profile_name)); + free(profile_name); + + json_object_set_new(new_json_rule, "id", json_integer(new_id)); + + json_array_append_new(new_json_rules, new_json_rule); + } + } + + json_object_set(new_file, "profiles", new_json_profiles); + json_object_set(new_file, "rules", new_json_rules); + json_object_set_new(new_file, "dirty", dirty ? json_true() : json_false()); + json_object_set_new(new_file, "new", json_false()); + + // Don't use the atime in the stat_buf; instead measure it here + json_object_set_new(new_file, "atime", json_integer(time(NULL))); + + // If we didn't fail anywhere above, add the file to our configuration + app_profile_config_insert_file_object(config, new_file); + + // Add the profiles in this file to the global profiles list + { + const char *key; + json_t *value; + json_object_foreach(new_json_profiles, key, value) { + json_object_set_new(config->profile_locations, key, json_string(filename)); + } + } + + // Add the rules in this file to the global rules list + size = json_array_size(new_json_rules); + for (i = 0; i < size; i++) { + char *key; + json_t *new_rule; + + new_rule = json_array_get(new_json_rules, i); + key = rule_id_to_key_string(json_integer_value(json_object_get(new_rule, "id"))); + json_object_set_new(config->rule_locations, key, json_string(filename)); + free(key); + } + config->next_free_rule_id = next_free_rule_id; + +done: + json_decref(orig_file); + json_decref(new_file); + json_decref(new_json_rules); + json_decref(new_json_profiles); + free(json_text); + free(orig_text); +} + +// Load app profile settings from a directory +static void app_profile_config_load_files_from_directory(AppProfileConfig *config, + const char *dirname) +{ + FILE *fp; + struct stat stat_buf; + struct dirent **namelist; + int i, n, ret; + + n = scandir(dirname, &namelist, NULL, alphasort); + + if (n < 0) { + nv_error_msg("Failed to open directory \"%s\"", dirname); + return; + } + + for (i = 0; i < n; i++) { + char *d_name = namelist[i]->d_name; + char *full_path; + + // Skip "." and ".." + if ((d_name[0] == '.') && + ((d_name[1] == '\0') || + ((d_name[1] == '.') && (d_name[2] == '\0')))) { + continue; + } + + full_path = nvstrcat(dirname, "/", d_name, NULL); + ret = open_and_stat(full_path, "r", &fp, &stat_buf); + + if (ret < 0) { + free(full_path); + free(namelist[i]); + continue; + } + + app_profile_config_load_file(config, + full_path, + &stat_buf, + fp); + fclose(fp); + free(full_path); + free(namelist[i]); + } + + free(namelist); +} + +static json_t *app_profile_config_load_global_options(const char *global_config_file) +{ + json_error_t error; + json_t *options = json_object(); + int ret; + FILE *fp; + struct stat stat_buf; + char *option_text; + json_t *options_from_file; + json_t *option; + + // By default, app profiles are disabled + json_object_set_new(options, "enabled", json_false()); + + if (!global_config_file) { + return options; + } + + ret = open_and_stat(global_config_file, "r", &fp, &stat_buf); + if ((ret < 0) || !S_ISREG(stat_buf.st_mode)) { + return options; + } + + option_text = slurp(fp); + fclose(fp); + + options_from_file = json_loads(option_text, 0, &error); + free(option_text); + + if (!options_from_file) { + nv_error_msg("App profile parse error in %s: %s on %s, line %d\n", + global_config_file, error.text, error.source, error.line); + return options; + } + + // Load the "enabled" option + option = json_object_get(options_from_file, "enabled"); + if (option && (json_is_true(option) || json_is_false(option))) { + json_object_set(options, "enabled", option); + } + + json_decref(options_from_file); + + return options; +} + +AppProfileConfig *nv_app_profile_config_load(const char *global_config_file, + char **search_path, + size_t search_path_count) +{ + size_t i; + AppProfileConfig *config = malloc(sizeof(AppProfileConfig)); + + if (!config) { + return NULL; + } + + // Initialize the config + config->next_free_rule_id = 0; + + config->parsed_files = json_array(); + config->profile_locations = json_object(); + config->rule_locations = json_object(); + + if (global_config_file) { + config->global_config_file = nvstrdup(global_config_file); + } else { + config->global_config_file = NULL; + } + + config->global_options = app_profile_config_load_global_options(global_config_file); + + config->search_path = malloc(sizeof(char *) * search_path_count); + config->search_path_count = search_path_count; + + for (i = 0; i < search_path_count; i++) { + config->search_path[i] = strdup(search_path[i]); + } + + for (i = 0; i < search_path_count; i++) { + int ret; + struct stat stat_buf; + const char *filename = search_path[i]; + FILE *fp; + + ret = open_and_stat(filename, "r", &fp, &stat_buf); + if (ret < 0) { + continue; + } + + if (S_ISDIR(stat_buf.st_mode)) { + // Parse files in the directory + fclose(fp); + app_profile_config_load_files_from_directory(config, filename); + } else { + // Load the individual file + app_profile_config_load_file(config, filename, &stat_buf, fp); + fclose(fp); + continue; + } + } + + return config; +} + +static int file_in_search_path(AppProfileConfig *config, const char *filename) +{ + size_t i; + for (i = 0; i < config->search_path_count; i++) { + if (!strcmp(filename, config->search_path[i])) { + return TRUE; + } + } + + return FALSE; +} + +/* + * Creates parent directories as needed, similarly to "mkdir -p" + */ +static int nv_mkdirp(const char *dirname) +{ + int ret = 0; + char *parent_name; + const char *cur, *next; + struct stat stat_buf; + cur = dirname; + + while (*cur && (next = strchr(cur + 1, '/'))) { + parent_name = nvstrndup(dirname, next - dirname); + ret = mkdir(parent_name, 0777); + if ((ret < 0) && (errno != EEXIST)) { + nv_error_msg("Could not create parent directory \"%s\" for full path \"%s\" (%s)", + parent_name, dirname, strerror(errno)); + free(parent_name); + return ret; + } + cur = next; + free(parent_name); + } + + ret = mkdir(dirname, 0777); + if (ret < 0) { + if (errno != EEXIST) { + nv_error_msg("Could not create directory \"%s\" (%s)", + dirname, strerror(errno)); + } else { + ret = stat(dirname, &stat_buf); + if (ret == 0) { + if (!S_ISDIR(stat_buf.st_mode)) { + nv_error_msg("Could not create directory \"%s\" (file " + "exists, but not as a directory)", + dirname); + ret = -1; + } + } + } + } + + return ret; +} + +char *nv_app_profile_config_get_backup_filename(AppProfileConfig *config, const char *filename) +{ + char *basename = NULL; + char *dirname = NULL; + char *backup_name = NULL; + + if ((config->global_config_file && + !strcmp(config->global_config_file, filename)) || + file_in_search_path(config, filename)) { + // Files in the top-level search path, and the global config file, can + // be renamed from "$FILE" to "$FILE.backup" without affecting the + // configuration + backup_name = nvasprintf("%s.backup", filename); + } else { + // Files in a search path directory *cannot* be renamed from "$FILE" to + // "$FILE.backup" without affecting the configuration due to the search + // path rules. Instead, attempt to move them to a subdirectory called + // ".backup". + dirname = nv_dirname(filename); + basename = nv_basename(filename); + assert(file_in_search_path(config, dirname)); + backup_name = nvasprintf("%s/.backup/%s", dirname, basename); + } + + free(dirname); + free(basename); + return backup_name; +} + +static int app_profile_config_backup_file(AppProfileConfig *config, + const char *filename) +{ + int ret; + char *backup_name = nv_app_profile_config_get_backup_filename(config, filename); + char *backup_dirname = nv_dirname(backup_name); + + ret = nv_mkdirp(backup_dirname); + if (ret < 0) { + nv_error_msg("Could not create backup directory \"%s\" (%s)", + backup_name, strerror(errno)); + goto done; + } + + ret = rename(filename, backup_name); + if (ret < 0) { + if (errno == ENOENT) { + // Clear the error; the file does not exist + ret = 0; + } else { + nv_error_msg("Could not rename file \"%s\" to \"%s\" for backup (%s)", + filename, backup_name, strerror(errno)); + } + } + + nv_info_msg("", "Backing up configuration file \"%s\" as \"%s\"\n", filename, backup_name); + +done: + free(backup_dirname); + free(backup_name); + return ret; +} + +static int app_profile_config_save_updates_to_file(AppProfileConfig *config, + const char *filename, + const char *update_text, + int backup) +{ + int file_is_new = FALSE; + struct stat stat_buf; + char *dirname = NULL; + FILE *fp; + int ret; + + ret = stat(filename, &stat_buf); + + if ((ret < 0) && (errno != ENOENT)) { + nv_error_msg("Could not stat file \"%s\" (%s)", filename, strerror(errno)); + goto done; + } else if ((ret < 0) && (errno == ENOENT)) { + file_is_new = TRUE; + // Check if the prefix is in the search path + dirname = nv_dirname(filename); + + if (file_in_search_path(config, dirname)) { + // This file is in a directory in the search path + ret = stat(dirname, &stat_buf); + if ((ret < 0) && (errno != ENOENT)) { + nv_error_msg("Could not stat file \"%s\" (%s)", dirname, strerror(errno)); + goto done; + } else if ((ret < 0) && errno == ENOENT) { + // Attempt to create the directory in the search path + ret = nv_mkdirp(dirname); + if (ret < 0) { + goto done; + } + } else if (S_ISREG(stat_buf.st_mode)) { + // If the search path entry is currently a regular file, + // unlink it and create a directory instead + if (backup) { + ret = app_profile_config_backup_file(config, dirname); + if (ret < 0) { + goto done; + } + } + ret = unlink(dirname); + if (ret < 0) { + nv_error_msg("Could not remove the file \"%s\" (%s)", dirname, strerror(errno)); + goto done; + } + ret = nv_mkdirp(dirname); + if (ret < 0) { + goto done; + } + } + } else { + // Attempt to create parent directories for this file + ret = nv_mkdirp(dirname); + if (ret < 0) { + goto done; + } + } + } else if (!S_ISREG(stat_buf.st_mode)) { + // XXX: if this is a directory, we could recursively remove files here, + // but that seems a little dangerous. Instead, complain and bail out + // here. + ret = -1; + nv_error_msg("Refusing to write to file \"%s\" since it is not a regular file", filename); + } + + if (!file_is_new && backup) { + ret = app_profile_config_backup_file(config, filename); + if (ret < 0) { + goto done; + } + } + ret = open_and_stat(filename, "w", &fp, &stat_buf); + if (ret < 0) { + nv_error_msg("Could not write to the file \"%s\" (%s)", filename, strerror(errno)); + goto done; + } + nv_info_msg("", "Writing to configuration file \"%s\"\n", filename); + fprintf(fp, "%s\n", update_text); + fclose(fp); + +done: + free(dirname); + return ret; +} + +int nv_app_profile_config_save_updates(AppProfileConfig *config, json_t *updates, int backup) +{ + json_t *update; + const char *filename; + const char *update_text; + size_t i, size; + int ret = 0; + + for (i = 0, size = json_array_size(updates); (ret == 0) && (i < size); i++) { + update = json_array_get(updates, i); + filename = json_string_value(json_object_get(update, "filename")); + update_text = json_string_value(json_object_get(update, "text")); + ret = app_profile_config_save_updates_to_file(config, filename, update_text, backup); + } + + assert(ret <= 0); + + return ret; +} + +AppProfileConfig *nv_app_profile_config_dup(AppProfileConfig *config) +{ + size_t i; + AppProfileConfig *new_config; + + new_config = malloc(sizeof(AppProfileConfig)); + new_config->parsed_files = json_deep_copy(config->parsed_files); + new_config->profile_locations = json_deep_copy(config->profile_locations); + new_config->rule_locations = json_deep_copy(config->rule_locations); + new_config->next_free_rule_id = config->next_free_rule_id; + + new_config->global_config_file = + config->global_config_file ? strdup(config->global_config_file) : NULL; + new_config->global_options = json_deep_copy(config->global_options); + + new_config->search_path = malloc(sizeof(char *) * config->search_path_count); + new_config->search_path_count = config->search_path_count; + + for (i = 0; i < config->search_path_count; i++) { + new_config->search_path[i] = strdup(config->search_path[i]); + } + + return new_config; +} + +void nv_app_profile_config_set_enabled(AppProfileConfig *config, + int enabled) +{ + json_t *global_options = config->global_options; + + json_object_set_new(global_options, "enabled", + enabled ? json_true() : json_false()); +} + +int nv_app_profile_config_get_enabled(AppProfileConfig *config) +{ + json_t *global_options = config->global_options; + json_t *enabled_json; + + enabled_json = json_object_get(global_options, "enabled"); + assert(enabled_json); + + return json_is_true(enabled_json); +} + +void nv_app_profile_config_free(AppProfileConfig *config) +{ + size_t i; + json_decref(config->global_options); + json_decref(config->parsed_files); + json_decref(config->profile_locations); + json_decref(config->rule_locations); + + for (i = 0; i < config->search_path_count; i++) { + free(config->search_path[i]); + } + + free(config->search_path); + free(config->global_config_file); + + free(config); +} + +static json_t *app_profile_config_lookup_file(AppProfileConfig *config, const char *filename) +{ + size_t i, size; + json_t *json_file, *json_filename; + + size = json_array_size(config->parsed_files); + + for (i = 0; i < size; i++) { + json_file = json_array_get(config->parsed_files, i); + json_filename = json_object_get(json_file, "filename"); + if (!strcmp(json_string_value(json_filename), filename)) { + return json_file; + } + } + + return NULL; +} + +static void app_profile_config_delete_file(AppProfileConfig *config, const char *filename) +{ + size_t i, size; + json_t *json_file, *json_filename; + + size = json_array_size(config->parsed_files); + + for (i = 0; i < size; i++) { + json_file = json_array_get(config->parsed_files, i); + json_filename = json_object_get(json_file, "filename"); + if (!strcmp(json_string_value(json_filename), filename)) { + json_array_remove(config->parsed_files, i); + return; + } + } +} + +static void app_profile_config_get_per_file_config(AppProfileConfig *config, + const char *filename, + json_t **file, + json_t **rules, + json_t **profiles) +{ + *file = app_profile_config_lookup_file(config, filename); + + if (!*file) { + *rules = NULL; + *profiles = NULL; + } else { + *rules = json_object_get(*file, "rules"); + *profiles = json_object_get(*file, "profiles"); + } +} + +/* + * Convert the internal representation of an application profile to a + * representation suitable for writing to disk. + */ +static json_t *app_profile_config_profile_output(const char *profile_name, const json_t *orig_profile) +{ + json_t *new_profile = json_object(); + + json_object_set_new(new_profile, "name", json_string(profile_name)); + json_object_set(new_profile, "settings", json_object_get(orig_profile, "settings")); + + return new_profile; +} + +static json_t *app_profile_config_rule_output(const json_t *orig_rule) +{ + json_t *new_rule = json_object(); + + json_object_set(new_rule, "pattern", json_object_get(orig_rule, "pattern")); + json_object_set(new_rule, "profile", json_object_get(orig_rule, "profile")); + + return new_rule; +} + +static char *config_to_cfg_file_syntax(json_t *old_rules, json_t *old_profiles) +{ + char *output = NULL; + const char *profile_name; + json_t *root, *rules_array, *profiles_array; + json_t *old_rule, *old_profile; + json_t *new_rule, *new_profile; + size_t i, size; + + root = json_object(); + if (!root) { + goto fail; + } + + rules_array = json_array(); + if (!rules_array) { + goto fail; + } + json_object_set_new(root, "rules", rules_array); + + profiles_array = json_array(); + if (!profiles_array) { + goto fail; + } + json_object_set_new(root, "profiles", profiles_array); + + if (old_rules) { + size = json_array_size(old_rules); + for (i = 0; i < size; i++) { + old_rule = json_array_get(old_rules, i); + new_rule = app_profile_config_rule_output(old_rule); + json_array_append_new(rules_array, new_rule); + } + } + + if (old_profiles) { + json_object_foreach(old_profiles, profile_name, old_profile) { + new_profile = app_profile_config_profile_output(profile_name, old_profile); + json_array_append_new(profiles_array, new_profile); + } + } + + output = json_dumps(root, JSON_ENSURE_ASCII | JSON_INDENT(4)); + +fail: + json_decref(root); + return output; +} + +static void add_files_from_config(AppProfileConfig *config, json_t *all_files, json_t *changed_files) +{ + json_t *file, *filename; + size_t i, size; + for (i = 0, size = json_array_size(config->parsed_files); i < size; i++) { + file = json_array_get(config->parsed_files, i); + filename = json_object_get(file, "filename"); + json_object_set_new(all_files, json_string_value(filename), json_true()); + if (json_is_true(json_object_get(file, "dirty"))) { + json_object_set_new(changed_files, json_string_value(filename), json_true()); + } + } +} + +static json_t *app_profile_config_validate_global_options(AppProfileConfig *new_config, + AppProfileConfig *old_config) +{ + json_t *update = NULL; + char *option_text; + + assert((!new_config->global_config_file && !old_config->global_config_file) || + (!strcmp(new_config->global_config_file, old_config->global_config_file))); + + if (new_config->global_config_file && + !json_equal(new_config->global_options, old_config->global_options)) { + update = json_object(); + json_object_set_new(update, "filename", json_string(new_config->global_config_file)); + option_text = json_dumps(new_config->global_options, JSON_ENSURE_ASCII | JSON_INDENT(4)); + json_object_set_new(update, "text", json_string(option_text)); + free(option_text); + } + + return update; +} + +json_t *nv_app_profile_config_validate(AppProfileConfig *new_config, + AppProfileConfig *old_config) +{ + json_t *all_files, *changed_files; + json_t *new_file, *new_rules, *old_rules; + json_t *old_file, *new_profiles, *old_profiles; + json_t *updates, *update; + const char *filename; + char *update_text; + json_t *unused; + + updates = json_array(); + + // Determine if the global config file needs to be updated + update = app_profile_config_validate_global_options(new_config, old_config); + if (update) { + json_array_append_new(updates, update); + } + + // Build a set of files to examine: this is the union of files specified + // by the old configuration and the new. + all_files = json_object(); + changed_files = json_object(); + add_files_from_config(new_config, all_files, changed_files); + add_files_from_config(old_config, all_files, changed_files); + + // For each file in the set, determine if it needs to be updated + json_object_foreach(all_files, filename, unused) { + app_profile_config_get_per_file_config(new_config, filename, &new_file, &new_rules, &new_profiles); + app_profile_config_get_per_file_config(old_config, filename, &old_file, &old_rules, &old_profiles); + + // Simply compare the JSON objects + if (!json_equal(old_rules, new_rules) || !json_equal(old_profiles, new_profiles)) { + json_object_set_new(changed_files, filename, json_true()); + } + } + + // For each file that changed, generate an update record with the new JSON + json_object_foreach(changed_files, filename, unused) { + update = json_object(); + + json_object_set_new(update, "filename", json_string(filename)); + app_profile_config_get_per_file_config(new_config, filename, &new_file, &new_rules, &new_profiles); + + update_text = config_to_cfg_file_syntax(new_rules, new_profiles); + json_object_set_new(update, "text", json_string(update_text)); + + json_array_append_new(updates, update); + free(update_text); + } + + json_decref(all_files); + json_decref(changed_files); + + return updates; +} + +static int file_object_is_empty(const json_t *file) +{ + const json_t *rules; + const json_t *profiles; + + rules = json_object_get(file, "rules"); + profiles = json_object_get(file, "profiles"); + + return (!json_array_size(rules) && !json_object_size(profiles)); +} + +/* + * Checks whether the given file is "empty" (contains no rules and profiles) + * and "new" (created in the configuration and not loaded from disk), and + * removes it from the configuration if both criteria are satisfied. + */ +static void app_profile_config_prune_empty_file(AppProfileConfig *config, const json_t *file) +{ + char *filename; + if (json_is_true(json_object_get(file, "new")) && file_object_is_empty(file)) { + filename = strdup(json_string_value(json_object_get(file, "filename"))); + app_profile_config_delete_file(config, filename); + free(filename); + } +} + +int nv_app_profile_config_update_profile(AppProfileConfig *config, + const char *filename, + const char *profile_name, + json_t *new_profile) +{ + json_t *file; + json_t *old_file = NULL; + json_t *file_profiles; + const char *old_filename; + + old_filename = json_string_value(json_object_get(config->profile_locations, profile_name)); + + if (old_filename) { + // Existing profile + old_file = app_profile_config_lookup_file(config, old_filename); + assert(old_file); + } + + // If there is an existing profile with a differing filename, delete it first + if (old_filename && (strcmp(filename, old_filename) != 0)) { + file = app_profile_config_lookup_file(config, old_filename); + file_profiles = json_object_get(file, "profiles"); + if (file) { + json_object_del(file_profiles, profile_name); + } + } + + file = app_profile_config_lookup_file(config, filename); + if (!file) { + file = app_profile_config_new_file(config, filename); + } + + file_profiles = json_object_get(file, "profiles"); + json_object_set(file_profiles, profile_name, new_profile); + json_object_set(config->profile_locations, profile_name, json_string(filename)); + + if (old_file) { + app_profile_config_prune_empty_file(config, old_file); + } + + return !old_filename; +} + +void nv_app_profile_config_delete_profile(AppProfileConfig *config, + const char *profile_name) +{ + json_t *file = NULL; + const char *filename = json_string_value(json_object_get(config->profile_locations, profile_name)); + + if (filename) { + file = app_profile_config_lookup_file(config, filename); + if (file) { + json_object_del(json_object_get(file, "profiles"), profile_name); + } + } + + json_object_del(config->profile_locations, profile_name); + + if (file) { + app_profile_config_prune_empty_file(config, file); + } +} + +int nv_app_profile_config_create_rule(AppProfileConfig *config, + const char *filename, + json_t *new_rule) +{ + char *key; + json_t *file, *file_rules; + json_t *new_rule_copy; + int new_id; + + file = app_profile_config_lookup_file(config, filename); + if (!file) { + file = app_profile_config_new_file(config, filename); + } + + file_rules = json_object_get(file, "rules"); + + // Add the rule to the head of the per-file list + json_array_append(file_rules, new_rule); + new_rule_copy = json_array_get(file_rules, json_array_size(file_rules) - 1); + + new_id = config->next_free_rule_id++; + json_object_set_new(new_rule_copy, "id", json_integer(new_id)); + + key = rule_id_to_key_string(new_id); + json_object_set(config->rule_locations, key, json_string(filename)); + free(key); + + return new_id; +} + +static int lookup_rule_index_in_array(json_t *rules, int id) +{ + json_t *rule, *rule_id; + size_t i, size; + for (i = 0, size = json_array_size(rules); i < size; i++) { + rule = json_array_get(rules, i); + rule_id = json_object_get(rule, "id"); + if (json_integer_value(rule_id) == id) { + return i; + } + } + + return -1; +} + +int nv_app_profile_config_update_rule(AppProfileConfig *config, + const char *filename, + int id, + json_t *new_rule) +{ + json_t *old_file, *new_file; + json_t *old_file_rules, *new_file_rules; + json_t *new_rule_copy; + const char *old_filename; + char *key; + int idx; + int rule_moved; + + key = rule_id_to_key_string(id); + old_filename = json_string_value(json_object_get(config->rule_locations, key)); + assert(old_filename); + + old_file = app_profile_config_lookup_file(config, old_filename); + assert(old_file); + + old_file_rules = json_object_get(old_file, "rules"); + + if (filename && (strcmp(filename, old_filename) != 0)) { + // If the rule has a new file, delete the rule and re-add it + new_file = app_profile_config_lookup_file(config, filename); + rule_moved = TRUE; + if (!new_file) { + new_file = app_profile_config_new_file(config, filename); + } + + new_file_rules = json_object_get(new_file, "rules"); + + idx = lookup_rule_index_in_array(old_file_rules, id); + if (idx != -1) { + json_array_remove(old_file_rules, idx); + } + json_array_insert(new_file_rules, 0, new_rule); + new_rule_copy = json_array_get(new_file_rules, 0); + json_object_set_new(new_rule_copy, "id", json_integer(id)); + + json_object_set_new(config->rule_locations, key, json_string(filename)); + } else { + // Otherwise, just edit the existing rule + rule_moved = FALSE; + idx = lookup_rule_index_in_array(old_file_rules, id); + if (idx != -1) { + json_array_set(old_file_rules, idx, new_rule); + new_rule_copy = json_array_get(old_file_rules, idx); + json_object_set_new(new_rule_copy, "id", json_integer(id)); + } + } + + free(key); + + app_profile_config_prune_empty_file(config, old_file); + + return rule_moved; +} + + +void nv_app_profile_config_delete_rule(AppProfileConfig *config, int id) +{ + json_t *file, *file_rules; + const char *filename; + char *key; + int idx; + + key = rule_id_to_key_string(id); + + filename = json_string_value(json_object_get(config->rule_locations, key)); + assert(filename); + + file = app_profile_config_lookup_file(config, filename); + assert(file); + + file_rules = json_object_get(file, "rules"); + + idx = lookup_rule_index_in_array(file_rules, id); + if (idx != -1) { + json_array_remove(file_rules, idx); + } + + json_object_del(config->rule_locations, key); + free(key); +} + +size_t nv_app_profile_config_count_rules(AppProfileConfig *config) +{ + return json_object_size(config->rule_locations); +} + +static size_t app_profile_config_count_rules_before(AppProfileConfig *config, const char *filename) +{ + size_t i, size; + size_t num_rules = 0; + json_t *cur_file, *cur_filename; + + for (i = 0, size = json_array_size(config->parsed_files); i < size; i++) { + cur_file = json_array_get(config->parsed_files, i); + cur_filename = json_object_get(cur_file, "filename"); + if (!strcmp(filename, json_string_value(cur_filename))) { + break; + } + num_rules += json_array_size(json_object_get(cur_file, "rules")); + } + + return num_rules; +} + +static void app_profile_config_insert_rule(AppProfileConfig *config, + json_t *rule, + size_t new_pri, + const char *old_filename) +{ + size_t i, j, size; + size_t num_rules = 0; + char *key; + const char *filename; + json_t *file, *file_rules; + json_t *target[2]; + size_t rules_before_target[2]; + + for (i = 0, j = 0, size = json_array_size(config->parsed_files); i < size; i++) { + file = json_array_get(config->parsed_files, i); + file_rules = json_object_get(file, "rules"); + if ((num_rules <= new_pri) && + (num_rules + json_array_size(file_rules) >= new_pri)) { + // Potential target file for this rule + rules_before_target[j] = num_rules; + target[j++] = file; + if (j >= 2) { + break; + } + } + num_rules += json_array_size(file_rules); + } + + assert((j > 0) && (j <= 2)); + + // If possible, we prefer to keep the rule in the same file as before + for (i = 0; i < j; i++) { + filename = json_string_value(json_object_get(target[i], "filename")); + if (!strcmp(filename, old_filename)) { + break; + } + } + i = (i == j) ? 0 : i; + + file_rules = json_object_get(target[i], "rules"); + json_array_insert_new(file_rules, new_pri - rules_before_target[i], rule); + // Update the hashtable to point to the new file + key = rule_id_to_key_string(json_integer_value(json_object_get(rule, "id"))); + filename = json_string_value(json_object_get(target[i], "filename")); + json_object_set_new(config->rule_locations, key, json_string(filename)); + free(key); +} + +size_t nv_app_profile_config_get_rule_priority(AppProfileConfig *config, + int id) +{ + json_t *file, *file_rules; + const char *filename; + int idx; + char *key; + + key = rule_id_to_key_string(id); + + filename = json_string_value(json_object_get(config->rule_locations, key)); + assert(filename); + + file = app_profile_config_lookup_file(config, filename); + assert(file); + + file_rules = json_object_get(file, "rules"); + + idx = lookup_rule_index_in_array(file_rules, id); + + free(key); + + return app_profile_config_count_rules_before(config, filename) + idx; +} + +static void app_profile_config_set_abs_rule_priority_internal(AppProfileConfig *config, + int id, + size_t new_pri, + size_t current_pri, + size_t lowest_pri) +{ + json_t *rule, *rule_copy; + json_t *file, *file_rules; + const char *filename; + int idx; + char *key; + + if (new_pri == current_pri) { + return; + } else if (new_pri >= lowest_pri) { + new_pri = lowest_pri - 1; + } + + key = rule_id_to_key_string(id); + + filename = json_string_value(json_object_get(config->rule_locations, key)); + assert(filename); + + file = app_profile_config_lookup_file(config, filename); + assert(file); + + file_rules = json_object_get(file, "rules"); + idx = lookup_rule_index_in_array(file_rules, id); + assert(idx >= 0); + rule = json_array_get(file_rules, idx); + + rule_copy = json_deep_copy(rule); + json_array_remove(file_rules, idx); + + app_profile_config_insert_rule(config, rule_copy, new_pri, filename); + + app_profile_config_prune_empty_file(config, file); + + free(key); +} + +void nv_app_profile_config_set_abs_rule_priority(AppProfileConfig *config, + int id, size_t new_pri) +{ + size_t current_pri = nv_app_profile_config_get_rule_priority(config, id); + size_t lowest_pri = nv_app_profile_config_count_rules(config); + app_profile_config_set_abs_rule_priority_internal(config, id, new_pri, current_pri, lowest_pri); +} + +void nv_app_profile_config_change_rule_priority(AppProfileConfig *config, + int id, + int delta) +{ + size_t lowest_pri = nv_app_profile_config_count_rules(config); + size_t current_pri = nv_app_profile_config_get_rule_priority(config, id); + size_t new_pri; + if ((delta < 0) && (((size_t)-delta) > current_pri)) { + new_pri = 0; + } else { + new_pri = current_pri + delta; + } + app_profile_config_set_abs_rule_priority_internal(config, id, new_pri, current_pri, lowest_pri); +} + +const json_t *nv_app_profile_config_get_profile(AppProfileConfig *config, + const char *profile_name) +{ + json_t *file, *file_profiles; + json_t *filename = json_object_get(config->profile_locations, profile_name); + + if (!filename) { + return NULL; + } + + file = app_profile_config_lookup_file(config, json_string_value(filename)); + file_profiles = json_object_get(file, "profiles"); + + return json_object_get(file_profiles, profile_name); +} + + +const json_t *nv_app_profile_config_get_rule(AppProfileConfig *config, + int id) +{ + char *key = rule_id_to_key_string(id); + json_t *file, *rule, *filename; + json_t *file_rules; + int idx; + + filename = json_object_get(config->rule_locations, key); + + if (!filename) { + free(key); + return NULL; + } + + file = app_profile_config_lookup_file(config, json_string_value(filename)); + file_rules = json_object_get(file, "rules"); + + idx = lookup_rule_index_in_array(file_rules, id); + if (idx != -1) { + rule = json_array_get(file_rules, idx); + } else { + assert(0); + rule = NULL; + } + + free(key); + return rule; +} + +struct AppProfileConfigProfileIterRec { + AppProfileConfig *config; + size_t file_idx; + json_t *profiles; + void *profile_iter; +}; + +AppProfileConfigProfileIter *nv_app_profile_config_profile_iter(AppProfileConfig *config) +{ + AppProfileConfigProfileIter *iter = malloc(sizeof(AppProfileConfigProfileIter)); + + iter->config = config; + iter->file_idx = 0; + iter->profile_iter = NULL; + + return nv_app_profile_config_profile_iter_next(iter); +} + +AppProfileConfigProfileIter *nv_app_profile_config_profile_iter_next(AppProfileConfigProfileIter *iter) +{ + AppProfileConfig *config = iter->config; + json_t *file; + int advance = TRUE; + size_t size; + + size = json_array_size(config->parsed_files); + while ((iter->file_idx < size) && + !iter->profile_iter) { + file = json_array_get(config->parsed_files, iter->file_idx); + iter->profiles = json_object_get(file, "profiles"); + iter->profile_iter = json_object_iter(iter->profiles); + iter->file_idx++; + advance = FALSE; + } + + if (!iter->profile_iter) { + free(iter); + return NULL; + } + + if (advance) { + iter->profile_iter = json_object_iter_next(iter->profiles, iter->profile_iter); + } + + while ((iter->file_idx < size) && + !iter->profile_iter) { + file = json_array_get(config->parsed_files, iter->file_idx); + iter->profiles = json_object_get(file, "profiles"); + iter->profile_iter = json_object_iter(iter->profiles); + iter->file_idx++; + } + + if (!iter->profile_iter) { + free(iter); + return NULL; + } + + return iter; +} + + +struct AppProfileConfigRuleIterRec { + AppProfileConfig *config; + size_t file_idx; + int rule_idx; + json_t *rules; +}; + +AppProfileConfigRuleIter *nv_app_profile_config_rule_iter(AppProfileConfig *config) +{ + AppProfileConfigRuleIter *iter = malloc(sizeof(AppProfileConfigRuleIter)); + + iter->file_idx = 0; + iter->rule_idx = -1; + iter->config = config; + + return nv_app_profile_config_rule_iter_next(iter); +} + +AppProfileConfigRuleIter *nv_app_profile_config_rule_iter_next(AppProfileConfigRuleIter *iter) +{ + AppProfileConfig *config = iter->config; + json_t *file; + int advance = TRUE; + size_t size; + + size = json_array_size(config->parsed_files); + while ((iter->file_idx < size) && + (iter->rule_idx == -1)) { + file = json_array_get(config->parsed_files, iter->file_idx); + iter->rules = json_object_get(file, "rules"); + if (json_array_size(iter->rules)) { + iter->rule_idx = 0; + } + iter->file_idx++; + advance = FALSE; + } + + if (iter->rule_idx == -1) { + free(iter); + return NULL; + } + + if (advance) { + iter->rule_idx++; + if (iter->rule_idx >= json_array_size(iter->rules)) { + iter->rule_idx = -1; + } + } + + while ((iter->file_idx < size) && + (iter->rule_idx == -1)) { + file = json_array_get(config->parsed_files, iter->file_idx); + iter->rules = json_object_get(file, "rules"); + if (json_array_size(iter->rules)) { + iter->rule_idx = 0; + } + iter->file_idx++; + } + + if (iter->rule_idx == -1) { + free(iter); + return NULL; + } + + return iter; +} + +const char *nv_app_profile_config_profile_iter_name(AppProfileConfigProfileIter *iter) +{ + return json_object_iter_key(iter->profile_iter); +} + +json_t *nv_app_profile_config_profile_iter_val(AppProfileConfigProfileIter *iter) +{ + return json_object_iter_value(iter->profile_iter); +} + +size_t nv_app_profile_config_rule_iter_pri(AppProfileConfigRuleIter *iter) +{ + json_t *rule = nv_app_profile_config_rule_iter_val(iter); + return nv_app_profile_config_get_rule_priority(iter->config, + json_integer_value(json_object_get(rule, "id"))); +} + +json_t *nv_app_profile_config_rule_iter_val(AppProfileConfigRuleIter *iter) +{ + return json_array_get(iter->rules, iter->rule_idx); +} + +const char *nv_app_profile_config_profile_iter_filename(AppProfileConfigProfileIter *iter) +{ + json_t *file = json_array_get(iter->config->parsed_files, iter->file_idx - 1); + return json_string_value(json_object_get(file, "filename")); +} + +const char *nv_app_profile_config_rule_iter_filename(AppProfileConfigRuleIter *iter) +{ + json_t *file = json_array_get(iter->config->parsed_files, iter->file_idx - 1); + return json_string_value(json_object_get(file, "filename")); +} + +const char *nv_app_profile_config_get_rule_filename(AppProfileConfig *config, + int id) +{ + const char *filename; + char *key; + + key = rule_id_to_key_string(id); + filename = json_string_value(json_object_get(config->rule_locations, key)); + free(key); + + return filename; +} + +const char *nv_app_profile_config_get_profile_filename(AppProfileConfig *config, + const char *profile_name) +{ + return json_string_value(json_object_get(config->profile_locations, profile_name)); +} + +static char *get_search_path_string(AppProfileConfig *config) +{ + size_t i; + char *new_s; + char *s = strdup(""); + for (i = 0; i < config->search_path_count; i++) { + new_s = nvasprintf("%s\t\"%s\"\n", + s, config->search_path[i]); + free(s); + s = new_s; + } + + return s; +} + +static int app_profile_config_check_is_prefix(const char *filename1, const char *filename2) +{ + char *dirname1, *dirname2; + int prefix_state; + dirname1 = nv_dirname(filename1); + dirname2 = nv_dirname(filename2); + + if (!strcmp(dirname1, filename2)) { + prefix_state = 1; + } else if (!strcmp(filename1, dirname2)) { + prefix_state = -1; + } else { + prefix_state = 0; + } + + free(dirname1); + free(dirname2); + + return prefix_state; +} + +int nv_app_profile_config_check_valid_source_file(AppProfileConfig *config, + const char *filename, + char **reason) +{ + const json_t *parsed_file; + size_t i, size; + const char *cur_filename; + char *dirname; + char *search_path_string; + int prefix_state; + + // Check if the source file can be found in the search path + dirname = NULL; + for (i = 0; i < config->search_path_count; i++) { + if (!strcmp(filename, config->search_path[i])) { + break; + } else { + if (!dirname) { + dirname = nv_dirname(filename); + } + if (!strcmp(dirname, config->search_path[i])) { + break; + } + } + } + free(dirname); + + if (i == config->search_path_count) { + search_path_string = get_search_path_string(config); + if (reason) { + *reason = nvasprintf("the filename is not valid in the search path:\n\n%s\n", + search_path_string); + } + free(search_path_string); + return FALSE; + } + + // Check if the source file is a prefix of some other file in the configuration, + // or vice versa + for (i = 0, size = json_array_size(config->parsed_files); i < size; i++) { + parsed_file = json_array_get(config->parsed_files, i); + cur_filename = json_string_value(json_object_get(parsed_file, "filename")); + + prefix_state = app_profile_config_check_is_prefix(filename, cur_filename); + + if (prefix_state) { + if (prefix_state > 0) { + if (reason) { + *reason = nvasprintf("the filename is a prefix of the existing file \"%s\".", + cur_filename); + } + } else if (reason) { + *reason = nvasprintf("the filename would be placed in the directory \"%s\", " + "but that is already a regular file in the configuration.", + cur_filename); + } + return FALSE; + } + } + + return TRUE; +} + +int nv_app_profile_config_check_backing_files(AppProfileConfig *config) +{ + json_t *file; + size_t i, size; + const char *filename; + FILE *fp; + time_t saved_atime; + struct stat stat_buf; + int ret; + int changed = FALSE; + for (i = 0, size = json_array_size(config->parsed_files); i < size; i++) { + file = json_array_get(config->parsed_files, i); + if (json_is_false(json_object_get(file, "new"))) { + // Stat the file and compare our saved atime to the file's mtime + filename = json_string_value(json_object_get(file, "filename")); + ret = open_and_stat(filename, "r", &fp, &stat_buf); + if (ret >= 0) { + fclose(fp); + saved_atime = (time_t)json_integer_value(json_object_get(file, "atime")); + if (stat_buf.st_mtime > saved_atime) { + json_object_set_new(file, "dirty", json_true()); + changed = TRUE; + } + } else { + // I/O errors: assume something changed + json_object_set_new(file, "dirty", json_true()); + changed = TRUE; + } + } + } + + return changed; +} + +/* + * Filenames in the search path ending in "*.d" are directories by convention, + * and should not be listed as valid default filenames. + */ +static inline int check_has_directory_suffix(const char *filename) +{ + size_t len = strlen(filename); + + return (len >= 2) && + (filename[len-2] == '.') && + (filename[len-1] == 'd'); +} + +json_t *nv_app_profile_config_get_source_filenames(AppProfileConfig *config) +{ + size_t i, j, size; + const char *filename; + json_t *filenames; + json_t *file; + int do_add_search_path_item; + + filenames = json_array(); + for (i = 0, size = json_array_size(config->parsed_files); i < size; i++) { + file = json_array_get(config->parsed_files, i); + json_array_append(filenames, json_object_get(file, "filename")); + } + + for (i = 0; i < config->search_path_count; i++) { + do_add_search_path_item = + !check_has_directory_suffix(config->search_path[i]); + for (j = 0; (j < size) && do_add_search_path_item; j++) { + file = json_array_get(config->parsed_files, j); + filename = json_string_value(json_object_get(file, "filename")); + if (!strcmp(config->search_path[i], filename) || + app_profile_config_check_is_prefix(config->search_path[i], + filename)) { + do_add_search_path_item = FALSE; + } + } + if (do_add_search_path_item) { + json_array_append_new(filenames, + json_string(config->search_path[i])); + } + } + + return filenames; +} + +int nv_app_profile_config_profile_name_change_fixup(AppProfileConfig *config, + const char *orig_name, + const char *new_name) +{ + int fixed_up = FALSE; + size_t i, j; + size_t num_files, num_rules; + json_t *file, *rules, *rule, *rule_profile; + const char *rule_profile_str; + + for (i = 0, num_files = json_array_size(config->parsed_files); i < num_files; i++) { + file = json_array_get(config->parsed_files, i); + rules = json_object_get(file, "rules"); + for (j = 0, num_rules = json_array_size(rules); j < num_rules; j++) { + rule = json_array_get(rules, j); + rule_profile = json_object_get(rule, "profile"); + assert(json_is_string(rule_profile)); + rule_profile_str = json_string_value(rule_profile); + if (!strcmp(rule_profile_str, orig_name)) { + json_object_set_new(rule, "profile", json_string(new_name)); + fixed_up = TRUE; + } + } + } + + return fixed_up; +} diff --git a/src/app-profiles.h b/src/app-profiles.h new file mode 100644 index 0000000..fa74e58 --- /dev/null +++ b/src/app-profiles.h @@ -0,0 +1,314 @@ +/* + * nvidia-settings: A tool for configuring the NVIDIA X driver on Unix + * and Linux systems. + * + * Copyright (C) 2013 NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses>. + */ + +/* + * app-profiles.h - prototypes for querying and assigning application profile + * settings, as well as parsing and saving application profile configuration + * files. + */ +#ifndef __APP_PROFILES_H__ +#define __APP_PROFILES_H__ + +#include <assert.h> +#include <jansson.h> + +/* + * A comment on the format of parsed configuration files: + * + * Each parsed file is described by a JSON object, with the following members: + * filename: This is the name of the file. + * rules: this is the same as the rules array in the original configuration file, + * except inline profiles are moved into the profiles array. Also see + * below. + * profiles: this is a JSON object which is used as a per-file hashtable + * mapping profile names to profiles. Note this differs from the + * format of the original configuration file, which stores + * profiles in an array. + * order: this is a JSON object which describes the priority of the file + * in the search path. This contains the following members: + * major: the index of the top-level search path entry + * minor: 0 if the file is in the top-level search path, or otherwise the order + * of the file within the top-level directory as determined by + * strcoll(3). + * dirty: a flag indicating this file should be overwritten even if + * validation does not detect any changes (used to handle invalid + * configuration, e.g. duplicate profile names). + * new: indicates whether the file object is new to the configuration or + * loaded from disk + * atime: time of last access as determined by time(2) during the + * nv_app_profile_config_load() call. Used to check if our local copy + * of the config file is stale. Undefined for new files + * + * Each rule in the configuration contains the following attributes: + * id: This is a unique integer that identifies the rule. + * pattern: This is the same as the pattern attribute in the rule read in + * from the original configuration. + * profile: This is a string describing the name of the profile applied by + * the rule. + * + * The priority of the rule is implicitly determined by its position in the + * configuration. + * + * Each profile in the configuration contains the following attributes: + * settings: This is an array of setting objects, and is the same as + * the settings array of the original profile read in from the + * original configuration. + * + * The name of the profile is implicitly determined by its key in its container + * JSON object. + */ + +/* + * The AppProfileConfig struct contains the current profile configuration + * as determined by nvidia-settings. This configuration contains a list + * of files which contain rules and profiles. + */ +typedef struct AppProfileConfigRec { + /* + * JSON object containing our global app profile options. Currently + * contains the following members: + * enabled: boolean indicating whether app profiles are enabled. + */ + json_t *global_options; + + /* + * JSON array of parsed files, each containing a rules and profiles array + * along with other metadata. + */ + json_t *parsed_files; + + /* + * We maintain secondary hashtables of profile and rule locations stored as + * JSON objects for quicker lookup of individual profiles and rules. This is + * also used to ensure that globally there are no two profiles with the same + * name (regardless of their file of origin). + */ + json_t *profile_locations; + json_t *rule_locations; + size_t next_free_rule_id; + + /* + * Copy of the global configuration filename + */ + char *global_config_file; + + /* + * Copy of the search path. This is needed to determine the order of files + * in the parsed_files array above + */ + char **search_path; + size_t search_path_count; +} AppProfileConfig; + +/* + * Save configuration specified by the JSON array updates to disk. See + * nv_app_profile_config_validate() below for the format of this array. + * backup indicates whether this should also make backups of the original files + * before saving. + * Returns 0 if successful, or a negative integer if an error was encountered. + */ +int nv_app_profile_config_save_updates(AppProfileConfig *config, json_t *updates, int backup); + +/* + * Load an application profile configuration from disk, using a list of files specified by search_path. + */ +AppProfileConfig *nv_app_profile_config_load(const char *global_config_file, + char **search_path, + size_t search_path_count); + +/* + * Duplicate the configuration; the copy can then be edited and compared against + * the original. + */ +AppProfileConfig *nv_app_profile_config_dup(AppProfileConfig *old_config); + +/* + * Toggle whether application profiles are enabled for this user. + */ +void nv_app_profile_config_set_enabled(AppProfileConfig *config, + int enabled); + +/* + * Destroy the configuration and free its backing memory. + */ +void nv_app_profile_config_free(AppProfileConfig *config); + +/* + * Validate the configuration specified by new_config against the pristine copy + * specified by old_config, and generate a list of changes needed in order to + * achieve the new configuration. + * + * This returns a JSON array which must be freed via json_decref(), containing + * a list of update objects. Each update object contains the following + * attributes: + * + * filename (string): the filename of the file that changed + * text (string): the new JSON that should be used + * + * This array can be passed directly to nv_app_profile_config_save_updates() to + * be saved to disk. + */ +json_t *nv_app_profile_config_validate(AppProfileConfig *new_config, + AppProfileConfig *old_config); + +#define INVALID_RULE_ID -1 + +// Accessor functions +const json_t *nv_app_profile_config_get_profile(AppProfileConfig *config, + const char *profile_name); + +const json_t *nv_app_profile_config_get_rule(AppProfileConfig *config, + int id); + +const char *nv_app_profile_config_get_rule_filename(AppProfileConfig *config, + int id); + +const char *nv_app_profile_config_get_profile_filename(AppProfileConfig *config, + const char *profile_name); + +int nv_app_profile_config_get_enabled(AppProfileConfig *config); + + +// Opaque iterator functions +typedef struct AppProfileConfigProfileIterRec AppProfileConfigProfileIter; +typedef struct AppProfileConfigRuleIterRec AppProfileConfigRuleIter; + +AppProfileConfigProfileIter *nv_app_profile_config_profile_iter(AppProfileConfig *config); +AppProfileConfigProfileIter *nv_app_profile_config_profile_iter_next(AppProfileConfigProfileIter *iter); +const char *nv_app_profile_config_profile_iter_filename(AppProfileConfigProfileIter *iter); +const char *nv_app_profile_config_profile_iter_name(AppProfileConfigProfileIter *iter); +json_t *nv_app_profile_config_profile_iter_val(AppProfileConfigProfileIter *iter); + +AppProfileConfigRuleIter *nv_app_profile_config_rule_iter(AppProfileConfig *config); +AppProfileConfigRuleIter *nv_app_profile_config_rule_iter_next(AppProfileConfigRuleIter *iter); +json_t *nv_app_profile_config_rule_iter_val(AppProfileConfigRuleIter *iter); +size_t nv_app_profile_config_rule_iter_pri(AppProfileConfigRuleIter *iter); +const char *nv_app_profile_config_rule_iter_filename(AppProfileConfigRuleIter *iter); + +size_t nv_app_profile_config_count_rules(AppProfileConfig *config); + +/* + * Update or create a new profile. Returns TRUE if this operation resulted in + * the creation of a new profile. + */ +int nv_app_profile_config_update_profile(AppProfileConfig *config, + const char *filename, + const char *profile_name, + json_t *new_profile); + +/* + * Delete a profile from the configuration. + */ +void nv_app_profile_config_delete_profile(AppProfileConfig *config, + const char *profile_name); + +/* + * Create a new rule. The rule will be given priority over all rules defined in + * the same file in the search path. Returns the ID of the newly-created rule. + * The memory pointed to by rule must be allocated by malloc(), and should not + * be freed explicitly by the caller. + */ +int nv_app_profile_config_create_rule(AppProfileConfig *config, + const char *filename, + json_t *new_rule); + +/* + * Update the attributes of an existing rule by its ID. If filename is + * non-NULL and differs from the file in which the rule currently resides, + * this operation will move the rule to the file specified by filename, + * potentially altering the rule's priority in the process. Returns TRUE + * if the rule is moved as a result of this operation. + */ +int nv_app_profile_config_update_rule(AppProfileConfig *config, + const char *filename, + int id, + json_t *rule); + +/* + * Delete the rule with the given ID from the configuration. + */ +void nv_app_profile_config_delete_rule(AppProfileConfig *config, int id); + +size_t nv_app_profile_config_get_rule_priority(AppProfileConfig *config, + int id); + +void nv_app_profile_config_set_abs_rule_priority(AppProfileConfig *config, + int id, size_t pri); + +/* + * Change the priority of the rule in the rule list. Existing rules with + * equal or lower priority in the list will be moved down in the list to make + * room. This operation also changes the source file of the rule as needed to + * ensure the rule's priority will be consistent with its location in the search + * path. + */ +void nv_app_profile_config_change_rule_priority(AppProfileConfig *config, + int id, int delta); + +/* + * This function walks the list of parsed files, and marks any files which have + * changed since the configuration was loaded on disk dirty for the validation + * step to generate an update for them. + * + * This function returns TRUE if any files read in this configuration have + * changed since this configuration was loaded from disk. + */ +int nv_app_profile_config_check_backing_files(AppProfileConfig *config); + +/* + * Utility function to strip comments and translate hex/octal values to decimal + * so the JSON parser can understand. + */ +char *nv_app_profile_cfg_file_syntax_to_json(const char *orig_s); + +int nv_app_profile_config_check_valid_source_file(AppProfileConfig *config, + const char *filename, + char **reason); + +/* + * Constructs a list of suggested filenames from the default search path and + * parsed files list. If an item in the search path appears in the prefix of a + * path to a parsed file, it will *not* appear in the list. The resulting json_t + * object should be freed via json_decref() once the caller is done using it. + */ +json_t *nv_app_profile_config_get_source_filenames(AppProfileConfig *config); + +/* + * Given a valid filename in the search path, this function constructs the name + * of the file that will be used as a backup file for display to the end user. + * The returned string should be freed by the caller. + */ +char *nv_app_profile_config_get_backup_filename(AppProfileConfig *config, const char *filename); + +/* + * Constructs an unused profile name for use with a new profile. The returned + * string should be freed by the caller. + */ +char *nv_app_profile_config_get_unused_profile_name(AppProfileConfig *config); + +/* + * This function looks for all rules referring to the profile name given by + * orig_name and updates them to refer to the profile name given by new_name. + * Returns TRUE if any rules have been altered as a result of this operation. + */ +int nv_app_profile_config_profile_name_change_fixup(AppProfileConfig *config, + const char *orig_name, + const char *new_name); + +#endif // __APP_PROFILES_H__ diff --git a/src/command-line.c b/src/command-line.c index 605b57b..70c001d 100644 --- a/src/command-line.c +++ b/src/command-line.c @@ -78,7 +78,7 @@ static void print_version(void) static void print_attribute_help(char *attr) { - AttributeTableEntry *entry; + const AttributeTableEntry *entry; int found = 0; int list_all = 0; int show_desc = 1; @@ -181,15 +181,14 @@ void print_help(void) * even use the gui. */ -Options *parse_command_line(int argc, char *argv[], char *dpy) +Options *parse_command_line(int argc, char *argv[], char *dpy, + CtrlHandlesArray *handles_array) { Options *op; int n, c; char *strval; - op = malloc(sizeof(Options)); - memset(op, 0, sizeof (Options)); - + op = nvalloc(sizeof(Options)); op->config = DEFAULT_RC_FILE; /* @@ -240,18 +239,19 @@ Options *parse_command_line(int argc, char *argv[], char *dpy) break; case 'a': n = op->num_assignments; - op->assignments = realloc(op->assignments, sizeof(char *) * (n+1)); + op->assignments = nvrealloc(op->assignments, + sizeof(char *) * (n+1)); op->assignments[n] = strval; op->num_assignments++; break; case 'q': n = op->num_queries; - op->queries = realloc(op->queries, sizeof(char *) * (n+1)); + op->queries = nvrealloc(op->queries, sizeof(char *) * (n+1)); op->queries[n] = strval; op->num_queries++; break; case CONFIG_FILE_OPTION: op->config = strval; break; - case 'g': print_glxinfo(NULL); exit(0); break; + case 'g': print_glxinfo(NULL, handles_array); exit(0); break; case 't': __terse = NV_TRUE; break; case 'd': __display_device_string = NV_TRUE; break; case 'e': print_attribute_help(strval); exit(0); break; diff --git a/src/command-line.h b/src/command-line.h index ccd60d3..75d04ce 100644 --- a/src/command-line.h +++ b/src/command-line.h @@ -22,6 +22,11 @@ #include "common-utils.h" +/* + * Forward declaration to break circular dependancy with query-assign.h + */ +struct _CtrlHandlesArray; + #define DEFAULT_RC_FILE "~/.nvidia-settings-rc" #define CONFIG_FILE_OPTION 1 @@ -89,6 +94,7 @@ typedef struct { } Options; -Options *parse_command_line(int argc, char *argv[], char *dpy); +Options *parse_command_line(int argc, char *argv[], char *dpy, + struct _CtrlHandlesArray *handles_array); #endif /* __COMMAND_LINE_H__ */ diff --git a/src/common-unix/virtual-resolutions/nvvr.c b/src/common-unix/virtual-resolutions/nvvr.c new file mode 100644 index 0000000..d18f542 --- /dev/null +++ b/src/common-unix/virtual-resolutions/nvvr.c @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2012-2013 NVIDIA Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <string.h> +#include <stdlib.h> + +#include "nvvr.h" + +/*! + * Compute ViewPortOut, given raster size, ViewPortIn size, and scaling type. + * + * ViewPortOut should fit within the raster size, scaled to the raster + * size in one dimension, and scaled in the other dimension such that + * the aspect ratio of ViewPortIn is preserved. + * + * \param[in] raster raster size + * \param[in] viewPortIn ViewPortIn size + * \param[in] scaled scaling type + * + * \return The ViewPortOut {x,y,w,h} + */ +NVVRBoxRecXYWH NVVRGetScaledViewPortOut(const NVVRSize *raster, + const NVVRSize *viewPortIn, + const NVVRScalingType scaling) +{ + NVVRBoxRecXYWH viewPortOut; + float scaleX, scaleY; + + memset(&viewPortOut, 0, sizeof(viewPortOut)); + + switch (scaling) { + default: + /* fall through */ + + case NVVR_SCALING_ASPECT_SCALED: + scaleX = (float) raster->w / (float) viewPortIn->w; + scaleY = (float) raster->h / (float) viewPortIn->h; + + if (scaleX < scaleY) { + viewPortOut.w = raster->w; + viewPortOut.h = viewPortIn->h * scaleX; + viewPortOut.x = 0; + viewPortOut.y = (raster->h - viewPortOut.h) / 2; + } else { + viewPortOut.w = viewPortIn->w * scaleY; + viewPortOut.h = raster->h; + viewPortOut.x = (raster->w - viewPortOut.w) / 2; + viewPortOut.y = 0; + } + break; + + case NVVR_SCALING_SCALED: + viewPortOut.x = 0; + viewPortOut.y = 0; + viewPortOut.w = raster->w; + viewPortOut.h = raster->h; + break; + + case NVVR_SCALING_CENTERED: + /* if raster is smaller than viewPortIn, fall back to scaling */ + if (raster->w >= viewPortIn->w) { + viewPortOut.w = viewPortIn->w; + viewPortOut.x = (raster->w - viewPortIn->w) / 2; + } else { + viewPortOut.w = raster->w; + viewPortOut.x = 0; + } + + if (raster->h >= viewPortIn->h) { + viewPortOut.h = viewPortIn->h; + viewPortOut.y = (raster->h - viewPortIn->h) / 2; + } else { + viewPortOut.h = raster->h; + viewPortOut.y = 0; + } + break; + } + + return viewPortOut; +} + +/*! + * Return a constant array of a (-1, -1) terminated list of common + * resolutions. + * + * \return A constant array of NVVRSize. + */ +const NVVRSize* NVVRGetCommonResolutions() +{ + static const NVVRSize commonRes[] = { + { 3840, 2400 }, + { 2560, 1600 }, + { 2560, 1440 }, + { 1920, 1200 }, + { 1920, 1080 }, + { 1680, 1050 }, + { 1600, 1200 }, + { 1440, 900 }, + { 1366, 768 }, + { 1280, 1024 }, + { 1280, 800 }, + { 1280, 720 }, + { 1024, 768 }, + { 800, 600 }, + { 640, 480 }, + { -1, -1 }, + }; + + return commonRes; +} diff --git a/src/common-unix/virtual-resolutions/nvvr.h b/src/common-unix/virtual-resolutions/nvvr.h new file mode 100644 index 0000000..c934ad7 --- /dev/null +++ b/src/common-unix/virtual-resolutions/nvvr.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2012-2013 NVIDIA Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef NVVR_H +#define NVVR_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { int w, h; } NVVRSize; +typedef struct { int x, y, w, h; } NVVRBoxRecXYWH; + +typedef enum NVVRScalingTypeEnum { + NVVR_SCALING_ASPECT_SCALED = 0, + NVVR_SCALING_SCALED, + NVVR_SCALING_CENTERED, +} NVVRScalingType; + + +NVVRBoxRecXYWH NVVRGetScaledViewPortOut(const NVVRSize *raster, + const NVVRSize *viewPortIn, + const NVVRScalingType scaling); + +const NVVRSize* NVVRGetCommonResolutions(void); + +#ifdef __cplusplus +} +#endif + +#endif /* NVVR_H */ diff --git a/src/common-unix/virtual-resolutions/src.mk b/src/common-unix/virtual-resolutions/src.mk new file mode 100644 index 0000000..de2ec7d --- /dev/null +++ b/src/common-unix/virtual-resolutions/src.mk @@ -0,0 +1,6 @@ +# makefile fragment included by nvidia-settings + +VIRTUAL_RESOLUTIONS_SRC += nvvr.c + +VIRTUAL_RESOLUTIONS_EXTRA_DIST += nvvr.h +VIRTUAL_RESOLUTIONS_EXTRA_DIST += src.mk diff --git a/src/common-utils/common-utils.c b/src/common-utils/common-utils.c index a8ee75a..a11d996 100644 --- a/src/common-utils/common-utils.c +++ b/src/common-utils/common-utils.c @@ -200,6 +200,44 @@ char *nvstrtolower(char *s) /* + * nvstrtoupper() - convert the given string to uppercase. + */ + +char *nvstrtoupper(char *s) +{ + char *start = s; + + if (s == NULL) return NULL; + + while (*s) { + *s = toupper(*s); + s++; + } + + return start; + +} /* nvstrtoupper() */ + + + +/* + * nvasprintf() - implementation of asprintf() that checks return values; if an + * error occurs, an error is printed to stderr and exit is called. + * -- this function will only return on success. + */ +char *nvasprintf(const char *fmt, ...) +{ + char *str; + + NV_VSNPRINTF(str, fmt); + + return str; + +} /* nvasprintf() */ + + + +/* * nvfree() - frees memory allocated with nvalloc(), provided * a non-NULL pointer is provided. */ @@ -646,3 +684,12 @@ char *fget_next_line(FILE *fp, int *eof) return NULL; /* should never get here */ } + +char *nvstrchrnul(char *s, int c) +{ + char *result = strchr(s, c); + if (!result) { + return (s + strlen(s)); + } + return result; +} diff --git a/src/common-utils/common-utils.h b/src/common-utils/common-utils.h index 626fd1b..7e2838f 100644 --- a/src/common-utils/common-utils.h +++ b/src/common-utils/common-utils.h @@ -40,6 +40,18 @@ #define VERBOSITY_DEFAULT VERBOSITY_ERROR +/* + * Define a printf format attribute macro. This definition is based on the one + * from Xfuncproto.h, available in the 'xproto' package at + * http://xorg.freedesktop.org/releases/individual/proto/ + */ + +#if defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 203) +# define NV_ATTRIBUTE_PRINTF(x,y) __attribute__((__format__(__printf__,x,y))) +#else /* not gcc >= 2.3 */ +# define NV_ATTRIBUTE_PRINTF(x,y) +#endif + typedef struct { char **t; /* the text rows */ int n; /* number of rows */ @@ -52,6 +64,9 @@ void *nvrealloc(void *ptr, size_t size); char *nvstrdup(const char *s); char *nvstrndup(const char *s, size_t n); char *nvstrtolower(char *s); +char *nvstrtoupper(char *s); +char *nvstrchrnul(char *s, int c); +char *nvasprintf(const char *fmt, ...) NV_ATTRIBUTE_PRINTF(1, 2); void nvfree(void *s); char *tilde_expansion(const char *str); @@ -66,11 +81,11 @@ void nv_free_text_rows(TextRows *t); void reset_current_terminal_width(unsigned short new_val); void silence_fmt(int val); -void fmtout(const char *fmt, ...); -void fmtoutp(const char *prefix, const char *fmt, ...); -void fmterr(const char *fmt, ...); -void fmtwarn(const char *fmt, ...); -void fmt(FILE *stream, const char *prefix, const char *fmt, ...); +void fmtout(const char *fmt, ...) NV_ATTRIBUTE_PRINTF(1, 2); +void fmtoutp(const char *prefix, const char *fmt, ...) NV_ATTRIBUTE_PRINTF(2, 3); +void fmterr(const char *fmt, ...) NV_ATTRIBUTE_PRINTF(1, 2); +void fmtwarn(const char *fmt, ...) NV_ATTRIBUTE_PRINTF(1, 2); +void fmt(FILE *stream, const char *prefix, const char *fmt, ...) NV_ATTRIBUTE_PRINTF(3, 4); char *fget_next_line(FILE *fp, int *eof); @@ -99,9 +114,9 @@ do { \ va_list ap; \ int len, current_len = NV_FMT_BUF_LEN; \ \ - (buf) = malloc(current_len); \ - \ while (1) { \ + (buf) = nvalloc(current_len); \ + \ va_start(ap, fmt); \ len = vsnprintf((buf), current_len, (fmt), ap); \ va_end(ap); \ @@ -113,8 +128,8 @@ do { \ } else { \ current_len += NV_FMT_BUF_LEN; \ } \ - free(buf); \ - (buf) = malloc(current_len); \ + \ + nvfree(buf); \ } \ } \ } while (0) diff --git a/src/config-file.c b/src/config-file.c index 3f8d39b..ff64d8a 100644 --- a/src/config-file.c +++ b/src/config-file.c @@ -65,7 +65,8 @@ static ParsedAttributeWrapper *parse_config_file(char *buf, static int process_config_file_attributes(const char *file, ParsedAttributeWrapper *w, - const char *display_name); + const char *display_name, + CtrlHandlesArray *handles_array); static void save_gui_parsed_attributes(ParsedAttributeWrapper *w, ParsedAttribute *p); @@ -101,7 +102,8 @@ extern int __verbosity_level_changed; */ int nv_read_config_file(const char *file, const char *display_name, - ParsedAttribute *p, ConfigProperties *conf) + ParsedAttribute *p, ConfigProperties *conf, + CtrlHandlesArray *handles_array) { int fd, ret, length; struct stat stat_buf; @@ -180,7 +182,8 @@ int nv_read_config_file(const char *file, const char *display_name, /* process the parsed attributes */ - ret = process_config_file_attributes(file, w, display_name); + ret = process_config_file_attributes(file, w, display_name, + handles_array); /* * add any relevant parsed attributes back to the list to be @@ -214,7 +217,6 @@ int nv_write_config_file(const char *filename, CtrlHandles *h, int screen, ret, entry, bit, val, display, randr_gamma_available; FILE *stream; time_t now; - AttributeTableEntry *a; ReturnStatus status; NVCTRLAttributeValidValuesRec valid; uint32 mask; @@ -301,9 +303,8 @@ int nv_write_config_file(const char *filename, CtrlHandles *h, /* loop over all the entries in the table */ for (entry = 0; attributeTable[entry].name; entry++) { + const AttributeTableEntry *a = &attributeTable[entry]; - a = &attributeTable[entry]; - /* * skip all attributes that are not supposed to be written * to the config file @@ -422,9 +423,8 @@ int nv_write_config_file(const char *filename, CtrlHandles *h, /* loop over all the entries in the table */ for (entry = 0; attributeTable[entry].name; entry++) { + const AttributeTableEntry *a = &attributeTable[entry]; - a = &attributeTable[entry]; - /* * skip all attributes that are not supposed to be written * to the config file @@ -487,7 +487,7 @@ int nv_write_config_file(const char *filename, CtrlHandles *h, if ((p->flags & NV_PARSER_HAS_TARGET) && (p->target_type != NV_CTRL_TARGET_TYPE_X_SCREEN)) { - TargetTypeEntry *targetTypeEntry; + const TargetTypeEntry *targetTypeEntry; /* Find the target name of the target type */ targetTypeEntry = nv_get_target_type_entry_by_nvctrl(p->target_type); @@ -584,7 +584,7 @@ static ParsedAttributeWrapper *parse_config_file(char *buf, const char *file, if (tmp) { free(tmp); } - tmp = malloc(sizeof(char) * current_tmp_len); + tmp = nvalloc(sizeof(char) * current_tmp_len); } strncpy (tmp, cur, len); @@ -594,7 +594,7 @@ static ParsedAttributeWrapper *parse_config_file(char *buf, const char *file, if (!parse_config_property(file, tmp, conf)) { - w = realloc(w, sizeof(ParsedAttributeWrapper) * (n+1)); + w = nvrealloc(w, sizeof(ParsedAttributeWrapper) * (n+1)); ret = nv_parse_attribute_string(tmp, NV_PARSER_ASSIGNMENT, @@ -619,7 +619,7 @@ static ParsedAttributeWrapper *parse_config_file(char *buf, const char *file, free(tmp); /* mark the end of the array */ - w = realloc(w, sizeof(ParsedAttributeWrapper) * (n+1)); + w = nvrealloc(w, sizeof(ParsedAttributeWrapper) * (n+1)); w[n].line = -1; return w; @@ -642,10 +642,10 @@ static ParsedAttributeWrapper *parse_config_file(char *buf, const char *file, static int process_config_file_attributes(const char *file, ParsedAttributeWrapper *w, - const char *display_name) + const char *display_name, + CtrlHandlesArray *handles_array) { - int i, j, found, n = 0; - CtrlHandles **h = NULL; + int i; int old_verbosity = __verbosity; @@ -668,32 +668,8 @@ static int process_config_file_attributes(const char *file, /* build the list of CtrlHandles */ for (i = 0; w[i].line != -1; i++) { - found = NV_FALSE; - for (j = 0; j < n; j++) { - if (nv_strcasecmp(h[j]->display, w[i].a.display)) { - w[i].h = h[j]; - found = NV_TRUE; - break; - } - } - - /* - * no handle found for this display, need to create a new - * handle. - * - * XXX we should really just build a list of what ctrl_handles - * we need, and what attributes on which ctrl_handles, so that - * we don't have to pass NV_CTRL_ATTRIBUTES_ALL_SUBSYSTEMS to - * NvCtrlAttributeInit (done in nv_alloc_ctrl_handles()) - * unless we really need it. - */ - - if (!found) { - h = realloc(h, sizeof(CtrlHandles *) * (n + 1)); - h[n] = nv_alloc_ctrl_handles(w[i].a.display); - w[i].h = h[n]; - n++; - } + w[i].h = nv_alloc_ctrl_handles_and_add_to_array(w[i].a.display, + handles_array); } /* now process each attribute, passing in the correct CtrlHandles */ @@ -718,15 +694,6 @@ static int process_config_file_attributes(const char *file, __verbosity = old_verbosity; } - /* free all the CtrlHandles we allocated */ - - for (i = 0; i < n; i++) { - nv_free_ctrl_handles(h[i]); - } - - if (h) free(h); - - return NV_TRUE; } /* process_config_file_attributes() */ @@ -791,6 +758,8 @@ ConfigPropertiesTableEntry configPropertyTable[] = { { "IncludeDisplayNameInConfigFile", CONFIG_PROPERTIES_INCLUDE_DISPLAY_NAME_IN_CONFIG_FILE }, { "ShowQuitDialog", CONFIG_PROPERTIES_SHOW_QUIT_DIALOG }, + { "UpdateRulesOnProfileNameChange", + CONFIG_PROPERTIES_UPDATE_RULES_ON_PROFILE_NAME_CHANGE }, { NULL, 0 } }; @@ -838,23 +807,9 @@ static int parse_config_property(const char *file, const char *line, ConfigPrope if (!token) goto done; - c = malloc(sizeof(TimerConfigProperty)); - if (!c) { - nv_warning_msg("Error parsing configuration file '%s': could " - "not allocate memory for timer '%s'.", - file, timer); - ret = NV_TRUE; - goto done; - } + c = nvalloc(sizeof(TimerConfigProperty)); c->description = replace_characters(token, '_', ' '); - if (!c->description) { - nv_warning_msg("Error parsing configuration file '%s': could " - "not allocate memory for timer '%s'.", - file, timer); - ret = NV_TRUE; - goto done; - } token = strtok(NULL, ","); if (!token) @@ -940,8 +895,6 @@ static void write_config_properties(FILE *stream, ConfigProperties *conf, char * for (c = conf->timers; (c != NULL); c = c->next) { description = replace_characters(c->description, ' ', '_'); - if (!description) - continue; fprintf(stream, "Timer = %s,%s,%u\n", description, c->user_enabled ? "Yes" : "No", c->interval); @@ -964,7 +917,8 @@ void init_config_properties(ConfigProperties *conf) (CONFIG_PROPERTIES_TOOLTIPS | CONFIG_PROPERTIES_DISPLAY_STATUS_BAR | CONFIG_PROPERTIES_SLIDER_TEXT_ENTRIES | - CONFIG_PROPERTIES_SHOW_QUIT_DIALOG); + CONFIG_PROPERTIES_SHOW_QUIT_DIALOG | + CONFIG_PROPERTIES_UPDATE_RULES_ON_PROFILE_NAME_CHANGE); conf->locale = strdup(setlocale(LC_NUMERIC, NULL)); diff --git a/src/config-file.h b/src/config-file.h index d4eaeb4..d6006c1 100644 --- a/src/config-file.h +++ b/src/config-file.h @@ -35,6 +35,7 @@ #define CONFIG_PROPERTIES_SLIDER_TEXT_ENTRIES (1<<2) #define CONFIG_PROPERTIES_INCLUDE_DISPLAY_NAME_IN_CONFIG_FILE (1<<3) #define CONFIG_PROPERTIES_SHOW_QUIT_DIALOG (1<<4) +#define CONFIG_PROPERTIES_UPDATE_RULES_ON_PROFILE_NAME_CHANGE (1<<5) typedef struct _TimerConfigProperty { char *description; @@ -53,7 +54,8 @@ typedef struct { void init_config_properties(ConfigProperties *conf); int nv_read_config_file(const char *, const char *, - ParsedAttribute *, ConfigProperties *); + ParsedAttribute *, ConfigProperties *, + CtrlHandlesArray *); int nv_write_config_file(const char *, CtrlHandles *, ParsedAttribute *, ConfigProperties *); diff --git a/src/glxinfo.c b/src/glxinfo.c index dfeffaf..2d5252c 100644 --- a/src/glxinfo.c +++ b/src/glxinfo.c @@ -65,10 +65,7 @@ format_extension_list(const char *ext) * Allocate buffer that will hold the extension string with * commas in it */ - extTmp = malloc( (strlen(ext) +i +1) *sizeof(char) ); - if ( extTmp == NULL ) { - return NULL; - } + extTmp = nvalloc( (strlen(ext) +i +1) *sizeof(char) ); /* Copy extension string to buffer, adding commas */ i = 0; @@ -276,7 +273,7 @@ if ( (m) != NULL ) { \ #define NULL_TO_EMPTY(s) \ ((s)!=NULL)?(s):"" -void print_glxinfo(const char *display_name) +void print_glxinfo(const char *display_name, CtrlHandlesArray *handles_array) { int screen; CtrlHandles *h; @@ -300,7 +297,7 @@ void print_glxinfo(const char *display_name) char *formated_ext_str = NULL; - h = nv_alloc_ctrl_handles(display_name); + h = nv_alloc_ctrl_handles_and_add_to_array(display_name, handles_array); if ( h == NULL ) { return; } @@ -489,7 +486,7 @@ void print_glxinfo(const char *display_name) SAFE_FREE(opengl_version); SAFE_FREE(opengl_extensions); SAFE_FREE(fbconfig_attribs); - - nv_free_ctrl_handles(h); + nv_free_ctrl_handles_array(handles_array); + } /* print_glxinfo() */ diff --git a/src/glxinfo.h b/src/glxinfo.h index df3d4ee..6ad5d07 100644 --- a/src/glxinfo.h +++ b/src/glxinfo.h @@ -21,6 +21,7 @@ #define __GLXINFO_H__ #include <GL/glx.h> +#include "query-assign.h" #ifndef GLX_VERSION_1_3 @@ -33,7 +34,7 @@ const char * caveat_abbrev(int caveat); #endif -void print_glxinfo(const char *display_name); +void print_glxinfo(const char *display_name, CtrlHandlesArray *handles_array); #endif /* __GLXINFO_H__ */ diff --git a/src/gtk+-2.x/ctk3dvisionpro.c b/src/gtk+-2.x/ctk3dvisionpro.c index e08f407..872de0d 100644 --- a/src/gtk+-2.x/ctk3dvisionpro.c +++ b/src/gtk+-2.x/ctk3dvisionpro.c @@ -26,6 +26,7 @@ #include "ctk3dvisionpro.h" #include "ctkconfig.h" #include "ctkhelp.h" +#include "ctkdropdownmenu.h" #include <string.h> #include <stdlib.h> @@ -100,7 +101,7 @@ typedef struct _RemoveGlassesDlg { typedef void (*BUTTON_CLICK)(GtkButton *button, gpointer user_data); -static void channel_range_changed(GtkOptionMenu *option_menu, gpointer user_data); +static void channel_range_changed(GtkWidget *widget, gpointer user_data); //----------------------------------------------------------------------------- @@ -241,57 +242,57 @@ static GtkWidget * add_label(char *text, GtkWidget *pack_in) return label; } -static void glasses_name_changed(GtkOptionMenu *option_menu, +static void glasses_name_changed(GtkWidget *widget, gpointer user_data) { RemoveGlassesDlg *dlg = (RemoveGlassesDlg *)user_data; - dlg->glasses_selected_index = gtk_option_menu_get_history(option_menu); + CtkDropDownMenu *menu = CTK_DROP_DOWN_MENU(widget); + dlg->glasses_selected_index = + ctk_drop_down_menu_get_current_value(menu); } static GtkWidget *create_glasses_list_menu(Ctk3DVisionPro *ctk_3d_vision_pro, GlassesInfo **glasses_info, guint num_glasses, gpointer dlg) { - GtkWidget *menu; - GtkWidget *menu_item; - GtkWidget *mnu_glasses_name; + CtkDropDownMenu *mnu_glasses_name; int i; - mnu_glasses_name = gtk_option_menu_new(); + mnu_glasses_name = (CtkDropDownMenu *) + ctk_drop_down_menu_new(CTK_DROP_DOWN_MENU_FLAG_COMBO); g_signal_connect(G_OBJECT(mnu_glasses_name), "changed", G_CALLBACK(glasses_name_changed), (gpointer) dlg); - menu = gtk_menu_new(); - ctk_config_set_tooltip(ctk_3d_vision_pro->ctk_config, mnu_glasses_name, + ctk_config_set_tooltip(ctk_3d_vision_pro->ctk_config, + GTK_WIDGET(mnu_glasses_name), __mnu_glasses_name_tooltip); + g_signal_handlers_block_by_func(G_OBJECT(mnu_glasses_name), + G_CALLBACK(glasses_name_changed), + (gpointer) dlg); + for (i = 0; i < num_glasses; i++) { - menu_item = gtk_menu_item_new_with_label(glasses_info[i]->name); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); - gtk_widget_show(menu_item); + ctk_drop_down_menu_append_item(mnu_glasses_name, + glasses_info[i]->name, i); } /* Setup the menu and select the glasses name */ - g_signal_handlers_block_by_func(G_OBJECT(mnu_glasses_name), - G_CALLBACK(glasses_name_changed), - (gpointer) dlg); - gtk_option_menu_set_menu(GTK_OPTION_MENU(mnu_glasses_name), menu); - gtk_option_menu_set_history(GTK_OPTION_MENU(mnu_glasses_name), 0); + ctk_drop_down_menu_set_current_value(mnu_glasses_name, 0); /* If dropdown has only one item, disable menu selection */ if (num_glasses > 1) { - gtk_widget_set_sensitive(mnu_glasses_name, True); + gtk_widget_set_sensitive(GTK_WIDGET(mnu_glasses_name), True); } else { - gtk_widget_set_sensitive(mnu_glasses_name, False); + gtk_widget_set_sensitive(GTK_WIDGET(mnu_glasses_name), False); } g_signal_handlers_unblock_by_func (G_OBJECT(mnu_glasses_name), G_CALLBACK(glasses_name_changed), (gpointer) dlg); - return mnu_glasses_name; + return GTK_WIDGET(mnu_glasses_name); } static const char *new_glasses_name_activate(GtkWidget *widget, @@ -485,21 +486,7 @@ static void create_glasses_info_table(GlassesInfoTable *table, GlassesInfo** gla update_glasses_info_data_table(table, glasses_info); } -static void add_menu_item(GtkWidget *menu, const char *caption) -{ - GtkWidget *menu_item; - GtkWidget *hbox; - GtkWidget *label; - - menu_item = gtk_menu_item_new(); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); - - hbox = gtk_hbox_new(FALSE, 0); - gtk_container_add(GTK_CONTAINER(menu_item), hbox); - label = gtk_label_new(caption); - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); -} static void init_glasses_info_widgets(GlassesInfo *glasses) { @@ -517,10 +504,9 @@ static void callback_glasses_paired(GtkObject *object, gpointer arg1, { int battery_level; char *glasses_name = NULL; - int target_id = 0; unsigned int glasses_id; GlassesInfo *glasses; - Bool ret; + ReturnStatus ret; CtkEventStruct *event_struct; char temp[64]; //scratchpad memory used to construct labels. int index; @@ -544,21 +530,17 @@ static void callback_glasses_paired(GtkObject *object, gpointer arg1, } } - ret = XNVCTRLQueryTargetStringAttribute(NvCtrlGetDisplayPtr(ctk_3d_vision_pro->handle), - NV_CTRL_TARGET_TYPE_3D_VISION_PRO_TRANSCEIVER, - target_id, glasses_id, - NV_CTRL_STRING_3D_VISION_PRO_GLASSES_NAME, - &glasses_name); - if (ret != TRUE) { + ret = NvCtrlGetStringDisplayAttribute(ctk_3d_vision_pro->handle, glasses_id, + NV_CTRL_STRING_3D_VISION_PRO_GLASSES_NAME, + &glasses_name); + if (ret != NvCtrlSuccess) { glasses_name = NULL; } - ret = XNVCTRLQueryTargetAttribute(NvCtrlGetDisplayPtr(ctk_3d_vision_pro->handle), - NV_CTRL_TARGET_TYPE_3D_VISION_PRO_TRANSCEIVER, - target_id, glasses_id, - NV_CTRL_3D_VISION_PRO_GLASSES_BATTERY_LEVEL, - (int *)&battery_level); - if (ret != TRUE) { + ret = NvCtrlGetDisplayAttribute(ctk_3d_vision_pro->handle, glasses_id, + NV_CTRL_3D_VISION_PRO_GLASSES_BATTERY_LEVEL, + (int *)&battery_level); + if (ret != NvCtrlSuccess) { battery_level = 0; } // Create glasses_info @@ -674,12 +656,10 @@ static gboolean poll_pairing(gpointer user_data) } if (ctk_3d_vision_pro->add_glasses_dlg->in_pairing) { - - XNVCTRLSetTargetAttribute(NvCtrlGetDisplayPtr(ctk_3d_vision_pro->handle), - NV_CTRL_TARGET_TYPE_3D_VISION_PRO_TRANSCEIVER, - 0, 0, - NV_CTRL_3D_VISION_PRO_PAIR_GLASSES, - PAIRING_TIMEOUT); // pairing on + /* Enable pairing for PAIRING_TIMEOUT seconds */ + NvCtrlSetAttribute(ctk_3d_vision_pro->handle, + NV_CTRL_3D_VISION_PRO_PAIR_GLASSES, + PAIRING_TIMEOUT); XFlush(NvCtrlGetDisplayPtr(ctk_3d_vision_pro->handle)); } @@ -701,7 +681,7 @@ static void enable_widgets(Ctk3DVisionPro *ctk_3d_vision_pro, Bool enable) gtk_widget_set_sensitive(ctk_3d_vision_pro->table.hscrollbar, enable); } -static void svp_config_changed(GtkObject *object, gpointer arg1, +static void svp_config_changed(GtkWidget *widget, gpointer arg1, gpointer user_data) { CtkEventStruct *event_struct; @@ -723,17 +703,18 @@ static void svp_config_changed(GtkObject *object, gpointer arg1, case NV_CTRL_3D_VISION_PRO_TRANSCEIVER_MODE: { SVP_RANGE range; - range = gtk_option_menu_get_history(GTK_OPTION_MENU(ctk_3d_vision_pro->option_menu)); + CtkDropDownMenu *menu = CTK_DROP_DOWN_MENU(widget); + range = ctk_drop_down_menu_get_current_value(menu); if (range != CHANNEL_RANGE_TO_OPTION_MENU_IDX(event_struct->value)) { - g_signal_handlers_block_by_func(ctk_3d_vision_pro->option_menu, + g_signal_handlers_block_by_func(ctk_3d_vision_pro->menu, channel_range_changed, ctk_3d_vision_pro); HTU(0)->channel_range = event_struct->value; - gtk_option_menu_set_history(GTK_OPTION_MENU(ctk_3d_vision_pro->option_menu), + ctk_drop_down_menu_set_current_value(menu, CHANNEL_RANGE_TO_OPTION_MENU_IDX(event_struct->value)); enable_widgets(ctk_3d_vision_pro, (HTU(0)->channel_range == SVP_LONG_RANGE ? FALSE : TRUE)); - g_signal_handlers_unblock_by_func(ctk_3d_vision_pro->option_menu, + g_signal_handlers_unblock_by_func(ctk_3d_vision_pro->menu, channel_range_changed, ctk_3d_vision_pro); } break; @@ -759,20 +740,18 @@ static void svp_config_changed(GtkObject *object, gpointer arg1, { int i; for (i = 0; i < HTU(0)->num_glasses; i++) { - int target_id = 0; - Bool ret; + ReturnStatus ret; char *glasses_name = NULL; GlassesInfo *glasses = glasses = HTU(0)->glasses_info[i]; - ret = XNVCTRLQueryTargetStringAttribute(NvCtrlGetDisplayPtr(ctk_3d_vision_pro->handle), - NV_CTRL_TARGET_TYPE_3D_VISION_PRO_TRANSCEIVER, - target_id, glasses->glasses_id, - NV_CTRL_STRING_3D_VISION_PRO_GLASSES_NAME, - &glasses_name); + ret = NvCtrlGetStringDisplayAttribute(ctk_3d_vision_pro->handle, + glasses->glasses_id, + NV_CTRL_STRING_3D_VISION_PRO_GLASSES_NAME, + &glasses_name); - if (ret != TRUE || glasses_name == NULL) { + if (ret != NvCtrlSuccess || glasses_name == NULL) { continue; - } + } strncpy(glasses->name, glasses_name, sizeof(glasses->name)); glasses->name[sizeof(glasses->name)-1] = '\0'; free(glasses_name); @@ -803,16 +782,14 @@ static void refresh_button_clicked(GtkButton *button, gpointer user_data) Bool ret; for (i = 0; i < HTU(0)->num_glasses; i++) { - int target_id = 0; int battery_level; GlassesInfo *glasses = glasses = HTU(0)->glasses_info[i]; - ret = XNVCTRLQueryTargetAttribute(NvCtrlGetDisplayPtr(ctk_3d_vision_pro->handle), - NV_CTRL_TARGET_TYPE_3D_VISION_PRO_TRANSCEIVER, - target_id, glasses->glasses_id, - NV_CTRL_3D_VISION_PRO_GLASSES_BATTERY_LEVEL, - (int *)&battery_level); - if (ret != TRUE) { + ret = NvCtrlGetDisplayAttribute(ctk_3d_vision_pro->handle, + glasses->glasses_id, + NV_CTRL_3D_VISION_PRO_GLASSES_BATTERY_LEVEL, + (int *)&battery_level); + if (ret != NvCtrlSuccess) { battery_level = 0; } @@ -821,11 +798,10 @@ static void refresh_button_clicked(GtkButton *button, gpointer user_data) update_glasses_info_data_table(&(ctk_3d_vision_pro->table), HTU(0)->glasses_info); gtk_widget_show_all(GTK_WIDGET(ctk_3d_vision_pro->table.data_table)); - ret = XNVCTRLQueryTargetAttribute(NvCtrlGetDisplayPtr(ctk_3d_vision_pro->handle), - NV_CTRL_TARGET_TYPE_3D_VISION_PRO_TRANSCEIVER, - 0, HTU(0)->channel_num, - NV_CTRL_3D_VISION_PRO_TRANSCEIVER_CHANNEL_QUALITY, - (int *)&(HTU(0)->signal_strength)); + ret = NvCtrlGetDisplayAttribute(ctk_3d_vision_pro->handle, + HTU(0)->channel_num, + NV_CTRL_3D_VISION_PRO_TRANSCEIVER_CHANNEL_QUALITY, + (int *)&(HTU(0)->signal_strength)); if (ret != TRUE) { HTU(0)->signal_strength = 0; } @@ -977,11 +953,9 @@ static void add_glasses_button_clicked(GtkButton *button, gpointer user_data) break; default: for (i = 0; i < dlg->new_glasses; i++) { - XNVCTRLSetTargetAttribute(NvCtrlGetDisplayPtr(ctk_3d_vision_pro->handle), - NV_CTRL_TARGET_TYPE_3D_VISION_PRO_TRANSCEIVER, - 0, 0, - NV_CTRL_3D_VISION_PRO_UNPAIR_GLASSES, - dlg->glasses_info[i]->glasses_id); + NvCtrlSetAttribute(ctk_3d_vision_pro->handle, + NV_CTRL_3D_VISION_PRO_UNPAIR_GLASSES, + dlg->glasses_info[i]->glasses_id); } break; } @@ -1046,6 +1020,7 @@ static RemoveGlassesDlg *create_remove_glasses_dlg(Ctk3DVisionPro *ctk_3d_vision static void remove_button_clicked(GtkButton *button, gpointer user_data) { Ctk3DVisionPro *ctk_3d_vision_pro = CTK_3D_VISION_PRO(user_data); + CtkDropDownMenu *menu; RemoveGlassesDlg *dlg; gint result; @@ -1071,17 +1046,16 @@ static void remove_button_clicked(GtkButton *button, gpointer user_data) /* Handle user's response */ switch (result) { case GTK_RESPONSE_OK: - dlg->glasses_selected_index = gtk_option_menu_get_history(GTK_OPTION_MENU(dlg->mnu_glasses_name)); + menu = CTK_DROP_DOWN_MENU(dlg->mnu_glasses_name); + dlg->glasses_selected_index = ctk_drop_down_menu_get_current_value(menu); if (dlg->glasses_selected_index >= 0 && dlg->glasses_selected_index < HTU(0)->num_glasses) { unsigned int glasses_id = HTU(0)->glasses_info[dlg->glasses_selected_index]->glasses_id; - XNVCTRLSetTargetAttribute(NvCtrlGetDisplayPtr(ctk_3d_vision_pro->handle), - NV_CTRL_TARGET_TYPE_3D_VISION_PRO_TRANSCEIVER, - 0, 0, - NV_CTRL_3D_VISION_PRO_UNPAIR_GLASSES, - glasses_id); + NvCtrlSetAttribute(ctk_3d_vision_pro->handle, + NV_CTRL_3D_VISION_PRO_UNPAIR_GLASSES, + glasses_id); } break; default: @@ -1173,17 +1147,16 @@ static void identify_button_clicked(GtkButton *button, gpointer user_data) /* Handle user's response */ switch (result) { case GTK_RESPONSE_OK: - dlg->glasses_selected_index = gtk_option_menu_get_history(GTK_OPTION_MENU(dlg->mnu_glasses_name)); + dlg->glasses_selected_index = + ctk_drop_down_menu_get_current_value(CTK_DROP_DOWN_MENU(dlg->mnu_glasses_name)); if (dlg->glasses_selected_index >= 0 && dlg->glasses_selected_index < HTU(0)->num_glasses) { glasses_id = HTU(0)->glasses_info[dlg->glasses_selected_index]->glasses_id; - XNVCTRLSetTargetAttribute(NvCtrlGetDisplayPtr(ctk_3d_vision_pro->handle), - NV_CTRL_TARGET_TYPE_3D_VISION_PRO_TRANSCEIVER, - 0, 0, - NV_CTRL_3D_VISION_PRO_IDENTIFY_GLASSES, - glasses_id); + NvCtrlSetAttribute(ctk_3d_vision_pro->handle, + NV_CTRL_3D_VISION_PRO_IDENTIFY_GLASSES, + glasses_id); } break; default: @@ -1294,7 +1267,7 @@ static void rename_button_clicked(GtkButton *button, gpointer user_data) if (result == GTK_RESPONSE_ACCEPT) { int i; dlg->glasses_selected_index = - gtk_option_menu_get_history(GTK_OPTION_MENU(dlg->mnu_glasses_name)); + ctk_drop_down_menu_get_current_value(CTK_DROP_DOWN_MENU(dlg->mnu_glasses_name)); if (dlg->glasses_new_name == NULL || strlen(dlg->glasses_new_name) == 0) { continue; @@ -1309,16 +1282,15 @@ static void rename_button_clicked(GtkButton *button, gpointer user_data) if (i == HTU(0)->num_glasses) { if (dlg->glasses_selected_index >= 0 && dlg->glasses_selected_index < HTU(0)->num_glasses) { - Bool ret; + ReturnStatus ret; unsigned int glasses_id = HTU(0)->glasses_info[dlg->glasses_selected_index]->glasses_id; - ret = XNVCTRLSetTargetStringAttribute(NvCtrlGetDisplayPtr(ctk_3d_vision_pro->handle), - NV_CTRL_TARGET_TYPE_3D_VISION_PRO_TRANSCEIVER, - 0, glasses_id, + ret = NvCtrlSetStringDisplayAttribute(ctk_3d_vision_pro->handle, + glasses_id, NV_CTRL_STRING_3D_VISION_PRO_GLASSES_NAME, - dlg->glasses_new_name); - - if (ret == False) { + dlg->glasses_new_name, + NULL); + if (ret != NvCtrlSuccess) { continue; } strncpy(HTU(0)->glasses_info[dlg->glasses_selected_index]->name, dlg->glasses_new_name, @@ -1403,17 +1375,19 @@ static ChannelRangeDlg *create_channel_range_change_dlg(Ctk3DVisionPro *ctk_3d_v } static void channel_range_changed( - GtkOptionMenu *option_menu, + GtkWidget *widget, gpointer user_data ) { Ctk3DVisionPro *ctk_3d_vision_pro = CTK_3D_VISION_PRO(user_data); + CtkDropDownMenu *menu = CTK_DROP_DOWN_MENU(widget); ChannelRangeDlg *dlg; gint result; SVP_RANGE range; SVP_RANGE prev_range; - range = OPTION_MENU_IDX_TO_CHANNEL_RANGE(gtk_option_menu_get_history(option_menu)); + range = + OPTION_MENU_IDX_TO_CHANNEL_RANGE(ctk_drop_down_menu_get_current_value(menu)); prev_range = HTU(0)->channel_range; if (HTU(0)->channel_range == range) { @@ -1439,18 +1413,16 @@ static void channel_range_changed( case GTK_RESPONSE_YES: HTU(0)->channel_range = range; /* Send NV-Control command */ - XNVCTRLSetTargetAttribute(NvCtrlGetDisplayPtr(ctk_3d_vision_pro->handle), - NV_CTRL_TARGET_TYPE_3D_VISION_PRO_TRANSCEIVER, - 0, 0, - NV_CTRL_3D_VISION_PRO_TRANSCEIVER_MODE, - (HTU(0)->channel_range)); + NvCtrlSetAttribute(ctk_3d_vision_pro->handle, + NV_CTRL_3D_VISION_PRO_TRANSCEIVER_MODE, + (HTU(0)->channel_range)); enable_widgets(ctk_3d_vision_pro, (HTU(0)->channel_range == SVP_LONG_RANGE ? FALSE : TRUE)); break; case GTK_RESPONSE_NO: - gtk_option_menu_set_history(GTK_OPTION_MENU(ctk_3d_vision_pro->option_menu), - CHANNEL_RANGE_TO_OPTION_MENU_IDX(prev_range)); + ctk_drop_down_menu_set_current_value(menu, + CHANNEL_RANGE_TO_OPTION_MENU_IDX(prev_range)); break; default: /* do nothing. */ @@ -1482,12 +1454,11 @@ GtkWidget* ctk_3d_vision_pro_new(NvCtrlAttributeHandle *handle, GtkWidget *hseparator; int i; GtkWidget *image; - GtkWidget *menu; + CtkDropDownMenu *menu; char temp[64]; //scratchpad memory used to construct labels. unsigned char *paired_glasses_list = NULL; int len; - int target_id = 0; - Bool ret; + ReturnStatus ret; object = g_object_new(CTK_TYPE_3D_VISION_PRO, NULL); ctk_3d_vision_pro = CTK_3D_VISION_PRO(object); @@ -1506,43 +1477,35 @@ GtkWidget* ctk_3d_vision_pro_new(NvCtrlAttributeHandle *handle, HTU(i) = htu; } - ret = XNVCTRLQueryTargetAttribute(NvCtrlGetDisplayPtr(handle), - NV_CTRL_TARGET_TYPE_3D_VISION_PRO_TRANSCEIVER, - 0, 0, - NV_CTRL_3D_VISION_PRO_TRANSCEIVER_CHANNEL, - (int *)&(HTU(0)->channel_num)); - if (ret != TRUE) { + ret = NvCtrlGetAttribute(handle, + NV_CTRL_3D_VISION_PRO_TRANSCEIVER_CHANNEL, + (int *)&(HTU(0)->channel_num)); + if (ret != NvCtrlSuccess) { HTU(0)->channel_num = 0; } - ret = XNVCTRLQueryTargetAttribute(NvCtrlGetDisplayPtr(handle), - NV_CTRL_TARGET_TYPE_3D_VISION_PRO_TRANSCEIVER, - 0, HTU(0)->channel_num, - NV_CTRL_3D_VISION_PRO_TRANSCEIVER_CHANNEL_QUALITY, - (int *)&(HTU(0)->signal_strength)); - if (ret != TRUE) { + ret = NvCtrlGetDisplayAttribute(handle, HTU(0)->channel_num, + NV_CTRL_3D_VISION_PRO_TRANSCEIVER_CHANNEL_QUALITY, + (int *)&(HTU(0)->signal_strength)); + if (ret != NvCtrlSuccess) { HTU(0)->signal_strength = 0; } - ret = XNVCTRLQueryTargetAttribute(NvCtrlGetDisplayPtr(handle), - NV_CTRL_TARGET_TYPE_3D_VISION_PRO_TRANSCEIVER, - 0, 0, - NV_CTRL_3D_VISION_PRO_TRANSCEIVER_MODE, - (int *)&(HTU(0)->channel_range)); - if (ret != TRUE || + ret = NvCtrlGetAttribute(handle, + NV_CTRL_3D_VISION_PRO_TRANSCEIVER_MODE, + (int *)&(HTU(0)->channel_range)); + if (ret != NvCtrlSuccess || (!(HTU(0)->channel_range == SVP_SHORT_RANGE || HTU(0)->channel_range == SVP_MEDIUM_RANGE || HTU(0)->channel_range == SVP_LONG_RANGE))) { HTU(0)->channel_range = SVP_SHORT_RANGE; } - ret = XNVCTRLQueryTargetBinaryData(NvCtrlGetDisplayPtr(ctk_3d_vision_pro->handle), - NV_CTRL_TARGET_TYPE_3D_VISION_PRO_TRANSCEIVER, - target_id, 0, - NV_CTRL_BINARY_DATA_GLASSES_PAIRED_TO_3D_VISION_PRO_TRANSCEIVER, - &paired_glasses_list, &len); + ret = NvCtrlGetBinaryAttribute(handle, 0, + NV_CTRL_BINARY_DATA_GLASSES_PAIRED_TO_3D_VISION_PRO_TRANSCEIVER, + &paired_glasses_list, &len); - if (ret != TRUE) { + if (ret != NvCtrlSuccess) { HTU(0)->num_glasses = 0; HTU(0)->glasses_info = NULL; } else { @@ -1559,21 +1522,17 @@ GtkWidget* ctk_3d_vision_pro_new(NvCtrlAttributeHandle *handle, HTU(0)->glasses_info[i] = glasses; - ret = XNVCTRLQueryTargetStringAttribute(NvCtrlGetDisplayPtr(ctk_3d_vision_pro->handle), - NV_CTRL_TARGET_TYPE_3D_VISION_PRO_TRANSCEIVER, - target_id, glasses_id, - NV_CTRL_STRING_3D_VISION_PRO_GLASSES_NAME, - &glasses_name); - if (ret != TRUE) { + ret = NvCtrlGetStringDisplayAttribute(handle, glasses_id, + NV_CTRL_STRING_3D_VISION_PRO_GLASSES_NAME, + &glasses_name); + if (ret != NvCtrlSuccess) { glasses_name = NULL; } - ret = XNVCTRLQueryTargetAttribute(NvCtrlGetDisplayPtr(ctk_3d_vision_pro->handle), - NV_CTRL_TARGET_TYPE_3D_VISION_PRO_TRANSCEIVER, - target_id, glasses_id, - NV_CTRL_3D_VISION_PRO_GLASSES_BATTERY_LEVEL, - (int *)&battery_level); - if (ret != TRUE) { + ret = NvCtrlGetDisplayAttribute(handle, glasses_id, + NV_CTRL_3D_VISION_PRO_GLASSES_BATTERY_LEVEL, + (int *)&battery_level); + if (ret != NvCtrlSuccess) { battery_level = 0; } @@ -1711,31 +1670,31 @@ GtkWidget* ctk_3d_vision_pro_new(NvCtrlAttributeHandle *handle, gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); hbox = gtk_hbox_new(FALSE, 5); - menu = gtk_menu_new(); - add_menu_item(menu, "Short Range (up to 5 meters)"); - add_menu_item(menu, "Medium Range (up to 15 meters)"); - add_menu_item(menu, "Long Range"); - ctk_3d_vision_pro->option_menu = gtk_option_menu_new (); - gtk_option_menu_set_menu - (GTK_OPTION_MENU(ctk_3d_vision_pro->option_menu), menu); + menu = (CtkDropDownMenu *) + ctk_drop_down_menu_new(CTK_DROP_DOWN_MENU_FLAG_COMBO); + ctk_drop_down_menu_append_item(menu, "Short Range (up to 5 meters)", 0); + ctk_drop_down_menu_append_item(menu, "Medium Range (up to 15 meters)", 1); + ctk_drop_down_menu_append_item(menu, "Long Range", 2); + + ctk_3d_vision_pro->menu = GTK_WIDGET(menu); alignment = gtk_alignment_new(0, 1, 0, 0); gtk_box_pack_start(GTK_BOX(hbox), alignment, TRUE, TRUE, 0); - gtk_container_add(GTK_CONTAINER(alignment), ctk_3d_vision_pro->option_menu); + gtk_container_add(GTK_CONTAINER(alignment), ctk_3d_vision_pro->menu); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); - g_object_set_data(G_OBJECT(ctk_3d_vision_pro->option_menu), + g_object_set_data(G_OBJECT(ctk_3d_vision_pro->menu), "channel_range", GINT_TO_POINTER(0)); - gtk_option_menu_set_history(GTK_OPTION_MENU(ctk_3d_vision_pro->option_menu), - CHANNEL_RANGE_TO_OPTION_MENU_IDX(HTU(0)->channel_range)); - g_signal_connect(G_OBJECT(ctk_3d_vision_pro->option_menu), "changed", + ctk_drop_down_menu_set_current_value((menu), + CHANNEL_RANGE_TO_OPTION_MENU_IDX(HTU(0)->channel_range)); + g_signal_connect(G_OBJECT(ctk_3d_vision_pro->menu), "changed", G_CALLBACK(channel_range_changed), (gpointer) ctk_3d_vision_pro); enable_widgets(ctk_3d_vision_pro, (HTU(0)->channel_range == SVP_LONG_RANGE ? FALSE : TRUE)); - ctk_config_set_tooltip(ctk_config, ctk_3d_vision_pro->option_menu, + ctk_config_set_tooltip(ctk_config, ctk_3d_vision_pro->menu, __channel_range_tooltip); ctk_3d_vision_pro->parent_wnd = GTK_WINDOW(gtk_widget_get_parent(GTK_WIDGET(ctk_3d_vision_pro))); @@ -1872,12 +1831,12 @@ GtkTextBuffer *ctk_3d_vision_pro_create_help(GtkTextTagTable *table) ctk_help_para(b, &i, "Shows the signal strength of the current hub channel as an icon " "and also value in percentage. \n" "Signal strength is from one of the six ranges below-\n" - "\tExcellent\t\t [100%]\n" - "\tVery Good\t [>75% - <100%]\n" - "\tGood \t\t [>50% - <75%]\n" - "\tLow \t\t [>25% - <50%]\n" - "\tVery Low \t\t [>0% - <25%]\n" - "\tNo Signal\t\t [0%]"); + "\tExcellent\t\t [100%%]\n" + "\tVery Good\t [>75%% - <100%%]\n" + "\tGood \t\t [>50%% - <75%%]\n" + "\tLow \t\t [>25%% - <50%%]\n" + "\tVery Low \t\t [>0%% - <25%%]\n" + "\tNo Signal\t\t [0%%]"); ctk_help_heading(b, &i, "Hub Range"); ctk_help_para(b, &i, __channel_range_tooltip); diff --git a/src/gtk+-2.x/ctk3dvisionpro.h b/src/gtk+-2.x/ctk3dvisionpro.h index 45ec99a..4786f13 100644 --- a/src/gtk+-2.x/ctk3dvisionpro.h +++ b/src/gtk+-2.x/ctk3dvisionpro.h @@ -108,7 +108,7 @@ struct _Ctk3DVisionPro NvCtrlAttributeHandle *handle; GtkWindow *parent_wnd; CtkConfig *ctk_config; - GtkWidget *option_menu; + GtkWidget *menu; guint num_htu; HtuInfo** htu_info; diff --git a/src/gtk+-2.x/ctkapcprofilemodel.c b/src/gtk+-2.x/ctkapcprofilemodel.c new file mode 100644 index 0000000..d815195 --- /dev/null +++ b/src/gtk+-2.x/ctkapcprofilemodel.c @@ -0,0 +1,686 @@ +/* + * nvidia-settings: A tool for configuring the NVIDIA X driver on Unix + * and Linux systems. + * + * Copyright (C) 2013 NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses>. + */ + +// Tree model implementation for operating on profiles in an AppProfileConfig + +#include <gtk/gtk.h> +#include <stdint.h> +#include "ctkapcprofilemodel.h" +#include "ctkappprofile.h" +#include <string.h> +#include <stdlib.h> + +static GObjectClass *parent_class = NULL; + +// Forward declarations +static void apc_profile_model_init(CtkApcProfileModel *prof_model); +static void apc_profile_model_finalize(GObject *object); +static void apc_profile_model_tree_model_init(GtkTreeModelIface *iface); +static GtkTreeModelFlags apc_profile_model_get_flags(GtkTreeModel *tree_model); +static gint apc_profile_model_get_n_columns(GtkTreeModel *tree_model); +static GType apc_profile_model_get_column_type(GtkTreeModel *tree_model, gint index); +static gboolean apc_profile_model_get_iter(GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreePath *path); +static GtkTreePath *apc_profile_model_get_path(GtkTreeModel *tree_model, GtkTreeIter *iter); +static gboolean apc_profile_model_iter_next(GtkTreeModel *tree_model, + GtkTreeIter *iter); +static gboolean apc_profile_model_iter_children(GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent); +static gboolean apc_profile_model_iter_has_child(GtkTreeModel *tree_model, + GtkTreeIter *iter); +static gint apc_profile_model_iter_n_children(GtkTreeModel *tree_model, + GtkTreeIter *iter); +static gboolean +apc_profile_model_iter_nth_child(GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent, + gint n); +static gboolean apc_profile_model_iter_parent(GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *child); +static void apc_profile_model_class_init(CtkApcProfileModelClass *klass); +static void apc_profile_model_get_value(GtkTreeModel *tree_model, + GtkTreeIter *iter, + gint column, + GValue *value); + + +static void +apc_profile_model_tree_sortable_init(GtkTreeSortableIface *iface); + +static gboolean apc_profile_model_get_sort_column_id(GtkTreeSortable *sortable, + gint *sort_column_id, + GtkSortType *order); +static void apc_profile_model_set_sort_column_id(GtkTreeSortable *sortable, + gint sort_column_id, + GtkSortType order); +static void apc_profile_model_set_sort_func(GtkTreeSortable *sortable, + gint sort_column_id, + GtkTreeIterCompareFunc sort_func, + gpointer user_data, + GDestroyNotify destroy); +static void apc_profile_model_set_default_sort_func(GtkTreeSortable *sortable, + GtkTreeIterCompareFunc sort_func, + gpointer user_data, + GDestroyNotify destroy); +static gboolean apc_profile_model_has_default_sort_func(GtkTreeSortable *sortable); + + +GType ctk_apc_profile_model_get_type(void) +{ + static GType apc_profile_model_type = 0; + if (!apc_profile_model_type) { + static const GTypeInfo apc_profile_model_info = { + sizeof (CtkApcProfileModelClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) apc_profile_model_class_init, /* constructor */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (CtkApcProfileModel), + 0, /* n_preallocs */ + (GInstanceInitFunc) apc_profile_model_init, /* instance_init */ + NULL /* value_table */ + }; + static const GInterfaceInfo tree_model_info = + { + (GInterfaceInitFunc) apc_profile_model_tree_model_init, /* interface_init */ + NULL, /* interface_finalize */ + NULL /* interface_data */ + }; + static const GInterfaceInfo tree_sortable_info = + { + (GInterfaceInitFunc) apc_profile_model_tree_sortable_init, /* interface_init */ + NULL, /* interface_finalize */ + NULL /* interface_data */ + }; + + apc_profile_model_type = + g_type_register_static(G_TYPE_OBJECT, "CtkApcProfileModel", + &apc_profile_model_info, 0); + + g_type_add_interface_static(apc_profile_model_type, GTK_TYPE_TREE_MODEL, &tree_model_info); + g_type_add_interface_static(apc_profile_model_type, GTK_TYPE_TREE_SORTABLE, &tree_sortable_info); + } + + return apc_profile_model_type; +} + +static void apc_profile_model_class_init(CtkApcProfileModelClass *klass) +{ + GObjectClass *object_class; + + parent_class = (GObjectClass *)g_type_class_peek_parent(klass); + object_class = (GObjectClass *)klass; + + object_class->finalize = apc_profile_model_finalize; +} + +static gint apc_profile_model_sort_name(GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) +{ + gint result; + gchar *profile_name_a, *profile_name_b; + gtk_tree_model_get(model, a, CTK_APC_PROFILE_MODEL_COL_NAME, &profile_name_a, -1); + gtk_tree_model_get(model, b, CTK_APC_PROFILE_MODEL_COL_NAME, &profile_name_b, -1); + result = strcmp(profile_name_a, profile_name_b); + free(profile_name_a); + free(profile_name_b); + return result; +} + +static gint apc_profile_model_sort_filename(GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) +{ + gint result; + gchar *filename_a, *filename_b; + gtk_tree_model_get(model, a, CTK_APC_PROFILE_MODEL_COL_FILENAME, &filename_a, -1); + gtk_tree_model_get(model, b, CTK_APC_PROFILE_MODEL_COL_FILENAME, &filename_b, -1); + result = strcmp(filename_a, filename_b); + free(filename_a); + free(filename_b); + return result; +} + +static gint apc_profile_model_sort_settings(GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) +{ + gint result; + json_t *settings_a, *settings_b; + char *settings_string_a, *settings_string_b; + gtk_tree_model_get(model, a, CTK_APC_PROFILE_MODEL_COL_SETTINGS, &settings_a, -1); + gtk_tree_model_get(model, b, CTK_APC_PROFILE_MODEL_COL_SETTINGS, &settings_b, -1); + settings_string_a = serialize_settings(settings_a, FALSE); + settings_string_b = serialize_settings(settings_b, FALSE); + result = strcmp(settings_string_a, settings_string_b); + free(settings_string_a); + free(settings_string_b); + json_decref(settings_a); + json_decref(settings_b); + return result; +} + +static void apc_profile_model_init(CtkApcProfileModel *prof_model) +{ + prof_model->stamp = g_random_int(); // random int to catch iterator type mismatches + prof_model->config = NULL; + + prof_model->profiles = g_array_new(FALSE, TRUE, sizeof(char *)); + prof_model->sort_column_id = CTK_APC_PROFILE_MODEL_DEFAULT_SORT_COL; + prof_model->order = GTK_SORT_DESCENDING; + + prof_model->sort_funcs[CTK_APC_PROFILE_MODEL_COL_NAME] = apc_profile_model_sort_name; + prof_model->sort_funcs[CTK_APC_PROFILE_MODEL_COL_FILENAME] = apc_profile_model_sort_filename; + prof_model->sort_funcs[CTK_APC_PROFILE_MODEL_COL_SETTINGS] = apc_profile_model_sort_settings; + + memset(prof_model->sort_user_data, 0, sizeof(prof_model->sort_user_data)); + memset(prof_model->sort_destroy_notify, 0, sizeof(prof_model->sort_destroy_notify)); +} + +static void apc_profile_model_finalize(GObject *object) +{ + guint i; + CtkApcProfileModel *prof_model = CTK_APC_PROFILE_MODEL(object); + + for (i = 0; i < prof_model->profiles->len; i++) { + char *profile_name = g_array_index(prof_model->profiles, char*, i); + free(profile_name); + } + + for (i = 0; i < CTK_APC_PROFILE_MODEL_N_COLUMNS; i++) { + if (prof_model->sort_destroy_notify[i]) { + (*prof_model->sort_destroy_notify[i])( + prof_model->sort_user_data[i] + ); + } + } + + g_array_free(prof_model->profiles, TRUE); + parent_class->finalize(object); +} + +static void +apc_profile_model_tree_model_init(GtkTreeModelIface *iface) +{ + iface->get_flags = apc_profile_model_get_flags; + iface->get_n_columns = apc_profile_model_get_n_columns; + iface->get_column_type = apc_profile_model_get_column_type; + iface->get_iter = apc_profile_model_get_iter; + iface->get_path = apc_profile_model_get_path; + iface->get_value = apc_profile_model_get_value; + iface->iter_next = apc_profile_model_iter_next; + iface->iter_children = apc_profile_model_iter_children; + iface->iter_has_child = apc_profile_model_iter_has_child; + iface->iter_n_children = apc_profile_model_iter_n_children; + iface->iter_nth_child = apc_profile_model_iter_nth_child; + iface->iter_parent = apc_profile_model_iter_parent; +} + +static void +apc_profile_model_tree_sortable_init(GtkTreeSortableIface *iface) +{ + iface->get_sort_column_id = apc_profile_model_get_sort_column_id; + iface->set_sort_column_id = apc_profile_model_set_sort_column_id; + iface->set_sort_func = apc_profile_model_set_sort_func; + iface->set_default_sort_func = apc_profile_model_set_default_sort_func; + iface->has_default_sort_func = apc_profile_model_has_default_sort_func; +} + +static GtkTreeModelFlags apc_profile_model_get_flags(GtkTreeModel *tree_model) +{ + return GTK_TREE_MODEL_LIST_ONLY; +} + +static gint apc_profile_model_get_n_columns(GtkTreeModel *tree_model) +{ + return CTK_APC_PROFILE_MODEL_N_COLUMNS; +} + +static GType apc_profile_model_get_column_type(GtkTreeModel *tree_model, gint index) +{ + switch (index) { + case CTK_APC_PROFILE_MODEL_COL_NAME: + return G_TYPE_STRING; + case CTK_APC_PROFILE_MODEL_COL_FILENAME: + return G_TYPE_STRING; + case CTK_APC_PROFILE_MODEL_COL_SETTINGS: + return G_TYPE_POINTER; + default: + assert(0); + return G_TYPE_INVALID; + } +} + +static inline void set_iter_to_index(GtkTreeIter *iter, intptr_t idx) +{ + iter->user_data = (gpointer)idx; + iter->user_data2 = NULL; // unused + iter->user_data3 = NULL; // unused +} + +static gboolean apc_profile_model_get_iter(GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreePath *path) +{ + CtkApcProfileModel *prof_model; + gint depth, *indices; + intptr_t n; + + assert(path); + prof_model = CTK_APC_PROFILE_MODEL(tree_model); + + indices = gtk_tree_path_get_indices(path); + depth = gtk_tree_path_get_depth(path); + + assert(depth == 1); + (void)(depth); + + n = indices[0]; + + if (n >= prof_model->profiles->len || n < 0) { + return FALSE; + } + + iter->stamp = prof_model->stamp; + set_iter_to_index(iter, n); + + return TRUE; +} + +static GtkTreePath *apc_profile_model_get_path(GtkTreeModel *tree_model, GtkTreeIter *iter) +{ + GtkTreePath *path; + intptr_t n; + + g_return_val_if_fail(iter, NULL); + + n = (intptr_t)iter->user_data; + + path = gtk_tree_path_new(); + gtk_tree_path_append_index(path, n); + + return path; +} + +static void apc_profile_model_get_value(GtkTreeModel *tree_model, + GtkTreeIter *iter, + gint column, + GValue *value) +{ + const char *profile_name; + const char *filename; + CtkApcProfileModel *prof_model; + const json_t *profile; + json_t *settings; + intptr_t n; + + g_value_init(value, apc_profile_model_get_column_type(tree_model, column)); + prof_model = CTK_APC_PROFILE_MODEL(tree_model); + + n = (intptr_t)iter->user_data; + profile_name = g_array_index(prof_model->profiles, char*, n); + + switch (column) { + case CTK_APC_PROFILE_MODEL_COL_NAME: + g_value_set_string(value, profile_name); + break; + case CTK_APC_PROFILE_MODEL_COL_FILENAME: + filename = nv_app_profile_config_get_profile_filename(prof_model->config, + profile_name); + assert(filename); + g_value_set_string(value, filename); + break; + case CTK_APC_PROFILE_MODEL_COL_SETTINGS: + profile = nv_app_profile_config_get_profile(prof_model->config, + profile_name); + settings = json_deep_copy(json_object_get(profile, "settings")); + assert(settings); + + g_value_set_pointer(value, settings); + break; + default: + assert(0); + break; + } +} + +static gboolean apc_profile_model_iter_next(GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + CtkApcProfileModel *prof_model; + intptr_t n; + + prof_model = CTK_APC_PROFILE_MODEL(tree_model); + + if (!iter) { + return FALSE; + } + + n = (intptr_t)iter->user_data; + n++; + + if (n >= prof_model->profiles->len) { + return FALSE; + } + + set_iter_to_index(iter, n); + + return TRUE; +} + +static gboolean apc_profile_model_iter_children(GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent) +{ + CtkApcProfileModel *prof_model = CTK_APC_PROFILE_MODEL(tree_model); + + if (parent) { + return FALSE; + } + + // (parent == NULL) => return first profile + + if (!prof_model->profiles->len) { + return FALSE; + } + + iter->stamp = prof_model->stamp; + set_iter_to_index(iter, 0); + + return TRUE; +} + +static gboolean apc_profile_model_iter_has_child(GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + return FALSE; +} + +static gint apc_profile_model_iter_n_children(GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + CtkApcProfileModel *prof_model = CTK_APC_PROFILE_MODEL(tree_model); + + return iter ? 0 : (gint)prof_model->profiles->len; +} + +static gboolean +apc_profile_model_iter_nth_child(GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent, + gint n_in) +{ + intptr_t n = (intptr_t)n_in; + CtkApcProfileModel *prof_model = CTK_APC_PROFILE_MODEL(tree_model); + + if (parent || + (n < 0) || + (n >= prof_model->profiles->len)) { + return FALSE; + } + + iter->stamp = prof_model->stamp; + set_iter_to_index(iter, n); + + return TRUE; +} + +static gboolean +apc_profile_model_iter_parent(GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *child) +{ + return FALSE; +} + +void ctk_apc_profile_model_attach(CtkApcProfileModel *prof_model, AppProfileConfig *config) +{ + GtkTreePath *path; + GtkTreeIter iter; + AppProfileConfigProfileIter *prof_iter; + const char *profile_name, *dup_profile_name; + gint i; + prof_model->config = config; + + // Clear existing profiles from the model + path = gtk_tree_path_new_from_indices(0, -1); + for (i = 0; i < prof_model->profiles->len; i++) { + // Emit a "row-deleted" signal for each deleted profile + // (we can just keep calling this on row 0) + gtk_tree_model_row_deleted(GTK_TREE_MODEL(prof_model), path); + } + gtk_tree_path_free(path); + g_array_set_size(prof_model->profiles, 0); + + // Load the profiles from the config into the model + for (prof_iter = nv_app_profile_config_profile_iter(config), i = 0; + prof_iter; + prof_iter = nv_app_profile_config_profile_iter_next(prof_iter)) { + profile_name = nv_app_profile_config_profile_iter_name(prof_iter); + dup_profile_name = strdup(profile_name); + g_array_append_val(prof_model->profiles, dup_profile_name); + + // emit a "row-inserted" signal for each new profile + path = gtk_tree_path_new_from_indices(i++, -1); + apc_profile_model_get_iter(GTK_TREE_MODEL(prof_model), &iter, path); + gtk_tree_model_row_inserted(GTK_TREE_MODEL(prof_model), path, &iter); + gtk_tree_path_free(path); + } +} + +CtkApcProfileModel *ctk_apc_profile_model_new(AppProfileConfig *config) +{ + CtkApcProfileModel *prof_model; + + prof_model = CTK_APC_PROFILE_MODEL(g_object_new(CTK_TYPE_APC_PROFILE_MODEL, NULL)); + assert(prof_model); + + ctk_apc_profile_model_attach(prof_model, config); + + return prof_model; +} + +static gint find_index_of_profile(CtkApcProfileModel *prof_model, const char *profile_name) +{ + gint i; + for (i = 0; i < prof_model->profiles->len; i++) { + if (!strcmp(g_array_index(prof_model->profiles, char*, i), profile_name)) { + return i; + } + } + + return -1; +} + +void ctk_apc_profile_model_update_profile(CtkApcProfileModel *prof_model, + const char *filename, + const char *profile_name, + json_t *profile) +{ + int profile_created; + GtkTreeIter iter; + GtkTreePath *path; + gint n; + + profile_created = nv_app_profile_config_update_profile(prof_model->config, + filename, + profile_name, + profile); + + if (profile_created) { + char *dup_profile_name = strdup(profile_name); + n = prof_model->profiles->len; + g_array_append_val(prof_model->profiles, dup_profile_name); + + // emit a "row-inserted" signal + path = gtk_tree_path_new_from_indices(n, -1); + apc_profile_model_get_iter(GTK_TREE_MODEL(prof_model), &iter, path); + gtk_tree_model_row_inserted(GTK_TREE_MODEL(prof_model), path, &iter); + gtk_tree_path_free(path); + + // TODO resort the array if necessary + } else { + // emit a "row-changed" signal + n = find_index_of_profile(prof_model, profile_name); + path = gtk_tree_path_new_from_indices(n, -1); + apc_profile_model_get_iter(GTK_TREE_MODEL(prof_model), &iter, path); + gtk_tree_model_row_changed(GTK_TREE_MODEL(prof_model), path, &iter); + gtk_tree_path_free(path); + } +} + +void ctk_apc_profile_model_delete_profile(CtkApcProfileModel *prof_model, + const char *profile_name) +{ + GtkTreePath *path; + gint n; + + n = find_index_of_profile(prof_model, profile_name); + assert(n >= 0); + + nv_app_profile_config_delete_profile(prof_model->config, profile_name); + g_array_remove_index(prof_model->profiles, n); + + // emit a "row-deleted" signal + path = gtk_tree_path_new_from_indices(n, -1); + gtk_tree_model_row_deleted(GTK_TREE_MODEL(prof_model), path); + gtk_tree_path_free(path); +} + +// XXX Not available in GTK+-2.2.1 +#ifndef GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID +# define GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID -2 +#endif + +static gboolean apc_profile_model_get_sort_column_id(GtkTreeSortable *sortable, + gint *sort_column_id, + GtkSortType *order) +{ + CtkApcProfileModel *prof_model = CTK_APC_PROFILE_MODEL(sortable); + if (sort_column_id) { + *sort_column_id = prof_model->sort_column_id; + } + if (order) { + *order = prof_model->order; + } + + return (prof_model->sort_column_id != GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID) && + (prof_model->sort_column_id != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID); +} + +static gint compare_profiles(gconstpointer a, gconstpointer b, gpointer user_data) +{ + gint result; + GtkTreeIter iter_a, iter_b; + intptr_t ia, ib; + char * const *buf; + gint sort_column_id; + CtkApcProfileModel *prof_model = (CtkApcProfileModel *)user_data; + + sort_column_id = prof_model->sort_column_id; + if (sort_column_id <= GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID) { + return 0; + } + + // XXX hack: get the position of these pointers in the container array + buf = (char * const *)prof_model->profiles->data; + ia = ((char * const *)a - buf); + ib = ((char * const *)b - buf); + + // Build iterators to be used by the comparison function + iter_a.stamp = prof_model->stamp; + set_iter_to_index(&iter_a, ia); + + iter_b.stamp = prof_model->stamp; + set_iter_to_index(&iter_b, ib); + + result = (*prof_model->sort_funcs[sort_column_id])(GTK_TREE_MODEL(prof_model), + &iter_a, + &iter_b, + prof_model->sort_user_data[sort_column_id]); + + return (prof_model->order == GTK_SORT_DESCENDING) ? -result : result; +} + +static void apc_profile_model_resort(CtkApcProfileModel *prof_model) +{ + // Emit the "sort-column-changed" signal + gtk_tree_sortable_sort_column_changed(GTK_TREE_SORTABLE(prof_model)); + + // Reorder the model based on the sort func + g_array_sort_with_data(prof_model->profiles, compare_profiles, (gpointer)prof_model); +} + +static void apc_profile_model_set_sort_column_id(GtkTreeSortable *sortable, + gint sort_column_id, + GtkSortType order) +{ + CtkApcProfileModel *prof_model = CTK_APC_PROFILE_MODEL(sortable); + + if ((prof_model->sort_column_id != sort_column_id) || + (prof_model->order != order)) { + prof_model->sort_column_id = sort_column_id; + prof_model->order = order; + + apc_profile_model_resort(prof_model); + } +} + +static void apc_profile_model_set_sort_func(GtkTreeSortable *sortable, + gint sort_column_id, + GtkTreeIterCompareFunc sort_func, + gpointer user_data, + GDestroyNotify destroy) +{ + CtkApcProfileModel *prof_model = CTK_APC_PROFILE_MODEL(sortable); + + g_return_if_fail((sort_column_id >= 0) && + (sort_column_id < CTK_APC_PROFILE_MODEL_N_COLUMNS)); + + prof_model->sort_funcs[sort_column_id] = sort_func; + + if (prof_model->sort_destroy_notify[sort_column_id]) { + (*prof_model->sort_destroy_notify[sort_column_id])( + prof_model->sort_user_data[sort_column_id] + ); + } + + prof_model->sort_user_data[sort_column_id] = user_data; + prof_model->sort_destroy_notify[sort_column_id] = destroy; + + if (sort_column_id == prof_model->sort_column_id) { + apc_profile_model_resort(prof_model); + } +} +static void apc_profile_model_set_default_sort_func(GtkTreeSortable *sortable, + GtkTreeIterCompareFunc sort_func, + gpointer user_data, + GDestroyNotify destroy) +{ + // do nothing +} + +static gboolean apc_profile_model_has_default_sort_func(GtkTreeSortable *sortable) +{ + return FALSE; +} diff --git a/src/gtk+-2.x/ctkapcprofilemodel.h b/src/gtk+-2.x/ctkapcprofilemodel.h new file mode 100644 index 0000000..91c06dd --- /dev/null +++ b/src/gtk+-2.x/ctkapcprofilemodel.h @@ -0,0 +1,108 @@ +/* + * nvidia-settings: A tool for configuring the NVIDIA X driver on Unix + * and Linux systems. + * + * Copyright (C) 2013 NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses>. + */ + +// Tree model implementation for operating on profiles in an AppProfileConfig + +#ifndef __CTK_APC_PROFILE_MODEL_H__ +#define __CTK_APC_PROFILE_MODEL_H__ + +#include <gtk/gtk.h> +#include <assert.h> +#include "app-profiles.h" + +G_BEGIN_DECLS + +#define CTK_TYPE_APC_PROFILE_MODEL (ctk_apc_profile_model_get_type()) + +#define CTK_APC_PROFILE_MODEL(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), CTK_TYPE_APC_PROFILE_MODEL, CtkApcProfileModel)) + +#define CTK_APC_PROFILE_MODEL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), CTK_TYPE_APC_PROFILE_MODEL, CtkApcProfileModelClass)) + +#define CTK_IS_APC_PROFILE_MODEL(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CTK_TYPE_APC_PROFILE_MODEL)) + +#define CTK_IS_APC_PROFILE_MODEL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), CTK_TYPE_APC_PROFILE_MODEL)) + +#define CTK_APC_PROFILE_MODEL_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), CTK_TYPE_APC_PROFILE_MODEL, CtkApcProfileModelClass)) + +enum { + CTK_APC_PROFILE_MODEL_COL_NAME = 0, + CTK_APC_PROFILE_MODEL_COL_FILENAME, + CTK_APC_PROFILE_MODEL_COL_SETTINGS, + CTK_APC_PROFILE_MODEL_N_COLUMNS, + CTK_APC_PROFILE_MODEL_DEFAULT_SORT_COL = CTK_APC_PROFILE_MODEL_COL_NAME +}; + +typedef struct _CtkApcProfileModel CtkApcProfileModel; +typedef struct _CtkApcProfileModelClass CtkApcProfileModelClass; + +#define CTK_APC_PROFILE_MODEL_MAX_ITERS 16 + +struct _CtkApcProfileModel +{ + GObject parent; + gint stamp; + + AppProfileConfig *config; + + // A sortable array of profile names cached from the config, used for + // presentation and iteration. + GArray *profiles; + gint sort_column_id; + GtkSortType order; + + GtkTreeIterCompareFunc sort_funcs[CTK_APC_PROFILE_MODEL_N_COLUMNS]; + gpointer sort_user_data[CTK_APC_PROFILE_MODEL_N_COLUMNS]; + GDestroyNotify sort_destroy_notify[CTK_APC_PROFILE_MODEL_N_COLUMNS]; +}; + +struct _CtkApcProfileModelClass +{ + GObjectClass parent_class; +}; + +GType ctk_apc_profile_model_class_get_type (void) G_GNUC_CONST; +GType ctk_apc_profile_model_get_type(void) G_GNUC_CONST; +CtkApcProfileModel *ctk_apc_profile_model_new (AppProfileConfig *config); + +void ctk_apc_profile_model_update_profile(CtkApcProfileModel *prof_model, + const char *filename, + const char *profile_name, + json_t *profile); + +void ctk_apc_profile_model_delete_profile(CtkApcProfileModel *prof_model, + const char *profile_name); + +void ctk_apc_profile_model_attach(CtkApcProfileModel *prof_model, AppProfileConfig *config); + +// Thin wrapper around nv_app_profile_config_get_profile() to promote +// modularity (all requests for config data should go through the models). +static inline const json_t *ctk_apc_profile_model_get_profile(CtkApcProfileModel *prof_model, + const char *profile_name) +{ + return nv_app_profile_config_get_profile(prof_model->config, profile_name); +} + +G_END_DECLS + +#endif diff --git a/src/gtk+-2.x/ctkapcrulemodel.c b/src/gtk+-2.x/ctkapcrulemodel.c new file mode 100644 index 0000000..15354dc --- /dev/null +++ b/src/gtk+-2.x/ctkapcrulemodel.c @@ -0,0 +1,717 @@ +/* + * nvidia-settings: A tool for configuring the NVIDIA X driver on Unix + * and Linux systems. + * + * Copyright (C) 2013 NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses>. + */ + +// Tree model implementation for operating on rules in an AppProfileConfig + +#include <gtk/gtk.h> +#include <stdint.h> +#include "ctkutils.h" +#include "ctkapcrulemodel.h" + +static GObjectClass *parent_class = NULL; + +// Forward declarations +GType ctk_apc_rule_model_get_type(void); +static void apc_rule_model_class_init(CtkApcRuleModelClass *klass); +static void apc_rule_model_init(CtkApcRuleModel *rule_model); +static void apc_rule_model_finalize(GObject *object); +static void apc_rule_model_tree_model_init(GtkTreeModelIface *iface); +static void apc_rule_model_drag_source_init(GtkTreeDragSourceIface *iface); +static void apc_rule_model_drag_dest_init(GtkTreeDragDestIface *iface); +static GtkTreeModelFlags apc_rule_model_get_flags(GtkTreeModel *tree_model); +static gint apc_rule_model_get_n_columns(GtkTreeModel *tree_model); +static GType apc_rule_model_get_column_type(GtkTreeModel *tree_model, gint index); +static gboolean apc_rule_model_get_iter(GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreePath *path); +static GtkTreePath *apc_rule_model_get_path(GtkTreeModel *tree_model, GtkTreeIter *iter); +static void apc_rule_model_get_value(GtkTreeModel *tree_model, + GtkTreeIter *iter, + gint column, + GValue *value); +static gboolean apc_rule_model_iter_next(GtkTreeModel *tree_model, + GtkTreeIter *iter); +static gboolean apc_rule_model_iter_children(GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent); +static gboolean apc_rule_model_iter_has_child(GtkTreeModel *tree_model, + GtkTreeIter *iter); +static gint apc_rule_model_iter_n_children(GtkTreeModel *tree_model, + GtkTreeIter *iter); +static gboolean apc_rule_model_iter_nth_child(GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent, + gint n); +static gboolean apc_rule_model_iter_parent(GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *child); +CtkApcRuleModel *ctk_apc_rule_model_new(AppProfileConfig *config); +int ctk_apc_rule_model_create_rule(CtkApcRuleModel *rule_model, + const char *filename, + json_t *new_rule); +void ctk_apc_rule_model_update_rule(CtkApcRuleModel *rule_model, + const char *filename, + int id, + json_t *rule); +void ctk_apc_rule_model_delete_rule(CtkApcRuleModel *rule_model, int id); +static void apc_rule_model_post_set_rule_priority_common(CtkApcRuleModel *rule_model, + int id); +void ctk_apc_rule_model_set_abs_rule_priority(CtkApcRuleModel *rule_model, + int id, size_t pri); +void ctk_apc_rule_model_change_rule_priority(CtkApcRuleModel *rule_model, + int id, int delta); +static gboolean apc_rule_model_row_draggable(GtkTreeDragSource *drag_source, GtkTreePath *path); +static gboolean apc_rule_model_drag_data_get(GtkTreeDragSource *drag_source, GtkTreePath *path, GtkSelectionData *selection_data); +static gboolean apc_rule_model_drag_data_delete(GtkTreeDragSource *drag_source, GtkTreePath *path); +static gboolean apc_rule_model_drag_data_received(GtkTreeDragDest *drag_dest, + GtkTreePath *dest, + GtkSelectionData *selection_data); +static gboolean apc_rule_model_row_drop_possible(GtkTreeDragDest *drag_dest, + GtkTreePath *dest_path, + GtkSelectionData *selection_data); + + +GType ctk_apc_rule_model_get_type(void) +{ + static GType apc_rule_model_type = 0; + if (!apc_rule_model_type) { + static const GTypeInfo apc_rule_model_info = { + sizeof (CtkApcRuleModelClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) apc_rule_model_class_init, /* constructor */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (CtkApcRuleModel), + 0, /* n_preallocs */ + (GInstanceInitFunc) apc_rule_model_init, /* instance_init */ + NULL /* value_table */ + }; + static const GInterfaceInfo tree_model_info = + { + (GInterfaceInitFunc) apc_rule_model_tree_model_init, /* interface_init */ + NULL, /* interface_finalize */ + NULL /* interface_data */ + }; + static const GInterfaceInfo drag_source_info = + { + (GInterfaceInitFunc) apc_rule_model_drag_source_init, /* interface_init */ + NULL, /* interface_finalize */ + NULL /* interface_data */ + }; + static const GInterfaceInfo drag_dest_info = + { + (GInterfaceInitFunc) apc_rule_model_drag_dest_init, /* interface_init */ + NULL, /* interface_finalize */ + NULL /* interface_data */ + }; + + apc_rule_model_type = + g_type_register_static(G_TYPE_OBJECT, "CtkApcRuleModel", + &apc_rule_model_info, 0); + + g_type_add_interface_static(apc_rule_model_type, GTK_TYPE_TREE_MODEL, &tree_model_info); + g_type_add_interface_static(apc_rule_model_type, GTK_TYPE_TREE_DRAG_SOURCE, &drag_source_info); + g_type_add_interface_static(apc_rule_model_type, GTK_TYPE_TREE_DRAG_DEST, &drag_dest_info); + } + + return apc_rule_model_type; +} + +static void apc_rule_model_class_init(CtkApcRuleModelClass *klass) +{ + GObjectClass *object_class; + + parent_class = (GObjectClass *)g_type_class_peek_parent(klass); + object_class = (GObjectClass *)klass; + + object_class->finalize = apc_rule_model_finalize; +} + +static void apc_rule_model_init(CtkApcRuleModel *rule_model) +{ + rule_model->stamp = g_random_int(); // random int to catch iterator type mismatches + rule_model->config = NULL; + rule_model->rules = g_array_new(FALSE, FALSE, sizeof(gint)); +} + +static void apc_rule_model_finalize(GObject *object) +{ + CtkApcRuleModel *rule_model = CTK_APC_RULE_MODEL(object); + g_array_free(rule_model->rules, TRUE); + parent_class->finalize(object); +} + +static void apc_rule_model_tree_model_init(GtkTreeModelIface *iface) +{ + iface->get_flags = apc_rule_model_get_flags; + iface->get_n_columns = apc_rule_model_get_n_columns; + iface->get_column_type = apc_rule_model_get_column_type; + iface->get_iter = apc_rule_model_get_iter; + iface->get_path = apc_rule_model_get_path; + iface->get_value = apc_rule_model_get_value; + iface->iter_next = apc_rule_model_iter_next; + iface->iter_children = apc_rule_model_iter_children; + iface->iter_has_child = apc_rule_model_iter_has_child; + iface->iter_n_children = apc_rule_model_iter_n_children; + iface->iter_nth_child = apc_rule_model_iter_nth_child; + iface->iter_parent = apc_rule_model_iter_parent; +} + +static void apc_rule_model_drag_source_init(GtkTreeDragSourceIface *iface) +{ + iface->row_draggable = apc_rule_model_row_draggable; + iface->drag_data_get = apc_rule_model_drag_data_get; + iface->drag_data_delete = apc_rule_model_drag_data_delete; +} + +static void apc_rule_model_drag_dest_init(GtkTreeDragDestIface *iface) +{ + iface->drag_data_received = apc_rule_model_drag_data_received; + iface->row_drop_possible = apc_rule_model_row_drop_possible; +} + +static gboolean apc_rule_model_row_draggable(GtkTreeDragSource *drag_source, GtkTreePath *path) +{ + return TRUE; +} + +static gboolean apc_rule_model_drag_data_get(GtkTreeDragSource *drag_source, GtkTreePath *path, GtkSelectionData *selection_data) +{ + if (gtk_tree_set_row_drag_data(selection_data, + GTK_TREE_MODEL(drag_source), + path)) { + return TRUE; + } else { + // XXX text targets? + return FALSE; + } +} + +static gboolean apc_rule_model_drag_data_delete(GtkTreeDragSource *drag_source, GtkTreePath *path) +{ + CtkApcRuleModel *rule_model; + gint *indices, depth; + + rule_model = CTK_APC_RULE_MODEL(drag_source); + indices = gtk_tree_path_get_indices(path); + depth = gtk_tree_path_get_depth(path); + if (depth != 1) { + return FALSE; + } + if ((indices[0] < 0) || (indices[0] > rule_model->rules->len)) { + return FALSE; + } + + // XXX the actual deletion is handled in the data_received() callback + // If we ever use targets other than the view itself, this will need to have + // a real implementation. + return TRUE; +} + +static gboolean apc_rule_model_drag_data_received(GtkTreeDragDest *drag_dest, + GtkTreePath *dest, + GtkSelectionData *selection_data) +{ + CtkApcRuleModel *rule_model; + GtkTreeModel *tree_model = GTK_TREE_MODEL(drag_dest); + GtkTreeModel *src_model = NULL; + GtkTreeIter iter; + GtkTreePath *src = NULL; + gint *dest_indices, dest_depth; + gint src_depth; + gint dest_n; + GValue id = G_VALUE_INIT; + + if (gtk_tree_get_row_drag_data(selection_data, &src_model, &src) && + (src_model == tree_model)) { + // Move the given row + rule_model = CTK_APC_RULE_MODEL(drag_dest); + + dest_indices = gtk_tree_path_get_indices(dest); + dest_depth = gtk_tree_path_get_depth(dest); + assert(dest_depth == 1); + (void)(dest_depth); + + dest_n = dest_indices[0]; + + src_depth = gtk_tree_path_get_depth(src); + assert(src_depth == 1); + (void)(src_depth); + + gtk_tree_model_get_iter(tree_model, + &iter, src); + gtk_tree_model_get_value(tree_model, + &iter, CTK_APC_RULE_MODEL_COL_ID, &id); + + // Move the rule to the right location + ctk_apc_rule_model_set_abs_rule_priority(rule_model, + g_value_get_int(&id), + dest_n); + gtk_tree_path_free(src); + } else { + // XXX text targets? + } + + return TRUE; +} + +static gboolean apc_rule_model_row_drop_possible(GtkTreeDragDest *drag_dest, + GtkTreePath *dest_path, + GtkSelectionData *selection_data) +{ + CtkApcRuleModel *rule_model; + gint n, *indices, depth; + + rule_model = CTK_APC_RULE_MODEL(drag_dest); + indices = gtk_tree_path_get_indices(dest_path); + depth = gtk_tree_path_get_depth(dest_path); + + assert(depth >= 1); + n = indices[0]; + + return (depth == 1) && (n >= 0) && (n <= rule_model->rules->len); +} + +static GtkTreeModelFlags apc_rule_model_get_flags(GtkTreeModel *tree_model) +{ + return GTK_TREE_MODEL_LIST_ONLY; +} + +static gint apc_rule_model_get_n_columns(GtkTreeModel *tree_model) +{ + return CTK_APC_RULE_MODEL_N_COLUMNS; +} + +static GType apc_rule_model_get_column_type(GtkTreeModel *tree_model, gint index) +{ + switch (index) { + case CTK_APC_RULE_MODEL_COL_ID: + return G_TYPE_INT; + case CTK_APC_RULE_MODEL_COL_FEATURE: + return G_TYPE_STRING; + case CTK_APC_RULE_MODEL_COL_MATCHES: + return G_TYPE_STRING; + case CTK_APC_RULE_MODEL_COL_PROFILE_NAME: + return G_TYPE_STRING; + case CTK_APC_RULE_MODEL_COL_FILENAME: + return G_TYPE_STRING; + default: + assert(0); + return G_TYPE_INVALID; + } +} + +static gboolean apc_rule_model_get_iter(GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreePath *path) +{ + CtkApcRuleModel *rule_model; + gint depth, *indices; + intptr_t n; + + assert(path); + rule_model = CTK_APC_RULE_MODEL(tree_model); + + indices = gtk_tree_path_get_indices(path); + depth = gtk_tree_path_get_depth(path); + + assert(depth == 1); + (void)(depth); + + n = indices[0]; + + if (n >= rule_model->rules->len || n < 0) { + return FALSE; + } + + iter->stamp = rule_model->stamp; + + iter->user_data = (gpointer)n; + iter->user_data2 = NULL; // unused + iter->user_data3 = NULL; // unused + + return TRUE; +} + +static GtkTreePath *apc_rule_model_get_path(GtkTreeModel *tree_model, GtkTreeIter *iter) +{ + GtkTreePath *path; + intptr_t n = (intptr_t)iter->user_data; + + g_return_val_if_fail(iter, NULL); + + path = gtk_tree_path_new(); + gtk_tree_path_append_index(path, n); + + return path; +} + +static void apc_rule_model_get_value(GtkTreeModel *tree_model, + GtkTreeIter *iter, + gint column, + GValue *value) +{ + const char *filename; + CtkApcRuleModel *rule_model; + const json_t *rule; + const json_t *rule_pattern; + int rule_id; + intptr_t n; + + g_value_init(value, apc_rule_model_get_column_type(tree_model, column)); + rule_model = CTK_APC_RULE_MODEL(tree_model); + + n = (intptr_t)iter->user_data; + rule_id = g_array_index(rule_model->rules, gint, n); + + rule = nv_app_profile_config_get_rule(rule_model->config, + rule_id); + rule_pattern = json_object_get(rule, "pattern"); + + switch (column) { + case CTK_APC_RULE_MODEL_COL_ID: + g_value_set_int(value, rule_id); + break; + case CTK_APC_RULE_MODEL_COL_FEATURE: + g_value_set_string(value, json_string_value(json_object_get(rule_pattern, "feature"))); + break; + case CTK_APC_RULE_MODEL_COL_MATCHES: + g_value_set_string(value, json_string_value(json_object_get(rule_pattern, "matches"))); + break; + case CTK_APC_RULE_MODEL_COL_PROFILE_NAME: + g_value_set_string(value, json_string_value(json_object_get(rule, "profile"))); + break; + case CTK_APC_RULE_MODEL_COL_FILENAME: + filename = nv_app_profile_config_get_rule_filename(rule_model->config, rule_id); + assert(filename); + g_value_set_string(value, filename); + break; + default: + assert(0); + break; + } +} + +static gboolean apc_rule_model_iter_next(GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + CtkApcRuleModel *rule_model; + intptr_t n; + + rule_model = CTK_APC_RULE_MODEL(tree_model); + + if (!iter) { + return FALSE; + } + + n = (intptr_t)iter->user_data; + n++; + + if (n >= rule_model->rules->len) { + return FALSE; + } + + iter->user_data = (gpointer)n; + + return TRUE; +} + +static gboolean apc_rule_model_iter_children(GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent) +{ + CtkApcRuleModel *rule_model = CTK_APC_RULE_MODEL(tree_model); + + if (parent) { + return FALSE; + } + + // (parent == NULL) => return first profile + + if (!rule_model->rules->len) { + return FALSE; + } + + iter->stamp = rule_model->stamp; + iter->user_data = (gpointer)0; + iter->user_data2 = NULL; + iter->user_data3 = NULL; + + return TRUE; +} + +static gboolean apc_rule_model_iter_has_child(GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + return FALSE; +} + +static gint apc_rule_model_iter_n_children(GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + CtkApcRuleModel *rule_model = CTK_APC_RULE_MODEL(tree_model); + + return iter ? 0 : rule_model->rules->len; +} + +static gboolean +apc_rule_model_iter_nth_child(GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent, + gint n_in) +{ + CtkApcRuleModel *rule_model = CTK_APC_RULE_MODEL(tree_model); + intptr_t n = (intptr_t)n_in; + + if (parent || + (n < 0) || + (n >= rule_model->rules->len)) { + return FALSE; + } + + iter->stamp = rule_model->stamp; + iter->user_data = (gpointer)n; + iter->user_data2 = NULL; // unused + iter->user_data3 = NULL; // unused + + return TRUE; +} + +static gboolean +apc_rule_model_iter_parent(GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *child) +{ + return FALSE; +} + +void ctk_apc_rule_model_attach(CtkApcRuleModel *rule_model, AppProfileConfig *config) +{ + GtkTreePath *path; + GtkTreeIter iter; + json_t *rule; + AppProfileConfigRuleIter *rule_iter; + gint i; + gint id; + + rule_model->config = config; + + // Clear existing rules from the model + path = gtk_tree_path_new_from_indices(0, -1); + for (i = 0; i < rule_model->rules->len; i++) { + // Emit a "row-deleted" signal for each deleted rule + // (we can just keep calling this on row 0) + gtk_tree_model_row_deleted(GTK_TREE_MODEL(rule_model), path); + } + gtk_tree_path_free(path); + g_array_set_size(rule_model->rules, 0); + + // Load rules from the config into the model + for (rule_iter = nv_app_profile_config_rule_iter(config), i = 0; + rule_iter; + rule_iter = nv_app_profile_config_rule_iter_next(rule_iter)) { + rule = nv_app_profile_config_rule_iter_val(rule_iter); + id = (int)json_integer_value(json_object_get(rule, "id")); + g_array_append_val(rule_model->rules, id); + + // Emit a "row-inserted" signal for each new rule + path = gtk_tree_path_new_from_indices(i++, -1); + apc_rule_model_get_iter(GTK_TREE_MODEL(rule_model), &iter, path); + gtk_tree_model_row_inserted(GTK_TREE_MODEL(rule_model), path, &iter); + gtk_tree_path_free(path); + } +} + +CtkApcRuleModel *ctk_apc_rule_model_new(AppProfileConfig *config) +{ + CtkApcRuleModel *rule_model; + + rule_model = CTK_APC_RULE_MODEL(g_object_new(CTK_TYPE_APC_RULE_MODEL, NULL)); + assert(rule_model); + + ctk_apc_rule_model_attach(rule_model, config); + + return rule_model; +} + +int ctk_apc_rule_model_create_rule(CtkApcRuleModel *rule_model, + const char *filename, + json_t *new_rule) +{ + GtkTreeIter iter; + GtkTreePath *path; + gint n; + int rule_id; + + rule_id = nv_app_profile_config_create_rule(rule_model->config, + filename, + new_rule); + + n = (gint)nv_app_profile_config_get_rule_priority(rule_model->config, + rule_id); + g_array_insert_val(rule_model->rules, n, rule_id); + + // Emit a "row-inserted" signal + path = gtk_tree_path_new_from_indices(n, -1); + apc_rule_model_get_iter(GTK_TREE_MODEL(rule_model), &iter, path); + gtk_tree_model_row_inserted(GTK_TREE_MODEL(rule_model), path, &iter); + gtk_tree_path_free(path); + + return rule_id; +} + + +static gint find_index_of_rule(CtkApcRuleModel *rule_model, int id) +{ + gint i; + for (i = 0; i < rule_model->rules->len; i++) { + if (g_array_index(rule_model->rules, gint, i) == id) { + return i; + } + } + + return -1; +} + +void ctk_apc_rule_model_update_rule(CtkApcRuleModel *rule_model, + const char *filename, + int id, + json_t *rule) +{ + int rule_moved; + GtkTreeIter iter; + GtkTreePath *path; + gint n; + size_t new_pri, old_pri; + gint *new_order; + GArray *new_rules; + int cur_id; + + rule_moved = nv_app_profile_config_update_rule(rule_model->config, + filename, + id, + rule); + + if (rule_moved) { + // Compute the new ordering + new_rules = g_array_new(FALSE, FALSE, sizeof(gint)); + new_order = malloc(sizeof(gint) * rule_model->rules->len); + + for (old_pri = 0; old_pri < rule_model->rules->len; old_pri++) { + cur_id = g_array_index(rule_model->rules, gint, old_pri); + new_pri = nv_app_profile_config_get_rule_priority(rule_model->config, cur_id); + new_order[new_pri] = old_pri; + } + + for (new_pri = 0; new_pri < rule_model->rules->len; new_pri++) { + cur_id = g_array_index(rule_model->rules, gint, new_order[new_pri]); + g_array_append_val(new_rules, cur_id); + } + + g_array_free(rule_model->rules, TRUE); + rule_model->rules = new_rules; + + // emit a "rows-reordered" signal + path = gtk_tree_path_new(); + gtk_tree_model_rows_reordered(GTK_TREE_MODEL(rule_model), + path, NULL, new_order); + gtk_tree_path_free(path); + free(new_order); + } else { + // emit a "row-changed" signal + n = find_index_of_rule(rule_model, id); + path = gtk_tree_path_new_from_indices(n, -1); + apc_rule_model_get_iter(GTK_TREE_MODEL(rule_model), &iter, path); + gtk_tree_model_row_changed(GTK_TREE_MODEL(rule_model), path, &iter); + gtk_tree_path_free(path); + } +} + +void ctk_apc_rule_model_delete_rule(CtkApcRuleModel *rule_model, int id) +{ + GtkTreePath *path; + gint n; + + n = find_index_of_rule(rule_model, id); + assert(n >= 0); + + nv_app_profile_config_delete_rule(rule_model->config, id); + g_array_remove_index(rule_model->rules, n); + + // emit a "row-deleted" signal + path = gtk_tree_path_new_from_indices(n, -1); + gtk_tree_model_row_deleted(GTK_TREE_MODEL(rule_model), path); + gtk_tree_path_free(path); +} + +static void apc_rule_model_post_set_rule_priority_common(CtkApcRuleModel *rule_model, + int id) +{ + gint *new_order; + size_t old_pri, new_pri; + int cur_id; + gint n; + GArray *new_rules; + GtkTreePath *path; + GtkTreeIter iter; + + // Compute the new ordering + new_rules = g_array_new(FALSE, FALSE, sizeof(gint)); + new_order = malloc(sizeof(gint) * rule_model->rules->len); + + for (old_pri = 0; old_pri < rule_model->rules->len; old_pri++) { + cur_id = g_array_index(rule_model->rules, gint, old_pri); + new_pri = nv_app_profile_config_get_rule_priority(rule_model->config, cur_id); + new_order[new_pri] = old_pri; + } + + for (new_pri = 0; new_pri < rule_model->rules->len; new_pri++) { + cur_id = g_array_index(rule_model->rules, gint, new_order[new_pri]); + g_array_append_val(new_rules, cur_id); + } + + g_array_free(rule_model->rules, TRUE); + rule_model->rules = new_rules; + + // emit a "rows-reordered" signal + path = gtk_tree_path_new(); + gtk_tree_model_rows_reordered(GTK_TREE_MODEL(rule_model), + path, NULL, new_order); + gtk_tree_path_free(path); + free(new_order); + + // emit a "row-changed" signal for the rule whose priority has changed + n = find_index_of_rule(rule_model, id); + + path = gtk_tree_path_new_from_indices(n, -1); + apc_rule_model_get_iter(GTK_TREE_MODEL(rule_model), &iter, path); + gtk_tree_model_row_changed(GTK_TREE_MODEL(rule_model), path, &iter); + gtk_tree_path_free(path); +} + +void ctk_apc_rule_model_set_abs_rule_priority(CtkApcRuleModel *rule_model, + int id, size_t pri) +{ + nv_app_profile_config_set_abs_rule_priority(rule_model->config, id, pri); + apc_rule_model_post_set_rule_priority_common(rule_model, id); +} + +void ctk_apc_rule_model_change_rule_priority(CtkApcRuleModel *rule_model, + int id, int delta) +{ + nv_app_profile_config_change_rule_priority(rule_model->config, id, delta); + apc_rule_model_post_set_rule_priority_common(rule_model, id); +} diff --git a/src/gtk+-2.x/ctkapcrulemodel.h b/src/gtk+-2.x/ctkapcrulemodel.h new file mode 100644 index 0000000..68e0a0b --- /dev/null +++ b/src/gtk+-2.x/ctkapcrulemodel.h @@ -0,0 +1,97 @@ +/* + * nvidia-settings: A tool for configuring the NVIDIA X driver on Unix + * and Linux systems. + * + * Copyright (C) 2013 NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses>. + */ + +// Tree model implementation for operating on rules in an AppProfileConfig + +#ifndef __CTK_APC_RULE_MODEL_H__ +#define __CTK_APC_RULE_MODEL_H__ + +#include <gtk/gtk.h> +#include <assert.h> +#include "app-profiles.h" + +G_BEGIN_DECLS + +#define CTK_TYPE_APC_RULE_MODEL (ctk_apc_rule_model_get_type()) + +#define CTK_APC_RULE_MODEL(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), CTK_TYPE_APC_RULE_MODEL, CtkApcRuleModel)) + +#define CTK_APC_RULE_MODEL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), CTK_TYPE_APC_RULE_MODEL, CtkApcRuleModelClass)) + +#define CTK_IS_APC_RULE_MODEL(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CTK_TYPE_APC_RULE_MODEL)) + +#define CTK_IS_APC_RULE_MODEL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), CTK_TYPE_APC_RULE_MODEL)) + +#define CTK_APC_RULE_MODEL_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), CTK_TYPE_APC_RULE_MODEL, CtkApcRuleModelClass)) + +enum { + CTK_APC_RULE_MODEL_COL_ID = 0, + CTK_APC_RULE_MODEL_COL_FEATURE, + CTK_APC_RULE_MODEL_COL_MATCHES, + CTK_APC_RULE_MODEL_COL_PROFILE_NAME, + CTK_APC_RULE_MODEL_COL_FILENAME, + CTK_APC_RULE_MODEL_N_COLUMNS +}; + +typedef struct _CtkApcRuleModel CtkApcRuleModel; +typedef struct _CtkApcRuleModelClass CtkApcRuleModelClass; + +struct _CtkApcRuleModel +{ + GObject parent; + gint stamp; + + AppProfileConfig *config; + + // A sortable array of rule IDs (int) cached from the config, + // used for presentation and iteration. + GArray *rules; +}; + +struct _CtkApcRuleModelClass +{ + GObjectClass parent_class; +}; + +GType ctk_apc_rule_model_class_get_type (void) G_GNUC_CONST; +CtkApcRuleModel *ctk_apc_rule_model_new (AppProfileConfig *config); + +int ctk_apc_rule_model_create_rule(CtkApcRuleModel *rule_model, + const char *filename, + json_t *new_rule); +void ctk_apc_rule_model_update_rule(CtkApcRuleModel *rule_model, + const char *filename, + int id, + json_t *rule); +void ctk_apc_rule_model_delete_rule(CtkApcRuleModel *rule_model, int id); +void ctk_apc_rule_model_set_abs_rule_priority(CtkApcRuleModel *rule_model, + int id, size_t pri); +void ctk_apc_rule_model_change_rule_priority(CtkApcRuleModel *rule_model, + int id, int delta); + +void ctk_apc_rule_model_attach(CtkApcRuleModel *rule_model, AppProfileConfig *config); + +G_END_DECLS + +#endif diff --git a/src/gtk+-2.x/ctkappprofile.c b/src/gtk+-2.x/ctkappprofile.c new file mode 100644 index 0000000..a7183ed --- /dev/null +++ b/src/gtk+-2.x/ctkappprofile.c @@ -0,0 +1,4156 @@ +/* + * nvidia-settings: A tool for configuring the NVIDIA X driver on Unix + * and Linux systems. + * + * Copyright (C) 2013 NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses>. + */ + +#define _GNU_SOURCE + +#include <stdlib.h> +#include <assert.h> +#include <string.h> +#include <gtk/gtk.h> +#include <gdk/gdk.h> +#include <gdk/gdkkeysyms.h> +#include "ctkutils.h" +#include "ctkbanner.h" +#include "ctkhelp.h" +#include "ctkappprofile.h" +#include "common-utils.h" +#include "msg.h" + +#define UPDATE_RULE_LABEL "Update Rule" +#define UPDATE_PROFILE_LABEL "Update Profile" + +#define STATUSBAR_UPDATE_WARNING "This will take effect after changes are saved." + +enum { + PROFILE_SETTING_GL_FSAA_MODE, + PROFILE_SETTING_GL_LOG_MAX_ANISO, + PROFILE_SETTING_GL_NO_DSO_FINALIZER, + PROFILE_SETTING_GL_SINGLE_THREADED, + PROFILE_SETTING_GL_SYNC_DISPLAY_DEVICE, + PROFILE_SETTING_GL_SYNC_TO_VBLANK, + PROFILE_SETTING_GL_SORT_FBCONFIGS, + PROFILE_SETTING_GL_ALLOW_UNOFFICIAL_PROTOCOL, + PROFILE_SETTING_GL_SELINUX_BOOLEANS, + PROFILE_SETTING_GL_SHADER_DISK_CACHE, + PROFILE_SETTING_GL_SHADER_DISK_CACHE_PATH, + PROFILE_SETTING_GL_YIELD, + PROFILE_SETTING_GL_THREADED_OPTIMIZATIONS, + PROFILE_SETTING_GL_DOOM3, + PROFILE_SETTING_GL_EXTENSION_STRING_VERSION, + NUM_PROFILE_SETTINGS +}; + +static const char *profile_setting_keys[] = { + "GLFSAAMode", // PROFILE_SETTING_GL_FSAA_MODE + "GLLogMaxAniso", // PROFILE_SETTING_GL_LOG_MAX_ANISO + "GLNoDsoFinalizer", // PROFILE_SETTING_GL_NO_DSO_FINALIZER + "GLSingleThreaded", // PROFILE_SETTING_GL_SINGLE_THREADED + "GLSyncDisplayDevice", // PROFILE_SETTING_GL_SYNC_DISPLAY_DEVICE + "GLSyncToVblank", // PROFILE_SETTING_GL_SYNC_TO_VBLANK + "GLSortFbconfigs", // PROFILE_SETTING_GL_SORT_FBCONFIGS + "GLAllowUnofficialProtocol", // PROFILE_SETTING_GL_ALLOW_UNOFFICIAL_PROTOCOL + "GLSELinuxBooleans", // PROFILE_SETTING_GL_SELINUX_BOOLEANS + "GLShaderDiskCache", // PROFILE_SETTING_GL_SHADER_DISK_CACHE + "GLShaderDiskCachePath", // PROFILE_SETTING_GL_SHADER_DISK_CACHE_PATH + "GLYield", // PROFILE_SETTING_GL_YIELD + "GLThreadedOptimizations", // PROFILE_SETTING_GL_THREADED_OPTIMIZATIONS + "GLDoom3", // PROFILE_SETTING_GL_DOOM3 + "GLExtensionStringVersion" // PROFILE_SETTING_GL_EXTENSION_STRING_VERSION +}; + +/* + * XXX: it might be a good idea to generate some of these descriptions + * dynamically based on other tables used by nvidia-settings + */ +static const char *profile_setting_descriptions[] = { + // PROFILE_SETTING_GL_FSAA_MODE + "This setting enables full-scene antialiasing in a process using OpenGL. This expects " + "the same integer value that can be used to configure FSAA through nvidia-settings " + "and the NV-CONTROL X extension. To see available FSAA values, run:\n\n" + "\tnvidia-settings --query=fsaa --verbose", + // PROFILE_SETTING_GL_LOG_MAX_ANISO + "This enables anisotropic texture filtering. The possible values are:\n\n" + "\t0\tNo anisotropic filtering\n" + "\t1\t2x anisotropic filtering\n" + "\t2\t4x anisotropic filtering\n" + "\t3\t8x anisotropic filtering\n" + "\t4\t16x anisotropic filtering", + // PROFILE_SETTING_GL_NO_DSO_FINALIZER + "This works around problems with certain multithreaded applications in which " + "one thread exits while others are executing OpenGL code. This may be set to true or false.", + // PROFILE_SETTING_GL_SINGLE_THREADED + "This works around some legacy dynamic loaders which can cause applications linked against pthreads " + "which dlopen() libGL multiple times to crash. This may be set to true or false. ", + // PROFILE_SETTING_GL_SYNC_DISPLAY_DEVICE + "This allows an application to specify target a display device to sync with if sync to vblank is enabled. " + "This should be set to a string containing a valid display device name (for example, \"CRT-1\").", + // PROFILE_SETTING_GL_SYNC_TO_VBLANK + "This enables sync to vblank for an application. This may be set to true or false. ", + // PROFILE_SETTING_GL_SORT_FBCONFIGS + "By default the NVIDIA GLX implementation will sort FBConfigs as described by the specification. This " + "may be set to false to disable this behavior.", + // PROFILE_SETTING_GL_ALLOW_UNOFFICIAL_PROTOCOL + "Setting this to true will allow the client-side NVIDIA GLX implementation to send \"incomplete\" GLX protocol.", + // PROFILE_SETTING_GL_SELINUX_BOOLEANS + "This allows the user to override driver detection of specific SELinux policy booleans, which may " + "work around problems when running the driver under SELinux in permissive mode. This should be set to a " + "string value; see __GL_SELINUX_BOOLEANS in the README for a description of legal string formats.", + // PROFILE_SETTING_GL_SHADER_DISK_CACHE + "This enables the shader disk cache for direct rendering. This value may be set to true or false.", + // PROFILE_SETTING_GL_SHADER_DISK_CACHE_PATH + "This setting affects where shader caches are stored on disk for a given application. " + "This value should be set to a string containing a valid pathname.", + // PROFILE_SETTING_GL_YIELD + "This controls how the NVIDIA graphics driver will perform a yield. This may be set to one of the following strings:\n\n" + "\t\"USLEEP\"\tOpenGL will call usleep(0) to yield\n" + "\t\"NOTHING\"\tOpenGL will never yield\n" + "\t<any other value>\tOpenGL will call sched_yield() to yield (default)", + // PROFILE_SETTING_GL_THREADED_OPTIMIZATIONS + "This setting enables multi-threaded optimizations in the OpenGL driver which may improve application performance. " + "This may be set to true or false.", + // PROFILE_SETTING_GL_DOOM3 + "This enables optimal SLI and Multi-GPU settings for games such as Doom 3 and Quake 4. " + "This may be set to true or false.", + // PROFILE_SETTING_GL_EXTENSION_STRING_VERSION + "This forces the extension string returned by glXQueryExtensionsString() to one that appeared in an earlier " + "version of the NVIDIA graphics driver. This may work around bugs in certain applications which expect an extension " + "string to be smaller than a certain size. This value should be set to a string value or integer containing the " + "desired version number (e.g. \"17700\" to force the extension string in the 177.* driver series).", +}; + +enum { + RULE_FEATURE_PROCNAME, + RULE_FEATURE_DSO, + RULE_FEATURE_TRUE, + NUM_RULE_FEATURES +}; + +static const char *rule_feature_label_strings[] = { + "Process Name (procname)", // RULE_FEATURE_PROCNAME + "Shared Object Name (dso)", // RULE_FEATURE_DSO + "Always Applies (true)" // RULE_FEATURE_TRUE +}; + +static const char *rule_feature_identifiers[] = { + "procname", // RULE_FEATURE_PROCNAME + "dso", // RULE_FEATURE_DSO + "true" // RULE_FEATURE_TRUE +}; + +#define MATCHES_INPUT_DESCRIPTION "\"Matches this string...\" text entry box" + +static const char *rule_feature_help_text[] = { + "Patterns using this feature compare the string provided by the " MATCHES_INPUT_DESCRIPTION " " + "against the pathname of the current process with the leading directory components removed, " + "and match if they are equal.", // RULE_FEATURE_PROCNAME + "Patterns using this feature compare the string provided by the " MATCHES_INPUT_DESCRIPTION " " + "against the list of currently loaded libraries in the current process, and match if " + "the string matches one of the entries in the list (with leading directory components removed).", // RULE_FEATURE_DSO + "Patterns using this feature will always match the process, regardless of the " + "contents of the string specified in the " MATCHES_INPUT_DESCRIPTION ".", // RULE_FEATURE_TRUE +}; + +enum { + SETTING_LIST_STORE_COL_SETTING, + SETTING_LIST_STORE_NUM_COLS, +}; + +/* + * Struct containing metadata on widgets created via + * populate_{toolbar,tree_view}(). + */ +typedef struct _WidgetDataItem { + gchar *label; + GtkWidget *widget; +} WidgetDataItem; + +/* + * Template used to construct toolbar buttons and generate help text with populate_toolbar(). + */ +typedef struct _ToolbarItemTemplate { + const gchar *text; + const gchar *icon_id; + GCallback callback; + gpointer user_data; + + const gchar *help_text; + const gchar *extended_help_text; +} ToolbarItemTemplate; + +/* + * Template used to construct tree view columns and generate help text with + * populate_tree_view(). + */ +typedef struct _TreeViewColumnTemplate { + const gchar *title; + + GtkTreeCellDataFunc renderer_func; + gpointer func_data; + + const gchar *attribute; + gint attr_col; + gint min_width; + + gboolean sortable; + gint sort_column_id; + + gboolean editable; + GCallback edit_callback; + + const gchar *help_text; + const gchar *extended_help_text; +} TreeViewColumnTemplate; + +#if JSON_INTEGER_IS_LONG_LONG +# define JSON_INTEGER_HEX_FORMAT "llx" +#else +# define JSON_INTEGER_HEX_FORMAT "lx" +#endif + +/* + * Function prototypes + */ +static void app_profile_class_init(CtkAppProfileClass *ctk_object_class); +static void app_profile_finalize(GObject *object); +static void edit_rule_dialog_destroy(EditRuleDialog *dialog); +static void edit_profile_dialog_destroy(EditProfileDialog *dialog); +static void save_app_profile_changes_dialog_destroy(SaveAppProfileChangesDialog *dialog); + +/* + * Get a UTF8 bullet string suitable for printing + */ +static const char *get_bullet(void) +{ + gint len; + static gboolean initialized = FALSE; + static gchar bullet[8]; + + // Convert unicode "bullet" character into a UTF8 string + if (!initialized) { + len = g_unichar_to_utf8(0x2022, bullet); + bullet[len] = '\0'; + initialized = TRUE; + } + return bullet; +} + +static char *vmarkup_string(const char *s, gboolean add_markup, const char *tag, va_list ap) +{ + char *escaped_s; + GString *tagged_str; + char *tagged_s_return; + char *attrib, *attrib_val; + + if (!add_markup) { + return strdup(s); + } + + tagged_str = g_string_new(""); + escaped_s = g_markup_escape_text(s, -1); + + g_string_append_printf(tagged_str, "<%s ", tag); + do { + attrib = va_arg(ap, char *); + if (attrib) { + attrib_val = va_arg(ap, char *); + g_string_append_printf(tagged_str, "%s=\"%s\"", attrib, attrib_val); + } + } while (attrib); + g_string_append_printf(tagged_str, ">%s</%s>", escaped_s, tag); + + tagged_s_return = strdup(tagged_str->str); + g_string_free(tagged_str, TRUE); + free(escaped_s); + + return tagged_s_return; +} + +static char *markup_string(const char *s, gboolean add_markup, const char *tag, ...) +{ + char *tagged_s; + va_list ap; + va_start(ap, tag); + tagged_s = vmarkup_string(s, add_markup, tag, ap); + va_end(ap); + + return tagged_s; +} + + +GType ctk_app_profile_get_type(void) +{ + static GType ctk_app_profile_type = 0; + + if (!ctk_app_profile_type) { + static const GTypeInfo ctk_app_profile_info = { + sizeof (CtkAppProfileClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) app_profile_class_init, /* constructor */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (CtkAppProfile), + 0, /* n_preallocs */ + NULL, /* instance_init */ + NULL /* value_table */ + }; + + ctk_app_profile_type = + g_type_register_static(GTK_TYPE_VBOX, "CtkAppProfile", + &ctk_app_profile_info, 0); + } + + return ctk_app_profile_type; + +} /* ctk_app_profile_get_type() */ + +static void app_profile_class_init(CtkAppProfileClass *ctk_object_class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS(ctk_object_class); + gobject_class->finalize = app_profile_finalize; +} + +static void app_profile_finalize(GObject *object) +{ + CtkAppProfile *ctk_app_profile = CTK_APP_PROFILE(object); + + edit_rule_dialog_destroy(ctk_app_profile->edit_rule_dialog); + edit_profile_dialog_destroy(ctk_app_profile->edit_profile_dialog); + save_app_profile_changes_dialog_destroy(ctk_app_profile->save_app_profile_changes_dialog); + + ctk_help_data_list_free_full(ctk_app_profile->global_settings_help_data); + ctk_help_data_list_free_full(ctk_app_profile->rules_help_data); + ctk_help_data_list_free_full(ctk_app_profile->rules_columns_help_data); + ctk_help_data_list_free_full(ctk_app_profile->profiles_help_data); + ctk_help_data_list_free_full(ctk_app_profile->profiles_columns_help_data); + ctk_help_data_list_free_full(ctk_app_profile->save_reload_help_data); +} + +static void button_set_label_and_stock_icon(GtkButton *button, const gchar *label_text, const gchar *icon_id) +{ + GtkWidget *hbox; + GtkWidget *icon; + GtkWidget *label; + GtkWidget *button_child; + hbox = gtk_hbox_new(FALSE, 0); + icon = gtk_image_new_from_stock(icon_id, GTK_ICON_SIZE_SMALL_TOOLBAR); + label = gtk_label_new(label_text); + gtk_box_pack_start(GTK_BOX(hbox), icon, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0); + button_child = gtk_bin_get_child(GTK_BIN(button)); + if (button_child) { + gtk_container_remove(GTK_CONTAINER(button), button_child); + } + gtk_widget_show_all(GTK_WIDGET(hbox)); + gtk_container_add(GTK_CONTAINER(button), hbox); +} + +static gint find_widget_in_widget_data_list_cb(gconstpointer a, gconstpointer b) +{ + const WidgetDataItem *item = (const WidgetDataItem *)a; + const char *label = (const char *)b; + + return strcmp(item->label, label); +} + +static GtkWidget *find_widget_in_widget_data_list(GList *list, const char *label) +{ + GList *list_item; + WidgetDataItem *item; + + list_item = g_list_find_custom(list, (gconstpointer)label, find_widget_in_widget_data_list_cb); + assert(list_item); + + item = (WidgetDataItem *)list_item->data; + return item->widget; +} + +static void widget_data_list_free_cb(gpointer data, gpointer user_data) +{ + WidgetDataItem *item = (WidgetDataItem *)data; + free(item->label); + // Don't free item->widget; it's owned by its parent widget + free(item); +} + +static void widget_data_list_free_full(GList *list) +{ + g_list_foreach(list, widget_data_list_free_cb, NULL); + g_list_free(list); +} + + +/* Simple helper function to fill a toolbar with buttons from a table */ +static void populate_toolbar(GtkToolbar *toolbar, + const ToolbarItemTemplate *item, + size_t num_items, + GList **help_data, + GList **widget_data) +{ + WidgetDataItem *widget_data_item; + GtkWidget *widget; + GtkWidget *icon; + + if (help_data) { + *help_data = NULL; + } + if (widget_data) { + *widget_data = NULL; + } + + while (num_items--) { + if (item->icon_id) { + icon = gtk_image_new_from_stock(item->icon_id, GTK_ICON_SIZE_SMALL_TOOLBAR); + } else { + icon = NULL; + } + widget = gtk_toolbar_append_item(toolbar, + item->text, + item->help_text, + NULL, + icon, + item->callback, + item->user_data); + if (help_data) { + ctk_help_data_list_prepend(help_data, + item->text, + item->help_text, + item->extended_help_text); + } + if (widget_data) { + widget_data_item = malloc(sizeof(WidgetDataItem)); + widget_data_item->label = strdup(item->text); + widget_data_item->widget = widget; + *widget_data = g_list_prepend(*widget_data, widget_data_item); + } + item++; + } + + if (help_data) { + *help_data = g_list_reverse(*help_data); + } + if (widget_data) { + *widget_data = g_list_reverse(*widget_data); + } +} + +typedef struct CellRendererRegisterKeyDataRec { + GtkTreeView *tree_view; +} CellRendererRegisterKeyData; + +static void destroy_cell_renderer_register_key_data(gpointer data, GClosure *closure) +{ + free((CellRendererRegisterKeyData *)data); +} + +static void tree_view_get_cursor_path_and_column_idx(GtkTreeView *tree_view, + GtkTreePath **path, + gint *column_idx, + gint *column_count) +{ + GtkTreeViewColumn *focus_column; + GList *column_list, *column_in_list; + + column_list = gtk_tree_view_get_columns(tree_view); + gtk_tree_view_get_cursor(tree_view, path, &focus_column); + + // Lame... + column_in_list = g_list_find(column_list, focus_column); + *column_idx = g_list_position(column_list, column_in_list); + + *column_count = g_list_length(column_list); + + g_list_free(column_list); +} + +static void cell_renderer_editable(gpointer data, gpointer user_data) +{ + gboolean this_editable; + GtkCellRenderer *renderer = GTK_CELL_RENDERER(data); + gboolean *editable = (gboolean *)user_data; + + if (*editable) { + return; + } + + g_object_get(G_OBJECT(renderer), "editable", &this_editable, NULL); + + if (this_editable) { + *editable = TRUE; + } +} + +static gboolean tree_view_column_is_editable(GtkTreeViewColumn *tree_column) +{ + gboolean editable = FALSE; + GList *renderers = gtk_tree_view_column_get_cell_renderers(tree_column); + g_list_foreach(renderers, cell_renderer_editable, &editable); + return editable; +} + +static gboolean cell_renderer_widget_key_press_event(GtkWidget *widget, + GdkEvent *event, + gpointer user_data) +{ + CellRendererRegisterKeyData *data = (CellRendererRegisterKeyData *)user_data; + GtkTreeView *tree_view; + GtkTreeModel *tree_model; + GdkEventKey *key_event; + gint column_idx, column_count; + GtkTreeViewColumn *column; + GtkTreePath *path; + gint depth, *indices, row_idx; + gint row_count; + gint dx, dy; + + if (event->type == GDK_KEY_PRESS) { + key_event = (GdkEventKey *)event; + dx = dy = 0; + + if ((key_event->keyval == GDK_Tab) || + (key_event->keyval == GDK_ISO_Left_Tab)) { + dx = (key_event->state & GDK_SHIFT_MASK) ? -1 : 1; + } else if (key_event->keyval == GDK_Up) { + dy = -1; + } else if ((key_event->keyval == GDK_Down) || + (key_event->keyval == GDK_Return)) { + dy = 1; + } + + if (dx || dy) { + assert(!dx || !dy); + tree_view = data->tree_view; + tree_model = gtk_tree_view_get_model(tree_view); + + row_count = gtk_tree_model_iter_n_children(tree_model, NULL); + + // Done editing this cell + gtk_cell_editable_editing_done(GTK_CELL_EDITABLE(widget)); + gtk_cell_editable_remove_widget(GTK_CELL_EDITABLE(widget)); + + // Get currently highlighted row + tree_view_get_cursor_path_and_column_idx(tree_view, + &path, + &column_idx, + &column_count); + + depth = gtk_tree_path_get_depth(path); + assert(depth == 1); + (void)depth; + + indices = gtk_tree_path_get_indices(path); + row_idx = indices[0]; + + gtk_tree_path_free(path); + + if (dx) { + do { + column_idx += dx; + + assert(column_count >= 1); + + if (column_idx < 0) { + // go to previous row, if possible + row_idx--; + column_idx = column_count - 1; + } else if (column_idx >= column_count) { + // go to next row, if possible + row_idx++; + column_idx = 0; + } + + column = gtk_tree_view_get_column(tree_view, column_idx); + } while (!tree_view_column_is_editable(column) && + (row_idx >= 0) && (row_idx < row_count)); + } else { + row_idx += dy; + column = gtk_tree_view_get_column(tree_view, column_idx); + } + + if ((row_idx >= 0) && (row_idx < row_count)) { + path = gtk_tree_path_new(); + gtk_tree_path_append_index(path, row_idx); + + gtk_tree_view_set_cursor(tree_view, path, column, TRUE); + + gtk_tree_path_free(path); + } + + return TRUE; + } + } + + // Use default handlers + return FALSE; +} + +static gboolean cell_renderer_widget_focus_out_event(GtkWidget *widget, + GdkEvent *event, + gpointer user_data) +{ + // Done editing this cell + gtk_cell_editable_editing_done(GTK_CELL_EDITABLE(widget)); + gtk_cell_editable_remove_widget(GTK_CELL_EDITABLE(widget)); + + // Use default handlers + return FALSE; +} + + +static void cell_renderer_register_key_shortcuts(GtkCellRenderer *renderer, + GtkCellEditable *editable, + gchar *path, + gpointer user_data) +{ + if (GTK_IS_WIDGET(editable)) { + GtkWidget *widget = GTK_WIDGET(editable); + g_signal_connect(G_OBJECT(widget), "key-press-event", + G_CALLBACK(cell_renderer_widget_key_press_event), + user_data); + g_signal_connect(G_OBJECT(widget), "focus-out-event", + G_CALLBACK(cell_renderer_widget_focus_out_event), + user_data); + } else { + // XXX implement other types? + } +} + +/* Simple helper function to fill a tree view with text columns */ +static void populate_tree_view(GtkTreeView *tree_view, + const TreeViewColumnTemplate *column_template, + CtkAppProfile *ctk_app_profile, + size_t num_columns, + GList **help_data) +{ + GtkCellRenderer *cell_renderer; + GtkTreeViewColumn *tree_view_column; + GtkWidget *label; + + if (help_data) { + *help_data = NULL; + } + + while (num_columns--) { + cell_renderer = gtk_cell_renderer_text_new(); + tree_view_column = gtk_tree_view_column_new(); + + label = gtk_label_new(column_template->title); + if (column_template->help_text) { + ctk_config_set_tooltip(ctk_app_profile->ctk_config, + label, + column_template->help_text); + } + // Necessary since the label isn't part of the CtkAppProfile's hierarchy + gtk_widget_show(label); + + gtk_tree_view_column_set_widget(tree_view_column, label); + + + gtk_tree_view_column_pack_start(tree_view_column, cell_renderer, FALSE); + + if (column_template->renderer_func) { + assert(!column_template->attribute); + gtk_tree_view_column_set_cell_data_func(tree_view_column, + cell_renderer, + column_template->renderer_func, + column_template->func_data, + NULL); + } else { + gtk_tree_view_column_add_attribute(tree_view_column, + cell_renderer, + column_template->attribute, + column_template->attr_col); + assert(column_template->attribute); + } + + if (column_template->min_width > 0) { + gtk_tree_view_column_set_min_width(tree_view_column, column_template->min_width); + } + + if (column_template->sortable) { + gtk_tree_view_column_set_sort_column_id(tree_view_column, column_template->sort_column_id); + } + + if (column_template->editable) { + CellRendererRegisterKeyData *rk_data = malloc(sizeof(CellRendererRegisterKeyData)); + rk_data->tree_view = tree_view; + g_object_set(G_OBJECT(cell_renderer), "editable", TRUE, NULL); + g_signal_connect(G_OBJECT(cell_renderer), "edited", column_template->edit_callback, + column_template->func_data); + + // Generic code to implement navigating between fields with tab/shift-tab + g_signal_connect_data(G_OBJECT(cell_renderer), "editing-started", + G_CALLBACK(cell_renderer_register_key_shortcuts), + (gpointer)rk_data, destroy_cell_renderer_register_key_data, + 0); + } + + if (help_data) { + ctk_help_data_list_prepend(help_data, + column_template->title, + column_template->help_text, + column_template->extended_help_text); + } + + + gtk_tree_view_append_column(tree_view, tree_view_column); + column_template++; + } + + if (help_data) { + *help_data = g_list_reverse(*help_data); + } +} + +static void rule_order_renderer_func(GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + char *markup; + GtkTreePath *path; + gint *indices, depth; + + path = gtk_tree_model_get_path(model, iter); + indices = gtk_tree_path_get_indices(path); + depth = gtk_tree_path_get_depth(path); + + assert(depth == 1); + (void)depth; + + markup = nvasprintf("%d", indices[0] + 1); + + g_object_set(cell, "markup", markup, NULL); + + free(markup); + gtk_tree_path_free(path); +} + +static void rule_pattern_renderer_func(GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + char *feature, *matches; + char *feature_plain; + char *feature_markup; + char *matches_markup; + char *markup; + + gtk_tree_model_get(model, iter, + CTK_APC_RULE_MODEL_COL_FEATURE, &feature, + CTK_APC_RULE_MODEL_COL_MATCHES, &matches, + -1); + + feature_plain = nvasprintf("[%s]", feature); + feature_markup = markup_string(feature_plain, TRUE, "span", "color", "#444411", + "style", "italic", NULL); + matches_markup = g_markup_escape_text(matches, -1); + + markup = nvstrcat(feature_markup, " ", matches_markup, NULL); + + g_object_set(cell, "markup", markup, NULL); + + free(markup); + free(feature_markup); + free(matches_markup); + free(feature_plain); + free(feature); + free(matches); +} + + +static inline void setting_get_key_value(const json_t *setting, + char **key, + char **value, + gboolean add_markup) +{ + json_t *json_value; + const char *plain_key; + char *plain_value; + + if (key) { + plain_key = json_string_value(json_object_get(setting, "key")); + *key = markup_string(plain_key, add_markup, "span", "color", "#000033", NULL); + } + + if (value) { + json_value = json_object_get(setting, "value"); + switch(json_typeof(json_value)) { + case JSON_STRING: + case JSON_TRUE: + case JSON_FALSE: + case JSON_REAL: + plain_value = json_dumps(json_value, JSON_ENCODE_ANY); + break; + case JSON_INTEGER: + // Prefer hex to integer values + plain_value = nvasprintf("0x%" JSON_INTEGER_HEX_FORMAT, json_integer_value(json_value)); + break; + default: + plain_value = strdup("?"); + assert(0); + } + *value = markup_string(plain_value, add_markup, "span", "color", "#003300", NULL); + free(plain_value); + } +} + +char *serialize_settings(const json_t *settings, gboolean add_markup) +{ + char *old_markup, *markup; + char *one_setting, *value; + char *key; + json_t *setting; + size_t i, size; + + if (!settings) { + return markup_string("(no such profile)", add_markup, + "span", "color", "#555555", NULL); + } + + old_markup = strdup(""); + for (i = 0, size = json_array_size(settings); i < size; i++) { + if (i != 0) { + markup = nvstrcat(old_markup, ", ", NULL); + free(old_markup); + old_markup = markup; + } + setting = json_array_get(settings, i); + setting_get_key_value(setting, &key, &value, add_markup); + one_setting = nvasprintf("%s=%s", key, value); + + markup = nvstrcat(old_markup, one_setting, NULL); + free(one_setting); + free(key); + free(value); + free(old_markup); + old_markup = markup; + } + + return old_markup; +} + +static void rule_profile_settings_renderer_func(GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + CtkAppProfile *ctk_app_profile = (CtkAppProfile *)data; + char *settings_string; + const json_t *profile; + char *profile_name; + json_t *settings; + + gtk_tree_model_get(model, iter, CTK_APC_RULE_MODEL_COL_PROFILE_NAME, &profile_name, -1); + + profile = ctk_apc_profile_model_get_profile(ctk_app_profile->apc_profile_model, + profile_name); + settings = json_object_get(profile, "settings"); + + settings_string = serialize_settings(settings, TRUE); + + g_object_set(cell, "markup", settings_string, NULL); + + free(settings_string); + free(profile_name); +} + +static void increase_rule_priority_callback(GtkWidget *widget, gpointer user_data) +{ + GtkTreeViewColumn *focus_column; + GtkTreeIter iter; + GtkTreePath *path; + CtkAppProfile *ctk_app_profile = (CtkAppProfile *)user_data; + GValue id = G_VALUE_INIT; + + // Get currently highlighted row + gtk_tree_view_get_cursor(ctk_app_profile->main_rule_view, + &path, &focus_column); + if (!path) { + return; + } + + if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(ctk_app_profile->apc_rule_model), + &iter, path)) { + return; + } + + gtk_tree_model_get_value(GTK_TREE_MODEL(ctk_app_profile->apc_rule_model), + &iter, CTK_APC_RULE_MODEL_COL_ID, &id); + + // Increment the row + ctk_apc_rule_model_change_rule_priority(ctk_app_profile->apc_rule_model, + g_value_get_int(&id), + -1); + + ctk_config_statusbar_message(ctk_app_profile->ctk_config, + "Priority of rule increased. %s", + STATUSBAR_UPDATE_WARNING); + + gtk_tree_path_free(path); + g_value_unset(&id); +} + +static void decrease_rule_priority_callback(GtkWidget *widget, gpointer user_data) +{ + GtkTreeViewColumn *focus_column; + GtkTreeIter iter; + GtkTreePath *path; + CtkAppProfile *ctk_app_profile = (CtkAppProfile *)user_data; + GValue id = G_VALUE_INIT; + + // Get currently highlighted row + gtk_tree_view_get_cursor(ctk_app_profile->main_rule_view, + &path, &focus_column); + if (!path) { + return; + } + + if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(ctk_app_profile->apc_rule_model), + &iter, path)) { + return; + } + gtk_tree_model_get_value(GTK_TREE_MODEL(ctk_app_profile->apc_rule_model), + &iter, CTK_APC_RULE_MODEL_COL_ID, &id); + + // Decrement the row + ctk_apc_rule_model_change_rule_priority(ctk_app_profile->apc_rule_model, + g_value_get_int(&id), + 1); + + ctk_config_statusbar_message(ctk_app_profile->ctk_config, + "Priority of rule decreased. %s", + STATUSBAR_UPDATE_WARNING); + + gtk_tree_path_free(path); + g_value_unset(&id); +} + +static void string_list_free_cb(gpointer data, gpointer user_data) +{ + free(data); +} + +static void string_list_free_full(GList *list) +{ + g_list_foreach(list, string_list_free_cb, NULL); + g_list_free(list); +} + +static GList *get_source_filenames(CtkAppProfile *ctk_app_profile) +{ + size_t i, size; + json_t *json_filename, *json_filenames; + GList *filenames = NULL; + + json_filenames = nv_app_profile_config_get_source_filenames(ctk_app_profile->cur_config); + + for (i = 0, size = json_array_size(json_filenames); i < size; i++) { + json_filename = json_array_get(json_filenames, i); + filenames = g_list_prepend(filenames, strdup(json_string_value(json_filename))); + } + + filenames = g_list_reverse(filenames); + json_decref(json_filenames); + + return filenames; +} + +static gboolean append_profile_name(GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + GList **profile_names = (GList **)data; + gchar *profile_name; + + gtk_tree_model_get(model, iter, CTK_APC_PROFILE_MODEL_COL_NAME, &profile_name, -1); + + *profile_names = g_list_prepend(*profile_names, profile_name); + + return FALSE; +} + +static GList *get_profile_names(CtkAppProfile *ctk_app_profile) +{ + GList *profile_names = NULL; + + gtk_tree_model_foreach(GTK_TREE_MODEL(ctk_app_profile->apc_profile_model), + append_profile_name, + (gpointer)&profile_names); + + profile_names = g_list_reverse(profile_names); + return profile_names; +} + +static gboolean unref_setting_object(GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + json_t *setting; + + gtk_tree_model_get(model, iter, SETTING_LIST_STORE_COL_SETTING, &setting, -1); + + json_decref(setting); + + return FALSE; +} + +static void load_settings_from_profile(CtkAppProfile *ctk_app_profile, + GtkListStore *list_store, + const char *profile_name) +{ + GtkTreeIter iter; + size_t i, size; + const json_t *profile; + const json_t *settings; + json_t *setting; + + gtk_tree_model_foreach(GTK_TREE_MODEL(list_store), unref_setting_object, NULL); + gtk_list_store_clear(list_store); + + profile = ctk_apc_profile_model_get_profile(ctk_app_profile->apc_profile_model, + profile_name); + if (!profile) { + return; + } + + settings = json_object_get(profile, "settings"); + if (!settings) { + return; + } + for (i = 0, size = json_array_size(settings); i < size; i++) { + setting = json_deep_copy(json_array_get(settings, i)); + gtk_list_store_append(list_store, &iter); + gtk_list_store_set(list_store, &iter, SETTING_LIST_STORE_COL_SETTING, setting, -1); + } +} + +static void edit_rule_dialog_load_profile(EditRuleDialog *dialog, + const char *profile_name) +{ + GList *strings; + GtkCombo *combo; + + // profile name + combo = GTK_COMBO(dialog->profile_name_combo); + strings = get_profile_names(CTK_APP_PROFILE(dialog->parent)); + gtk_combo_set_popdown_strings(combo, strings); + + if (!profile_name) { + if (g_list_length(strings)) { + // Choose first string in the list + g_string_assign(dialog->profile_name, (gchar *)strings->data); + } else { + g_string_assign(dialog->profile_name, ""); + } + } else { + g_string_assign(dialog->profile_name, profile_name); + } + + gtk_entry_set_text(GTK_ENTRY(combo->entry), dialog->profile_name->str); + + string_list_free_full(strings); + + // profile settings + load_settings_from_profile(CTK_APP_PROFILE(dialog->parent), + dialog->profile_settings_store, + dialog->profile_name->str); +} + +static void edit_rule_dialog_load_values(EditRuleDialog *dialog) +{ + GList *strings; + GtkCombo *combo; + char *profile_name_copy; + + // window title + gtk_window_set_title(GTK_WINDOW(dialog->top_window), + dialog->new_rule ? "Add new rule" : "Edit existing rule"); + + // add/edit button + button_set_label_and_stock_icon(GTK_BUTTON(dialog->add_edit_rule_button), + "Update Rule", + dialog->new_rule ? GTK_STOCK_ADD : GTK_STOCK_PREFERENCES); + + // source file + combo = GTK_COMBO(dialog->source_file_combo); + strings = get_source_filenames(CTK_APP_PROFILE(dialog->parent)); + + gtk_combo_set_popdown_strings(combo, strings); + + if (dialog->new_rule) { + if (g_list_length(strings)) { + // Choose first string in the list + g_string_assign(dialog->source_file, (gchar *)strings->data); + } else { + g_string_assign(dialog->source_file, ""); + } + } + + gtk_entry_set_text(GTK_ENTRY(combo->entry), dialog->source_file->str); + + string_list_free_full(strings); + + // feature and matches + ctk_drop_down_menu_set_current_value(CTK_DROP_DOWN_MENU(dialog->feature_menu), + dialog->feature); + gtk_entry_set_text(dialog->matches_entry, dialog->matches->str); + + + // profile name and settings + profile_name_copy = dialog->new_rule ? NULL : strdup(dialog->profile_name->str); + edit_rule_dialog_load_profile(dialog, profile_name_copy); + free(profile_name_copy); + +} + +static void edit_rule_dialog_show(EditRuleDialog *dialog) +{ + // Temporarily disable the "changed" signal to prevent races between + // the update below and callbacks which fire when the window opens + g_signal_handler_block(G_OBJECT(dialog->feature_menu), + dialog->feature_changed_signal); + g_signal_handler_block(G_OBJECT(GTK_COMBO(dialog->profile_name_combo)->entry), + dialog->rule_profile_name_changed_signal); + + edit_rule_dialog_load_values(dialog); + gtk_widget_show_all(dialog->top_window); + g_signal_handler_unblock(G_OBJECT(dialog->feature_menu), + dialog->feature_changed_signal); + g_signal_handler_unblock(G_OBJECT(GTK_COMBO(dialog->profile_name_combo)->entry), + dialog->rule_profile_name_changed_signal); + + // disable focusing to main window until this window closed + gtk_window_set_transient_for(GTK_WINDOW(dialog->top_window), + GTK_WINDOW(gtk_widget_get_toplevel(dialog->parent))); + gtk_widget_set_sensitive(dialog->parent, FALSE); +} + +static void add_rule_callback(GtkWidget *widget, gpointer user_data) +{ + CtkAppProfile *ctk_app_profile = (CtkAppProfile *)user_data; + EditRuleDialog *dialog = ctk_app_profile->edit_rule_dialog; + + dialog->new_rule = TRUE; + dialog->rule_id = -1; + + g_string_assign(dialog->source_file, ""); + dialog->feature = RULE_FEATURE_PROCNAME; + g_string_assign(dialog->matches, ""); + g_string_assign(dialog->profile_name, ""); + + edit_rule_dialog_show(dialog); + +} + +static gint parse_feature(const char *feature_str) +{ + size_t i; + for (i = 0; i < ARRAY_LEN(rule_feature_identifiers); i++) { + if (!strcmp(feature_str, rule_feature_identifiers[i])) { + return i; + } + } + return 0; +} + +static void edit_rule_callbacks_common(CtkAppProfile *ctk_app_profile, + GtkTreePath *path) +{ + EditRuleDialog *dialog = ctk_app_profile->edit_rule_dialog; + gint id; + gchar *feature, *matches, *profile_name, *filename; + GtkTreeIter iter; + + if (!path) { + return; + } + + if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(ctk_app_profile->apc_rule_model), + &iter, path)) { + return; + } + + gtk_tree_model_get(GTK_TREE_MODEL(ctk_app_profile->apc_rule_model), + &iter, + CTK_APC_RULE_MODEL_COL_ID, &id, + CTK_APC_RULE_MODEL_COL_FEATURE, &feature, + CTK_APC_RULE_MODEL_COL_MATCHES, &matches, + CTK_APC_RULE_MODEL_COL_PROFILE_NAME, &profile_name, + CTK_APC_RULE_MODEL_COL_FILENAME, &filename, + -1); + + dialog->new_rule = FALSE; + dialog->rule_id = id; + g_string_assign(dialog->source_file, filename); + dialog->feature = parse_feature(feature); + g_string_assign(dialog->matches, matches); + g_string_assign(dialog->profile_name, profile_name); + + edit_rule_dialog_show(dialog); + + + free(filename); + free(feature); + free(matches); + free(profile_name); +} + +static void edit_rule_callback(GtkWidget *widget, gpointer user_data) +{ + GtkTreeViewColumn *focus_column; + GtkTreePath *path; + CtkAppProfile *ctk_app_profile = (CtkAppProfile *)user_data; + + // Get currently highlighted row + gtk_tree_view_get_cursor(ctk_app_profile->main_rule_view, + &path, &focus_column); + + edit_rule_callbacks_common(ctk_app_profile, path); + + gtk_tree_path_free(path); +} + +static void choose_next_row_in_list_view(GtkTreeView *tree_view, + GtkTreeModel *tree_model, + GtkTreePath **path) +{ + gint num_rows; + gint depth, *indices; + + num_rows = gtk_tree_model_iter_n_children(tree_model, NULL); + depth = gtk_tree_path_get_depth(*path); + indices = gtk_tree_path_get_indices(*path); + + assert(depth == 1); + (void)depth; + + if ((num_rows > 0) && (indices[0] == num_rows)) { + // Choose the previous row instead of the current one + gtk_tree_path_free(*path); + *path = gtk_tree_path_new(); + gtk_tree_path_append_index(*path, num_rows - 1); + } +} + +static void delete_rule_callback_common(CtkAppProfile *ctk_app_profile) +{ + GtkTreeViewColumn *focus_column; + GtkTreeIter iter; + GtkTreePath *path; + gint id; + + // Get currently highlighted row + gtk_tree_view_get_cursor(ctk_app_profile->main_rule_view, + &path, &focus_column); + if (!path) { + return; + } + + if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(ctk_app_profile->apc_rule_model), + &iter, path)) { + return; + } + + gtk_tree_model_get(GTK_TREE_MODEL(ctk_app_profile->apc_rule_model), + &iter, CTK_APC_RULE_MODEL_COL_ID, &id, -1); + + // Delete the row + ctk_apc_rule_model_delete_rule(ctk_app_profile->apc_rule_model, id); + + // Select next rule in the list, if available + choose_next_row_in_list_view(ctk_app_profile->main_rule_view, + GTK_TREE_MODEL(ctk_app_profile->apc_rule_model), + &path); + gtk_tree_view_set_cursor(ctk_app_profile->main_rule_view, + path, NULL, FALSE); + + ctk_config_statusbar_message(ctk_app_profile->ctk_config, + "Rule deleted. %s", + STATUSBAR_UPDATE_WARNING); + + gtk_tree_path_free(path); +} + +static void delete_rule_callback(GtkWidget *widget, gpointer user_data) +{ + CtkAppProfile *ctk_app_profile = (CtkAppProfile *)user_data; + delete_rule_callback_common(ctk_app_profile); +} + +static gboolean rules_tree_view_key_press_event(GtkWidget *widget, + GdkEvent *event, + gpointer user_data) +{ + CtkAppProfile *ctk_app_profile = (CtkAppProfile *)user_data; + GdkEventKey *key_event; + + if (event->type == GDK_KEY_PRESS) { + key_event = (GdkEventKey *)event; + if (key_event->keyval == GDK_Delete) { + delete_rule_callback_common(ctk_app_profile); + return TRUE; + } + } + + // Use default handlers + return FALSE; +} + +static gboolean rule_browse_button_clicked(GtkWidget *widget, gpointer user_data) +{ + EditRuleDialog *dialog = (EditRuleDialog *)user_data; + const gchar *filename = dialog->source_file->str; + gint result; + + gtk_window_set_transient_for(GTK_WINDOW(dialog->file_sel), + GTK_WINDOW(dialog->top_window)); + + gtk_file_selection_set_filename(GTK_FILE_SELECTION(dialog->file_sel), filename); + + result = gtk_dialog_run(GTK_DIALOG(dialog->file_sel)); + + switch (result) { + case GTK_RESPONSE_ACCEPT: + case GTK_RESPONSE_OK: + filename = gtk_file_selection_get_filename(GTK_FILE_SELECTION(dialog->file_sel)); + gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(dialog->source_file_combo)->entry), filename); + default: + break; + } + + gtk_widget_hide(dialog->file_sel); + return FALSE; +} + +static gboolean profile_browse_button_clicked(GtkWidget *widget, gpointer user_data) +{ + EditProfileDialog *dialog = (EditProfileDialog *)user_data; + const gchar *filename = dialog->source_file->str; + gint result; + + gtk_window_set_transient_for(GTK_WINDOW(dialog->file_sel), + GTK_WINDOW(dialog->top_window)); + + gtk_file_selection_set_filename(GTK_FILE_SELECTION(dialog->file_sel), filename); + + result = gtk_dialog_run(GTK_DIALOG(dialog->file_sel)); + + switch (result) { + case GTK_RESPONSE_ACCEPT: + case GTK_RESPONSE_OK: + filename = gtk_file_selection_get_filename(GTK_FILE_SELECTION(dialog->file_sel)); + gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(dialog->source_file_combo)->entry), filename); + default: + break; + } + + gtk_widget_hide(dialog->file_sel); + return FALSE; +} + +static const char __rule_pattern_help[] = + "In this section, you write the pattern that will be used to determine whether " + "the settings in this rule will apply to a given application."; + +static const char __rule_pattern_extended_help[] = + "A pattern is comprised of two parts: a feature of the " + "process which will be retrieved by the driver at runtime, and a string against " + "which the driver will compare the feature and determine if there is a match. " + "If the pattern matches, then the settings determined by the rule's associated " + "profile will be applied to the process, assuming they don't conflict with " + "settings determined by other matching rules with higher priority.\n\n" + "See the \"Supported Features\" help section for a list of supported features."; + +static const char __rule_profile_help[] = + "In this section, you choose the profile that will be applied if the rule's pattern " + "matches a given process."; + +static const char __rule_profile_extended_help[] = + "This section contains a drop-down box for choosing a profile name, and convenience " + "buttons for modifying an existing profile or creating a new profile to be used by " + "the rule. This section also has a table which lets you preview the settings that " + "will be applied by the given profile. The table is read-only: to modify individual " + "settings, click the \"Edit Profile\" button."; + +static void config_create_source_file_entry(CtkConfig *ctk_config, + GtkWidget **pcontainer, + GtkWidget **psource_file_combo, + GList **help_data_list, + const char *name, + GCallback browse_button_clicked_callback, + gpointer user_data) +{ + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *combo_box; + GtkWidget *browse_button; + + GString *help_string; + + help_string = g_string_new(""); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_set_spacing(GTK_BOX(hbox), 4); + + label = gtk_label_new("Source File"); + + g_string_printf(help_string, "You can specify the source file where the %s is defined in this drop-down box.", name); + + ctk_config_set_tooltip_and_add_help_data(ctk_config, + label, + help_data_list, + "Source File", + help_string->str, + NULL); + + combo_box = gtk_combo_new(); + browse_button = gtk_button_new(); + + button_set_label_and_stock_icon(GTK_BUTTON(browse_button), + "Browse...", GTK_STOCK_OPEN); + + g_string_printf(help_string, "Clicking this button opens a file selection dialog box which allows you to choose an " + "appropriate configuration file for the %s.", name); + + ctk_config_set_tooltip_and_add_help_data(ctk_config, + browse_button, + help_data_list, + "Browse...", + help_string->str, + NULL); + + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), combo_box, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(hbox), browse_button, FALSE, FALSE, 0); + + g_signal_connect(G_OBJECT(browse_button), "clicked", + browse_button_clicked_callback, + user_data); + + *pcontainer = hbox; + *psource_file_combo = combo_box; + + g_string_free(help_string, TRUE); +} + + +static void feature_changed(GtkWidget *widget, gpointer user_data) +{ + EditRuleDialog *dialog = (EditRuleDialog *)user_data; + dialog->feature = + ctk_drop_down_menu_get_current_value(CTK_DROP_DOWN_MENU(dialog->feature_menu)); +} + +static GtkWidget *create_feature_menu(EditRuleDialog *dialog) +{ + size_t i; + + dialog->feature_menu = ctk_drop_down_menu_new(CTK_DROP_DOWN_MENU_FLAG_COMBO); + + for (i = 0; i < NUM_RULE_FEATURES; i++) { + ctk_drop_down_menu_append_item(CTK_DROP_DOWN_MENU(dialog->feature_menu), + rule_feature_label_strings[i], i); + } + + dialog->feature_changed_signal = + g_signal_connect(G_OBJECT(dialog->feature_menu), + "changed", G_CALLBACK(feature_changed), (gpointer)dialog); + + return dialog->feature_menu; +} + +static void rule_profile_name_changed(GtkWidget *widget, gpointer user_data) +{ + const char *profile_name; + EditRuleDialog *dialog; + dialog = (EditRuleDialog *)user_data; + profile_name = gtk_entry_get_text(GTK_ENTRY(widget)); + g_string_assign(dialog->profile_name, profile_name); + + load_settings_from_profile(CTK_APP_PROFILE(dialog->parent), + dialog->profile_settings_store, + profile_name); +} + +typedef struct FindPathOfProfileParamsRec { + const char *profile_name; // in + GtkTreePath *path; // out +} FindPathOfProfileParams; + +/* + * find_path_of_profile() is a callback to gtk_tree_model_foreach which looks + * through the CtkApcProfileModel for a profile with the given profile name. + * For this to work properly, find_path_of_profile_params should be initialized + * as follows before calling gtk_tree_model_foreach(): + * profile_name: the name of the profile + * path: NULL + */ +static gboolean find_path_of_profile(GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + char *profile_name; + FindPathOfProfileParams *find_path_of_profile_params = + (FindPathOfProfileParams *)data; + + gtk_tree_model_get(model, iter, CTK_APC_PROFILE_MODEL_COL_NAME, &profile_name, -1); + + if (!strcmp(profile_name, find_path_of_profile_params->profile_name)) { + find_path_of_profile_params->path = + gtk_tree_path_copy(path); + return TRUE; // Done + } else { + return FALSE; // Keep going + } +} + +static void edit_profile_callbacks_common(CtkAppProfile *ctk_app_profile, + GtkTreePath *path, + GtkWidget *caller); + +static void edit_profile_dialog_show(EditProfileDialog *dialog); + +static gboolean rule_profile_entry_edit_profile_button_clicked(GtkWidget *widget, + gpointer user_data) +{ + EditRuleDialog *rule_dialog = (EditRuleDialog *)user_data; + CtkAppProfile *ctk_app_profile = CTK_APP_PROFILE(rule_dialog->parent); + FindPathOfProfileParams find_path_of_profile_params; + + memset(&find_path_of_profile_params, 0, sizeof(FindPathOfProfileParams)); + + find_path_of_profile_params.profile_name = rule_dialog->profile_name->str; + find_path_of_profile_params.path = NULL; + + gtk_tree_model_foreach(GTK_TREE_MODEL(ctk_app_profile->apc_profile_model), + find_path_of_profile, + &find_path_of_profile_params); + + edit_profile_callbacks_common(ctk_app_profile, + find_path_of_profile_params.path, + rule_dialog->top_window); + + gtk_tree_path_free(find_path_of_profile_params.path); + + return FALSE; +} + +static void add_profile_callbacks_common(CtkAppProfile *ctk_app_profile, + GtkWidget *caller); + +static gboolean rule_profile_entry_new_profile_button_clicked(GtkWidget *widget, + gpointer user_data) +{ + EditRuleDialog *rule_dialog = (EditRuleDialog *)user_data; + CtkAppProfile *ctk_app_profile = CTK_APP_PROFILE(rule_dialog->parent); + + add_profile_callbacks_common(ctk_app_profile, rule_dialog->top_window); + + return FALSE; +} + +static GtkWidget *create_rule_profile_name_entry(EditRuleDialog *dialog) +{ + GtkWidget *hbox; + GtkWidget *button; + GtkWidget *label; + GtkWidget *combo_box; + + hbox = gtk_hbox_new(FALSE, 8); + + label = gtk_label_new("Profile Name"); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + + dialog->profile_name_combo = combo_box = gtk_combo_new(); + + gtk_box_pack_start(GTK_BOX(hbox), combo_box, TRUE, TRUE, 0); + + dialog->rule_profile_name_changed_signal = + g_signal_connect(G_OBJECT(GTK_COMBO(combo_box)->entry), "changed", + G_CALLBACK(rule_profile_name_changed), + (gpointer)dialog); + + button = gtk_button_new_with_label("Edit Profile"); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(rule_profile_entry_edit_profile_button_clicked), + (gpointer)dialog); + + button = gtk_button_new_with_label("New Profile"); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(rule_profile_entry_new_profile_button_clicked), + (gpointer)dialog); + + return hbox; +} + +static void setting_key_renderer_func(GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + const char *key; + json_t *setting; + gtk_tree_model_get(model, iter, SETTING_LIST_STORE_COL_SETTING, &setting, -1); + key = json_string_value(json_object_get(setting, "key")); + g_object_set(cell, "text", key, NULL); +} + +static void setting_type_renderer_func(GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + const char *type = NULL; + json_t *setting, *value; + gtk_tree_model_get(model, iter, SETTING_LIST_STORE_COL_SETTING, &setting, -1); + value = json_object_get(setting, "value"); + + switch(json_typeof(value)) { + case JSON_STRING: + type = "string"; + break; + case JSON_INTEGER: + type = "int"; + break; + case JSON_REAL: + type = "float"; + break; + case JSON_TRUE: + case JSON_FALSE: + type = "bool"; + break; + default: + assert(0); + } + + g_object_set(cell, "text", type, NULL); +} + +static void setting_value_renderer_func(GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + json_t *setting; + char *value; + gtk_tree_model_get(model, iter, SETTING_LIST_STORE_COL_SETTING, &setting, -1); + setting_get_key_value(setting, NULL, &value, TRUE); + g_object_set(cell, "markup", value, NULL); + free(value); +} + +static gboolean run_error_dialog(GtkWindow *window, + GString *fatal_errors, + GString *nonfatal_errors, + const char *op_string) +{ + GString *error_string; + GtkWidget *error_dialog; + gboolean success; + gint result; + + if (!fatal_errors->len && !nonfatal_errors->len) { + return TRUE; + } + + error_string = g_string_new(""); + if (fatal_errors->len) { + g_string_append_printf(error_string, + "nvidia-settings encountered the following configuration errors:\n\n" + "%s\n", + fatal_errors->str); + } + if (nonfatal_errors->len) { + g_string_append_printf(error_string, + "%snvidia-settings encountered the following configuration issues:\n\n" + "%s\n", + fatal_errors->len ? "Also, " : "", nonfatal_errors->str); + } + + if (fatal_errors->len) { + g_string_append_printf(error_string, + "Please fix the configuration errors before attempting to %s.\n", + op_string); + } else { + g_string_append_printf(error_string, + "Continue to %s anyway?\n", op_string); + } + + error_dialog = gtk_message_dialog_new(window, + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + fatal_errors->len ? GTK_MESSAGE_ERROR : GTK_MESSAGE_QUESTION, + fatal_errors->len ? GTK_BUTTONS_CLOSE : GTK_BUTTONS_YES_NO, + "%s", error_string->str); + result = gtk_dialog_run(GTK_DIALOG(error_dialog)); + + if (!fatal_errors->len) { + success = (result == GTK_RESPONSE_YES); + } else { + success = FALSE; + } + + gtk_widget_destroy(error_dialog); + + return success; +} + + +static inline gboolean check_valid_source_file(CtkAppProfile *ctk_app_profile, + const char *source_file_str, + char **reason) +{ + return !!nv_app_profile_config_check_valid_source_file(ctk_app_profile->cur_config, + source_file_str, + reason); +} + +// Check for inconsistencies and errors in the rule dialog box settings, +// and warn the user if any are detected +static gboolean edit_rule_dialog_validate(EditRuleDialog *dialog) +{ + CtkAppProfile *ctk_app_profile = CTK_APP_PROFILE(dialog->parent); + GString *fatal_errors; + GString *nonfatal_errors; + gboolean success; + char *reason; + + fatal_errors = g_string_new(""); + nonfatal_errors = g_string_new(""); + + if (!check_valid_source_file(ctk_app_profile, dialog->source_file->str, &reason)) { + g_string_append_printf(fatal_errors, "%s\tThe source filename \"%s\" is not valid in this configuration " + "because %s\n", get_bullet(), dialog->source_file->str, reason); + free(reason); + } + + if (!ctk_apc_profile_model_get_profile(ctk_app_profile->apc_profile_model, dialog->profile_name->str)) { + g_string_append_printf(nonfatal_errors, "%s\tThe profile \"%s\" referenced by this rule does not exist.\n", + get_bullet(), dialog->profile_name->str); + } + + success = run_error_dialog(GTK_WINDOW(dialog->top_window), + fatal_errors, + nonfatal_errors, + "save this rule"); + + g_string_free(fatal_errors, TRUE); + g_string_free(nonfatal_errors, TRUE); + + return success; +} + +static void edit_rule_dialog_save_changes(GtkWidget *widget, gpointer user_data) +{ + EditRuleDialog *dialog = (EditRuleDialog *)user_data; + CtkAppProfile *ctk_app_profile = CTK_APP_PROFILE(dialog->parent); + GtkWidget *source_file_entry = GTK_COMBO(dialog->source_file_combo)->entry; + json_t *rule_json = json_object(); + json_t *pattern_json = json_object(); + + // Get the latest values from our widgets + g_string_assign(dialog->matches, gtk_entry_get_text(GTK_ENTRY(dialog->matches_entry))); + g_string_assign(dialog->source_file, gtk_entry_get_text(GTK_ENTRY(source_file_entry))); + + // Chceck for inconsistencies and errors + if (!edit_rule_dialog_validate(dialog)) { + return; + } + + // Construct the update object + json_object_set_new(pattern_json, "feature", json_string(rule_feature_identifiers[dialog->feature])); + json_object_set_new(pattern_json, "matches", json_string(dialog->matches->str)); + json_object_set_new(rule_json, "profile", json_string(dialog->profile_name->str)); + json_object_set_new(rule_json, "pattern", pattern_json); + + // Update the rule in the configuration + if (dialog->new_rule) { + ctk_apc_rule_model_create_rule(ctk_app_profile->apc_rule_model, + dialog->source_file->str, rule_json); + + } else { + ctk_apc_rule_model_update_rule(ctk_app_profile->apc_rule_model, + dialog->source_file->str, + dialog->rule_id, + rule_json); + } + + json_decref(rule_json); + + // Close the window, and re-sensitize the parent + gtk_widget_set_sensitive(dialog->parent, TRUE); + gtk_widget_hide(dialog->top_window); + + ctk_config_statusbar_message(ctk_app_profile->ctk_config, + "Rule updated. %s", + STATUSBAR_UPDATE_WARNING); +} + +static void edit_rule_dialog_cancel(GtkWidget *widget, gpointer user_data) +{ + EditRuleDialog *dialog = (EditRuleDialog *)user_data; + // Close the window, and re-sensitize the parent + gtk_widget_set_sensitive(dialog->parent, TRUE); + gtk_widget_hide(dialog->top_window); +} + +static ToolbarItemTemplate *get_edit_rule_dialog_toolbar_items(EditRuleDialog *dialog, + size_t *num_items) +{ + ToolbarItemTemplate *items_copy; + const ToolbarItemTemplate items[] = { + { + .text = UPDATE_RULE_LABEL, + .help_text = "The Update Rule button allows you to save changes made to the rule definition.", + .icon_id = GTK_STOCK_SAVE, + .callback = G_CALLBACK(edit_rule_dialog_save_changes), + .user_data = dialog, + }, + { + .text = "Cancel", + .help_text = "The Cancel button allows you to discard any changes made to the rule definition.", + .icon_id = GTK_STOCK_CANCEL, + .callback = G_CALLBACK(edit_rule_dialog_cancel), + .user_data = dialog, + } + }; + + items_copy = malloc(sizeof(items)); + memcpy(items_copy, items, sizeof(items)); + + *num_items = ARRAY_LEN(items); + return items_copy; +} + +static gboolean edit_rule_dialog_handle_delete(GtkWidget *widget, + GdkEvent *event, + gpointer user_data) +{ + EditRuleDialog *dialog = (EditRuleDialog *)user_data; + gtk_widget_set_sensitive(dialog->parent, TRUE); + gtk_widget_hide(widget); + + return TRUE; +} + +static EditRuleDialog* edit_rule_dialog_new(CtkAppProfile *ctk_app_profile) +{ + ToolbarItemTemplate *edit_rule_dialog_toolbar_items; + size_t num_edit_rule_dialog_toolbar_items; + EditRuleDialog *dialog; + GtkWidget *label; + GtkWidget *container; + GtkWidget *main_vbox, *vbox; + GtkWidget *feature_menu, *profile_name_entry; + GtkWidget *table; + GtkWidget *frame; + GtkWidget *entry; + GtkWidget *tree_view; + GtkWidget *toolbar; + GtkWidget *alignment; + GtkWidget *scroll_win; + GList *toolbar_help_items; + GList *toolbar_widget_items; + + const TreeViewColumnTemplate settings_tree_view_columns[] = { + { + .title = "Key", + .renderer_func = setting_key_renderer_func, + .func_data = NULL, + .min_width = 200, + .help_text = "Each entry in the \"Key\" column describes a key for a setting." + }, + { + .title = "Type", + .renderer_func = setting_type_renderer_func, + .min_width = 100, + .func_data = NULL, + .help_text = "Each entry in the \"Type\" column describes the underlying JSON type for " + "a setting value." + }, + { + .title = "Value", + .renderer_func = setting_value_renderer_func, + .func_data = NULL, + .help_text = "Each entry in the \"Value\" column describes the value of a setting." + } + }; + + + dialog = malloc(sizeof(EditRuleDialog)); + if (!dialog) { + return NULL; + } + + dialog->help_data = NULL; + + dialog->parent = GTK_WIDGET(ctk_app_profile); + dialog->top_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + + gtk_window_set_modal(GTK_WINDOW(dialog->top_window), TRUE); + + g_signal_connect(G_OBJECT(dialog->top_window), "delete-event", + G_CALLBACK(edit_rule_dialog_handle_delete), dialog); + + dialog->source_file = g_string_new(""); + dialog->feature = RULE_FEATURE_PROCNAME; + dialog->matches = g_string_new(""); + dialog->profile_name = g_string_new(""); + dialog->profile_settings_store = gtk_list_store_new(SETTING_LIST_STORE_NUM_COLS, G_TYPE_POINTER); + dialog->file_sel = gtk_file_selection_new("Please select a source file for the rule"); + + gtk_widget_set_size_request(dialog->top_window, 500, 480); + gtk_container_set_border_width(GTK_CONTAINER(dialog->top_window), 8); + + main_vbox = gtk_vbox_new(FALSE, 0); + gtk_box_set_spacing(GTK_BOX(main_vbox), 8); + + gtk_container_add(GTK_CONTAINER(dialog->top_window), main_vbox); + + config_create_source_file_entry(ctk_app_profile->ctk_config, + &container, + &dialog->source_file_combo, + &dialog->help_data, + "rule", + G_CALLBACK(rule_browse_button_clicked), + (gpointer)dialog); + + gtk_box_pack_start(GTK_BOX(main_vbox), container, FALSE, FALSE, 0); + + frame = gtk_frame_new("Rule Pattern"); + gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN); + + label = gtk_frame_get_label_widget(GTK_FRAME(frame)); + + ctk_config_set_tooltip_and_add_help_data(ctk_app_profile->ctk_config, + label, + &dialog->help_data, + "Rule Pattern", + __rule_pattern_help, + __rule_pattern_extended_help); + + // Add widgets to the "Rule Pattern" section + vbox = gtk_vbox_new(FALSE, 0); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 4); + + label = gtk_label_new("The following profile will be used if..."); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); + + table = gtk_table_new(2, 2, FALSE); + + label = gtk_label_new("This feature:"); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1); + + feature_menu = create_feature_menu(dialog); + gtk_table_attach_defaults(GTK_TABLE(table), feature_menu, 1, 2, 0, 1); + + label = gtk_label_new("Matches this string:"); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2); + + entry = gtk_entry_new(); + dialog->matches_entry = GTK_ENTRY(entry); + gtk_table_attach_defaults(GTK_TABLE(table), entry, 1, 2, 1, 2); + + gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, TRUE, 0); + + gtk_container_add(GTK_CONTAINER(frame), vbox); + + gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 0); + + frame = gtk_frame_new("Rule Profile"); + gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN); + + label = gtk_frame_get_label_widget(GTK_FRAME(frame)); + + ctk_config_set_tooltip_and_add_help_data(ctk_app_profile->ctk_config, + label, + &dialog->help_data, + "Rule Profile", + __rule_profile_help, + __rule_profile_extended_help); + + vbox = gtk_vbox_new(FALSE, 8); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 8); + + profile_name_entry = create_rule_profile_name_entry(dialog); + gtk_box_pack_start(GTK_BOX(vbox), profile_name_entry, FALSE, FALSE, 0); + + label = gtk_label_new("This profile will apply the following settings..."); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); + + tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(dialog->profile_settings_store)); + populate_tree_view(GTK_TREE_VIEW(tree_view), + settings_tree_view_columns, + ctk_app_profile, + ARRAY_LEN(settings_tree_view_columns), + NULL); + + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tree_view), TRUE); + + scroll_win = gtk_scrolled_window_new(NULL, NULL); + gtk_container_add(GTK_CONTAINER(scroll_win), tree_view); + gtk_box_pack_start(GTK_BOX(vbox), scroll_win, TRUE, TRUE, 0); + + gtk_container_add(GTK_CONTAINER(frame), vbox); + + gtk_box_pack_start(GTK_BOX(main_vbox), frame, TRUE, TRUE, 0); + + alignment = gtk_alignment_new(1.0, 0.5, 0.0, 0.0); + toolbar = gtk_toolbar_new(); + + dialog->help_data = g_list_reverse(dialog->help_data); + + edit_rule_dialog_toolbar_items = get_edit_rule_dialog_toolbar_items(dialog, &num_edit_rule_dialog_toolbar_items); + populate_toolbar(GTK_TOOLBAR(toolbar), + edit_rule_dialog_toolbar_items, + num_edit_rule_dialog_toolbar_items, + &toolbar_help_items, + &toolbar_widget_items); + + dialog->help_data = g_list_concat(dialog->help_data, toolbar_help_items); + + // Save off the "Update Rule" button for later use + dialog->add_edit_rule_button = find_widget_in_widget_data_list(toolbar_widget_items, UPDATE_RULE_LABEL); + + free(edit_rule_dialog_toolbar_items); + + gtk_container_add(GTK_CONTAINER(alignment), toolbar); + gtk_box_pack_start(GTK_BOX(main_vbox), alignment, FALSE, FALSE, 0); + + return dialog; +} + +static void edit_rule_dialog_destroy(EditRuleDialog *dialog) +{ + g_string_free(dialog->source_file, TRUE); + g_string_free(dialog->matches, TRUE); + g_string_free(dialog->profile_name, TRUE); + ctk_help_data_list_free_full(dialog->help_data); + free(dialog); +} + +static void edit_profile_dialog_settings_new_row(GtkTreeView *tree_view, + GtkTreeModel *tree_model, + GtkTreePath **path, + GtkTreeViewColumn **column) +{ + GtkTreeIter iter; + json_t *setting = json_object(); + json_object_set(setting, "key", json_string("")); + json_object_set(setting, "value", json_false()); + gtk_list_store_append(GTK_LIST_STORE(tree_model), &iter); + gtk_list_store_set(GTK_LIST_STORE(tree_model), &iter, SETTING_LIST_STORE_COL_SETTING, setting, -1); + + *path = gtk_tree_model_get_path(tree_model, &iter); + *column = gtk_tree_view_get_column(tree_view, 0); +} + +static void edit_profile_dialog_add_setting(GtkWidget *widget, gpointer user_data) +{ + EditProfileDialog *dialog = (EditProfileDialog *)user_data; + GtkTreePath *path; + GtkTreeViewColumn *column; + + edit_profile_dialog_settings_new_row(dialog->settings_view, + GTK_TREE_MODEL(dialog->settings_store), + &path, + &column); + + gtk_widget_grab_focus(GTK_WIDGET(dialog->settings_view)); + gtk_tree_view_set_cursor(dialog->settings_view, path, column, TRUE); + + gtk_tree_path_free(path); +} + +static void edit_profile_dialog_delete_setting_common(EditProfileDialog *dialog) +{ + GtkTreeIter iter; + GtkTreePath *path; + GtkTreeViewColumn *focus_column; + + // Set the focus to NULL to terminate any editing currently taking place + // XXX: Since this row is about to be deleted, set the + // setting_update_canceled flag to ensure that the model isn't saving + // anything to this row and displaying bogus warnings to the user. + dialog->setting_update_canceled = TRUE; + gtk_window_set_focus(GTK_WINDOW(dialog->top_window), NULL); + dialog->setting_update_canceled = FALSE; + + // Get currently highlighted row + gtk_tree_view_get_cursor(dialog->settings_view, &path, &focus_column); + + if (!path) { + return; + } + + if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->settings_store), &iter, path)) { + return; + } + + // Delete the row + gtk_list_store_remove(dialog->settings_store, &iter); + + // Select next setting in the list, if available + choose_next_row_in_list_view(dialog->settings_view, + GTK_TREE_MODEL(dialog->settings_store), + &path); + gtk_tree_view_set_cursor(dialog->settings_view, path, NULL, FALSE); + + gtk_tree_path_free(path); +} + +static void edit_profile_dialog_delete_setting(GtkWidget *widget, gpointer user_data) +{ + EditProfileDialog *dialog = (EditProfileDialog *)user_data; + edit_profile_dialog_delete_setting_common(dialog); +} + +static void edit_profile_dialog_edit_setting(GtkWidget *widget, gpointer user_data) +{ + EditProfileDialog *dialog = (EditProfileDialog *)user_data; + GtkTreePath *path; + GtkTreeViewColumn *first_column; + + // Get currently highlighted row + gtk_tree_view_get_cursor(dialog->settings_view, &path, NULL); + if (!path) { + return; + } + + first_column = gtk_tree_view_get_column(dialog->settings_view, 0); + assert(first_column); + + gtk_widget_grab_focus(GTK_WIDGET(dialog->settings_view)); + gtk_tree_view_set_cursor(dialog->settings_view, path, first_column, TRUE); + + gtk_tree_path_free(path); +} + +static gboolean append_setting(GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + json_t *settings = (json_t *)data; + json_t *setting; + + gtk_tree_model_get(model, iter, SETTING_LIST_STORE_COL_SETTING, &setting, -1); + json_array_append(settings, setting); + + return FALSE; +} + +static void edit_profile_dialog_update_settings(EditProfileDialog *dialog) +{ + json_array_clear(dialog->settings); + gtk_tree_model_foreach(GTK_TREE_MODEL(dialog->settings_store), + append_setting, + (gpointer)dialog->settings); +} + +static gboolean widget_get_visible(GtkWidget *widget) +{ + gboolean visible; + + g_object_get(G_OBJECT(widget), + "visible", &visible, NULL); + + return visible; +} + +static const gchar *get_canonical_setting_key(const gchar *key) +{ + size_t i; + for (i = 0; i < NUM_PROFILE_SETTINGS; i++) { + if (!strcasecmp(key, profile_setting_keys[i])) { + return profile_setting_keys[i]; + } + } + return NULL; +} + +static gboolean check_unrecognized_setting_keys(const json_t *settings) +{ + const json_t *setting; + const char *setting_key; + size_t i, size; + for (i = 0, size = json_array_size(settings); i < size; i++) { + setting = json_array_get(settings, i); + setting_key = json_string_value(json_object_get(setting, "key")); + if (!get_canonical_setting_key(setting_key)) { + return TRUE; + } + } + + return FALSE; +} + +// Check for inconsistencies and errors in the profile dialog box settings, +// and warn the user if any are detected +static gboolean edit_profile_dialog_validate(EditProfileDialog *dialog) +{ + CtkAppProfile *ctk_app_profile = CTK_APP_PROFILE(dialog->parent); + GString *fatal_errors; + GString *nonfatal_errors; + gboolean success; + char *reason; + + fatal_errors = g_string_new(""); + nonfatal_errors = g_string_new(""); + + if (!strcmp(dialog->name->str, "")) { + g_string_append_printf(nonfatal_errors, "%s\tThe profile name is empty.\n", get_bullet()); + } + + if ((dialog->new_profile || strcmp(dialog->name->str, dialog->orig_name->str)) && + ctk_apc_profile_model_get_profile(ctk_app_profile->apc_profile_model, + dialog->name->str)) { + if (dialog->new_profile) { + g_string_append_printf(nonfatal_errors, + "%s\tA profile with the name \"%s\" already exists and will be " + "overwritten.\n", + get_bullet(), dialog->name->str); + } else { + g_string_append_printf(nonfatal_errors, + "%s\tRenaming this profile from \"%s\" to \"%s\" will " + "overwrite an existing profile.\n", get_bullet(), + dialog->orig_name->str, + dialog->name->str); + } + } + + if (!check_valid_source_file(ctk_app_profile, dialog->source_file->str, &reason)) { + g_string_append_printf(fatal_errors, "%s\tThe source filename \"%s\" is not valid in this configuration " + "because %s\n", get_bullet(), dialog->source_file->str, reason); + free(reason); + } + + if (check_unrecognized_setting_keys(dialog->settings)) { + g_string_append_printf(nonfatal_errors, "%s\tThis profile has settings with keys that may not be recognized " + "by the NVIDIA graphics driver. Consult the on-line help for a list " + "of valid keys.\n", get_bullet()); + } + + success = run_error_dialog(GTK_WINDOW(dialog->top_window), + fatal_errors, + nonfatal_errors, + "save this profile"); + + g_string_free(fatal_errors, TRUE); + g_string_free(nonfatal_errors, TRUE); + + return success; +} + +static void edit_profile_dialog_save_changes(GtkWidget *widget, gpointer user_data) +{ + EditProfileDialog *profile_dialog = (EditProfileDialog *)user_data; + EditRuleDialog *rule_dialog; + CtkAppProfile *ctk_app_profile = CTK_APP_PROFILE(profile_dialog->parent); + GtkWidget *source_file_entry = GTK_COMBO(profile_dialog->source_file_combo)->entry; + json_t *profile_json = json_object(); + GtkCombo *combo; + GList *source_filenames; + gboolean rules_fixed_up = FALSE; + + rule_dialog = ctk_app_profile->edit_rule_dialog; + + // Set the focus to NULL to terminate any editing currently taking place + gtk_window_set_focus(GTK_WINDOW(profile_dialog->top_window), NULL); + + // Get the latest values from our widgets + g_string_assign(profile_dialog->name, gtk_entry_get_text(GTK_ENTRY(profile_dialog->name_entry))); + g_string_assign(profile_dialog->source_file, gtk_entry_get_text(GTK_ENTRY(source_file_entry))); + edit_profile_dialog_update_settings(profile_dialog); + + // TODO delete any unset settings (nil key and value)? + + // Check for inconsistencies and errors + if (!edit_profile_dialog_validate(profile_dialog)) { + return; + } + + // Construct the update object, using a deep copy of the settings array. + json_object_set_new(profile_json, "settings", + json_deep_copy(profile_dialog->settings)); + + // If this is an edit and the profile name changed, delete the old profile + if (!profile_dialog->new_profile && + strcmp(profile_dialog->name->str, profile_dialog->orig_name->str)) { + ctk_apc_profile_model_delete_profile(ctk_app_profile->apc_profile_model, + profile_dialog->orig_name->str); + if (ctk_app_profile->ctk_config->conf->booleans & + CONFIG_PROPERTIES_UPDATE_RULES_ON_PROFILE_NAME_CHANGE) { + rules_fixed_up = + nv_app_profile_config_profile_name_change_fixup( + ctk_app_profile->cur_config, + profile_dialog->orig_name->str, + profile_dialog->name->str); + } + } + + + // Update the profile in the configuration + ctk_apc_profile_model_update_profile(ctk_app_profile->apc_profile_model, + profile_dialog->source_file->str, + profile_dialog->name->str, + profile_json); + + // Refresh the view in the rule, if necessary + if (widget_get_visible(rule_dialog->top_window)) { + // XXX could this be abstracted? + edit_rule_dialog_load_profile(rule_dialog, profile_dialog->name->str); + source_filenames = get_source_filenames(ctk_app_profile); + combo = GTK_COMBO(rule_dialog->source_file_combo); + gtk_combo_set_popdown_strings(combo, source_filenames); + gtk_entry_set_text(GTK_ENTRY(combo->entry), rule_dialog->source_file->str); + string_list_free_full(source_filenames); + } + + json_decref(profile_json); + + ctk_config_statusbar_message(ctk_app_profile->ctk_config, + "Profile \"%s\" updated. %s%s", + profile_dialog->name->str, + rules_fixed_up ? + "Some rules have been updated to refer " + "to the new profile name. " : "", + STATUSBAR_UPDATE_WARNING); + + // Close the window, and re-sensitize the caller + gtk_widget_set_sensitive(profile_dialog->caller, TRUE); + gtk_widget_hide(profile_dialog->top_window); +} + +static void edit_profile_dialog_cancel(GtkWidget *widget, gpointer user_data) +{ + EditProfileDialog *dialog = (EditProfileDialog *)user_data; + + // Close the window, and re-sensitize the caller + gtk_widget_set_sensitive(dialog->caller, TRUE); + gtk_widget_hide(dialog->top_window); +} + +static void get_profile_dialog_toolbar_items(EditProfileDialog *dialog, + ToolbarItemTemplate **settings_items_copy, + size_t *num_settings_items, + ToolbarItemTemplate **dialog_items_copy, + size_t *num_dialog_items) +{ + const ToolbarItemTemplate settings_items[] = { + { + .text = "Add Setting", + .help_text = "The Add Setting button allows you to create a new setting in the profile.", + .icon_id = GTK_STOCK_ADD, + .callback = G_CALLBACK(edit_profile_dialog_add_setting), + .user_data = dialog, + }, + { + .text = "Delete Setting", + .help_text = "The Delete Setting button allows you to delete a highlighted setting from the profile.", + .extended_help_text = "A setting can also be deleted from the profile by highlighting it in the list " + "and hitting the Delete key.", + .icon_id = GTK_STOCK_REMOVE, + .callback = G_CALLBACK(edit_profile_dialog_delete_setting), + .user_data = dialog, + }, + { + .text = "Edit Setting", + .help_text = "The Edit Setting button allows you to edit a highlighted setting in the profile.", + .extended_help_text = "This will activate an entry box in the setting's key column. To modify the setting's " + "value, hit the Tab key or Right Arrow key, or double-click on the value.", + .icon_id = GTK_STOCK_PREFERENCES, + .callback = G_CALLBACK(edit_profile_dialog_edit_setting), + .user_data = dialog, + }, + }; + + const ToolbarItemTemplate dialog_items[] = { + { + .text = UPDATE_PROFILE_LABEL, + .help_text = "The Update Profile button allows you to save changes made to the profile definition.", + .icon_id = GTK_STOCK_SAVE, + .callback = G_CALLBACK(edit_profile_dialog_save_changes), + .user_data = dialog, + }, + { + .text = "Cancel", + .help_text = "The Cancel button allows you to discard any changes made to the profile definition.", + .icon_id = GTK_STOCK_CANCEL, + .callback = G_CALLBACK(edit_profile_dialog_cancel), + .user_data = dialog, + } + }; + + *settings_items_copy = malloc(sizeof(settings_items)); + memcpy(*settings_items_copy, settings_items, sizeof(settings_items)); + *num_settings_items = ARRAY_LEN(settings_items); + + *dialog_items_copy = malloc(sizeof(dialog_items)); + memcpy(*dialog_items_copy, dialog_items, sizeof(dialog_items)); + *num_dialog_items = ARRAY_LEN(dialog_items); +} + + +static void setting_key_edited(GtkCellRendererText *renderer, + gchar *path_s, + gchar *new_text, + gpointer user_data) +{ + EditProfileDialog *dialog = (EditProfileDialog *)user_data; + GtkTreePath *path; + GtkTreeIter iter; + json_t *setting; + GString *error_string; + const gchar *canonical_key; + + if (dialog->setting_update_canceled) { + // Don't update anything + return; + } + + path = gtk_tree_path_new_from_string(path_s); + if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->settings_store), + &iter, path)) { + // The row might have been deleted. Cancel any update + return; + } + + gtk_tree_model_get(GTK_TREE_MODEL(dialog->settings_store), &iter, + SETTING_LIST_STORE_COL_SETTING, &setting, -1); + + canonical_key = get_canonical_setting_key(new_text); + if (!canonical_key) { + error_string = g_string_new(""); + g_string_printf(error_string, "The key [%s] is not recognized by nvidia-settings." + "Please check for spelling errors (keys " + "are NOT case sensitive).", new_text); + gtk_statusbar_push(GTK_STATUSBAR(dialog->error_statusbar), + dialog->setting_error_context_id, + error_string->str); + g_string_free(error_string, TRUE); + } + + if (canonical_key) { + json_object_set_new(setting, "key", json_string(canonical_key)); + } else { + json_object_set_new(setting, "key", json_string(new_text)); + } + + gtk_tree_path_free(path); +} + +static gboolean is_valid_setting_value(json_t *value, char **invalid_type_str) +{ + switch (json_typeof(value)) { + case JSON_STRING: + case JSON_TRUE: + case JSON_FALSE: + case JSON_REAL: + case JSON_INTEGER: + *invalid_type_str = NULL; + return TRUE; + case JSON_NULL: + *invalid_type_str = "null"; + return FALSE; + case JSON_OBJECT: + *invalid_type_str = "object"; + return FALSE; + case JSON_ARRAY: + *invalid_type_str = "array"; + return FALSE; + default: + assert(0); + return FALSE; + } +} + +static void setting_value_edited(GtkCellRendererText *renderer, + gchar *path_s, + gchar *new_text, + gpointer user_data) +{ + gchar *invalid_type_str = NULL; + gchar *new_text_in_json; + EditProfileDialog *dialog = (EditProfileDialog *)user_data; + GtkTreePath *path; + GtkTreeIter iter; + json_t *setting; + json_t *value; + json_error_t error; + GString *error_string; + + if (dialog->setting_update_canceled) { + // Don't update anything + return; + } + + path = gtk_tree_path_new_from_string(path_s); + if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->settings_store), + &iter, path)) { + return; + } + + gtk_tree_model_get(GTK_TREE_MODEL(dialog->settings_store), &iter, + SETTING_LIST_STORE_COL_SETTING, &setting, -1); + + new_text_in_json = nv_app_profile_cfg_file_syntax_to_json(new_text); + value = json_loads(new_text_in_json, JSON_DECODE_ANY, &error); + + if (!value) { + error_string = g_string_new(""); + g_string_printf(error_string, "The value [%s] was not understood by the JSON parser.", + new_text); + gtk_statusbar_push(GTK_STATUSBAR(dialog->error_statusbar), + dialog->setting_error_context_id, + error_string->str); + g_string_free(error_string, TRUE); + value = json_false(); + } else if (!is_valid_setting_value(value, &invalid_type_str)) { + error_string = g_string_new(""); + g_string_printf(error_string, "A value of type \"%s\" is not allowed in the configuration.", + invalid_type_str); + gtk_statusbar_push(GTK_STATUSBAR(dialog->error_statusbar), + dialog->setting_error_context_id, + error_string->str); + g_string_free(error_string, TRUE); + value = json_false(); + } + + json_object_set_new(setting, "value", value); + free(new_text_in_json); + gtk_tree_path_free(path); +} + +static TreeViewColumnTemplate *get_profile_settings_tree_view_columns(EditProfileDialog *dialog, + size_t *num_columns) +{ + TreeViewColumnTemplate *settings_tree_view_columns_copy; + const TreeViewColumnTemplate settings_tree_view_columns[] = { + { + .title = "Key", + .renderer_func = setting_key_renderer_func, + .func_data = dialog, + .min_width = 200, + .editable = TRUE, + .edit_callback = G_CALLBACK(setting_key_edited), + .help_text = "Each entry in the \"Key\" column describes a key for a setting. " + "Any string is a valid key in the configuration, but only some strings " + "will be understood by the driver at runtime. See the \"Supported Setting Keys\" " + "section in the Application Profiles help page for a list of valid " + "application profile setting keys. To edit a setting key, double-click " + "on the cell containing the key." + }, + { + .title = "Type", + .renderer_func = setting_type_renderer_func, + .min_width = 100, + .func_data = NULL, + .help_text = "Each entry in the \"Type\" column describes the underlying JSON type for " + "a setting value. Supported JSON types are: string, true, false, and number. " + "This column is read-only." + }, + { + .title = "Value", + .renderer_func = setting_value_renderer_func, + .func_data = dialog, + .editable = TRUE, + .edit_callback = G_CALLBACK(setting_value_edited), + .help_text = "Each entry in the \"Value\" column describes the value of a setting. To " + "edit a setting value, double-click on the cell containing the value. " + "Valid input is: an arbitrary string in double-quotes, true, false, or " + "an integer or floating-point number. Numbers can optionally be written in " + "hexadecimal or octal." + } + }; + + settings_tree_view_columns_copy = malloc(sizeof(settings_tree_view_columns)); + memcpy(settings_tree_view_columns_copy, settings_tree_view_columns, sizeof(settings_tree_view_columns)); + *num_columns = ARRAY_LEN(settings_tree_view_columns); + + return settings_tree_view_columns_copy; +} + +static gboolean profile_settings_tree_view_key_press_event(GtkWidget *widget, + GdkEvent *event, + gpointer user_data) +{ + gboolean propagate = FALSE; // Whether to call other handlers in the stack + EditProfileDialog *dialog = (EditProfileDialog *)user_data; + GdkEventKey *key_event; + + if (event->type == GDK_KEY_PRESS) { + key_event = (GdkEventKey *)event; + if (key_event->keyval == GDK_Delete) { + edit_profile_dialog_delete_setting_common(dialog); + propagate = TRUE; + } + + gtk_statusbar_pop(GTK_STATUSBAR(dialog->error_statusbar), dialog->setting_error_context_id); + } + + return propagate; +} + +static gboolean edit_profile_dialog_handle_delete(GtkWidget *widget, + GdkEvent *event, + gpointer user_data) +{ + EditProfileDialog *dialog = (EditProfileDialog *)user_data; + gtk_widget_set_sensitive(dialog->caller, TRUE); + gtk_widget_hide(widget); + + return TRUE; +} + +static gboolean edit_profile_dialog_generate_name_button_clicked(GtkWidget *widget, + gpointer user_data) +{ + EditProfileDialog *dialog = (EditProfileDialog *)user_data; + CtkAppProfile *ctk_app_profile = CTK_APP_PROFILE(dialog->parent); + char *unused_profile_name; + + unused_profile_name = nv_app_profile_config_get_unused_profile_name(ctk_app_profile->cur_config); + g_string_assign(dialog->name, unused_profile_name); + gtk_entry_set_text(GTK_ENTRY(dialog->name_entry), dialog->name->str); + + return FALSE; +} + +static const char __profile_name_help[] = "This entry box contains the current profile name, which is a unique identifier for " + "this profile. Renaming the profile to an existing profile will cause the existing " + "profile to be overwritten with this profile's contents."; +static const char __generate_name_button_help[] = "This button generates a unique name that is not currently used " + "by the configuration. This can be used to quickly add a new profile without " + "needing to worry about collisions with existing profile names."; + +static EditProfileDialog *edit_profile_dialog_new(CtkAppProfile *ctk_app_profile) +{ + EditProfileDialog *dialog; + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *main_vbox; + GtkWidget *container; + GtkWidget *entry; + GtkWidget *toolbar; + GtkWidget *tree_view; + GtkWidget *scroll_win; + GtkWidget *alignment; + GtkWidget *statusbar; + GtkWidget *button; + GList *toolbar_widget_items; + + ToolbarItemTemplate *edit_profile_settings_toolbar_items, *edit_profile_dialog_toolbar_items; + size_t num_edit_profile_settings_toolbar_items, num_edit_profile_dialog_toolbar_items; + TreeViewColumnTemplate *settings_tree_view_columns; + size_t num_settings_tree_view_columns; + + + dialog = malloc(sizeof(EditProfileDialog)); + if (!dialog) { + return NULL; + } + + settings_tree_view_columns = get_profile_settings_tree_view_columns(dialog, &num_settings_tree_view_columns); + + get_profile_dialog_toolbar_items(dialog, + &edit_profile_settings_toolbar_items, + &num_edit_profile_settings_toolbar_items, + &edit_profile_dialog_toolbar_items, + &num_edit_profile_dialog_toolbar_items); + + dialog->parent = GTK_WIDGET(ctk_app_profile); + dialog->top_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + + dialog->top_help_data = NULL; + dialog->setting_toolbar_help_data = NULL; + dialog->bottom_help_data = NULL; + + gtk_window_set_modal(GTK_WINDOW(dialog->top_window), TRUE); + + g_signal_connect(G_OBJECT(dialog->top_window), "delete-event", + G_CALLBACK(edit_profile_dialog_handle_delete), dialog); + + gtk_widget_set_size_request(dialog->top_window, 500, 480); + gtk_container_set_border_width(GTK_CONTAINER(dialog->top_window), 8); + + dialog->name = g_string_new(""); + dialog->orig_name = g_string_new(""); + dialog->source_file = g_string_new(""); + dialog->settings = json_array(); + + dialog->settings_store = gtk_list_store_new(SETTING_LIST_STORE_NUM_COLS, G_TYPE_POINTER); + dialog->file_sel = gtk_file_selection_new("Please select a source file for the profile"); + + main_vbox = gtk_vbox_new(FALSE, 0); + gtk_box_set_spacing(GTK_BOX(main_vbox), 8); + + gtk_container_add(GTK_CONTAINER(dialog->top_window), main_vbox); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_set_spacing(GTK_BOX(hbox), 4); + + label = gtk_label_new("Profile Name"); + dialog->name_entry = entry = gtk_entry_new(); + + ctk_config_set_tooltip_and_add_help_data(ctk_app_profile->ctk_config, + label, + &dialog->top_help_data, + "Profile Name", + __profile_name_help, + NULL); + + dialog->generate_name_button = button = gtk_button_new_with_label("Generate Name"); + + ctk_config_set_tooltip_and_add_help_data(ctk_app_profile->ctk_config, + button, + &dialog->top_help_data, + "Generate Name", + __generate_name_button_help, + NULL); + + dialog->top_help_data = g_list_reverse(dialog->top_help_data); + + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(edit_profile_dialog_generate_name_button_clicked), + (gpointer)dialog); + + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); + + gtk_box_pack_start(GTK_BOX(main_vbox), hbox, FALSE, FALSE, 0); + + config_create_source_file_entry(ctk_app_profile->ctk_config, + &container, + &dialog->source_file_combo, + &dialog->top_help_data, + "profile", + G_CALLBACK(profile_browse_button_clicked), + (gpointer)dialog); + + gtk_box_pack_start(GTK_BOX(main_vbox), container, FALSE, FALSE, 0); + + toolbar = gtk_toolbar_new(); + populate_toolbar(GTK_TOOLBAR(toolbar), + edit_profile_settings_toolbar_items, + num_edit_profile_settings_toolbar_items, + &dialog->setting_toolbar_help_data, + NULL); + + gtk_box_pack_start(GTK_BOX(main_vbox), toolbar, FALSE, FALSE, 0); + + tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(dialog->settings_store)); + populate_tree_view(GTK_TREE_VIEW(tree_view), + settings_tree_view_columns, + ctk_app_profile, + num_settings_tree_view_columns, + &dialog->setting_column_help_data); + + g_signal_connect(G_OBJECT(tree_view), "key-press-event", + G_CALLBACK(profile_settings_tree_view_key_press_event), + (gpointer)dialog); + + dialog->settings_view = GTK_TREE_VIEW(tree_view); + + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tree_view), TRUE); + + scroll_win = gtk_scrolled_window_new(NULL, NULL); + gtk_container_add(GTK_CONTAINER(scroll_win), tree_view); + gtk_box_pack_start(GTK_BOX(main_vbox), scroll_win, TRUE, TRUE, 0); + + dialog->setting_update_canceled = FALSE; + + dialog->error_statusbar = statusbar = gtk_statusbar_new(); + gtk_box_pack_start(GTK_BOX(main_vbox), statusbar, FALSE, FALSE, 0); + + dialog->setting_error_context_id = + gtk_statusbar_get_context_id(GTK_STATUSBAR(statusbar), + "Profile Settings"); + + alignment = gtk_alignment_new(1.0, 0.5, 0.0, 0.0); + toolbar = gtk_toolbar_new(); + populate_toolbar(GTK_TOOLBAR(toolbar), + edit_profile_dialog_toolbar_items, + num_edit_profile_dialog_toolbar_items, + &dialog->bottom_help_data, + &toolbar_widget_items); + + // Save off the "Update Profile" button for later use + dialog->add_edit_profile_button = find_widget_in_widget_data_list(toolbar_widget_items, UPDATE_PROFILE_LABEL); + + widget_data_list_free_full(toolbar_widget_items); + + gtk_container_add(GTK_CONTAINER(alignment), toolbar); + gtk_box_pack_start(GTK_BOX(main_vbox), alignment, FALSE, FALSE, 0); + + free(edit_profile_settings_toolbar_items); + free(edit_profile_dialog_toolbar_items); + free(settings_tree_view_columns); + + return dialog; +} + +static void edit_profile_dialog_destroy(EditProfileDialog *dialog) +{ + g_string_free(dialog->name, TRUE); + g_string_free(dialog->orig_name, TRUE); + g_string_free(dialog->source_file, TRUE); + json_decref(dialog->settings); + + ctk_help_data_list_free_full(dialog->top_help_data); + ctk_help_data_list_free_full(dialog->setting_column_help_data); + ctk_help_data_list_free_full(dialog->setting_toolbar_help_data); + ctk_help_data_list_free_full(dialog->bottom_help_data); + free(dialog); +} + +static void rules_tree_view_row_activated_callback(GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + gpointer user_data) +{ + CtkAppProfile *ctk_app_profile = (CtkAppProfile *)user_data; + edit_rule_callbacks_common(ctk_app_profile, path); +} + +static GtkWidget* create_rules_page(CtkAppProfile *ctk_app_profile) +{ + GtkWidget *vbox; + GtkWidget *scroll_win; + GtkWidget *tree_view; + GtkWidget *toolbar; + GtkTreeModel *model; + + const ToolbarItemTemplate rules_toolbar_items[] = { + { + .text = "Add Rule", + .help_text = "The Add Rule button allows you to create a new rule for applying custom settings " + "to applications which match a given pattern.", + .extended_help_text = "See the \"Add/Edit Rule Dialog Box\" help section for more " + "information on adding new rules.", + .icon_id = GTK_STOCK_ADD, + .callback = (GCallback)add_rule_callback, + .user_data = ctk_app_profile + }, + { + .text = "Delete Rule", + .help_text = "The Delete Rule button allows you to remove a highlighted rule from the list.", + .icon_id = GTK_STOCK_REMOVE, + .callback = (GCallback)delete_rule_callback, + .user_data = ctk_app_profile + }, + { + .text = "Increase Rule Priority", + .help_text = "This increases the priority of the highlighted rule in the list. If multiple rules " + "with a conflicting driver setting match the same application, the application will " + "take on the setting value of the highest-priority rule (lowest number) in the list.", + .extended_help_text = "Note that the priority of a rule is partially determined by the source file " + "where the rule is defined, since the NVIDIA driver prioritizes rules based " + "on their position along the configuration file search path. Hence, nvidia-settings " + "may move the rule to a different source file if it is necessary for the rule to achieve " + "a particular priority.", + .icon_id = GTK_STOCK_GO_UP, + .callback = (GCallback)increase_rule_priority_callback, + .user_data = ctk_app_profile + }, + { + .text = "Decrease Rule Priority", + .help_text = "This decreases the priority of the highlighted rule in the list. If multiple rules " + "with a conflicting driver setting match the same application, the application will " + "take on the setting value of the highest-priority rule (lowest number) in the list.", + .icon_id = GTK_STOCK_GO_DOWN, + .callback = (GCallback)decrease_rule_priority_callback, + .user_data = ctk_app_profile + }, + { + .text = "Edit Rule", + .help_text = "The Edit Rule button allows you to edit a highlighted rule in the list.", + .extended_help_text = "See the \"Add/Edit Rule Dialog Box\" help section for more " + "information on editing rules.", + // Would be nice to use GTK_STOCK_EDIT here, but unfortunately only + // available from 2.6 onwards... + .icon_id = GTK_STOCK_PREFERENCES, + .callback = (GCallback)edit_rule_callback, + .user_data = ctk_app_profile + }, + }; + + const TreeViewColumnTemplate rules_tree_view_columns[] = { + // TODO asterisk column to denote changes + { + .title = "Priority", + .renderer_func = rule_order_renderer_func, + .func_data = NULL, + .help_text = "This column describes the priority of each rule in the configuration. " + "If two rules match the same process and affect settings which overlap, " + "the overlapping settings will be set to the values specified by the rule " + "with the lower number (higher priority) in this column." + }, + { + .title = "Pattern", + .renderer_func = rule_pattern_renderer_func, + .func_data = NULL, + .help_text = "This column describes the pattern against which the driver will compare " + "the currently running process to determine if it should apply profile settings. ", + .extended_help_text = "See the \"Supported Features\" help section for more information on " + "supported pattern types." + }, + { + .title = "Profile Settings", + .renderer_func = rule_profile_settings_renderer_func, + .func_data = (gpointer)ctk_app_profile, + .help_text = "This column describes the settings that will be applied to processes " + "that match the pattern in each rule. Note that profile settings are properties " + "of the profile itself, and not the associated rule." + }, + { + .title = "Profile Name", + .attribute = "text", + .attr_col = CTK_APC_RULE_MODEL_COL_PROFILE_NAME, + .help_text = "This column describes the name of the profile that will be applied to processes " + "that match the pattern in each rule." + }, + { + .title = "Source File", + .attribute = "text", + .attr_col = CTK_APC_RULE_MODEL_COL_FILENAME, + .help_text = "This column describes the configuration file where the rule is defined. Note that " + "the NVIDIA® Linux Graphics Driver searches for application profiles along a fixed " + "search path, and the location of the configuration file in the search path can " + "affect a rule's priority. See the README for more details." + }, + }; + + vbox = gtk_vbox_new(FALSE, 0); + + /* Create the toolbar */ + toolbar = gtk_toolbar_new(); + populate_toolbar(GTK_TOOLBAR(toolbar), + rules_toolbar_items, + ARRAY_LEN(rules_toolbar_items), + &ctk_app_profile->rules_help_data, + NULL); + + gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0); + + /* Create the main tree view */ + scroll_win = gtk_scrolled_window_new(NULL, NULL); + + model = GTK_TREE_MODEL(ctk_app_profile->apc_rule_model); + + tree_view = gtk_tree_view_new_with_model(model); + + populate_tree_view(GTK_TREE_VIEW(tree_view), + rules_tree_view_columns, + ctk_app_profile, + ARRAY_LEN(rules_tree_view_columns), + &ctk_app_profile->rules_columns_help_data); + + g_signal_connect(G_OBJECT(tree_view), "row-activated", + G_CALLBACK(rules_tree_view_row_activated_callback), + (gpointer)ctk_app_profile); + + g_signal_connect(G_OBJECT(tree_view), "key-press-event", + G_CALLBACK(rules_tree_view_key_press_event), + (gpointer)ctk_app_profile); + + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tree_view), TRUE); + + gtk_tree_view_set_reorderable(GTK_TREE_VIEW(tree_view), TRUE); + + gtk_container_add(GTK_CONTAINER(scroll_win), tree_view); + + ctk_app_profile->main_rule_view = GTK_TREE_VIEW(tree_view); + + gtk_box_pack_start(GTK_BOX(vbox), scroll_win, TRUE, TRUE, 0); + + return vbox; +} + +static void profile_settings_renderer_func(GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + char *settings_string; + json_t *settings; + + gtk_tree_model_get(model, iter, CTK_APC_PROFILE_MODEL_COL_SETTINGS, &settings, -1); + + settings_string = serialize_settings(settings, TRUE); + + g_object_set(cell, "markup", settings_string, NULL); + + free(settings_string); + json_decref(settings); +} + +static void delete_profile_callback_common(CtkAppProfile *ctk_app_profile) +{ + GtkTreeViewColumn *focus_column; + GtkTreeIter iter; + GtkTreePath *path; + char *profile_name; + + // Get currently highlighted row + gtk_tree_view_get_cursor(ctk_app_profile->main_profile_view, + &path, &focus_column); + if (!path) { + return; + } + + if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(ctk_app_profile->apc_profile_model), + &iter, path)) { + return; + } + + gtk_tree_model_get(GTK_TREE_MODEL(ctk_app_profile->apc_profile_model), + &iter, CTK_APC_PROFILE_MODEL_COL_NAME, &profile_name, -1); + + // Delete the row + ctk_apc_profile_model_delete_profile(ctk_app_profile->apc_profile_model, + profile_name); + + // Select next profile in the list, if available + choose_next_row_in_list_view(ctk_app_profile->main_profile_view, + GTK_TREE_MODEL(ctk_app_profile->apc_profile_model), + &path); + gtk_tree_view_set_cursor(ctk_app_profile->main_profile_view, + path, NULL, FALSE); + + ctk_config_statusbar_message(ctk_app_profile->ctk_config, + "Profile \"%s\" deleted. %s", + profile_name, + STATUSBAR_UPDATE_WARNING); + + gtk_tree_path_free(path); + free(profile_name); +} + +static void delete_profile_callback(GtkWidget *widget, gpointer user_data) +{ + CtkAppProfile *ctk_app_profile = (CtkAppProfile *)user_data; + delete_profile_callback_common(ctk_app_profile); +} + +static gboolean profiles_tree_view_key_press_event(GtkWidget *widget, + GdkEvent *event, + gpointer user_data) +{ + CtkAppProfile *ctk_app_profile = (CtkAppProfile *)user_data; + GdkEventKey *key_event; + + if (event->type == GDK_KEY_PRESS) { + key_event = (GdkEventKey *)event; + if (key_event->keyval == GDK_Delete) { + delete_profile_callback_common(ctk_app_profile); + return TRUE; + } + } + + // Use default handlers + return FALSE; +} + +static void edit_profile_dialog_load_values(EditProfileDialog *dialog) +{ + GList *strings; + GtkCombo *combo; + + // window title + gtk_window_set_title(GTK_WINDOW(dialog->top_window), + dialog->new_profile ? "Add new profile" : "Edit existing profile"); + + // add/edit button + button_set_label_and_stock_icon(GTK_BUTTON(dialog->add_edit_profile_button), + "Update Profile", + dialog->new_profile ? GTK_STOCK_ADD : GTK_STOCK_PREFERENCES); + + // profile name + gtk_entry_set_text(GTK_ENTRY(dialog->name_entry), dialog->name->str); + + // source file + combo = GTK_COMBO(dialog->source_file_combo); + strings = get_source_filenames(CTK_APP_PROFILE(dialog->parent)); + + gtk_combo_set_popdown_strings(combo, strings); + + if (dialog->new_profile) { + if (g_list_length(strings)) { + // Choose first string in the list + g_string_assign(dialog->source_file, (gchar *)strings->data); + } else { + g_string_assign(dialog->source_file, ""); + } + } + + gtk_entry_set_text(GTK_ENTRY(combo->entry), dialog->source_file->str); + + string_list_free_full(strings); + + // profile settings + if (!dialog->new_profile) { + load_settings_from_profile(CTK_APP_PROFILE(dialog->parent), + dialog->settings_store, + dialog->name->str); + } else { + gtk_list_store_clear(dialog->settings_store); + } +} + +static void edit_profile_dialog_show(EditProfileDialog *dialog) +{ + edit_profile_dialog_load_values(dialog); + gtk_widget_show_all(dialog->top_window); + + // disable focusing to calling window until this window closed + gtk_window_set_transient_for(GTK_WINDOW(dialog->top_window), + GTK_WINDOW(gtk_widget_get_toplevel(dialog->caller))); + gtk_widget_set_sensitive(dialog->caller, FALSE); +} + +static void add_profile_callbacks_common(CtkAppProfile *ctk_app_profile, + GtkWidget *caller) +{ + EditProfileDialog *dialog = ctk_app_profile->edit_profile_dialog; + char *unused_profile_name = nv_app_profile_config_get_unused_profile_name(ctk_app_profile->cur_config); + + dialog->new_profile = TRUE; + dialog->caller = caller; + + g_string_assign(dialog->name, unused_profile_name); + g_string_truncate(dialog->orig_name, 0); + + free(unused_profile_name); + + edit_profile_dialog_show(dialog); +} + +static void add_profile_callback(GtkWidget *widget, gpointer user_data) +{ + CtkAppProfile *ctk_app_profile = (CtkAppProfile *)user_data; + add_profile_callbacks_common(ctk_app_profile, GTK_WIDGET(ctk_app_profile)); +} + +static void edit_profile_callbacks_common(CtkAppProfile *ctk_app_profile, + GtkTreePath *path, + GtkWidget *caller) +{ + GtkTreeIter iter; + EditProfileDialog *dialog = ctk_app_profile->edit_profile_dialog; + gchar *name, *filename; + json_t *settings; + + if (!path) { + return; + } + + if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(ctk_app_profile->apc_profile_model), + &iter, path)) { + return; + } + + gtk_tree_model_get(GTK_TREE_MODEL(ctk_app_profile->apc_profile_model), + &iter, + CTK_APC_PROFILE_MODEL_COL_NAME, &name, + CTK_APC_PROFILE_MODEL_COL_SETTINGS, &settings, + CTK_APC_PROFILE_MODEL_COL_FILENAME, &filename, + -1); + + dialog->new_profile = FALSE; + dialog->caller = caller; + + g_string_assign(dialog->name, name); + g_string_assign(dialog->orig_name, name); + + dialog->settings = json_deep_copy(settings); + g_string_assign(dialog->source_file, filename); + + edit_profile_dialog_show(dialog); + + json_decref(settings); + free(name); + free(filename); +} + +static void edit_profile_callback(GtkWidget *widget, gpointer user_data) +{ + GtkTreeViewColumn *focus_column; + GtkTreePath *path; + CtkAppProfile *ctk_app_profile = (CtkAppProfile *)user_data; + + // Get currently highlighted row + gtk_tree_view_get_cursor(ctk_app_profile->main_profile_view, + &path, &focus_column); + + edit_profile_callbacks_common(ctk_app_profile, path, GTK_WIDGET(ctk_app_profile)); + + gtk_tree_path_free(path); +} + +static void profiles_tree_view_row_activated_callback(GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + gpointer user_data) +{ + CtkAppProfile *ctk_app_profile = (CtkAppProfile *)user_data; + edit_profile_callbacks_common(ctk_app_profile, path, GTK_WIDGET(ctk_app_profile)); +} + + +static GtkWidget* create_profiles_page(CtkAppProfile *ctk_app_profile) +{ + GtkWidget *vbox; + GtkWidget *toolbar; + GtkWidget *scroll_win; + GtkWidget *tree_view; + GtkTreeModel *model; + + const ToolbarItemTemplate profiles_toolbar_items[] = { + { + .text = "Add Profile", + .help_text = "The Add Profile button allows you to create a new profile for applying custom settings " + "to applications which match a given pattern.", + .extended_help_text = "See the \"Add/Edit Profile Dialog Box\" help section for more " + "information on adding new profiles.", + .icon_id = GTK_STOCK_ADD, + .callback = (GCallback)add_profile_callback, + .user_data = ctk_app_profile + }, + { + .text = "Delete Profile", + .help_text = "The Delete Profile button allows you to remove a highlighted profile from the list.", + .icon_id = GTK_STOCK_REMOVE, + .callback = (GCallback)delete_profile_callback, + .user_data = ctk_app_profile + }, + { + .text = "Edit Profile", + .help_text = "The Edit Profile button allows you to edit a highlighted profile in the list.", + .extended_help_text = "See the \"Add/Edit Profile Dialog Box\" help section for more " + "information on editing profiles.", + // Would be nice to use GTK_STOCK_EDIT here, but unfortunately only + // available from 2.6 onwards... + .icon_id = GTK_STOCK_PREFERENCES, + .callback = (GCallback)edit_profile_callback, + .user_data = ctk_app_profile + }, + }; + + const TreeViewColumnTemplate profiles_tree_view_columns[] = { + // TODO asterisk column to denote changes + { + .title = "Profile Name", + .attribute = "text", + .attr_col = CTK_APC_PROFILE_MODEL_COL_NAME, + .sortable = TRUE, + .sort_column_id = CTK_APC_PROFILE_MODEL_COL_NAME, + .help_text = "This column describes the name of the profile." + }, + { + .title = "Profile Settings", + .renderer_func = profile_settings_renderer_func, + .func_data = NULL, + .sortable = TRUE, + .sort_column_id = CTK_APC_PROFILE_MODEL_COL_SETTINGS, + .help_text = "This column describes the settings that will be applied by rules " + "which use this profile." + }, + { + .title = "Source File", + .attribute = "text", + .attr_col = CTK_APC_PROFILE_MODEL_COL_FILENAME, + .sortable = TRUE, + .sort_column_id = CTK_APC_PROFILE_MODEL_COL_FILENAME, + .help_text = "This column describes the configuration file where the profile is defined." + }, + }; + + vbox = gtk_vbox_new(FALSE, 0); + + /* Create the toolbar */ + toolbar = gtk_toolbar_new(); + populate_toolbar(GTK_TOOLBAR(toolbar), + profiles_toolbar_items, + ARRAY_LEN(profiles_toolbar_items), + &ctk_app_profile->profiles_help_data, + NULL); + + gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0); + + /* Create the main tree view */ + scroll_win = gtk_scrolled_window_new(NULL, NULL); + + model = GTK_TREE_MODEL(ctk_app_profile->apc_profile_model); + tree_view = gtk_tree_view_new_with_model(model); + + populate_tree_view(GTK_TREE_VIEW(tree_view), + profiles_tree_view_columns, + ctk_app_profile, + ARRAY_LEN(profiles_tree_view_columns), + &ctk_app_profile->profiles_columns_help_data); + + g_signal_connect(G_OBJECT(tree_view), "row-activated", + G_CALLBACK(profiles_tree_view_row_activated_callback), + (gpointer)ctk_app_profile); + + g_signal_connect(G_OBJECT(tree_view), "key-press-event", + G_CALLBACK(profiles_tree_view_key_press_event), + (gpointer)ctk_app_profile); + + ctk_app_profile->main_profile_view = GTK_TREE_VIEW(tree_view); + + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tree_view), TRUE); + + gtk_container_add(GTK_CONTAINER(scroll_win), tree_view); + + gtk_box_pack_start(GTK_BOX(vbox), scroll_win, TRUE, TRUE, 0); + + return vbox; +} + +static char *get_default_global_config_file(void) +{ + const char *homeStr = getenv("HOME"); + if (homeStr) { + return nvstrcat(homeStr, "/.nv/nvidia-application-profile-globals-rc", NULL); + } else { + nv_error_msg("The environment variable HOME is not set. Any " + "modifications to global application profile settings " + "will not be saved."); + return NULL; + } +} + +#define SEARCH_PATH_NUM_FILES 4 + +static char **get_default_search_path(size_t *num_files) +{ + size_t i = 0; + char **filenames = malloc(SEARCH_PATH_NUM_FILES * sizeof(char *)); + const char *homeStr = getenv("HOME"); + + if (homeStr) { + filenames[i++] = nvstrcat(homeStr, "/.nv/nvidia-application-profiles-rc", NULL); + filenames[i++] = nvstrcat(homeStr, "/.nv/nvidia-application-profiles-rc.d", NULL); + } + filenames[i++] = strdup("/etc/nvidia/nvidia-application-profiles-rc"); + filenames[i++] = strdup("/etc/nvidia/nvidia-application-profiles-rc.d"); + + *num_files = i; + assert(i <= SEARCH_PATH_NUM_FILES); + + return filenames; +} + +static void free_search_path(char **search_path, size_t search_path_size) +{ + while (search_path_size--) { + free(search_path[search_path_size]); + } + free(search_path); +} + +static void app_profile_load_global_settings(CtkAppProfile *ctk_app_profile, + AppProfileConfig *config) +{ + // Temporarily disable propagating statusbar messages since the + // enabled_check_button_toggled() callback will otherwise update the + // statusbar + ctk_app_profile->ctk_config->status_bar.enabled = FALSE; + gtk_toggle_button_set_active( + GTK_TOGGLE_BUTTON(ctk_app_profile->enable_check_button), + nv_app_profile_config_get_enabled(config)); + ctk_app_profile->ctk_config->status_bar.enabled = TRUE; +} + +static void app_profile_reload(CtkAppProfile *ctk_app_profile) +{ + char *global_config_file; + char **search_path; + size_t search_path_size; + + nv_app_profile_config_free(ctk_app_profile->cur_config); + nv_app_profile_config_free(ctk_app_profile->gold_config); + + search_path = get_default_search_path(&search_path_size); + global_config_file = get_default_global_config_file(); + ctk_app_profile->gold_config = nv_app_profile_config_load(global_config_file, + search_path, + search_path_size); + ctk_app_profile->cur_config = nv_app_profile_config_dup(ctk_app_profile->gold_config); + free_search_path(search_path, search_path_size); + free(global_config_file); + + ctk_apc_profile_model_attach(ctk_app_profile->apc_profile_model, ctk_app_profile->cur_config); + ctk_apc_rule_model_attach(ctk_app_profile->apc_rule_model, ctk_app_profile->cur_config); + app_profile_load_global_settings(ctk_app_profile, ctk_app_profile->cur_config); +} + + +static void reload_callback(GtkWidget *widget, gpointer user_data) +{ + CtkAppProfile *ctk_app_profile = (CtkAppProfile *)user_data; + json_t *updates; + gboolean do_reload = TRUE; + GString *fatal_errors = g_string_new(""); + GString *nonfatal_errors = g_string_new(""); + + static const char unsaved_changes_error[] = + "There are unsaved changes in the configuration which will be permanently lost if " + "the configuration is reloaded from disk.\n"; + static const char files_altered_error[] = + "Some configuration files may have been modified externally since the configuration " + "was last loaded from disk.\n"; + + updates = nv_app_profile_config_validate(ctk_app_profile->cur_config, + ctk_app_profile->gold_config); + + if (json_array_size(updates) > 0) { + g_string_append_printf(nonfatal_errors, "%s\t%s", get_bullet(), unsaved_changes_error); + } + if (nv_app_profile_config_check_backing_files(ctk_app_profile->cur_config)) { + g_string_append_printf(nonfatal_errors, "%s\t%s", get_bullet(), files_altered_error); + } + + do_reload = run_error_dialog(GTK_WINDOW(gtk_widget_get_toplevel( + GTK_WIDGET(ctk_app_profile))), + fatal_errors, + nonfatal_errors, + "reload the configuration from disk"); + + + if (do_reload) { + app_profile_reload(ctk_app_profile); + ctk_config_statusbar_message(ctk_app_profile->ctk_config, + "Application profile configuration reloaded from disk."); + } + + g_string_free(fatal_errors, TRUE); + g_string_free(nonfatal_errors, TRUE); +} + +static void save_changes_callback(GtkWidget *widget, gpointer user_data); + +static ToolbarItemTemplate *get_save_reload_toolbar_items(CtkAppProfile *ctk_app_profile, size_t *num_save_reload_toolbar_items) +{ + ToolbarItemTemplate *save_reload_toolbar_items_copy; + const ToolbarItemTemplate save_reload_toolbar_items[] = { + { + .text = "Save Changes", + .help_text = "The Save Changes button allows you to save any changes to application profile " + "configuration files to disk.", + .extended_help_text = "This button displays a dialog box which allows you to preview the changes " + "that will be made to the JSON configuration files, and toggle whether nvidia-settings " + "should make backup copies of the original files before overwriting existing files.", + .icon_id = GTK_STOCK_SAVE, + .callback = (GCallback)save_changes_callback, + .user_data = ctk_app_profile + }, + { + .text = "Reload", + .help_text = "The Reload button allows you to reload application profile configuration from " + "disk, reverting any unsaved changes.", + .extended_help_text = "If nvidia-settings detects unsaved changes in the configuration, this button will " + "display a dialog box to warn you before attempting to reload.", + .icon_id = GTK_STOCK_REFRESH, + .callback = (GCallback)reload_callback, + .user_data = ctk_app_profile + } + }; + + save_reload_toolbar_items_copy = malloc(sizeof(save_reload_toolbar_items)); + memcpy(save_reload_toolbar_items_copy, save_reload_toolbar_items, sizeof(save_reload_toolbar_items)); + + *num_save_reload_toolbar_items = ARRAY_LEN(save_reload_toolbar_items); + return save_reload_toolbar_items_copy; +} + +static void save_app_profile_changes_dialog_save_changes(GtkWidget *widget, gpointer user_data) +{ + gboolean do_save = TRUE; + gboolean do_reload = TRUE; + gboolean do_backup = TRUE; + gint result; + int ret; + GtkWidget *error_dialog; + SaveAppProfileChangesDialog *dialog = (SaveAppProfileChangesDialog *)user_data; + CtkAppProfile *ctk_app_profile = CTK_APP_PROFILE(dialog->parent); + static const char config_files_changed_string[] = + "nvidia-settings has detected that configuration files have changed " + "since the configuration was last loaded. Saving the configuration " + "may cause these changes to be permanently lost. Continue anyway?\n"; + static const char write_errors_occurred_string[] = + "nvidia-settings encountered errors when writing to the configuration. " + "Some changes may not have been saved. Reload the configuration anyway?\n"; + + // First check for possible conflicts + if (nv_app_profile_config_check_backing_files(ctk_app_profile->cur_config)) { + error_dialog = gtk_message_dialog_new(GTK_WINDOW(dialog->top_window), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_YES_NO, + "%s", config_files_changed_string); + result = gtk_dialog_run(GTK_DIALOG(error_dialog)); + if (result != GTK_RESPONSE_YES) { + do_save = FALSE; + } + gtk_widget_destroy(error_dialog); + } + + do_backup = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->backup_check_button)); + + if (do_save) { + ret = nv_app_profile_config_save_updates(ctk_app_profile->cur_config, dialog->updates, + do_backup); + if (ret < 0) { + error_dialog = gtk_message_dialog_new(GTK_WINDOW(dialog->top_window), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_YES_NO, + "%s", write_errors_occurred_string); + result = gtk_dialog_run(GTK_DIALOG(error_dialog)); + if (result != GTK_RESPONSE_YES) { + do_reload = FALSE; + } + gtk_widget_destroy(error_dialog); + } + + if (do_reload) { + app_profile_reload(CTK_APP_PROFILE(dialog->parent)); + } + + ctk_config_statusbar_message(ctk_app_profile->ctk_config, + "Application profile configuration saved to disk."); + } + + json_decref(dialog->updates); + dialog->updates = NULL; + + gtk_widget_set_sensitive(dialog->parent, TRUE); + gtk_widget_hide(dialog->top_window); +} + +static void save_app_profile_changes_dialog_cancel(GtkWidget *widget, gpointer user_data) +{ + SaveAppProfileChangesDialog *dialog = (SaveAppProfileChangesDialog *)user_data; + + json_decref(dialog->updates); + dialog->updates = NULL; + + gtk_widget_set_sensitive(dialog->parent, TRUE); + gtk_widget_hide(dialog->top_window); +} + +static ToolbarItemTemplate *get_save_app_profile_changes_toolbar_items(SaveAppProfileChangesDialog *dialog, + size_t *num_items) +{ + ToolbarItemTemplate *items_copy; + const ToolbarItemTemplate items[] = { + { + .text = "Save Changes", + .help_text = "Save the changes to disk.", + .icon_id = GTK_STOCK_SAVE, + .callback = G_CALLBACK(save_app_profile_changes_dialog_save_changes), + .user_data = dialog, + }, + { + .text = "Cancel", + .help_text = "Cancel the save operation.", + .icon_id = GTK_STOCK_CANCEL, + .callback = G_CALLBACK(save_app_profile_changes_dialog_cancel), + .user_data = dialog, + } + }; + + items_copy = malloc(sizeof(items)); + memcpy(items_copy, items, sizeof(items)); + + *num_items = ARRAY_LEN(items); + return items_copy; +} + +static void save_app_profile_changes_dialog_set_preview_visibility(SaveAppProfileChangesDialog *dialog, + gboolean visible) +{ + dialog->show_preview = visible; + if (visible) { + gtk_widget_show(dialog->preview_vbox); + gtk_window_set_resizable(GTK_WINDOW(dialog->top_window), TRUE); + gtk_widget_set_size_request(dialog->preview_vbox, -1, 400); + gtk_button_set_label(GTK_BUTTON(dialog->preview_button), "Hide Preview"); + } else { + gtk_widget_hide(dialog->preview_vbox); + gtk_window_set_resizable(GTK_WINDOW(dialog->top_window), FALSE); + gtk_button_set_label(GTK_BUTTON(dialog->preview_button), "Show Preview"); + } +} + +static gboolean save_app_profile_changes_show_preview_button_clicked(GtkWidget *widget, gpointer user_data) +{ + SaveAppProfileChangesDialog *save_dialog = (SaveAppProfileChangesDialog *)user_data; + + // Toggle visibility of the preview window + save_app_profile_changes_dialog_set_preview_visibility(save_dialog, !save_dialog->show_preview); + return FALSE; +} + +static void save_app_profile_settings_dialog_load_current_update(SaveAppProfileChangesDialog *dialog) +{ + CtkAppProfile *ctk_app_profile; + size_t i, size; + CtkDropDownMenu *menu; + const char *filename; + const char *text; + char *backup_filename; + GtkTextBuffer *text_buffer; + json_t *update, *update_filename; + + ctk_app_profile = CTK_APP_PROFILE(dialog->parent); + + menu = CTK_DROP_DOWN_MENU(dialog->preview_file_menu); + filename = ctk_drop_down_menu_get_current_name(menu); + + text = NULL; + for (i = 0, size = json_array_size(dialog->updates); i < size; i++) { + update = json_array_get(dialog->updates, i); + update_filename = json_object_get(update, "filename"); + if (!strcmp(json_string_value(update_filename), filename)) { + text = json_string_value(json_object_get(update, "text")); + } + } + + backup_filename = nv_app_profile_config_get_backup_filename(ctk_app_profile->cur_config, filename); + gtk_entry_set_text(GTK_ENTRY(dialog->preview_backup_entry), backup_filename); + free(backup_filename); + + text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(dialog->preview_text_view)); + + if (text) { + gtk_text_buffer_set_text(text_buffer, text, -1); + } else { + gtk_text_buffer_set_text(text_buffer, "", -1); + } +} + +static void save_app_profile_changes_dialog_preview_changed(GtkWidget *widget, gpointer user_data) +{ + SaveAppProfileChangesDialog *dialog = (SaveAppProfileChangesDialog *)user_data; + save_app_profile_settings_dialog_load_current_update(dialog); +} + +static gboolean save_app_profile_changes_dialog_handle_delete(GtkWidget *widget, + GdkEvent *event, + gpointer user_data) +{ + SaveAppProfileChangesDialog *dialog = (SaveAppProfileChangesDialog *)user_data; + gtk_widget_set_sensitive(dialog->parent, TRUE); + gtk_widget_hide(widget); + + return TRUE; +} + +static SaveAppProfileChangesDialog *save_app_profile_changes_dialog_new(CtkAppProfile *ctk_app_profile) +{ + ToolbarItemTemplate *toolbar_items; + size_t num_toolbar_items; + GtkWidget *toolbar; + GtkWidget *alignment; + GtkWidget *vbox, *preview_vbox, *hbox; + GtkWidget *label; + GtkWidget *menu; + GtkWidget *check_button; + GtkWidget *scroll_win, *text_view; + SaveAppProfileChangesDialog *dialog = malloc(sizeof(SaveAppProfileChangesDialog)); + + dialog->parent = GTK_WIDGET(ctk_app_profile); + dialog->top_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + + dialog->show_preview = FALSE; + + gtk_window_set_title(GTK_WINDOW(dialog->top_window), "Save Changes"); + + gtk_window_set_modal(GTK_WINDOW(dialog->top_window), TRUE); + gtk_container_set_border_width(GTK_CONTAINER(dialog->top_window), 8); + + g_signal_connect(G_OBJECT(dialog->top_window), "delete-event", + G_CALLBACK(save_app_profile_changes_dialog_handle_delete), dialog); + + + gtk_widget_set_size_request(dialog->top_window, 500, -1); + + vbox = gtk_vbox_new(FALSE, 0); + gtk_box_set_spacing(GTK_BOX(vbox), 8); + + gtk_container_add(GTK_CONTAINER(dialog->top_window), vbox); + + label = gtk_label_new("The following files will be modified after the configuration is saved."); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); + + hbox = gtk_hbox_new(FALSE, 8); + + dialog->preview_file_menu = menu = ctk_drop_down_menu_new(CTK_DROP_DOWN_MENU_FLAG_COMBO); + gtk_box_pack_start(GTK_BOX(hbox), menu, TRUE, TRUE, 0); + + dialog->preview_changed_signal = + g_signal_connect(G_OBJECT(menu), "changed", + G_CALLBACK(save_app_profile_changes_dialog_preview_changed), + (gpointer)dialog); + + dialog->preview_button = gtk_button_new_with_label("Show Preview"); + gtk_box_pack_start(GTK_BOX(hbox), dialog->preview_button, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(dialog->preview_button), "clicked", + G_CALLBACK(save_app_profile_changes_show_preview_button_clicked), + (gpointer)dialog); + ctk_config_set_tooltip(ctk_app_profile->ctk_config, + dialog->preview_button, + "This button allows you to toggle previewing the new contents of " + "the currently selected configuration file."); + + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + + dialog->preview_vbox = preview_vbox = gtk_vbox_new(FALSE, 8); + + hbox = gtk_hbox_new(FALSE, 8); + + label = gtk_label_new("Backup filename"); + ctk_config_set_tooltip(ctk_app_profile->ctk_config, + label, + "This text field contains the filename that nvidia-settings will use " + "to back up the currently selected configuration file when saving the " + "configuration."); + + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + dialog->preview_backup_entry = gtk_entry_new(); + gtk_box_pack_start(GTK_BOX(hbox), dialog->preview_backup_entry, TRUE, TRUE, 0); + gtk_entry_set_editable(GTK_ENTRY(dialog->preview_backup_entry), FALSE); + + gtk_box_pack_start(GTK_BOX(preview_vbox), hbox, FALSE, FALSE, 0); + + scroll_win = gtk_scrolled_window_new(NULL, NULL); + dialog->preview_text_view = text_view = gtk_text_view_new(); + gtk_text_view_set_editable(GTK_TEXT_VIEW(text_view), FALSE); + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text_view), GTK_WRAP_CHAR); + gtk_container_add(GTK_CONTAINER(scroll_win), text_view); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll_win), GTK_SHADOW_IN); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_win), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + gtk_box_pack_start(GTK_BOX(preview_vbox), scroll_win, TRUE, TRUE, 0); + + gtk_box_pack_start(GTK_BOX(vbox), preview_vbox, TRUE, TRUE, 0); + + dialog->backup_check_button = check_button = + gtk_check_button_new_with_label("Back up original files"); + // Enable backups by default + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_button), TRUE); + + ctk_config_set_tooltip(ctk_app_profile->ctk_config, + check_button, + "This checkbox determines whether nvidia-settings will attempt to back up " + "the original configuration files before saving the new configuration."); + + gtk_box_pack_start(GTK_BOX(vbox), check_button, FALSE, FALSE, 0); + + alignment = gtk_alignment_new(1.0, 0.5, 0.0, 0.0); + toolbar = gtk_toolbar_new(); + toolbar_items = get_save_app_profile_changes_toolbar_items(dialog, &num_toolbar_items); + populate_toolbar(GTK_TOOLBAR(toolbar), + toolbar_items, + num_toolbar_items, + NULL, + NULL); + free(toolbar_items); + + gtk_container_add(GTK_CONTAINER(alignment), toolbar); + gtk_box_pack_start(GTK_BOX(vbox), alignment, FALSE, FALSE, 0); + + return dialog; +} + +static void save_app_profile_changes_dialog_destroy(SaveAppProfileChangesDialog *dialog) +{ + json_decref(dialog->updates); + ctk_help_data_list_free_full(dialog->help_data); + free(dialog); +} + +static GList *get_update_filenames(json_t *updates) +{ + GList *update_filenames = NULL; + json_t *update, *update_filename_json; + gchar *update_filename; + size_t i, size; + for (i = 0, size = json_array_size(updates); i < size; i++) { + update = json_array_get(updates, i); + update_filename_json = json_object_get(update, "filename"); + update_filename = strdup(json_string_value(update_filename_json)); + update_filenames = g_list_prepend(update_filenames, update_filename); + } + + return update_filenames; +} + +static void add_preview_file(gpointer data, gpointer user_data) +{ + const char *filename = (const char*)data; + CtkDropDownMenu *menu = (CtkDropDownMenu *)user_data; + + ctk_drop_down_menu_append_item(menu, filename, 0); +} + +static void save_app_profile_changes_dialog_load_values(SaveAppProfileChangesDialog *dialog) +{ + GList *update_filenames; + + update_filenames = get_update_filenames(dialog->updates); + ctk_drop_down_menu_reset(CTK_DROP_DOWN_MENU(dialog->preview_file_menu)); + g_list_foreach(update_filenames, add_preview_file, (gpointer)dialog->preview_file_menu); + + save_app_profile_settings_dialog_load_current_update(dialog); + + string_list_free_full(update_filenames); +} + +static void save_app_profile_changes_dialog_show(SaveAppProfileChangesDialog *dialog) +{ + // Temporarily disable the "changed" signal to prevent races between the + // update below and callbacks which fire when the window opens + g_signal_handler_block(G_OBJECT(dialog->preview_file_menu), + dialog->preview_changed_signal); + + save_app_profile_changes_dialog_load_values(dialog); + gtk_widget_show_all(dialog->top_window); + // Hide preview window by default + save_app_profile_changes_dialog_set_preview_visibility(dialog, dialog->show_preview); + + g_signal_handler_unblock(G_OBJECT(dialog->preview_file_menu), + dialog->preview_changed_signal); + + gtk_window_set_transient_for(GTK_WINDOW(dialog->top_window), + GTK_WINDOW(gtk_widget_get_toplevel(dialog->parent))); + gtk_widget_set_sensitive(dialog->parent, FALSE); +} + +static void save_changes_callback(GtkWidget *widget, gpointer user_data) +{ + CtkAppProfile *ctk_app_profile = (CtkAppProfile *)user_data; + SaveAppProfileChangesDialog *dialog = ctk_app_profile->save_app_profile_changes_dialog; + json_t *updates; + + nv_app_profile_config_check_backing_files(ctk_app_profile->cur_config); + + updates = nv_app_profile_config_validate(ctk_app_profile->cur_config, + ctk_app_profile->gold_config); + + if (json_array_size(updates)) { + dialog->updates = updates; + save_app_profile_changes_dialog_show(dialog); + } +} + +static const char __enabling_application_profiles_help[] = + "Application profile support can be toggled by clicking on the \"Enable application profiles\" " + "checkbox. Note that changes to this setting will not be saved to disk until the \"Save Changes\" " + "button is clicked."; +static const char __rules_page_help[] = + "The Rules page allows you to specify rules for assigning profiles to applications."; +static const char __rules_page_extended_help[] = + "Rules are presented in a list sorted by priority; higher-priority items appear farther " + "up in the list and have a smaller priority number. Dragging and dropping a rule in this list " + "reorders it (potentially modifying its source file; see below), and double-clicking on a " + "given rule will open a dialog box which lets the user edit the rule (see the \"Add/Edit Rule " + "Dialog Box\" help section for more information). A rule can be deleted by highlighting it in " + "the view and hitting the Delete key.\n\n" + "Note that changes made to rules in this page are not saved to disk until the \"Save Changes\" " + "button is clicked."; +static const char __profiles_page_help[] = + "The Profiles page allows you to create and modify profiles in the configuration."; +static const char __profiles_page_extended_help[] = + "Profiles are presented in a list which can be sorted by profile name, profile settings, and " + "originating source file. Double-clicking on a profile will open a dialog box which lets the user " + "edit the rule (see the \"Add/Edit Profile Dialog Box\" help section for more information). A " + "profile can be deleted by highlighting it in the view and hitting the Delete key.\n\n" + "Note that changes made to profiles in this page are not saved to disk until the \"Save Changes\" " + "button is clicked."; + + +GtkTextBuffer *ctk_app_profile_create_help(CtkAppProfile *ctk_app_profile, GtkTextTagTable *table) +{ + size_t j; + GtkTextIter i; + GtkTextBuffer *b; + + b = gtk_text_buffer_new(table); + gtk_text_buffer_get_iter_at_offset(b, &i, 0); + ctk_help_title(b, &i, "Application Profiles Help"); + + ctk_help_para(b, &i, "Use this page to configure application profiles for " + "use with the NVIDIA® Linux Graphics Driver. Application profiles " + "are collections of settings that are applied on a per-process basis. " + "When the driver is loaded into the process, it detects various attributes " + "of the running process and determines whether settings should be applied " + "based on these attributes. This mechanism allows users to selectively override " + "driver settings for a particular application without the need to set environment " + "variables on the command line prior to running the application."); + ctk_help_para(b, &i, "Application profile configuration consists of \"rules\" and \"profiles\". A \"profile\" defines " + "what settings to use, and a \"rule\" identifies an application and defines what profile " + "should be used with that application."); + + ctk_help_para(b, &i, "A rule identifies an application by describing various features of the application; for example, " + "the name of the application binary (e.g. \"glxgears\") or a shared library loaded into the application " + "(e.g. \"libpthread.so.0\"). The particular features supported by this NVIDIA® Linux implementation " + "are listed below in the \"Supported Features\" section."); + + ctk_help_para(b, &i, "For more information on application profiles, please consult the README."); + + ctk_help_heading(b, &i, "Global Settings"); + ctk_help_para(b, &i, "These settings apply to all profiles and rules within the configuration. "); + + ctk_help_data_list_print_terms(b, &i, ctk_app_profile->global_settings_help_data); + + ctk_help_heading(b, &i, "Rules Page"); + ctk_help_para(b, &i, __rules_page_help); + ctk_help_para(b, &i, __rules_page_extended_help); + + ctk_help_para(b, &i, "There are several buttons above the list of rules " + "which can be used to modify the configuration:"); + ctk_help_data_list_print_terms(b, &i, ctk_app_profile->rules_help_data); + + ctk_help_heading(b, &i, "Rule Properties"); + ctk_help_para(b, &i, "Each row in the list of rules is divided into several " + "columns which describe different properties of a rule: "); + ctk_help_data_list_print_terms(b, &i, ctk_app_profile->rules_columns_help_data); + + ctk_help_heading(b, &i, "Add/Edit Rule Dialog Box"); + ctk_help_para(b, &i, "When adding a new rule or editing an existing rule, nvidia-settings " + "opens a dialog box for you to modify the rule's attributes. "); + ctk_help_data_list_print_terms(b, &i, ctk_app_profile->edit_rule_dialog->help_data); + + ctk_help_heading(b, &i, "Profiles Page"); + ctk_help_para(b, &i, __profiles_page_help); + ctk_help_para(b, &i, __profiles_page_extended_help); + ctk_help_para(b, &i, "There are several buttons above the list of profiles " + "which can be used to modify the configuration:"); + ctk_help_data_list_print_terms(b, &i, ctk_app_profile->profiles_help_data); + + ctk_help_heading(b, &i, "Profile Properties"); + ctk_help_para(b, &i, "Each row in the list of profiles is divided into several " + "columns which describe different properties of a profile:"); + ctk_help_data_list_print_terms(b, &i, ctk_app_profile->profiles_columns_help_data); + + ctk_help_heading(b, &i, "Add/Edit Profile Dialog Box"); + ctk_help_para(b, &i, "When adding a new profile or editing an existing profile, nvidia-settings " + "opens a dialog box for you to modify the profile's attributes. " + "See \"Editing Settings in a Profile\" for information on editing settings."); + ctk_help_data_list_print_terms(b, &i, ctk_app_profile->edit_profile_dialog->top_help_data); + ctk_help_data_list_print_terms(b, &i, ctk_app_profile->edit_profile_dialog->bottom_help_data); + + ctk_help_heading(b, &i, "Editing Settings in a Profile"); + ctk_help_para(b, &i, "Settings in a profile are presented in a list view with the following columns: "); + ctk_help_data_list_print_terms(b, &i, ctk_app_profile->edit_profile_dialog->setting_column_help_data); + + ctk_help_para(b, &i, "Settings can be modified using the following toolbar buttons: "); + ctk_help_data_list_print_terms(b, &i, ctk_app_profile->edit_profile_dialog->setting_toolbar_help_data); + + ctk_help_heading(b, &i, "Saving and Reverting Changes"); + + ctk_help_para(b, &i, "Changes made to the application profile configuration will not take effect until " + "they are saved to disk. Buttons to save and restore the configuration " + "are located on the bottom of the Application Profiles page."); + ctk_help_data_list_print_terms(b, &i, ctk_app_profile->save_reload_help_data); + + ctk_help_heading(b, &i, "Supported Features"); + + ctk_help_para(b, &i, "This NVIDIA® Linux Graphics Driver supports detection of the following features:"); + + for (j = 0; j < NUM_RULE_FEATURES; j++) { + ctk_help_term(b, &i, rule_feature_label_strings[j]); + ctk_help_para(b, &i, rule_feature_help_text[j]); + } + + ctk_help_heading(b, &i, "Supported Setting Keys"); + + ctk_help_para(b, &i, "This NVIDIA® Linux Graphics Driver supports the following application profile setting " + "keys. For more information on a given key, please consult the README."); + + for (j = 0; j < NUM_PROFILE_SETTINGS; j++) { + ctk_help_term(b, &i, profile_setting_keys[j]); + ctk_help_para(b, &i, profile_setting_descriptions[j]); + } + + ctk_help_finish(b); + + return b; +} + +static void enabled_check_button_toggled(GtkToggleButton *toggle_button, + gpointer user_data) +{ + CtkAppProfile *ctk_app_profile = (CtkAppProfile *)user_data; + + nv_app_profile_config_set_enabled(ctk_app_profile->cur_config, + gtk_toggle_button_get_active(toggle_button)); + + ctk_config_statusbar_message(ctk_app_profile->ctk_config, + "Application profiles are %s. %s", + gtk_toggle_button_get_active(toggle_button) ? + "enabled" : "disabled", + STATUSBAR_UPDATE_WARNING); +} + +GtkWidget* ctk_app_profile_new(CtkConfig *ctk_config) +{ + GObject *object; + CtkAppProfile *ctk_app_profile; + GtkWidget *banner; + GtkWidget *hseparator; + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *alignment; + GtkWidget *notebook; + GtkWidget *rules_page, *profiles_page; + GtkWidget *toolbar; + + char *global_config_file; + char **search_path; + size_t search_path_size; + ToolbarItemTemplate *save_reload_toolbar_items; + size_t num_save_reload_toolbar_items; + + + /* Create the CtkAppProfile object */ + object = g_object_new(CTK_TYPE_APP_PROFILE, NULL); + + ctk_app_profile = CTK_APP_PROFILE(object); + ctk_app_profile->ctk_config = ctk_config; + + gtk_box_set_spacing(GTK_BOX(ctk_app_profile), 10); + + /* Load app profile settings */ + // TODO only load this if the page is exposed + search_path = get_default_search_path(&search_path_size); + global_config_file = get_default_global_config_file(); + ctk_app_profile->gold_config = nv_app_profile_config_load(global_config_file, + search_path, + search_path_size); + ctk_app_profile->cur_config = nv_app_profile_config_dup(ctk_app_profile->gold_config); + free_search_path(search_path, search_path_size); + free(global_config_file); + + ctk_app_profile->apc_profile_model = ctk_apc_profile_model_new(ctk_app_profile->cur_config); + ctk_app_profile->apc_rule_model = ctk_apc_rule_model_new(ctk_app_profile->cur_config); + + /* Create the banner */ + banner = ctk_banner_image_new(BANNER_ARTWORK_CONFIG); + gtk_box_pack_start(GTK_BOX(ctk_app_profile), banner, FALSE, FALSE, 0); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(ctk_app_profile), hbox, FALSE, FALSE, 0); + + label = gtk_label_new("Application Profiles"); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + + hseparator = gtk_hseparator_new(); + gtk_box_pack_start(GTK_BOX(hbox), hseparator, TRUE, TRUE, 5); + + ctk_app_profile->enable_check_button = + gtk_check_button_new_with_label("Enable application profiles"); + gtk_box_pack_start(GTK_BOX(ctk_app_profile), + ctk_app_profile->enable_check_button, + FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(ctk_app_profile->enable_check_button), "toggled", + G_CALLBACK(enabled_check_button_toggled), + (gpointer)ctk_app_profile); + + + ctk_app_profile->global_settings_help_data = NULL; + ctk_config_set_tooltip_and_add_help_data(ctk_app_profile->ctk_config, + ctk_app_profile->enable_check_button, + &ctk_app_profile->global_settings_help_data, + "Enabling Application Profiles", + __enabling_application_profiles_help, + NULL); + + app_profile_load_global_settings(ctk_app_profile, + ctk_app_profile->cur_config); + + // XXX add a search box? + + /* Create the primary notebook for rule/profile config */ + ctk_app_profile->notebook = notebook = gtk_notebook_new(); + + /* Build the rules page */ + rules_page = create_rules_page(ctk_app_profile); + label = gtk_label_new("Rules"); + + ctk_config_set_tooltip(ctk_app_profile->ctk_config, label, __rules_page_help); + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), rules_page, label); + + /* Build the profiles page */ + profiles_page = create_profiles_page(ctk_app_profile); + label = gtk_label_new("Profiles"); + + ctk_config_set_tooltip(ctk_app_profile->ctk_config, label, __profiles_page_help); + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), profiles_page, label); + + /* Add the notebook to the main container */ + gtk_box_pack_start(GTK_BOX(ctk_app_profile), notebook, TRUE, TRUE, 0); + + /* Create the save and restore buttons */ + alignment = gtk_alignment_new(1.0, 0.5, 0.0, 0.0); + toolbar = gtk_toolbar_new(); + save_reload_toolbar_items = get_save_reload_toolbar_items(ctk_app_profile, &num_save_reload_toolbar_items); + populate_toolbar(GTK_TOOLBAR(toolbar), + save_reload_toolbar_items, + num_save_reload_toolbar_items, + &ctk_app_profile->save_reload_help_data, + NULL); + free(save_reload_toolbar_items); + + gtk_container_add(GTK_CONTAINER(alignment), toolbar); + gtk_box_pack_start(GTK_BOX(ctk_app_profile), alignment, FALSE, FALSE, 0); + + gtk_widget_show_all(GTK_WIDGET(ctk_app_profile)); + + /* Create edit profile/rule window */ + ctk_app_profile->edit_rule_dialog = edit_rule_dialog_new(ctk_app_profile); + ctk_app_profile->edit_profile_dialog = edit_profile_dialog_new(ctk_app_profile); + ctk_app_profile->save_app_profile_changes_dialog = save_app_profile_changes_dialog_new(ctk_app_profile); + + return GTK_WIDGET(ctk_app_profile); +} diff --git a/src/gtk+-2.x/ctkappprofile.h b/src/gtk+-2.x/ctkappprofile.h new file mode 100644 index 0000000..add8118 --- /dev/null +++ b/src/gtk+-2.x/ctkappprofile.h @@ -0,0 +1,204 @@ +/* + * nvidia-settings: A tool for configuring the NVIDIA X driver on Unix + * and Linux systems. + * + * Copyright (C) 2013 NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses>. + */ + +#ifndef __CTK_APP_PROFILE_H__ +#define __CTK_APP_PROFILE_H__ + +#include "NvCtrlAttributes.h" +#include "app-profiles.h" +#include "ctkevent.h" +#include "ctkconfig.h" +#include "ctkapcprofilemodel.h" +#include "ctkapcrulemodel.h" +#include "ctkdropdownmenu.h" + +G_BEGIN_DECLS + +#define CTK_TYPE_APP_PROFILE (ctk_app_profile_get_type()) + +#define CTK_APP_PROFILE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), CTK_TYPE_APP_PROFILE, CtkAppProfile)) + +#define CTK_APP_PROFILE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), CTK_TYPE_APP_PROFILE, CtkAppProfileClass)) + +#define CTK_IS_APP_PROFILE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CTK_TYPE_APP_PROFILE)) + +#define CTK_IS_APP_PROFILE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), CTK_TYPE_APP_PROFILE)) + +#define CTK_APP_PROFILE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), CTK_TYPE_APP_PROFILE, CtkAppProfileClass)) + + +typedef struct _CtkAppProfile CtkAppProfile; +typedef struct _CtkAppProfileClass CtkAppProfileClass; + +typedef struct _EditRuleDialog { + GtkWidget *parent; + GtkWidget *top_window; + + gboolean new_rule; + gint rule_id; + + // Canonical dialog box values + GString *source_file; + gint feature; + GString *matches; + GString *profile_name; + + // Widgets + GtkWidget *source_file_combo; + GtkWidget *feature_menu; + GtkEntry *matches_entry; + GtkWidget *profile_name_combo; + GtkListStore *profile_settings_store; + GtkWidget *file_sel; + + GtkWidget *add_edit_rule_button; + + // Data for constructing the help text for this dialog + GList *help_data; + + // Signals + gulong rule_profile_name_changed_signal; + gulong feature_changed_signal; +} EditRuleDialog; + +typedef struct _EditProfileDialog { + GtkWidget *parent; + + // For convenience the profile dialog box can be opened + // from the main window *or* the rule dialog box. Track + // which is the caller here. + GtkWidget *caller; + + GtkWidget *top_window; + + gboolean new_profile; + + // Canonical dialog box values + GString *name; + GString *orig_name; // The original name, before any editing took place + GString *source_file; + json_t *settings; + + // Widgets + GtkWidget *name_entry; + GtkWidget *generate_name_button; + + GtkWidget *source_file_combo; + GtkWidget *file_sel; + + GtkWidget *add_edit_profile_button; + + // Used in the special case where a currently edited row + // will be deleted, in which case we don't want to update + // the model. + gboolean setting_update_canceled; + + GtkWidget *error_statusbar; + guint setting_error_context_id; + + // Data for constructing the help text for this dialog + GList *top_help_data; + GList *setting_column_help_data; + GList *setting_toolbar_help_data; + GList *bottom_help_data; + + GtkTreeView *settings_view; + GtkListStore *settings_store; + +} EditProfileDialog; + +typedef struct _SaveAppProfileChangesDialog { + GtkWidget *parent; + GtkWidget *top_window; + + gboolean show_preview; + + // Canonical dialog box values + json_t *updates; + + // Widgets + GtkWidget *preview_button; + GtkWidget *preview_backup_entry; + GtkWidget *preview_text_view; + GtkWidget *preview_file_menu; + GtkWidget *preview_vbox; + GtkWidget *backup_check_button; + + // Data for constructing the help text for this dialog + GList *help_data; + + // Signals + gulong preview_changed_signal; +} SaveAppProfileChangesDialog; + +struct _CtkAppProfile +{ + GtkVBox parent; + CtkConfig *ctk_config; + + AppProfileConfig *gold_config, *cur_config; + + // Interfaces layered on top of the config object for use with GtkTreeView + CtkApcProfileModel *apc_profile_model; + CtkApcRuleModel *apc_rule_model; + + // Widgets + GtkTreeView *main_profile_view; + GtkTreeView *main_rule_view; + GtkWidget *notebook; + GtkWidget *enable_check_button; + + // Dialog boxes + EditRuleDialog *edit_rule_dialog; + EditProfileDialog *edit_profile_dialog; + SaveAppProfileChangesDialog *save_app_profile_changes_dialog; + + // Data for constructing the help text for this page + GList *global_settings_help_data; + + GList *rules_help_data; + GList *rules_columns_help_data; + + GList *profiles_help_data; + GList *profiles_columns_help_data; + + GList *save_reload_help_data; + + // TODO: provide undo functionality +}; + +struct _CtkAppProfileClass +{ + GtkVBoxClass parent_class; +}; + +GType ctk_app_profile_get_type (void) G_GNUC_CONST; +GtkWidget* ctk_app_profile_new (CtkConfig *); +GtkTextBuffer* ctk_app_profile_create_help (CtkAppProfile *, GtkTextTagTable *); + +char *serialize_settings(const json_t *settings, gboolean add_markup); + +G_END_DECLS + +#endif /* __CTK_APP_PROFILE_H__ */ diff --git a/src/gtk+-2.x/ctkbanner.c b/src/gtk+-2.x/ctkbanner.c index 1ee026a..88c645c 100644 --- a/src/gtk+-2.x/ctkbanner.c +++ b/src/gtk+-2.x/ctkbanner.c @@ -39,7 +39,6 @@ #include "color_pixdata.h" #include "config_pixdata.h" #include "crt_pixdata.h" -#include "cursor_shadow_pixdata.h" #include "dfp_pixdata.h" #include "display_config_pixdata.h" #include "framelock_pixdata.h" @@ -54,8 +53,8 @@ #include "slimm_pixdata.h" #include "solaris_pixdata.h" #include "thermal_pixdata.h" -#include "tv_pixdata.h" #include "vcs_pixdata.h" +#include "vdpau_pixdata.h" #include "x_pixdata.h" #include "xvideo_pixdata.h" #include "svp_3dvp_pixdata.h" @@ -355,7 +354,6 @@ static gboolean select_artwork(BannerArtworkType artwork, { BANNER_ARTWORK_COLOR, FALSE, 16, &color_pixdata }, { BANNER_ARTWORK_CONFIG, FALSE, 16, &config_pixdata }, { BANNER_ARTWORK_CRT, FALSE, 16, &crt_pixdata }, - { BANNER_ARTWORK_CURSOR_SHADOW, FALSE, 16, &cursor_shadow_pixdata }, { BANNER_ARTWORK_DFP, FALSE, 16, &dfp_pixdata }, { BANNER_ARTWORK_DISPLAY_CONFIG, FALSE, 16, &display_config_pixdata }, { BANNER_ARTWORK_FRAMELOCK, FALSE, 16, &framelock_pixdata }, @@ -370,8 +368,8 @@ static gboolean select_artwork(BannerArtworkType artwork, { BANNER_ARTWORK_SLIMM, FALSE, 16, &slimm_pixdata }, { BANNER_ARTWORK_SOLARIS, TRUE, 16, &solaris_pixdata }, { BANNER_ARTWORK_THERMAL, FALSE, 16, &thermal_pixdata }, - { BANNER_ARTWORK_TV, FALSE, 16, &tv_pixdata }, { BANNER_ARTWORK_VCS, FALSE, 16, &vcs_pixdata }, + { BANNER_ARTWORK_VDPAU, FALSE, 16, &vdpau_pixdata }, { BANNER_ARTWORK_X, FALSE, 16, &x_pixdata }, { BANNER_ARTWORK_XVIDEO, FALSE, 16, &xvideo_pixdata }, { BANNER_ARTWORK_SVP, FALSE, 16, &svp_3dvp_pixdata }, diff --git a/src/gtk+-2.x/ctkbanner.h b/src/gtk+-2.x/ctkbanner.h index 256d4b0..565460c 100644 --- a/src/gtk+-2.x/ctkbanner.h +++ b/src/gtk+-2.x/ctkbanner.h @@ -50,7 +50,6 @@ typedef enum { BANNER_ARTWORK_COLOR, BANNER_ARTWORK_CONFIG, BANNER_ARTWORK_CRT, - BANNER_ARTWORK_CURSOR_SHADOW, BANNER_ARTWORK_DFP, BANNER_ARTWORK_DISPLAY_CONFIG, BANNER_ARTWORK_FRAMELOCK, @@ -65,8 +64,8 @@ typedef enum { BANNER_ARTWORK_SLIMM, BANNER_ARTWORK_SOLARIS, BANNER_ARTWORK_THERMAL, - BANNER_ARTWORK_TV, BANNER_ARTWORK_VCS, + BANNER_ARTWORK_VDPAU, BANNER_ARTWORK_X, BANNER_ARTWORK_XVIDEO, BANNER_ARTWORK_SVP, diff --git a/src/gtk+-2.x/ctkclocks.c b/src/gtk+-2.x/ctkclocks.c index b943445..84d056f 100644 --- a/src/gtk+-2.x/ctkclocks.c +++ b/src/gtk+-2.x/ctkclocks.c @@ -31,6 +31,7 @@ #include "ctkhelp.h" #include "ctkevent.h" #include "ctkconstants.h" +#include "ctkdropdownmenu.h" @@ -69,7 +70,7 @@ static void set_clocks_value(CtkClocks *ctk_object, int clocks, static void adjustment_value_changed(GtkAdjustment *adjustment, gpointer user_data); -static void clock_menu_changed(GtkOptionMenu *option_menu, gpointer user_data); +static void clock_menu_changed(GtkWidget *widget, gpointer user_data); static void apply_clocks_clicked(GtkWidget *widget, gpointer user_data); static void detect_clocks_clicked(GtkWidget *widget, gpointer user_data); @@ -191,8 +192,7 @@ GtkWidget* ctk_clocks_new(NvCtrlAttributeHandle *handle, GtkObject *adjustment; GtkWidget *alignment; GtkWidget *scale; - GtkWidget *menu; - GtkWidget *menu_item; + CtkDropDownMenu *menu; GtkWidget *label; @@ -203,7 +203,7 @@ GtkWidget* ctk_clocks_new(NvCtrlAttributeHandle *handle, ReturnStatus ret; /* NvCtrlxxx function return value */ - int value; + int value, i = 0; int clocks_2D; NVCTRLAttributeValidValuesRec ranges_2D; NVCTRLAttributeValidValuesRec range_detection; @@ -294,21 +294,19 @@ GtkWidget* ctk_clocks_new(NvCtrlAttributeHandle *handle, /* Create the Clock menu widget */ - menu = gtk_menu_new(); + menu = (CtkDropDownMenu *) + ctk_drop_down_menu_new(CTK_DROP_DOWN_MENU_FLAG_COMBO); + i = 0; if ( can_access_2d_clocks ) { - menu_item = gtk_menu_item_new_with_label("2D Clock Frequencies"); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); + ctk_drop_down_menu_append_item(menu, "2D Clock Frequencies", i++); } if ( can_access_3d_clocks ) { - menu_item = gtk_menu_item_new_with_label("3D Clock Frequencies"); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); + ctk_drop_down_menu_append_item(menu, "3D Clock Frequencies", i++); } - ctk_object->clock_menu = gtk_option_menu_new (); - gtk_option_menu_set_menu - (GTK_OPTION_MENU(ctk_object->clock_menu), menu); + ctk_object->clock_menu = GTK_WIDGET(menu); g_signal_connect(G_OBJECT(ctk_object->clock_menu), "changed", G_CALLBACK(clock_menu_changed), @@ -915,7 +913,9 @@ static void sync_gui_to_modify_clocks(CtkClocks *ctk_object, int which_clocks) int clk_values; int default_clk_values; NVCTRLAttributeValidValuesRec clk_ranges; - + CtkDropDownMenu *menu; + + menu = CTK_DROP_DOWN_MENU(ctk_object->clock_menu); /* Obtain the current value and range of the desired clocks */ @@ -975,8 +975,7 @@ static void sync_gui_to_modify_clocks(CtkClocks *ctk_object, int which_clocks) G_CALLBACK(clock_menu_changed), (gpointer) ctk_object); - gtk_option_menu_set_history(GTK_OPTION_MENU(ctk_object->clock_menu), - (which_clocks==CLOCKS_2D)?0:1); + ctk_drop_down_menu_set_current_value(menu, (which_clocks==CLOCKS_2D)?0:1); g_signal_handlers_unblock_by_func(G_OBJECT(ctk_object->clock_menu), G_CALLBACK(clock_menu_changed), @@ -1084,15 +1083,16 @@ static void adjustment_value_changed(GtkAdjustment *adjustment, * Signal handler - User selected a clock set from the clock menu. * */ -static void clock_menu_changed(GtkOptionMenu *option_menu, gpointer user_data) +static void clock_menu_changed(GtkWidget *widget, gpointer user_data) { CtkClocks *ctk_object = CTK_CLOCKS(user_data); + CtkDropDownMenu *menu = CTK_DROP_DOWN_MENU(widget); gint history; /* Sync to allow user to modify the clocks */ - history = gtk_option_menu_get_history(option_menu); + history = ctk_drop_down_menu_get_current_value(menu); switch (history) { default: /* Fall throught */ diff --git a/src/gtk+-2.x/ctkcolorcontrols.c b/src/gtk+-2.x/ctkcolorcontrols.c index c93309d..97b5d87 100644 --- a/src/gtk+-2.x/ctkcolorcontrols.c +++ b/src/gtk+-2.x/ctkcolorcontrols.c @@ -32,6 +32,7 @@ #include "ctkconfig.h" #include "ctkhelp.h" #include "ctkcolorcontrols.h" +#include "ctkdropdownmenu.h" /* function prototypes */ static gboolean build_color_space_table(CtkColorControls *ctk_color_controls, @@ -45,9 +46,9 @@ gboolean update_color_space_menu_info(CtkColorControls *ctk_color_controls); static void setup_reset_button(CtkColorControls *ctk_color_controls); -static void color_space_menu_changed(GtkOptionMenu *color_space_menu, +static void color_space_menu_changed(GtkWidget *widget, gpointer user_data); -static void color_range_menu_changed(GtkOptionMenu *color_range_menu, +static void color_range_menu_changed(GtkWidget *widget, gpointer user_data); static void color_control_update_received(GtkObject *object, gpointer arg1, @@ -113,7 +114,8 @@ GtkWidget* ctk_color_controls_new(NvCtrlAttributeHandle *handle, GObject *object; CtkColorControls *ctk_color_controls; GtkWidget *frame, *hbox, *label; - GtkWidget *menu, *table, *menu_item = NULL, *separator; + GtkWidget *table, *separator; + CtkDropDownMenu *menu; ReturnStatus ret1, ret2; NVCTRLAttributeValidValuesRec valid1, valid2; gint i; @@ -163,29 +165,24 @@ GtkWidget* ctk_color_controls_new(NvCtrlAttributeHandle *handle, gtk_container_set_border_width(GTK_CONTAINER(table), 5); /* dropdown list for color space */ - menu = gtk_menu_new(); + menu = (CtkDropDownMenu *) + ctk_drop_down_menu_new(CTK_DROP_DOWN_MENU_FLAG_COMBO); for (i = 0; i < ctk_color_controls->color_space_table_size; i++) { switch (ctk_color_controls->color_space_table[i]) { case NV_CTRL_COLOR_SPACE_YCbCr422: - menu_item = gtk_menu_item_new_with_label("YCbCr422"); + ctk_drop_down_menu_append_item(menu, "YCbCr422", i); break; case NV_CTRL_COLOR_SPACE_YCbCr444: - menu_item = gtk_menu_item_new_with_label("YCbCr444"); + ctk_drop_down_menu_append_item(menu, "YCbCr444", i); break; default: case NV_CTRL_COLOR_SPACE_RGB: - menu_item = gtk_menu_item_new_with_label("RGB"); + ctk_drop_down_menu_append_item(menu, "RGB", i); break; } - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); - gtk_widget_show(menu_item); } - - ctk_color_controls->color_space_menu = gtk_option_menu_new(); - gtk_option_menu_set_menu - (GTK_OPTION_MENU(ctk_color_controls->color_space_menu), - menu); + ctk_color_controls->color_space_menu = GTK_WIDGET(menu); ctk_config_set_tooltip(ctk_config, ctk_color_controls->color_space_menu, __color_space_help); @@ -226,8 +223,10 @@ GtkWidget* ctk_color_controls_new(NvCtrlAttributeHandle *handle, /* Build color widgets & pack them in table */ /* dropdown list for color range */ - - ctk_color_controls->color_range_menu = gtk_option_menu_new(); + + ctk_color_controls->color_range_menu = + ctk_drop_down_menu_new(CTK_DROP_DOWN_MENU_FLAG_COMBO); + ctk_config_set_tooltip(ctk_config, ctk_color_controls->color_range_menu, __color_range_help); @@ -279,6 +278,7 @@ static void setup_reset_button(CtkColorControls *ctk_color_controls) { gint history; gint val; + CtkDropDownMenu *color_space_menu, *color_range_menu; if (!GTK_WIDGET_SENSITIVE(ctk_color_controls->color_controls_box)) { /* Nothing is available, don't bother enabling the reset button yet. */ @@ -286,8 +286,9 @@ static void setup_reset_button(CtkColorControls *ctk_color_controls) } /* The color space menu is always available */ - history = gtk_option_menu_get_history - (GTK_OPTION_MENU(ctk_color_controls->color_space_menu)); + color_space_menu = CTK_DROP_DOWN_MENU(ctk_color_controls->color_space_menu); + history = color_space_menu->current_selected_item; + val = ctk_color_controls->color_space_table[history]; if (val != NV_CTRL_COLOR_SPACE_RGB) { goto enable; @@ -295,8 +296,9 @@ static void setup_reset_button(CtkColorControls *ctk_color_controls) /* Color range is dependent on the color space */ if (GTK_WIDGET_SENSITIVE(ctk_color_controls->color_range_menu)) { - history = gtk_option_menu_get_history - (GTK_OPTION_MENU(ctk_color_controls->color_range_menu)); + color_range_menu = + CTK_DROP_DOWN_MENU(ctk_color_controls->color_range_menu); + history = color_range_menu->current_selected_item; val = ctk_color_controls->color_range_table[history]; if (val != NV_CTRL_COLOR_RANGE_FULL) { goto enable; @@ -356,9 +358,8 @@ static gboolean update_color_space_menu_info(CtkColorControls *ctk_color_control G_CALLBACK(color_space_menu_changed), (gpointer) ctk_color_controls); - gtk_option_menu_set_history - (GTK_OPTION_MENU(ctk_color_controls->color_space_menu), - color_space); + ctk_drop_down_menu_set_current_value + (CTK_DROP_DOWN_MENU(ctk_color_controls->color_space_menu), color_space); g_signal_handlers_unblock_by_func (G_OBJECT(ctk_color_controls->color_space_menu), @@ -409,48 +410,36 @@ void post_color_space_update(CtkColorControls *ctk_color_controls, ctk_color_controls->name); } -static void color_range_menu_changed(GtkOptionMenu *color_range_menu, +static void color_range_menu_changed(GtkWidget *widget, gpointer user_data) { CtkColorControls *ctk_color_controls = CTK_COLOR_CONTROLS(user_data); + CtkDropDownMenu *menu = CTK_DROP_DOWN_MENU(widget); gint history, color_range = NV_CTRL_COLOR_RANGE_FULL; - history = gtk_option_menu_get_history(color_range_menu); + history = ctk_drop_down_menu_get_current_value(menu); color_range = ctk_color_controls->color_range_table[history]; NvCtrlSetAttribute(ctk_color_controls->handle, NV_CTRL_COLOR_RANGE, color_range); - g_signal_handlers_block_by_func - (G_OBJECT(ctk_color_controls->color_range_menu), - G_CALLBACK(color_range_menu_changed), - (gpointer) ctk_color_controls); - - gtk_option_menu_set_history - (GTK_OPTION_MENU(ctk_color_controls->color_range_menu), - color_range); - - g_signal_handlers_unblock_by_func - (G_OBJECT(ctk_color_controls->color_range_menu), - G_CALLBACK(color_range_menu_changed), - (gpointer) ctk_color_controls); - /* reflecting the change to statusbar message and the reset button */ post_color_range_update(ctk_color_controls, color_range); } /* color_range_menu_changed() */ -static void color_space_menu_changed(GtkOptionMenu *color_space_menu, +static void color_space_menu_changed(GtkWidget *widget, gpointer user_data) { CtkColorControls *ctk_color_controls = CTK_COLOR_CONTROLS(user_data); + CtkDropDownMenu *menu = CTK_DROP_DOWN_MENU(widget); gint history, color_space = NV_CTRL_COLOR_SPACE_RGB; - - history = gtk_option_menu_get_history(color_space_menu); + + history = ctk_drop_down_menu_get_current_value(menu); color_space = ctk_color_controls->color_space_table[history]; @@ -460,19 +449,6 @@ static void color_space_menu_changed(GtkOptionMenu *color_space_menu, color_space = map_nvctrl_value_to_table(ctk_color_controls, color_space); - g_signal_handlers_block_by_func - (G_OBJECT(ctk_color_controls->color_space_menu), - G_CALLBACK(color_space_menu_changed), - (gpointer) ctk_color_controls); - - gtk_option_menu_set_history - (GTK_OPTION_MENU(ctk_color_controls->color_space_menu), - color_space); - - g_signal_handlers_unblock_by_func - (G_OBJECT(ctk_color_controls->color_space_menu), - G_CALLBACK(color_space_menu_changed), - (gpointer) ctk_color_controls); /* reflecting the change in color space to other widgets & reset button */ ctk_color_controls_setup(ctk_color_controls); @@ -510,7 +486,7 @@ void add_color_controls_help(CtkColorControls *ctk_color_controls, GtkTextBuffer *b, GtkTextIter *i) { - if (ctk_color_controls == NULL) { + if (!ctk_color_controls) { return; } @@ -597,8 +573,8 @@ static gboolean setup_color_range_dropdown(CtkColorControls *ctk_color_controls) gint i, n = 0, color_range_count = 0; gint mask, val; ReturnStatus ret; - GtkWidget *menu, *menu_item; NVCTRLAttributeValidValuesRec valid; + CtkDropDownMenu *d; ret = NvCtrlGetValidAttributeValues(ctk_color_controls->handle, NV_CTRL_COLOR_RANGE, @@ -636,20 +612,25 @@ static gboolean setup_color_range_dropdown(CtkColorControls *ctk_color_controls) } /* dropdown list for color range */ - menu = gtk_menu_new(); + d = (CtkDropDownMenu *) ctk_color_controls->color_range_menu; + + g_signal_handlers_block_by_func + (G_OBJECT(ctk_color_controls->color_range_menu), + G_CALLBACK(color_range_menu_changed), + (gpointer) ctk_color_controls); + + ctk_drop_down_menu_reset(d); for (i = 0; i < ctk_color_controls->color_range_table_size; i++) { switch (ctk_color_controls->color_range_table[i]) { case NV_CTRL_COLOR_RANGE_FULL: - menu_item = gtk_menu_item_new_with_label("Full"); + ctk_drop_down_menu_append_item(d, "Full", i); break; default: case NV_CTRL_COLOR_RANGE_LIMITED: - menu_item = gtk_menu_item_new_with_label("Limited"); + ctk_drop_down_menu_append_item(d, "Limited", i); break; } - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); - gtk_widget_show(menu_item); } /* color range */ @@ -660,18 +641,7 @@ static gboolean setup_color_range_dropdown(CtkColorControls *ctk_color_controls) val = NV_CTRL_COLOR_RANGE_FULL; } - g_signal_handlers_block_by_func - (G_OBJECT(ctk_color_controls->color_range_menu), - G_CALLBACK(color_range_menu_changed), - (gpointer) ctk_color_controls); - - gtk_option_menu_set_menu - (GTK_OPTION_MENU(ctk_color_controls->color_range_menu), - menu); - - gtk_option_menu_set_history - (GTK_OPTION_MENU(ctk_color_controls->color_range_menu), - val); + ctk_drop_down_menu_set_current_value(d, val); g_signal_handlers_unblock_by_func (G_OBJECT(ctk_color_controls->color_range_menu), diff --git a/src/gtk+-2.x/ctkcolorcorrection.c b/src/gtk+-2.x/ctkcolorcorrection.c index de96242..2aae8ec 100644 --- a/src/gtk+-2.x/ctkcolorcorrection.c +++ b/src/gtk+-2.x/ctkcolorcorrection.c @@ -857,65 +857,76 @@ static void apply_parsed_attribute_list( target_id = NvCtrlGetTargetId(ctk_color_correction->handle); while (p) { + CtrlHandleTargetNode *node; if (!p->next) goto next_attribute; if (!(p->flags & NV_PARSER_TYPE_COLOR_ATTRIBUTE)) goto next_attribute; /* - * if this parsed attribute's target_type, target_id does not match the - * current target_type and target_id then ignore + * Apply the parsed attribute's settings only if the color + * correction's target matches one of the (parse attribute's) + * specification targets. */ - if ((p->target_type != target_type) || - (p->target_id != target_id)) goto next_attribute; + for (node = p->targets; node && node->next; node = node->next) { + + int attr_target_type = NvCtrlGetTargetType(node->t->h); + int attr_target_id = NvCtrlGetTargetId(node->t->h); + + if ((attr_target_type != target_type) || + (attr_target_id != target_id)) { + continue; + } + + switch (p->attr & (ALL_VALUES | ALL_CHANNELS)) { + case (CONTRAST_VALUE | RED_CHANNEL): + set_color_state(ctk_color_correction, CONTRAST, + RED_CHANNEL, p->val.f, TRUE); break; + case (CONTRAST_VALUE | GREEN_CHANNEL): + set_color_state(ctk_color_correction, CONTRAST, + GREEN_CHANNEL, p->val.f, TRUE); break; + case (CONTRAST_VALUE | BLUE_CHANNEL): + set_color_state(ctk_color_correction, CONTRAST, + BLUE_CHANNEL, p->val.f, TRUE); break; + case (CONTRAST_VALUE | ALL_CHANNELS): + set_color_state(ctk_color_correction, CONTRAST, + ALL_CHANNELS, p->val.f, TRUE); break; + + case (BRIGHTNESS_VALUE | RED_CHANNEL): + set_color_state(ctk_color_correction, BRIGHTNESS, + RED_CHANNEL, p->val.f, TRUE); break; + case (BRIGHTNESS_VALUE | GREEN_CHANNEL): + set_color_state(ctk_color_correction, BRIGHTNESS, + GREEN_CHANNEL, p->val.f, TRUE); break; + case (BRIGHTNESS_VALUE | BLUE_CHANNEL): + set_color_state(ctk_color_correction, BRIGHTNESS, + BLUE_CHANNEL, p->val.f, TRUE); break; + case (BRIGHTNESS_VALUE | ALL_CHANNELS): + set_color_state(ctk_color_correction, BRIGHTNESS, + ALL_CHANNELS, p->val.f, TRUE); break; + + case (GAMMA_VALUE | RED_CHANNEL): + set_color_state(ctk_color_correction, GAMMA, + RED_CHANNEL, p->val.f, TRUE); break; + case (GAMMA_VALUE | GREEN_CHANNEL): + set_color_state(ctk_color_correction, GAMMA, + GREEN_CHANNEL, p->val.f, TRUE); break; + case (GAMMA_VALUE | BLUE_CHANNEL): + set_color_state(ctk_color_correction, GAMMA, + BLUE_CHANNEL, p->val.f, TRUE); break; + case (GAMMA_VALUE | ALL_CHANNELS): + set_color_state(ctk_color_correction, GAMMA, + ALL_CHANNELS, p->val.f, TRUE); break; + + default: + continue; + } - switch (p->attr & (ALL_VALUES | ALL_CHANNELS)) { - case (CONTRAST_VALUE | RED_CHANNEL): - set_color_state(ctk_color_correction, CONTRAST, - RED_CHANNEL, p->val.f, TRUE); break; - case (CONTRAST_VALUE | GREEN_CHANNEL): - set_color_state(ctk_color_correction, CONTRAST, - GREEN_CHANNEL, p->val.f, TRUE); break; - case (CONTRAST_VALUE | BLUE_CHANNEL): - set_color_state(ctk_color_correction, CONTRAST, - BLUE_CHANNEL, p->val.f, TRUE); break; - case (CONTRAST_VALUE | ALL_CHANNELS): - set_color_state(ctk_color_correction, CONTRAST, - ALL_CHANNELS, p->val.f, TRUE); break; - - case (BRIGHTNESS_VALUE | RED_CHANNEL): - set_color_state(ctk_color_correction, BRIGHTNESS, - RED_CHANNEL, p->val.f, TRUE); break; - case (BRIGHTNESS_VALUE | GREEN_CHANNEL): - set_color_state(ctk_color_correction, BRIGHTNESS, - GREEN_CHANNEL, p->val.f, TRUE); break; - case (BRIGHTNESS_VALUE | BLUE_CHANNEL): - set_color_state(ctk_color_correction, BRIGHTNESS, - BLUE_CHANNEL, p->val.f, TRUE); break; - case (BRIGHTNESS_VALUE | ALL_CHANNELS): - set_color_state(ctk_color_correction, BRIGHTNESS, - ALL_CHANNELS, p->val.f, TRUE); break; - - case (GAMMA_VALUE | RED_CHANNEL): - set_color_state(ctk_color_correction, GAMMA, - RED_CHANNEL, p->val.f, TRUE); break; - case (GAMMA_VALUE | GREEN_CHANNEL): - set_color_state(ctk_color_correction, GAMMA, - GREEN_CHANNEL, p->val.f, TRUE); break; - case (GAMMA_VALUE | BLUE_CHANNEL): - set_color_state(ctk_color_correction, GAMMA, - BLUE_CHANNEL, p->val.f, TRUE); break; - case (GAMMA_VALUE | ALL_CHANNELS): - set_color_state(ctk_color_correction, GAMMA, - ALL_CHANNELS, p->val.f, TRUE); break; + attr |= (p->attr & (ALL_VALUES | ALL_CHANNELS)); - default: - goto next_attribute; } - attr |= (p->attr & (ALL_VALUES | ALL_CHANNELS)); - next_attribute: p = p->next; @@ -1027,20 +1038,19 @@ static gboolean do_confirm_countdown(gpointer data) } /* do_confirm_countdown() */ -GtkTextBuffer *ctk_color_correction_create_help(GtkTextTagTable *table, - const gchar *title, - gboolean randr) +GtkTextBuffer *ctk_color_correction_create_help(GtkTextTagTable *table) { GtkTextIter i; GtkTextBuffer *b; - + const gchar *title = "X Server Color Correction"; + b = gtk_text_buffer_new(table); gtk_text_buffer_get_iter_at_offset(b, &i, 0); ctk_help_title(b, &i, "%s Help", title); - ctk_color_correction_tab_help(b, &i, title, randr); + ctk_color_correction_tab_help(b, &i, title, FALSE /* randr */); ctk_help_heading(b, &i, "Reset Hardware Defaults"); ctk_help_para(b, &i, __resest_button_help); diff --git a/src/gtk+-2.x/ctkcolorcorrection.h b/src/gtk+-2.x/ctkcolorcorrection.h index c9dfe48..cbeaa09 100644 --- a/src/gtk+-2.x/ctkcolorcorrection.h +++ b/src/gtk+-2.x/ctkcolorcorrection.h @@ -79,9 +79,7 @@ GType ctk_color_correction_get_type (void) G_GNUC_CONST; GtkWidget* ctk_color_correction_new (NvCtrlAttributeHandle *, CtkConfig *, ParsedAttribute *, CtkEvent *); -GtkTextBuffer *ctk_color_correction_create_help(GtkTextTagTable *, - const gchar *title, - gboolean randr); +GtkTextBuffer *ctk_color_correction_create_help(GtkTextTagTable *); void ctk_color_correction_tab_help(GtkTextBuffer *b, GtkTextIter *i, const gchar *title, gboolean randr); diff --git a/src/gtk+-2.x/ctkcolorcorrectionpage.c b/src/gtk+-2.x/ctkcolorcorrectionpage.c index 3b15260..003ff03 100644 --- a/src/gtk+-2.x/ctkcolorcorrectionpage.c +++ b/src/gtk+-2.x/ctkcolorcorrectionpage.c @@ -123,8 +123,7 @@ GtkWidget* ctk_color_correction_page_new(NvCtrlAttributeHandle *handle, } -GtkTextBuffer *ctk_color_correction_page_create_help(GtkTextTagTable *table, - const gchar *title) +GtkTextBuffer *ctk_color_correction_page_create_help(GtkTextTagTable *table) { - return ctk_color_correction_create_help(table, title, FALSE /* randr */); + return ctk_color_correction_create_help(table); } diff --git a/src/gtk+-2.x/ctkcolorcorrectionpage.h b/src/gtk+-2.x/ctkcolorcorrectionpage.h index 5512985..5a9f5d9 100644 --- a/src/gtk+-2.x/ctkcolorcorrectionpage.h +++ b/src/gtk+-2.x/ctkcolorcorrectionpage.h @@ -79,8 +79,7 @@ GType ctk_color_correction_page_get_type (void) G_GNUC_CONST; GtkWidget* ctk_color_correction_page_new (NvCtrlAttributeHandle *, CtkConfig *, ParsedAttribute *, CtkEvent *); -GtkTextBuffer *ctk_color_correction_page_create_help(GtkTextTagTable *, - const gchar *title); +GtkTextBuffer *ctk_color_correction_page_create_help(GtkTextTagTable *); G_END_DECLS diff --git a/src/gtk+-2.x/ctkconfig.c b/src/gtk+-2.x/ctkconfig.c index caadc1f..7293f9a 100644 --- a/src/gtk+-2.x/ctkconfig.c +++ b/src/gtk+-2.x/ctkconfig.c @@ -72,6 +72,12 @@ static const char *__save_current_config_help = "by default). Use this button to save the current X server " "configuration immediately, optionally to a different file."; +static const char *__update_rules_on_profile_name_change_help = +"If this option is enabled, changing the name of a profile in the " +"Application Profile page of nvidia-settings will cause any rules " +"that refer to that profile to also be updated to refer to the new " +"profile name."; + static void ctk_config_class_init(CtkConfigClass *ctk_config_class); static void display_status_bar_toggled(GtkWidget *, gpointer); @@ -79,6 +85,9 @@ static void tooltips_toggled(GtkWidget *, gpointer); static void slider_text_entries_toggled(GtkWidget *, gpointer); static void display_name_toggled(GtkWidget *widget, gpointer user_data); static void show_quit_dialog_toggled(GtkWidget *widget, gpointer user_data); +static void update_rules_on_profile_name_change_toggled(GtkWidget *widget, + gpointer user_data); + static void save_rc_clicked(GtkWidget *widget, gpointer user_data); static GtkWidget *create_timer_list(CtkConfig *); @@ -112,8 +121,16 @@ GType ctk_config_get_type( return ctk_config_type; } +static void config_finalize(GObject *object) +{ + CtkConfig *ctk_config = CTK_CONFIG(object); + ctk_help_data_list_free_full(ctk_config->help_data); +} + static void ctk_config_class_init(CtkConfigClass *ctk_config_class) { + GObjectClass *gobject_class = G_OBJECT_CLASS(ctk_config_class); + gobject_class->finalize = config_finalize; signals[0] = g_signal_new("slider_text_entry_toggled", G_OBJECT_CLASS_TYPE(ctk_config_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, @@ -123,6 +140,7 @@ static void ctk_config_class_init(CtkConfigClass *ctk_config_class) GtkWidget* ctk_config_new(ConfigProperties *conf, CtrlHandles *pCtrlHandles) { + gint i; GObject *object; CtkConfig *ctk_config; GtkWidget *hbox; @@ -134,6 +152,52 @@ GtkWidget* ctk_config_new(ConfigProperties *conf, CtrlHandles *pCtrlHandles) GtkWidget *alignment; gboolean b; + struct { + const char *label; + unsigned int mask; + GCallback toggled_callback; + const char *help_text; + } config_check_button_entries[] = + { + { + "Enable ToolTips", + CONFIG_PROPERTIES_TOOLTIPS, + G_CALLBACK(tooltips_toggled), + __tooltip_help + }, + { + "Display Status Bar", + CONFIG_PROPERTIES_DISPLAY_STATUS_BAR, + G_CALLBACK(display_status_bar_toggled), + __status_bar_help + }, + { + "Slider Text Entries", + CONFIG_PROPERTIES_SLIDER_TEXT_ENTRIES, + G_CALLBACK(slider_text_entries_toggled), + __slider_text_entries_help + }, + { + "Include X Display Names in the Config File", + CONFIG_PROPERTIES_INCLUDE_DISPLAY_NAME_IN_CONFIG_FILE, + G_CALLBACK(display_name_toggled), + __x_display_names_help + }, + { + "Show \"Really Quit?\" Dialog", + CONFIG_PROPERTIES_SHOW_QUIT_DIALOG, + G_CALLBACK(show_quit_dialog_toggled), + __show_quit_dialog_help + }, + { + "Update Rules when an Application Profile Name changes", + CONFIG_PROPERTIES_UPDATE_RULES_ON_PROFILE_NAME_CHANGE, + G_CALLBACK(update_rules_on_profile_name_change_toggled), + __update_rules_on_profile_name_change_help + }, + + }; + object = g_object_new(CTK_TYPE_CONFIG, NULL); ctk_config = CTK_CONFIG(object); @@ -147,6 +211,7 @@ GtkWidget* ctk_config_new(ConfigProperties *conf, CtrlHandles *pCtrlHandles) ctk_config->status_bar.widget = gtk_statusbar_new(); ctk_config->status_bar.prev_message_id = 0; + ctk_config->status_bar.enabled = TRUE; gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR(ctk_config->status_bar.widget), FALSE); @@ -183,101 +248,36 @@ GtkWidget* ctk_config_new(ConfigProperties *conf, CtrlHandles *pCtrlHandles) vbox = gtk_vbox_new(FALSE, 2); gtk_box_pack_start(GTK_BOX(ctk_config), vbox, FALSE, FALSE, 0); - /* enable tooltips */ - - label = gtk_label_new("Enable ToolTips"); - - check_button = gtk_check_button_new(); - gtk_container_add(GTK_CONTAINER(check_button), label); - - b = !!(ctk_config->conf->booleans & CONFIG_PROPERTIES_TOOLTIPS); - - if (b) { - gtk_tooltips_enable(ctk_config->tooltips.object); - } else { - gtk_tooltips_disable(ctk_config->tooltips.object); - } - - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_button), b); - gtk_box_pack_start(GTK_BOX(vbox), check_button, FALSE, FALSE, 0); - - g_signal_connect(G_OBJECT(check_button), "toggled", - G_CALLBACK(tooltips_toggled), ctk_config); - - ctk_config_set_tooltip(ctk_config, check_button, __tooltip_help); - - /* display status bar */ - - label = gtk_label_new("Display Status Bar"); - - check_button = gtk_check_button_new(); - gtk_container_add(GTK_CONTAINER(check_button), label); - - b = !!(ctk_config->conf->booleans & CONFIG_PROPERTIES_DISPLAY_STATUS_BAR); - - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_button), b); - gtk_box_pack_start(GTK_BOX(vbox), check_button, FALSE, FALSE, 0); - - g_signal_connect(G_OBJECT(check_button), "toggled", - G_CALLBACK(display_status_bar_toggled), ctk_config); - - ctk_config_set_tooltip(ctk_config, check_button, __status_bar_help); - - /* display the slider text entries */ - - label = gtk_label_new("Slider Text Entries"); - - check_button = gtk_check_button_new(); - gtk_container_add(GTK_CONTAINER(check_button), label); - - b = !!(ctk_config->conf->booleans & CONFIG_PROPERTIES_SLIDER_TEXT_ENTRIES); - - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_button), b); - gtk_box_pack_start(GTK_BOX(vbox), check_button, FALSE, FALSE, 0); - - g_signal_connect(G_OBJECT(check_button), "toggled", - G_CALLBACK(slider_text_entries_toggled), ctk_config); - - ctk_config_set_tooltip(ctk_config, check_button, - __slider_text_entries_help); - - /* specify display name in config file */ - - label = gtk_label_new("Include X Display Names in the Config File"); - - check_button = gtk_check_button_new(); - gtk_container_add(GTK_CONTAINER(check_button), label); - - b = !!(ctk_config->conf->booleans & - CONFIG_PROPERTIES_INCLUDE_DISPLAY_NAME_IN_CONFIG_FILE); - - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_button), b); - - gtk_box_pack_start(GTK_BOX(vbox), check_button, FALSE, FALSE, 0); - - g_signal_connect(G_OBJECT(check_button), "toggled", - G_CALLBACK(display_name_toggled), ctk_config); + ctk_config->help_data = NULL; - ctk_config_set_tooltip(ctk_config, check_button, __x_display_names_help); + for (i = 0; i < ARRAY_LEN(config_check_button_entries); i++) { + label = gtk_label_new(config_check_button_entries[i].label); - /* show quit dialog */ + check_button = gtk_check_button_new(); + gtk_container_add(GTK_CONTAINER(check_button), label); - label = gtk_label_new("Show \"Really Quit?\" Dialog"); - - check_button = gtk_check_button_new(); - gtk_container_add(GTK_CONTAINER(check_button), label); - - b = !!(ctk_config->conf->booleans & CONFIG_PROPERTIES_SHOW_QUIT_DIALOG); - - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_button), b); - - gtk_box_pack_start(GTK_BOX(vbox), check_button, FALSE, FALSE, 0); - - g_signal_connect(G_OBJECT(check_button), "toggled", - G_CALLBACK(show_quit_dialog_toggled), ctk_config); + b = !!(ctk_config->conf->booleans & config_check_button_entries[i].mask); + if (config_check_button_entries[i].mask == CONFIG_PROPERTIES_TOOLTIPS) { + if (b) { + gtk_tooltips_enable(ctk_config->tooltips.object); + } else { + gtk_tooltips_disable(ctk_config->tooltips.object); + } + } + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_button), b); + gtk_box_pack_start(GTK_BOX(vbox), check_button, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(check_button), "toggled", + config_check_button_entries[i].toggled_callback, + ctk_config); + ctk_config_set_tooltip_and_add_help_data(ctk_config, + check_button, + &ctk_config->help_data, + config_check_button_entries[i].label, + config_check_button_entries[i].help_text, + NULL); + } - ctk_config_set_tooltip(ctk_config, check_button, __show_quit_dialog_help); - + ctk_config->help_data = g_list_reverse(ctk_config->help_data); /* timer list */ @@ -360,6 +360,7 @@ void ctk_config_statusbar_message(CtkConfig *ctk_config, const char *fmt, ...) gchar *str; if ((!ctk_config) || + (!ctk_config->status_bar.enabled) || (!ctk_config->status_bar.widget) || (!(ctk_config->conf->booleans & CONFIG_PROPERTIES_DISPLAY_STATUS_BAR))) { @@ -411,6 +412,7 @@ static void display_status_bar_toggled( if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) { gtk_widget_show(ctk_config->status_bar.widget); ctk_config->conf->booleans |= CONFIG_PROPERTIES_DISPLAY_STATUS_BAR; + ctk_config_statusbar_message(ctk_config, "Status bar enabled."); } else { gtk_widget_hide(ctk_config->status_bar.widget); @@ -428,26 +430,35 @@ static void display_status_bar_toggled( static void tooltips_toggled(GtkWidget *widget, gpointer user_data) { CtkConfig *ctk_config = CTK_CONFIG(user_data); + gboolean active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); - if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) { + if (active) { gtk_tooltips_enable(ctk_config->tooltips.object); ctk_config->conf->booleans |= CONFIG_PROPERTIES_TOOLTIPS; } else { gtk_tooltips_disable(ctk_config->tooltips.object); ctk_config->conf->booleans &= ~CONFIG_PROPERTIES_TOOLTIPS; } + + ctk_config_statusbar_message(ctk_config, "Tooltips %s.", + active ? "enabled" : "disabled"); } static void slider_text_entries_toggled(GtkWidget *widget, gpointer user_data) { CtkConfig *ctk_config = CTK_CONFIG(user_data); + gboolean active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); - if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) { + if (active) { ctk_config->conf->booleans |= CONFIG_PROPERTIES_SLIDER_TEXT_ENTRIES; } else { ctk_config->conf->booleans &= ~CONFIG_PROPERTIES_SLIDER_TEXT_ENTRIES; } + + ctk_config_statusbar_message(ctk_config, + "Slider text entries %s.", + active ? "enabled" : "disabled"); g_signal_emit(ctk_config, signals[0], 0); } @@ -455,25 +466,55 @@ static void slider_text_entries_toggled(GtkWidget *widget, gpointer user_data) static void display_name_toggled(GtkWidget *widget, gpointer user_data) { CtkConfig *ctk_config = CTK_CONFIG(user_data); + gboolean active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); - if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) { + if (active) { ctk_config->conf->booleans |= CONFIG_PROPERTIES_INCLUDE_DISPLAY_NAME_IN_CONFIG_FILE; } else { ctk_config->conf->booleans &= ~CONFIG_PROPERTIES_INCLUDE_DISPLAY_NAME_IN_CONFIG_FILE; } + + ctk_config_statusbar_message(ctk_config, + "Including X Display Names in Config File %s.", + active ? "enabled" : "disabled"); } static void show_quit_dialog_toggled(GtkWidget *widget, gpointer user_data) { CtkConfig *ctk_config = CTK_CONFIG(user_data); + gboolean active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); - if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) { + if (active) { ctk_config->conf->booleans |= CONFIG_PROPERTIES_SHOW_QUIT_DIALOG; } else { ctk_config->conf->booleans &= ~CONFIG_PROPERTIES_SHOW_QUIT_DIALOG; } + + ctk_config_statusbar_message(ctk_config, + "Quit confirmation dialog %s.", + active ? "enabled" : "disabled"); +} + +static void update_rules_on_profile_name_change_toggled(GtkWidget *widget, + gpointer user_data) +{ + CtkConfig *ctk_config = CTK_CONFIG(user_data); + gboolean active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); + + if (active) { + ctk_config->conf->booleans |= + CONFIG_PROPERTIES_UPDATE_RULES_ON_PROFILE_NAME_CHANGE; + } else { + ctk_config->conf->booleans &= + ~CONFIG_PROPERTIES_UPDATE_RULES_ON_PROFILE_NAME_CHANGE; + } + + ctk_config_statusbar_message(ctk_config, + "Updating rules when an application profile " + "name changes is %s.", + active ? "enabled" : "disabled"); } @@ -483,7 +524,7 @@ gboolean ctk_config_slider_text_entry_shown(CtkConfig *ctk_config) CONFIG_PROPERTIES_SLIDER_TEXT_ENTRIES); } -GtkTextBuffer *ctk_config_create_help(GtkTextTagTable *table) +GtkTextBuffer *ctk_config_create_help(CtkConfig *ctk_config, GtkTextTagTable *table) { GtkTextIter i; GtkTextBuffer *b; @@ -493,21 +534,8 @@ GtkTextBuffer *ctk_config_create_help(GtkTextTagTable *table) gtk_text_buffer_get_iter_at_offset(b, &i, 0); ctk_help_title(b, &i, "nvidia-settings Configuration Help"); - - ctk_help_heading(b, &i, "Enable ToolTips"); - ctk_help_para(b, &i, __tooltip_help); - ctk_help_heading(b, &i, "Display Status Bar"); - ctk_help_para(b, &i, __status_bar_help); - - ctk_help_heading(b, &i, "Slider Text Entries"); - ctk_help_para(b, &i, __slider_text_entries_help); - - ctk_help_heading(b, &i, "Include X Display Names in the Config File"); - ctk_help_para(b, &i, __x_display_names_help); - - ctk_help_heading(b, &i, "Show \"Really Quit?\" Dialog"); - ctk_help_para(b, &i, __show_quit_dialog_help); + ctk_help_data_list_print_sections(b, &i, ctk_config->help_data); ctk_help_heading(b, &i, "Active Timers"); ctk_help_para(b, &i, "Some attributes are polled periodically " @@ -815,6 +843,11 @@ static void timer_enable_toggled(GtkCellRendererToggle *cell, g_source_remove(handle); } } + + ctk_config_statusbar_message(ctk_config, "Timer \"%s\" %s.", + timer_config->description, + timer_config->user_enabled ? + "enabled" : "disabled"); } void ctk_config_add_timer(CtkConfig *ctk_config, @@ -989,3 +1022,18 @@ void ctk_config_stop_timer(CtkConfig *ctk_config, GSourceFunc function, gpointer valid = gtk_tree_model_iter_next(model, &iter); } } + +/* + * Helper function to add a tooltip to a widget *and* append a section to the + * help text for that widget, for pages which use CtkHelpDataItem lists + */ +void ctk_config_set_tooltip_and_add_help_data(CtkConfig *config, + GtkWidget *widget, + GList **help_data_list, + const gchar *label, + const gchar *help_text, + const gchar *extended_help_text) +{ + ctk_help_data_list_prepend(help_data_list, label, help_text, extended_help_text); + ctk_config_set_tooltip(config, widget, help_text); +} diff --git a/src/gtk+-2.x/ctkconfig.h b/src/gtk+-2.x/ctkconfig.h index 75cafec..b3bf286 100644 --- a/src/gtk+-2.x/ctkconfig.h +++ b/src/gtk+-2.x/ctkconfig.h @@ -60,6 +60,9 @@ struct _CtkStatusBar { GtkWidget *widget; guint prev_message_id; + + // determines if ctk_config_statusbar_message() will update the statusbar + gboolean enabled; }; struct _CtkToolTips @@ -81,6 +84,7 @@ struct _CtkConfig GtkWidget *rc_file_selector; gboolean timer_list_visible; CtrlHandles *pCtrlHandles; + GList *help_data; }; struct _CtkConfigClass @@ -90,11 +94,11 @@ struct _CtkConfigClass GType ctk_config_get_type (void) G_GNUC_CONST; GtkWidget* ctk_config_new (ConfigProperties *, CtrlHandles*); -void ctk_config_statusbar_message (CtkConfig *, const char *, ...); +void ctk_config_statusbar_message (CtkConfig *, const char *, ...) NV_ATTRIBUTE_PRINTF(2, 3); GtkWidget* ctk_config_get_statusbar (CtkConfig *); void ctk_config_set_tooltip (CtkConfig *, GtkWidget *, const gchar *); -GtkTextBuffer *ctk_config_create_help (GtkTextTagTable *); +GtkTextBuffer *ctk_config_create_help (CtkConfig *, GtkTextTagTable *); void ctk_config_add_timer(CtkConfig *, guint, gchar *, GSourceFunc, gpointer); void ctk_config_remove_timer(CtkConfig *, GSourceFunc); @@ -104,6 +108,13 @@ void ctk_config_stop_timer(CtkConfig *, GSourceFunc, gpointer); gboolean ctk_config_slider_text_entry_shown(CtkConfig *); +void ctk_config_set_tooltip_and_add_help_data(CtkConfig *config, + GtkWidget *widget, + GList **help_data_list, + const gchar *label, + const gchar *help_text, + const gchar *extended_help_text); + G_END_DECLS #endif /* __CTK_CONFIG_H__ */ diff --git a/src/gtk+-2.x/ctkcursorshadow.c b/src/gtk+-2.x/ctkcursorshadow.c deleted file mode 100644 index 92ea1fa..0000000 --- a/src/gtk+-2.x/ctkcursorshadow.c +++ /dev/null @@ -1,1414 +0,0 @@ -/* - * nvidia-settings: A tool for configuring the NVIDIA X driver on Unix - * and Linux systems. - * - * Copyright (C) 2004 NVIDIA Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses>. - */ - -/* - * The CursorShadow widget provides a way to enable and tweak the - * parameters of the cursor shadow. With the advent of the Xcursor - * library and ARGB cursors, this is less interesting. - * - * Note that the cursor shadow and ARGB cursors cannot be used at the - * same time, so if the user enables the cursor shadow but ARGB - * cursors are currently in use, print a warning dialog box. - * - * TODO: - * - * - provide mechanism for configuring ARGB cursor themes, etc... - */ - - -#include <stdio.h> /* sprintf */ -#include <gtk/gtk.h> - -#include "NvCtrlAttributes.h" - -#include "ctkbanner.h" - -#include "ctkcursorshadow.h" -#include "ctkscale.h" -#include "ctkhelp.h" -#include "ctkconstants.h" - - -static const char *__enable_cursor_shadow_help = -"The Enable Cursor Shadow checkbox enables cursor shadow functionality. " -"Note that this functionality cannot be applied to ARGB cursors."; - -static const char *__x_offset_help = -"The cursor shadow's X offset is the offset, in pixels, that the shadow " -"image will be shifted to the right from the real cursor image."; - -static const char *__y_offset_help = -"The cursor shadow's Y offset is the offset, in pixels, that the shadow " -"image will be shifted down from the real cursor image."; - -static const char *__alpha_help = -"The cursor shadow's alpha affects how transparent or opaque the cursor " -"shadow is."; - -static const char *__color_selector_help = -"The Cursor Shadow Color Selector button toggles " -"the Cursor Shadow Color Selector window, which allows " -"you to select the color for the cursor shadow."; - -static const char *__reset_button_help = -"The Reset Hardware Defaults button restores " -"the Cursor Shadow settings to their default values."; - - -/* - * Define a table of defaults for each slider - */ - -#define __CURSOR_SHADOW_X_OFFSET_DEFAULT 4 -#define __CURSOR_SHADOW_Y_OFFSET_DEFAULT 2 -#define __CURSOR_SHADOW_ALPHA_DEFAULT 64 -#define __CURSOR_SHADOW_RED_DEFAULT 0 -#define __CURSOR_SHADOW_GREEN_DEFAULT 0 -#define __CURSOR_SHADOW_BLUE_DEFAULT 0 - -typedef struct { - gint attribute; - gint value; -} CursorShadowDefault; - -#define X_OFFSET_INDEX 0 -#define Y_OFFSET_INDEX 1 -#define ALPHA_INDEX 2 -#define RED_INDEX 3 -#define GREEN_INDEX 4 -#define BLUE_INDEX 5 - -static const CursorShadowDefault cursorShadowSliderDefaultsTable[] = { - { NV_CTRL_CURSOR_SHADOW_X_OFFSET, - __CURSOR_SHADOW_X_OFFSET_DEFAULT }, // X_OFFSET_INDEX - { NV_CTRL_CURSOR_SHADOW_Y_OFFSET, - __CURSOR_SHADOW_Y_OFFSET_DEFAULT }, // Y_OFFSET_INDEX - { NV_CTRL_CURSOR_SHADOW_ALPHA, - __CURSOR_SHADOW_ALPHA_DEFAULT }, // ALPHA_INDEX - { NV_CTRL_CURSOR_SHADOW_RED, - __CURSOR_SHADOW_RED_DEFAULT }, // RED_INDEX - { NV_CTRL_CURSOR_SHADOW_GREEN, - __CURSOR_SHADOW_GREEN_DEFAULT }, // GREEN_INDEX - { NV_CTRL_CURSOR_SHADOW_BLUE, - __CURSOR_SHADOW_BLUE_DEFAULT }, // BLUE_INDEX -}; - - - -/* local prototypes */ - -static void color_toggled(GtkWidget *widget, gpointer user_data); - -static void shadow_toggled(GtkWidget *widget, gpointer user_data); - -static GtkWidget *create_slider(CtkCursorShadow *ctk_cursor_shadow, - GtkWidget *vbox, const gchar *name, - const char *help, gint attribute); - -static void reset_defaults(GtkButton *button, gpointer user_data); - -static void adjustment_value_changed(GtkAdjustment *adjustment, - gpointer user_data); - -static void set_cursor_shadow_sensitivity(CtkCursorShadow *ctk_cursor_shadow, - gboolean enabled); -static gboolean -get_initial_reset_button_sensitivity(CtkCursorShadow *ctk_cursor_shadow); - -static void init_color_selector(CtkCursorShadow *ctk_cursor_shadow); - -static void -color_selector_close_button_clicked(GtkButton *button, gpointer user_data); - -static gboolean -color_selector_window_destroy(GtkWidget *widget, GdkEvent *event, - gpointer user_data); - -static void post_color_selector_changed(CtkCursorShadow *ctk_cursor_shadow, - gint red, gint green, gint blue); - -static void color_selector_changed(GtkColorSelection *colorselection, - gpointer user_data); -static guint16 nvctrl2gtk_color(NVCTRLAttributeValidValuesRec *range, int val); -static int gtk2nvctrl_color(NVCTRLAttributeValidValuesRec *range, - guint16 value); -static int get_value_and_range(CtkCursorShadow *ctk_cursor_shadow, - gint attribute, guint16 *value, - NVCTRLAttributeValidValuesRec *range); - -static void cursor_shadow_update_received(GtkObject *object, - gpointer arg1, gpointer user_data); - -static void adjustment_update_received(GtkObject *object, - gpointer arg1, gpointer user_data); - -static void color_update_received(GtkObject *object, - gpointer arg1, gpointer user_data); - -GType ctk_cursor_shadow_get_type(void) -{ - static GType ctk_cursor_shadow_type = 0; - - if (!ctk_cursor_shadow_type) { - static const GTypeInfo ctk_cursor_shadow_info = { - sizeof (CtkCursorShadowClass), - NULL, /* base_init */ - NULL, /* base_finalize */ - NULL, /* class_init */ - NULL, /* class_finalize */ - NULL, /* class_data */ - sizeof(CtkCursorShadow), - 0, /* n_preallocs */ - NULL, /* instance_init */ - NULL /* value_table */ - }; - - ctk_cursor_shadow_type = g_type_register_static - (GTK_TYPE_VBOX, "CtkCursorShadow", &ctk_cursor_shadow_info, 0); - } - - return ctk_cursor_shadow_type; -} - - -/* - * ctk_cursor_shadow_new() - constructor for the CursorShadow widget - */ - -GtkWidget* ctk_cursor_shadow_new(NvCtrlAttributeHandle *handle, - CtkConfig *ctk_config, - CtkEvent *ctk_event) -{ - GObject *object; - CtkCursorShadow *ctk_cursor_shadow; - GtkWidget *banner; - GtkWidget *frame; - GtkWidget *alignment; - GtkWidget *hbox; - GtkWidget *vbox; - GtkWidget *check_button; - GtkWidget *label; - GdkColor color; - ReturnStatus ret; - gint enabled; - gint red, green, blue; - char str[16]; - - /* check to see if we can support cursor shadow */ - - ret = NvCtrlGetAttribute(handle, NV_CTRL_CURSOR_SHADOW, &enabled); - - if (ret != NvCtrlSuccess) { - return NULL; - } - - /* create the cursor shadow object */ - - object = g_object_new(CTK_TYPE_CURSOR_SHADOW, NULL); - ctk_cursor_shadow = CTK_CURSOR_SHADOW(object); - - ctk_cursor_shadow->handle = handle; - ctk_cursor_shadow->ctk_config = ctk_config; - ctk_cursor_shadow->ctk_event = ctk_event; - - gtk_box_set_spacing(GTK_BOX(ctk_cursor_shadow), 10); - - /* banner */ - - banner = ctk_banner_image_new(BANNER_ARTWORK_CURSOR_SHADOW); - gtk_box_pack_start(GTK_BOX(ctk_cursor_shadow), banner, FALSE, FALSE, 0); - - /* vbox */ - - vbox = gtk_vbox_new(FALSE, 5); - gtk_box_pack_start(GTK_BOX(object), vbox, FALSE, FALSE, 0); - - /* enable cursor checkbox */ - - check_button = - gtk_check_button_new_with_label("Enable Cursor Shadow"); - - ctk_cursor_shadow->cursor_shadow_check_button = check_button; - - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_button), enabled); - - gtk_box_pack_start(GTK_BOX(vbox), check_button, FALSE, FALSE, 0); - - g_signal_connect(G_OBJECT(check_button), "toggled", - G_CALLBACK(shadow_toggled), - (gpointer) ctk_cursor_shadow); - - /* receive the event when another NV-CONTROL client changes this */ - - g_signal_connect(G_OBJECT(ctk_event), - CTK_EVENT_NAME(NV_CTRL_CURSOR_SHADOW), - G_CALLBACK(cursor_shadow_update_received), - (gpointer) ctk_cursor_shadow); - - ctk_config_set_tooltip(ctk_config, check_button, - __enable_cursor_shadow_help); - - /* sliders */ - - ctk_cursor_shadow->scales[X_OFFSET_INDEX] = - create_slider(ctk_cursor_shadow, vbox, "X Offset", __x_offset_help, - NV_CTRL_CURSOR_SHADOW_X_OFFSET); - - ctk_cursor_shadow->scales[Y_OFFSET_INDEX] = - create_slider(ctk_cursor_shadow, vbox, "Y Offset", __y_offset_help, - NV_CTRL_CURSOR_SHADOW_Y_OFFSET); - - ctk_cursor_shadow->scales[ALPHA_INDEX] = - create_slider(ctk_cursor_shadow, vbox, "Alpha", __alpha_help, - NV_CTRL_CURSOR_SHADOW_ALPHA); - - if (!ctk_cursor_shadow->scales[X_OFFSET_INDEX] || - !ctk_cursor_shadow->scales[Y_OFFSET_INDEX] || - !ctk_cursor_shadow->scales[ALPHA_INDEX]) return NULL; - - g_signal_connect(G_OBJECT(ctk_event), - CTK_EVENT_NAME(NV_CTRL_CURSOR_SHADOW_X_OFFSET), - G_CALLBACK(adjustment_update_received), - (gpointer) ctk_cursor_shadow); - - g_signal_connect(G_OBJECT(ctk_event), - CTK_EVENT_NAME(NV_CTRL_CURSOR_SHADOW_Y_OFFSET), - G_CALLBACK(adjustment_update_received), - (gpointer) ctk_cursor_shadow); - - g_signal_connect(G_OBJECT(ctk_event), - CTK_EVENT_NAME(NV_CTRL_CURSOR_SHADOW_ALPHA), - G_CALLBACK(adjustment_update_received), - (gpointer) ctk_cursor_shadow); - - - /* "Color Shadow Selector" button */ - - ctk_cursor_shadow->color_selector_button = - gtk_toggle_button_new(); - - /* Cursor Shadow Color Box */ - - frame = gtk_aspect_frame_new(NULL, 0, 0, 1, FALSE); - - gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT); - - gtk_container_set_border_width(GTK_CONTAINER(frame), 1); - - - ctk_cursor_shadow->cursor_shadow_bg = gtk_event_box_new(); - - gtk_widget_set_size_request(ctk_cursor_shadow->cursor_shadow_bg, 10, 10); - - /* Grab current cursor shadow color */ - - NvCtrlGetAttribute(ctk_cursor_shadow->handle, - NV_CTRL_CURSOR_SHADOW_RED, &red); - - NvCtrlGetAttribute(ctk_cursor_shadow->handle, - NV_CTRL_CURSOR_SHADOW_GREEN, &green); - - NvCtrlGetAttribute(ctk_cursor_shadow->handle, - NV_CTRL_CURSOR_SHADOW_BLUE, &blue); - - sprintf(str, "#%2.2X%2.2X%2.2X", red, green, blue); - - gdk_color_parse (str, &color); - - gtk_widget_modify_bg(ctk_cursor_shadow->cursor_shadow_bg, - GTK_STATE_NORMAL, &color); - - gtk_widget_modify_bg(ctk_cursor_shadow->cursor_shadow_bg, - GTK_STATE_ACTIVE, &color); - - gtk_widget_modify_bg(ctk_cursor_shadow->cursor_shadow_bg, - GTK_STATE_PRELIGHT, &color); - - /* pack cursor color selector button */ - - gtk_container_add(GTK_CONTAINER(frame), - ctk_cursor_shadow->cursor_shadow_bg); - - label = gtk_label_new("Cursor Shadow Color Selector"); - - hbox = gtk_hbox_new(FALSE, 0); - - gtk_box_pack_start( GTK_BOX(hbox), frame, TRUE, TRUE, 2); - - gtk_box_pack_end( GTK_BOX(hbox), label, FALSE, FALSE, 5); - - gtk_container_add(GTK_CONTAINER(ctk_cursor_shadow->color_selector_button), - hbox); - - hbox = gtk_hbox_new(FALSE, 5); - gtk_box_pack_start(GTK_BOX(hbox), - ctk_cursor_shadow->color_selector_button, - FALSE, FALSE, 0); - - gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5); - - gtk_toggle_button_set_active - (GTK_TOGGLE_BUTTON(ctk_cursor_shadow->color_selector_button), - FALSE); - - g_signal_connect(G_OBJECT(ctk_cursor_shadow->color_selector_button), - "clicked", G_CALLBACK(color_toggled), - (gpointer) ctk_cursor_shadow); - - ctk_config_set_tooltip(ctk_config, - ctk_cursor_shadow->color_selector_button, - __color_selector_help); - - /* Color Selector */ - - init_color_selector(ctk_cursor_shadow); - - - /* reset button */ - - label = gtk_label_new("Reset Hardware Defaults"); - hbox = gtk_hbox_new(FALSE, 0); - ctk_cursor_shadow->reset_button = gtk_button_new(); - - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 15); - gtk_container_add(GTK_CONTAINER(ctk_cursor_shadow->reset_button), hbox); - - g_signal_connect(G_OBJECT(ctk_cursor_shadow->reset_button), "clicked", - G_CALLBACK(reset_defaults), (gpointer) ctk_cursor_shadow); - - alignment = gtk_alignment_new(1, 1, 0, 0); - gtk_container_add(GTK_CONTAINER(alignment), - ctk_cursor_shadow->reset_button); - gtk_box_pack_start(GTK_BOX(object), alignment, TRUE, TRUE, 0); - - ctk_config_set_tooltip(ctk_config, ctk_cursor_shadow->reset_button, - __reset_button_help); - - /* set the sensitivity of the scales and the reset button */ - - ctk_cursor_shadow->reset_button_sensitivity = - get_initial_reset_button_sensitivity(ctk_cursor_shadow); - - set_cursor_shadow_sensitivity(ctk_cursor_shadow, enabled); - - /* finally, show the widget */ - - gtk_widget_show_all(GTK_WIDGET(ctk_cursor_shadow)); - - return GTK_WIDGET(ctk_cursor_shadow); - -} /* ctk_cursor_shadow_new() */ - - - -/* - * color_toggled() - called when the shadow color check button has - * been toggled. - */ - -static void color_toggled(GtkWidget *widget, gpointer user_data) -{ - CtkCursorShadow *ctk_cursor_shadow = CTK_CURSOR_SHADOW(user_data); - gboolean enabled; - - /* get the enabled state */ - - enabled = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); - - if (enabled) { - gtk_widget_show_all(ctk_cursor_shadow->color_selector_window); - } else { - gtk_widget_hide(ctk_cursor_shadow->color_selector_window); - } - - ctk_config_statusbar_message(ctk_cursor_shadow->ctk_config, - "Cursor Shadow Color Selector %s.", - enabled ? "enabled" : "disabled"); - -} /* color_toggled() */ - - - -/* - * post_shadow_toggled() - helper function for shadow_toggled() and - * cursor_shadow_update_received(); this does whatever work is - * necessary after the cursor shadow enable/disable state has been - * toggled -- update the reset button's sensitivity and post a - * statusbar message. - */ - -static void post_shadow_toggled(CtkCursorShadow *ctk_cursor_shadow, - gboolean enabled) -{ - /* update the sliders and reset button sensitivity */ - - set_cursor_shadow_sensitivity(ctk_cursor_shadow, enabled); - - /* update the status bar */ - - ctk_config_statusbar_message(ctk_cursor_shadow->ctk_config, - "Cursor Shadow %s.", - enabled ? "enabled" : "disabled"); - -} /* post_shadow_toggled() */ - - - -/* - * shadow_toggled() - callback when the "Enable Cursor Shadow" - * checkbox is toggled. - */ - -static void shadow_toggled(GtkWidget *widget, gpointer user_data) -{ - CtkCursorShadow *ctk_cursor_shadow = CTK_CURSOR_SHADOW(user_data); - gboolean enabled; - - /* get the enabled state */ - - enabled = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); - - /* update the server as appropriate */ - - NvCtrlSetAttribute(ctk_cursor_shadow->handle, - NV_CTRL_CURSOR_SHADOW, enabled); - - post_shadow_toggled(ctk_cursor_shadow, enabled); - -} /* shadow_toggled() */ - - - -/* - * create_slider() - a single slider - */ - -static GtkWidget *create_slider(CtkCursorShadow *ctk_cursor_shadow, - GtkWidget *vbox, const gchar *name, - const char *help, gint attribute) -{ - GtkObject *adjustment; - GtkWidget *scale, *widget; - gint min, max, val, step_incr, page_incr; - NVCTRLAttributeValidValuesRec range; - ReturnStatus ret; - - /* get the attribute value */ - - ret = NvCtrlGetAttribute(ctk_cursor_shadow->handle, attribute, &val); - - if (ret != NvCtrlSuccess) return NULL; - - /* get the range for the attribute */ - - ret = NvCtrlGetValidAttributeValues(ctk_cursor_shadow->handle, - attribute, &range); - - if (ret != NvCtrlSuccess) return NULL; - - if (range.type != ATTRIBUTE_TYPE_RANGE) return NULL; - - min = range.u.range.min; - max = range.u.range.max; - - step_incr = ((max) - (min))/250; - if (step_incr <= 0) step_incr = 1; - - page_incr = ((max) - (min))/25; - if (page_incr <= 0) page_incr = 1; - - /* create the adjustment and scale */ - - adjustment = gtk_adjustment_new(val, min, max, - step_incr, page_incr, 0.0); - - g_object_set_data(G_OBJECT(adjustment), "cursor_shadow_attribute", - GINT_TO_POINTER(attribute)); - - g_signal_connect(G_OBJECT(adjustment), "value_changed", - G_CALLBACK(adjustment_value_changed), - (gpointer) ctk_cursor_shadow); - - scale = ctk_scale_new(GTK_ADJUSTMENT(adjustment), name, - ctk_cursor_shadow->ctk_config, G_TYPE_INT); - - /* pack the scale in the vbox */ - - gtk_box_pack_start(GTK_BOX(vbox), scale, TRUE, TRUE, 0); - - /* set the tooltip for the slider */ - - widget = CTK_SCALE(scale)->gtk_scale; - ctk_config_set_tooltip(ctk_cursor_shadow->ctk_config, widget, help); - - return scale; - -} /* create_slider() */ - - - -/* - * reset_slider() - reset the slider; called by reset_defaults after - * the reset button was pressed. - */ - -static void reset_slider(CtkCursorShadow *ctk_cursor_shadow, - GtkWidget *widget, gint attribute, gint value) -{ - GtkAdjustment *adjustment; - - adjustment = CTK_SCALE(widget)->gtk_adjustment; - - /* set the default value for this attribute */ - - NvCtrlSetAttribute(ctk_cursor_shadow->handle, attribute, value); - - /* reset the slider, but ignore any signals while we reset it */ - - g_signal_handlers_block_matched - (G_OBJECT(adjustment), G_SIGNAL_MATCH_FUNC, 0, 0, NULL, - G_CALLBACK(adjustment_value_changed), NULL); - - gtk_adjustment_set_value(GTK_ADJUSTMENT(adjustment), value); - - g_signal_handlers_unblock_matched - (G_OBJECT(adjustment), G_SIGNAL_MATCH_FUNC, 0, 0, NULL, - G_CALLBACK(adjustment_value_changed), NULL); - -} /* reset_slider() */ - - - -/* - * reset_defaults() - called when the "reset defaults" button is - * pressed; clears the sliders and the reset button. - */ - -static void reset_defaults(GtkButton *button, gpointer user_data) -{ - CtkCursorShadow *ctk_cursor_shadow = CTK_CURSOR_SHADOW(user_data); - GdkColor color; - int i; - - for (i = 0; i < 3; i++) { - reset_slider(ctk_cursor_shadow, ctk_cursor_shadow->scales[i], - cursorShadowSliderDefaultsTable[i].attribute, - cursorShadowSliderDefaultsTable[i].value); - } - - /* reset color the selector */ - - g_signal_handlers_block_matched - (G_OBJECT(ctk_cursor_shadow->color_selector), - G_SIGNAL_MATCH_FUNC, 0, 0, NULL, - G_CALLBACK(color_selector_changed), NULL); - - color.red = nvctrl2gtk_color - (&ctk_cursor_shadow->red_range, - cursorShadowSliderDefaultsTable[RED_INDEX].value); - - color.green = nvctrl2gtk_color - (&ctk_cursor_shadow->green_range, - cursorShadowSliderDefaultsTable[GREEN_INDEX].value); - - color.blue = nvctrl2gtk_color - (&ctk_cursor_shadow->blue_range, - cursorShadowSliderDefaultsTable[BLUE_INDEX].value); - - gtk_color_selection_set_current_color - (GTK_COLOR_SELECTION(ctk_cursor_shadow->color_selector), &color); - - post_color_selector_changed(ctk_cursor_shadow, - color.red, color.green, color.blue); - - g_signal_handlers_unblock_matched - (G_OBJECT(ctk_cursor_shadow->color_selector), - G_SIGNAL_MATCH_FUNC, 0, 0, NULL, - G_CALLBACK(color_selector_changed), NULL); - - /* send the default colors to the server */ - - NvCtrlSetAttribute(ctk_cursor_shadow->handle, - NV_CTRL_CURSOR_SHADOW_RED, - cursorShadowSliderDefaultsTable[RED_INDEX].value); - - NvCtrlSetAttribute(ctk_cursor_shadow->handle, - NV_CTRL_CURSOR_SHADOW_GREEN, - cursorShadowSliderDefaultsTable[GREEN_INDEX].value); - - NvCtrlSetAttribute(ctk_cursor_shadow->handle, - NV_CTRL_CURSOR_SHADOW_BLUE, - cursorShadowSliderDefaultsTable[BLUE_INDEX].value); - - /* clear the sensitivity of the reset button */ - - ctk_cursor_shadow->reset_button_sensitivity = FALSE; - - gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE); - - ctk_config_statusbar_message(ctk_cursor_shadow->ctk_config, - "Reset Cursor Shadow hardware defaults."); - -} /* reset_defaults() */ - - - -/* - * post_adjustment_value_changed() - helper function for - * adjustment_value_changed() and adjustment_update_received(); this - * does whatever work is necessary after an adjustment has been - * changed -- update the reset button's sensitivity and post a - * statusbar message. - */ - -static void post_adjustment_value_changed(CtkCursorShadow *ctk_cursor_shadow, - gint attribute, gint value) -{ - gchar *attribute_str; - - /* make the reset button sensitive */ - - ctk_cursor_shadow->reset_button_sensitivity = TRUE; - gtk_widget_set_sensitive - (GTK_WIDGET(ctk_cursor_shadow->reset_button), TRUE); - - /* - * get a string description of this attribute (for use in the - * statusbar message) - */ - - switch(attribute) { - case NV_CTRL_CURSOR_SHADOW_X_OFFSET: attribute_str = "X Offset"; break; - case NV_CTRL_CURSOR_SHADOW_Y_OFFSET: attribute_str = "Y Offset"; break; - case NV_CTRL_CURSOR_SHADOW_ALPHA: attribute_str = "Alpha"; break; - default: - return; - } - - /* update the status bar */ - - ctk_config_statusbar_message(ctk_cursor_shadow->ctk_config, - "Cursor Shadow %s set to %d.", - attribute_str, value); - -} /* post_adjustment_value_changed() */ - - - -/* - * adjustment_value_changed() - called when any of the adjustments are - * changed. - */ - -static void adjustment_value_changed(GtkAdjustment *adjustment, - gpointer user_data) -{ - CtkCursorShadow *ctk_cursor_shadow = CTK_CURSOR_SHADOW(user_data); - gint attribute, value; - - /* retrieve which attribute this adjustment controls */ - - attribute = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(adjustment), - "cursor_shadow_attribute")); - /* get the current value in the adjustment */ - - value = (gint) adjustment->value; - - /* send the new value to the server */ - - NvCtrlSetAttribute(ctk_cursor_shadow->handle, attribute, value); - - post_adjustment_value_changed(ctk_cursor_shadow, attribute, value); - -} /* adjustment_value_changed() */ - - - -/* - * set_cursor_shadow_sensitivity() - set the sensitivity for the - * sliders and the reset button, based on whether the cursor shadow is - * enabled. - */ - -static void set_cursor_shadow_sensitivity(CtkCursorShadow *ctk_cursor_shadow, - gboolean enabled) -{ - int i; - - for (i = 0; i < 3; i++) { - gtk_widget_set_sensitive(ctk_cursor_shadow->scales[i], enabled); - } - - gtk_widget_set_sensitive(ctk_cursor_shadow->color_selector, enabled); - - gtk_widget_set_sensitive(ctk_cursor_shadow->color_selector_button, enabled); - - /* - * We separately track whether the reset button should be - * sensitive because, unlike the sliders (which should be - * sensitive whenever CursorShadow is enabled), the reset button - * should only be sensitive when the CursorShadow is enabled *and* - * when the sliders have been altered. - * - * So, here we only want to make the reset button sensitive if - * CursorShadow is enabled and our separate tracking says the - * reset button should be sensitive. - */ - - if (enabled && ctk_cursor_shadow->reset_button_sensitivity) { - enabled = TRUE; - } else { - enabled = FALSE; - } - - gtk_widget_set_sensitive(GTK_WIDGET(ctk_cursor_shadow->reset_button), - enabled); - -} /* set_cursor_shadow_sensitivity() */ - - - -/* - * get_initial_reset_button_sensitivity() - determine if all the - * sliders are in their default position; this is done by looking - * through the defaults table and comparing the default value with the - * current value. If any of the values are different, return TRUE to - * indicate that the reset button should be sensitive. If all the - * values were the same, return FALSE to indicate that the reset - * button should not be sensitive. - */ - -static gboolean -get_initial_reset_button_sensitivity(CtkCursorShadow *ctk_cursor_shadow) -{ - CtkScale *ctk_scale; - gint i, value, red, green, blue; - GdkColor color; - - for (i = 0; i < 3; i++) { - ctk_scale = CTK_SCALE(ctk_cursor_shadow->scales[i]); - value = gtk_adjustment_get_value(ctk_scale->gtk_adjustment); - if (value != cursorShadowSliderDefaultsTable[i].value) { - return TRUE; - } - } - - /* check if the color selector needs resetting */ - - gtk_color_selection_get_current_color - (GTK_COLOR_SELECTION(ctk_cursor_shadow->color_selector), &color); - - /* convert the values from GTK ranges [0,65536) to NV-CONTROL ranges */ - - red = gtk2nvctrl_color(&ctk_cursor_shadow->red_range, color.red); - green = gtk2nvctrl_color(&ctk_cursor_shadow->green_range, color.green); - blue = gtk2nvctrl_color(&ctk_cursor_shadow->blue_range, color.blue); - - /* check the current colors against the defaults */ - - if ((red != cursorShadowSliderDefaultsTable[RED_INDEX].value) || - (green != cursorShadowSliderDefaultsTable[GREEN_INDEX].value) || - (blue != cursorShadowSliderDefaultsTable[BLUE_INDEX].value)) { - return TRUE; - } - - return FALSE; - -} /* get_initial_reset_button_sensitivity() */ - - - -/* - * init_color_selector() - initialize the color selector window - */ - -static void init_color_selector(CtkCursorShadow *ctk_cursor_shadow) -{ - GdkColor color; - GtkWidget *window; - GtkWidget *vbox; - GtkWidget *hbox; - GtkWidget *banner; - GtkWidget *hseparator; - GtkWidget *button; - GtkWidget *alignment; - guint ret; - - /* create the color selector window */ - - window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - gtk_window_set_title(GTK_WINDOW(window), "Cursor Shadow Color Selector"); - gtk_container_set_border_width(GTK_CONTAINER(window), CTK_WINDOW_PAD); - - /* create a vbox to pack all the window contents in */ - - vbox = gtk_vbox_new(FALSE, 10); - gtk_container_add(GTK_CONTAINER(window), vbox); - - /* add a banner */ - - hbox = gtk_hbox_new(FALSE, 0); - gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); - - banner = ctk_banner_image_new(BANNER_ARTWORK_CURSOR_SHADOW); - gtk_box_pack_start(GTK_BOX(hbox), banner, TRUE, TRUE, 0); - - /* create the color selector */ - - ctk_cursor_shadow->color_selector = gtk_color_selection_new(); - gtk_box_pack_start(GTK_BOX(vbox), ctk_cursor_shadow->color_selector, - TRUE, TRUE, 0); - - /* place a horizontal separator */ - - hseparator = gtk_hseparator_new(); - gtk_box_pack_start(GTK_BOX(vbox), hseparator, FALSE, FALSE, 0); - - /* create and place the close button */ - - hbox = gtk_hbox_new(FALSE, 0); - - gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); - - button = gtk_button_new_from_stock(GTK_STOCK_CLOSE); - - alignment = gtk_alignment_new(1, 1, 0, 0); - gtk_container_add(GTK_CONTAINER(alignment), button); - gtk_box_pack_start(GTK_BOX(hbox), alignment, TRUE, TRUE, 0); - - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(color_selector_close_button_clicked), - (gpointer) ctk_cursor_shadow); - - /* handle destructive events to the window */ - - g_signal_connect(G_OBJECT(window), "destroy-event", - G_CALLBACK(color_selector_window_destroy), - (gpointer) ctk_cursor_shadow); - g_signal_connect(G_OBJECT(window), "delete-event", - G_CALLBACK(color_selector_window_destroy), - (gpointer) ctk_cursor_shadow); - - - /* update settings in the color selector */ - - - /* turn off the palette and alpha */ - - gtk_color_selection_set_has_opacity_control - (GTK_COLOR_SELECTION(ctk_cursor_shadow->color_selector), FALSE); - - gtk_color_selection_set_has_palette - (GTK_COLOR_SELECTION(ctk_cursor_shadow->color_selector), FALSE); - - g_object_set(G_OBJECT(ctk_cursor_shadow->color_selector), - "has-opacity-control", FALSE, NULL); - - g_object_set(G_OBJECT(ctk_cursor_shadow->color_selector), - "has-palette", FALSE, NULL); - - /* update the color continuously XXX this is deprecated? */ - - gtk_color_selection_set_update_policy - (GTK_COLOR_SELECTION(ctk_cursor_shadow->color_selector), - GTK_UPDATE_CONTINUOUS); - - /* retrieve the current values, and initialize the ranges */ - - ret = 0; - - ret |= get_value_and_range(ctk_cursor_shadow, - NV_CTRL_CURSOR_SHADOW_RED, - &color.red, - &ctk_cursor_shadow->red_range); - - ret |= get_value_and_range(ctk_cursor_shadow, - NV_CTRL_CURSOR_SHADOW_GREEN, - &color.green, - &ctk_cursor_shadow->green_range); - - ret |= get_value_and_range(ctk_cursor_shadow, - NV_CTRL_CURSOR_SHADOW_BLUE, - &color.blue, - &ctk_cursor_shadow->blue_range); - - /* something failed? give up */ - - if (ret) return; - - g_signal_connect(G_OBJECT(ctk_cursor_shadow->ctk_event), - CTK_EVENT_NAME(NV_CTRL_CURSOR_SHADOW_RED), - G_CALLBACK(color_update_received), - (gpointer) ctk_cursor_shadow); - - g_signal_connect(G_OBJECT(ctk_cursor_shadow->ctk_event), - CTK_EVENT_NAME(NV_CTRL_CURSOR_SHADOW_GREEN), - G_CALLBACK(color_update_received), - (gpointer) ctk_cursor_shadow); - - g_signal_connect(G_OBJECT(ctk_cursor_shadow->ctk_event), - CTK_EVENT_NAME(NV_CTRL_CURSOR_SHADOW_BLUE), - G_CALLBACK(color_update_received), - (gpointer) ctk_cursor_shadow); - - gtk_color_selection_set_current_color - (GTK_COLOR_SELECTION(ctk_cursor_shadow->color_selector), &color); - - /* register the callback */ - - g_signal_connect(G_OBJECT(ctk_cursor_shadow->color_selector), - "color-changed", - G_CALLBACK(color_selector_changed), - (gpointer) ctk_cursor_shadow); - - ctk_cursor_shadow->color_selector_window = window; - -} /* init_color_selector() */ - - -static void -color_selector_close_button_clicked(GtkButton *button, gpointer user_data) -{ - CtkCursorShadow *ctk_cursor_shadow = CTK_CURSOR_SHADOW(user_data); - - gtk_toggle_button_set_active - (GTK_TOGGLE_BUTTON(ctk_cursor_shadow->color_selector_button), - FALSE); - -} /* color_selector_close_button_clicked() */ - - - -static gboolean -color_selector_window_destroy(GtkWidget *widget, GdkEvent *event, - gpointer user_data) -{ - CtkCursorShadow *ctk_cursor_shadow = CTK_CURSOR_SHADOW(user_data); - - gtk_toggle_button_set_active - (GTK_TOGGLE_BUTTON(ctk_cursor_shadow->color_selector_button), - FALSE); - - return TRUE; - -} /* color_selector_window_destroy() */ - - - - -/* - * get_value_and_range() - helper function for init_color_selector(); - * retrieve the current value and the valid range for the given - * attribute. On success, return 0; on failure, return 1 (so that - * init_color_selector() can accumulate failures by or'ing the return - * values together). - */ - -static int get_value_and_range(CtkCursorShadow *ctk_cursor_shadow, - gint attribute, guint16 *value, - NVCTRLAttributeValidValuesRec *range) -{ - ReturnStatus ret0, ret1; - gint val; - - ret0 = NvCtrlGetAttribute(ctk_cursor_shadow->handle, attribute, &val); - - ret1 = NvCtrlGetValidAttributeValues(ctk_cursor_shadow->handle, - attribute, range); - - if ((ret0 == NvCtrlSuccess) && - (ret1 == NvCtrlSuccess) && - (range->type == ATTRIBUTE_TYPE_RANGE)) { - - *value = nvctrl2gtk_color(range, val); - - return 0; - } - - return 1; - -} /* get_value_and_range() */ - - - -/* - * post_color_selector_changed() - helper function for - * color_selector_changed() and color_update_received(); this does - * whatever work is necessary after the color selector has been - * changed -- update the reset button's sensitivity and post a - * statusbar message. - */ - -static void post_color_selector_changed(CtkCursorShadow *ctk_cursor_shadow, - gint red, gint green, gint blue) -{ - GdkColor color; - char str[16]; - sprintf(str, "#%2.2X%2.2X%2.2X", red, green, blue); - - - /* Update the color square */ - - gdk_color_parse(str, &color); - gtk_widget_modify_bg(ctk_cursor_shadow->cursor_shadow_bg, - GTK_STATE_NORMAL, &color); - - gtk_widget_modify_bg(ctk_cursor_shadow->cursor_shadow_bg, - GTK_STATE_ACTIVE, &color); - - gtk_widget_modify_bg(ctk_cursor_shadow->cursor_shadow_bg, - GTK_STATE_PRELIGHT, &color); - - /* make the reset button sensitive */ - - ctk_cursor_shadow->reset_button_sensitivity = TRUE; - gtk_widget_set_sensitive - (GTK_WIDGET(ctk_cursor_shadow->reset_button), TRUE); - - /* update the status bar */ - - ctk_config_statusbar_message(ctk_cursor_shadow->ctk_config, - "Cursor Shadow Color set to " - "[R:%d G:%d B:%d].", red, green, blue); - -} /* post_color_selector_changed() */ - - - -/* - * color_selector_changed() - called whenever the color selector - * changes - */ - -static void color_selector_changed(GtkColorSelection *colorselection, - gpointer user_data) -{ - CtkCursorShadow *ctk_cursor_shadow = CTK_CURSOR_SHADOW(user_data); - GdkColor color; - gint red, green, blue; - - /* retrieve the current color */ - - gtk_color_selection_get_current_color - (GTK_COLOR_SELECTION(ctk_cursor_shadow->color_selector), &color); - - /* convert the values from GTK ranges [0,65536) to NV-CONTROL ranges */ - - red = gtk2nvctrl_color(&ctk_cursor_shadow->red_range, color.red); - green = gtk2nvctrl_color(&ctk_cursor_shadow->green_range, color.green); - blue = gtk2nvctrl_color(&ctk_cursor_shadow->blue_range, color.blue); - - /* send the values to the server */ - - NvCtrlSetAttribute(ctk_cursor_shadow->handle, - NV_CTRL_CURSOR_SHADOW_RED, red); - - NvCtrlSetAttribute(ctk_cursor_shadow->handle, - NV_CTRL_CURSOR_SHADOW_GREEN, green); - - NvCtrlSetAttribute(ctk_cursor_shadow->handle, - NV_CTRL_CURSOR_SHADOW_BLUE, blue); - - post_color_selector_changed(ctk_cursor_shadow, red, green, blue); - -} /* color_selector_changed() */ - - - -/* - * nvctrl2gtk_color() - convert a color value in the NV-CONTROL range - * (given by the range argument) to the GTK color range [0,65536) - */ - -static guint16 nvctrl2gtk_color(NVCTRLAttributeValidValuesRec *range, int val) -{ - gdouble d0, d1, x0, x1; - - d0 = (double) (range->u.range.max - range->u.range.min); - d1 = 65535.0; - x0 = (double) (val - range->u.range.min); - x1 = x0 * (d1/d0); - - return (guint16) x1; - -} /* nvctrl2gtk_color() */ - - - -/* - * gtk2nvctrl_color() - convert a color value in the GTK range - * [0,65536) to the NV-CONTROL range (given by the range argument). - */ - -static int gtk2nvctrl_color(NVCTRLAttributeValidValuesRec *range, - guint16 value) -{ - gdouble d0, d1, x0, x1; - - d0 = (double) (range->u.range.max - range->u.range.min); - d1 = 65535.0; - x1 = (double) value; - x0 = x1 * (d0/d1); - - return ((guint16) x0) + range->u.range.min; - -} /* gtk2nvctrl_color() */ - - - -/* - * cursor_shadow_update_received() - callback function for when the - * NV_CTRL_CURSOR_SHADOW attribute is changed by another NV-CONTROL - * client. - */ - -static void cursor_shadow_update_received(GtkObject *object, - gpointer arg1, gpointer user_data) -{ - CtkEventStruct *event_struct = (CtkEventStruct *) arg1; - CtkCursorShadow *ctk_cursor_shadow = CTK_CURSOR_SHADOW(user_data); - gboolean enabled = event_struct->value; - GtkWidget *w = ctk_cursor_shadow->cursor_shadow_check_button; - - g_signal_handlers_block_by_func(G_OBJECT(w), - G_CALLBACK(shadow_toggled), - (gpointer) ctk_cursor_shadow); - - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), enabled); - - post_shadow_toggled(ctk_cursor_shadow, enabled); - - g_signal_handlers_unblock_by_func(G_OBJECT(w), - G_CALLBACK(shadow_toggled), - (gpointer) ctk_cursor_shadow); - -} /* cursor_shadow_update_received() */ - - - -/* - * set_reset_button() - helper function for - * adjustment_update_received() and color_update_received(); evaluate - * whether any of the attributes have non-default values, and set the - * sensitivity of the reset button appropriately (ie: only make the - * button sensitive if any attribute has a non-default value). - */ - -static void set_reset_button(CtkCursorShadow *ctk_cursor_shadow) -{ - ctk_cursor_shadow->reset_button_sensitivity = - get_initial_reset_button_sensitivity(ctk_cursor_shadow); - - gtk_widget_set_sensitive(GTK_WIDGET(ctk_cursor_shadow->reset_button), - ctk_cursor_shadow->reset_button_sensitivity); - -} /* set_reset_button() */ - - - -/* - * adjustment_update_received() - callback function that handles an - * event where another NV-CONTROL client modified any of the cursor - * shadow attributes that we have sliders for (x offset, y offset, and - * alpha). In that case, we need to update the slider with the new - * value. - */ - -static void adjustment_update_received(GtkObject *object, - gpointer arg1, gpointer user_data) -{ - CtkCursorShadow *ctk_cursor_shadow = CTK_CURSOR_SHADOW(user_data); - CtkEventStruct *event_struct = (CtkEventStruct *) arg1; - gint value = event_struct->value; - gint attribute = event_struct->attribute; - gint index; - CtkScale *ctk_scale; - GtkAdjustment *adjustment; - - /* choose the index into the scales array */ - - switch (event_struct->attribute) { - case NV_CTRL_CURSOR_SHADOW_X_OFFSET: index = X_OFFSET_INDEX; break; - case NV_CTRL_CURSOR_SHADOW_Y_OFFSET: index = Y_OFFSET_INDEX; break; - case NV_CTRL_CURSOR_SHADOW_ALPHA: index = ALPHA_INDEX; break; - default: - return; - } - - /* get the appropriate adjustment */ - - ctk_scale = CTK_SCALE(ctk_cursor_shadow->scales[index]); - adjustment = GTK_ADJUSTMENT(ctk_scale->gtk_adjustment); - - /* update the adjustment with the new value */ - - g_signal_handlers_block_matched - (G_OBJECT(adjustment), G_SIGNAL_MATCH_FUNC, 0, 0, NULL, - G_CALLBACK(adjustment_value_changed), NULL); - - gtk_adjustment_set_value(GTK_ADJUSTMENT(adjustment), value); - - post_adjustment_value_changed(ctk_cursor_shadow, attribute, value); - - g_signal_handlers_unblock_matched - (G_OBJECT(adjustment), G_SIGNAL_MATCH_FUNC, 0, 0, NULL, - G_CALLBACK(adjustment_value_changed), NULL); - - /* update the state of the reset button */ - - set_reset_button(ctk_cursor_shadow); - -} /* adjustment_update_received() */ - - - -/* - * color_update_received() - callback function that handles an event - * where another NV-CONTROL client modified the cursor shadow color. - * In that case, we need to retrieve the current color, update the - * appropriate channel with the new value, and update the color - * selector with the new color. - */ - -static void color_update_received(GtkObject *object, - gpointer arg1, gpointer user_data) -{ - CtkCursorShadow *ctk_cursor_shadow = CTK_CURSOR_SHADOW(user_data); - CtkEventStruct *event_struct = (CtkEventStruct *) arg1; - gint red, green, blue; - GdkColor color; - - /* retrieve the current color */ - - gtk_color_selection_get_current_color - (GTK_COLOR_SELECTION(ctk_cursor_shadow->color_selector), &color); - - /* convert the values from GTK ranges [0,65536) to NV-CONTROL ranges */ - - red = gtk2nvctrl_color(&ctk_cursor_shadow->red_range, color.red); - green = gtk2nvctrl_color(&ctk_cursor_shadow->green_range, color.green); - blue = gtk2nvctrl_color(&ctk_cursor_shadow->blue_range, color.blue); - - /* modify the color, keying off of which attribute was updated */ - - switch(event_struct->attribute) { - case NV_CTRL_CURSOR_SHADOW_RED: - red = event_struct->value; - color.red = nvctrl2gtk_color(&ctk_cursor_shadow->red_range, red); - break; - case NV_CTRL_CURSOR_SHADOW_GREEN: - green = event_struct->value; - color.green = nvctrl2gtk_color(&ctk_cursor_shadow->green_range, green); - break; - case NV_CTRL_CURSOR_SHADOW_BLUE: - blue = event_struct->value; - color.blue = nvctrl2gtk_color(&ctk_cursor_shadow->blue_range, blue); - break; - default: - return; - } - - /* update the color selector*/ - - g_signal_handlers_block_matched - (G_OBJECT(ctk_cursor_shadow->color_selector), - G_SIGNAL_MATCH_FUNC, 0, 0, NULL, - G_CALLBACK(color_selector_changed), NULL); - - gtk_color_selection_set_current_color - (GTK_COLOR_SELECTION(ctk_cursor_shadow->color_selector), &color); - - post_color_selector_changed(ctk_cursor_shadow, red, green, blue); - - g_signal_handlers_unblock_matched - (G_OBJECT(ctk_cursor_shadow->color_selector), - G_SIGNAL_MATCH_FUNC, 0, 0, NULL, - G_CALLBACK(color_selector_changed), NULL); - - /* update the state of the reset button */ - - set_reset_button(ctk_cursor_shadow); - -} /* color_update_received() */ - - - -/* - * ctk_cursor_shadow_create_help() - - */ - -GtkTextBuffer * -ctk_cursor_shadow_create_help(GtkTextTagTable *table, - CtkCursorShadow *ctk_cursor_shadow) -{ - GtkTextIter i; - GtkTextBuffer *b; - - b = gtk_text_buffer_new(table); - - gtk_text_buffer_get_iter_at_offset(b, &i, 0); - - ctk_help_title(b, &i, "Cursor Shadow Help"); - - ctk_help_para(b, &i, "The Cursor Shadow page allows you to configure " - "a shadow beneath X core cursors. This extends the " - "functionality exposed with the \"CursorShadow\" " - "X config file option."); - - ctk_help_para(b, &i, "Note that this functionality cannot be applied " - "to ARGB cursors, which already have their own built-in " - "shadows. Most recent distributions and desktop " - "environments enable ARGB cursors by default. If you wish " - "to disable ARGB cursors, add the line " - "\"Xcursor.core:true\" to your ~/.Xresources file."); - - ctk_help_heading(b, &i, "Enable Cursor Shadow"); - ctk_help_para(b, &i, __enable_cursor_shadow_help); - - ctk_help_heading(b, &i, "Cursor Shadow X Offset"); - ctk_help_para(b, &i, "The cursor shadow's X offset is the offset, " - "in pixels, that the shadow image will be shifted to the " - "right from the real cursor image. This functionality can " - "also be configured with the \"CursorShadowXOffset\" X " - "config file option."); - - ctk_help_heading(b, &i, "Cursor Shadow Y Offset"); - ctk_help_para(b, &i, "The cursor shadow's Y offset is the offset, " - "in pixels, that the shadow image will be shifted down " - "from the real cursor image. This functionality can " - "also be configured with the \"CursorShadowYOffset\" X " - "config file option."); - - ctk_help_heading(b, &i, "Cursor Shadow Alpha"); - ctk_help_para(b, &i, "The cursor shadow's alpha affects how transparent " - "or opaque the cursor shadow is. This functionality can " - "also be configured with the \"CursorShadowAlpha\" X " - "config file option."); - - ctk_help_heading(b, &i, "Cursor Shadow Color Selector"); - ctk_help_para(b, &i, __color_selector_help); - - ctk_help_heading(b, &i, "Reset Hardware Defaults"); - ctk_help_para(b, &i, __reset_button_help); - - ctk_help_finish(b); - - return b; - -} /* ctk_cursor_shadow_create_help() */ diff --git a/src/gtk+-2.x/ctkcursorshadow.h b/src/gtk+-2.x/ctkcursorshadow.h deleted file mode 100644 index 0169a38..0000000 --- a/src/gtk+-2.x/ctkcursorshadow.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * nvidia-settings: A tool for configuring the NVIDIA X driver on Unix - * and Linux systems. - * - * Copyright (C) 2004 NVIDIA Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses>. - */ - -#ifndef __CTK_CURSOR_SHADOW_H__ -#define __CTK_CURSOR_SHADOW_H__ - -#include "NvCtrlAttributes.h" -#include "ctkconfig.h" -#include "ctkevent.h" - -G_BEGIN_DECLS - -#define CTK_TYPE_CURSOR_SHADOW (ctk_cursor_shadow_get_type()) - -#define CTK_CURSOR_SHADOW(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), CTK_TYPE_CURSOR_SHADOW, \ - CtkCursorShadow)) - -#define CTK_CURSOR_SHADOW_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), CTK_TYPE_CURSOR_SHADOW, \ - CtkCursorShadowClass)) - -#define CTK_IS_CURSOR_SHADOW(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CTK_TYPE_CURSOR_SHADOW)) - -#define CTK_IS_CURSOR_SHADOW_CLASS(class) \ - (G_TYPE_CHECK_CLASS_TYPE ((klass), CTK_TYPE_CURSOR_SHADOW)) - -#define CTK_CURSOR_SHADOW_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), CTK_TYPE_CURSOR_SHADOW, \ - CtkCursorShadowClass)) - - -typedef struct _CtkCursorShadow CtkCursorShadow; -typedef struct _CtkCursorShadowClass CtkCursorShadowClass; - -struct _CtkCursorShadow -{ - GtkVBox parent; - - NvCtrlAttributeHandle *handle; - CtkConfig *ctk_config; - CtkEvent *ctk_event; - GtkWidget *scales[3]; - GtkWidget *reset_button; - GtkWidget *color_selector_button; - GtkWidget *cursor_shadow_bg; - GtkWidget *cursor_shadow_check_button; - GtkWidget *color_selector; - GtkWidget *color_selector_window; - gboolean reset_button_sensitivity; - NVCTRLAttributeValidValuesRec red_range; - NVCTRLAttributeValidValuesRec green_range; - NVCTRLAttributeValidValuesRec blue_range; -}; - -struct _CtkCursorShadowClass -{ - GtkVBoxClass parent_class; -}; - -GType ctk_cursor_shadow_get_type (void) G_GNUC_CONST; -GtkWidget* ctk_cursor_shadow_new (NvCtrlAttributeHandle *handle, - CtkConfig *ctk_config, - CtkEvent *ctk_event); - -GtkTextBuffer *ctk_cursor_shadow_create_help(GtkTextTagTable *, - CtkCursorShadow *); - -G_END_DECLS - -#endif /* __CTK_CURSOR_SHADOW_H__ */ - diff --git a/src/gtk+-2.x/ctkdisplayconfig-utils.c b/src/gtk+-2.x/ctkdisplayconfig-utils.c index f4df85f..36c0e04 100644 --- a/src/gtk+-2.x/ctkdisplayconfig-utils.c +++ b/src/gtk+-2.x/ctkdisplayconfig-utils.c @@ -196,32 +196,28 @@ void apply_monitor_token(char *token, char *value, void *data) /** apply_screen_info_token() **************************************** * - * Modifies the ScreenInfo structure (pointed to by data) with + * Modifies the GdkRectangle structure (pointed to by data) with * information from the token-value pair given. Currently accepts * position and width/height data. * **/ void apply_screen_info_token(char *token, char *value, void *data) { - ScreenInfo *screen_info = (ScreenInfo *)data; + GdkRectangle *screen_info = (GdkRectangle *)data; if (!screen_info || !token || !strlen(token)) { return; } - /* X */ if (!strcasecmp("x", token)) { screen_info->x = atoi(value); - /* Y */ } else if (!strcasecmp("y", token)) { screen_info->y = atoi(value); - /* Width */ } else if (!strcasecmp("width", token)) { screen_info->width = atoi(value); - /* Height */ } else if (!strcasecmp("height", token)) { screen_info->height = atoi(value); @@ -230,8 +226,7 @@ void apply_screen_info_token(char *token, char *value, void *data) nv_warning_msg("Unknown screen info token value pair: %s=%s", token, value); } - -} /* apply_screen_info_token() */ +} @@ -435,43 +430,127 @@ static nvModeLinePtr modeline_parse(nvDisplayPtr display, /** MODE FUNCTIONS ***********************************************************/ /*****************************************************************************/ -void mode_set_dims_from_modeline(nvModePtr mode, nvModeLinePtr modeline) + +/*! + * Clamps the given dimensions to be no smaller than the mode's viewPortIn + * + * \param[in, out] rect The GdkRectangle to clamp. + * \param[in] mode The mode to clamp against. + */ +void clamp_rect_to_viewportin(GdkRectangle *rect, const nvMode *mode) +{ + if (rect->width < mode->viewPortIn.width) { + rect->width = mode->viewPortIn.width; + } + if (rect->height < mode->viewPortIn.height) { + rect->height = mode->viewPortIn.height; + } +} + + + +/*! + * Clamps the mode's panning domain to the mode's viewPortIn dimensions + * + * \param[in, out] mode The mode who's panning to clamp. + */ +void clamp_mode_panning(nvModePtr mode) { - int newW; - int newH; + clamp_rect_to_viewportin(&(mode->pan), mode); +} + + + +/*! + * Fills a rectangle struct with both position and size information of the + * given mode's viewPortIn. + * + * \param[in] mode The mode to return information for. + * \param[in, out] rect The GdkRectangle structure to populate. + */ +void get_viewportin_rect(const nvMode *mode, GdkRectangle *rect) +{ + rect->x = mode->pan.x; + rect->y = mode->pan.y; + rect->width = mode->viewPortIn.width; + rect->height = mode->viewPortIn.height; +} + - mode->viewPortOut[X] = 0; - mode->viewPortOut[Y] = 0; - if (modeline) { - mode->viewPortOut[W] = modeline->data.hdisplay; - mode->viewPortOut[H] = modeline->data.vdisplay; +void mode_set_modeline(nvModePtr mode, + nvModeLinePtr modeline, + const nvSize *providedViewPortIn, + const GdkRectangle *providedViewPortOut) +{ + int width; + int height; + Bool panning_modified; + + /* Figure out what dimensions to use */ + if (providedViewPortIn) { + width = providedViewPortIn->width; + height = providedViewPortIn->height; + } else if (modeline) { + width = modeline->data.hdisplay; + height = modeline->data.vdisplay; + } else { + /* NULL modeline given (display is being turned off), use a default + * resolution to show the display. + */ + if (mode->display->modelines) { + // XXX assumes that the first modeline in the display's list is the + // default (nvidia-auto-select). + width = mode->display->modelines->data.hdisplay; + height = mode->display->modelines->data.vdisplay; + } else { + /* display has no modelines, 800x600 seems reasonable */ + width = 800; + height = 600; + } + } + + /* Reset the viewPortOut to match the full visible size of the modeline */ + // XXX Only do this if viewport out has not been tweaked? + // XXX - Should we do any clamping? + if (providedViewPortOut) { + mode->viewPortOut = *providedViewPortOut; } else { - mode->viewPortOut[W] = 800; - mode->viewPortOut[H] = 600; + mode->viewPortOut.x = 0; + mode->viewPortOut.y = 0; + mode->viewPortOut.width = width; + mode->viewPortOut.height = height; } - /* Determine new dimensions to use */ + /* Oriented the dimensions to use for the viewPortIn and Panning */ if ((mode->rotation == ROTATION_90) || (mode->rotation == ROTATION_270)) { - newW = mode->viewPortOut[H]; - newH = mode->viewPortOut[W]; - } else { - newW = mode->viewPortOut[W]; - newH = mode->viewPortOut[H]; + int temp = width; + width = height; + height = temp; } - /* Resolve and clamp the panning domain */ - if ((mode->pan[W] == mode->viewPortIn[W]) || - mode->pan[W] < newW) { - mode->pan[W] = newW; + /* XXX Later, keep a flag in nvModePtr to track if the panning has + * been modified */ + panning_modified = + (mode->pan.width != mode->viewPortIn.width) || + (mode->pan.height != mode->viewPortIn.height); + + /* XXX Only set this if the user has not modified viewPortIn... */ + { + mode->viewPortIn.width = width; + mode->viewPortIn.height = height; + /* Panning domain must include viewPortIn */ + clamp_mode_panning(mode); } - if ((mode->pan[H] == mode->viewPortIn[H]) || - mode->pan[H] < newH) { - mode->pan[H] = newH; + + /* Only set this if the user has not modified panning... */ + if (!panning_modified) { + mode->pan.width = width; + mode->pan.height = height; } - mode->viewPortIn[W] = newW; - mode->viewPortIn[H] = newH; + + mode->modeline = modeline; } @@ -504,13 +583,13 @@ Bool mode_set_rotation(nvModePtr mode, Rotation rotation) mode->rotation = rotation; if (old_is_horiz != new_is_horiz) { - tmp = mode->viewPortIn[W]; - mode->viewPortIn[W] = mode->viewPortIn[H]; - mode->viewPortIn[H] = tmp; + tmp = mode->viewPortIn.width; + mode->viewPortIn.width = mode->viewPortIn.height; + mode->viewPortIn.height = tmp; - tmp = mode->pan[W]; - mode->pan[W] = mode->pan[H]; - mode->pan[H] = tmp; + tmp = mode->pan.width; + mode->pan.width = mode->pan.height; + mode->pan.height = tmp; } /* Mark mode as being modified */ @@ -551,20 +630,20 @@ static void apply_mode_attribute_token(char *token, char *value, void *data) /* ViewPortIn */ } else if (!strcasecmp("viewportin", token)) { parse_read_integer_pair(value, 'x', - &(mode->viewPortIn[W]), - &(mode->viewPortIn[H])); + &(mode->viewPortIn.width), + &(mode->viewPortIn.height)); /* ViewPortOut */ } else if (!strcasecmp("viewportout", token)) { const char *str; str = parse_read_integer_pair(value, 'x', - &(mode->viewPortOut[W]), - &(mode->viewPortOut[H])); + &(mode->viewPortOut.width), + &(mode->viewPortOut.height)); str = parse_read_integer_pair(str, 0, - &(mode->viewPortOut[X]), - &(mode->viewPortOut[Y])); + &(mode->viewPortOut.x), + &(mode->viewPortOut.y)); } /* Rotation */ @@ -615,10 +694,11 @@ nvModePtr mode_parse(nvDisplayPtr display, const char *mode_str) nvModePtr mode; char *mode_name; /* Modeline reference name */ const char *str = mode_str; + nvModeLinePtr modeline; - if (!str || !display || !display->modelines) return NULL; + if (!str || !display) return NULL; /* Allocate a Mode structure */ @@ -639,21 +719,17 @@ nvModePtr mode_parse(nvDisplayPtr display, const char *mode_str) if (!str || !mode_name) goto fail; - /* Match the mode name to one of the modelines */ - mode->modeline = display->modelines; - while (mode->modeline) { - if (!strcmp(mode_name, mode->modeline->data.identifier)) { + /* Find the display's modeline that matches the given mode name */ + modeline = display->modelines; + while (modeline) { + if (!strcmp(mode_name, modeline->data.identifier)) { break; } - mode->modeline = mode->modeline->next; + modeline = modeline->next; } - /* If we can't find a matching modeline, don't add this mode. If the - * metamode has other (valid) displays, a NULL mode will be added for this - * display at that time - this is done to avoid potentially adding a - * metamode with no active displays. - */ - if (!mode->modeline) { + /* If we can't find a matching modeline, set the NULL mode. */ + if (!modeline) { if (strcmp(mode_str, "NULL")) { nv_warning_msg("Mode name '%s' does not match any modelines for " "display device '%s' in modeline '%s'.", @@ -661,12 +737,20 @@ nvModePtr mode_parse(nvDisplayPtr display, const char *mode_str) } free(mode_name); - mode_set_dims_from_modeline(mode, display->modelines); + mode_set_modeline(mode, + NULL /* modeline */, + NULL /* viewPortIn */, + NULL /* viewPortOut */); return mode; } free(mode_name); + /* Don't call mode_set_modeline() here since we want to apply the values + * from the string we're parsing, so just link the modeline + */ + mode->modeline = modeline; + /* Read mode information */ while (*str) { @@ -675,15 +759,16 @@ nvModePtr mode_parse(nvDisplayPtr display, const char *mode_str) if (*str == '@') { str++; str = parse_read_integer_pair(str, 'x', - &(mode->pan[W]), &(mode->pan[H])); + &(mode->pan.width), + &(mode->pan.height)); } /* Read position */ else if (*str == '+') { str++; str = parse_read_integer_pair(str, 0, - &(mode->viewPortIn[X]), - &(mode->viewPortIn[Y])); + &(mode->pan.x), + &(mode->pan.y)); } /* Read extra params */ @@ -717,13 +802,13 @@ nvModePtr mode_parse(nvDisplayPtr display, const char *mode_str) } /* Initialize defaults for the viewports if unspecified */ - if ((mode->viewPortOut[W] == 0) || (mode->viewPortOut[H] == 0)) { - mode->viewPortOut[W] = mode->modeline->data.hdisplay; - mode->viewPortOut[H] = mode->modeline->data.vdisplay; + if ((mode->viewPortOut.width == 0) || (mode->viewPortOut.height == 0)) { + mode->viewPortOut.width = mode->modeline->data.hdisplay; + mode->viewPortOut.height = mode->modeline->data.vdisplay; } - if ((mode->viewPortIn[W] == 0) || (mode->viewPortIn[H] == 0)) { - mode->viewPortIn[W] = mode->viewPortOut[W]; - mode->viewPortIn[H] = mode->viewPortOut[H]; + if ((mode->viewPortIn.width == 0) || (mode->viewPortIn.height == 0)) { + mode->viewPortIn.width = mode->viewPortOut.width; + mode->viewPortIn.height = mode->viewPortOut.height; } /* If rotation is specified, swap W/H if they are still set to the @@ -732,20 +817,15 @@ nvModePtr mode_parse(nvDisplayPtr display, const char *mode_str) */ if (((mode->rotation == ROTATION_90) || (mode->rotation == ROTATION_270)) && - (mode->viewPortIn[W] == mode->viewPortOut[W]) && - (mode->viewPortIn[H] == mode->viewPortOut[H])) { - int tmp = mode->viewPortIn[W]; - mode->viewPortIn[W] = mode->viewPortIn[H]; - mode->viewPortIn[H] = tmp; + (mode->viewPortIn.width == mode->viewPortOut.width) && + (mode->viewPortIn.height == mode->viewPortOut.height)) { + int tmp = mode->viewPortIn.width; + mode->viewPortIn.width = mode->viewPortIn.height; + mode->viewPortIn.height = tmp; } /* Clamp the panning domain */ - if (mode->pan[W] < mode->viewPortIn[W]) { - mode->pan[W] = mode->viewPortIn[W]; - } - if (mode->pan[H] < mode->viewPortIn[H]) { - mode->pan[H] = mode->viewPortIn[H]; - } + clamp_mode_panning(mode); return mode; @@ -762,6 +842,80 @@ nvModePtr mode_parse(nvDisplayPtr display, const char *mode_str) +/** apply_underscan_to_viewportout() ********************************** + * + * Modifies the given ViewPortOut with an underscan border of the given size in + * horizontal pixels. The original aspect ratio is preserved. + * + */ +void apply_underscan_to_viewportout(const nvSize raster_size, + const int hpixel_value, + GdkRectangle *viewPortOut) +{ + float scale_factor, x_offset, y_offset; + + /* Preserve aspect ratio */ + scale_factor = (float) raster_size.width / raster_size.height; + + x_offset = (float) hpixel_value; + y_offset = x_offset / scale_factor; + + viewPortOut->x = (gint) x_offset; + viewPortOut->y = (gint) y_offset; + viewPortOut->width = (gint) (raster_size.width - (2 * x_offset)); + viewPortOut->height = (gint) (raster_size.height - (2 * y_offset)); + + /* Limit ViewPortOut to a minimum size */ + viewPortOut->width = MAX(viewPortOut->width, 10); + viewPortOut->height = MAX(viewPortOut->height, 10); +} + + + +/** get_underscan_percent_from_viewportout() ************************** + * + * Retrieve underscan from the current ViewPortOut. + * + * We could just try to revert the formula, i.e. check that: + * viewPortOut.width + (2 * viewPortOut.x) == raster.width && + * viewPortOut.height + (2 * viewPortOut.y) == raster.height + * + * But this doesn't match the case where the initial ViewPortOut + * computation had rounding issues. + * + * Instead, we compute a new ViewPortOut from the current x offset + * and check that the result matches the current ViewPortOut. + * + * Returns the underscan value in percentage. Defaults to -1 if no + * underscan could be found. + * + */ +void get_underscan_settings_from_viewportout(const nvSize raster_size, + const GdkRectangle viewPortOut, + gfloat *percent_value, + gint *pixel_value) +{ + GdkRectangle dummyViewPortOut; + + if (!percent_value || !pixel_value) { + return; + } + + apply_underscan_to_viewportout(raster_size, + (int) viewPortOut.x, + &dummyViewPortOut); + + if (!memcmp(&viewPortOut, &dummyViewPortOut, sizeof(GdkRectangle))) { + *percent_value = (gfloat) viewPortOut.x / raster_size.width * 100; + *pixel_value = viewPortOut.x; + } else { + *percent_value = -1; + *pixel_value = -1; + } +} + + + /** mode_get_str() *************************************************** * * Returns the mode string of the given mode in the following format: @@ -818,10 +972,10 @@ static gchar *mode_get_str(nvModePtr mode, int be_generic) /* Panning domain */ - if (!be_generic || (mode->pan[W] != mode->viewPortIn[W] || - mode->pan[H] != mode->viewPortIn[H])) { + if (!be_generic || (mode->pan.width != mode->viewPortIn.width || + mode->pan.height != mode->viewPortIn.height)) { tmp = g_strdup_printf("%s @%dx%d", - mode_str, mode->pan[W], mode->pan[H]); + mode_str, mode->pan.width, mode->pan.height); g_free(mode_str); mode_str = tmp; } @@ -848,8 +1002,8 @@ static gchar *mode_get_str(nvModePtr mode, int be_generic) tmp = g_strdup_printf("%s +%d+%d", mode_str, /* Make mode position relative */ - mode->viewPortIn[X] - mode->metamode->edim[X], - mode->viewPortIn[Y] - mode->metamode->edim[Y]); + mode->pan.x - mode->metamode->edim.x, + mode->pan.y - mode->metamode->edim.y); g_free(mode_str); mode_str = tmp; @@ -945,34 +1099,35 @@ static gchar *mode_get_str(nvModePtr mode, int be_generic) */ if ((mode->rotation == ROTATION_90) || (mode->rotation == ROTATION_270)) { - width = mode->viewPortOut[H]; - height = mode->viewPortOut[W]; + width = mode->viewPortOut.height; + height = mode->viewPortOut.width; } else { - width = mode->viewPortOut[W]; - height = mode->viewPortOut[H]; + width = mode->viewPortOut.width; + height = mode->viewPortOut.height; } - if (mode->viewPortIn[W] && mode->viewPortIn[H] && - ((mode->viewPortIn[W] != width) || - (mode->viewPortIn[H] != height))) { + if (mode->viewPortIn.width && mode->viewPortIn.height && + ((mode->viewPortIn.width != width) || + (mode->viewPortIn.height != height))) { tmp = g_strdup_printf("%s, viewportin=%dx%d", (flags_str ? flags_str : ""), - mode->viewPortIn[W], mode->viewPortIn[H]); + mode->viewPortIn.width, + mode->viewPortIn.height); g_free(flags_str); flags_str = tmp; } } /* ViewPortOut */ - if (mode->viewPortOut[X] || - mode->viewPortOut[Y] || - (mode->viewPortOut[W] && mode->viewPortOut[H] && - ((mode->viewPortOut[W] != mode->modeline->data.hdisplay) || - (mode->viewPortOut[H] != mode->modeline->data.vdisplay)))) { + if (mode->viewPortOut.x || + mode->viewPortOut.y || + (mode->viewPortOut.width && mode->viewPortOut.height && + ((mode->viewPortOut.width != mode->modeline->data.hdisplay) || + (mode->viewPortOut.height != mode->modeline->data.vdisplay)))) { tmp = g_strdup_printf("%s, viewportout=%dx%d%+d%+d", (flags_str ? flags_str : ""), - mode->viewPortOut[W], mode->viewPortOut[H], - mode->viewPortOut[X], mode->viewPortOut[Y]); + mode->viewPortOut.width, mode->viewPortOut.height, + mode->viewPortOut.x, mode->viewPortOut.y); g_free(flags_str); flags_str = tmp; } @@ -1099,14 +1254,14 @@ int display_find_closest_mode_matching_modeline(nvDisplayPtr display, */ if (best_mode) { Bool current_match_vpin = - (mode->viewPortIn[W] == targetWidth && - mode->viewPortIn[H] == targetHeight); + (mode->viewPortIn.width == targetWidth && + mode->viewPortIn.height == targetHeight); Bool best_match_vpin = - (best_mode->viewPortIn[W] == targetWidth && - best_mode->viewPortIn[H] == targetHeight); + (best_mode->viewPortIn.width == targetWidth && + best_mode->viewPortIn.height == targetHeight); Bool best_match_vpout = - (best_mode->viewPortOut[W] == targetWidth && - best_mode->viewPortOut[H] == targetHeight); + (best_mode->viewPortOut.width == targetWidth && + best_mode->viewPortOut.height == targetHeight); /* Try to find reasons why we should prefer the * previous match over the currently considered @@ -1203,6 +1358,38 @@ Bool modelines_match(nvModeLinePtr modeline1, +/** viewport_in_match() ********************************************** + * + * Helper function that returns TRUE of FALSE based on whether + * the ViewPortIn arguments match each other. + * + **/ +Bool viewports_in_match(const nvSize viewPortIn1, + const nvSize viewPortIn2) +{ + return ((viewPortIn1.width == viewPortIn2.width) && + (viewPortIn1.height == viewPortIn2.height)); +} + + + +/** viewport_out_match() ********************************************* + * + * Helper function that returns TRUE of FALSE based on whether + * the ViewPortOut arguments match each other. + * + **/ +Bool viewports_out_match(const GdkRectangle viewPortOut1, + const GdkRectangle viewPortOut2) +{ + return ((viewPortOut1.x == viewPortOut2.x) && + (viewPortOut1.y == viewPortOut2.y) && + (viewPortOut1.width == viewPortOut2.width) && + (viewPortOut1.height == viewPortOut2.height)); +} + + + /** display_has_modeline() ******************************************* * * Helper function that returns TRUE or FALSE based on whether @@ -1448,6 +1635,23 @@ static void display_free(nvDisplayPtr display) /*****************************************************************************/ +/*! + * Clamps the given (screen) dimensions to the minimum allowed screen size. + * + * \param[in, out] rect The dimensions to clamp + */ +void clamp_screen_size_rect(GdkRectangle *rect) +{ + if (rect->width < 304) { + rect->width = 304; + } + if (rect->height < 200) { + rect->height = 200; + } +} + + + /** screen_find_named_display() ************************************** * * Finds a display named 'display_name' in the list of displays on the @@ -1927,10 +2131,6 @@ static Bool screen_add_metamode(nvScreenPtr screen, const char *metamode_str, /* Make sure each display has the right number of (NULL) modes */ screen_check_metamodes(screen); - /* Set the panning offset */ - mode->pan[X] = mode->viewPortIn[X]; - mode->pan[Y] = mode->viewPortIn[Y]; - /* Add the mode at the end of the display's mode list */ xconfigAddListItem((GenericListPtr *)(&display->modes), (GenericListPtr)mode); @@ -2013,10 +2213,8 @@ static Bool screen_check_metamodes(nvScreenPtr screen) /* Duplicate position information of the last mode */ if (last_mode) { - mode->viewPortIn[X] = last_mode->viewPortIn[X]; - mode->viewPortIn[Y] = last_mode->viewPortIn[Y]; - mode->pan[X] = last_mode->pan[X]; - mode->pan[Y] = last_mode->pan[Y]; + mode->pan.x = last_mode->pan.x; + mode->pan.y = last_mode->pan.y; mode->position_type = last_mode->position_type; mode->relative_to = last_mode->relative_to; } @@ -2066,11 +2264,8 @@ static void screen_assign_dummy_metamode_positions(nvScreenPtr screen) if (ok_mode) { for (mode = display->modes; mode; mode = mode->next) { if (!mode->dummy) continue; - mode->viewPortIn[X] = ok_mode->viewPortIn[X]; - mode->viewPortIn[Y] = ok_mode->viewPortIn[Y]; - - mode->pan[X] = ok_mode->viewPortIn[X]; - mode->pan[Y] = ok_mode->viewPortIn[Y]; + mode->pan.x = ok_mode->pan.x; + mode->pan.y = ok_mode->pan.y; } } } @@ -2667,7 +2862,10 @@ Bool gpu_add_screenless_modes_to_displays(nvGpuPtr gpu) mode->display = display; mode->dummy = 1; - mode_set_dims_from_modeline(mode, NULL); + mode_set_modeline(mode, + NULL /* modeline */, + NULL /* viewPortIn */, + NULL /* viewPortOut */); /* Add the mode to the display */ display->modes = mode; @@ -3198,8 +3396,8 @@ static Bool layout_add_screen_from_server(nvLayoutPtr layout, screen->depth = NvCtrlGetScreenPlanes(screen->handle); /* Initialize the virtual X screen size */ - screen->dim[W] = NvCtrlGetScreenWidth(screen->handle); - screen->dim[H] = NvCtrlGetScreenHeight(screen->handle); + screen->dim.width = NvCtrlGetScreenWidth(screen->handle); + screen->dim.height = NvCtrlGetScreenHeight(screen->handle); /* Add the screen to the layout */ layout_add_screen(layout, screen); diff --git a/src/gtk+-2.x/ctkdisplayconfig-utils.h b/src/gtk+-2.x/ctkdisplayconfig-utils.h index 14252aa..8b31f69 100644 --- a/src/gtk+-2.x/ctkdisplayconfig-utils.h +++ b/src/gtk+-2.x/ctkdisplayconfig-utils.h @@ -33,13 +33,6 @@ G_BEGIN_DECLS /* Token parsing handlers */ -typedef struct _ScreenInfo { - int x; - int y; - int width; - int height; -} ScreenInfo; - void apply_modeline_token(char *token, char *value, void *data); void apply_metamode_token(char *token, char *value, void *data); void apply_monitor_token(char *token, char *value, void *data); @@ -48,9 +41,22 @@ void apply_screen_info_token(char *token, char *value, void *data); /* Mode functions */ -void mode_set_dims_from_modeline(nvModePtr mode, nvModeLinePtr modeline); +void clamp_rect_to_viewportin(GdkRectangle *rect, const nvMode *mode); +void clamp_mode_panning(nvModePtr mode); +void get_viewportin_rect(const nvMode *mode, GdkRectangle *rect); +void mode_set_modeline(nvModePtr mode, + nvModeLinePtr modeline, + const nvSize *providedViewPortIn, + const GdkRectangle *providedViewPortOut); Bool mode_set_rotation(nvModePtr mode, Rotation rotation); nvModePtr mode_parse(nvDisplayPtr display, const char *mode_str); +void apply_underscan_to_viewportout(const nvSize raster_size, + const int hpixel_value, + GdkRectangle *viewPortOut); +void get_underscan_settings_from_viewportout(const nvSize raster_size, + const GdkRectangle viewPortOut, + gfloat *percent_value, + gint *pixel_value); /* ModeLine functions */ @@ -59,6 +65,15 @@ void modeline_free(nvModeLinePtr m); +/* ViewPort functions */ + +Bool viewports_in_match(const nvSize viewPortIn1, + const nvSize viewPortIn2); +Bool viewports_out_match(const GdkRectangle viewPortOut1, + const GdkRectangle viewPortOut2); + + + /* Display functions */ int display_find_closest_mode_matching_modeline(nvDisplayPtr display, @@ -75,6 +90,7 @@ Bool display_set_modes_rotation(nvDisplayPtr display, Rotation rotation); /* Screen functions */ +void clamp_screen_size_rect(GdkRectangle *rect); void renumber_xscreens(nvLayoutPtr layout); void screen_unlink_display(nvDisplayPtr display); void screen_link_display(nvScreenPtr screen, nvDisplayPtr display); diff --git a/src/gtk+-2.x/ctkdisplayconfig.c b/src/gtk+-2.x/ctkdisplayconfig.c index a0c5c01..b9797f5 100644 --- a/src/gtk+-2.x/ctkdisplayconfig.c +++ b/src/gtk+-2.x/ctkdisplayconfig.c @@ -33,6 +33,8 @@ #include "parse.h" #include "lscf.h" +#include "nvvr.h" + #include "ctkutils.h" #include "ctkbanner.h" @@ -64,6 +66,10 @@ static void display_stereo_changed(GtkWidget *widget, gpointer user_data); static void display_rotation_changed(GtkWidget *widget, gpointer user_data); static void display_reflection_changed(GtkWidget *widget, gpointer user_data); +static void display_underscan_value_changed(GtkAdjustment *adjustment, + gpointer user_data); +static void display_underscan_activate(GtkWidget *widget, gpointer user_data); + static void display_position_type_changed(GtkWidget *widget, gpointer user_data); static void display_position_offset_activate(GtkWidget *widget, gpointer user_data); static void display_position_relative_changed(GtkWidget *widget, gpointer user_data); @@ -149,6 +155,10 @@ typedef struct SwitchModeCallbackInfoRec { #define DPY_CFG_SEPARATE_X_SCREEN 1 #define DPY_CFG_TWINVIEW 2 +/* Underscan range of values */ +#define UNDERSCAN_MIN_PERCENT 0 +#define UNDERSCAN_MAX_PERCENT 35 + /*** G L O B A L S ***********************************************************/ static int __position_table[] = { CONF_ADJ_ABSOLUTE, @@ -221,6 +231,10 @@ static const char * __dpy_position_relative_help = "relative to. This is only available when multiple display " "devices are present."; +static const char * __dpy_underscan_text_help = +"The Underscan feature allows configuration of an underscan border " +"(in pixels) around the ViewPortOut."; + static const char * __dpy_position_offset_help = "The Position Offset identifies the top left of the display device " "as an offset from the top left of the X screen position. This is only " @@ -322,11 +336,11 @@ static void get_cur_screen_pos(CtkDisplayConfig *ctk_object) { nvScreenPtr screen = ctk_display_layout_get_selected_screen (CTK_DISPLAY_LAYOUT(ctk_object->obj_layout)); - + if (!screen) return; - ctk_object->cur_screen_pos[X] = screen->dim[X]; - ctk_object->cur_screen_pos[Y] = screen->dim[Y]; + ctk_object->cur_screen_pos.x = screen->dim.x; + ctk_object->cur_screen_pos.y = screen->dim.y; } /* get_cur_screen_pos() */ @@ -341,17 +355,17 @@ static void get_cur_screen_pos(CtkDisplayConfig *ctk_object) static void check_screen_pos_changed(CtkDisplayConfig *ctk_object) { - int old_dim[2]; + GdkPoint old_pos; /* Cache the old position */ - old_dim[X] = ctk_object->cur_screen_pos[X]; - old_dim[Y] = ctk_object->cur_screen_pos[Y]; + old_pos.x = ctk_object->cur_screen_pos.x; + old_pos.y = ctk_object->cur_screen_pos.y; /* Get the new position */ get_cur_screen_pos(ctk_object); - if (old_dim[X] != ctk_object->cur_screen_pos[X] || - old_dim[Y] != ctk_object->cur_screen_pos[Y]) { + if (old_pos.x != ctk_object->cur_screen_pos.x || + old_pos.y != ctk_object->cur_screen_pos.y) { ctk_object->apply_possible = FALSE; } @@ -640,17 +654,17 @@ static int generate_xconf_metamode_str(CtkDisplayConfig *ctk_object, metamode_strs = screen_get_metamode_str(screen, screen->cur_metamode_idx, 1); len = strlen(metamode_strs); - start_width = screen->cur_metamode->edim[W]; - start_height = screen->cur_metamode->edim[H]; + start_width = screen->cur_metamode->edim.width; + start_height = screen->cur_metamode->edim.height; } else { - start_width = screen->metamodes->edim[W]; - start_height = screen->metamodes->edim[H]; + start_width = screen->metamodes->edim.width; + start_height = screen->metamodes->edim.height; } for (metamode_idx = 0, metamode = screen->metamodes; (metamode_idx < screen->num_metamodes) && metamode; metamode_idx++, metamode = metamode->next) { - + int metamode_len; /* Only write out metamodes that were specified by the user */ @@ -670,21 +684,21 @@ static int generate_xconf_metamode_str(CtkDisplayConfig *ctk_object, * in an unwanted panning domain being setup for the first mode. */ if ((!ctk_object->advanced_mode) && - ((metamode->edim[W] > start_width) || - (metamode->edim[H] > start_height))) + ((metamode->edim.width > start_width) || + (metamode->edim.height > start_height))) continue; - + metamode_str = screen_get_metamode_str(screen, metamode_idx, 1); if (!metamode_str) continue; - + metamode_len = strlen(metamode_str); if (!longStringsOK && (len + metamode_len > 900)) { GtkWidget *dlg; gchar *msg; GtkWidget *parent; gint result; - + msg = g_strdup_printf ("Truncate the MetaMode list?\n" "\n" @@ -758,9 +772,10 @@ static int generate_xconf_metamode_str(CtkDisplayConfig *ctk_object, * * Assign the initial position of the X screens. * - * - If Xinerama is enabled, query the XINERAMA_SCREEN_INFO. + * - If Xinerama is enabled or the X server ABI >= 12, + query to the SCREEN_RECTANGLE returns position. * - * - If Xinerama is disabled, assume "right-of" orientation. (bleh!) + * - Otherwise assume "right-of" orientation. * **/ @@ -769,28 +784,20 @@ static void assign_screen_positions(CtkDisplayConfig *ctk_object) nvLayoutPtr layout = ctk_object->layout; nvScreenPtr prev_screen = NULL; nvScreenPtr screen; - int xinerama; - int initialize = 0; char *screen_info; - ScreenInfo screen_parsed_info; + GdkRectangle screen_parsed_info; ReturnStatus ret; - /* If xinerama is enabled, we can get the screen size! */ - ret = NvCtrlGetAttribute(ctk_object->handle, NV_CTRL_XINERAMA, &xinerama); - if (ret != NvCtrlSuccess) { - initialize = 1; /* Fallback to right-of positioning */ - } - /* Setup screen positions */ for (screen = layout->screens; screen; screen = screen->next_in_layout) { screen_info = NULL; - if (screen->handle && !initialize) { + if (screen->handle) { ret = NvCtrlGetStringAttribute (screen->handle, - NV_CTRL_STRING_XINERAMA_SCREEN_INFO, + NV_CTRL_STRING_SCREEN_RECTANGLE, &screen_info); if (ret != NvCtrlSuccess) { @@ -1443,6 +1450,31 @@ GtkWidget* ctk_display_config_new(NvCtrlAttributeHandle *handle, "changed", G_CALLBACK(display_reflection_changed), (gpointer) ctk_object); + /* Display Underscan text box and slider */ + ctk_object->txt_display_underscan = gtk_entry_new_with_max_length(6); + gtk_entry_set_width_chars(GTK_ENTRY(ctk_object->txt_display_underscan), 6); + gtk_entry_set_text(GTK_ENTRY(ctk_object->txt_display_underscan), "0"); + ctk_config_set_tooltip(ctk_config, ctk_object->txt_display_underscan, + __dpy_underscan_text_help); + g_signal_connect(G_OBJECT(ctk_object->txt_display_underscan), "activate", + G_CALLBACK(display_underscan_activate), + (gpointer) ctk_object); + + ctk_object->adj_display_underscan = + gtk_adjustment_new(0, + UNDERSCAN_MIN_PERCENT, + UNDERSCAN_MAX_PERCENT, + 1, 1, 0.0); + ctk_object->sld_display_underscan = + gtk_hscale_new(GTK_ADJUSTMENT(ctk_object->adj_display_underscan)); + gtk_scale_set_draw_value(GTK_SCALE(ctk_object->sld_display_underscan), + FALSE); + ctk_config_set_tooltip(ctk_config, ctk_object->sld_display_underscan, + __dpy_underscan_text_help); + g_signal_connect(G_OBJECT(ctk_object->adj_display_underscan), "value_changed", + G_CALLBACK(display_underscan_value_changed), + (gpointer) ctk_object); + /* Display Position Type (Absolute/Relative Menu) */ ctk_object->mnu_display_position_type = gtk_option_menu_new(); menu = gtk_menu_new(); @@ -1564,6 +1596,8 @@ GtkWidget* ctk_display_config_new(NvCtrlAttributeHandle *handle, gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); menu_item = gtk_menu_item_new_with_label("NVIDIA 3D Vision Pro"); gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); + menu_item = gtk_menu_item_new_with_label("HDMI 3D"); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); gtk_option_menu_set_menu (GTK_OPTION_MENU(ctk_object->mnu_screen_stereo), menu); ctk_config_set_tooltip(ctk_config, ctk_object->mnu_screen_stereo, @@ -1858,6 +1892,27 @@ GtkWidget* ctk_display_config_new(NvCtrlAttributeHandle *handle, ctk_object->box_display_orientation = hbox2; } + /* Display underscan */ + { + GtkWidget *hbox2 = gtk_hbox_new(TRUE, 0); + label = gtk_label_new("Underscan:"); + labels = g_slist_append(labels, label); + + hbox = gtk_hbox_new(FALSE, 5); + gtk_box_pack_start(GTK_BOX(hbox2), hbox, FALSE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 5); + gtk_box_pack_start(GTK_BOX(hbox), + ctk_object->txt_display_underscan, + FALSE, FALSE, 0); + + gtk_box_pack_start(GTK_BOX(hbox), + ctk_object->sld_display_underscan, + TRUE, TRUE, 3); + + gtk_box_pack_start(GTK_BOX(vbox), hbox2, FALSE, TRUE, 0); + ctk_object->box_display_underscan = hbox2; + } + /* Display positioning */ label = gtk_label_new("Position:"); labels = g_slist_append(labels, label); @@ -2180,6 +2235,11 @@ GtkTextBuffer *ctk_display_config_create_help(GtkTextTagTable *table, "image is rotated and/or reflected. %s %s Note that " "reflection is applied before rotation.", __dpy_rotation_help, __dpy_reflection_help); + ctk_help_heading(b, &i, "Underscan"); + ctk_help_para(b, &i, "%s The aspect ratio of the ViewPortOut is preserved " + " and the ViewPortIn is updated to exactly match this new " + "size. This feature is formerly known as Overscan " + "Compensation.", __dpy_underscan_text_help); ctk_help_heading(b, &i, "Position Type"); ctk_help_para(b, &i, __dpy_position_type_help); ctk_help_heading(b, &i, "Position Relative"); @@ -2834,6 +2894,31 @@ static void setup_display_refresh_dropdown(CtkDisplayConfig *ctk_object) +/** get_default_modeline() ******************************************* + * + * Finds the default modeline in the list of modelines. + * + * Returns the default modeline if found, NULL otherwise. + * + */ + +static nvModeLinePtr get_default_modeline(const nvDisplayPtr display) +{ + nvModeLinePtr modeline = display->modelines; + + while (modeline) { + if (IS_NVIDIA_DEFAULT_MODE(modeline)) { + return modeline; + } + + modeline = modeline->next; + } + + return NULL; +} + + + /** allocate_selected_mode() ***************************************** * * Allocates, fills and returns a nvSelectedModePtr. @@ -2843,7 +2928,9 @@ static void setup_display_refresh_dropdown(CtkDisplayConfig *ctk_object) static nvSelectedModePtr allocate_selected_mode(char *name, nvModeLinePtr modeline, - Bool isSpecial) + Bool isSpecial, + NVVRSize *viewPortIn, + NVVRBoxRecXYWH *viewPortOut) { nvSelectedModePtr selected_mode; @@ -2853,6 +2940,19 @@ allocate_selected_mode(char *name, selected_mode->modeline = modeline; selected_mode->isSpecial = isSpecial; + selected_mode->isScaled = (viewPortIn || viewPortOut); + + if (viewPortIn) { + selected_mode->viewPortIn.width = viewPortIn->w; + selected_mode->viewPortIn.height = viewPortIn->h; + } + + if (viewPortOut) { + selected_mode->viewPortOut.x = viewPortOut->x; + selected_mode->viewPortOut.y = viewPortOut->y; + selected_mode->viewPortOut.width = viewPortOut->w; + selected_mode->viewPortOut.height = viewPortOut->h; + } return selected_mode; } @@ -2880,8 +2980,9 @@ free_selected_modes(nvSelectedModePtr selected_mode) * * Appends a selected mode to the given list only if it doesn't already exist. * Special modes ("Auto", "Off") are not checked. Two selected modes are unique - * if their [hv]display differ. Returns TRUE if the selected mode has been - * added, FALSE otherwise. + * if their [hv]display differ in the case of regular modes, or if the + * ViewPortIn of the given mode doesn't match any existing [hv]display. + * Returns TRUE if the selected mode has been added, FALSE otherwise. * */ @@ -2889,21 +2990,47 @@ static Bool append_unique_selected_mode(nvSelectedModePtr head, const nvSelectedModePtr mode) { + int targetWidth, targetHeight; nvSelectedModePtr iter, prev = NULL; - nvModeLinePtr ml1 = mode->modeline; + if (mode->isScaled) { + targetWidth = mode->viewPortIn.width; + targetHeight = mode->viewPortIn.height; + } else { + targetWidth = mode->modeline->data.hdisplay; + targetHeight = mode->modeline->data.vdisplay; + } + + /* Keep the list sorted by targetted resolution */ iter = head; - while (iter) - { - nvModeLinePtr ml2 = iter->modeline; + while (iter) { + int currentWidth, currentHeight; + nvModeLinePtr ml = iter->modeline; + + if (!ml || iter->isSpecial) { + goto next; + } - if (ml1 && ml2 && - !iter->isSpecial && !mode->isSpecial && - (ml1->data.hdisplay == ml2->data.hdisplay) && - (ml1->data.vdisplay == ml2->data.vdisplay)) { + if (iter->isScaled) { + currentWidth = iter->viewPortIn.width; + currentHeight = iter->viewPortIn.height; + } else { + currentWidth = ml->data.hdisplay; + currentHeight = ml->data.vdisplay; + } + + /* If we are past the sort order, stop looping */ + if ((targetWidth > currentWidth) || + ((targetWidth == currentWidth) && (targetHeight > currentHeight))) { + break; + } + + if (ml && !mode->isSpecial && + (targetWidth == currentWidth) && (targetHeight == currentHeight)) { return FALSE; } +next: prev = iter; iter = iter->next; } @@ -2912,6 +3039,8 @@ append_unique_selected_mode(nvSelectedModePtr head, return FALSE; } + /* Insert the selected mode */ + mode->next = prev->next; prev->next = mode; return TRUE; @@ -2923,37 +3052,76 @@ append_unique_selected_mode(nvSelectedModePtr head, * * Checks whether the provided selected mode matches the current mode. * + * We need to distinguish between custom modes and scaled modes. + * + * Custom modes are modes with custom ViewPort settings, such as an + * Underscan configuration. These modes don't have an entry in the + * resolution dropdown menu. Instead, the corresponding modeline must be + * selected. + * + * Scaled modes are generated by the CPL, have a fixed ViewPort{In,Out} + * configuration and are displayed in the dropdown menu in basic mode. + * + * Therefore, we compare the raster size and the ViewPorts first, then only + * the raster size. This works because the list of selected_modes is + * generated before the scaled ones. The latter can then overwrite the + * cur_selected_mode if we find a better match. + * * Returns TRUE if the provided selected mode matches the current mode, FALSE * otherwise. * */ static Bool matches_current_selected_mode(const nvDisplayPtr display, - const nvSelectedModePtr selected_mode) + const nvSelectedModePtr selected_mode, + const Bool compare_viewports) { nvModeLinePtr ml1, ml2; + nvModePtr cur_mode; + Bool mode_match; if (!display || !display->cur_mode || !selected_mode) { return FALSE; } - ml1 = display->cur_mode->modeline; + cur_mode = display->cur_mode; + ml1 = cur_mode->modeline; ml2 = selected_mode->modeline; if (!ml1 || !ml2) { return FALSE; } - return (!IS_NVIDIA_DEFAULT_MODE(ml1) && - (ml1->data.hdisplay == ml2->data.hdisplay) && - (ml1->data.vdisplay == ml2->data.vdisplay)); + mode_match = ((ml1->data.hdisplay == ml2->data.hdisplay) && + (ml1->data.vdisplay == ml2->data.vdisplay)); + + if (compare_viewports) { + nvSize rotatedViewPortIn; + + memcpy(&rotatedViewPortIn, &selected_mode->viewPortIn, sizeof(nvSize)); + + if (cur_mode->rotation == ROTATION_90 || + cur_mode->rotation == ROTATION_270) { + int temp = rotatedViewPortIn.width; + rotatedViewPortIn.width = rotatedViewPortIn.height; + rotatedViewPortIn.height = temp; + } + + return (mode_match && + viewports_in_match(cur_mode->viewPortIn, + rotatedViewPortIn) && + viewports_out_match(cur_mode->viewPortOut, + selected_mode->viewPortOut)); + } else { + return (!IS_NVIDIA_DEFAULT_MODE(ml1) && mode_match); + } } /** generate_selected_modes() **************************************** * - * Generates a list of selected mode. The list is generated by parsing + * Generates a list of selected modes. The list is generated by parsing * modelines. This function makes sure that each item of the list is unique * and sorted. * @@ -2967,7 +3135,9 @@ static void generate_selected_modes(const nvDisplayPtr display) /* Add the off item */ selected_mode = allocate_selected_mode("Off", NULL /* modeline */, - TRUE /* isSpecial */); + TRUE /* isSpecial */, + NULL /* viewPortIn */, + NULL /* viewPortOut */); display->num_selected_modes = 1; display->selected_modes = selected_mode; @@ -2987,14 +3157,17 @@ static void generate_selected_modes(const nvDisplayPtr display) isSpecial = FALSE; } - selected_mode = allocate_selected_mode(name, modeline, isSpecial); + selected_mode = allocate_selected_mode(name, modeline, isSpecial, + NULL /* viewPortIn */, + NULL /* viewPortOut */); g_free(name); if (append_unique_selected_mode(display->selected_modes, selected_mode)) { display->num_selected_modes++; - if (matches_current_selected_mode(display, selected_mode)) { + if (matches_current_selected_mode(display, selected_mode, + FALSE /* compare_viewports */)) { display->cur_selected_mode = selected_mode; } } else { @@ -3007,6 +3180,76 @@ static void generate_selected_modes(const nvDisplayPtr display) +/** generate_scaled_selected_modes() ********************************* + * + * Appends a list of scaled selected modes. The list is generated by parsing + * an array of common resolutions. This function makes sure that each item + * of the list is unique and sorted. The generated items are appended to the + * list of selected modes returned by generate_selected_modes(). + * + */ + +static void generate_scaled_selected_modes(const nvDisplayPtr display) +{ + int resIndex; + nvModeLinePtr default_modeline; + nvSelectedModePtr selected_mode = NULL; + const NVVRSize *commonResolutions; + NVVRSize raster; + gchar *name; + + if (!display || !display->modelines) { + return; + } + + default_modeline = get_default_modeline(display); + if (default_modeline == NULL) { + return; + } + + raster.w = default_modeline->data.hdisplay; + raster.h = default_modeline->data.vdisplay; + + commonResolutions = NVVRGetCommonResolutions(); + + resIndex = 0; + while ((commonResolutions[resIndex].w != -1) && + (commonResolutions[resIndex].h != -1)) { + NVVRBoxRecXYWH viewPortOut; + NVVRSize viewPortIn = commonResolutions[resIndex]; + + resIndex++; + + /* Skip resolutions that are bigger than the maximum raster size */ + if ((viewPortIn.w > raster.w) || (viewPortIn.h > raster.h)) { + continue; + } + + viewPortOut = NVVRGetScaledViewPortOut(&raster, &viewPortIn, + NVVR_SCALING_ASPECT_SCALED); + + name = g_strdup_printf("%dx%d (scaled)", viewPortIn.w, viewPortIn.h); + selected_mode = allocate_selected_mode(name, default_modeline, + FALSE /* isSpecial */, + &viewPortIn, &viewPortOut); + g_free(name); + + if (append_unique_selected_mode(display->selected_modes, + selected_mode)) { + display->num_selected_modes++; + + if (matches_current_selected_mode(display, selected_mode, + TRUE /* compare_viewports */)) { + display->cur_selected_mode = selected_mode; + } + } else { + free(selected_mode); + } + } +} + + + /** setup_display_resolution_dropdown() ****************************** * * Generates the resolution dropdown based on the currently selected @@ -3041,6 +3284,10 @@ static void setup_display_resolution_dropdown(CtkDisplayConfig *ctk_object) display->cur_selected_mode = NULL; generate_selected_modes(display); + if (!ctk_object->advanced_mode) { + generate_scaled_selected_modes(display); + } + if (ctk_object->resolution_table) { free(ctk_object->resolution_table); ctk_object->resolution_table_len = 0; @@ -3239,19 +3486,6 @@ static void setup_display_rotation_dropdown(CtkDisplayConfig *ctk_object) (CTK_DISPLAY_LAYOUT(ctk_object->obj_layout)); int idx; - if (!display->screen) { - gtk_widget_hide(ctk_object->box_display_orientation); - return; - } - gtk_widget_show(ctk_object->box_display_orientation); - - if (!display->cur_mode || !display->cur_mode->modeline || - !are_display_composition_transformations_allowed(display->screen)) { - gtk_widget_set_sensitive(ctk_object->mnu_display_rotation, FALSE); - return; - } - gtk_widget_set_sensitive(ctk_object->mnu_display_rotation, TRUE); - /* Set the selected rotation */ g_signal_handlers_block_by_func (G_OBJECT(ctk_object->mnu_display_rotation), @@ -3298,19 +3532,6 @@ static void setup_display_reflection_dropdown(CtkDisplayConfig *ctk_object) (CTK_DISPLAY_LAYOUT(ctk_object->obj_layout)); int idx; - if (!display->screen) { - gtk_widget_hide(ctk_object->box_display_orientation); - return; - } - gtk_widget_show(ctk_object->box_display_orientation); - - if (!display->cur_mode || !display->cur_mode->modeline || - !are_display_composition_transformations_allowed(display->screen)) { - gtk_widget_set_sensitive(ctk_object->mnu_display_reflection, FALSE); - return; - } - gtk_widget_set_sensitive(ctk_object->mnu_display_reflection, TRUE); - /* Set the selected reflection */ g_signal_handlers_block_by_func (G_OBJECT(ctk_object->mnu_display_reflection), @@ -3344,6 +3565,119 @@ static void setup_display_reflection_dropdown(CtkDisplayConfig *ctk_object) +/** setup_display_orientation() ************************************** + * + * Sets up the display orientation section to reflect the rotation + * and reflection settings for the currently selected display. + * + **/ + +static void setup_display_orientation(CtkDisplayConfig *ctk_object) +{ + nvDisplayPtr display = ctk_display_layout_get_selected_display + (CTK_DISPLAY_LAYOUT(ctk_object->obj_layout)); + + + /* Display needs to be included in an X screen to show widgets */ + if (!display || !display->screen) { + gtk_widget_hide(ctk_object->box_display_orientation); + return; + } + gtk_widget_show(ctk_object->box_display_orientation); + + /* If the display is off, disable the orientation widgets */ + if (!display->cur_mode || !display->cur_mode->modeline || + !are_display_composition_transformations_allowed(display->screen)) { + gtk_widget_set_sensitive(ctk_object->box_display_orientation, FALSE); + return; + } + gtk_widget_set_sensitive(ctk_object->box_display_orientation, TRUE); + + /* Setup the display orientation widgets */ + setup_display_rotation_dropdown(ctk_object); + setup_display_reflection_dropdown(ctk_object); +} + + + +/** setup_display_underscan() ***************************************** + * + * Sets up the display underscan to reflect the ViewPortOut settings + * for the currently selected display. + * + * Tries to detect whether the current ViewPortOut configuration + * corresponds to a border; then sets the underscan text entry and + * slider accordingly. Defaults to 0. + **/ + +static void setup_display_underscan(CtkDisplayConfig *ctk_object) +{ + nvDisplayPtr display = ctk_display_layout_get_selected_display + (CTK_DISPLAY_LAYOUT(ctk_object->obj_layout)); + nvModePtr cur_mode; + nvSize raster_size; + gfloat adj_value; + gint hpixel_value; + gchar *txt_entry; + + if (!display || !display->screen || ctk_object->advanced_mode) { + gtk_widget_hide(ctk_object->box_display_underscan); + return; + } + gtk_widget_show(ctk_object->box_display_underscan); + + cur_mode = display->cur_mode; + + /* + * If the display is off or a scaled mode is selected, disable the + * underscan widget. + */ + if (!cur_mode || !cur_mode->modeline || + (display->cur_selected_mode && display->cur_selected_mode->isScaled)) { + gtk_widget_set_sensitive(ctk_object->box_display_underscan, FALSE); + return; + } + gtk_widget_set_sensitive(ctk_object->box_display_underscan, TRUE); + + raster_size.height = cur_mode->modeline->data.vdisplay; + raster_size.width = cur_mode->modeline->data.hdisplay; + + get_underscan_settings_from_viewportout(raster_size, cur_mode->viewPortOut, + &adj_value, &hpixel_value); + + /* Setup the slider */ + g_signal_handlers_block_by_func + (G_OBJECT(ctk_object->adj_display_underscan), + G_CALLBACK(display_underscan_value_changed), (gpointer) ctk_object); + + gtk_adjustment_set_value(GTK_ADJUSTMENT(ctk_object->adj_display_underscan), + (adj_value < 0) ? 0 : adj_value); + + g_signal_handlers_unblock_by_func + (G_OBJECT(ctk_object-> adj_display_underscan), + G_CALLBACK(display_underscan_value_changed), (gpointer) ctk_object); + + + /* Setup the text entry */ + g_signal_handlers_block_by_func + (G_OBJECT(ctk_object->txt_display_underscan), + G_CALLBACK(display_underscan_activate), (gpointer) ctk_object); + + if (hpixel_value < 0) { + txt_entry = g_strdup_printf("n/a"); + } else { + txt_entry = g_strdup_printf("%d", hpixel_value); + } + gtk_entry_set_text(GTK_ENTRY(ctk_object->txt_display_underscan), txt_entry); + g_free(txt_entry); + + g_signal_handlers_unblock_by_func + (G_OBJECT(ctk_object-> txt_display_underscan), + G_CALLBACK(display_underscan_activate), (gpointer) ctk_object); +} /* setup_display_underscan() */ + + + /** setup_display_viewport_in() ************************************** * * Sets up the display ViewPortIn text entry to reflect the currently @@ -3374,8 +3708,8 @@ static void setup_display_viewport_in(CtkDisplayConfig *ctk_object) mode = display->cur_mode; tmp_str = g_strdup_printf("%dx%d", - mode->viewPortIn[W], - mode->viewPortIn[H]); + mode->viewPortIn.width, + mode->viewPortIn.height); gtk_entry_set_text(GTK_ENTRY(ctk_object->txt_display_viewport_in), tmp_str); @@ -3417,10 +3751,10 @@ static void setup_display_viewport_out(CtkDisplayConfig *ctk_object) mode = display->cur_mode; tmp_str = g_strdup_printf("%dx%d%+d%+d", - mode->viewPortOut[W], - mode->viewPortOut[H], - mode->viewPortOut[X], - mode->viewPortOut[Y]); + mode->viewPortOut.width, + mode->viewPortOut.height, + mode->viewPortOut.x, + mode->viewPortOut.y); gtk_entry_set_text(GTK_ENTRY(ctk_object->txt_display_viewport_out), tmp_str); @@ -3610,8 +3944,8 @@ static void setup_display_position_offset(CtkDisplayConfig *ctk_object) mode = display->cur_mode; tmp_str = g_strdup_printf("%+d%+d", - mode->viewPortIn[X] - mode->metamode->edim[X], - mode->viewPortIn[Y] - mode->metamode->edim[Y]); + mode->pan.x - mode->metamode->edim.x, + mode->pan.y - mode->metamode->edim.y); gtk_entry_set_text(GTK_ENTRY(ctk_object->txt_display_position_offset), tmp_str); @@ -3741,7 +4075,7 @@ static void setup_display_panning(CtkDisplayConfig *ctk_object) /* Update the panning text */ mode = display->cur_mode; - tmp_str = g_strdup_printf("%dx%d", mode->pan[W], mode->pan[H]); + tmp_str = g_strdup_printf("%dx%d", mode->pan.width, mode->pan.height); gtk_entry_set_text(GTK_ENTRY(ctk_object->txt_display_panning), tmp_str); @@ -3753,8 +4087,8 @@ static void setup_display_panning(CtkDisplayConfig *ctk_object) /** setup_display_page() ******************************************** * - * Sets up the display frame to reflect the currently selected - * display. + * Updates the display frame to reflect the current state of the + * currently selected display. * **/ @@ -3782,8 +4116,8 @@ static void setup_display_page(CtkDisplayConfig *ctk_object) setup_display_modename(ctk_object); setup_display_resolution_dropdown(ctk_object); setup_display_stereo_dropdown(ctk_object); - setup_display_rotation_dropdown(ctk_object); - setup_display_reflection_dropdown(ctk_object); + setup_display_orientation(ctk_object); + setup_display_underscan(ctk_object); setup_display_viewport_in(ctk_object); setup_display_viewport_out(ctk_object); setup_display_position(ctk_object); @@ -3819,12 +4153,12 @@ static void setup_screen_virtual_size(CtkDisplayConfig *ctk_object) /* Update the virtual size text */ - tmp_str = g_strdup_printf("%dx%d", screen->dim[W], screen->dim[H]); + tmp_str = g_strdup_printf("%dx%d", screen->dim.width, screen->dim.height); gtk_entry_set_text(GTK_ENTRY(ctk_object->txt_screen_virtual_size), tmp_str); g_free(tmp_str); - + } /* setup_screen_virtual_size() */ @@ -4178,7 +4512,7 @@ static void setup_screen_position_offset(CtkDisplayConfig *ctk_object) /* Update the position text */ - tmp_str = g_strdup_printf("%+d%+d", screen->dim[X], screen->dim[Y]); + tmp_str = g_strdup_printf("%+d%+d", screen->dim.x, screen->dim.y); gtk_entry_set_text(GTK_ENTRY(ctk_object->txt_screen_position_offset), tmp_str); @@ -4305,7 +4639,7 @@ static void setup_screen_page(CtkDisplayConfig *ctk_object) **/ static gint validation_fix_crowded_metamodes(CtkDisplayConfig *ctk_object, - nvScreenPtr screen) + nvScreenPtr screen) { nvDisplayPtr display; nvModePtr first_mode = NULL; @@ -4350,7 +4684,10 @@ static gint validation_fix_crowded_metamodes(CtkDisplayConfig *ctk_object, if (num > screen->gpu->max_displays) { ctk_display_layout_set_mode_modeline (CTK_DISPLAY_LAYOUT(ctk_object->obj_layout), - mode, NULL); + mode, + NULL /* modeline */, + NULL /* viewPortIn */, + NULL /* viewPortOut */); nv_info_msg(TAB, "Setting display device '%s' as Off " "for MetaMode %d on Screen %d. (There are " @@ -4388,7 +4725,9 @@ static gint validation_fix_crowded_metamodes(CtkDisplayConfig *ctk_object, ctk_display_layout_set_mode_modeline (CTK_DISPLAY_LAYOUT(ctk_object->obj_layout), first_mode, - first_mode->display->modelines); + first_mode->display->modelines, + NULL /* viewPortIn */, + NULL /* viewPortOut */); nv_info_msg(TAB, "Activating display device '%s' for MetaMode " "%d on Screen %d. (Minimally, a Screen must have " @@ -4844,12 +5183,11 @@ static void do_enable_display_for_xscreen(CtkDisplayConfig *ctk_object, /* Setup the mode */ mode = display->modes; - - mode->modeline = display->modelines; mode->metamode = metamode; - /* XXX Hopefully display->modelines is 'nvidia-auto-select' */ - mode_set_dims_from_modeline(mode, display->modelines); + mode_set_modeline(mode, display->modelines, + NULL /* viewPortIn */, + NULL /* viewPortOut */); mode->position_type = CONF_ADJ_ABSOLUTE; @@ -4875,8 +5213,8 @@ static void do_enable_display_for_xscreen(CtkDisplayConfig *ctk_object, /* Compute the right-most screen */ for (other = layout->screens; other; other = other->next_in_layout) { if (!rightmost || - ((other->dim[X] + other->dim[W]) > - (rightmost->dim[X] + rightmost->dim[W]))) { + ((other->dim.x + other->dim.width) > + (rightmost->dim.x + rightmost->dim.width))) { rightmost = other; } } @@ -4885,14 +5223,14 @@ static void do_enable_display_for_xscreen(CtkDisplayConfig *ctk_object, if (rightmost) { screen->position_type = CONF_ADJ_RIGHTOF; screen->relative_to = rightmost; - screen->dim[X] = mode->viewPortIn[X] = rightmost->dim[X]; - screen->dim[Y] = mode->viewPortIn[Y] = rightmost->dim[Y]; + screen->dim.x = mode->pan.x = rightmost->dim.x; + screen->dim.y = mode->pan.y = rightmost->dim.y; } else { screen->position_type = CONF_ADJ_ABSOLUTE; screen->relative_to = NULL; - screen->dim[X] = mode->viewPortIn[X]; - screen->dim[Y] = mode->viewPortIn[Y]; + screen->dim.x = mode->pan.x; + screen->dim.y = mode->pan.y; } @@ -5041,8 +5379,8 @@ static void do_enable_display_for_twinview(CtkDisplayConfig *ctk_object, for (other = screen->displays; other; other = other->next_in_screen) { for (mode = other->modes; mode; mode = mode->next) { if (!rightmost || - ((mode->viewPortIn[X] + mode->viewPortIn[W]) > - (rightmost->viewPortIn[X] + rightmost->viewPortIn[W]))) { + ((mode->pan.x + mode->pan.width) > + (rightmost->pan.x + rightmost->pan.width))) { rightmost = mode; } } @@ -5064,17 +5402,13 @@ static void do_enable_display_for_twinview(CtkDisplayConfig *ctk_object, if (rightmost) { mode->position_type = CONF_ADJ_RIGHTOF; mode->relative_to = rightmost->display; - mode->viewPortIn[X] = rightmost->display->cur_mode->viewPortIn[X]; - mode->viewPortIn[Y] = rightmost->display->cur_mode->viewPortIn[Y]; - mode->pan[X] = mode->viewPortIn[X]; - mode->pan[Y] = mode->viewPortIn[Y]; + mode->pan.x = rightmost->display->cur_mode->pan.x; + mode->pan.y = rightmost->display->cur_mode->pan.y; } else { mode->position_type = CONF_ADJ_ABSOLUTE; mode->relative_to = NULL; - mode->viewPortIn[X] = metamode->dim[X] + metamode->dim[W]; - mode->viewPortIn[Y] = metamode->dim[Y]; - mode->pan[X] = mode->viewPortIn[X]; - mode->pan[Y] = mode->viewPortIn[Y]; + mode->pan.x = metamode->dim.x + metamode->dim.width; + mode->pan.y = metamode->dim.y; } @@ -5321,10 +5655,8 @@ static void do_configure_display_for_twinview(CtkDisplayConfig *ctk_object, /* Duplicate position information of the last mode */ if (last_mode) { - mode->viewPortIn[X] = last_mode->viewPortIn[X]; - mode->viewPortIn[Y] = last_mode->viewPortIn[Y]; - mode->pan[X] = last_mode->pan[X]; - mode->pan[Y] = last_mode->pan[Y]; + mode->pan.x = last_mode->pan.x; + mode->pan.y = last_mode->pan.y; mode->position_type = last_mode->position_type; mode->relative_to = last_mode->relative_to; } @@ -5618,8 +5950,9 @@ static void display_refresh_changed(GtkWidget *widget, gpointer user_data) /* Update the display's currently selected mode */ ctk_display_layout_set_mode_modeline (CTK_DISPLAY_LAYOUT(ctk_object->obj_layout), - display->cur_mode, modeline); - + display->cur_mode, modeline, + &display->cur_mode->viewPortIn, + &display->cur_mode->viewPortOut); /* Update the modename */ setup_display_modename(ctk_object); @@ -5655,7 +5988,7 @@ static void display_resolution_changed(GtkWidget *widget, gpointer user_data) /* cache the selected index */ last_idx = ctk_object->last_resolution_idx; ctk_object->last_resolution_idx = idx; - + /* Ignore selecting same resolution */ if (idx == last_idx) { return; @@ -5677,20 +6010,26 @@ static void display_resolution_changed(GtkWidget *widget, gpointer user_data) display->screen, metamode_idx); } } - /* Select the new modeline for its resolution */ - ctk_display_layout_set_mode_modeline - (CTK_DISPLAY_LAYOUT(ctk_object->obj_layout), - display->cur_mode, selected_mode->modeline); - + if (selected_mode->isScaled) { + ctk_display_layout_set_mode_modeline + (CTK_DISPLAY_LAYOUT(ctk_object->obj_layout), + display->cur_mode, + selected_mode->modeline, + &selected_mode->viewPortIn, + &selected_mode->viewPortOut); + } else { + ctk_display_layout_set_mode_modeline + (CTK_DISPLAY_LAYOUT(ctk_object->obj_layout), + display->cur_mode, + selected_mode->modeline, + NULL /* viewPortIn */, + NULL /* viewPortOut */); + } /* Update the UI */ - setup_display_refresh_dropdown(ctk_object); - setup_display_viewport_in(ctk_object); - setup_display_viewport_out(ctk_object); - setup_display_position(ctk_object); - setup_display_panning(ctk_object); + setup_display_page(ctk_object); user_changed_attributes(ctk_object); @@ -5834,6 +6173,140 @@ static void display_reflection_changed(GtkWidget *widget, gpointer user_data) +/** post_display_underscan_value_changed() **************************** + * + * Modifies the ViewPortOut of the current mode according to the value + * of the Underscan slider. + * + **/ +static void post_display_underscan_value_changed(CtkDisplayConfig *ctk_object, + const int hpixel_value) +{ + CtkDisplayLayout *ctk_display; + nvDisplayPtr display; + nvModePtr cur_mode; + nvSize raster_size; + + ctk_display = CTK_DISPLAY_LAYOUT(ctk_object->obj_layout); + display = ctk_display_layout_get_selected_display(ctk_display); + + cur_mode = display->cur_mode; + + if (!cur_mode || !cur_mode->modeline) { + return; + } + + raster_size.height = cur_mode->modeline->data.vdisplay; + raster_size.width = cur_mode->modeline->data.hdisplay; + + /* Update ViewPortOut, ViewPortIn and panning. Erase previous data */ + apply_underscan_to_viewportout(raster_size, hpixel_value, + &cur_mode->viewPortOut); + + ctk_display_layout_set_mode_viewport_in(ctk_display, + cur_mode, + cur_mode->viewPortOut.width, + cur_mode->viewPortOut.height, + TRUE /* update_panning_size */); + + /* Enable the apply button */ + gtk_widget_set_sensitive(ctk_object->btn_apply, TRUE); + + /* Update status bar */ + ctk_config_statusbar_message(ctk_object->ctk_config, + "Underscan set to %d horizontal pixels.", + hpixel_value); +} + + + +/** display_underscan_value_changed() ********************************* + * + * Called when user modifies the value of the Underscan slider. + * + **/ +static void display_underscan_value_changed(GtkAdjustment *adjustment, + gpointer user_data) +{ + CtkDisplayConfig *ctk_object = CTK_DISPLAY_CONFIG(user_data); + nvDisplayPtr display; + nvModePtr cur_mode; + int hpixel_value; + gfloat value; + gchar *txt_entry; + + display = ctk_display_layout_get_selected_display + (CTK_DISPLAY_LAYOUT(ctk_object->obj_layout)); + + if (!display) { + return; + } + + cur_mode = display->cur_mode; + + if (!cur_mode || !cur_mode->modeline) { + return; + } + + value = (gfloat) gtk_adjustment_get_value(adjustment); + hpixel_value = cur_mode->modeline->data.hdisplay * (value / 100); + + txt_entry = g_strdup_printf("%d", hpixel_value); + gtk_entry_set_text(GTK_ENTRY(ctk_object->txt_display_underscan), txt_entry); + g_free(txt_entry); + + post_display_underscan_value_changed(ctk_object, hpixel_value); +} + + + +/** display_underscan_activate() ************************************** + * + * Called when user modifies the display Underscan text entry. + * + * This then calls display_underscan_value_changed() by + * modifying the value of the Underscan slider. + * + **/ +static void display_underscan_activate(GtkWidget *widget, + gpointer user_data) +{ + CtkDisplayConfig *ctk_object = CTK_DISPLAY_CONFIG(user_data); + const gchar *txt_entry = gtk_entry_get_text(GTK_ENTRY(widget)); + nvDisplayPtr display; + nvModePtr cur_mode; + int hdisplay, hpixel_value; + gfloat adj_value; + + display = ctk_display_layout_get_selected_display + (CTK_DISPLAY_LAYOUT(ctk_object->obj_layout)); + + if (!display) { + return; + } + + cur_mode = display->cur_mode; + + if (!cur_mode || !cur_mode->modeline) { + return; + } + + parse_read_integer(txt_entry, &hpixel_value); + + hdisplay = cur_mode->modeline->data.hdisplay; + adj_value = ((gfloat) hpixel_value / hdisplay) * 100; + + /* Sanitize adjustment value */ + adj_value = MIN(adj_value, UNDERSCAN_MAX_PERCENT); + adj_value = MAX(adj_value, UNDERSCAN_MIN_PERCENT); + + /* This sends a value_changed signal to the adjustment object */ + gtk_adjustment_set_value(GTK_ADJUSTMENT(ctk_object->adj_display_underscan), + adj_value); +} + + + /** display_position_type_changed() ********************************** * * Called when user selects a new display position method (relative/ @@ -5876,8 +6349,8 @@ static void display_position_type_changed(GtkWidget *widget, ctk_display_layout_set_display_position (CTK_DISPLAY_LAYOUT(ctk_object->obj_layout), display, position_type, relative_to, - display->cur_mode->viewPortIn[X], - display->cur_mode->viewPortIn[Y]); + display->cur_mode->pan.x, + display->cur_mode->pan.y); } @@ -5977,8 +6450,8 @@ static void display_position_offset_activate(GtkWidget *widget, } /* Make coordinates relative to top left of Screen */ - x += display->cur_mode->metamode->edim[X]; - y += display->cur_mode->metamode->edim[Y]; + x += display->cur_mode->metamode->edim.x; + y += display->cur_mode->metamode->edim.y; /* Update the absolute position */ @@ -6019,7 +6492,8 @@ static void display_viewport_in_activate(GtkWidget *widget, gpointer user_data) } ctk_display_layout_set_mode_viewport_in - (CTK_DISPLAY_LAYOUT(ctk_object->obj_layout), display->cur_mode, w, h); + (CTK_DISPLAY_LAYOUT(ctk_object->obj_layout), display->cur_mode, w, h, + FALSE /* update_panning_size */); } /* display_viewport_in_activate() */ @@ -6285,8 +6759,8 @@ static void screen_position_type_changed(GtkWidget *widget, ctk_display_layout_set_screen_position (CTK_DISPLAY_LAYOUT(ctk_object->obj_layout), screen, position_type, relative_to, - screen->dim[X], - screen->dim[Y]); + screen->dim.x, + screen->dim.y); } @@ -6652,8 +7126,8 @@ static Bool switch_to_current_metamode(CtkDisplayConfig *ctk_object, metamode = screen->cur_metamode; - new_width = metamode->edim[W]; - new_height = metamode->edim[H]; + new_width = metamode->edim.width; + new_height = metamode->edim.height; new_rate = metamode->id; @@ -6787,7 +7261,7 @@ static Bool switch_to_current_metamode(CtkDisplayConfig *ctk_object, gtk_widget_grab_focus(ctk_object->btn_display_apply_cancel); result = gtk_dialog_run(GTK_DIALOG(ctk_object->dlg_display_confirm)); gtk_widget_hide(ctk_object->dlg_display_confirm); - + /* Kill the timer */ g_source_remove(ctk_object->display_confirm_timer); @@ -6795,7 +7269,7 @@ static Bool switch_to_current_metamode(CtkDisplayConfig *ctk_object, { case GTK_RESPONSE_ACCEPT: break; - + case GTK_RESPONSE_REJECT: default: /* Fall back to previous settings */ @@ -6815,7 +7289,7 @@ static Bool switch_to_current_metamode(CtkDisplayConfig *ctk_object, NULL); if (ret != NvCtrlSuccess) { nv_warning_msg("Failed to re-write current MetaMode (%d) to " - "'%s' on X screen $d!", + "'%s' on X screen %d!", old_rate, cur_metamode_str, NvCtrlGetTargetId(screen->handle)); @@ -7357,8 +7831,8 @@ static int update_screen_metamodes(CtkDisplayConfig *ctk_object, ctk_config_statusbar_message(ctk_object->ctk_config, "Switched to MetaMode %dx%d.", - screen->cur_metamode->edim[W], - screen->cur_metamode->edim[H]); + screen->cur_metamode->edim.width, + screen->cur_metamode->edim.height); nv_info_msg(TAB, "Using > %s", screen->cur_metamode->string); @@ -7769,9 +8243,9 @@ static Bool add_display_to_screen(nvScreenPtr screen, /* Configure the virtual screen size */ if (screen->no_scanout) { conf_display = conf_screen->displays; - - conf_display->virtualX = screen->dim[W]; - conf_display->virtualY = screen->dim[H]; + + conf_display->virtualX = screen->dim.width; + conf_display->virtualY = screen->dim.height; } /* XXX Don't do any further tweaking to the display subsection. @@ -7786,7 +8260,7 @@ static Bool add_display_to_screen(nvScreenPtr screen, xconfigFreeDisplayList(&conf_screen->displays); return FALSE; - + } /* add_display_to_screen() */ @@ -8074,11 +8548,11 @@ static Bool add_adjacency_to_xconfig(nvScreenPtr screen, XConfigPtr config) adj->scrnum = screen->scrnum; adj->screen = screen->conf_screen; adj->screen_name = xconfigStrdup(screen->conf_screen->identifier); - + /* Position the X screen */ if (screen->position_type == CONF_ADJ_ABSOLUTE) { - adj->x = screen->dim[X]; - adj->y = screen->dim[Y]; + adj->x = screen->dim.x; + adj->y = screen->dim.y; } else { adj->where = screen->position_type; adj->refscreen = diff --git a/src/gtk+-2.x/ctkdisplayconfig.h b/src/gtk+-2.x/ctkdisplayconfig.h index 126620e..965673d 100644 --- a/src/gtk+-2.x/ctkdisplayconfig.h +++ b/src/gtk+-2.x/ctkdisplayconfig.h @@ -121,6 +121,11 @@ typedef struct _CtkDisplayConfig GtkWidget *mnu_display_rotation; GtkWidget *mnu_display_reflection; + GtkWidget *box_display_underscan; + GtkWidget *txt_display_underscan; + GtkWidget *sld_display_underscan; + GtkObject *adj_display_underscan; + GtkWidget *box_display_viewport; GtkWidget *box_display_viewport_in; @@ -205,7 +210,7 @@ typedef struct _CtkDisplayConfig gboolean notify_user_of_reset; /* User was notified of reset requirement */ gboolean ignore_reset_events; /* Ignore reset-causing events */ - int cur_screen_pos[2]; /* Keep track of the selected X screen's position */ + GdkPoint cur_screen_pos; /* Keep track of the selected X screen's position */ GtkWidget *btn_save; GtkWidget *btn_probe; diff --git a/src/gtk+-2.x/ctkdisplaylayout.c b/src/gtk+-2.x/ctkdisplaylayout.c index 2066d99..68155bb 100644 --- a/src/gtk+-2.x/ctkdisplaylayout.c +++ b/src/gtk+-2.x/ctkdisplaylayout.c @@ -311,23 +311,22 @@ static nvModePtr get_mode(nvDisplayPtr display, int mode_idx) -/** get_screen_dim *************************************************** +/** get_screen_rect ************************************************** * * Returns the dimension array to use as the screen's dimensions. * **/ -static int *get_screen_dim(nvScreenPtr screen, Bool edim) +static GdkRectangle *get_screen_rect(nvScreenPtr screen, Bool edim) { if (!screen) return NULL; if (screen->no_scanout || !screen->cur_metamode) { - return screen->dim; + return &(screen->dim); } - return edim ? screen->cur_metamode->edim : screen->cur_metamode->dim; - -} /* get_screen_dim() */ + return edim ? &(screen->cur_metamode->edim) : &(screen->cur_metamode->dim); +} @@ -343,7 +342,7 @@ static Bool get_modify_info(CtkDisplayLayout *ctk_object) { ModifyInfo *info = &(ctk_object->modify_info); Bool use_screen_instead; - int *sdim; + GdkRectangle *screen_rect; info->screen = ctk_object->selected_screen; @@ -365,11 +364,8 @@ static Bool get_modify_info(CtkDisplayLayout *ctk_object) /* Gather the initial screen dimensions */ - sdim = get_screen_dim(info->screen, 0); - info->orig_screen_dim[X] = sdim[X]; - info->orig_screen_dim[Y] = sdim[Y]; - info->orig_screen_dim[W] = sdim[W]; - info->orig_screen_dim[H] = sdim[H]; + screen_rect = get_screen_rect(info->screen, 0); + info->orig_screen_dim = *(screen_rect); /* If a display device is being moved (not panned) and @@ -406,30 +402,18 @@ static Bool get_modify_info(CtkDisplayLayout *ctk_object) if (info->display) { info->target_position_type = &(info->display->cur_mode->position_type); - if (ctk_object->modify_info.modify_panning) { - info->target_dim = info->display->cur_mode->pan; - } else { - info->target_dim = info->display->cur_mode->viewPortIn; - } + info->target_dim = &(info->display->cur_mode->pan); info->gpu = info->display->gpu; } else { info->target_position_type = &(info->screen->position_type); - info->target_dim = sdim; + info->target_dim = screen_rect; info->gpu = info->screen->gpu; } info->orig_position_type = *(info->target_position_type); - info->orig_dim[X] = info->target_dim[X]; - info->orig_dim[Y] = info->target_dim[Y]; - info->orig_dim[W] = info->target_dim[W]; - info->orig_dim[H] = info->target_dim[H]; - + info->orig_dim = *(info->target_dim); /* Initialize where we moved to */ - info->dst_dim[X] = info->orig_dim[X]; - info->dst_dim[Y] = info->orig_dim[Y]; - info->dst_dim[W] = info->orig_dim[W]; - info->dst_dim[H] = info->orig_dim[H]; - + info->dst_dim = info->orig_dim; /* Initialize snapping */ info->best_snap_v = ctk_object->snap_strength +1; @@ -438,10 +422,7 @@ static Bool get_modify_info(CtkDisplayLayout *ctk_object) /* Make sure the modify dim is up to date */ if (info->modify_dirty) { - info->modify_dim[X] = info->orig_dim[X]; - info->modify_dim[Y] = info->orig_dim[Y]; - info->modify_dim[W] = info->orig_dim[W]; - info->modify_dim[H] = info->orig_dim[H]; + info->modify_dim = info->orig_dim; info->modify_dirty = 0; } @@ -459,15 +440,15 @@ static Bool get_modify_info(CtkDisplayLayout *ctk_object) static Bool sync_scaling(CtkDisplayLayout *ctk_object) { - int *dim = ctk_object->layout->dim; + GdkRectangle *dim = &(ctk_object->layout->dim); float wscale; float hscale; float prev_scale = ctk_object->scale; - wscale = (float)(ctk_object->img_dim[W]) / (float)(dim[W]); - hscale = (float)(ctk_object->img_dim[H]) / (float)(dim[H]); + wscale = (float)(ctk_object->img_dim.width) / (float)(dim->width); + hscale = (float)(ctk_object->img_dim.height) / (float)(dim->height); - if (wscale * dim[H] > ctk_object->img_dim[H]) { + if (wscale * dim->height > ctk_object->img_dim.height) { ctk_object->scale = hscale; } else { ctk_object->scale = wscale; @@ -485,27 +466,42 @@ static Bool sync_scaling(CtkDisplayLayout *ctk_object) * **/ -static int point_in_dim(int *dim, int x, int y) +static int point_in_rect(const GdkRectangle *rect, int x, int y) { - if (x > dim[X] && x < (dim[X] + dim[W]) && - y > dim[Y] && y < (dim[Y] + dim[H])) { + if (x > rect->x && x < (rect->x + rect->width) && + y > rect->y && y < (rect->y + rect->height)) { return 1; } - + return 0; +} -} /* point_in_dim() */ +static int point_in_display(nvDisplayPtr display, int x, int y) +{ + if (!display->cur_mode) { + return 0; + } + + return point_in_rect(&(display->cur_mode->pan), x, y); +} + +static int point_in_screen(nvScreenPtr screen, int x, int y) +{ + GdkRectangle *screen_rect = get_screen_rect(screen, 1); + + return point_in_rect(screen_rect, x, y); +} /** get_point_relative_position() ************************************ * - * Determines the relative position of a point to the given dimensions - * of a box. + * Returns where the point (x, y) is, relative to the given rectangle + * as: above, below, left-of, right-of, inside/clones. * **/ -static int get_point_relative_position(int *dim, int x, int y) +static int get_point_relative_position(GdkRectangle *rect, int x, int y) { float m1, b1; float m2, b2; @@ -513,30 +509,30 @@ static int get_point_relative_position(int *dim, int x, int y) /* Point insize dim */ - if ((x >= dim[X]) && (x <= dim[X] + dim[W]) && - (y >= dim[Y]) && (y <= dim[Y] + dim[H])) { + if (point_in_rect(rect, x, y)) { return CONF_ADJ_RELATIVE; } - + /* Compute cross lines of dimensions */ - m1 = ((float) dim[H]) / ((float) dim[W]); - b1 = ((float) dim[Y]) - (m1 * ((float) dim[X])); - + m1 = ((float) rect->height) / ((float) rect->width); + b1 = ((float) rect->y) - (m1 * ((float) rect->x)); + m2 = -m1; - b2 = ((float) dim[Y]) + ((float) dim[H]) - (m2 * ((float) dim[X])); - + b2 = ((float) rect->y) + ((float) rect->height) - + (m2 * ((float) rect->x)); + /* Compute where point is relative to cross lines */ l1 = m1 * ((float) x) + b1 - ((float) y); l2 = m2 * ((float) x) + b2 - ((float) y); - + if (l1 > 0.0f) { - if (l2 > 0.0f) { + if (l2 > 0.0f) { return CONF_ADJ_ABOVE; } else { return CONF_ADJ_RIGHTOF; } } else { - if (l2 > 0.0f) { + if (l2 > 0.0f) { return CONF_ADJ_LEFTOF; } else { return CONF_ADJ_BELOW; @@ -559,10 +555,8 @@ static int get_point_relative_position(int *dim, int x, int y) /* Offset a single mode */ static void offset_mode(nvModePtr mode, int x, int y) { - mode->viewPortIn[X] += x; - mode->viewPortIn[Y] += y; - mode->pan[X] = mode->viewPortIn[X]; - mode->pan[Y] = mode->viewPortIn[Y]; + mode->pan.x += x; + mode->pan.y += y; } /* Offset a display by offsetting the current mode */ @@ -579,14 +573,14 @@ static void offset_screen(nvScreenPtr screen, int x, int y) { nvMetaModePtr metamode; - screen->dim[X] += x; - screen->dim[Y] += y; - + screen->dim.x += x; + screen->dim.y += y; + for (metamode = screen->metamodes; metamode; metamode = metamode->next) { - metamode->dim[X] += x; - metamode->dim[Y] += y; - metamode->edim[X] += x; - metamode->edim[Y] += y; + metamode->dim.x += x; + metamode->dim.y += y; + metamode->edim.x += x; + metamode->edim.y += y; } } @@ -597,8 +591,8 @@ static void offset_layout(nvLayoutPtr layout, int x, int y) nvScreenPtr screen; nvDisplayPtr display; - layout->dim[X] += x; - layout->dim[Y] += y; + layout->dim.x += x; + layout->dim.y += y; /* Offset screens */ for (screen = layout->screens; screen; screen = screen->next_in_layout) { @@ -628,60 +622,60 @@ static void offset_layout(nvLayoutPtr layout, int x, int y) **/ static int resolve_display(nvDisplayPtr display, int mode_idx, - int pos[4]) + GdkRectangle *pos) { nvModePtr mode = get_mode(display, mode_idx); - int relative_pos[4]; - + GdkRectangle relative_pos; + if (!mode) return 0; /* Set the dimensions */ - pos[W] = mode->pan[W]; - pos[H] = mode->pan[H]; + pos->width = mode->pan.width; + pos->height = mode->pan.height; /* Find the position */ switch (mode->position_type) { case CONF_ADJ_ABSOLUTE: - pos[X] = mode->pan[X]; - pos[Y] = mode->pan[Y]; + pos->x = mode->pan.x; + pos->y = mode->pan.y; break; case CONF_ADJ_RIGHTOF: - resolve_display(mode->relative_to, mode_idx, relative_pos); - pos[X] = relative_pos[X] + relative_pos[W]; - pos[Y] = relative_pos[Y]; + resolve_display(mode->relative_to, mode_idx, &relative_pos); + pos->x = relative_pos.x + relative_pos.width; + pos->y = relative_pos.y; break; case CONF_ADJ_LEFTOF: - resolve_display(mode->relative_to, mode_idx, relative_pos); - pos[X] = relative_pos[X] - pos[W]; - pos[Y] = relative_pos[Y]; + resolve_display(mode->relative_to, mode_idx, &relative_pos); + pos->x = relative_pos.x - pos->width; + pos->y = relative_pos.y; break; case CONF_ADJ_BELOW: - resolve_display(mode->relative_to, mode_idx, relative_pos); - pos[X] = relative_pos[X]; - pos[Y] = relative_pos[Y] + relative_pos[H]; + resolve_display(mode->relative_to, mode_idx, &relative_pos); + pos->x = relative_pos.x; + pos->y = relative_pos.y + relative_pos.height; break; case CONF_ADJ_ABOVE: - resolve_display(mode->relative_to, mode_idx, relative_pos); - pos[X] = relative_pos[X]; - pos[Y] = relative_pos[Y] - pos[H]; + resolve_display(mode->relative_to, mode_idx, &relative_pos); + pos->x = relative_pos.x; + pos->y = relative_pos.y - pos->height; break; case CONF_ADJ_RELATIVE: /* Clone */ - resolve_display(mode->relative_to, mode_idx, relative_pos); - pos[X] = relative_pos[X]; - pos[Y] = relative_pos[Y]; + resolve_display(mode->relative_to, mode_idx, &relative_pos); + pos->x = relative_pos.x; + pos->y = relative_pos.y; break; default: return 0; } - + return 1; } /* resolve_display() */ @@ -699,7 +693,7 @@ static void resolve_displays_in_screen(nvScreenPtr screen, int resolve_all_modes) { nvDisplayPtr display; - int pos[4]; + GdkRectangle rect; int first_idx; int last_idx; int mode_idx; @@ -718,12 +712,10 @@ static void resolve_displays_in_screen(nvScreenPtr screen, display = display->next_in_screen) { for (mode_idx = first_idx; mode_idx <= last_idx; mode_idx++) { - if (resolve_display(display, mode_idx, pos)) { + if (resolve_display(display, mode_idx, &rect)) { nvModePtr mode = get_mode(display, mode_idx); - mode->viewPortIn[X] = pos[X]; - mode->viewPortIn[Y] = pos[Y]; - mode->pan[X] = pos[X]; - mode->pan[Y] = pos[Y]; + mode->pan.x = rect.x; + mode->pan.y = rect.y; } } } @@ -746,61 +738,60 @@ static void resolve_displays_in_screen(nvScreenPtr screen, * **/ -static int resolve_screen(nvScreenPtr screen, int pos[4]) +static int resolve_screen(nvScreenPtr screen, GdkRectangle *pos) { - int *sdim = get_screen_dim(screen, 0); - int relative_pos[4]; - + GdkRectangle *screen_rect = get_screen_rect(screen, 0); + GdkRectangle relative_pos; - if (!sdim) return 0; + if (!screen_rect) return 0; /* Set the dimensions */ - pos[W] = sdim[W]; - pos[H] = sdim[H]; + pos->width = screen_rect->width; + pos->height = screen_rect->height; /* Find the position */ switch (screen->position_type) { case CONF_ADJ_ABSOLUTE: - pos[X] = sdim[X]; - pos[Y] = sdim[Y]; + pos->x = screen_rect->x; + pos->y = screen_rect->y; break; case CONF_ADJ_RIGHTOF: - resolve_screen(screen->relative_to, relative_pos); - pos[X] = relative_pos[X] + relative_pos[W]; - pos[Y] = relative_pos[Y]; + resolve_screen(screen->relative_to, &relative_pos); + pos->x = relative_pos.x + relative_pos.width; + pos->y = relative_pos.y; break; case CONF_ADJ_LEFTOF: - resolve_screen(screen->relative_to, relative_pos); - pos[X] = relative_pos[X] - pos[W]; - pos[Y] = relative_pos[Y]; + resolve_screen(screen->relative_to, &relative_pos); + pos->x = relative_pos.x - pos->width; + pos->y = relative_pos.y; break; case CONF_ADJ_BELOW: - resolve_screen(screen->relative_to, relative_pos); - pos[X] = relative_pos[X]; - pos[Y] = relative_pos[Y] + relative_pos[H]; + resolve_screen(screen->relative_to, &relative_pos); + pos->x = relative_pos.x; + pos->y = relative_pos.y + relative_pos.height; break; case CONF_ADJ_ABOVE: - resolve_screen(screen->relative_to, relative_pos); - pos[X] = relative_pos[X]; - pos[Y] = relative_pos[Y] - pos[H]; + resolve_screen(screen->relative_to, &relative_pos); + pos->x = relative_pos.x; + pos->y = relative_pos.y - pos->height; break; case CONF_ADJ_RELATIVE: /* Clone */ - resolve_screen(screen->relative_to, relative_pos); - pos[X] = relative_pos[X]; - pos[Y] = relative_pos[Y]; + resolve_screen(screen->relative_to, &relative_pos); + pos->x = relative_pos.x; + pos->y = relative_pos.y; break; default: return 0; } - + return 1; } /* resolve_screen() */ @@ -817,19 +808,19 @@ static int resolve_screen(nvScreenPtr screen, int pos[4]) static void resolve_screen_in_layout(nvScreenPtr screen) { nvDisplayPtr display; - int pos[4]; + GdkRectangle pos; int x, y; - int *sdim; + GdkRectangle *screen_rect; /* Resolve the current screen location */ - if (resolve_screen(screen, pos)) { + if (resolve_screen(screen, &pos)) { /* Move the screen and the displays by offsetting */ - sdim = get_screen_dim(screen, 0); + screen_rect = get_screen_rect(screen, 0); - x = pos[X] - sdim[X]; - y = pos[Y] - sdim[Y]; + x = pos.x - screen_rect->x; + y = pos.y - screen_rect->y; offset_screen(screen, x, y); @@ -884,23 +875,20 @@ static void calc_metamode(nvScreenPtr screen, nvMetaModePtr metamode) nvModePtr mode; int init = 1; int einit = 1; - int *dim; // Bounding box for all modes, including NULL modes. - int *edim; // Bounding box for non-NULL modes. + GdkRectangle *dim; // Bounding box for all modes, including NULL modes. + GdkRectangle *edim; // Bounding box for non-NULL modes. if (!screen || !metamode) { return; } - dim = metamode->dim; - edim = metamode->edim; + dim = &(metamode->dim); + edim = &(metamode->edim); - dim[X] = edim[X] = 0; - dim[Y] = edim[Y] = 0; - dim[W] = edim[W] = 0; - dim[H] = edim[H] = 0; + memset(dim, 0, sizeof(*dim)); + memset(edim, 0, sizeof(*edim)); - /* Calculate its dimensions */ for (display = screen->displays; display; display = display->next_in_screen) { @@ -912,42 +900,23 @@ static void calc_metamode(nvScreenPtr screen, nvMetaModePtr metamode) if (!mode) continue; if (init) { - dim[X] = mode->pan[X]; - dim[Y] = mode->pan[Y]; - dim[W] = mode->pan[X] +mode->pan[W]; - dim[H] = mode->pan[Y] +mode->pan[H]; + *dim = mode->pan; init = 0; } else { - dim[X] = MIN(dim[X], mode->viewPortIn[X]); - dim[Y] = MIN(dim[Y], mode->viewPortIn[Y]); - dim[W] = MAX(dim[W], mode->viewPortIn[X] +mode->pan[W]); - dim[H] = MAX(dim[H], mode->viewPortIn[Y] +mode->pan[H]); + gdk_rectangle_union(dim, &(mode->pan), dim); } /* Don't include NULL modes in the effective dimension calculation */ if (!mode->modeline) continue; if (einit) { - edim[X] = mode->pan[X]; - edim[Y] = mode->pan[Y]; - edim[W] = mode->pan[X] +mode->pan[W]; - edim[H] = mode->pan[Y] +mode->pan[H]; + *edim = mode->pan; einit = 0; } else { - edim[X] = MIN(edim[X], mode->viewPortIn[X]); - edim[Y] = MIN(edim[Y], mode->viewPortIn[Y]); - edim[W] = MAX(edim[W], mode->viewPortIn[X] +mode->pan[W]); - edim[H] = MAX(edim[H], mode->viewPortIn[Y] +mode->pan[H]); + gdk_rectangle_union(edim, &(mode->pan), edim); } } - - dim[W] = dim[W] - dim[X]; - dim[H] = dim[H] - dim[Y]; - - edim[W] = edim[W] - edim[X]; - edim[H] = edim[H] - edim[Y]; - -} /* calc_metamode() */ +} @@ -963,43 +932,31 @@ static void calc_metamode(nvScreenPtr screen, nvMetaModePtr metamode) static void calc_screen(nvScreenPtr screen) { nvMetaModePtr metamode; - int *dim; + GdkRectangle *dim; if (!screen || screen->no_scanout) return; - dim = screen->dim; + dim = &(screen->dim); metamode = screen->metamodes; if (!metamode) { - dim[X] = 0; - dim[Y] = 0; - dim[W] = 0; - dim[H] = 0; + memset(dim, 0, sizeof(*dim)); return; } + /* Init screen dimensions to size of first metamode */ calc_metamode(screen, metamode); - dim[X] = metamode->dim[X]; - dim[Y] = metamode->dim[Y]; - dim[W] = metamode->dim[X] +metamode->dim[W]; - dim[H] = metamode->dim[Y] +metamode->dim[H]; - + *dim = metamode->dim; + for (metamode = metamode->next; metamode; metamode = metamode->next) { calc_metamode(screen, metamode); - dim[X] = MIN(dim[X], metamode->dim[X]); - dim[Y] = MIN(dim[Y], metamode->dim[Y]); - dim[W] = MAX(dim[W], metamode->dim[X] +metamode->dim[W]); - dim[H] = MAX(dim[H], metamode->dim[Y] +metamode->dim[H]); + gdk_rectangle_union(dim, &(metamode->dim), dim); } - - dim[W] = dim[W] - dim[X]; - dim[H] = dim[H] - dim[Y]; - -} /* calc_screen() */ +} @@ -1020,7 +977,7 @@ static void calc_layout(nvLayoutPtr layout) nvScreenPtr screen; nvDisplayPtr display; int init = 1; - int *dim; + GdkRectangle *dim; int x, y; @@ -1028,58 +985,41 @@ static void calc_layout(nvLayoutPtr layout) resolve_layout(layout); - dim = layout->dim; - dim[X] = 0; - dim[Y] = 0; - dim[W] = 0; - dim[H] = 0; + dim = &(layout->dim); + memset(dim, 0, sizeof(*dim)); for (screen = layout->screens; screen; screen = screen->next_in_layout) { - int *sdim; + GdkRectangle *screem_rect; calc_screen(screen); - sdim = get_screen_dim(screen, 0); + screem_rect = get_screen_rect(screen, 0); if (init) { - dim[X] = sdim[X]; - dim[Y] = sdim[Y]; - dim[W] = sdim[X] +sdim[W]; - dim[H] = sdim[Y] +sdim[H]; + *dim = *screem_rect; init = 0; continue; } - - dim[X] = MIN(dim[X], sdim[X]); - dim[Y] = MIN(dim[Y], sdim[Y]); - dim[W] = MAX(dim[W], sdim[X] +sdim[W]); - dim[H] = MAX(dim[H], sdim[Y] +sdim[H]); + gdk_rectangle_union(dim, screem_rect, dim); } - dim[W] = dim[W] - dim[X]; - dim[H] = dim[H] - dim[Y]; - - /* Position disabled display devices off to the top right */ - x = dim[W] + dim[X]; - y = dim[Y]; + x = dim->x + dim->width; + y = dim->y; for (gpu = layout->gpus; gpu; gpu = gpu->next_in_layout) { for (display = gpu->displays; display; display = display->next_on_gpu) { if (display->screen) continue; - display->cur_mode->viewPortIn[X] = x; - display->cur_mode->pan[X] = x; - display->cur_mode->viewPortIn[Y] = y; - display->cur_mode->pan[Y] = y; + display->cur_mode->pan.x = x; + display->cur_mode->pan.y = y; - x += display->cur_mode->viewPortIn[W]; - dim[W] += display->cur_mode->viewPortIn[W]; - dim[H] = MAX(dim[H], display->cur_mode->viewPortIn[H]); + x += display->cur_mode->pan.width; + dim->width += display->cur_mode->pan.width; + dim->height = MAX(dim->height, display->cur_mode->pan.height); } } - -} /* calc_layout() */ +} @@ -1101,8 +1041,8 @@ static void recenter_screen(nvScreenPtr screen) nvModePtr mode; for (mode = display->modes; mode; mode = mode->next) { - int offset_x = (screen->dim[X] - mode->metamode->dim[X]); - int offset_y = (screen->dim[Y] - mode->metamode->dim[Y]); + int offset_x = (screen->dim.x - mode->metamode->dim.x); + int offset_y = (screen->dim.y - mode->metamode->dim.y); offset_mode(mode, offset_x, offset_y); } } @@ -1140,8 +1080,8 @@ static Bool set_screen_metamode(nvLayoutPtr layout, nvScreenPtr screen, /* Recalculate the layout dimensions */ calc_layout(layout); - if (layout->dim[X] || layout->dim[Y]) { - offset_layout(layout, -layout->dim[X], -layout->dim[Y]); + if (layout->dim.x || layout->dim.y) { + offset_layout(layout, -layout->dim.x, -layout->dim.y); return TRUE; } @@ -1204,8 +1144,8 @@ static Bool recenter_layout(nvLayoutPtr layout) static void reposition_screen(nvScreenPtr screen, int resolve_all_modes) { - int orig_screen_x = screen->dim[X]; - int orig_screen_y = screen->dim[Y]; + int orig_screen_x = screen->dim.x; + int orig_screen_y = screen->dim.y; /* Resolve new relative positions. In basic mode, * relative position changes apply to all modes of a @@ -1215,8 +1155,8 @@ static void reposition_screen(nvScreenPtr screen, int resolve_all_modes) resolve_displays_in_screen(screen, resolve_all_modes); /* Reestablish the screen's original position */ - screen->dim[X] = orig_screen_x; - screen->dim[Y] = orig_screen_y; + screen->dim.x = orig_screen_x; + screen->dim.y = orig_screen_y; recenter_screen(screen); } /* reposition_screen() */ @@ -1247,7 +1187,7 @@ static void switch_screen_to_absolute(nvScreenPtr screen) /** snap_dim_to_dim() *********************************************** * - * Snaps the sides of two rectangles together. + * Snaps the sides of two rectangles together. * * Snaps the dimensions of "src" to those of "snap" if any part * of the "src" rectangle is within "snap_strength" of the "snap" @@ -1260,7 +1200,8 @@ static void switch_screen_to_absolute(nvScreenPtr screen) * **/ -static void snap_dim_to_dim(int *dst, int *src, int *snap, int snap_strength, +static void snap_dim_to_dim(GdkRectangle *dst, GdkRectangle *src, + GdkRectangle *snap, int snap_strength, int *best_vert, int *best_horz) { int dist; @@ -1270,43 +1211,43 @@ static void snap_dim_to_dim(int *dst, int *src, int *snap, int snap_strength, if (best_vert) { /* Snap top side to top side */ - dist = abs(snap[Y] - src[Y]); + dist = abs(snap->y - src->y); if (dist < *best_vert) { - dst[Y] = snap[Y]; + dst->y = snap->y; *best_vert = dist; } - + /* Snap top side to bottom side */ - dist = abs((snap[Y] + snap[H]) - src[Y]); + dist = abs((snap->y + snap->height) - src->y); if (dist < *best_vert) { - dst[Y] = snap[Y] + snap[H]; + dst->y = snap->y + snap->height; *best_vert = dist; } - + /* Snap bottom side to top side */ - dist = abs(snap[Y] - (src[Y] + src[H])); + dist = abs(snap->y - (src->y + src->height)); if (dist < *best_vert) { - dst[Y] = snap[Y] - src[H]; + dst->y = snap->y - src->height; *best_vert = dist; } - + /* Snap bottom side to bottom side */ - dist = abs((snap[Y] + snap[H]) - (src[Y] + src[H])); + dist = abs((snap->y + snap->height) - (src->y + src->height)); if (dist < *best_vert) { - dst[Y] = snap[Y] + snap[H] - src[H]; + dst->y = snap->y + snap->height - src->height; *best_vert = dist; } - + /* Snap midlines */ if (/* Top of 'src' is above bottom of 'snap' */ - (src[Y] <= snap[Y] + snap[H] + snap_strength) && + (src->y <= snap->y + snap->height + snap_strength) && /* Bottom of 'src' is below top of 'snap' */ - (src[Y] + src[H] >= snap[Y] - snap_strength)) { - + (src->y + src->height >= snap->y - snap_strength)) { + /* Snap vertically */ - dist = abs((snap[Y] + snap[H]/2) - (src[Y]+src[H]/2)); + dist = abs((snap->y + snap->height/2) - (src->y + src->height/2)); if (dist < *best_vert) { - dst[Y] = snap[Y] + snap[H]/2 - src[H]/2; + dst->y = snap->y + snap->height/2 - src->height/2; *best_vert = dist; } } @@ -1315,45 +1256,45 @@ static void snap_dim_to_dim(int *dst, int *src, int *snap, int snap_strength, /* Snap horizontally */ if (best_horz) { - + /* Snap left side to left side */ - dist = abs(snap[X] - src[X]); + dist = abs(snap->x - src->x); if (dist < *best_horz) { - dst[X] = snap[X]; + dst->x = snap->x; *best_horz = dist; } - + /* Snap left side to right side */ - dist = abs((snap[X] + snap[W]) - src[X]); + dist = abs((snap->x + snap->width) - src->x); if (dist < *best_horz) { - dst[X] = snap[X] + snap[W]; + dst->x = snap->x + snap->width; *best_horz = dist; } - + /* Snap right side to left side */ - dist = abs(snap[X] - (src[X] + src[W])); + dist = abs(snap->x - (src->x + src->width)); if (dist < *best_horz) { - dst[X] = snap[X] - src[W]; + dst->x = snap->x - src->width; *best_horz = dist; } - + /* Snap right side to right side */ - dist = abs((snap[X] + snap[W]) - (src[X]+src[W])); + dist = abs((snap->x + snap->width) - (src->x + src->width)); if (dist < *best_horz) { - dst[X] = snap[X] + snap[W] - src[W]; + dst->x = snap->x + snap->width - src->width; *best_horz = dist; } - + /* Snap midlines */ if (/* Left of 'src' is before right of 'snap' */ - (src[X] <= snap[X] + snap[W] + snap_strength) && + (src->x <= snap->x + snap->width + snap_strength) && /* Right of 'src' is after left of 'snap' */ - (src[X] + src[W] >= snap[X] - snap_strength)) { - + (src->x + src->width >= snap->x - snap_strength)) { + /* Snap vertically */ - dist = abs((snap[X] + snap[W]/2) - (src[X]+src[W]/2)); + dist = abs((snap->x + snap->width/2) - (src->x + src->width/2)); if (dist < *best_horz) { - dst[X] = snap[X] + snap[W]/2 - src[W]/2; + dst->x = snap->x + snap->width/2 - src->width/2; *best_horz = dist; } } @@ -1371,26 +1312,27 @@ static void snap_dim_to_dim(int *dst, int *src, int *snap, int snap_strength, * **/ -static void snap_side_to_dim(int *dst, int *src, int *snap, +static void snap_side_to_dim(GdkRectangle *dst, GdkRectangle *src, + GdkRectangle *snap, int *best_vert, int *best_horz) { int dist; - + /* Snap vertically */ if (best_vert) { /* Snap side to top side */ - dist = abs(snap[Y] - (src[Y] + src[H])); + dist = abs(snap->y - (src->y + src->height)); if (dist < *best_vert) { - dst[H] = snap[Y] - src[Y]; + dst->height = snap->y - src->y; *best_vert = dist; } - + /* Snap side to bottom side */ - dist = abs((snap[Y] + snap[H]) - (src[Y] + src[H])); + dist = abs((snap->y + snap->height) - (src->y + src->height)); if (dist < *best_vert) { - dst[H] = snap[Y] + snap[H] - src[Y]; + dst->height = snap->y + snap->height - src->y; *best_vert = dist; } } @@ -1400,16 +1342,16 @@ static void snap_side_to_dim(int *dst, int *src, int *snap, if (best_horz) { /* Snap side to left side */ - dist = abs(snap[X] - (src[X] + src[W])); + dist = abs(snap->x - (src->x + src->width)); if (dist < *best_horz) { - dst[W] = snap[X] - src[X]; + dst->width = snap->x - src->x; *best_horz = dist; } - + /* Snap side to right side */ - dist = abs((snap[X] + snap[W]) - (src[X] + src[W])); + dist = abs((snap->x + snap->width) - (src->x + src->width)); if (dist < *best_horz) { - dst[W] = snap[X] + snap[W] - src[X]; + dst->width = snap->x + snap->width - src->x; *best_horz = dist; } } @@ -1438,15 +1380,15 @@ static void snap_move(CtkDisplayLayout *ctk_object) nvLayoutPtr layout = ctk_object->layout; nvScreenPtr screen; nvDisplayPtr other; - int *sdim; + GdkRectangle *screen_rect; /* Snap to other display's modes */ if (info->display) { for (i = 0; i < ctk_object->Zcount; i++) { - + if (ctk_object->Zorder[i].type != ZNODE_TYPE_DISPLAY) continue; - + other = ctk_object->Zorder[i].u.display; /* Other display must have a mode */ @@ -1493,18 +1435,22 @@ static void snap_move(CtkDisplayLayout *ctk_object) (info->screen->relative_to == other->screen)) { bv = NULL; } - + /* Snap to other display's panning dimensions */ - snap_dim_to_dim(info->dst_dim, - info->src_dim, - other->cur_mode->pan, + snap_dim_to_dim(&(info->dst_dim), + &(info->src_dim), + &(other->cur_mode->pan), ctk_object->snap_strength, bv, bh); /* Snap to other display's dimensions */ - snap_dim_to_dim(info->dst_dim, - info->src_dim, - other->cur_mode->viewPortIn, - ctk_object->snap_strength, bv, bh); + { + GdkRectangle rect; + get_viewportin_rect(other->cur_mode, &rect); + snap_dim_to_dim(&(info->dst_dim), + &(info->src_dim), + &rect, + ctk_object->snap_strength, bv, bh); + } } } /* Done snapping to other displays */ @@ -1542,7 +1488,7 @@ static void snap_move(CtkDisplayLayout *ctk_object) */ if (!bh && info->display && - info->display->cur_mode->viewPortIn[Y] == info->screen->dim[Y]) { + info->display->cur_mode->pan.y == info->screen->dim.y) { bv = NULL; } @@ -1563,14 +1509,14 @@ static void snap_move(CtkDisplayLayout *ctk_object) */ if (!bv && info->display && - info->display->cur_mode->viewPortIn[X] == info->screen->dim[X]) { + info->display->cur_mode->pan.x == info->screen->dim.x) { bh = NULL; } - sdim = get_screen_dim(screen, 0); - snap_dim_to_dim(info->dst_dim, - info->src_dim, - sdim, + screen_rect = get_screen_rect(screen, 0); + snap_dim_to_dim(&(info->dst_dim), + &(info->src_dim), + screen_rect, ctk_object->snap_strength, bv, bh); } @@ -1579,18 +1525,18 @@ static void snap_move(CtkDisplayLayout *ctk_object) bh = &info->best_snap_h; if (info->display) { - dist = abs( (info->screen->dim[X] + info->gpu->max_width) - -(info->src_dim[X] + info->src_dim[W])); + dist = abs( (info->screen->dim.x + info->gpu->max_width) + -(info->src_dim.x + info->src_dim.width)); if (dist < *bh) { - info->dst_dim[X] = - info->screen->dim[X] + info->gpu->max_width - info->src_dim[W]; + info->dst_dim.x = info->screen->dim.x + info->gpu->max_width - + info->src_dim.width; *bh = dist; } - dist = abs( (info->screen->dim[Y] + info->gpu->max_height) - -(info->src_dim[Y] + info->src_dim[H])); + dist = abs( (info->screen->dim.y + info->gpu->max_height) + -(info->src_dim.y + info->src_dim.height)); if (dist < *bv) { - info->dst_dim[Y] = - info->screen->dim[Y] + info->gpu->max_height - info->src_dim[H]; + info->dst_dim.y = info->screen->dim.y + info->gpu->max_height - + info->src_dim.height; *bv = dist; } } @@ -1622,38 +1568,40 @@ static void snap_pan(CtkDisplayLayout *ctk_object) nvLayoutPtr layout = ctk_object->layout; nvScreenPtr screen; nvDisplayPtr other; - int *sdim; + GdkRectangle *screen_rect; if (info->display) { - int *cur_mode_dim = info->display->cur_mode->viewPortIn; - /* Snap to multiples of the display's dimensions */ + const nvSize *cur_mode_size = &(info->display->cur_mode->viewPortIn); + bh = &(info->best_snap_h); bv = &(info->best_snap_v); - dist = info->src_dim[W] % cur_mode_dim[W]; + dist = info->src_dim.width % cur_mode_size->width; if (dist < *bh) { - info->dst_dim[W] = cur_mode_dim[W] * - (int)(info->src_dim[W] / cur_mode_dim[W]); + info->dst_dim.width = cur_mode_size->width * + (int)(info->src_dim.width / cur_mode_size->width); *bh = dist; } - dist = cur_mode_dim[W] - (info->src_dim[W] % cur_mode_dim[W]); + dist = cur_mode_size->width - + (info->src_dim.width % cur_mode_size->width); if (dist < *bh) { - info->dst_dim[W] = cur_mode_dim[W] * - (1 + (int)(info->src_dim[W] / cur_mode_dim[W])); + info->dst_dim.width = cur_mode_size->width * + (1 + (int)(info->src_dim.width / cur_mode_size->width)); *bh = dist; } - dist = abs(info->src_dim[H] % cur_mode_dim[H]); + dist = abs(info->src_dim.height % cur_mode_size->height); if (dist < *bv) { - info->dst_dim[H] = cur_mode_dim[H] * - (int)(info->src_dim[H] / cur_mode_dim[H]); + info->dst_dim.height = cur_mode_size->height * + (int)(info->src_dim.height / cur_mode_size->height); *bv = dist; } - dist = cur_mode_dim[H] - (info->src_dim[H] % cur_mode_dim[H]); + dist = cur_mode_size->height - + (info->src_dim.height % cur_mode_size->height); if (dist < *bv) { - info->dst_dim[H] = cur_mode_dim[H] * - (1 + (int)(info->src_dim[H] / cur_mode_dim[H])); + info->dst_dim.height = cur_mode_size->height * + (1 + (int)(info->src_dim.height / cur_mode_size->height)); *bv = dist; } } @@ -1725,16 +1673,20 @@ static void snap_pan(CtkDisplayLayout *ctk_object) } /* Snap to other display panning dimensions */ - snap_side_to_dim(info->dst_dim, - info->src_dim, - other->cur_mode->pan, + snap_side_to_dim(&(info->dst_dim), + &(info->src_dim), + &(other->cur_mode->pan), bv, bh); /* Snap to other display dimensions */ - snap_side_to_dim(info->dst_dim, - info->src_dim, - other->cur_mode->viewPortIn, - bv, bh); + { + GdkRectangle rect; + get_viewportin_rect(other->cur_mode, &rect); + snap_side_to_dim(&(info->dst_dim), + &(info->src_dim), + &rect, + bv, bh); + } } @@ -1772,10 +1724,10 @@ static void snap_pan(CtkDisplayLayout *ctk_object) bv = NULL; } - sdim = get_screen_dim(screen, 0); - snap_side_to_dim(info->dst_dim, - info->src_dim, - sdim, + screen_rect = get_screen_rect(screen, 0); + snap_side_to_dim(&(info->dst_dim), + &(info->src_dim), + screen_rect, bv, bh); } @@ -1783,20 +1735,20 @@ static void snap_pan(CtkDisplayLayout *ctk_object) bv = &(info->best_snap_v); /* Snap to the maximum screen width */ - dist = abs((info->screen->dim[X] + info->gpu->max_width) - -(info->src_dim[X] + info->src_dim[W])); + dist = abs((info->screen->dim.x + info->gpu->max_width) + -(info->src_dim.x + info->src_dim.width)); if (dist < *bh) { - info->dst_dim[W] = info->screen->dim[X] + info->gpu->max_width - - info->src_dim[X]; + info->dst_dim.width = info->screen->dim.x + info->gpu->max_width - + info->src_dim.x; *bh = dist; } /* Snap to the maximum screen height */ - dist = abs((info->screen->dim[Y] + info->gpu->max_height) - -(info->src_dim[Y] + info->src_dim[H])); + dist = abs((info->screen->dim.y + info->gpu->max_height) + -(info->src_dim.y + info->src_dim.height)); if (dist < *bv) { - info->dst_dim[H] = info->screen->dim[Y] + info->gpu->max_height - - info->src_dim[Y]; + info->dst_dim.height = info->screen->dim.y + info->gpu->max_height - + info->src_dim.y; *bv = dist; } @@ -1820,8 +1772,8 @@ static int move_selected(CtkDisplayLayout *ctk_object, int x, int y, int snap) ModifyInfo *info = &(ctk_object->modify_info); int modified = 0; - int *dim; /* Temp dimensions */ - int *sdim; /* Temp screen dimensions */ + GdkRectangle *dim; /* Temp dimensions */ + GdkRectangle *sdim; /* Temp screen dimensions */ info->modify_panning = 0; @@ -1836,15 +1788,15 @@ static int move_selected(CtkDisplayLayout *ctk_object, int x, int y, int snap) * fairly cleanly with common code, so do that here. */ if (info->orig_position_type != CONF_ADJ_ABSOLUTE) { - int p_x = (ctk_object->mouse_x - ctk_object->img_dim[X]) / + int p_x = (ctk_object->mouse_x - ctk_object->img_dim.x) / ctk_object->scale; - int p_y = (ctk_object->mouse_y - ctk_object->img_dim[Y]) / + int p_y = (ctk_object->mouse_y - ctk_object->img_dim.y) / ctk_object->scale; if (info->display) { - dim = info->display->cur_mode->relative_to->cur_mode->viewPortIn; + dim = &(info->display->cur_mode->relative_to->cur_mode->pan); } else { - dim = get_screen_dim(info->screen->relative_to, 0); + dim = get_screen_rect(info->screen->relative_to, 0); } if (dim) { @@ -1876,87 +1828,77 @@ static int move_selected(CtkDisplayLayout *ctk_object, int x, int y, int snap) /* Move via absolute positioning */ /* Compute pre-snap dimensions */ - info->modify_dim[X] += x; - info->modify_dim[Y] += y; + info->modify_dim.x += x; + info->modify_dim.y += y; - info->dst_dim[X] = info->modify_dim[X]; - info->dst_dim[Y] = info->modify_dim[Y]; - info->dst_dim[W] = info->modify_dim[W]; - info->dst_dim[H] = info->modify_dim[H]; + info->dst_dim = info->modify_dim; /* Snap to other screens and displays */ if (snap && ctk_object->snap_strength) { - info->src_dim[X] = info->dst_dim[X]; - info->src_dim[Y] = info->dst_dim[Y]; - info->src_dim[W] = info->dst_dim[W]; - info->src_dim[H] = info->dst_dim[H]; - + info->src_dim = info->dst_dim; snap_move(ctk_object); if (info->display) { /* Also snap display's panning box to other screens/displays */ - info->src_dim[W] = info->display->cur_mode->pan[W]; - info->src_dim[H] = info->display->cur_mode->pan[H]; - info->dst_dim[W] = info->src_dim[W]; - info->dst_dim[H] = info->src_dim[H]; + info->src_dim.width = info->display->cur_mode->pan.width; + info->src_dim.height = info->display->cur_mode->pan.height; + + info->dst_dim.width = info->src_dim.width; + info->dst_dim.height = info->src_dim.height; snap_move(ctk_object); } } /* Get the bounding dimensions of what is being moved */ - if (info->display) { - dim = info->display->cur_mode->pan; - } else { - dim = info->target_dim; - } - sdim = get_screen_dim(info->screen, 1); + dim = info->target_dim; + sdim = get_screen_rect(info->screen, 1); /* Prevent moving out of the max layout bounds */ - x = MAX_LAYOUT_WIDTH - dim[W]; - if (info->dst_dim[X] > x) { - info->modify_dim[X] += x - info->dst_dim[X]; - info->dst_dim[X] = x; + x = MAX_LAYOUT_WIDTH - dim->width; + if (info->dst_dim.x > x) { + info->modify_dim.x += x - info->dst_dim.x; + info->dst_dim.x = x; } - y = MAX_LAYOUT_HEIGHT - dim[H]; - if (info->dst_dim[Y] > y) { - info->modify_dim[Y] += y - info->dst_dim[Y]; - info->dst_dim[Y] = y; + y = MAX_LAYOUT_HEIGHT - dim->height; + if (info->dst_dim.y > y) { + info->modify_dim.y += y - info->dst_dim.y; + info->dst_dim.y = y; } - x = layout->dim[W] - MAX_LAYOUT_WIDTH; - if (info->dst_dim[X] < x) { - info->modify_dim[X] += x - info->dst_dim[X]; - info->dst_dim[X] = x; + x = layout->dim.width - MAX_LAYOUT_WIDTH; + if (info->dst_dim.x < x) { + info->modify_dim.x += x - info->dst_dim.x; + info->dst_dim.x = x; } - y = layout->dim[H] - MAX_LAYOUT_HEIGHT; - if (info->dst_dim[Y] < y) { - info->modify_dim[Y] += y - info->dst_dim[Y]; - info->dst_dim[Y] = y; + y = layout->dim.height - MAX_LAYOUT_HEIGHT; + if (info->dst_dim.y < y) { + info->modify_dim.y += y - info->dst_dim.y; + info->dst_dim.y = y; } /* Prevent screen from growing too big */ - x = sdim[X] + info->gpu->max_width - dim[W]; - if (info->dst_dim[X] > x) { - info->modify_dim[X] += x - info->dst_dim[X]; - info->dst_dim[X] = x; + x = sdim->x + info->gpu->max_width - dim->width; + if (info->dst_dim.x > x) { + info->modify_dim.x += x - info->dst_dim.x; + info->dst_dim.x = x; } - y = sdim[Y] + info->gpu->max_height - dim[H]; - if (info->dst_dim[Y] > y) { - info->modify_dim[Y] += y - info->dst_dim[Y]; - info->dst_dim[Y] = y; + y = sdim->y + info->gpu->max_height - dim->height; + if (info->dst_dim.y > y) { + info->modify_dim.y += y - info->dst_dim.y; + info->dst_dim.y = y; } - x = sdim[X] + sdim[W] - info->gpu->max_width; - if (info->dst_dim[X] < x) { - info->modify_dim[X] += x - info->dst_dim[X]; - info->dst_dim[X] = x; + x = sdim->x + sdim->width - info->gpu->max_width; + if (info->dst_dim.x < x) { + info->modify_dim.x += x - info->dst_dim.x; + info->dst_dim.x = x; } - y = sdim[Y] + sdim[H] - info->gpu->max_height; - if (info->dst_dim[Y] < y) { - info->modify_dim[Y] += y - info->dst_dim[Y]; - info->dst_dim[Y] = y; + y = sdim->y + sdim->height - info->gpu->max_height; + if (info->dst_dim.y < y) { + info->modify_dim.y += y - info->dst_dim.y; + info->dst_dim.y = y; } @@ -1965,8 +1907,8 @@ static int move_selected(CtkDisplayLayout *ctk_object, int x, int y, int snap) /* Move the screen */ nvDisplayPtr display; - x = info->dst_dim[X] - info->orig_dim[X]; - y = info->dst_dim[Y] - info->orig_dim[Y]; + x = info->dst_dim.x - info->orig_dim.x; + y = info->dst_dim.y - info->orig_dim.y; /* Offset the screen and all its displays */ offset_screen(info->screen, x, y); @@ -1978,10 +1920,8 @@ static int move_selected(CtkDisplayLayout *ctk_object, int x, int y, int snap) } else { /* Move the display to its destination */ - info->display->cur_mode->viewPortIn[X] = info->dst_dim[X]; - info->display->cur_mode->viewPortIn[Y] = info->dst_dim[Y]; - info->display->cur_mode->pan[X] = info->dst_dim[X]; - info->display->cur_mode->pan[Y] = info->dst_dim[Y]; + info->display->cur_mode->pan.x = info->dst_dim.x; + info->display->cur_mode->pan.y = info->dst_dim.y; /* If the screen of the display that was moved is using absolute * positioning, we should check to see if the position of the @@ -1997,8 +1937,8 @@ static int move_selected(CtkDisplayLayout *ctk_object, int x, int y, int snap) resolve_displays_in_screen(info->screen, 0); calc_metamode(info->screen, info->screen->cur_metamode); - x = info->screen->cur_metamode->dim[X] - info->orig_screen_dim[X]; - y = info->screen->cur_metamode->dim[Y] - info->orig_screen_dim[Y]; + x = info->screen->cur_metamode->dim.x - info->orig_screen_dim.x; + y = info->screen->cur_metamode->dim.y - info->orig_screen_dim.y; if (x || y) { nvDisplayPtr other; @@ -2036,17 +1976,17 @@ static int move_selected(CtkDisplayLayout *ctk_object, int x, int y, int snap) /* If what we moved required the layout to be shifted, offset * the modify dim (used for snapping) by the same displacement. */ - x = info->target_dim[X] - info->dst_dim[X]; - y = info->target_dim[Y] - info->dst_dim[Y]; + x = info->target_dim->x - info->dst_dim.x; + y = info->target_dim->y - info->dst_dim.y; if (x || y) { - info->modify_dim[X] += x; - info->modify_dim[Y] += y; + info->modify_dim.x += x; + info->modify_dim.y += y; } /* Check if the item being moved has a new position */ if (*(info->target_position_type) != info->orig_position_type || - info->target_dim[X] != info->orig_dim[X] || - info->target_dim[Y] != info->orig_dim[Y]) { + info->target_dim->x != info->orig_dim.x || + info->target_dim->y != info->orig_dim.y) { modified = 1; } @@ -2070,7 +2010,7 @@ static int pan_selected(CtkDisplayLayout *ctk_object, int x, int y, int snap) ModifyInfo *info = &(ctk_object->modify_info); int modified = 0; - int *dim; + GdkRectangle *dim; int extra; @@ -2087,96 +2027,69 @@ static int pan_selected(CtkDisplayLayout *ctk_object, int x, int y, int snap) /* Compute pre-snap dimensions */ - info->modify_dim[W] += x; - info->modify_dim[H] += y; + info->modify_dim.width += x; + info->modify_dim.height += y; - /* Don't allow the panning domain to get too small */ + /* Don't allow the thing being modified to get too small */ if (info->display) { - dim = info->display->cur_mode->viewPortIn; - if (info->modify_dim[W] < dim[W]) { - info->modify_dim[W] = dim[W]; - } - if (info->modify_dim[H] < dim[H]) { - info->modify_dim[H] = dim[H]; - } + clamp_rect_to_viewportin(&(info->modify_dim), info->display->cur_mode); } else if (info->screen->no_scanout) { - if (info->modify_dim[W] < 304) { - info->modify_dim[W] = 304; - } - if (info->modify_dim[H] < 200) { - info->modify_dim[H] = 200; - } + clamp_screen_size_rect(&(info->modify_dim)); } - info->dst_dim[W] = info->modify_dim[W]; - info->dst_dim[H] = info->modify_dim[H]; + info->dst_dim.width = info->modify_dim.width; + info->dst_dim.height = info->modify_dim.height; /* Snap to other screens and dimensions */ if (snap && ctk_object->snap_strength) { - - info->src_dim[X] = info->dst_dim[X]; - info->src_dim[Y] = info->dst_dim[Y]; - info->src_dim[W] = info->dst_dim[W]; - info->src_dim[H] = info->dst_dim[H]; - + info->src_dim = info->dst_dim; snap_pan(ctk_object); } /* Make sure no-scanout virtual screen width is at least a multiple of 8 */ if (info->screen->no_scanout) { - extra = (info->dst_dim[W] % 8); + extra = (info->dst_dim.width % 8); if (extra > 0) { - info->dst_dim[W] += (8 - extra); + info->dst_dim.width += (8 - extra); } } /* Panning should not cause us to exceed the maximum layout dimensions */ - x = MAX_LAYOUT_WIDTH - info->dst_dim[X]; - if (info->dst_dim[W] > x) { - info->modify_dim[W] += x - info->dst_dim[W]; - info->dst_dim[W] = x; + x = MAX_LAYOUT_WIDTH - info->dst_dim.x; + if (info->dst_dim.width > x) { + info->modify_dim.width += x - info->dst_dim.width; + info->dst_dim.width = x; } - y = MAX_LAYOUT_HEIGHT - info->dst_dim[Y]; - if (info->dst_dim[H] > y) { - info->modify_dim[H] += y - info->dst_dim[H]; - info->dst_dim[H] = y; + y = MAX_LAYOUT_HEIGHT - info->dst_dim.y; + if (info->dst_dim.height > y) { + info->modify_dim.height += y - info->dst_dim.height; + info->dst_dim.height = y; } /* Panning should not cause us to exceed the maximum screen dimensions */ - dim = get_screen_dim(info->screen, 1); - x = dim[X] + info->gpu->max_width - info->dst_dim[X]; - if (info->dst_dim[W] > x) { - info->modify_dim[W] += x - info->dst_dim[W]; - info->dst_dim[W] = x; + dim = get_screen_rect(info->screen, 1); + x = dim->x + info->gpu->max_width - info->dst_dim.x; + if (info->dst_dim.width > x) { + info->modify_dim.width += x - info->dst_dim.width; + info->dst_dim.width = x; } - y = dim[Y] + info->gpu->max_height - info->dst_dim[Y]; - if (info->dst_dim[H] > y) { - info->modify_dim[H] += y - info->dst_dim[H]; - info->dst_dim[H] = y; + y = dim->y + info->gpu->max_height - info->dst_dim.y; + if (info->dst_dim.height > y) { + info->modify_dim.height += y - info->dst_dim.height; + info->dst_dim.height = y; } /* Panning domain can never be smaller then the display ViewPortIn */ if (info->display) { - dim = info->display->cur_mode->viewPortIn; - if (info->dst_dim[W] < dim[W]) { - info->dst_dim[W] = dim[W]; - } - if (info->dst_dim[H] < dim[H]) { - info->dst_dim[H] = dim[H]; - } + clamp_rect_to_viewportin(&(info->dst_dim), info->display->cur_mode); } else if (info->screen->no_scanout) { - if (info->dst_dim[W] < 304) { - info->dst_dim[W] = 304; - } - if (info->dst_dim[H] < 200) { - info->dst_dim[H] = 200; - } + clamp_screen_size_rect(&(info->dst_dim)); } /* Assign the new size */ - info->target_dim[W] = info->dst_dim[W]; - info->target_dim[H] = info->dst_dim[H]; + info->target_dim->width = info->dst_dim.width; + info->target_dim->height = info->dst_dim.height; /* Recalculate layout dimensions and scaling */ @@ -2190,8 +2103,8 @@ static int pan_selected(CtkDisplayLayout *ctk_object, int x, int y, int snap) /* Check if the item being moved has a new position */ /* Report if anything changed */ - if (info->target_dim[W] != info->orig_dim[W] || - info->target_dim[H] != info->orig_dim[H]) { + if (info->target_dim->width != info->orig_dim.width || + info->target_dim->height != info->orig_dim.height) { modified = 1; } @@ -2347,7 +2260,7 @@ static void select_display(CtkDisplayLayout *ctk_object, nvDisplayPtr display) * */ -#define DIST_SQR(D) (((D)[X] * (D)[X]) + ((D)[Y] * (D)[Y])) +#define DIST_SQR(D) (((D).x * (D).x) + ((D).y * (D).y)) static void select_default_item(CtkDisplayLayout *ctk_object) { @@ -2368,7 +2281,7 @@ static void select_default_item(CtkDisplayLayout *ctk_object) /* Ignore disabled displays */ if (!display->cur_mode) continue; - dst = DIST_SQR(display->cur_mode->viewPortIn); + dst = DIST_SQR(display->cur_mode->pan); if (best_dst < 0 || dst < best_dst) { best_dst = dst; sel_display = display; @@ -2554,21 +2467,19 @@ static char *get_tooltip_under_mouse(CtkDisplayLayout *ctk_object, nvDisplayPtr display = NULL; nvScreenPtr screen = NULL; char *tip = NULL; - int *sdim; - - + + /* Scale and offset x & y so they reside in clickable area */ - x = (x -ctk_object->img_dim[X]) / ctk_object->scale; - y = (y -ctk_object->img_dim[Y]) / ctk_object->scale; + x = (x -ctk_object->img_dim.x) / ctk_object->scale; + y = (y -ctk_object->img_dim.y) / ctk_object->scale; /* Go through the Z-order looking for what we are under */ for (i = 0; i < ctk_object->Zcount; i++) { - + if (ctk_object->Zorder[i].type == ZNODE_TYPE_DISPLAY) { display = ctk_object->Zorder[i].u.display; - if (display->cur_mode && - point_in_dim(display->cur_mode->pan, x, y)) { + if (point_in_display(display, x, y)) { screen = NULL; if (display == last_display) { goto found; @@ -2579,8 +2490,7 @@ static char *get_tooltip_under_mouse(CtkDisplayLayout *ctk_object, } else if (ctk_object->Zorder[i].type == ZNODE_TYPE_SCREEN) { screen = ctk_object->Zorder[i].u.screen; - sdim = get_screen_dim(screen, 1); - if (point_in_dim(sdim, x, y)) { + if (point_in_screen(screen, x, y)) { display = NULL; if (screen == last_screen) { goto found; @@ -2622,7 +2532,6 @@ static int click_layout(CtkDisplayLayout *ctk_object, int x, int y) nvScreenPtr cur_selected_screen = ctk_object->selected_screen; nvDisplayPtr display; nvScreenPtr screen; - int *sdim; GdkModifierType state; @@ -2639,17 +2548,14 @@ static int click_layout(CtkDisplayLayout *ctk_object, int x, int y) for (i = 0; i < ctk_object->Zcount; i++) { if (ctk_object->Zorder[i].type == ZNODE_TYPE_DISPLAY) { display = ctk_object->Zorder[i].u.display; - if (display->cur_mode && - point_in_dim(display->cur_mode->pan, x, y)) { - + if (point_in_display(display, x, y)) { select_display(ctk_object, display); ctk_object->clicked_outside = 0; break; } } else if (ctk_object->Zorder[i].type == ZNODE_TYPE_SCREEN) { screen = ctk_object->Zorder[i].u.screen; - sdim = get_screen_dim(screen, 1); - if (point_in_dim(sdim, x, y)) { + if (point_in_screen(screen, x, y)) { select_screen(ctk_object, screen); ctk_object->clicked_outside = 0; break; @@ -2874,16 +2780,13 @@ static GdkGC *get_widget_fg_gc(GtkWidget *widget) **/ static void draw_rect(CtkDisplayLayout *ctk_object, - int *dim, + GdkRectangle *rect, GdkColor *color, int fill) { GtkWidget *drawing_area = ctk_object->drawing_area; GdkGC *fg_gc = get_widget_fg_gc(drawing_area); - int w = (int)(ctk_object->scale * (dim[X] + dim[W])) - (int)(ctk_object->scale * (dim[X])); - int h = (int)(ctk_object->scale * (dim[Y] + dim[H])) - (int)(ctk_object->scale * (dim[Y])); - /* Setup color to use */ gdk_gc_set_rgb_fg_color(fg_gc, color); @@ -2891,10 +2794,10 @@ static void draw_rect(CtkDisplayLayout *ctk_object, gdk_draw_rectangle(ctk_object->pixmap, fg_gc, fill, - ctk_object->img_dim[X] + ctk_object->scale * dim[X], - ctk_object->img_dim[Y] + ctk_object->scale * dim[Y], - w, - h); + ctk_object->img_dim.x + ctk_object->scale * rect->x, + ctk_object->img_dim.y + ctk_object->scale * rect->y, + ctk_object->scale * rect->width, + ctk_object->scale * rect->height); } /* draw_rect() */ @@ -2908,7 +2811,7 @@ static void draw_rect(CtkDisplayLayout *ctk_object, **/ static void draw_rect_strs(CtkDisplayLayout *ctk_object, - int *dim, + GdkRectangle *rect, GdkColor *color, const char *str_1, const char *str_2) @@ -2929,8 +2832,8 @@ static void draw_rect_strs(CtkDisplayLayout *ctk_object, pango_layout_set_text(ctk_object->pango_layout, str_1, -1); pango_layout_get_pixel_size(ctk_object->pango_layout, &txt_w, &txt_h); - if (txt_w +8 <= ctk_object->scale * dim[W] && - txt_h +8 <= ctk_object->scale * dim[H]) { + if (txt_w +8 <= ctk_object->scale * rect->width && + txt_h +8 <= ctk_object->scale * rect->height) { draw_1 = 1; } } @@ -2939,8 +2842,8 @@ static void draw_rect_strs(CtkDisplayLayout *ctk_object, pango_layout_set_text(ctk_object->pango_layout, str_2, -1); pango_layout_get_pixel_size(ctk_object->pango_layout, &txt_w, &txt_h); - if (txt_w +8 <= ctk_object->scale * dim[W] && - txt_h +8 <= ctk_object->scale * dim[H]) { + if (txt_w +8 <= ctk_object->scale * rect->width && + txt_h +8 <= ctk_object->scale * rect->height) { draw_2 = 1; } @@ -2950,7 +2853,7 @@ static void draw_rect_strs(CtkDisplayLayout *ctk_object, pango_layout_get_pixel_size(ctk_object->pango_layout, &txt_w, &txt_h); if (draw_1 && draw_2 && - txt_h +8 > ctk_object->scale * dim[H]) { + txt_h +8 > ctk_object->scale * rect->height) { draw_2 = 0; } @@ -2961,16 +2864,16 @@ static void draw_rect_strs(CtkDisplayLayout *ctk_object, pango_layout_set_text(ctk_object->pango_layout, str_1, -1); pango_layout_get_pixel_size(ctk_object->pango_layout, &txt_w, &txt_h); - txt_x1 = ctk_object->scale*(dim[X] + dim[W] / 2) - (txt_w / 2); - txt_y1 = ctk_object->scale*(dim[Y] + dim[H] / 2) - (txt_h / 2); + txt_x1 = ctk_object->scale*(rect->x + rect->width / 2) - (txt_w / 2); + txt_y1 = ctk_object->scale*(rect->y + rect->height / 2) - (txt_h / 2); /* Write name */ gdk_gc_set_rgb_fg_color(fg_gc, color); gdk_draw_layout(ctk_object->pixmap, fg_gc, - ctk_object->img_dim[X] + txt_x1, - ctk_object->img_dim[Y] + txt_y1, + ctk_object->img_dim.x + txt_x1, + ctk_object->img_dim.y + txt_y1, ctk_object->pango_layout); } @@ -2978,16 +2881,16 @@ static void draw_rect_strs(CtkDisplayLayout *ctk_object, pango_layout_set_text(ctk_object->pango_layout, str_2, -1); pango_layout_get_pixel_size(ctk_object->pango_layout, &txt_w, &txt_h); - txt_x2 = ctk_object->scale*(dim[X] + dim[W] / 2) - (txt_w / 2); - txt_y2 = ctk_object->scale*(dim[Y] + dim[H] / 2) - (txt_h / 2); + txt_x2 = ctk_object->scale*(rect->x + rect->width / 2) - (txt_w / 2); + txt_y2 = ctk_object->scale*(rect->y + rect->height / 2) - (txt_h / 2); /* Write dimensions */ gdk_gc_set_rgb_fg_color(fg_gc, color); gdk_draw_layout(ctk_object->pixmap, fg_gc, - ctk_object->img_dim[X] + txt_x2, - ctk_object->img_dim[Y] + txt_y2, + ctk_object->img_dim.x + txt_x2, + ctk_object->img_dim.y + txt_y2, ctk_object->pango_layout); } @@ -2997,16 +2900,16 @@ static void draw_rect_strs(CtkDisplayLayout *ctk_object, pango_layout_set_text(ctk_object->pango_layout, str, -1); pango_layout_get_pixel_size(ctk_object->pango_layout, &txt_w, &txt_h); - txt_x = ctk_object->scale*(dim[X] + dim[W] / 2) - (txt_w / 2); - txt_y = ctk_object->scale*(dim[Y] + dim[H] / 2) - (txt_h / 2); + txt_x = ctk_object->scale*(rect->x + rect->width / 2) - (txt_w / 2); + txt_y = ctk_object->scale*(rect->y + rect->height / 2) - (txt_h / 2); /* Write both */ gdk_gc_set_rgb_fg_color(fg_gc, color); gdk_draw_layout(ctk_object->pixmap, fg_gc, - ctk_object->img_dim[X] + txt_x, - ctk_object->img_dim[Y] + txt_y, + ctk_object->img_dim.x + txt_x, + ctk_object->img_dim.y + txt_y, ctk_object->pango_layout); g_free(str); @@ -3029,6 +2932,7 @@ static void draw_display(CtkDisplayLayout *ctk_object, int base_color_idx; int color_idx; char *tmp_str; + GdkRectangle rect; if (!display || !(display->cur_mode)) { return; @@ -3041,29 +2945,29 @@ static void draw_display(CtkDisplayLayout *ctk_object, /* Draw panning */ color_idx = base_color_idx + ((mode->modeline) ? BG_PAN_ON : BG_PAN_OFF); - draw_rect(ctk_object, mode->pan, &(ctk_object->color_palettes[color_idx]), - 1); - draw_rect(ctk_object, mode->pan, &(ctk_object->fg_color), 0); + draw_rect(ctk_object, &(mode->pan), + &(ctk_object->color_palettes[color_idx]), 1); + draw_rect(ctk_object, &(mode->pan), &(ctk_object->fg_color), 0); /* Draw ViewPortIn */ + get_viewportin_rect(mode, &rect); color_idx = base_color_idx + ((mode->modeline) ? BG_SCR_ON : BG_SCR_OFF); - draw_rect(ctk_object, mode->viewPortIn, &(ctk_object->color_palettes[color_idx]), - 1); - draw_rect(ctk_object, mode->viewPortIn, &(ctk_object->fg_color), 0); + draw_rect(ctk_object, &rect, &(ctk_object->color_palettes[color_idx]), 1); + draw_rect(ctk_object, &rect, &(ctk_object->fg_color), 0); /* Draw text information */ if (!mode->display->screen) { tmp_str = g_strdup("(Disabled)"); } else if (mode->modeline) { - tmp_str = g_strdup_printf("%dx%d", mode->viewPortIn[W], - mode->viewPortIn[H]); + tmp_str = g_strdup_printf("%dx%d", mode->viewPortIn.width, + mode->viewPortIn.height); } else { tmp_str = g_strdup("(Off)"); } draw_rect_strs(ctk_object, - mode->viewPortIn, + &rect, &(ctk_object->fg_color), display->logName, tmp_str); @@ -3085,7 +2989,7 @@ static void draw_screen(CtkDisplayLayout *ctk_object, GtkWidget *drawing_area = ctk_object->drawing_area; GdkGC *fg_gc = get_widget_fg_gc(drawing_area); - int *sdim; /* Screen dimensions */ + GdkRectangle *sdim; /* Screen dimensions */ GdkColor bg_color; /* Background color */ GdkColor bd_color; /* Border color */ char *tmp_str; @@ -3098,7 +3002,7 @@ static void draw_screen(CtkDisplayLayout *ctk_object, gdk_color_parse("#888888", &bg_color); gdk_color_parse("#777777", &bd_color); - sdim = get_screen_dim(screen, 1); + sdim = get_screen_rect(screen, 1); /* Draw the screen background */ draw_rect(ctk_object, sdim, &bg_color, 1); @@ -3108,16 +3012,16 @@ static void draw_screen(CtkDisplayLayout *ctk_object, GDK_CAP_NOT_LAST, GDK_JOIN_ROUND); draw_rect(ctk_object, sdim, &(ctk_object->fg_color), 0); - + gdk_gc_set_line_attributes(fg_gc, 1, GDK_LINE_SOLID, GDK_CAP_NOT_LAST, GDK_JOIN_ROUND); /* Show the name of the screen if no-scanout is selected */ if (screen->no_scanout) { tmp_str = g_strdup_printf("X Screen %d", screen->scrnum); - + draw_rect_strs(ctk_object, - screen->dim, + &(screen->dim), &(ctk_object->fg_color), tmp_str, "(No Scanout)"); @@ -3163,17 +3067,20 @@ static void draw_layout(CtkDisplayLayout *ctk_object) int w, h; int size; /* Hilite line size */ int offset; /* Hilite box offset */ - int *dim; + GdkRectangle vpiRect; + GdkRectangle *rect; if (ctk_object->selected_display) { - dim = ctk_object->selected_display->cur_mode->viewPortIn; + get_viewportin_rect(ctk_object->selected_display->cur_mode, + &vpiRect); + rect = &vpiRect; } else { - dim = get_screen_dim(ctk_object->selected_screen, 0); + rect = get_screen_rect(ctk_object->selected_screen, 0); } /* Draw red selection border */ - w = (int)(ctk_object->scale * (dim[X] + dim[W])) - (int)(ctk_object->scale * (dim[X])); - h = (int)(ctk_object->scale * (dim[Y] + dim[H])) - (int)(ctk_object->scale * (dim[Y])); + w = (int)(ctk_object->scale * rect->width); + h = (int)(ctk_object->scale * rect->height); /* Setup color to use */ gdk_gc_set_rgb_fg_color(fg_gc, &(ctk_object->select_color)); @@ -3183,8 +3090,8 @@ static void draw_layout(CtkDisplayLayout *ctk_object) offset = (size/2) +1; if ((w -(2* offset) < 0) || (h -(2 *offset) < 0)) { - draw_rect(ctk_object, dim, &(ctk_object->select_color), 1); - draw_rect(ctk_object, dim, &(ctk_object->fg_color), 0); + draw_rect(ctk_object, rect, &(ctk_object->select_color), 1); + draw_rect(ctk_object, rect, &(ctk_object->fg_color), 0); } else { gdk_gc_set_line_attributes(fg_gc, size, GDK_LINE_SOLID, @@ -3193,8 +3100,8 @@ static void draw_layout(CtkDisplayLayout *ctk_object) gdk_draw_rectangle(ctk_object->pixmap, fg_gc, 0, - ctk_object->img_dim[X] +(ctk_object->scale * dim[X]) +offset, - ctk_object->img_dim[Y] +(ctk_object->scale * dim[Y]) +offset, + ctk_object->img_dim.x +(ctk_object->scale * rect->x) +offset, + ctk_object->img_dim.y +(ctk_object->scale * rect->y) +offset, w -(2 * offset), h -(2 * offset)); @@ -3218,14 +3125,14 @@ static void draw_layout(CtkDisplayLayout *ctk_object) // with display devices that are "off" gdk_color_parse("#0000FF", &bg_color); draw_rect(ctk_object, - ctk_object->selected_screen->cur_metamode->viewPortIn, + &(ctk_object->selected_screen->cur_metamode->viewPortIn), &(bg_color), 0); // Shows the current screen dimensions used for relative // positioning of the screen (w/o displays that are "off") gdk_color_parse("#FF00FF", &bg_color); draw_rect(ctk_object, - ctk_object->selected_screen->cur_metamode->edim, + &(ctk_object->selected_screen->cur_metamode->edim), &(bg_color), 0); } } @@ -3318,8 +3225,8 @@ static Bool sync_layout(nvLayoutPtr layout) calc_layout(layout); /* If layout needs to offset, it was modified */ - if (layout->dim[X] || layout->dim[Y]) { - offset_layout(layout, -layout->dim[X], -layout->dim[Y]); + if (layout->dim.x || layout->dim.y) { + offset_layout(layout, -layout->dim.x, -layout->dim.y); modified = TRUE; } @@ -3689,13 +3596,15 @@ void ctk_display_layout_disable_display(CtkDisplayLayout *ctk_object, /** ctk_display_layout_set_mode_modeline() *************************** * - * Sets which modeline the mode should use. + * Sets which modeline, ViewPortIn and ViewPortOut the mode should use. * **/ void ctk_display_layout_set_mode_modeline(CtkDisplayLayout *ctk_object, nvModePtr mode, - nvModeLinePtr modeline) + nvModeLinePtr modeline, + const nvSize *viewPortIn, + const GdkRectangle *viewPortOut) { nvModeLinePtr old_modeline; @@ -3705,16 +3614,8 @@ void ctk_display_layout_set_mode_modeline(CtkDisplayLayout *ctk_object, /* Set the new modeline */ old_modeline = mode->modeline; - mode->modeline = modeline; - - /* Setup the mode's dimensions based on the modeline */ - if (modeline) { - mode_set_dims_from_modeline(mode, modeline); - } else if (mode->display) { - /* Display is being turned off, set the default width/height */ - mode_set_dims_from_modeline(mode, mode->display->modelines); - } + mode_set_modeline(mode, modeline, viewPortIn, viewPortOut); /* In advanced mode, changing the resolution a display uses for a * particular metamode should make this metamode non-implicit. @@ -3745,7 +3646,8 @@ void ctk_display_layout_set_mode_modeline(CtkDisplayLayout *ctk_object, */ void ctk_display_layout_set_mode_viewport_in(CtkDisplayLayout *ctk_object, nvModePtr mode, - int w, int h) + int w, int h, + Bool update_panning_size) { Bool modified = TRUE; @@ -3760,17 +3662,16 @@ void ctk_display_layout_set_mode_viewport_in(CtkDisplayLayout *ctk_object, h = 1; } - mode->viewPortIn[W] = w; - mode->viewPortIn[H] = h; + mode->viewPortIn.width = w; + mode->viewPortIn.height = h; - /* Clamp the panning domain to the new ViewPortIn dimensions */ - if (mode->pan[W] < mode->viewPortIn[W]) { - mode->pan[W] = mode->viewPortIn[W]; - } - if (mode->pan[H] < mode->viewPortIn[H]) { - mode->pan[H] = mode->viewPortIn[H]; + if (update_panning_size) { + mode->pan.width = w; + mode->pan.height = h; } + clamp_mode_panning(mode); + if (modified) { /* Update the layout */ ctk_display_layout_update(ctk_object); @@ -3845,10 +3746,10 @@ void ctk_display_layout_set_mode_viewport_out(CtkDisplayLayout *ctk_object, y = 0; } - mode->viewPortOut[X] = x; - mode->viewPortOut[Y] = y; - mode->viewPortOut[W] = w; - mode->viewPortOut[H] = h; + mode->viewPortOut.x = x; + mode->viewPortOut.y = y; + mode->viewPortOut.width = w; + mode->viewPortOut.height = h; if (modified) { /* Update the layout */ @@ -3939,15 +3840,15 @@ void ctk_display_layout_set_display_position(CtkDisplayLayout *ctk_object, /* Do the move by offsetting */ ctk_object->modify_info.modify_dirty = 1; modified = move_selected(ctk_object, - x - display->cur_mode->viewPortIn[X], - y - display->cur_mode->viewPortIn[Y], + x - display->cur_mode->pan.x, + y - display->cur_mode->pan.y, 0); /* Report back result of move */ if (ctk_object->modified_callback && (modified || - x != display->cur_mode->viewPortIn[X] || - y != display->cur_mode->viewPortIn[Y])) { + x != display->cur_mode->pan.x || + y != display->cur_mode->pan.y)) { ctk_object->modified_callback (ctk_object->layout, ctk_object->modified_callback_data); } @@ -3986,15 +3887,15 @@ void ctk_display_layout_set_display_panning(CtkDisplayLayout *ctk_object, /* Change the panning */ ctk_object->modify_info.modify_dirty = 1; modified = pan_selected(ctk_object, - width - display->cur_mode->pan[W], - height - display->cur_mode->pan[H], + width - display->cur_mode->pan.width, + height - display->cur_mode->pan.height, 0); /* Report back result of move */ if (ctk_object->modified_callback && (modified || - width != display->cur_mode->pan[W] || - height != display->cur_mode->pan[H])) { + width != display->cur_mode->pan.width || + height != display->cur_mode->pan.height)) { ctk_object->modified_callback(ctk_object->layout, ctk_object->modified_callback_data); } @@ -4213,14 +4114,14 @@ void ctk_display_layout_set_screen_virtual_size(CtkDisplayLayout *ctk_object, // what is selected. ctk_object->modify_info.modify_dirty = 1; modified = pan_selected(ctk_object, - width - screen->dim[W], - height - screen->dim[H], + width - screen->dim.width, + height - screen->dim.height, 0); if (ctk_object->modified_callback && (modified || - width != screen->dim[W] || - height != screen->dim[H])) { + width != screen->dim.width || + height != screen->dim.height)) { ctk_object->modified_callback(ctk_object->layout, ctk_object->modified_callback_data); } @@ -4297,9 +4198,9 @@ void ctk_display_layout_set_screen_position(CtkDisplayLayout *ctk_object, case CONF_ADJ_ABSOLUTE: { nvDisplayPtr other; - int x_offset = x - screen->dim[X]; - int y_offset = y - screen->dim[Y]; - int *sdim; + int x_offset = x - screen->dim.x; + int y_offset = y - screen->dim.y; + GdkRectangle *sdim; /* Make sure this screen use absolute positioning */ switch_screen_to_absolute(screen); @@ -4316,8 +4217,8 @@ void ctk_display_layout_set_screen_position(CtkDisplayLayout *ctk_object, ctk_display_layout_update(ctk_object); /* Report back result of move */ - sdim = get_screen_dim(screen, 1); - if (x != sdim[X] || y != sdim[Y]) { + sdim = get_screen_rect(screen, 1); + if (x != sdim->x || y != sdim->y) { modified = 1; } @@ -4474,10 +4375,10 @@ configure_event_callback(GtkWidget *widget, GdkEventConfigure *event, int width = widget->allocation.width; int height = widget->allocation.height; - ctk_object->img_dim[X] = LAYOUT_IMG_OFFSET + LAYOUT_IMG_BORDER_PADDING; - ctk_object->img_dim[Y] = LAYOUT_IMG_OFFSET + LAYOUT_IMG_BORDER_PADDING; - ctk_object->img_dim[W] = width -2*(ctk_object->img_dim[X]); - ctk_object->img_dim[H] = height -2*(ctk_object->img_dim[Y]); + ctk_object->img_dim.x = LAYOUT_IMG_OFFSET + LAYOUT_IMG_BORDER_PADDING; + ctk_object->img_dim.y = LAYOUT_IMG_OFFSET + LAYOUT_IMG_BORDER_PADDING; + ctk_object->img_dim.width = width -2*(ctk_object->img_dim.x); + ctk_object->img_dim.height = height -2*(ctk_object->img_dim.y); sync_scaling(ctk_object); @@ -4602,8 +4503,8 @@ button_press_event_callback(GtkWidget *widget, GdkEventButton *event, CtkDisplayLayout *ctk_object = CTK_DISPLAY_LAYOUT(data); /* Scale and offset x & y so they reside in the clickable area */ - int x = (event->x -ctk_object->img_dim[X]) / ctk_object->scale; - int y = (event->y -ctk_object->img_dim[Y]) / ctk_object->scale; + int x = (event->x -ctk_object->img_dim.x) / ctk_object->scale; + int y = (event->y -ctk_object->img_dim.y) / ctk_object->scale; GdkEvent *next_event; diff --git a/src/gtk+-2.x/ctkdisplaylayout.h b/src/gtk+-2.x/ctkdisplaylayout.h index cbf1a6b..82d1dd8 100644 --- a/src/gtk+-2.x/ctkdisplaylayout.h +++ b/src/gtk+-2.x/ctkdisplaylayout.h @@ -57,17 +57,6 @@ G_BEGIN_DECLS #define MAX_DEVICES 8 /* Max number of GPUs */ -/* Rectangle/Dim data positions */ -#define LEFT 0 -#define TOP 1 -#define WIDTH 2 -#define HEIGHT 3 -#define X LEFT -#define Y TOP -#define W WIDTH -#define H HEIGHT - - /* XF86VIDMODE */ #define V_PHSYNC 0x0001 #define V_NHSYNC 0x0002 @@ -151,6 +140,13 @@ typedef enum { METAMODE_SOURCE_RANDR, } MetaModeSource; + +typedef struct nvSizeRec { + int width; + int height; +} nvSize; + + typedef struct nvModeLineRec { struct nvModeLineRec *next; @@ -174,6 +170,10 @@ typedef struct nvSelectedModeRec { nvModeLinePtr modeline; /* Modeline this mode references */ Bool isSpecial; /* Whether this mode is "Off" or "Auto" */ + Bool isScaled; /* Whether custom viewports are set */ + + nvSize viewPortIn; + GdkRectangle viewPortOut; } nvSelectedMode, *nvSelectedModePtr; @@ -201,10 +201,10 @@ typedef struct nvModeRec { struct nvModeLineRec *modeline; /* Modeline this mode references */ int dummy; /* Dummy mode, don't print out */ - int viewPortIn[4]; /* Viewport In (absolute) */ - int pan[4]; /* Panning Domain (absolute) */ + nvSize viewPortIn; /* Viewport In */ + GdkRectangle pan; /* Panning Domain (absolute) */ - int viewPortOut[4]; /* ViewPort Out (WH) */ + GdkRectangle viewPortOut; /* ViewPort Out (WH) */ int position_type; /* Relative, Absolute, etc. */ struct nvDisplayRec *relative_to; /* Display Relative/RightOf etc */ @@ -265,10 +265,10 @@ typedef struct nvMetaModeRec { Bool switchable; /* Can the metamode be accessed through Ctrl Alt +- */ // Used for drawing & moving metamode boxes - int dim[4]; /* Bounding box of all modes */ + GdkRectangle dim; /* Bounding box of all modes */ // Used for applying and generating metamodes (effective dimensions) - int edim[4]; /* Bounding box of all non-NULL modes */ + GdkRectangle edim; /* Bounding box of all non-NULL modes */ char *string; /* Temp string used for modifying the metamode list */ @@ -311,7 +311,7 @@ typedef struct nvScreenRec { int cur_metamode_idx; /* Current metamode to display */ nvDisplayPtr primaryDisplay; // Used for generating metamode strings. - int dim[4]; /* Bounding box of all metamodes (Absolute coords) */ + GdkRectangle dim; /* Bounding box of all metamodes (Absolute coords) */ int position_type; /* Relative, Absolute, etc. */ struct nvScreenRec *relative_to; /* Screen Relative/RightOf etc */ @@ -382,7 +382,7 @@ typedef struct nvLayoutRec { int num_screens; /* Used for drawing the layout */ - int dim[4]; /* Bounding box of All X screens (Absolute coords) */ + GdkRectangle dim; /* Bounding box of All X screens (Absolute coords) */ int xinerama_enabled; @@ -403,14 +403,14 @@ typedef struct ModifyInfoRec { /* What to move */ nvDisplayPtr display; nvScreenPtr screen; - int orig_screen_dim[4]; // Used when moding display = moding screen. + GdkRectangle orig_screen_dim; // Used when moding display = moding screen. nvGpuPtr gpu; int orig_position_type; // Original values of what is being - int orig_dim[4]; // modified. + GdkRectangle orig_dim; // modified. int *target_position_type; // Pointers to values of thing that - int *target_dim; // is being modified. + GdkRectangle *target_dim; // is being modified. /* Snapping */ @@ -418,12 +418,12 @@ typedef struct ModifyInfoRec { int best_snap_v; int best_snap_h; - int modify_dirty; // Sync the modify_dim before moving/panning. - int modify_panning; // Modifying panning (instead of position) - int modify_dim[4]; // Dimensions to snap from + int modify_dirty; // Sync the modify_dim before moving/panning. + int modify_panning; // Modifying panning (instead of position) + GdkRectangle modify_dim; // Dimensions to snap from - int src_dim[4]; // Pre-snap (To allow snapping of pan box on move) - int dst_dim[4]; // Post-snap position + GdkRectangle src_dim; // Pre-snap (To allow snapping of pan box on move) + GdkRectangle dst_dim; // Post-snap position } ModifyInfo; @@ -463,8 +463,8 @@ typedef struct _CtkDisplayLayout GdkPixmap *pixmap; /* Image information */ - int img_dim[4]; /* Dimensions used to draw in */ - float scale; + GdkRectangle img_dim; + float scale; /* Colors */ GdkColor *color_palettes; /* Colors to use to display screens */ @@ -538,13 +538,16 @@ nvScreenPtr ctk_display_layout_get_selected_screen (CtkDisplayLayout *); nvGpuPtr ctk_display_layout_get_selected_gpu (CtkDisplayLayout *); -void ctk_display_layout_set_mode_modeline (CtkDisplayLayout *, - nvModePtr mode, - nvModeLinePtr modeline); +void ctk_display_layout_set_mode_modeline(CtkDisplayLayout *, + nvModePtr mode, + nvModeLinePtr modeline, + const nvSize *viewPortIn, + const GdkRectangle *viewPortOut); void ctk_display_layout_set_mode_viewport_in(CtkDisplayLayout *ctk_object, nvModePtr mode, - int w, int h); + int w, int h, + Bool update_panning_size); void ctk_display_layout_set_mode_viewport_out(CtkDisplayLayout *ctk_object, nvModePtr mode, diff --git a/src/gtk+-2.x/ctkditheringcontrols.c b/src/gtk+-2.x/ctkditheringcontrols.c index 51ec82b..f8204de 100644 --- a/src/gtk+-2.x/ctkditheringcontrols.c +++ b/src/gtk+-2.x/ctkditheringcontrols.c @@ -32,6 +32,7 @@ #include "ctkconfig.h" #include "ctkhelp.h" #include "ctkditheringcontrols.h" +#include "ctkdropdownmenu.h" /* function prototypes */ static gboolean build_dithering_mode_table(CtkDitheringControls *ctk_dithering_controls); @@ -41,13 +42,21 @@ static gint map_nvctrl_value_to_table(CtkDitheringControls *ctk_dithering_contro static Bool update_dithering_info(gpointer user_data); +static void setup_dithering_info(CtkDitheringControls *ctk_dithering_controls); + static void setup_reset_button(CtkDitheringControls *ctk_dithering_controls); -static void dithering_depth_menu_changed(GtkOptionMenu *dithering_depth_menu, +static void setup_dithering_config_menu(CtkDitheringControls *ctk_dithering_controls); + +static void setup_dithering_mode_menu(CtkDitheringControls *ctk_dithering_controls); + +static void setup_dithering_depth_menu(CtkDitheringControls *ctk_dithering_controls); + +static void dithering_depth_menu_changed(GtkWidget *dithering_depth_menu, gpointer user_data); -static void dithering_mode_menu_changed(GtkOptionMenu *dithering_mode_menu, +static void dithering_mode_menu_changed(GtkWidget *dithering_mode_menu, gpointer user_data); -static void dithering_config_menu_changed(GtkOptionMenu *dithering_config_menu, +static void dithering_config_menu_changed(GtkWidget *dithering_config_menu, gpointer user_data); static void dithering_update_received(GtkObject *object, gpointer arg1, @@ -125,7 +134,8 @@ GtkWidget* ctk_dithering_controls_new(NvCtrlAttributeHandle *handle, GObject *object; CtkDitheringControls *ctk_dithering_controls; GtkWidget *frame, *vbox, *hbox, *label; - GtkWidget *menu, *table, *menu_item = NULL, *separator; + GtkWidget *table, *separator; + CtkDropDownMenu *menu; ReturnStatus ret; int tmp; @@ -164,24 +174,15 @@ GtkWidget* ctk_dithering_controls_new(NvCtrlAttributeHandle *handle, /* Build Dithering widgets & pack them in table */ /* dropdown list for dithering configuration */ - menu = gtk_menu_new(); - - menu_item = gtk_menu_item_new_with_label("Auto"); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); - gtk_widget_show(menu_item); - - menu_item = gtk_menu_item_new_with_label("Enabled"); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); - gtk_widget_show(menu_item); + menu = (CtkDropDownMenu *) + ctk_drop_down_menu_new(CTK_DROP_DOWN_MENU_FLAG_COMBO); - menu_item = gtk_menu_item_new_with_label("Disabled"); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); - gtk_widget_show(menu_item); + ctk_drop_down_menu_append_item(menu, "Auto", 0); + ctk_drop_down_menu_append_item(menu, "Enabled", 1); + ctk_drop_down_menu_append_item(menu, "Disabled", 2); - ctk_dithering_controls->dithering_config_menu = gtk_option_menu_new(); - gtk_option_menu_set_menu - (GTK_OPTION_MENU(ctk_dithering_controls->dithering_config_menu), - menu); + ctk_dithering_controls->dithering_config_menu = GTK_WIDGET(menu); + ctk_config_set_tooltip(ctk_config, ctk_dithering_controls->dithering_config_menu, __dithering_config_help); @@ -230,7 +231,8 @@ GtkWidget* ctk_dithering_controls_new(NvCtrlAttributeHandle *handle, GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); /* dropdown list for dithering modes - populated in setup */ - ctk_dithering_controls->dithering_mode_menu = gtk_option_menu_new(); + ctk_dithering_controls->dithering_mode_menu = + ctk_drop_down_menu_new(CTK_DROP_DOWN_MENU_FLAG_COMBO); ctk_config_set_tooltip(ctk_config, ctk_dithering_controls->dithering_mode_menu, __dithering_mode_help); @@ -280,24 +282,15 @@ GtkWidget* ctk_dithering_controls_new(NvCtrlAttributeHandle *handle, GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); /* dithering depth */ - menu = gtk_menu_new(); + menu = (CtkDropDownMenu *) + ctk_drop_down_menu_new(CTK_DROP_DOWN_MENU_FLAG_COMBO); - menu_item = gtk_menu_item_new_with_label("Auto"); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); - gtk_widget_show(menu_item); + ctk_drop_down_menu_append_item(menu, "Auto", 0); + ctk_drop_down_menu_append_item(menu, "6 bpc", 1); + ctk_drop_down_menu_append_item(menu, "8 bpc", 2); - menu_item = gtk_menu_item_new_with_label("6 bpc"); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); - gtk_widget_show(menu_item); - - menu_item = gtk_menu_item_new_with_label("8 bpc"); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); - gtk_widget_show(menu_item); - - ctk_dithering_controls->dithering_depth_menu = gtk_option_menu_new(); - gtk_option_menu_set_menu - (GTK_OPTION_MENU(ctk_dithering_controls->dithering_depth_menu), - menu); + ctk_dithering_controls->dithering_depth_menu = GTK_WIDGET(menu); + ctk_config_set_tooltip(ctk_config, ctk_dithering_controls->dithering_depth_menu, __dithering_depth_help); @@ -383,6 +376,9 @@ static void setup_reset_button(CtkDitheringControls *ctk_dithering_controls) { gint history; gint val; + CtkDropDownMenu *dithering_config_menu; + CtkDropDownMenu *dithering_mode_menu; + CtkDropDownMenu *dithering_depth_menu; if (!GTK_WIDGET_SENSITIVE(ctk_dithering_controls->dithering_controls_box)) { /* Nothing is available, don't bother enabling the reset button yet. */ @@ -390,16 +386,18 @@ static void setup_reset_button(CtkDitheringControls *ctk_dithering_controls) } /* The config menu is always available */ - history = gtk_option_menu_get_history - (GTK_OPTION_MENU(ctk_dithering_controls->dithering_config_menu)); + dithering_config_menu = + CTK_DROP_DOWN_MENU(ctk_dithering_controls->dithering_config_menu); + history = ctk_drop_down_menu_get_current_value(dithering_config_menu); val = map_dithering_config_menu_idx_to_nvctrl(history); if (val != NV_CTRL_DITHERING_AUTO) { goto enable; } if (GTK_WIDGET_SENSITIVE(ctk_dithering_controls->dithering_mode_box)) { - history = gtk_option_menu_get_history - (GTK_OPTION_MENU(ctk_dithering_controls->dithering_mode_menu)); + dithering_mode_menu = + CTK_DROP_DOWN_MENU(ctk_dithering_controls->dithering_mode_menu); + history = ctk_drop_down_menu_get_current_value(dithering_mode_menu); val = ctk_dithering_controls->dithering_mode_table[history]; if (val != NV_CTRL_DITHERING_MODE_AUTO) { goto enable; @@ -407,8 +405,9 @@ static void setup_reset_button(CtkDitheringControls *ctk_dithering_controls) } if (GTK_WIDGET_SENSITIVE(ctk_dithering_controls->dithering_depth_box)) { - history = gtk_option_menu_get_history - (GTK_OPTION_MENU(ctk_dithering_controls->dithering_depth_menu)); + dithering_depth_menu = + CTK_DROP_DOWN_MENU(ctk_dithering_controls->dithering_depth_menu); + history = ctk_drop_down_menu_get_current_value(dithering_depth_menu); val = map_dithering_depth_menu_idx_to_nvctrl(history); if (val != NV_CTRL_DITHERING_DEPTH_AUTO) { goto enable; @@ -426,91 +425,163 @@ static void setup_reset_button(CtkDitheringControls *ctk_dithering_controls) -/* - * ctk_dithering_controls_setup() - Setup routine for dithering attributes. Used - * in DFP setup stage as well as for updating the GUI when there is change in - * dithering mode or config (enabled/disabled). - */ -void ctk_dithering_controls_setup(CtkDitheringControls *ctk_dithering_controls) +static void +setup_dithering_depth_menu(CtkDitheringControls *ctk_dithering_controls) { - GtkWidget *menu, *menu_item = NULL; - gint val, i; - - if (!ctk_dithering_controls) { - return; - } + CtkDropDownMenu *dithering_depth_menu; + gint val; + ReturnStatus ret; + dithering_depth_menu = + CTK_DROP_DOWN_MENU(ctk_dithering_controls->dithering_depth_menu); - /* dithering */ - if (NvCtrlSuccess != - NvCtrlGetAttribute(ctk_dithering_controls->handle, - NV_CTRL_DITHERING, &val)) { - val = NV_CTRL_DITHERING_AUTO; + /* dithering depth */ + ret = NvCtrlGetAttribute(ctk_dithering_controls->handle, + NV_CTRL_DITHERING_DEPTH, &val); + if (ret != NvCtrlSuccess) { + val = NV_CTRL_DITHERING_DEPTH_AUTO; } g_signal_handlers_block_by_func - (G_OBJECT(ctk_dithering_controls->dithering_config_menu), - G_CALLBACK(dithering_config_menu_changed), + (G_OBJECT(ctk_dithering_controls->dithering_depth_menu), + G_CALLBACK(dithering_depth_menu_changed), (gpointer) ctk_dithering_controls); - gtk_option_menu_set_history - (GTK_OPTION_MENU(ctk_dithering_controls->dithering_config_menu), - val); + ctk_drop_down_menu_set_current_value(dithering_depth_menu, val); g_signal_handlers_unblock_by_func - (G_OBJECT(ctk_dithering_controls->dithering_config_menu), - G_CALLBACK(dithering_config_menu_changed), + (G_OBJECT(ctk_dithering_controls->dithering_depth_menu), + G_CALLBACK(dithering_depth_menu_changed), (gpointer) ctk_dithering_controls); +} + + + +static void +setup_dithering_mode_menu(CtkDitheringControls *ctk_dithering_controls) +{ + CtkDropDownMenu *dithering_mode_menu; + gint val, i; + ReturnStatus ret; + dithering_mode_menu = + CTK_DROP_DOWN_MENU(ctk_dithering_controls->dithering_mode_menu); /* setup dithering modes */ build_dithering_mode_table(ctk_dithering_controls); + g_signal_handlers_block_by_func + (G_OBJECT(ctk_dithering_controls->dithering_mode_menu), + G_CALLBACK(dithering_mode_menu_changed), + (gpointer) ctk_dithering_controls); + /* populate dropdown list for dithering modes */ - menu = gtk_menu_new(); + + ctk_drop_down_menu_reset(dithering_mode_menu); + for (i = 0; i < ctk_dithering_controls->dithering_mode_table_size; i++) { switch (ctk_dithering_controls->dithering_mode_table[i]) { case NV_CTRL_DITHERING_MODE_DYNAMIC_2X2: - menu_item = gtk_menu_item_new_with_label("Dynamic 2x2"); + ctk_drop_down_menu_append_item(dithering_mode_menu, "Dynamic 2x2", i); break; case NV_CTRL_DITHERING_MODE_STATIC_2X2: - menu_item = gtk_menu_item_new_with_label("Static 2x2"); + ctk_drop_down_menu_append_item(dithering_mode_menu, "Static 2x2", i); break; case NV_CTRL_DITHERING_MODE_TEMPORAL: - menu_item = gtk_menu_item_new_with_label("Temporal"); + ctk_drop_down_menu_append_item(dithering_mode_menu, "Temporal", i); break; default: case NV_CTRL_DITHERING_MODE_AUTO: - menu_item = gtk_menu_item_new_with_label("Auto"); + ctk_drop_down_menu_append_item(dithering_mode_menu, "Auto", i); break; } - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); - gtk_widget_show(menu_item); } - g_signal_handlers_block_by_func + /* dithering mode */ + ret = NvCtrlGetAttribute(ctk_dithering_controls->handle, + NV_CTRL_DITHERING_MODE, &val); + if (ret != NvCtrlSuccess) { + val = NV_CTRL_DITHERING_MODE_AUTO; + } + + val = map_nvctrl_value_to_table(ctk_dithering_controls, val); + + ctk_drop_down_menu_set_current_value(dithering_mode_menu, val); + + + g_signal_handlers_unblock_by_func (G_OBJECT(ctk_dithering_controls->dithering_mode_menu), G_CALLBACK(dithering_mode_menu_changed), (gpointer) ctk_dithering_controls); +} + - gtk_option_menu_set_menu - (GTK_OPTION_MENU(ctk_dithering_controls->dithering_mode_menu), - menu); + +static void +setup_dithering_config_menu(CtkDitheringControls *ctk_dithering_controls) +{ + CtkDropDownMenu *dithering_config_menu; + gint val; + dithering_config_menu = + CTK_DROP_DOWN_MENU(ctk_dithering_controls->dithering_config_menu); + + /* dithering */ + if (NvCtrlSuccess != + NvCtrlGetAttribute(ctk_dithering_controls->handle, + NV_CTRL_DITHERING, &val)) { + val = NV_CTRL_DITHERING_AUTO; + return; + } + + g_signal_handlers_block_by_func + (G_OBJECT(ctk_dithering_controls->dithering_config_menu), + G_CALLBACK(dithering_config_menu_changed), + (gpointer) ctk_dithering_controls); + + ctk_drop_down_menu_set_current_value(dithering_config_menu, val); g_signal_handlers_unblock_by_func - (G_OBJECT(ctk_dithering_controls->dithering_mode_menu), - G_CALLBACK(dithering_mode_menu_changed), + (G_OBJECT(ctk_dithering_controls->dithering_config_menu), + G_CALLBACK(dithering_config_menu_changed), (gpointer) ctk_dithering_controls); +} + + + +/* + * ctk_dithering_controls_setup() - Setup routine for dithering attributes. Used + * in DFP setup stage as well as for updating the GUI when there is change in + * dithering mode or config (enabled/disabled). + */ +void ctk_dithering_controls_setup(CtkDitheringControls *ctk_dithering_controls) +{ + + if (!ctk_dithering_controls) { + return; + } + + /* setup dithering config menu */ + setup_dithering_config_menu(ctk_dithering_controls); + + /* setup dithering mode menu */ + setup_dithering_mode_menu(ctk_dithering_controls); + + /* setup dithering depth menu */ + setup_dithering_depth_menu(ctk_dithering_controls); + + setup_dithering_info(ctk_dithering_controls); + +} /* ctk_dithering_controls_setup() */ +static void setup_dithering_info(CtkDitheringControls *ctk_dithering_controls) +{ if (!update_dithering_info((gpointer)ctk_dithering_controls)) { gtk_widget_hide(ctk_dithering_controls->dithering_controls_box); } else { gtk_widget_show(ctk_dithering_controls->dithering_controls_box); } - setup_reset_button(ctk_dithering_controls); -} /* ctk_dithering_controls_setup() */ - +} /* setup_dithering_info() */ static Bool update_dithering_info(gpointer user_data) { @@ -554,28 +625,6 @@ static Bool update_dithering_info(gpointer user_data) "Disabled"); } - /* dithering mode */ - ret = NvCtrlGetAttribute(ctk_dithering_controls->handle, - NV_CTRL_DITHERING_MODE, &val); - if (ret != NvCtrlSuccess) { - val = NV_CTRL_DITHERING_MODE_AUTO; - } - - val = map_nvctrl_value_to_table(ctk_dithering_controls, val); - - g_signal_handlers_block_by_func - (G_OBJECT(ctk_dithering_controls->dithering_mode_menu), - G_CALLBACK(dithering_mode_menu_changed), - (gpointer) ctk_dithering_controls); - - gtk_option_menu_set_history - (GTK_OPTION_MENU(ctk_dithering_controls->dithering_mode_menu), val); - - g_signal_handlers_unblock_by_func - (G_OBJECT(ctk_dithering_controls->dithering_mode_menu), - G_CALLBACK(dithering_mode_menu_changed), - (gpointer) ctk_dithering_controls); - /* current dithering mode */ ret = NvCtrlGetAttribute(ctk_dithering_controls->handle, NV_CTRL_CURRENT_DITHERING_MODE, &val); @@ -602,27 +651,6 @@ static Bool update_dithering_info(gpointer user_data) "None"); break; } - - /* dithering depth */ - ret = NvCtrlGetAttribute(ctk_dithering_controls->handle, - NV_CTRL_DITHERING_DEPTH, &val); - if (ret != NvCtrlSuccess) { - val = NV_CTRL_DITHERING_DEPTH_AUTO; - } - - g_signal_handlers_block_by_func - (G_OBJECT(ctk_dithering_controls->dithering_depth_menu), - G_CALLBACK(dithering_depth_menu_changed), - (gpointer) ctk_dithering_controls); - - gtk_option_menu_set_history - (GTK_OPTION_MENU(ctk_dithering_controls->dithering_depth_menu), val); - - g_signal_handlers_unblock_by_func - (G_OBJECT(ctk_dithering_controls->dithering_depth_menu), - G_CALLBACK(dithering_depth_menu_changed), - (gpointer) ctk_dithering_controls); - /* current dithering depth */ ret = NvCtrlGetAttribute(ctk_dithering_controls->handle, NV_CTRL_CURRENT_DITHERING_DEPTH, &val); @@ -717,14 +745,15 @@ void post_dithering_depth_update(CtkDitheringControls *ctk_dithering_controls, ctk_dithering_controls->name); } -static void dithering_config_menu_changed(GtkOptionMenu *dithering_config_menu, +static void dithering_config_menu_changed(GtkWidget *dithering_config_menu, gpointer user_data) { CtkDitheringControls *ctk_dithering_controls = CTK_DITHERING_CONTROLS(user_data); + CtkDropDownMenu *menu = CTK_DROP_DOWN_MENU(dithering_config_menu); gint history, dithering_config = NV_CTRL_DITHERING_AUTO; - history = gtk_option_menu_get_history(dithering_config_menu); + history = ctk_drop_down_menu_get_current_value(menu); dithering_config = map_dithering_config_menu_idx_to_nvctrl(history); @@ -732,35 +761,22 @@ static void dithering_config_menu_changed(GtkOptionMenu *dithering_config_menu, NV_CTRL_DITHERING, dithering_config); - g_signal_handlers_block_by_func - (G_OBJECT(ctk_dithering_controls->dithering_config_menu), - G_CALLBACK(dithering_config_menu_changed), - (gpointer) ctk_dithering_controls); - - gtk_option_menu_set_history - (GTK_OPTION_MENU(ctk_dithering_controls->dithering_config_menu), - dithering_config); - - g_signal_handlers_unblock_by_func - (G_OBJECT(ctk_dithering_controls->dithering_config_menu), - G_CALLBACK(dithering_config_menu_changed), - (gpointer) ctk_dithering_controls); - /* reflecting the change in configuration to other widgets & reset button */ - ctk_dithering_controls_setup(ctk_dithering_controls); + setup_dithering_info(ctk_dithering_controls); post_dithering_config_update(ctk_dithering_controls, dithering_config); } /* dithering_config_menu_changed() */ -static void dithering_mode_menu_changed(GtkOptionMenu *dithering_mode_menu, +static void dithering_mode_menu_changed(GtkWidget *dithering_mode_menu, gpointer user_data) { CtkDitheringControls *ctk_dithering_controls = CTK_DITHERING_CONTROLS(user_data); + CtkDropDownMenu *menu = CTK_DROP_DOWN_MENU(dithering_mode_menu); gint history, dithering_mode = NV_CTRL_DITHERING_MODE_AUTO; - history = gtk_option_menu_get_history(dithering_mode_menu); + history = ctk_drop_down_menu_get_current_value(menu); dithering_mode = ctk_dithering_controls->dithering_mode_table[history]; @@ -770,34 +786,21 @@ static void dithering_mode_menu_changed(GtkOptionMenu *dithering_mode_menu, dithering_mode = map_nvctrl_value_to_table(ctk_dithering_controls, dithering_mode); - g_signal_handlers_block_by_func - (G_OBJECT(ctk_dithering_controls->dithering_mode_menu), - G_CALLBACK(dithering_mode_menu_changed), - (gpointer) ctk_dithering_controls); - - gtk_option_menu_set_history - (GTK_OPTION_MENU(ctk_dithering_controls->dithering_mode_menu), - dithering_mode); - - g_signal_handlers_unblock_by_func - (G_OBJECT(ctk_dithering_controls->dithering_mode_menu), - G_CALLBACK(dithering_mode_menu_changed), - (gpointer) ctk_dithering_controls); - - /* reflecting the change in mode to other widgets & reset button */ - ctk_dithering_controls_setup(ctk_dithering_controls); + /* reflecting the change in mode to the reset button */ + setup_dithering_info(ctk_dithering_controls); post_dithering_mode_update(ctk_dithering_controls, dithering_mode); } /* dithering_mode_menu_changed() */ -static void dithering_depth_menu_changed(GtkOptionMenu *dithering_depth_menu, +static void dithering_depth_menu_changed(GtkWidget *dithering_depth_menu, gpointer user_data) { CtkDitheringControls *ctk_dithering_controls = CTK_DITHERING_CONTROLS(user_data); + CtkDropDownMenu *menu = CTK_DROP_DOWN_MENU(dithering_depth_menu); gint history, dithering_depth = NV_CTRL_DITHERING_DEPTH_AUTO; - history = gtk_option_menu_get_history(dithering_depth_menu); + history = ctk_drop_down_menu_get_current_value(menu); dithering_depth = map_dithering_depth_menu_idx_to_nvctrl(history); @@ -805,22 +808,8 @@ static void dithering_depth_menu_changed(GtkOptionMenu *dithering_depth_menu, NV_CTRL_DITHERING_DEPTH, dithering_depth); - g_signal_handlers_block_by_func - (G_OBJECT(ctk_dithering_controls->dithering_depth_menu), - G_CALLBACK(dithering_depth_menu_changed), - (gpointer) ctk_dithering_controls); - - gtk_option_menu_set_history - (GTK_OPTION_MENU(ctk_dithering_controls->dithering_depth_menu), - dithering_depth); - - g_signal_handlers_unblock_by_func - (G_OBJECT(ctk_dithering_controls->dithering_depth_menu), - G_CALLBACK(dithering_depth_menu_changed), - (gpointer) ctk_dithering_controls); - /* reflecting the change in configuration to other widgets & reset button */ - ctk_dithering_controls_setup(ctk_dithering_controls); + setup_dithering_info(ctk_dithering_controls); post_dithering_depth_update(ctk_dithering_controls, dithering_depth); } /* dithering_depth_menu_changed() */ @@ -848,7 +837,7 @@ void ctk_dithering_controls_reset(CtkDitheringControls *ctk_dithering_controls) NV_CTRL_DITHERING_DEPTH, NV_CTRL_DITHERING_DEPTH_AUTO); - ctk_dithering_controls_setup(ctk_dithering_controls); + setup_dithering_info(ctk_dithering_controls); } /* ctk_dithering_controls_reset() */ @@ -859,7 +848,7 @@ void add_dithering_controls_help(CtkDitheringControls *ctk_dithering_controls, GtkTextBuffer *b, GtkTextIter *i) { - if (ctk_dithering_controls == NULL) { + if (!ctk_dithering_controls) { return; } diff --git a/src/gtk+-2.x/ctkdropdownmenu.c b/src/gtk+-2.x/ctkdropdownmenu.c index 7166be0..ec830b5 100644 --- a/src/gtk+-2.x/ctkdropdownmenu.c +++ b/src/gtk+-2.x/ctkdropdownmenu.c @@ -110,6 +110,10 @@ static void ctk_drop_down_menu_free(GObject *object) g_free(d->values); + if (d->glist) { + g_list_free(d->glist); + } + } /* ctk_drop_down_menu_free() */ @@ -206,7 +210,7 @@ GtkWidget* ctk_drop_down_menu_new(guint flags) void ctk_drop_down_menu_reset(CtkDropDownMenu *d) { if (d->glist) { - g_free(d->glist); + g_list_free(d->glist); d->glist = NULL; } if (d->values) { @@ -215,11 +219,12 @@ void ctk_drop_down_menu_reset(CtkDropDownMenu *d) } d->num_entries = 0; - - d->menu = gtk_menu_new(); - gtk_option_menu_set_menu(GTK_OPTION_MENU(d->option_menu), d->menu); - + if (!(d->flags & CTK_DROP_DOWN_MENU_FLAG_COMBO)) { + d->menu = gtk_menu_new(); + gtk_option_menu_set_menu(GTK_OPTION_MENU(d->option_menu), d->menu); + } + } /* ctk_drop_down_menu_reset() */ @@ -278,7 +283,7 @@ GtkWidget *ctk_drop_down_menu_append_item(CtkDropDownMenu *d, /* * ctk_drop_down_menu_get_current_value() - return the current value - * selected in the drop down menu. + * selected in the drop down menu, or 0 if the current item is undefined. */ gint ctk_drop_down_menu_get_current_value(CtkDropDownMenu *d) @@ -286,19 +291,43 @@ gint ctk_drop_down_menu_get_current_value(CtkDropDownMenu *d) gint i; if (d->flags & CTK_DROP_DOWN_MENU_FLAG_COMBO) { - return d->current_selected_item; + i = d->current_selected_item; } else { i = gtk_option_menu_get_history(GTK_OPTION_MENU(d->option_menu)); + } - if (i < d->num_entries) { - return d->values[i].value; - } + if (i < d->num_entries) { + return d->values[i].value; + } else { + return 0; /* XXX??? */ } - return 0; /* XXX??? */ } /* ctk_drop_down_menu_get_current_value() */ +/* + * ctk_drop_down_menu_get_current_name() - get the current name in the menu, or + * an empty string if the current item is undefined. The returned string points + * to internally allocated storage in the widget and must not be modified, + * freed, or stored. + */ +const char *ctk_drop_down_menu_get_current_name(CtkDropDownMenu *d) +{ + gint i; + + if (d->flags & CTK_DROP_DOWN_MENU_FLAG_COMBO) { + i = d->current_selected_item; + } else { + i = gtk_option_menu_get_history(GTK_OPTION_MENU(d->option_menu)); + + } + + if (i < d->num_entries) { + return d->values[i].glist_item; + } else { + return ""; /* XXX??? */ + } +} /* * ctk_drop_down_menu_set_current_value() - set the current value in diff --git a/src/gtk+-2.x/ctkdropdownmenu.h b/src/gtk+-2.x/ctkdropdownmenu.h index f1d0557..8b6bc9d 100644 --- a/src/gtk+-2.x/ctkdropdownmenu.h +++ b/src/gtk+-2.x/ctkdropdownmenu.h @@ -68,7 +68,7 @@ struct _CtkDropDownMenu GtkWidget *menu; GtkWidget *option_menu; - + guint flags; gint num_entries; @@ -90,8 +90,9 @@ GtkWidget* ctk_drop_down_menu_new (guint flags); GtkWidget* ctk_drop_down_menu_append_item (CtkDropDownMenu *d, const gchar *name, const gint value); -gint ctk_drop_down_menu_get_current_value (CtkDropDownMenu *d); -void ctk_drop_down_menu_set_current_value (CtkDropDownMenu *d, +gint ctk_drop_down_menu_get_current_value (CtkDropDownMenu *d); +const char *ctk_drop_down_menu_get_current_name (CtkDropDownMenu *d); +void ctk_drop_down_menu_set_current_value (CtkDropDownMenu *d, gint value); void ctk_drop_down_menu_set_value_sensitive (CtkDropDownMenu *d, gint value, diff --git a/src/gtk+-2.x/ctkevent.c b/src/gtk+-2.x/ctkevent.c index 6238756..e22757a 100644 --- a/src/gtk+-2.x/ctkevent.c +++ b/src/gtk+-2.x/ctkevent.c @@ -157,13 +157,6 @@ static void ctk_event_class_init(CtkEventClass *ctk_event_class) MAKE_SIGNAL(NV_CTRL_FORCE_STEREO); MAKE_SIGNAL(NV_CTRL_ARCHITECTURE); MAKE_SIGNAL(NV_CTRL_TEXTURE_CLAMPING); - MAKE_SIGNAL(NV_CTRL_CURSOR_SHADOW); - MAKE_SIGNAL(NV_CTRL_CURSOR_SHADOW_ALPHA); - MAKE_SIGNAL(NV_CTRL_CURSOR_SHADOW_RED); - MAKE_SIGNAL(NV_CTRL_CURSOR_SHADOW_GREEN); - MAKE_SIGNAL(NV_CTRL_CURSOR_SHADOW_BLUE); - MAKE_SIGNAL(NV_CTRL_CURSOR_SHADOW_X_OFFSET); - MAKE_SIGNAL(NV_CTRL_CURSOR_SHADOW_Y_OFFSET); MAKE_SIGNAL(NV_CTRL_FSAA_APPLICATION_CONTROLLED); MAKE_SIGNAL(NV_CTRL_LOG_ANISO_APPLICATION_CONTROLLED); MAKE_SIGNAL(NV_CTRL_IMAGE_SHARPENING); diff --git a/src/gtk+-2.x/ctkgvi.c b/src/gtk+-2.x/ctkgvi.c index 591c237..d0fd5db 100644 --- a/src/gtk+-2.x/ctkgvi.c +++ b/src/gtk+-2.x/ctkgvi.c @@ -31,6 +31,7 @@ #include "ctkgvi.h" #include "ctkgpu.h" #include "ctkbanner.h" +#include "ctkdropdownmenu.h" #define DEFAULT_UPDATE_VIDEO_FORMAT_INFO_TIME_INTERVAL 1000 @@ -260,20 +261,17 @@ static void update_sdi_input_info_simple(CtkGvi *ctk_gvi) } -static void jack_channel_changed(GtkOptionMenu *optionmenu, +static void jack_channel_changed(GtkWidget *widget, gpointer user_data) { CtkGvi *ctk_gvi = CTK_GVI(user_data); - GtkWidget *menu; - GtkWidget *menu_item; + CtkDropDownMenu *menu = CTK_DROP_DOWN_MENU(widget); + gint idx; /* Track new selection */ - menu = gtk_option_menu_get_menu(optionmenu); - menu_item = gtk_menu_get_active(GTK_MENU(menu)); - - ctk_gvi->cur_jack_channel = - GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(menu_item), - "JACK_CHANNEL")); + idx = ctk_drop_down_menu_get_current_value(menu); + + ctk_gvi->cur_jack_channel = ctk_gvi->jack_channel_table[idx]; update_sdi_input_info(ctk_gvi); } @@ -281,9 +279,7 @@ static void jack_channel_changed(GtkOptionMenu *optionmenu, static GtkWidget *create_jack_channel_menu(CtkGvi *ctk_gvi) { - GtkWidget *omenu; - GtkWidget *menu; - GtkWidget *menu_item; + CtkDropDownMenu *menu; gint idx; gchar *label_str; gint jack; @@ -293,9 +289,8 @@ static GtkWidget *create_jack_channel_menu(CtkGvi *ctk_gvi) /* Create the menu */ - omenu = gtk_option_menu_new(); - - menu = gtk_menu_new(); + menu = (CtkDropDownMenu *) + ctk_drop_down_menu_new(CTK_DROP_DOWN_MENU_FLAG_COMBO); /* Just show all jack/channel pairs in dropdown */ @@ -310,16 +305,11 @@ static GtkWidget *create_jack_channel_menu(CtkGvi *ctk_gvi) label_str = g_strdup_printf("Jack %d, Channel %d", jack+1, channel+1); - menu_item = gtk_menu_item_new_with_label(label_str); + ctk_drop_down_menu_append_item(menu, label_str, idx); g_free(label_str); - g_object_set_data(G_OBJECT(menu_item), - "JACK_CHANNEL", - GUINT_TO_POINTER(jack_channel)); - - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); - gtk_widget_show(menu_item); - + ctk_gvi->jack_channel_table[idx] = jack_channel; + if (jack_channel == ctk_gvi->cur_jack_channel) { selected_idx = idx; } @@ -328,15 +318,13 @@ static GtkWidget *create_jack_channel_menu(CtkGvi *ctk_gvi) } } - gtk_option_menu_set_menu(GTK_OPTION_MENU(omenu), menu); - - gtk_option_menu_set_history(GTK_OPTION_MENU(omenu), selected_idx); + ctk_drop_down_menu_set_current_value(menu, selected_idx); - g_signal_connect(G_OBJECT(omenu), "changed", + g_signal_connect(G_OBJECT(menu), "changed", G_CALLBACK(jack_channel_changed), (gpointer) ctk_gvi); - return omenu; + return GTK_WIDGET(menu); } @@ -662,6 +650,14 @@ GtkWidget* ctk_gvi_new(NvCtrlAttributeHandle *handle, hsep = gtk_hseparator_new(); gtk_box_pack_start(GTK_BOX(hbox), hsep, TRUE, TRUE, 5); + + /* Create look up table to store jack chanel */ + if (ctk_gvi->jack_channel_table) { + free(ctk_gvi->jack_channel_table); + } + ctk_gvi->jack_channel_table = + calloc(ctk_gvi->max_channels_per_jack * ctk_gvi->num_jacks, + sizeof(unsigned int)); /* Jack+Channel selection dropdown (hidden in condensed view) */ diff --git a/src/gtk+-2.x/ctkgvi.h b/src/gtk+-2.x/ctkgvi.h index 5ca2d7f..3f5e5df 100644 --- a/src/gtk+-2.x/ctkgvi.h +++ b/src/gtk+-2.x/ctkgvi.h @@ -65,6 +65,7 @@ struct _CtkGvi GtkWidget *show_detailed_info_btn; unsigned int cur_jack_channel; + unsigned int *jack_channel_table; }; struct _CtkGviClass diff --git a/src/gtk+-2.x/ctkhelp.c b/src/gtk+-2.x/ctkhelp.c index 24b8bed..9a4ac0a 100644 --- a/src/gtk+-2.x/ctkhelp.c +++ b/src/gtk+-2.x/ctkhelp.c @@ -26,6 +26,7 @@ #include "ctkbanner.h" #include <stdlib.h> +#include <assert.h> static GtkTextBuffer *create_default_help(CtkHelp *ctk_help); static void close_button_clicked(GtkButton *button, gpointer user_data); @@ -416,3 +417,82 @@ void ctk_help_reset_hardware_defaults(GtkTextBuffer *b, GtkTextIter *i, ctk_help_para(b, i, text); } + +static void help_data_list_free_cb(gpointer data, gpointer user_data) +{ + CtkHelpDataItem *item = (CtkHelpDataItem *)data; + free(item->label); + free(item->help_text); + free(item); +} + +void ctk_help_data_list_prepend(GList **list, + const gchar *label, + const gchar *help_text, + const gchar *extended_help_text) +{ + CtkHelpDataItem *item = nvalloc(sizeof(CtkHelpDataItem)); + + assert(label); + assert(help_text); + + item->label = nvstrdup(label); + item->help_text = nvstrdup(help_text); + item->extended_help_text = extended_help_text ? + nvstrdup(extended_help_text) : NULL; + + *list = g_list_prepend(*list, item); +} + +void ctk_help_data_list_free_full(GList *list) +{ + g_list_foreach(list, help_data_list_free_cb, NULL); + g_list_free(list); +} + +static void help_data_list_print_helper(GtkTextBuffer *b, + GtkTextIter *i, + GList *help_data_list, + gboolean use_headings) +{ + CtkHelpDataItem *item; + GList *cur; + GString *temp_string; + + temp_string = g_string_new(""); + for (cur = help_data_list; cur; cur = cur->next) { + item = (CtkHelpDataItem *)cur->data; + if (use_headings) { + ctk_help_heading(b, i, item->label); + } else { + ctk_help_term(b, i, item->label); + } + g_string_printf(temp_string, "%s", item->help_text); + if (item->extended_help_text) { + g_string_append_printf(temp_string, " %s", + item->extended_help_text); + } + ctk_help_para(b, i, temp_string->str); + } + + g_string_free(temp_string, TRUE); +} + +void ctk_help_data_list_print_terms(GtkTextBuffer *b, + GtkTextIter *i, + GList *help_data_list) +{ + help_data_list_print_helper(b, i, + help_data_list, + FALSE); +} + +void ctk_help_data_list_print_sections(GtkTextBuffer *b, + GtkTextIter *i, + GList *help_data_list) +{ + help_data_list_print_helper(b, i, + help_data_list, + TRUE); + +} diff --git a/src/gtk+-2.x/ctkhelp.h b/src/gtk+-2.x/ctkhelp.h index 1ca6a68..9f3482f 100644 --- a/src/gtk+-2.x/ctkhelp.h +++ b/src/gtk+-2.x/ctkhelp.h @@ -22,6 +22,8 @@ #include <gtk/gtk.h> +#include "common-utils.h" + G_BEGIN_DECLS @@ -45,6 +47,7 @@ G_BEGIN_DECLS typedef struct _CtkHelp CtkHelp; typedef struct _CtkHelpClass CtkHelpClass; +typedef struct _CtkHelpDataItem CtkHelpDataItem; struct _CtkHelp { @@ -57,6 +60,18 @@ struct _CtkHelp GtkWidget *toggle_button; }; +struct _CtkHelpDataItem +{ + // Header for the help section (usually corresponds to a label) + gchar *label; + + // A brief summary of the contents + gchar *help_text; + + // If non-NULL, elaborates on help_text above + gchar *extended_help_text; +}; + struct _CtkHelpClass { GtkWindowClass parent_class; @@ -67,15 +82,25 @@ GtkWidget *ctk_help_new (GtkWidget *, GtkTextTagTable *); void ctk_help_set_page (CtkHelp *, GtkTextBuffer *); GtkTextTagTable *ctk_help_create_tag_table (void); -void ctk_help_title (GtkTextBuffer *, GtkTextIter *, const gchar *, ...); -void ctk_help_para (GtkTextBuffer *, GtkTextIter *, const gchar *, ...); -void ctk_help_heading (GtkTextBuffer *, GtkTextIter *, const gchar *, ...); -void ctk_help_term (GtkTextBuffer *, GtkTextIter *, const gchar *, ...); +void ctk_help_title (GtkTextBuffer *, GtkTextIter *, const gchar *, ...) NV_ATTRIBUTE_PRINTF(3, 4); +void ctk_help_para (GtkTextBuffer *, GtkTextIter *, const gchar *, ...) NV_ATTRIBUTE_PRINTF(3, 4); +void ctk_help_heading (GtkTextBuffer *, GtkTextIter *, const gchar *, ...) NV_ATTRIBUTE_PRINTF(3, 4); +void ctk_help_term (GtkTextBuffer *, GtkTextIter *, const gchar *, ...) NV_ATTRIBUTE_PRINTF(3, 4); void ctk_help_finish (GtkTextBuffer *); void ctk_help_reset_hardware_defaults(GtkTextBuffer *, GtkTextIter *, gchar *); gchar *ctk_help_create_reset_hardware_defaults_text(gchar*, gchar *); +void ctk_help_data_list_prepend(GList **list, + const gchar *label, + const gchar *help_text, + const gchar *extended_help_text); +void ctk_help_data_list_free_full(GList *list); +void ctk_help_data_list_print_terms(GtkTextBuffer *b, GtkTextIter *i, + GList *help_data_list); +void ctk_help_data_list_print_sections(GtkTextBuffer *b, GtkTextIter *i, + GList *help_data_list); + #define CTK_HELP_TITLE_TAG "title" #define CTK_HELP_HEADING_TAG "heading" diff --git a/src/gtk+-2.x/ctkmultisample.c b/src/gtk+-2.x/ctkmultisample.c index be35e9a..e5f5eb7 100644 --- a/src/gtk+-2.x/ctkmultisample.c +++ b/src/gtk+-2.x/ctkmultisample.c @@ -157,27 +157,28 @@ static const char *__texture_sharpening_help = * help */ -#define __LOG_ANISO_RANGE (1 << 2) -#define __TEXTURE_SHARPEN (1 << 3) -#define __FSAA (1 << 4) -#define __FSAA_NONE (__FSAA << NV_CTRL_FSAA_MODE_NONE) -#define __FSAA_2x (__FSAA << NV_CTRL_FSAA_MODE_2x) -#define __FSAA_2x_5t (__FSAA << NV_CTRL_FSAA_MODE_2x_5t) -#define __FSAA_15x15 (__FSAA << NV_CTRL_FSAA_MODE_15x15) -#define __FSAA_2x2 (__FSAA << NV_CTRL_FSAA_MODE_2x2) -#define __FSAA_4x (__FSAA << NV_CTRL_FSAA_MODE_4x) -#define __FSAA_4x_9t (__FSAA << NV_CTRL_FSAA_MODE_4x_9t) -#define __FSAA_8x (__FSAA << NV_CTRL_FSAA_MODE_8x) -#define __FSAA_16x (__FSAA << NV_CTRL_FSAA_MODE_16x) -#define __FSAA_8xS (__FSAA << NV_CTRL_FSAA_MODE_8xS) -#define __FSAA_8xQ (__FSAA << NV_CTRL_FSAA_MODE_8xQ) -#define __FSAA_16xS (__FSAA << NV_CTRL_FSAA_MODE_16xS) -#define __FSAA_16xQ (__FSAA << NV_CTRL_FSAA_MODE_16xQ) -#define __FSAA_32xS (__FSAA << NV_CTRL_FSAA_MODE_32xS) -#define __FSAA_32x (__FSAA << NV_CTRL_FSAA_MODE_32x) -#define __FSAA_64xS (__FSAA << NV_CTRL_FSAA_MODE_64xS) -#define __FSAA_ENHANCE (__FSAA << (NV_CTRL_FSAA_MODE_MAX + 1)) -#define __FXAA (__FSAA << (NV_CTRL_FSAA_MODE_MAX + 2)) +#define __FSAA_NONE (1 << NV_CTRL_FSAA_MODE_NONE) +#define __FSAA_2x (1 << NV_CTRL_FSAA_MODE_2x) +#define __FSAA_2x_5t (1 << NV_CTRL_FSAA_MODE_2x_5t) +#define __FSAA_15x15 (1 << NV_CTRL_FSAA_MODE_15x15) +#define __FSAA_2x2 (1 << NV_CTRL_FSAA_MODE_2x2) +#define __FSAA_4x (1 << NV_CTRL_FSAA_MODE_4x) +#define __FSAA_4x_9t (1 << NV_CTRL_FSAA_MODE_4x_9t) +#define __FSAA_8x (1 << NV_CTRL_FSAA_MODE_8x) +#define __FSAA_16x (1 << NV_CTRL_FSAA_MODE_16x) +#define __FSAA_8xS (1 << NV_CTRL_FSAA_MODE_8xS) +#define __FSAA_8xQ (1 << NV_CTRL_FSAA_MODE_8xQ) +#define __FSAA_16xS (1 << NV_CTRL_FSAA_MODE_16xS) +#define __FSAA_16xQ (1 << NV_CTRL_FSAA_MODE_16xQ) +#define __FSAA_32xS (1 << NV_CTRL_FSAA_MODE_32xS) +#define __FSAA_32x (1 << NV_CTRL_FSAA_MODE_32x) +#define __FSAA_64xS (1 << NV_CTRL_FSAA_MODE_64xS) +#define __FSAA (1 << (NV_CTRL_FSAA_MODE_MAX + 1)) +#define __FSAA_ENHANCE (1 << (NV_CTRL_FSAA_MODE_MAX + 2)) +#define __FXAA (1 << (NV_CTRL_FSAA_MODE_MAX + 3)) +#define __LOG_ANISO_RANGE (1 << (NV_CTRL_FSAA_MODE_MAX + 4)) +#define __TEXTURE_SHARPEN (1 << (NV_CTRL_FSAA_MODE_MAX + 5)) + #define FRAME_PADDING 5 @@ -378,7 +379,7 @@ GtkWidget *ctk_multisample_new(NvCtrlAttributeHandle *handle, for (i = 0; i < ctk_multisample->fsaa_translation_table_size; i++) ctk_multisample->active_attributes |= - (__FSAA << ctk_multisample->fsaa_translation_table[i]); + (1 << ctk_multisample->fsaa_translation_table[i]); /* FXAA Option button */ @@ -725,7 +726,7 @@ static GtkWidget *create_fsaa_setting_menu(CtkMultisample *ctk_multisample, ctk_config_set_tooltip(ctk_multisample->ctk_config, d->menu, __aa_menu_help); - g_signal_connect(ctk_drop_down_menu_change_object(GTK_WIDGET(d)), + g_signal_connect(G_OBJECT(d), "changed", G_CALLBACK(fsaa_setting_menu_changed), (gpointer) ctk_multisample); @@ -940,7 +941,7 @@ static void fsaa_setting_update_received(GtkObject *object, GtkWidget *menu = ctk_multisample->fsaa_menu; g_signal_handlers_block_by_func - (ctk_drop_down_menu_change_object(GTK_WIDGET(menu)), + (G_OBJECT(menu), G_CALLBACK(fsaa_setting_menu_changed), (gpointer) ctk_multisample); @@ -948,7 +949,7 @@ static void fsaa_setting_update_received(GtkObject *object, (CTK_DROP_DOWN_MENU(ctk_multisample->fsaa_menu), idx); g_signal_handlers_unblock_by_func - (ctk_drop_down_menu_change_object(GTK_WIDGET(menu)), + (G_OBJECT(menu), G_CALLBACK(fsaa_setting_menu_changed), (gpointer) ctk_multisample); } else { diff --git a/src/gtk+-2.x/ctkpowermizer.c b/src/gtk+-2.x/ctkpowermizer.c index a25e7c7..ed0e746 100644 --- a/src/gtk+-2.x/ctkpowermizer.c +++ b/src/gtk+-2.x/ctkpowermizer.c @@ -29,6 +29,7 @@ #include "ctkhelp.h" #include "ctkpowermizer.h" #include "ctkbanner.h" +#include "ctkdropdownmenu.h" @@ -37,7 +38,7 @@ static gboolean update_powermizer_info(gpointer); static void update_powermizer_menu_info(gpointer); -static void powermizer_menu_changed(GtkOptionMenu*, gpointer); +static void powermizer_menu_changed(GtkWidget*, gpointer); static void update_powermizer_menu_event(GtkObject *object, gpointer arg1, gpointer user_data); @@ -48,6 +49,17 @@ static void dp_configuration_update_received(GtkObject *, gpointer, gpointer); static void post_dp_configuration_update(CtkPowermizer *); static void show_dp_toggle_warning_dlg(CtkPowermizer *ctk_powermizer); +typedef struct { + const char *label; + int attr; +} PowerMizerMode; + +PowerMizerMode __powermizer_modes[] = +{ + { "Adaptive", NV_CTRL_GPU_POWER_MIZER_MODE_ADAPTIVE }, + { "Prefer Maximum Performance", NV_CTRL_GPU_POWER_MIZER_MODE_PREFER_MAXIMUM_PERFORMANCE }, +}; + static const char *__adaptive_clock_help = "The Adaptive Clocking status describes if this feature " "is currently enabled in this GPU."; @@ -184,21 +196,25 @@ static void update_perf_mode_table(CtkPowermizer *ctk_powermizer, gtk_box_pack_start(GTK_BOX(ctk_powermizer->performance_table_hbox), table, FALSE, FALSE, 0); - - label = gtk_label_new("Performance Level"); - gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); - gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, - GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); - label = gtk_label_new("Graphics Clock"); - gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); - gtk_table_attach(GTK_TABLE(table), label, 1, 2, 0, 1, - GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + if (ctk_powermizer->performance_level) { + label = gtk_label_new("Performance Level"); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + } - label = gtk_label_new("Memory Clock"); - gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); - gtk_table_attach(GTK_TABLE(table), label, 2, 3, 0, 1, - GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + if (ctk_powermizer->gpu_clock && ctk_powermizer->memory_clock) { + label = gtk_label_new("Graphics Clock"); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 1, 2, 0, 1, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + label = gtk_label_new("Memory Clock"); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 2, 3, 0, 1, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + } if (ctk_powermizer->processor_clock) { label = gtk_label_new("Processor Clock"); @@ -245,27 +261,33 @@ static void update_perf_mode_table(CtkPowermizer *ctk_powermizer, gtk_table_resize(GTK_TABLE(table), row_idx+1, 4); - g_snprintf(tmp_str, 24, "%d", entry.perf_level); - label = gtk_label_new(tmp_str); - gtk_widget_set_sensitive(label, active); - gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); - gtk_table_attach(GTK_TABLE(table), label, 0, 1, row_idx, row_idx+1, - GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); - - g_snprintf(tmp_str, 24, "%d MHz", entry.nvclock); - label = gtk_label_new(tmp_str); - gtk_widget_set_sensitive(label, active); - gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); - gtk_table_attach(GTK_TABLE(table), label, 1, 2, row_idx, row_idx+1, - GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + if (ctk_powermizer->performance_level) { + g_snprintf(tmp_str, 24, "%d", entry.perf_level); + label = gtk_label_new(tmp_str); + gtk_widget_set_sensitive(label, active); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, + row_idx, row_idx+1, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + } - g_snprintf(tmp_str, 24, "%d MHz", entry.memclock); - label = gtk_label_new(tmp_str); - gtk_widget_set_sensitive(label, active); - gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); - gtk_table_attach(GTK_TABLE(table), label, 2, 3, row_idx, row_idx+1, - GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + if (ctk_powermizer->gpu_clock && ctk_powermizer->memory_clock) { + g_snprintf(tmp_str, 24, "%d MHz", entry.nvclock); + label = gtk_label_new(tmp_str); + gtk_widget_set_sensitive(label, active); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 1, 2, + row_idx, row_idx+1, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + g_snprintf(tmp_str, 24, "%d MHz", entry.memclock); + label = gtk_label_new(tmp_str); + gtk_widget_set_sensitive(label, active); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 2, 3, + row_idx, row_idx+1, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + } if (ctk_powermizer->processor_clock) { g_snprintf(tmp_str, 24, "%d MHz", entry.processorclock); label = gtk_label_new(tmp_str); @@ -305,68 +327,64 @@ static gboolean update_powermizer_info(gpointer user_data) ret = NvCtrlGetAttribute(handle, NV_CTRL_GPU_ADAPTIVE_CLOCK_STATE, &adaptive_clock); - if (ret != NvCtrlSuccess) { - return FALSE; - } + if (ret == NvCtrlSuccess && ctk_powermizer->adaptive_clock_status) { - if (adaptive_clock == NV_CTRL_GPU_ADAPTIVE_CLOCK_STATE_ENABLED) { - s = g_strdup_printf("Enabled"); - } - else if (adaptive_clock == NV_CTRL_GPU_ADAPTIVE_CLOCK_STATE_DISABLED) { - s = g_strdup_printf("Disabled"); - } - else { - s = g_strdup_printf("Error"); - } + if (adaptive_clock == NV_CTRL_GPU_ADAPTIVE_CLOCK_STATE_ENABLED) { + s = g_strdup_printf("Enabled"); + } + else if (adaptive_clock == NV_CTRL_GPU_ADAPTIVE_CLOCK_STATE_DISABLED) { + s = g_strdup_printf("Disabled"); + } + else { + s = g_strdup_printf("Error"); + } - gtk_label_set_text(GTK_LABEL(ctk_powermizer->adaptive_clock_status), s); - g_free(s); + gtk_label_set_text(GTK_LABEL(ctk_powermizer->adaptive_clock_status), s); + g_free(s); + } ret = NvCtrlGetAttribute(handle, NV_CTRL_GPU_CURRENT_CLOCK_FREQS, &clockret); - if (ret != NvCtrlSuccess) { - return FALSE; - } + if (ret == NvCtrlSuccess && ctk_powermizer->gpu_clock && + ctk_powermizer->memory_clock) { - memory_clock = clockret & 0x0000FFFF; - gpu_clock = (clockret >> 16); - - s = g_strdup_printf("%d Mhz", gpu_clock); - gtk_label_set_text(GTK_LABEL(ctk_powermizer->gpu_clock), s); - g_free(s); + memory_clock = clockret & 0x0000FFFF; + gpu_clock = (clockret >> 16); - s = g_strdup_printf("%d Mhz", memory_clock); - gtk_label_set_text(GTK_LABEL(ctk_powermizer->memory_clock), s); - g_free(s); + s = g_strdup_printf("%d Mhz", gpu_clock); + gtk_label_set_text(GTK_LABEL(ctk_powermizer->gpu_clock), s); + g_free(s); - if (ctk_powermizer->processor_clock) { - ret = NvCtrlGetAttribute(handle, - NV_CTRL_GPU_CURRENT_PROCESSOR_CLOCK_FREQS, - &processor_clock); - if (ret == NvCtrlSuccess) { - s = g_strdup_printf("%d Mhz", processor_clock); - gtk_label_set_text(GTK_LABEL(ctk_powermizer->processor_clock), s); - g_free(s); - } - } - - ret = NvCtrlGetAttribute(handle, NV_CTRL_GPU_POWER_SOURCE, &power_source); - if (ret != NvCtrlSuccess) { - return FALSE; + s = g_strdup_printf("%d Mhz", memory_clock); + gtk_label_set_text(GTK_LABEL(ctk_powermizer->memory_clock), s); + g_free(s); } - if (power_source == NV_CTRL_GPU_POWER_SOURCE_AC) { - s = g_strdup_printf("AC"); - } - else if (power_source == NV_CTRL_GPU_POWER_SOURCE_BATTERY) { - s = g_strdup_printf("Battery"); - } - else { - s = g_strdup_printf("Error"); + ret = NvCtrlGetAttribute(handle, + NV_CTRL_GPU_CURRENT_PROCESSOR_CLOCK_FREQS, + &processor_clock); + if (ret == NvCtrlSuccess && ctk_powermizer->processor_clock) { + s = g_strdup_printf("%d Mhz", processor_clock); + gtk_label_set_text(GTK_LABEL(ctk_powermizer->processor_clock), s); + g_free(s); } - gtk_label_set_text(GTK_LABEL(ctk_powermizer->power_source), s); - g_free(s); + ret = NvCtrlGetAttribute(handle, NV_CTRL_GPU_POWER_SOURCE, &power_source); + if (ret == NvCtrlSuccess && ctk_powermizer->power_source) { + + if (power_source == NV_CTRL_GPU_POWER_SOURCE_AC) { + s = g_strdup_printf("AC"); + } + else if (power_source == NV_CTRL_GPU_POWER_SOURCE_BATTERY) { + s = g_strdup_printf("Battery"); + } + else { + s = g_strdup_printf("Error"); + } + + gtk_label_set_text(GTK_LABEL(ctk_powermizer->power_source), s); + g_free(s); + } if (ctk_powermizer->pcie_gen_queriable) { /* NV_CTRL_GPU_PCIE_CURRENT_LINK_WIDTH */ @@ -384,18 +402,19 @@ static gboolean update_powermizer_info(gpointer user_data) ret = NvCtrlGetAttribute(handle, NV_CTRL_GPU_CURRENT_PERFORMANCE_LEVEL, &perf_level); - if (ret != NvCtrlSuccess) { - return FALSE; + if (ret == NvCtrlSuccess && ctk_powermizer->performance_level) { + s = g_strdup_printf("%d", perf_level); + gtk_label_set_text(GTK_LABEL(ctk_powermizer->performance_level), s); + g_free(s); } - s = g_strdup_printf("%d", perf_level); - gtk_label_set_text(GTK_LABEL(ctk_powermizer->performance_level), s); - g_free(s); + if (ctk_powermizer->performance_level && ctk_powermizer->gpu_clock && + ctk_powermizer->memory_clock) { + /* update the perf table */ - /* update the perf table */ + update_perf_mode_table(ctk_powermizer, perf_level); + } - update_perf_mode_table(ctk_powermizer, perf_level); - return TRUE; } @@ -406,15 +425,22 @@ GtkWidget* ctk_powermizer_new(NvCtrlAttributeHandle *handle, GObject *object; CtkPowermizer *ctk_powermizer; GtkWidget *hbox, *hbox2, *vbox, *vbox2, *hsep, *table; - GtkWidget *banner, *label, *menu, *menu_item; + GtkWidget *banner, *label; + CtkDropDownMenu *menu; ReturnStatus ret, ret1; gint attribute; gint val; gint row = 0; gchar *s = NULL; gint tmp; + gint i; gboolean processor_clock_available = FALSE; + gboolean power_source_available = FALSE; + gboolean perf_level_available = FALSE; + gboolean adaptive_clock_state_available = FALSE; + gboolean clock_freqs_available = FALSE; gboolean cuda_dp_ui = FALSE; + gboolean pcie_gen_queriable = FALSE; /* make sure we have a handle */ @@ -423,34 +449,47 @@ GtkWidget* ctk_powermizer_new(NvCtrlAttributeHandle *handle, /* check if this screen supports powermizer querying */ ret = NvCtrlGetAttribute(handle, NV_CTRL_GPU_POWER_SOURCE, &val); - if (ret != NvCtrlSuccess) { - return NULL; + if (ret == NvCtrlSuccess) { + power_source_available = TRUE; } ret = NvCtrlGetAttribute(handle, NV_CTRL_GPU_CURRENT_PERFORMANCE_LEVEL, &val); - if (ret != NvCtrlSuccess) { - return NULL; + if (ret == NvCtrlSuccess) { + perf_level_available = TRUE; } ret = NvCtrlGetAttribute(handle, NV_CTRL_GPU_ADAPTIVE_CLOCK_STATE, &val); - if (ret != NvCtrlSuccess) { - return NULL; + if (ret == NvCtrlSuccess) { + adaptive_clock_state_available = TRUE; } ret = NvCtrlGetAttribute(handle, NV_CTRL_GPU_CURRENT_CLOCK_FREQS, &val); - if (ret != NvCtrlSuccess) { - return NULL; + if (ret == NvCtrlSuccess) { + clock_freqs_available = TRUE; } - ret = NvCtrlGetAttribute(handle, NV_CTRL_GPU_CURRENT_PROCESSOR_CLOCK_FREQS, + ret = NvCtrlGetAttribute(handle, NV_CTRL_GPU_CURRENT_PROCESSOR_CLOCK_FREQS, &val); if (ret == NvCtrlSuccess) { processor_clock_available = TRUE; } + /* NV_CTRL_GPU_PCIE_GENERATION */ + + ret = NvCtrlGetAttribute(handle, NV_CTRL_GPU_PCIE_GENERATION, &tmp); + if (ret == NvCtrlSuccess) { + pcie_gen_queriable = TRUE; + } + + /* return early if query to attributes fail */ + if (!power_source_available && !perf_level_available && + !adaptive_clock_state_available && clock_freqs_available && + !processor_clock_available && !pcie_gen_queriable) { + return NULL; + } /* create the CtkPowermizer object */ object = g_object_new(CTK_TYPE_POWERMIZER, NULL); @@ -458,14 +497,7 @@ GtkWidget* ctk_powermizer_new(NvCtrlAttributeHandle *handle, ctk_powermizer = CTK_POWERMIZER(object); ctk_powermizer->attribute_handle = handle; ctk_powermizer->ctk_config = ctk_config; - ctk_powermizer->pcie_gen_queriable = FALSE; - - /* NV_CTRL_GPU_PCIE_GENERATION */ - - ret = NvCtrlGetAttribute(handle, NV_CTRL_GPU_PCIE_GENERATION, &tmp); - if (ret == NvCtrlSuccess) { - ctk_powermizer->pcie_gen_queriable = TRUE; - } + ctk_powermizer->pcie_gen_queriable = pcie_gen_queriable; /* set container properties for the CtkPowermizer widget */ @@ -495,51 +527,58 @@ GtkWidget* ctk_powermizer_new(NvCtrlAttributeHandle *handle, gtk_container_set_border_width(GTK_CONTAINER(table), 10); /* Adaptive Clocking State */ - - ctk_powermizer->adaptive_clock_status = - add_table_row_with_help_text(table, ctk_config, - __adaptive_clock_help, - row++, //row - 0, // column - 0.0f, - 0.5, - "Adaptive Clocking:", - 0.0, - 0.5, - NULL); - - /* spacing */ - row += 3; + if (adaptive_clock_state_available) { + ctk_powermizer->adaptive_clock_status = + add_table_row_with_help_text(table, ctk_config, + __adaptive_clock_help, + row++, //row + 0, // column + 0.0f, + 0.5, + "Adaptive Clocking:", + 0.0, + 0.5, + NULL); + } else { + ctk_powermizer->adaptive_clock_status = NULL; + } /* Clock Frequencies */ + if (clock_freqs_available) { + /* spacing */ + row += 3; + ctk_powermizer->gpu_clock = + add_table_row_with_help_text(table, ctk_config, + __gpu_clock_freq_help, + row++, //row + 0, // column + 0.0f, + 0.5, + "Graphics Clock:", + 0.0, + 0.5, + NULL); - ctk_powermizer->gpu_clock = - add_table_row_with_help_text(table, ctk_config, - __gpu_clock_freq_help, - row++, //row - 0, // column - 0.0f, - 0.5, - "Graphics Clock:", - 0.0, - 0.5, - NULL); - - ctk_powermizer->memory_clock = - add_table_row_with_help_text(table, ctk_config, - __memory_clock_freq_help, - row++, //row - 0, // column - 0.0f, - 0.5, - "Memory Clock:", - 0.0, - 0.5, - NULL); - + ctk_powermizer->memory_clock = + add_table_row_with_help_text(table, ctk_config, + __memory_clock_freq_help, + row++, //row + 0, // column + 0.0f, + 0.5, + "Memory Clock:", + 0.0, + 0.5, + NULL); + } else { + ctk_powermizer->gpu_clock = NULL; + ctk_powermizer->memory_clock = NULL; + } /* Processor clock */ if (processor_clock_available) { + /* spacing */ + row += 3; ctk_powermizer->processor_clock = add_table_row_with_help_text(table, ctk_config, __processor_clock_freq_help, @@ -551,27 +590,31 @@ GtkWidget* ctk_powermizer_new(NvCtrlAttributeHandle *handle, 0.0, 0.5, NULL); + } else { + ctk_powermizer->processor_clock = NULL; } - /* spacing */ - row += 3; - /* Power Source */ - ctk_powermizer->power_source = - add_table_row_with_help_text(table, ctk_config, - __power_source_help, - row++, //row - 0, // column - 0.0f, - 0.5, - "Power Source:", - 0.0, - 0.5, - NULL); - /* spacing */ - row += 3; - + if (power_source_available) { + /* spacing */ + row += 3; + ctk_powermizer->power_source = + add_table_row_with_help_text(table, ctk_config, + __power_source_help, + row++, //row + 0, // column + 0.0f, + 0.5, + "Power Source:", + 0.0, + 0.5, + NULL); + } else { + ctk_powermizer->power_source = NULL; + } /* Current PCIe Link Width */ if (ctk_powermizer->pcie_gen_queriable) { + /* spacing */ + row += 3; ctk_powermizer->link_width = add_table_row_with_help_text(table, ctk_config, __current_pcie_link_width_help, @@ -596,40 +639,49 @@ GtkWidget* ctk_powermizer_new(NvCtrlAttributeHandle *handle, 0.0, 0.5, NULL); - /* spacing */ - row += 3; + } else { + ctk_powermizer->link_width = NULL; + ctk_powermizer->link_speed = NULL; } /* Performance Level */ - ctk_powermizer->performance_level = - add_table_row_with_help_text(table, ctk_config, - __performance_level_help, - row++, //row - 0, // column - 0.0f, - 0.5, - "Performance Level:", - 0.0, - 0.5, - NULL); + if (perf_level_available) { + /* spacing */ + row += 3; + ctk_powermizer->performance_level = + add_table_row_with_help_text(table, ctk_config, + __performance_level_help, + row++, //row + 0, // column + 0.0f, + 0.5, + "Performance Level:", + 0.0, + 0.5, + NULL); + } else { + ctk_powermizer->performance_level = NULL; + } gtk_table_resize(GTK_TABLE(table), row, 2); /* Available Performance Level Title */ - hbox = gtk_hbox_new(FALSE, 0); - gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + if (perf_level_available && clock_freqs_available) { + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); - label = gtk_label_new("Performance Levels"); - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + label = gtk_label_new("Performance Levels"); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); - hsep = gtk_hseparator_new(); - gtk_box_pack_start(GTK_BOX(hbox), hsep, TRUE, TRUE, 5); + hsep = gtk_hseparator_new(); + gtk_box_pack_start(GTK_BOX(hbox), hsep, TRUE, TRUE, 5); - /* Available Performance Level Table */ + /* Available Performance Level Table */ - hbox = gtk_hbox_new(FALSE, 0); - gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); - ctk_powermizer->performance_table_hbox = hbox; + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + ctk_powermizer->performance_table_hbox = hbox; + } /* Register a timer callback to update the temperatures */ @@ -665,19 +717,14 @@ GtkWidget* ctk_powermizer_new(NvCtrlAttributeHandle *handle, /* Specifying drop down list */ - menu = gtk_menu_new(); + menu = (CtkDropDownMenu *) + ctk_drop_down_menu_new(CTK_DROP_DOWN_MENU_FLAG_COMBO); - menu_item = gtk_menu_item_new_with_label("Adaptive"); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); - gtk_widget_show(menu_item); - - menu_item = gtk_menu_item_new_with_label("Prefer Maximum Performance"); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); - gtk_widget_show(menu_item); + for (i=0; i < ARRAY_LEN(__powermizer_modes); i++) { + ctk_drop_down_menu_append_item(menu, __powermizer_modes[i].label, i); + } - ctk_powermizer->powermizer_menu = gtk_option_menu_new(); - gtk_option_menu_set_menu(GTK_OPTION_MENU(ctk_powermizer->powermizer_menu), - menu); + ctk_powermizer->powermizer_menu = GTK_WIDGET(menu); update_powermizer_menu_info(ctk_powermizer); g_signal_connect(G_OBJECT(ctk_powermizer->powermizer_menu), "changed", @@ -809,6 +856,8 @@ GtkWidget* ctk_powermizer_new(NvCtrlAttributeHandle *handle, G_CALLBACK(dp_configuration_update_received), (gpointer) ctk_powermizer); } + } else { + ctk_powermizer->configuration_button = NULL; } /* Updating the powermizer page */ @@ -837,6 +886,9 @@ static void update_powermizer_menu_info(gpointer user_data) gint powerMizerMode = NV_CTRL_GPU_POWER_MIZER_MODE_ADAPTIVE; NVCTRLAttributeValidValuesRec valid; ReturnStatus ret0, ret1; + CtkDropDownMenu *menu; + + menu = CTK_DROP_DOWN_MENU(ctk_powermizer->powermizer_menu); ret0 = NvCtrlGetValidAttributeValues(ctk_powermizer->attribute_handle, NV_CTRL_GPU_POWER_MIZER_MODE, @@ -853,8 +905,7 @@ static void update_powermizer_menu_info(gpointer user_data) G_CALLBACK(powermizer_menu_changed), (gpointer) ctk_powermizer); - gtk_option_menu_set_history(GTK_OPTION_MENU(ctk_powermizer->powermizer_menu), - powerMizerMode); + ctk_drop_down_menu_set_current_value(menu, powerMizerMode); g_signal_handlers_unblock_by_func(G_OBJECT(ctk_powermizer->powermizer_menu), G_CALLBACK(powermizer_menu_changed), @@ -864,24 +915,17 @@ static void update_powermizer_menu_info(gpointer user_data) } } -static void powermizer_menu_changed(GtkOptionMenu *powermizer_menu, +static void powermizer_menu_changed(GtkWidget *widget, gpointer user_data) { CtkPowermizer *ctk_powermizer; gint history, powerMizerMode = NV_CTRL_GPU_POWER_MIZER_MODE_ADAPTIVE; - + CtkDropDownMenu *menu = CTK_DROP_DOWN_MENU(widget); ctk_powermizer = CTK_POWERMIZER(user_data); - history = gtk_option_menu_get_history(powermizer_menu); + history = ctk_drop_down_menu_get_current_value(menu); - switch (history) { - case 1: - powerMizerMode = NV_CTRL_GPU_POWER_MIZER_MODE_PREFER_MAXIMUM_PERFORMANCE; - break; - case 0: - default: - powerMizerMode = NV_CTRL_GPU_POWER_MIZER_MODE_ADAPTIVE; - } + powerMizerMode = __powermizer_modes[history].attr; if (NvCtrlSuccess != NvCtrlSetAttribute(ctk_powermizer->attribute_handle, NV_CTRL_GPU_POWER_MIZER_MODE, @@ -889,12 +933,15 @@ static void powermizer_menu_changed(GtkOptionMenu *powermizer_menu, return; } + ctk_config_statusbar_message(ctk_powermizer->ctk_config, + "Preferred Mode set to %s.", + __powermizer_modes[history].label); + g_signal_handlers_block_by_func(G_OBJECT(ctk_powermizer->powermizer_menu), G_CALLBACK(powermizer_menu_changed), (gpointer) ctk_powermizer); - gtk_option_menu_set_history(GTK_OPTION_MENU(ctk_powermizer->powermizer_menu), - powerMizerMode); + ctk_drop_down_menu_set_current_value(menu, powerMizerMode); g_signal_handlers_unblock_by_func(G_OBJECT(ctk_powermizer->powermizer_menu), G_CALLBACK(powermizer_menu_changed), @@ -1075,25 +1122,30 @@ GtkTextBuffer *ctk_powermizer_create_help(GtkTextTagTable *table, gtk_text_buffer_get_iter_at_offset(b, &i, 0); ctk_help_title(b, &i, "PowerMizer Monitor Help"); - ctk_help_para(b, &i, "When SLI is enabled, this page is only exposed " - "on the master GPU, and changes here will apply to " - "all GPUs in the SLI group."); - - ctk_help_heading(b, &i, "Adaptive Clocking"); - ctk_help_para(b, &i, __adaptive_clock_help); + ctk_help_para(b, &i, "This page shows powermizer monitor options " + "available on this GPU."); - ctk_help_heading(b, &i, "Clock Frequencies"); - if (ctk_powermizer->processor_clock) { - s = "This indicates the GPU's current Graphics Clock, " - "Memory Clock and Processor Clock frequencies."; - } else { - s = "This indicates the GPU's current Graphics Clock and " - "Memory Clock frequencies."; + if (ctk_powermizer->adaptive_clock_status) { + ctk_help_heading(b, &i, "Adaptive Clocking"); + ctk_help_para(b, &i, __adaptive_clock_help); + } + + if (ctk_powermizer->gpu_clock && ctk_powermizer->memory_clock) { + ctk_help_heading(b, &i, "Clock Frequencies"); + if (ctk_powermizer->processor_clock) { + s = "This indicates the GPU's current Graphics Clock, " + "Memory Clock and Processor Clock frequencies."; + } else { + s = "This indicates the GPU's current Graphics Clock and " + "Memory Clock frequencies."; + } + ctk_help_para(b, &i, s); } - ctk_help_para(b, &i, s); - ctk_help_heading(b, &i, "Power Source"); - ctk_help_para(b, &i, __power_source_help); + if (ctk_powermizer->power_source) { + ctk_help_heading(b, &i, "Power Source"); + ctk_help_para(b, &i, __power_source_help); + } if (ctk_powermizer->pcie_gen_queriable) { ctk_help_heading(b, &i, "Current PCIe link width"); @@ -1102,14 +1154,17 @@ GtkTextBuffer *ctk_powermizer_create_help(GtkTextTagTable *table, ctk_help_para(b, &i, __current_pcie_link_speed_help); } - ctk_help_heading(b, &i, "Performance Level"); - ctk_help_para(b, &i, __performance_level_help); - - ctk_help_heading(b, &i, "Performance Levels (Table)"); - ctk_help_para(b, &i, __performance_levels_table_help); + if (ctk_powermizer->performance_level) { + ctk_help_heading(b, &i, "Performance Level"); + ctk_help_para(b, &i, __performance_level_help); + ctk_help_heading(b, &i, "Performance Levels (Table)"); + ctk_help_para(b, &i, __performance_levels_table_help); + } - ctk_help_heading(b, &i, "PowerMizer Settings"); - ctk_help_para(b, &i, __powermizer_menu_help); + if (ctk_powermizer->powermizer_menu) { + ctk_help_heading(b, &i, "PowerMizer Settings"); + ctk_help_para(b, &i, __powermizer_menu_help); + } if (ctk_powermizer->configuration_button) { ctk_help_heading(b, &i, "CUDA - Double precision"); diff --git a/src/gtk+-2.x/ctkslimm.c b/src/gtk+-2.x/ctkslimm.c index 3d784cd..5db917e 100644 --- a/src/gtk+-2.x/ctkslimm.c +++ b/src/gtk+-2.x/ctkslimm.c @@ -34,6 +34,7 @@ #include "ctkdisplayconfig-utils.h" #include "ctkhelp.h" #include "ctkutils.h" +#include "ctkdropdownmenu.h" /* Static function declarations */ @@ -221,6 +222,7 @@ static XConfigPtr xconfig_generate(XConfigPtr xconfCur, void *callback_data) { CtkSLIMM *ctk_object = (CtkSLIMM *)callback_data; + CtkDropDownMenu *menu = CTK_DROP_DOWN_MENU(ctk_object->mnu_display_config); gint idx; @@ -246,7 +248,7 @@ static XConfigPtr xconfig_generate(XConfigPtr xconfCur, if (checkbox_state) { GridConfig *grid_config; /* SLI MM needs to be enabled */ - idx = gtk_option_menu_get_history(GTK_OPTION_MENU(ctk_object->mnu_display_config)); + idx = ctk_drop_down_menu_get_current_value(menu); /* Get grid configuration values from index */ @@ -300,12 +302,15 @@ static Bool compute_screen_size(CtkSLIMM *ctk_object, gint *width, gint x_displays,y_displays; gint h_overlap, v_overlap; + CtkDropDownMenu *menu; + if (!ctk_object->cur_modeline) { return FALSE; } - config_idx = gtk_option_menu_get_history(GTK_OPTION_MENU(ctk_object->mnu_display_config)); + menu = CTK_DROP_DOWN_MENU(ctk_object->mnu_display_config); + config_idx = ctk_drop_down_menu_get_current_value(menu); /* Get grid configuration values from index */ grid_config = get_ith_valid_grid_config(config_idx); @@ -403,10 +408,11 @@ static void display_config_changed(GtkWidget *widget, gpointer user_data) static void display_refresh_changed(GtkWidget *widget, gpointer user_data) { CtkSLIMM *ctk_object = CTK_SLIMM(user_data); + CtkDropDownMenu *menu = CTK_DROP_DOWN_MENU(widget); gint idx; /* Get the modeline and display to set */ - idx = gtk_option_menu_get_history(GTK_OPTION_MENU(widget)); + idx = ctk_drop_down_menu_get_current_value(menu); /* Select the new modeline as current modeline */ ctk_object->cur_modeline = ctk_object->refresh_table[idx]; @@ -416,12 +422,13 @@ static void display_refresh_changed(GtkWidget *widget, gpointer user_data) static void display_resolution_changed(GtkWidget *widget, gpointer user_data) { CtkSLIMM *ctk_object = CTK_SLIMM(user_data); + CtkDropDownMenu *menu = CTK_DROP_DOWN_MENU(widget); gint idx; nvModeLinePtr modeline; /* Get the modeline and display to set */ - idx = gtk_option_menu_get_history(GTK_OPTION_MENU(widget)); + idx = ctk_drop_down_menu_get_current_value(menu); modeline = ctk_object->resolution_table[idx]; /* Ignore selecting same resolution */ @@ -516,8 +523,7 @@ static void setup_total_size_label(CtkSLIMM *ctk_object) static void setup_display_refresh_dropdown(CtkSLIMM *ctk_object) { - GtkWidget *menu; - GtkWidget *menu_item; + CtkDropDownMenu *menu; nvModeLinePtr modeline; float cur_rate; /* Refresh Rate */ int cur_idx = 0; /* Currently selected modeline */ @@ -546,7 +552,13 @@ static void setup_display_refresh_dropdown(CtkSLIMM *ctk_object) /* Generate the refresh dropdown */ - menu = gtk_menu_new(); + menu = CTK_DROP_DOWN_MENU(ctk_object->mnu_display_refresh); + + ctk_drop_down_menu_reset(menu); + + g_signal_handlers_block_by_func + (G_OBJECT(ctk_object->mnu_display_refresh), + G_CALLBACK(display_refresh_changed), (gpointer) ctk_object); /* Generate the refresh rate dropdown from the modelines list */ for (modeline = ctk_object->modelines; modeline; modeline = modeline->next) { @@ -650,22 +662,14 @@ static void setup_display_refresh_dropdown(CtkSLIMM *ctk_object) /* Add the modeline entry to the dropdown */ - menu_item = gtk_menu_item_new_with_label(name); + ctk_drop_down_menu_append_item(menu, name, ctk_object->refresh_table_len); g_free(name); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); - gtk_widget_show(menu_item); ctk_object->refresh_table[ctk_object->refresh_table_len++] = modeline; } /* Setup the menu and select the current mode */ - g_signal_handlers_block_by_func - (G_OBJECT(ctk_object->mnu_display_refresh), - G_CALLBACK(display_refresh_changed), (gpointer) ctk_object); - ctk_object->cur_modeline = ctk_object->refresh_table[cur_idx]; - - gtk_option_menu_set_menu(GTK_OPTION_MENU(ctk_object->mnu_display_refresh), menu); - gtk_option_menu_set_history(GTK_OPTION_MENU(ctk_object->mnu_display_refresh), cur_idx); + ctk_drop_down_menu_set_current_value(menu, cur_idx); gtk_widget_set_sensitive(ctk_object->mnu_display_refresh, True); g_signal_handlers_unblock_by_func @@ -693,8 +697,7 @@ static void setup_display_refresh_dropdown(CtkSLIMM *ctk_object) static void setup_display_resolution_dropdown(CtkSLIMM *ctk_object) { - GtkWidget *menu; - GtkWidget *menu_item; + CtkDropDownMenu *menu; nvModeLinePtr modeline; nvModeLinePtr cur_modeline = ctk_object->cur_modeline; @@ -713,11 +716,14 @@ static void setup_display_resolution_dropdown(CtkSLIMM *ctk_object) } /* Start the menu generation */ - menu = gtk_menu_new(); + menu = CTK_DROP_DOWN_MENU(ctk_object->mnu_display_resolution); modeline = ctk_object->modelines; cur_idx = 0; + g_signal_handlers_block_by_func + (G_OBJECT(ctk_object->mnu_display_resolution), + G_CALLBACK(display_resolution_changed), (gpointer) ctk_object); /* Generate the resolution menu */ while (modeline) { @@ -748,10 +754,9 @@ static void setup_display_resolution_dropdown(CtkSLIMM *ctk_object) name = g_strdup_printf("%dx%d", modeline->data.hdisplay, modeline->data.vdisplay); - menu_item = gtk_menu_item_new_with_label(name); + ctk_drop_down_menu_append_item(menu, name, + ctk_object->resolution_table_len); g_free(name); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); - gtk_widget_show(menu_item); ctk_object->resolution_table[ctk_object->resolution_table_len++] = modeline; } @@ -759,15 +764,8 @@ static void setup_display_resolution_dropdown(CtkSLIMM *ctk_object) } /* Setup the menu and select the current mode */ - g_signal_handlers_block_by_func - (G_OBJECT(ctk_object->mnu_display_resolution), - G_CALLBACK(display_resolution_changed), (gpointer) ctk_object); - gtk_option_menu_set_menu - (GTK_OPTION_MENU(ctk_object->mnu_display_resolution), menu); - - gtk_option_menu_set_history - (GTK_OPTION_MENU(ctk_object->mnu_display_resolution), cur_idx); + ctk_drop_down_menu_set_current_value(menu, cur_idx); /* If dropdown has only one item, disable menu selection */ if (ctk_object->resolution_table_len > 1) { @@ -785,9 +783,6 @@ static void setup_display_resolution_dropdown(CtkSLIMM *ctk_object) /* Handle failures */ fail: - gtk_option_menu_remove_menu - (GTK_OPTION_MENU(ctk_object->mnu_display_resolution)); - gtk_widget_set_sensitive(ctk_object->mnu_display_resolution, False); } /* setup_display_resolution_dropdown() */ @@ -1256,8 +1251,9 @@ GtkWidget* ctk_slimm_new(NvCtrlAttributeHandle *handle, GtkWidget *table; GtkWidget *button; - GtkWidget *optionmenu, *menu, *menuitem, *spinbutton; + GtkWidget *spinbutton; CtkSLIMM *ctk_object; + CtkDropDownMenu *menu; gchar *err_str = NULL; gchar *tmp; @@ -1489,9 +1485,9 @@ GtkWidget* ctk_slimm_new(NvCtrlAttributeHandle *handle, hbox = gtk_hbox_new(FALSE, 0); /* Option menu for Display Grid Configuration */ - optionmenu = gtk_option_menu_new(); - ctk_slimm->mnu_display_config = optionmenu; - menu = gtk_menu_new(); + menu = (CtkDropDownMenu *) + ctk_drop_down_menu_new(CTK_DROP_DOWN_MENU_FLAG_COMBO); + ctk_slimm->mnu_display_config = GTK_WIDGET(menu); grid_menu_selected_id = 0; count = 0; @@ -1502,9 +1498,7 @@ GtkWidget* ctk_slimm_new(NvCtrlAttributeHandle *handle, gridConfigs[iter].rows, gridConfigs[iter].columns); - menuitem = gtk_menu_item_new_with_label(tmp); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); - gtk_widget_show(menuitem); + ctk_drop_down_menu_append_item(menu, tmp, count); /* Update grid_config_id to set menu history */ if (iter == grid_config_id) { @@ -1513,9 +1507,7 @@ GtkWidget* ctk_slimm_new(NvCtrlAttributeHandle *handle, count++; } - gtk_option_menu_set_menu(GTK_OPTION_MENU(optionmenu), menu); - gtk_option_menu_set_history(GTK_OPTION_MENU(ctk_slimm->mnu_display_config), - grid_menu_selected_id); + ctk_drop_down_menu_set_current_value(menu, grid_menu_selected_id); g_signal_connect(G_OBJECT(ctk_object->mnu_display_config), "changed", G_CALLBACK(display_config_changed), @@ -1523,7 +1515,7 @@ GtkWidget* ctk_slimm_new(NvCtrlAttributeHandle *handle, label = gtk_label_new(""); gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5); - gtk_box_pack_start(GTK_BOX(hbox), optionmenu, TRUE, TRUE, 5); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(menu), TRUE, TRUE, 5); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); table = gtk_table_new(20, 2, FALSE); @@ -1554,8 +1546,8 @@ GtkWidget* ctk_slimm_new(NvCtrlAttributeHandle *handle, /* Option menu for resolutions */ hbox = gtk_hbox_new(FALSE, 0); - optionmenu = gtk_option_menu_new(); - ctk_slimm->mnu_display_resolution = optionmenu; + ctk_slimm->mnu_display_resolution = + ctk_drop_down_menu_new(CTK_DROP_DOWN_MENU_FLAG_COMBO); /* Create a drop down menu */ setup_display_resolution_dropdown(ctk_object); @@ -1571,9 +1563,9 @@ GtkWidget* ctk_slimm_new(NvCtrlAttributeHandle *handle, /* Option menu for refresh rates */ - optionmenu = gtk_option_menu_new(); hbox = gtk_hbox_new(FALSE, 0); - ctk_slimm->mnu_display_refresh = optionmenu; + ctk_slimm->mnu_display_refresh = + ctk_drop_down_menu_new(CTK_DROP_DOWN_MENU_FLAG_COMBO); setup_display_refresh_dropdown(ctk_object); g_signal_connect(G_OBJECT(ctk_object->mnu_display_refresh), "changed", G_CALLBACK(display_refresh_changed), @@ -1581,7 +1573,7 @@ GtkWidget* ctk_slimm_new(NvCtrlAttributeHandle *handle, label = gtk_label_new(""); gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5); - gtk_box_pack_end(GTK_BOX(hbox), optionmenu, TRUE, TRUE, 0); + gtk_box_pack_end(GTK_BOX(hbox), ctk_slimm->mnu_display_refresh, TRUE, TRUE, 0); gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 3, 4, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0.5, 0.5); diff --git a/src/gtk+-2.x/ctkthermal.c b/src/gtk+-2.x/ctkthermal.c index dc825cc..26c23b0 100644 --- a/src/gtk+-2.x/ctkthermal.c +++ b/src/gtk+-2.x/ctkthermal.c @@ -1661,7 +1661,7 @@ next_help: ctk_help_heading(b, &i, "ID"); ctk_help_para(b, &i, __fan_id_help); - ctk_help_heading(b, &i, "Speed (%)"); + ctk_help_heading(b, &i, "Speed (%%)"); ctk_help_para(b, &i, __fan_speed_help); ctk_help_heading(b, &i, "Type"); diff --git a/src/gtk+-2.x/ctkutils.h b/src/gtk+-2.x/ctkutils.h index 76d707d..8e4cac5 100644 --- a/src/gtk+-2.x/ctkutils.h +++ b/src/gtk+-2.x/ctkutils.h @@ -27,6 +27,12 @@ G_BEGIN_DECLS +// GValues must be initialized before they are used. This macro is +// only available since glib 2.30 +#ifndef G_VALUE_INIT +# define G_VALUE_INIT { 0, { { 0 } } } +#endif + gchar *get_pcie_generation_string(NvCtrlAttributeHandle *handle); gchar *get_pcie_link_width_string(NvCtrlAttributeHandle *handle, diff --git a/src/gtk+-2.x/ctkvdpau.c b/src/gtk+-2.x/ctkvdpau.c new file mode 100644 index 0000000..8a834e8 --- /dev/null +++ b/src/gtk+-2.x/ctkvdpau.c @@ -0,0 +1,1584 @@ +/* + * nvidia-settings: A tool for configuring the NVIDIA X driver on Unix + * and Linux systems. + * + * Copyright (C) 2012 NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses>. + * + * + * The VDPAU page is based on vdpauinfo 0.0.6: + * + * http://cgit.freedesktop.org/~aplattner/vdpauinfo/ + * http://people.freedesktop.org/~aplattner/vdpau/vdpauinfo-0.0.6.tar.gz + * + * which has the following copyright and license: + * + * Copyright (c) 2008 Wladimir J. van der Laan + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <stdlib.h> +#include <string.h> +#include <inttypes.h> +#include <dlfcn.h> + +#include <gtk/gtk.h> + +#include "ctkutils.h" +#include "ctkhelp.h" +#include "ctkvdpau.h" +#include "ctkbanner.h" + +const gchar* __vdpau_information_label_help = +"This page shows information about the Video Decode and Presentation API for " +"Unix-like systems (VDPAU) library."; + +const gchar* __base_information_help = +"This tab shows the VDPAU API version and supported codecs."; + +const gchar* __vdpau_api_version_help = +"This shows the VDPAU API version."; + +const gchar* __supported_codecs_help = +"This shows the supported codecs."; + +const gchar* __surface_limits_help = +"This tab shows the maximum supported resolution and formats for video, " +"bitmap and output surfaces."; + +const gchar* __video_surface_help = +"This shows the maximum supported resolution and formats for video surfaces."; + +const gchar* __bitmap_surface_help = +"This shows the maximum supported resolution and formats for bitmap surfaces."; + +const gchar* __ouput_surface_help = +"This shows the maximum supported resolution and formats for output surfaces."; + +const gchar* __decoder_limits_help = +"This tab shows the maximum level, number of macroblocks and resolution for " +"each supported VDPAU decoder."; + +const gchar* __video_mixer_help = +"This tab shows the capabilities of the VDPAU video mixer: the features, " +"parameters, and attributes."; + +const gchar* __video_mixer_feature_help = +"This shows the features supported by the video mixer."; + +const gchar* __video_mixer_parameter_help = +"This shows the video mixer parameters and any applicable ranges."; + +const gchar* __video_mixer_attribute_help = +"This shows the video mixer attributes and any applicable ranges."; + +static int queryOutputSurface(CtkVDPAU *ctk_vdpau, VdpDevice device, + VdpGetProcAddress *getProcAddress); + +static int queryBitmapSurface(CtkVDPAU *ctk_vdpau, VdpDevice device, + VdpGetProcAddress *getProcAddress); + +#define GETADDR(device, function_id, function_pointer) \ + getProcAddress(device, function_id, function_pointer) + +static void getAddressVDPAUDeviceFunctions(VdpDevice device, + VdpGetProcAddress *getProcAddress) +{ + GETADDR(device, VDP_FUNC_ID_GET_ERROR_STRING, + (void**)&VDPAUDeviceFunctions.GetErrorString); + GETADDR(device, VDP_FUNC_ID_GET_PROC_ADDRESS, + (void**)&VDPAUDeviceFunctions.GetProcAddress); + GETADDR(device, VDP_FUNC_ID_GET_API_VERSION, + (void**)&VDPAUDeviceFunctions.GetApiVersion); + GETADDR(device, VDP_FUNC_ID_GET_INFORMATION_STRING, + (void**)&VDPAUDeviceFunctions.GetInformationString); + GETADDR(device, VDP_FUNC_ID_VIDEO_SURFACE_QUERY_CAPABILITIES, + (void**)&VDPAUDeviceFunctions.VideoSurfaceQueryCapabilities); + GETADDR(device, VDP_FUNC_ID_VIDEO_SURFACE_QUERY_GET_PUT_BITS_Y_CB_CR_CAPABILITIES, + (void**)&VDPAUDeviceFunctions.VideoSurfaceQueryGetPutBitsYCbCrCapabilities); + GETADDR(device, VDP_FUNC_ID_OUTPUT_SURFACE_QUERY_CAPABILITIES, + (void**)&VDPAUDeviceFunctions.OutputSurfaceQueryCapabilities); + GETADDR(device, VDP_FUNC_ID_OUTPUT_SURFACE_QUERY_GET_PUT_BITS_NATIVE_CAPABILITIES, + (void**)&VDPAUDeviceFunctions.OutputSurfaceQueryGetPutBitsNativeCapabilities); + GETADDR(device, VDP_FUNC_ID_OUTPUT_SURFACE_QUERY_PUT_BITS_Y_CB_CR_CAPABILITIES, + (void**)&VDPAUDeviceFunctions.OutputSurfaceQueryPutBitsYCbCrCapabilities); + GETADDR(device, VDP_FUNC_ID_BITMAP_SURFACE_QUERY_CAPABILITIES, + (void**)&VDPAUDeviceFunctions.BitmapSurfaceQueryCapabilities); + GETADDR(device, VDP_FUNC_ID_DECODER_QUERY_CAPABILITIES, + (void**)&VDPAUDeviceFunctions.DecoderQueryCapabilities); + GETADDR(device, VDP_FUNC_ID_VIDEO_MIXER_QUERY_FEATURE_SUPPORT, + (void**)&VDPAUDeviceFunctions.VideoMixerQueryFeatureSupport); + GETADDR(device, VDP_FUNC_ID_VIDEO_MIXER_QUERY_PARAMETER_SUPPORT, + (void**)&VDPAUDeviceFunctions.VideoMixerQueryParameterSupport); + GETADDR(device, VDP_FUNC_ID_VIDEO_MIXER_QUERY_ATTRIBUTE_SUPPORT, + (void**)&VDPAUDeviceFunctions.VideoMixerQueryAttributeSupport); + GETADDR(device, VDP_FUNC_ID_VIDEO_MIXER_QUERY_PARAMETER_VALUE_RANGE, + (void**)&VDPAUDeviceFunctions.VideoMixerQueryParameterValueRange); + GETADDR(device, VDP_FUNC_ID_VIDEO_MIXER_QUERY_ATTRIBUTE_VALUE_RANGE, + (void**)&VDPAUDeviceFunctions.VideoMixerQueryAttributeValueRange); + +} +#undef GETADDR + + + +/* + * queryBaseInfo() - Query basic VDPAU information + */ + +static int queryBaseInfo(CtkVDPAU *ctk_vdpau, VdpDevice device, + VdpGetProcAddress *getProcAddress) +{ + static const Desc decoder_list[] = { + {"MPEG1", VDP_DECODER_PROFILE_MPEG1, 0x01}, + {"MPEG2", VDP_DECODER_PROFILE_MPEG2_SIMPLE, 0x02}, + {"MPEG2", VDP_DECODER_PROFILE_MPEG2_MAIN, 0x02}, + {"H264", VDP_DECODER_PROFILE_H264_BASELINE, 0x04}, + {"H264", VDP_DECODER_PROFILE_H264_MAIN, 0x04}, + {"H264", VDP_DECODER_PROFILE_H264_HIGH, 0x04}, + {"VC1", VDP_DECODER_PROFILE_VC1_SIMPLE, 0x08}, + {"VC1" , VDP_DECODER_PROFILE_VC1_MAIN, 0x08}, + {"VC1", VDP_DECODER_PROFILE_VC1_ADVANCED, 0x08}, + {"MPEG4", VDP_DECODER_PROFILE_MPEG4_PART2_SP, 0x10}, + {"MPEG4", VDP_DECODER_PROFILE_MPEG4_PART2_ASP, 0x10}, + {"DIVX4", VDP_DECODER_PROFILE_DIVX4_QMOBILE, 0x20}, + {"DIVX4", VDP_DECODER_PROFILE_DIVX4_MOBILE, 0x20}, + {"DIVX4", VDP_DECODER_PROFILE_DIVX4_HOME_THEATER, 0x20}, + {"DIVX4", VDP_DECODER_PROFILE_DIVX4_HD_1080P, 0x20}, + {"DIVX5", VDP_DECODER_PROFILE_DIVX5_QMOBILE, 0x40}, + {"DIVX5", VDP_DECODER_PROFILE_DIVX5_MOBILE, 0x40}, + {"DIVX5", VDP_DECODER_PROFILE_DIVX5_HOME_THEATER, 0x40}, + {"DIVX5", VDP_DECODER_PROFILE_DIVX5_HD_1080P, 0x40}, + }; + const size_t decoder_list_count = sizeof(decoder_list)/sizeof(Desc); + + GtkWidget *vbox, *hbox; + GtkWidget *table; + GtkWidget *label, *event; + uint32_t api; + GtkWidget *eventbox; + int x, count = 0; + uint32_t decoder_mask = 0; + + if (VDPAUDeviceFunctions.GetApiVersion && + (VDPAUDeviceFunctions.GetApiVersion(&api) != VDP_STATUS_OK)) { + return -1; + } + + /* Add base information */ + + vbox = gtk_vbox_new(FALSE, 0); + eventbox = gtk_event_box_new(); + gtk_container_add(GTK_CONTAINER(eventbox), vbox); + gtk_widget_modify_fg(eventbox, GTK_STATE_NORMAL, + &(eventbox->style->text[GTK_STATE_NORMAL])); + gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL, + &(eventbox->style->base[GTK_STATE_NORMAL])); + + gtk_notebook_append_page(GTK_NOTEBOOK(ctk_vdpau->notebook), eventbox, + gtk_label_new("Base Information")); + + hbox = gtk_hbox_new(FALSE, 0); + table = gtk_table_new(2, 2, FALSE); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 10); + gtk_box_pack_start(GTK_BOX(hbox), table, FALSE, FALSE, 10); + gtk_table_set_row_spacings(GTK_TABLE(table), 3); + gtk_table_set_col_spacings(GTK_TABLE(table), 15); + add_table_row_with_help_text(table, ctk_vdpau->ctk_config, + __vdpau_api_version_help, + 0, 0, + 0, 0, "API version:", + 0, 0, g_strdup_printf("%i", api)); + + label = gtk_label_new("Supported Codecs:"); + event = gtk_event_box_new(); + gtk_container_add(GTK_CONTAINER(event), label); + gtk_widget_modify_fg(event, GTK_STATE_NORMAL, + &(event->style->text[GTK_STATE_NORMAL])); + gtk_widget_modify_bg(event, GTK_STATE_NORMAL, + &(event->style->base[GTK_STATE_NORMAL])); + ctk_config_set_tooltip(ctk_vdpau->ctk_config, event, + __supported_codecs_help); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), event, 0, 1, 1, 2, + GTK_FILL, GTK_FILL | GTK_EXPAND, 0, 0); + + + for (x = 0; x < decoder_list_count; x++) { + VdpBool is_supported = FALSE; + VdpStatus ret; + uint32_t max_level, max_macroblocks, max_width, max_height; + + ret = VDPAUDeviceFunctions.DecoderQueryCapabilities(device, + decoder_list[x].id, + &is_supported, + &max_level, + &max_macroblocks, + &max_width, + &max_height); + if (ret == VDP_STATUS_OK && is_supported) { + gchar *str; + + if (decoder_mask & decoder_list[x].aux) { + continue; + } + gtk_table_resize(GTK_TABLE(table), 2+count, 2); + str = g_strdup_printf("%s", decoder_list[x].name); + label = gtk_label_new(str); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + g_free(str); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 1, 2, count+1, count+2, + GTK_FILL, GTK_FILL | GTK_EXPAND, 0, 0); + count++; + decoder_mask |= decoder_list[x].aux; + } + } + ctk_vdpau->baseInfoVbox = vbox; + + return 0; +} /* queryBaseInfo() */ + + + +/**************** Video surface ************/ +static const Desc ycbcr_types[] = { + {"NV12", VDP_YCBCR_FORMAT_NV12, 0}, + {"YV12", VDP_YCBCR_FORMAT_YV12, 0}, + {"UYVY", VDP_YCBCR_FORMAT_UYVY, 0}, + {"YUYV", VDP_YCBCR_FORMAT_YUYV, 0}, + {"Y8U8V8A8", VDP_YCBCR_FORMAT_Y8U8V8A8, 0}, + {"V8U8Y8A8", VDP_YCBCR_FORMAT_V8U8Y8A8, 0}, +}; +static const size_t ycbcr_type_count = sizeof(ycbcr_types)/sizeof(Desc); + +static const Desc rgb_types[] = { + {"B8G8R8A8", VDP_RGBA_FORMAT_B8G8R8A8, 0}, + {"R8G8B8A8", VDP_RGBA_FORMAT_R8G8B8A8, 0}, + {"R10G10B10A2", VDP_RGBA_FORMAT_R10G10B10A2, 0}, + {"B10G10R10A2", VDP_RGBA_FORMAT_B10G10R10A2, 0}, + {"A8", VDP_RGBA_FORMAT_A8, 0}, +}; +static const size_t rgb_type_count = sizeof(rgb_types)/sizeof(Desc); + +/* + * queryVideoSurface() - Query Video surface limits. + * + */ + +static int queryVideoSurface(CtkVDPAU *ctk_vdpau, VdpDevice device, + VdpGetProcAddress *getProcAddress) +{ + static const Desc chroma_types[] = { + {"420", VDP_CHROMA_TYPE_420, 0}, + {"422", VDP_CHROMA_TYPE_422, 0}, + {"444", VDP_CHROMA_TYPE_444, 0}, + }; + + const size_t chroma_type_count = sizeof(chroma_types)/sizeof(Desc); + + VdpStatus ret; + int x; + GtkWidget *vbox, *hbox; + GtkWidget *table; + GtkWidget *label, *hseparator, *scrollWin; + GtkWidget *eventbox, *event; + GString *str1 = g_string_new(""); + int count = 0; + + if ((VDPAUDeviceFunctions.VideoSurfaceQueryGetPutBitsYCbCrCapabilities == + NULL) || + (VDPAUDeviceFunctions.VideoSurfaceQueryGetPutBitsYCbCrCapabilities == + NULL)) { + return -1; + } + + /* Add Video surface limits */ + + vbox = gtk_vbox_new(FALSE, 0); + hbox = gtk_hbox_new(FALSE, 0); + label = gtk_label_new("Video Surface:"); + eventbox = gtk_event_box_new(); + gtk_widget_modify_fg(eventbox, GTK_STATE_NORMAL, + &(eventbox->style->text[GTK_STATE_NORMAL])); + gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL, + &(eventbox->style->base[GTK_STATE_NORMAL])); + event = gtk_event_box_new(); + gtk_container_add(GTK_CONTAINER(event), label); + gtk_widget_modify_fg(event, GTK_STATE_NORMAL, + &(event->style->text[GTK_STATE_NORMAL])); + gtk_widget_modify_bg(event, GTK_STATE_NORMAL, + &(event->style->base[GTK_STATE_NORMAL])); + ctk_config_set_tooltip(ctk_vdpau->ctk_config, event, + __video_surface_help); + hseparator = gtk_hseparator_new(); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), event, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), hseparator, TRUE, TRUE, 5); + + scrollWin = gtk_scrolled_window_new(NULL, NULL); + hbox = gtk_hbox_new(FALSE, 0); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollWin), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + gtk_container_add(GTK_CONTAINER(eventbox), hbox); + gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrollWin), + eventbox); + gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 5); + + /* Add tab to notebook */ + gtk_notebook_append_page(GTK_NOTEBOOK(ctk_vdpau->notebook), scrollWin, + gtk_label_new("Surface Limits")); + + ctk_vdpau->surfaceVbox = vbox; + + /* Generate a new table */ + + table = gtk_table_new(1, 4, FALSE); + gtk_table_set_row_spacings(GTK_TABLE(table), 3); + gtk_table_set_col_spacings(GTK_TABLE(table), 15); + gtk_container_set_border_width(GTK_CONTAINER(table), 5); + + label = gtk_label_new("Name"); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + label = gtk_label_new("Width"); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 1, 2, 0, 1, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + label = gtk_label_new("Height"); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 2, 3, 0, 1, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + label = gtk_label_new("Types"); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 3, 4, 0, 1, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + /* fill data to the table */ + + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), table, FALSE, FALSE, 0); + + for (x = 0; x < chroma_type_count; x++) { + VdpBool is_supported = FALSE; + uint32_t max_width, max_height; + + ret = VDPAUDeviceFunctions.VideoSurfaceQueryCapabilities(device, + chroma_types[x].id, + &is_supported, + &max_width, &max_height); + if (ret == VDP_STATUS_OK && is_supported) { + int y; + gchar *str = NULL; + + + gtk_table_resize(GTK_TABLE(table), count+2, 4); + str = g_strdup_printf("%s", chroma_types[x].name); + label = gtk_label_new(str); + g_free(str); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, count+1, count+2, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + str = g_strdup_printf("%i", max_width); + label = gtk_label_new(str); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + g_free(str); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 1, 2, count+1, count+2, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + str = g_strdup_printf("%i", max_height); + label = gtk_label_new(str); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + g_free(str); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 2, 3, count+1, count+2, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + + /* Find out supported formats */ + str1 = g_string_erase (str1, 0, -1); + for (y = 0; y < ycbcr_type_count; y++) { + is_supported = FALSE; + + ret = + VDPAUDeviceFunctions.VideoSurfaceQueryGetPutBitsYCbCrCapabilities + (device, chroma_types[x].id, ycbcr_types[y].id, + &is_supported); + if (ret == VDP_STATUS_OK && is_supported) { + const gchar* s = g_strdup_printf("%s ", + ycbcr_types[y].name); + str1 = g_string_append(str1, s); + } + } + label = gtk_label_new(g_strdup_printf(str1->str)); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 3, 4, count+1, count+2, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + } + count++; + } + g_string_free(str1, TRUE); + + queryOutputSurface(ctk_vdpau, device, getProcAddress); + queryBitmapSurface(ctk_vdpau, device, getProcAddress); + + return 0; +} /* queryVideoSurface() */ + + + +/******************* Decoder ****************/ + +/* + * queryDecoderCaps() - Query decoder capabilities. + */ + +static int queryDecoderCaps(CtkVDPAU *ctk_vdpau, VdpDevice device, + VdpGetProcAddress *getProcAddress) +{ + static const Desc decoder_profiles[] = { + {"MPEG1", VDP_DECODER_PROFILE_MPEG1, 0}, + {"MPEG2 Simple", VDP_DECODER_PROFILE_MPEG2_SIMPLE, 0}, + {"MPEG2 Main", VDP_DECODER_PROFILE_MPEG2_MAIN, 0}, + {"H264 Baseline", VDP_DECODER_PROFILE_H264_BASELINE, 0}, + {"H264 Main", VDP_DECODER_PROFILE_H264_MAIN, 0}, + {"H264 High", VDP_DECODER_PROFILE_H264_HIGH, 0}, + {"VC1 Simple", VDP_DECODER_PROFILE_VC1_SIMPLE, 0}, + {"VC1 Main", VDP_DECODER_PROFILE_VC1_MAIN, 0}, + {"VC1 Advanced", VDP_DECODER_PROFILE_VC1_ADVANCED, 0}, + {"MPEG4 part 2 simple profile", + VDP_DECODER_PROFILE_MPEG4_PART2_SP, 0}, + {"MPEG4 part 2 advanced simple profile", + VDP_DECODER_PROFILE_MPEG4_PART2_ASP, 0}, + {"DIVX4 QMobile", VDP_DECODER_PROFILE_DIVX4_QMOBILE, 0}, + {"DIVX4 Mobile", VDP_DECODER_PROFILE_DIVX4_MOBILE, 0}, + {"DIVX4 Home Theater", VDP_DECODER_PROFILE_DIVX4_HOME_THEATER, 0}, + {"DIVX4 HD 1080P", VDP_DECODER_PROFILE_DIVX4_HD_1080P, 0}, + {"DIVX5 QMobile", VDP_DECODER_PROFILE_DIVX5_QMOBILE, 0}, + {"DIVX5 Mobile", VDP_DECODER_PROFILE_DIVX5_MOBILE, 0}, + {"DIVX5 Home Theater", VDP_DECODER_PROFILE_DIVX5_HOME_THEATER, 0}, + {"DIVX5 HD 1080P", VDP_DECODER_PROFILE_DIVX5_HD_1080P, 0}, + }; + const size_t decoder_profile_count = sizeof(decoder_profiles)/sizeof(Desc); + + VdpStatus ret; + int x, count = 0; + GtkWidget *vbox, *hbox; + GtkWidget *table; + GtkWidget *label, *hseparator; + GtkWidget *eventbox; + + if (VDPAUDeviceFunctions.DecoderQueryCapabilities == NULL) { + return -1; + } + + /* Add Decoder capabilities */ + + vbox = gtk_vbox_new(FALSE, 0); + eventbox = gtk_event_box_new(); + gtk_container_add(GTK_CONTAINER(eventbox), vbox); + gtk_widget_modify_fg(eventbox, GTK_STATE_NORMAL, + &(eventbox->style->text[GTK_STATE_NORMAL])); + gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL, + &(eventbox->style->base[GTK_STATE_NORMAL])); + + /* Add tab to notebook */ + + gtk_notebook_append_page(GTK_NOTEBOOK(ctk_vdpau->notebook), eventbox, + gtk_label_new("Decoder Limits")); + + /* Generate a new table */ + + table = gtk_table_new(2, 5, FALSE); + gtk_table_set_row_spacings(GTK_TABLE(table), 3); + gtk_table_set_col_spacings(GTK_TABLE(table), 15); + gtk_container_set_border_width(GTK_CONTAINER(table), 5); + + gtk_widget_modify_fg(table, GTK_STATE_NORMAL, + &(table->style->text[GTK_STATE_NORMAL])); + gtk_widget_modify_bg(table, GTK_STATE_NORMAL, + &(table->style->base[GTK_STATE_NORMAL])); + + label = gtk_label_new("Name"); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + label = gtk_label_new("Level"); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 1, 2, 0, 1, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + label = gtk_label_new("Macroblocks"); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 2, 3, 0, 1, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + label = gtk_label_new("Width"); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 3, 4, 0, 1, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + label = gtk_label_new("Height"); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 4, 5, 0, 1, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + /* separator between heading and data */ + + hseparator = gtk_hseparator_new(); + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), hseparator, TRUE, TRUE, 0); + gtk_table_attach(GTK_TABLE(table), hbox, 0, 5, 1, 2, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), table, FALSE, FALSE, 0); + + /* Enter the data values */ + + for (x = 0; x < decoder_profile_count; x++) { + VdpBool is_supported = FALSE; + uint32_t max_level, max_macroblocks, max_width, max_height; + + ret = VDPAUDeviceFunctions.DecoderQueryCapabilities(device, + decoder_profiles[x].id, + &is_supported, &max_level, + &max_macroblocks, + &max_width, &max_height); + if (ret == VDP_STATUS_OK && is_supported) { + gchar *str = NULL; + + gtk_table_resize(GTK_TABLE(table), count+4, 5); + str = g_strdup_printf("%s", decoder_profiles[x].name); + label = gtk_label_new(str); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + g_free(str); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, count+3, count+4, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + str = g_strdup_printf("%i", max_level); + label = gtk_label_new(str); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + g_free(str); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 1, 2, count+3, count+4, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + str = g_strdup_printf("%i", max_macroblocks); + label = gtk_label_new(str); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + g_free(str); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 2, 3, count+3, count+4, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + str = g_strdup_printf("%i", max_width); + label = gtk_label_new(str); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + g_free(str); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 3, 4, count+3, count+4, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + str = g_strdup_printf("%i", max_height); + label = gtk_label_new(str); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + g_free(str); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 4, 5, count+3, count+4, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + } + count++; + } + return 0; +} /* queryDecoderCaps() */ + + + +/* + * queryOutputSurface() - Query Output surface information + */ + +static int queryOutputSurface(CtkVDPAU *ctk_vdpau, VdpDevice device, + VdpGetProcAddress *getProcAddress) +{ + VdpStatus ret; + int x, y, count = 0; + GString *str1 = g_string_new(""); + GtkWidget *vbox, *hbox; + GtkWidget *table; + GtkWidget *label, *hseparator; + GtkWidget *eventbox; + + if ((VDPAUDeviceFunctions.OutputSurfaceQueryCapabilities == NULL) || + (VDPAUDeviceFunctions.OutputSurfaceQueryGetPutBitsNativeCapabilities + == NULL)) { + return -1; + } + + /* Add Output surface information */ + + vbox = ctk_vdpau->surfaceVbox; + hbox = gtk_hbox_new(FALSE, 0); + label = gtk_label_new("Output Surface:"); + eventbox = gtk_event_box_new(); + gtk_container_add(GTK_CONTAINER(eventbox), label); + gtk_widget_modify_fg(eventbox, GTK_STATE_NORMAL, + &(eventbox->style->text[GTK_STATE_NORMAL])); + gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL, + &(eventbox->style->base[GTK_STATE_NORMAL])); + ctk_config_set_tooltip(ctk_vdpau->ctk_config, eventbox, + __ouput_surface_help); + hseparator = gtk_hseparator_new(); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), eventbox, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), hseparator, TRUE, TRUE, 5); + + /* Generate a new table */ + + table = gtk_table_new(1, 5, FALSE); + gtk_table_set_row_spacings(GTK_TABLE(table), 3); + gtk_table_set_col_spacings(GTK_TABLE(table), 15); + gtk_container_set_border_width(GTK_CONTAINER(table), 5); + + label = gtk_label_new("Name"); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + label = gtk_label_new("Width"); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 1, 2, 0, 1, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + label = gtk_label_new("Height"); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 2, 3, 0, 1, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + label = gtk_label_new("Native"); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 3, 4, 0, 1, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + label = gtk_label_new("Types"); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 4, 5, 0, 1, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), table, FALSE, FALSE, 0); + + /* fill output surface data */ + + for (x = 0; x < rgb_type_count; x++) { + VdpBool is_supported, native=FALSE; + uint32_t max_width, max_height; + + ret = VDPAUDeviceFunctions.OutputSurfaceQueryCapabilities(device, + rgb_types[x].id, + &is_supported, &max_width, + &max_height); + VDPAUDeviceFunctions.OutputSurfaceQueryGetPutBitsNativeCapabilities + (device, rgb_types[x].id, &native); + if (ret == VDP_STATUS_OK && is_supported) { + gchar *str = NULL; + + gtk_table_resize(GTK_TABLE(table), count+2, 5); + str = g_strdup_printf("%s", rgb_types[x].name); + label = gtk_label_new(str); + g_free(str); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, count+1, count+2, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + str = g_strdup_printf("%i", max_width); + label = gtk_label_new(str); + g_free(str); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 1, 2, count+1, count+2, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + str = g_strdup_printf("%i", max_height); + label = gtk_label_new(str); + g_free(str); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 2, 3, count+1, count+2, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + str = g_strdup_printf("%c", native?'y':'-'); + label = gtk_label_new(str); + g_free(str); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 3, 4, count+1, count+2, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + str1 = g_string_erase (str1, 0, -1); + + /* Find out supported formats */ + + for (y = 0; y < ycbcr_type_count; y++) { + is_supported = FALSE; + + ret = + VDPAUDeviceFunctions.OutputSurfaceQueryPutBitsYCbCrCapabilities + (device, rgb_types[x].id, ycbcr_types[y].id, + &is_supported); + if (ret == VDP_STATUS_OK && is_supported) { + gchar* s = g_strdup_printf("%s ", ycbcr_types[y].name); + str1 = g_string_append(str1, s); + } + } + label = gtk_label_new(g_strdup_printf(str1->str)); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 4, 5, count+1, count+2, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + } + count++; + } + return 0; +} /* queryOutputSurface() */ + + + +/* + * queryBitmapSurface() - Query Bitmap surface limits + */ + +static int queryBitmapSurface(CtkVDPAU *ctk_vdpau, VdpDevice device, + VdpGetProcAddress *getProcAddress) +{ + VdpStatus ret; + int x, count = 0; + GtkWidget *vbox, *hbox; + GtkWidget *table; + GtkWidget *label, *hseparator; + GtkWidget *eventbox; + + if (VDPAUDeviceFunctions.BitmapSurfaceQueryCapabilities == NULL) { + return -1; + } + + /* Add Bitmap surface information */ + + vbox = ctk_vdpau->surfaceVbox; + hbox = gtk_hbox_new(FALSE, 0); + label = gtk_label_new("Bitmap Surface:"); + eventbox = gtk_event_box_new(); + gtk_container_add(GTK_CONTAINER(eventbox), label); + gtk_widget_modify_fg(eventbox, GTK_STATE_NORMAL, + &(eventbox->style->text[GTK_STATE_NORMAL])); + gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL, + &(eventbox->style->base[GTK_STATE_NORMAL])); + ctk_config_set_tooltip(ctk_vdpau->ctk_config, eventbox, + __bitmap_surface_help); + hseparator = gtk_hseparator_new(); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), eventbox, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), hseparator, TRUE, TRUE, 5); + + /* Generate a new table */ + + table = gtk_table_new(1, 5, FALSE); + gtk_table_set_row_spacings(GTK_TABLE(table), 3); + gtk_table_set_col_spacings(GTK_TABLE(table), 15); + gtk_container_set_border_width(GTK_CONTAINER(table), 5); + + label = gtk_label_new("Name"); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + label = gtk_label_new("Width"); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 1, 2, 0, 1, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + label = gtk_label_new("Height"); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 2, 3, 0, 1, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), table, FALSE, FALSE, 0); + + /* fill the Bitmap surface data */ + + for (x = 0; x < rgb_type_count; x++) { + VdpBool is_supported; + uint32_t max_width, max_height; + + ret = VDPAUDeviceFunctions.BitmapSurfaceQueryCapabilities(device, + rgb_types[x].id, + &is_supported, + &max_width, + &max_height); + if (ret == VDP_STATUS_OK && is_supported) { + gchar *str = NULL; + + gtk_table_resize(GTK_TABLE(table), count+2, 5); + str = g_strdup_printf("%s", rgb_types[x].name); + label = gtk_label_new(str); + g_free(str); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, count+1, count+2, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + str = g_strdup_printf("%i", max_width); + label = gtk_label_new(str); + g_free(str); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 1, 2, count+1, count+2, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + str = g_strdup_printf("%i", max_height); + label = gtk_label_new(str); + g_free(str); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 2, 3, count+1, count+2, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + } + count++; + } + return 0; +} /* queryBitmapSurface() */ + + + +/******************* Video mixer ****************/ + +/* Type for value ranges */ +enum DataType +{ + DT_NONE, + DT_INT, + DT_UINT, + DT_FLOAT +}; + +/* + * display_range() - Print the range + */ + +static void display_range(GtkTable *table, gint x, uint32_t aux, + uint32_t minval, uint32_t maxval) +{ + gchar *str1 = NULL, *str2 = NULL; + GtkWidget *label; + + switch(aux) { + case DT_INT: + { + str1 = g_strdup_printf("%i", minval); + str2 = g_strdup_printf("%i", maxval); + break; + } + case DT_UINT: + { + str1 = g_strdup_printf("%u", minval); + str2 = g_strdup_printf("%u", maxval); + break; + } + case DT_FLOAT: + { + str1 = g_strdup_printf("%.2f",*((float*)&minval)); + str2 = g_strdup_printf("%.2f", *((float*)&maxval)); + break; + } + default: /* Ignore value which we don't know how to display */; + } + label = gtk_label_new(str1); + g_free(str1); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 2, 3, x+3, x+4, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + label = gtk_label_new(str2); + g_free(str2); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 3, 4, x+3, x+4, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); +} /* display_range() */ + + + +/* + * queryVideoMixer() - Query Video mixer information + */ + +static int queryVideoMixer(CtkVDPAU *ctk_vdpau, VdpDevice device, + VdpGetProcAddress *getProcAddress) +{ + static const Desc mixer_features[] = { + {"DEINTERLACE_TEMPORAL", + VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL, 0}, + {"DEINTERLACE_TEMPORAL_SPATIAL", + VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL, 0}, + {"INVERSE_TELECINE", + VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE, 0}, + {"NOISE_REDUCTION", + VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION, 0}, + {"SHARPNESS", + VDP_VIDEO_MIXER_FEATURE_SHARPNESS, 0}, + {"LUMA_KEY", + VDP_VIDEO_MIXER_FEATURE_LUMA_KEY, 0}, + {"HIGH QUALITY SCALING - L1", + VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1, 0}, + {"HIGH QUALITY SCALING - L2", + VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2, 0}, + {"HIGH QUALITY SCALING - L3", + VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3, 0}, + {"HIGH QUALITY SCALING - L4", + VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4, 0}, + {"HIGH QUALITY SCALING - L5", + VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5, 0}, + {"HIGH QUALITY SCALING - L6", + VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6, 0}, + {"HIGH QUALITY SCALING - L7", + VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7, 0}, + {"HIGH QUALITY SCALING - L8", + VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8, 0}, + {"HIGH QUALITY SCALING - L9", + VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9, 0}, + }; + static const size_t mixer_features_count = + sizeof(mixer_features)/sizeof(Desc); + + static const Desc mixer_parameters[] = { + {"VIDEO_SURFACE_WIDTH", + VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_WIDTH,DT_UINT}, + {"VIDEO_SURFACE_HEIGHT", + VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_HEIGHT,DT_UINT}, + {"CHROMA_TYPE",VDP_VIDEO_MIXER_PARAMETER_CHROMA_TYPE,DT_NONE}, + {"LAYERS",VDP_VIDEO_MIXER_PARAMETER_LAYERS,DT_UINT}, + }; + static const size_t mixer_parameters_count = + sizeof(mixer_parameters)/sizeof(Desc); + + static const Desc mixer_attributes[] = { + {"BACKGROUND_COLOR", + VDP_VIDEO_MIXER_ATTRIBUTE_BACKGROUND_COLOR,DT_NONE}, + {"CSC_MATRIX", + VDP_VIDEO_MIXER_ATTRIBUTE_CSC_MATRIX,DT_NONE}, + {"NOISE_REDUCTION_LEVEL", + VDP_VIDEO_MIXER_ATTRIBUTE_NOISE_REDUCTION_LEVEL,DT_FLOAT}, + {"SHARPNESS_LEVEL", + VDP_VIDEO_MIXER_ATTRIBUTE_SHARPNESS_LEVEL,DT_FLOAT}, + {"LUMA_KEY_MIN_LUMA", + VDP_VIDEO_MIXER_ATTRIBUTE_LUMA_KEY_MIN_LUMA,DT_NONE}, + {"LUMA_KEY_MAX_LUMA", + VDP_VIDEO_MIXER_ATTRIBUTE_LUMA_KEY_MAX_LUMA,DT_NONE}, + }; + static const size_t mixer_attributes_count = + sizeof(mixer_attributes)/sizeof(Desc); + + VdpStatus ret; + int x, count = 0; + GtkWidget *vbox, *hbox; + GtkWidget *table; + GtkWidget *label, *hseparator; + GtkWidget *eventbox; + GtkWidget *scrollWin, *event; + + if (VDPAUDeviceFunctions.VideoMixerQueryFeatureSupport == NULL) { + return -1; + } + + /* Add Video mixer information */ + + vbox = gtk_vbox_new(FALSE, 0); + label = gtk_label_new("Video Mixer:"); + eventbox = gtk_event_box_new(); + gtk_container_add(GTK_CONTAINER(eventbox), label); + gtk_widget_modify_fg(eventbox, GTK_STATE_NORMAL, + &(eventbox->style->text[GTK_STATE_NORMAL])); + gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL, + &(eventbox->style->base[GTK_STATE_NORMAL])); + ctk_config_set_tooltip(ctk_vdpau->ctk_config, eventbox, + __video_mixer_help); + + scrollWin = gtk_scrolled_window_new(NULL, NULL); + hbox = gtk_hbox_new(FALSE, 0); + event = gtk_event_box_new(); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollWin), + GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); + gtk_widget_modify_fg(event, GTK_STATE_NORMAL, + &(event->style->text[GTK_STATE_NORMAL])); + gtk_widget_modify_bg(event, GTK_STATE_NORMAL, + &(event->style->base[GTK_STATE_NORMAL])); + gtk_container_add(GTK_CONTAINER(event), hbox); + gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrollWin), + event); + gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 5); + gtk_widget_set_size_request(scrollWin, -1, 50); + + /* Add tab to notebook */ + + gtk_notebook_append_page(GTK_NOTEBOOK(ctk_vdpau->notebook), scrollWin, + gtk_label_new("Video Mixer")); + + /* Generate a new table */ + + table = gtk_table_new(2, 5, FALSE); + gtk_table_set_row_spacings(GTK_TABLE(table), 3); + gtk_table_set_col_spacings(GTK_TABLE(table), 15); + gtk_container_set_border_width(GTK_CONTAINER(table), 5); + gtk_widget_modify_fg(table, GTK_STATE_NORMAL, + &(table->style->text[GTK_STATE_NORMAL])); + gtk_widget_modify_bg(table, GTK_STATE_NORMAL, + &(table->style->base[GTK_STATE_NORMAL])); + + label = gtk_label_new("Feature Name"); + eventbox = gtk_event_box_new(); + gtk_container_add(GTK_CONTAINER(eventbox), label); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), eventbox, 0, 1, 0, 1, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + gtk_widget_modify_fg(eventbox, GTK_STATE_NORMAL, + &(eventbox->style->text[GTK_STATE_NORMAL])); + gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL, + &(eventbox->style->base[GTK_STATE_NORMAL])); + ctk_config_set_tooltip(ctk_vdpau->ctk_config, eventbox, + __video_mixer_feature_help); + + label = gtk_label_new("Supported"); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 1, 2, 0, 1, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + ctk_config_set_tooltip(ctk_vdpau->ctk_config, eventbox, + __video_mixer_attribute_help); + + /* separator between heading and data */ + + hseparator = gtk_hseparator_new(); + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), hseparator, TRUE, TRUE, 0); + gtk_table_attach(GTK_TABLE(table), hbox, 0, 5, 1, 2, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), table, FALSE, FALSE, 0); + + /* fill Mixer feature data */ + + for (x = 0; x < mixer_features_count; x++) { + gchar *str = NULL; + /* There seems to be a bug in VideoMixerQueryFeatureSupport, + * is_supported is only set if the feature is not supported + */ + VdpBool is_supported = TRUE; + ret = VDPAUDeviceFunctions.VideoMixerQueryFeatureSupport + (device, mixer_features[x].id, &is_supported); + is_supported = (ret == VDP_STATUS_OK && is_supported); + + gtk_table_resize(GTK_TABLE(table), count+4, 5); + str = g_strdup_printf("%s", mixer_features[x].name); + label = gtk_label_new(str); + g_free(str); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, count+3, count+4, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + str = g_strdup_printf("%c", is_supported?'y':'-'); + label = gtk_label_new(str); + g_free(str); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 1, 2, count+3, count+4, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + count++; + + } + + if (VDPAUDeviceFunctions.VideoMixerQueryParameterSupport == NULL) { + return -1; + } + + /* Generate a new table */ + + count = 0; + table = gtk_table_new(2, 5, FALSE); + gtk_table_set_row_spacings(GTK_TABLE(table), 3); + gtk_table_set_col_spacings(GTK_TABLE(table), 15); + gtk_container_set_border_width(GTK_CONTAINER(table), 5); + + label = gtk_label_new("Parameter Name"); + eventbox = gtk_event_box_new(); + gtk_container_add(GTK_CONTAINER(eventbox), label); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), eventbox, 0, 1, 0, 1, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + gtk_widget_modify_fg(eventbox, GTK_STATE_NORMAL, + &(eventbox->style->text[GTK_STATE_NORMAL])); + gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL, + &(eventbox->style->base[GTK_STATE_NORMAL])); + ctk_config_set_tooltip(ctk_vdpau->ctk_config, eventbox, + __video_mixer_parameter_help); + + label = gtk_label_new("Supported"); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 1, 2, 0, 1, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + label = gtk_label_new("Min"); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 2, 3, 0, 1, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + label = gtk_label_new("Max"); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 3, 4, 0, 1, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + /* separator between heading and data */ + + hseparator = gtk_hseparator_new(); + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), hseparator, TRUE, TRUE, 0); + gtk_table_attach(GTK_TABLE(table), hbox, 0, 5, 1, 2, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), table, FALSE, FALSE, 0); + + /* fill the Mixer parameter data */ + + for (x = 0; x < mixer_parameters_count; x++) { + uint32_t minval, maxval; + VdpBool is_supported = FALSE; + gchar *str = NULL; + + ret = VDPAUDeviceFunctions.VideoMixerQueryParameterSupport + (device, mixer_parameters[x].id, &is_supported); + is_supported = (ret == VDP_STATUS_OK && is_supported); + + gtk_table_resize(GTK_TABLE(table), count+4, 5); + str = g_strdup_printf("%s", mixer_parameters[x].name); + label = gtk_label_new(str); + g_free(str); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, count+3, count+4, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + str = g_strdup_printf("%c", is_supported?'y':'-'); + label = gtk_label_new(str); + g_free(str); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 1, 2, count+3, count+4, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + count++; + if (is_supported && mixer_parameters[x].aux != DT_NONE) { + ret = VDPAUDeviceFunctions.VideoMixerQueryParameterValueRange + (device, mixer_parameters[x].id, (void*)&minval, + (void*)&maxval); + display_range(GTK_TABLE(table), count-1, + mixer_parameters[x].aux, + minval, maxval); + } + } + + if (VDPAUDeviceFunctions.VideoMixerQueryAttributeSupport == NULL) { + return -1; + } + + /* Generate a new table */ + + count = 0; + table = gtk_table_new(2, 5, FALSE); + gtk_table_set_row_spacings(GTK_TABLE(table), 3); + gtk_table_set_col_spacings(GTK_TABLE(table), 15); + gtk_container_set_border_width(GTK_CONTAINER(table), 5); + + label = gtk_label_new("Attribute Name"); + eventbox = gtk_event_box_new(); + gtk_container_add(GTK_CONTAINER(eventbox), label); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), eventbox, 0, 1, 0, 1, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + gtk_widget_modify_fg(eventbox, GTK_STATE_NORMAL, + &(eventbox->style->text[GTK_STATE_NORMAL])); + gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL, + &(eventbox->style->base[GTK_STATE_NORMAL])); + ctk_config_set_tooltip(ctk_vdpau->ctk_config, eventbox, + __video_mixer_attribute_help); + + label = gtk_label_new("Supported"); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 1, 2, 0, 1, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + label = gtk_label_new("Min"); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 2, 3, 0, 1, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + label = gtk_label_new("Max"); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 3, 4, 0, 1, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + /* separator between heading and data */ + + hseparator = gtk_hseparator_new(); + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), hseparator, TRUE, TRUE, 0); + gtk_table_attach(GTK_TABLE(table), hbox, 0, 5, 1, 2, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), table, FALSE, FALSE, 0); + + /* fill the Attributes data */ + + for (x = 0; x < mixer_attributes_count; x++) { + VdpBool is_supported = FALSE; + gchar *str = NULL; + uint32_t minval, maxval; + + ret = VDPAUDeviceFunctions.VideoMixerQueryAttributeSupport + (device, mixer_attributes[x].id, &is_supported); + is_supported = (ret == VDP_STATUS_OK && is_supported); + + gtk_table_resize(GTK_TABLE(table), count+4, 5); + str = g_strdup_printf("%s", mixer_attributes[x].name); + label = gtk_label_new(str); + g_free(str); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, count+3, count+4, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + str = g_strdup_printf("%c", is_supported?'y':'-'); + label = gtk_label_new(str); + g_free(str); + gtk_label_set_selectable(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, 1, 2, count+3, count+4, + GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0); + + count++; + if (is_supported && mixer_attributes[x].aux != DT_NONE) { + ret = VDPAUDeviceFunctions.VideoMixerQueryAttributeValueRange + (device, mixer_attributes[x].id, + (void*)&minval, (void*)&maxval); + display_range(GTK_TABLE(table), count-1, mixer_attributes[x].aux, + minval, maxval); + } + } + return 0; +} /* queryVideoMixer() */ + + + +GType ctk_vdpau_get_type(void) +{ + static GType ctk_vdpau_type = 0; + + if (!ctk_vdpau_type) { + static const GTypeInfo ctk_vdpau_info = { + sizeof (CtkVDPAUClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + NULL, /* constructor */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (CtkVDPAU), + 0, /* n_preallocs */ + NULL, /* instance_init */ + NULL /* value_table */ + }; + + ctk_vdpau_type = + g_type_register_static(GTK_TYPE_VBOX, "CtkVDPAU", + &ctk_vdpau_info, 0); + } + + return ctk_vdpau_type; + +} /* ctk_vdpau_get_type() */ + + + +GtkWidget* ctk_vdpau_new(NvCtrlAttributeHandle *handle, + CtkConfig *ctk_config, + CtkEvent *ctk_event) +{ + GObject *object; + CtkVDPAU *ctk_vdpau; + GtkWidget *banner; + GtkWidget *hbox; + GtkWidget *vbox; + GtkWidget *vbox3; + GtkWidget *notebook; + GtkWidget *scrollWin; + GtkWidget *event; /* For setting the background color to white */ + + void *vdpau_handle = NULL; + VdpDevice device; + VdpGetProcAddress *getProcAddress = NULL; + VdpStatus ret; + VdpDeviceCreateX11 *VDPAUDeviceCreateX11 = NULL; + + /* make sure we have a handle */ + + g_return_val_if_fail(handle != NULL, NULL); + + /* Create the ctk vdpau object */ + object = g_object_new(CTK_TYPE_VDPAU, NULL); + ctk_vdpau = CTK_VDPAU(object); + + + /* Cache the attribute handle */ + ctk_vdpau->handle = handle; + + /* Set container properties of the object */ + ctk_vdpau->ctk_config = ctk_config; + gtk_box_set_spacing(GTK_BOX(ctk_vdpau), 10); + + /* Image banner */ + banner = ctk_banner_image_new(BANNER_ARTWORK_VDPAU); + gtk_box_pack_start(GTK_BOX(ctk_vdpau), banner, FALSE, FALSE, 0); + + /* open VDPAU library */ + vdpau_handle = dlopen("libvdpau.so", RTLD_NOW); + if (!vdpau_handle) { + goto fail; + } + + VDPAUDeviceCreateX11 = dlsym(vdpau_handle, "vdp_device_create_x11"); + if (!VDPAUDeviceCreateX11) { + goto fail; + } + + + /* get device and ProcAddress */ + ret = VDPAUDeviceCreateX11(NvCtrlGetDisplayPtr(handle), + NvCtrlGetScreen(handle), + &device, &getProcAddress); + + if ((ret != VDP_STATUS_OK) || !device || !getProcAddress) { + goto fail; + } + + getAddressVDPAUDeviceFunctions(device, getProcAddress); + + /* Return early if any function is NULL */ + if (VDPAUDeviceFunctions.GetErrorString == NULL && + VDPAUDeviceFunctions.GetProcAddress == NULL && + VDPAUDeviceFunctions.GetApiVersion == NULL && + VDPAUDeviceFunctions.GetInformationString == NULL && + VDPAUDeviceFunctions.VideoSurfaceQueryCapabilities == NULL && + VDPAUDeviceFunctions.VideoSurfaceQueryGetPutBitsYCbCrCapabilities == + NULL && + VDPAUDeviceFunctions.OutputSurfaceQueryCapabilities == NULL && + VDPAUDeviceFunctions.OutputSurfaceQueryGetPutBitsNativeCapabilities == + NULL && + VDPAUDeviceFunctions.OutputSurfaceQueryPutBitsYCbCrCapabilities == + NULL && + VDPAUDeviceFunctions.BitmapSurfaceQueryCapabilities == NULL && + VDPAUDeviceFunctions.DecoderQueryCapabilities == NULL && + VDPAUDeviceFunctions.VideoMixerQueryFeatureSupport == NULL && + VDPAUDeviceFunctions.VideoMixerQueryParameterSupport == NULL && + VDPAUDeviceFunctions.VideoMixerQueryAttributeSupport == NULL && + VDPAUDeviceFunctions.VideoMixerQueryParameterValueRange == NULL && + VDPAUDeviceFunctions.VideoMixerQueryAttributeValueRange == NULL) { + goto fail; + } + + /* Information Scroll Box */ + vbox3 = gtk_vbox_new(FALSE, 5); + vbox = gtk_vbox_new(FALSE, 5); + + scrollWin = gtk_scrolled_window_new(NULL, NULL); + hbox = gtk_hbox_new(FALSE, 0); + event = gtk_event_box_new(); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollWin), + GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); + gtk_widget_modify_fg(event, GTK_STATE_NORMAL, + &(event->style->text[GTK_STATE_NORMAL])); + gtk_widget_modify_bg(event, GTK_STATE_NORMAL, + &(event->style->base[GTK_STATE_NORMAL])); + gtk_container_add(GTK_CONTAINER(event), hbox); + gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrollWin), + event); + gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 5); + gtk_widget_set_size_request(scrollWin, -1, 50); + + gtk_box_pack_start(GTK_BOX(vbox3), scrollWin, TRUE, TRUE, 5); + + /* Create tabbed notebook for widget */ + + notebook = gtk_notebook_new(); + gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_TOP); + gtk_box_pack_start(GTK_BOX(ctk_vdpau), notebook, TRUE, TRUE, 0); + + /* Create first tab for device info */ + + ctk_vdpau->notebook = notebook; + + /* Query and print VDPAU information */ + queryBaseInfo(ctk_vdpau, device, getProcAddress); + queryVideoSurface(ctk_vdpau, device, getProcAddress); + queryDecoderCaps(ctk_vdpau, device, getProcAddress); + queryVideoMixer(ctk_vdpau, device, getProcAddress); + + gtk_widget_show_all(GTK_WIDGET(object)); + + /* close the handle */ + if (vdpau_handle) { + dlclose(vdpau_handle); + } + + return GTK_WIDGET(object); + + fail: + if (vdpau_handle) { + dlclose(vdpau_handle); + } + + return NULL; +} + + + +GtkTextBuffer *ctk_vdpau_create_help(GtkTextTagTable *table, + CtkVDPAU *ctk_vdpau) +{ + GtkTextIter i; + GtkTextBuffer *b; + + b = gtk_text_buffer_new(table); + + gtk_text_buffer_get_iter_at_offset(b, &i, 0); + + ctk_help_title(b, &i, "VDPAU Information Help"); + ctk_help_para(b, &i, __vdpau_information_label_help); + + ctk_help_heading(b, &i, "Base Information"); + ctk_help_para(b, &i, __base_information_help); + + ctk_help_heading(b, &i, "API Version"); + ctk_help_para(b, &i, __vdpau_api_version_help); + + ctk_help_heading(b, &i, "Supported Codecs"); + ctk_help_para(b, &i, __supported_codecs_help); + + ctk_help_heading(b, &i, "Surface Limits"); + ctk_help_para(b, &i, __surface_limits_help); + + ctk_help_heading(b, &i, "Video Surface"); + ctk_help_para(b, &i, __video_surface_help); + + ctk_help_heading(b, &i, "Output Surface"); + ctk_help_para(b, &i, __ouput_surface_help); + + ctk_help_heading(b, &i, "Bitmap Surface"); + ctk_help_para(b, &i, __bitmap_surface_help); + + ctk_help_heading(b, &i, "Decoder Limits"); + ctk_help_para(b, &i, __decoder_limits_help); + + ctk_help_heading(b, &i, "Video Mixer"); + ctk_help_para(b, &i, __video_mixer_help); + + ctk_help_term(b, &i, "Feature"); + ctk_help_para(b, &i, __video_mixer_feature_help); + + ctk_help_term(b, &i, "Parameter"); + ctk_help_para(b, &i, __video_mixer_parameter_help); + + ctk_help_term(b, &i, "Attribute"); + ctk_help_para(b, &i, __video_mixer_attribute_help); + + ctk_help_finish(b); + return b; +} diff --git a/src/gtk+-2.x/ctkvdpau.h b/src/gtk+-2.x/ctkvdpau.h new file mode 100644 index 0000000..38d521b --- /dev/null +++ b/src/gtk+-2.x/ctkvdpau.h @@ -0,0 +1,107 @@ +/* + * nvidia-settings: A tool for configuring the NVIDIA X driver on Unix + * and Linux systems. + * + * Copyright (C) 2012 NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses>. + */ + +#ifndef __CTK_VDPAU_H__ +#define __CTK_VDPAU_H__ + +#include "ctkevent.h" +#include "ctkconfig.h" + +#include "vdpau/vdpau.h" +#include "vdpau/vdpau_x11.h" + +G_BEGIN_DECLS + +#define CTK_TYPE_VDPAU (ctk_vdpau_get_type()) + +#define CTK_VDPAU(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), CTK_TYPE_VDPAU, CtkVDPAU)) + +#define CTK_VDPAU_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), CTK_TYPE_VDPAU, CtkVDPAUClass)) + +#define CTK_IS_VDPAU(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CTK_TYPE_VDPAU)) + +#define CTK_IS_VDPAU_CLASS(class) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), CTK_TYPE_VDPAU)) + +#define CTK_VDPAU_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), CTK_TYPE_VDPAU, CtkVDPAUClass)) + + +struct VDPAUDeviceImpl { + + VdpGetErrorString *GetErrorString; + VdpGetProcAddress *GetProcAddress; + VdpGetApiVersion *GetApiVersion; + VdpGetInformationString *GetInformationString; + VdpVideoSurfaceQueryCapabilities *VideoSurfaceQueryCapabilities; + VdpVideoSurfaceQueryGetPutBitsYCbCrCapabilities + *VideoSurfaceQueryGetPutBitsYCbCrCapabilities; + VdpOutputSurfaceQueryCapabilities *OutputSurfaceQueryCapabilities; + VdpOutputSurfaceQueryGetPutBitsNativeCapabilities + *OutputSurfaceQueryGetPutBitsNativeCapabilities; + VdpOutputSurfaceQueryPutBitsYCbCrCapabilities + *OutputSurfaceQueryPutBitsYCbCrCapabilities; + VdpBitmapSurfaceQueryCapabilities *BitmapSurfaceQueryCapabilities; + VdpDecoderQueryCapabilities *DecoderQueryCapabilities; + VdpVideoMixerQueryFeatureSupport *VideoMixerQueryFeatureSupport; + VdpVideoMixerQueryParameterSupport *VideoMixerQueryParameterSupport; + VdpVideoMixerQueryAttributeSupport *VideoMixerQueryAttributeSupport; + VdpVideoMixerQueryParameterValueRange *VideoMixerQueryParameterValueRange; + VdpVideoMixerQueryAttributeValueRange *VideoMixerQueryAttributeValueRange; +} VDPAUDeviceFunctions; + +/* Generic description structure */ +typedef struct +{ + const char *name; + uint32_t id; + uint32_t aux; /* optional extra parameter... */ +} Desc; + +typedef struct _CtkVDPAU CtkVDPAU; +typedef struct _CtkVDPAUClass CtkVDPAUClass; + +struct _CtkVDPAU +{ + GtkVBox parent; + + NvCtrlAttributeHandle *handle; + CtkConfig *ctk_config; + + GtkWidget* notebook; + GtkWidget* surfaceVbox; + GtkWidget* baseInfoVbox; +}; + +struct _CtkVDPAUClass +{ + GtkVBoxClass parent_class; +}; + +GType ctk_vdpau_get_type (void) G_GNUC_CONST; +GtkWidget* ctk_vdpau_new (NvCtrlAttributeHandle *, + CtkConfig *, CtkEvent *); +GtkTextBuffer* ctk_vdpau_create_help (GtkTextTagTable *, CtkVDPAU *); + +G_END_DECLS + +#endif /* __CTK_VDPAU_H__ */ diff --git a/src/gtk+-2.x/ctkwindow.c b/src/gtk+-2.x/ctkwindow.c index 8a6c418..6db77c3 100644 --- a/src/gtk+-2.x/ctkwindow.c +++ b/src/gtk+-2.x/ctkwindow.c @@ -46,7 +46,6 @@ #include "ctkcolorcorrection.h" #include "ctkcolorcorrectionpage.h" #include "ctkxvideo.h" -#include "ctkcursorshadow.h" #include "ctkopengl.h" #include "ctkglx.h" #include "ctkmultisample.h" @@ -62,6 +61,9 @@ #include "ctkdisplayconfig.h" #include "ctkserver.h" #include "ctkecc.h" +#include "ctkvdpau.h" + +#include "ctkappprofile.h" #include "ctkhelp.h" #include "ctkevent.h" @@ -687,10 +689,9 @@ GtkWidget *ctk_window_new(ParsedAttribute *p, ConfigProperties *conf, ctk_window->attribute_list, ctk_event); if (child) { - const char *title = "X Server Color Correction"; - help = ctk_color_correction_page_create_help(tag_table, title); + help = ctk_color_correction_page_create_help(tag_table); add_page(child, help, ctk_window, &iter, NULL, - title, NULL, NULL, NULL); + "X Server Color Correction", NULL, NULL, NULL); } } @@ -703,16 +704,6 @@ GtkWidget *ctk_window_new(ParsedAttribute *p, ConfigProperties *conf, "X Server XVideo Settings", NULL, NULL, NULL); } - /* cursor shadow */ - - child = ctk_cursor_shadow_new(screen_handle, ctk_config, ctk_event); - if (child) { - help = ctk_cursor_shadow_create_help(tag_table, - CTK_CURSOR_SHADOW(child)); - add_page(child, help, ctk_window, &iter, NULL, "Cursor Shadow", - NULL, NULL, NULL); - } - /* opengl settings */ child = ctk_opengl_new(screen_handle, ctk_config, ctk_event); @@ -744,6 +735,14 @@ GtkWidget *ctk_window_new(ParsedAttribute *p, ConfigProperties *conf, } + /* VDPAU Information */ + child = ctk_vdpau_new(screen_handle, ctk_config, ctk_event); + if (child) { + help = ctk_vdpau_create_help(tag_table, CTK_VDPAU(child)); + add_page(child, help, ctk_window, &iter, NULL, "VDPAU Information", + NULL, NULL, NULL); + } + /* gvo (Graphics To Video Out) */ child = ctk_gvo_new(screen_handle, ctk_config, ctk_event); @@ -1035,11 +1034,17 @@ GtkWidget *ctk_window_new(ParsedAttribute *p, ConfigProperties *conf, ctk_3d_vision_pro_select, ctk_3d_vision_pro_unselect); } + /* app profile configuration */ + widget = ctk_app_profile_new(ctk_config); + + add_page(widget, ctk_app_profile_create_help(CTK_APP_PROFILE(widget), tag_table), + ctk_window, NULL, NULL, "Application Profiles", + NULL, NULL, NULL); /* nvidia-settings configuration */ add_page(GTK_WIDGET(ctk_window->ctk_config), - ctk_config_create_help(tag_table), + ctk_config_create_help(ctk_config, tag_table), ctk_window, NULL, NULL, "nvidia-settings Configuration", NULL, NULL, NULL); @@ -1211,15 +1216,19 @@ static void add_page(GtkWidget *widget, GtkTextBuffer *help, if (!child_iter) child_iter = &tmp_child_iter; /* - * add another reference to the widget, so that it doesn't get - * destroyed the next time it gets hidden (removed from the page - * viewer). + * Add a reference to the object and sink (remove) the floating (gtk) + * reference. This sink needs to happen before the page gets packed + * to ensure that we become the propper owner of these widgets. This way, + * page will not be destroyed when they are hidden (removed from the page + * viewer), and we can properly destroy them when needed (for example, the + * display device pages are destroyed/recreated on hotplug events.) */ - - gtk_object_ref(GTK_OBJECT(widget)); - + + g_object_ref(G_OBJECT(widget)); + gtk_object_sink(GTK_OBJECT(widget)); + gtk_tree_store_append(ctk_window->tree_store, child_iter, iter); - + gtk_tree_store_set(ctk_window->tree_store, child_iter, CTK_WINDOW_LABEL_COLUMN, label, -1); gtk_tree_store_set(ctk_window->tree_store, child_iter, diff --git a/src/image_data/HOWTO-ADD-IMAGES b/src/image_data/HOWTO-ADD-IMAGES index 14cb788..14331a7 100644 --- a/src/image_data/HOWTO-ADD-IMAGES +++ b/src/image_data/HOWTO-ADD-IMAGES @@ -1,4 +1,7 @@ +If you are adding a new banner image for an nvidia-settings page, the +image should be <= 60 pixels high (or <= 110 pixels high for the tall banner). + If I have a png, how do I build it into nvidia-settings? - make sure you have the gdk-pixbuf-csource binary somewhere @@ -7,7 +10,8 @@ If I have a png, how do I build it into nvidia-settings? - run './png_to_c_header.sh foo.png' This will generate the foo_pixdata.h header file that can then be included in the nvidia-settings source code. - - add a foo_pixdata.h entry to the .../image_data/Makefile.inc + - add a foo_pixdata.h entry to the IMAGE_DATA_EXTRA_DIST variable + in .../src.mk (Also follow these next steps if this image is to be used in the banner) diff --git a/src/image_data/cursor_shadow.png b/src/image_data/cursor_shadow.png Binary files differdeleted file mode 100644 index 0ddf799..0000000 --- a/src/image_data/cursor_shadow.png +++ /dev/null diff --git a/src/image_data/cursor_shadow_pixdata.h b/src/image_data/cursor_shadow_pixdata.h deleted file mode 100644 index 81e099e..0000000 --- a/src/image_data/cursor_shadow_pixdata.h +++ /dev/null @@ -1,1225 +0,0 @@ -/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ - -static guint8 cursor_shadow_pixdata_pixel_data[] = { - 0xa3,0xff,0xff,0xff,0x00,0x01,0xff,0xff,0xff,0xb3,0x9a,0xff,0xff,0xff,0x00, - 0x82,0x00,0x00,0x00,0x00,0x02,0xff,0xff,0xff,0x02,0xff,0xff,0xff,0x03,0x8f, - 0xff,0xff,0xff,0x00,0x05,0xf0,0xf1,0xf1,0x00,0xdc,0xde,0xdd,0x00,0xdf,0xe0, - 0xdf,0x00,0xd7,0xd9,0xd6,0x00,0xd1,0xd3,0xd0,0x00,0x83,0xdb,0xdb,0xdb,0x00, - 0x0f,0xd0,0xd1,0xd0,0x00,0xcf,0xd1,0xce,0x00,0xd4,0xd4,0xd2,0x00,0x9a,0x9a, - 0x98,0x00,0x77,0x77,0x76,0x00,0x6e,0x6e,0x6e,0x00,0x70,0x70,0x6f,0x00,0x77, - 0x77,0x77,0x00,0x7a,0x7a,0x78,0x00,0x8b,0x8b,0x8b,0x00,0x73,0x74,0x72,0x00, - 0x70,0x70,0x6f,0x00,0x74,0x74,0x72,0x00,0x87,0x87,0x84,0x00,0xba,0xb9,0xb4, - 0x00,0x82,0xb8,0xb7,0xb3,0x00,0x04,0xb6,0xb4,0xb0,0x00,0xb3,0xb0,0xaa,0x00, - 0xa5,0xa5,0x9e,0x00,0xa3,0xa1,0x9c,0x00,0x82,0xa5,0xa2,0x9d,0x00,0x8c,0x00, - 0x00,0x00,0x00,0xa3,0xff,0xff,0xff,0x00,0x01,0xff,0xff,0xff,0xb3,0x9a,0xff, - 0xff,0xff,0x00,0x82,0x00,0x00,0x00,0x00,0x03,0xff,0xff,0xff,0x01,0xff,0xff, - 0xff,0x04,0xff,0xff,0xff,0x01,0x8e,0xff,0xff,0xff,0x00,0x82,0xd8,0xda,0xd9, - 0x00,0x03,0xd5,0xd7,0xd5,0x00,0xd6,0xd8,0xd5,0x00,0xd0,0xd2,0xcf,0x00,0x83, - 0xda,0xda,0xda,0x00,0x17,0xcf,0xd0,0xcf,0x00,0xcf,0xd1,0xce,0x00,0xce,0xce, - 0xcc,0x00,0x93,0x93,0x91,0x00,0x6d,0x6d,0x6c,0x00,0x69,0x69,0x69,0x00,0x67, - 0x67,0x66,0x00,0x75,0x75,0x75,0x00,0x71,0x71,0x6f,0x00,0x8b,0x8b,0x8b,0x00, - 0x72,0x72,0x70,0x00,0x69,0x68,0x67,0x00,0x73,0x73,0x71,0x00,0x7f,0x7f,0x7d, - 0x00,0x83,0x83,0x80,0x00,0xb3,0xb2,0xae,0x00,0xad,0xab,0xa8,0x00,0xa9,0xa6, - 0xa3,0x00,0xab,0xa7,0xa4,0x00,0xab,0xa9,0xa4,0x00,0xaf,0xad,0xa8,0x00,0xb9, - 0xb6,0xb2,0x00,0xc0,0xbd,0xb6,0x00,0x8c,0x00,0x00,0x00,0x00,0xa3,0xff,0xff, - 0xff,0x00,0x01,0xff,0xff,0xff,0xb3,0x99,0xff,0xff,0xff,0x00,0x82,0xff,0xff, - 0xff,0x01,0x04,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0xbf,0xbf,0xbf,0x04, - 0xd5,0xd5,0xd5,0x06,0x82,0x00,0x00,0x00,0x01,0x8b,0xfd,0xfd,0xfd,0x00,0x01, - 0xde,0xdf,0xde,0x00,0x82,0xd8,0xda,0xd9,0x00,0x03,0xd5,0xd7,0xd5,0x00,0xd6, - 0xd8,0xd5,0x00,0xd0,0xd2,0xcf,0x00,0x83,0xda,0xda,0xda,0x00,0x17,0xcf,0xd0, - 0xcf,0x00,0xcf,0xd1,0xce,0x00,0xce,0xce,0xcc,0x00,0x93,0x93,0x91,0x00,0x6d, - 0x6d,0x6c,0x00,0x69,0x69,0x69,0x00,0x67,0x67,0x66,0x00,0x75,0x75,0x75,0x00, - 0x71,0x71,0x6f,0x00,0x8b,0x8b,0x8b,0x00,0x72,0x72,0x70,0x00,0x69,0x68,0x67, - 0x00,0x73,0x73,0x71,0x00,0x7f,0x7f,0x7d,0x00,0x83,0x83,0x80,0x00,0xb3,0xb2, - 0xae,0x00,0xad,0xab,0xa8,0x00,0xa9,0xa6,0xa3,0x00,0xab,0xa7,0xa4,0x00,0xab, - 0xa9,0xa4,0x00,0xaf,0xad,0xa8,0x00,0xb9,0xb6,0xb2,0x00,0xc0,0xbd,0xb6,0x00, - 0x8c,0x00,0x00,0x00,0x00,0xa3,0xff,0xff,0xff,0x00,0x01,0xff,0xff,0xff,0xb3, - 0x9a,0xff,0xff,0xff,0x00,0x06,0xff,0xff,0xff,0x02,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x01,0xa8,0xa8,0xa8,0x03,0xe2,0xe2,0xe2,0x0a,0xbe,0xbe,0xbe,0x04, - 0x82,0x00,0x00,0x00,0x01,0x8a,0xfd,0xfd,0xfd,0x00,0x01,0xde,0xdf,0xde,0x00, - 0x82,0xd8,0xda,0xd9,0x00,0x03,0xd5,0xd7,0xd5,0x00,0xd6,0xd8,0xd5,0x00,0xd0, - 0xd2,0xcf,0x00,0x83,0xda,0xda,0xda,0x00,0x17,0xcf,0xd0,0xcf,0x00,0xcf,0xd1, - 0xce,0x00,0xce,0xce,0xcc,0x00,0x93,0x93,0x91,0x00,0x6d,0x6d,0x6c,0x00,0x69, - 0x69,0x69,0x00,0x67,0x67,0x66,0x00,0x75,0x75,0x75,0x00,0x71,0x71,0x6f,0x00, - 0x8b,0x8b,0x8b,0x00,0x72,0x72,0x70,0x00,0x69,0x68,0x67,0x00,0x73,0x73,0x71, - 0x00,0x7f,0x7f,0x7d,0x00,0x83,0x83,0x80,0x00,0xb3,0xb2,0xae,0x00,0xad,0xab, - 0xa8,0x00,0xa9,0xa6,0xa3,0x00,0xab,0xa7,0xa4,0x00,0xab,0xa9,0xa4,0x00,0xaf, - 0xad,0xa8,0x00,0xb9,0xb6,0xb2,0x00,0xc0,0xbd,0xb6,0x00,0x8c,0x00,0x00,0x00, - 0x00,0xa3,0xff,0xff,0xff,0x00,0x01,0xff,0xff,0xff,0xb3,0x97,0xff,0xff,0xff, - 0x00,0x01,0xff,0xff,0xff,0x01,0x82,0xff,0xff,0xff,0x00,0x07,0xff,0xff,0xff, - 0x01,0xff,0xff,0xff,0x02,0x00,0x00,0x00,0x01,0x7e,0x7e,0x7e,0x02,0xe3,0xe3, - 0xe3,0x0d,0xe1,0xe1,0xe1,0x15,0x00,0x00,0x00,0x02,0x82,0x00,0x00,0x00,0x01, - 0x88,0xea,0xe9,0xeb,0x00,0x02,0xde,0xdd,0xdd,0x00,0xde,0xdf,0xde,0x00,0x82, - 0xd8,0xda,0xd9,0x00,0x03,0xd5,0xd7,0xd5,0x00,0xd6,0xd8,0xd5,0x00,0xd0,0xd2, - 0xcf,0x00,0x83,0xda,0xda,0xda,0x00,0x17,0xcf,0xd0,0xcf,0x00,0xcf,0xd1,0xce, - 0x00,0xce,0xce,0xcc,0x00,0x93,0x93,0x91,0x00,0x6d,0x6d,0x6c,0x00,0x69,0x69, - 0x69,0x00,0x67,0x67,0x66,0x00,0x75,0x75,0x75,0x00,0x71,0x71,0x6f,0x00,0x8b, - 0x8b,0x8b,0x00,0x72,0x72,0x70,0x00,0x69,0x68,0x67,0x00,0x73,0x73,0x71,0x00, - 0x7f,0x7f,0x7d,0x00,0x83,0x83,0x80,0x00,0xb3,0xb2,0xae,0x00,0xad,0xab,0xa8, - 0x00,0xa9,0xa6,0xa3,0x00,0xab,0xa7,0xa4,0x00,0xab,0xa9,0xa4,0x00,0xaf,0xad, - 0xa8,0x00,0xb9,0xb6,0xb2,0x00,0xc0,0xbd,0xb6,0x00,0x8c,0x00,0x00,0x00,0x00, - 0x9c,0xff,0xff,0xff,0x00,0x0e,0xff,0xff,0xff,0x2f,0xff,0xff,0xff,0x61,0xff, - 0xff,0xff,0x8e,0xff,0xff,0xff,0xb4,0xff,0xff,0xff,0xd3,0xff,0xff,0xff,0xeb, - 0xff,0xff,0xff,0xfa,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xeb,0xff,0xff,0xff, - 0xd3,0xff,0xff,0xff,0xb4,0xff,0xff,0xff,0x8e,0xff,0xff,0xff,0x61,0xff,0xff, - 0xff,0x2f,0x91,0xff,0xff,0xff,0x00,0x01,0xff,0xff,0xff,0x01,0x82,0xff,0xff, - 0xff,0x00,0x0a,0xfc,0xfc,0xfc,0x01,0xfb,0xfb,0xfb,0x04,0x7e,0x7e,0x7e,0x02, - 0x00,0x00,0x00,0x01,0xc2,0xc2,0xc2,0x10,0xd9,0xd9,0xd9,0x2c,0xc0,0xc0,0xc0, - 0x13,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x01,0x87,0xea, - 0xe9,0xeb,0x00,0x02,0xde,0xdd,0xdd,0x00,0xde,0xdf,0xde,0x00,0x82,0xd8,0xda, - 0xd9,0x00,0x03,0xd5,0xd7,0xd5,0x00,0xd6,0xd8,0xd5,0x00,0xd0,0xd2,0xcf,0x00, - 0x83,0xda,0xda,0xda,0x00,0x17,0xcf,0xd0,0xcf,0x00,0xcf,0xd1,0xce,0x00,0xce, - 0xce,0xcc,0x00,0x93,0x93,0x91,0x00,0x6d,0x6d,0x6c,0x00,0x69,0x69,0x69,0x00, - 0x67,0x67,0x66,0x00,0x75,0x75,0x75,0x00,0x71,0x71,0x6f,0x00,0x8b,0x8b,0x8b, - 0x00,0x72,0x72,0x70,0x00,0x69,0x68,0x67,0x00,0x73,0x73,0x71,0x00,0x7f,0x7f, - 0x7d,0x00,0x83,0x83,0x80,0x00,0xb3,0xb2,0xae,0x00,0xad,0xab,0xa8,0x00,0xa9, - 0xa6,0xa3,0x00,0xab,0xa7,0xa4,0x00,0xab,0xa9,0xa4,0x00,0xaf,0xad,0xa8,0x00, - 0xb9,0xb6,0xb2,0x00,0xc0,0xbd,0xb6,0x00,0x8c,0x00,0x00,0x00,0x00,0x99,0xff, - 0xff,0xff,0x00,0x14,0xff,0xff,0xff,0x2c,0xff,0xff,0xff,0x74,0xff,0xff,0xff, - 0xb7,0xff,0xff,0xff,0xc9,0xff,0xff,0xff,0x9e,0xff,0xff,0xff,0x71,0xff,0xff, - 0xff,0x4b,0xff,0xff,0xff,0x2c,0xff,0xff,0xff,0x14,0xff,0xff,0xff,0x05,0xff, - 0xff,0xff,0xb4,0xff,0xff,0xff,0x14,0xff,0xff,0xff,0x2c,0xff,0xff,0xff,0x4b, - 0xff,0xff,0xff,0x71,0xff,0xff,0xff,0x9e,0xff,0xff,0xff,0xc9,0xff,0xff,0xff, - 0xb7,0xff,0xff,0xff,0x74,0xff,0xff,0xff,0x2c,0x8e,0xff,0xff,0xff,0x00,0x82, - 0xff,0xff,0xff,0x01,0x0c,0xfc,0xfc,0xfc,0x00,0x00,0x00,0x00,0x00,0xf6,0xf6, - 0xf6,0x05,0xdc,0xdc,0xdc,0x09,0x00,0x00,0x00,0x02,0xac,0xac,0xac,0x0f,0xc2, - 0xc1,0xc1,0x3c,0xc4,0xc3,0xc4,0x4a,0x40,0x40,0x41,0x0b,0x00,0x00,0x00,0x06, - 0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x86,0xea,0xe9,0xeb,0x00,0x02,0xde, - 0xdd,0xdd,0x00,0xde,0xdf,0xde,0x00,0x82,0xd8,0xda,0xd9,0x00,0x03,0xd5,0xd7, - 0xd5,0x00,0xd6,0xd8,0xd5,0x00,0xd0,0xd2,0xcf,0x00,0x83,0xda,0xda,0xda,0x00, - 0x17,0xcf,0xd0,0xcf,0x00,0xcf,0xd1,0xce,0x00,0xce,0xce,0xcc,0x00,0x93,0x93, - 0x91,0x00,0x6d,0x6d,0x6c,0x00,0x69,0x69,0x69,0x00,0x67,0x67,0x66,0x00,0x75, - 0x75,0x75,0x00,0x71,0x71,0x6f,0x00,0x8b,0x8b,0x8b,0x00,0x72,0x72,0x70,0x00, - 0x69,0x68,0x67,0x00,0x73,0x73,0x71,0x00,0x7f,0x7f,0x7d,0x00,0x83,0x83,0x80, - 0x00,0xb3,0xb2,0xae,0x00,0xad,0xab,0xa8,0x00,0xa9,0xa6,0xa3,0x00,0xab,0xa7, - 0xa4,0x00,0xab,0xa9,0xa4,0x00,0xaf,0xad,0xa8,0x00,0xb9,0xb6,0xb2,0x00,0xc0, - 0xbd,0xb6,0x00,0x8c,0x00,0x00,0x00,0x00,0x97,0xff,0xff,0xff,0x00,0x06,0xff, - 0xff,0xff,0x37,0xff,0xff,0xff,0x8c,0xff,0xff,0xff,0xb9,0xff,0xff,0xff,0x8b, - 0xff,0xff,0xff,0x48,0xff,0xff,0xff,0x08,0x86,0xff,0xff,0xff,0x00,0x01,0xff, - 0xff,0xff,0xb3,0x85,0xff,0xff,0xff,0x00,0x06,0xff,0xff,0xff,0x08,0xff,0xff, - 0xff,0x48,0xff,0xff,0xff,0x8b,0xff,0xff,0xff,0xb9,0xff,0xff,0xff,0x8c,0xff, - 0xff,0xff,0x37,0x8c,0xff,0xff,0xff,0x00,0x0f,0xfc,0xfc,0xfc,0x00,0xfb,0xfb, - 0xfb,0x02,0xfd,0xfd,0xfd,0x01,0x00,0x00,0x00,0x00,0xef,0xef,0xef,0x05,0xda, - 0xda,0xda,0x11,0xb5,0xb5,0xb5,0x08,0x4f,0x4e,0x4e,0x09,0xaf,0xad,0xad,0x44, - 0x9e,0x9b,0x9d,0x8c,0xac,0xab,0xad,0x4a,0x00,0x00,0x00,0x0c,0x00,0x00,0x00, - 0x08,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x85,0xd2,0xd2,0xd2,0x00,0x02, - 0xde,0xdd,0xdd,0x00,0xde,0xdf,0xde,0x00,0x82,0xd8,0xda,0xd9,0x00,0x03,0xd5, - 0xd7,0xd5,0x00,0xd6,0xd8,0xd5,0x00,0xd0,0xd2,0xcf,0x00,0x83,0xda,0xda,0xda, - 0x00,0x17,0xcf,0xd0,0xcf,0x00,0xcf,0xd1,0xce,0x00,0xce,0xce,0xcc,0x00,0x93, - 0x93,0x91,0x00,0x6d,0x6d,0x6c,0x00,0x69,0x69,0x69,0x00,0x67,0x67,0x66,0x00, - 0x75,0x75,0x75,0x00,0x71,0x71,0x6f,0x00,0x8b,0x8b,0x8b,0x00,0x72,0x72,0x70, - 0x00,0x69,0x68,0x67,0x00,0x73,0x73,0x71,0x00,0x7f,0x7f,0x7d,0x00,0x83,0x83, - 0x80,0x00,0xb3,0xb2,0xae,0x00,0xad,0xab,0xa8,0x00,0xa9,0xa6,0xa3,0x00,0xab, - 0xa7,0xa4,0x00,0xab,0xa9,0xa4,0x00,0xaf,0xad,0xa8,0x00,0xb9,0xb6,0xb2,0x00, - 0xc0,0xbd,0xb6,0x00,0x8c,0x00,0x00,0x00,0x00,0x95,0xff,0xff,0xff,0x00,0x05, - 0xff,0xff,0xff,0x1b,0xff,0xff,0xff,0x7c,0xff,0xff,0xff,0xad,0xff,0xff,0xff, - 0x73,0xff,0xff,0xff,0x20,0x89,0xff,0xff,0xff,0x00,0x01,0xff,0xff,0xff,0xb3, - 0x88,0xff,0xff,0xff,0x00,0x05,0xff,0xff,0xff,0x20,0xff,0xff,0xff,0x73,0xff, - 0xff,0xff,0xad,0xff,0xff,0xff,0x7c,0xff,0xff,0xff,0x1b,0x89,0xff,0xff,0xff, - 0x00,0x11,0xfc,0xfc,0xfc,0x00,0xfb,0xfb,0xfb,0x00,0xf6,0xf6,0xf6,0x02,0xf8, - 0xf8,0xf8,0x04,0x00,0x00,0x00,0x00,0xeb,0xeb,0xeb,0x04,0xc9,0xc8,0xc8,0x17, - 0xc5,0xc4,0xc5,0x1d,0x19,0x19,0x19,0x09,0x95,0x94,0x94,0x2b,0x86,0x84,0x87, - 0xa0,0x8b,0x8b,0x8d,0xa8,0x51,0x51,0x51,0x22,0x00,0x00,0x00,0x10,0x00,0x00, - 0x00,0x08,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x84,0xd2,0xd2,0xd2,0x00, - 0x02,0xde,0xdd,0xdd,0x00,0xde,0xdf,0xde,0x00,0x82,0xd8,0xda,0xd9,0x00,0x03, - 0xd5,0xd7,0xd5,0x00,0xd6,0xd8,0xd5,0x00,0xd0,0xd2,0xcf,0x00,0x83,0xda,0xda, - 0xda,0x00,0x17,0xcf,0xd0,0xcf,0x00,0xcf,0xd1,0xce,0x00,0xce,0xce,0xcc,0x00, - 0x93,0x93,0x91,0x00,0x6d,0x6d,0x6c,0x00,0x69,0x69,0x69,0x00,0x67,0x67,0x66, - 0x00,0x75,0x75,0x75,0x00,0x71,0x71,0x6f,0x00,0x8b,0x8b,0x8b,0x00,0x72,0x72, - 0x70,0x00,0x69,0x68,0x67,0x00,0x73,0x73,0x71,0x00,0x7f,0x7f,0x7d,0x00,0x83, - 0x83,0x80,0x00,0xb3,0xb2,0xae,0x00,0xad,0xab,0xa8,0x00,0xa9,0xa6,0xa3,0x00, - 0xab,0xa7,0xa4,0x00,0xab,0xa9,0xa4,0x00,0xaf,0xad,0xa8,0x00,0xb9,0xb6,0xb2, - 0x00,0xc0,0xbd,0xb6,0x00,0x8c,0x00,0x00,0x00,0x00,0x94,0xff,0xff,0xff,0x00, - 0x04,0xff,0xff,0xff,0x48,0xff,0xff,0xff,0x9f,0xff,0xff,0xff,0x83,0xff,0xff, - 0xff,0x23,0x8b,0xff,0xff,0xff,0x00,0x01,0xff,0xff,0xff,0xb3,0x8a,0xff,0xff, - 0xff,0x00,0x04,0xff,0xff,0xff,0x23,0xff,0xff,0xff,0x83,0xff,0xff,0xff,0x9f, - 0xff,0xff,0xff,0x48,0x87,0xff,0xff,0xff,0x00,0x12,0xff,0xff,0xff,0x01,0xfa, - 0xfa,0xfa,0x00,0xfb,0xfb,0xfb,0x00,0xef,0xef,0xef,0x03,0xe9,0xe9,0xe9,0x08, - 0xf3,0xf3,0xf3,0x03,0xec,0xeb,0xe9,0x01,0xc6,0xc4,0xc4,0x18,0xa0,0x9c,0x9e, - 0x37,0x99,0x98,0x9a,0x20,0x00,0x00,0x00,0x12,0x87,0x87,0x88,0x86,0x59,0x5d, - 0x62,0xee,0x88,0x88,0x89,0x8e,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x11,0x00, - 0x00,0x00,0x07,0x00,0x00,0x00,0x01,0x83,0xbc,0xbc,0xbc,0x00,0x03,0xcd,0xce, - 0xcd,0x00,0xde,0xdd,0xdd,0x00,0xde,0xdf,0xde,0x00,0x82,0xd8,0xda,0xd9,0x00, - 0x03,0xd5,0xd7,0xd5,0x00,0xd6,0xd8,0xd5,0x00,0xd0,0xd2,0xcf,0x00,0x83,0xda, - 0xda,0xda,0x00,0x17,0xcf,0xd0,0xcf,0x00,0xcf,0xd1,0xce,0x00,0xce,0xce,0xcc, - 0x00,0x93,0x93,0x91,0x00,0x6d,0x6d,0x6c,0x00,0x69,0x69,0x69,0x00,0x67,0x67, - 0x66,0x00,0x75,0x75,0x75,0x00,0x71,0x71,0x6f,0x00,0x8b,0x8b,0x8b,0x00,0x72, - 0x72,0x70,0x00,0x69,0x68,0x67,0x00,0x73,0x73,0x71,0x00,0x7f,0x7f,0x7d,0x00, - 0x83,0x83,0x80,0x00,0xb3,0xb2,0xae,0x00,0xad,0xab,0xa8,0x00,0xa9,0xa6,0xa3, - 0x00,0xab,0xa7,0xa4,0x00,0xab,0xa9,0xa4,0x00,0xaf,0xad,0xa8,0x00,0xb9,0xb6, - 0xb2,0x00,0xc0,0xbd,0xb6,0x00,0x8c,0x00,0x00,0x00,0x00,0x93,0xff,0xff,0xff, - 0x00,0x03,0xff,0xff,0xff,0x66,0xff,0xff,0xff,0x9c,0xff,0xff,0xff,0x4d,0x8d, - 0xff,0xff,0xff,0x00,0x01,0xff,0xff,0xff,0xb3,0x8c,0xff,0xff,0xff,0x00,0x03, - 0xff,0xff,0xff,0x4d,0xff,0xff,0xff,0x9c,0xff,0xff,0xff,0x66,0x85,0xff,0xff, - 0xff,0x00,0x14,0xfc,0xfc,0xfc,0x00,0xfb,0xfb,0xfb,0x01,0xf3,0xf3,0xf3,0x00, - 0xeb,0xeb,0xeb,0x00,0xeb,0xeb,0xeb,0x02,0xd3,0xd2,0xd2,0x0b,0xd6,0xd5,0xd6, - 0x0e,0xea,0xe9,0xeb,0x01,0xcf,0xcd,0xcd,0x0c,0x8b,0x89,0x8c,0x3d,0x85,0x85, - 0x87,0x45,0x28,0x28,0x28,0x18,0x66,0x66,0x66,0x42,0x65,0x69,0x6b,0xdf,0x5e, - 0x63,0x68,0xf1,0x6a,0x6a,0x68,0x53,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x0e, - 0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x01,0x82,0xbc,0xbc,0xbc,0x00,0x03,0xcd, - 0xce,0xcd,0x00,0xde,0xdd,0xdd,0x00,0xde,0xdf,0xde,0x00,0x82,0xd8,0xda,0xd9, - 0x00,0x03,0xd5,0xd7,0xd5,0x00,0xd6,0xd8,0xd5,0x00,0xd0,0xd2,0xcf,0x00,0x83, - 0xda,0xda,0xda,0x00,0x17,0xcf,0xd0,0xcf,0x00,0xcf,0xd1,0xce,0x00,0xce,0xce, - 0xcc,0x00,0x93,0x93,0x91,0x00,0x6d,0x6d,0x6c,0x00,0x69,0x69,0x69,0x00,0x67, - 0x67,0x66,0x00,0x75,0x75,0x75,0x00,0x71,0x71,0x6f,0x00,0x8b,0x8b,0x8b,0x00, - 0x72,0x72,0x70,0x00,0x69,0x68,0x67,0x00,0x73,0x73,0x71,0x00,0x7f,0x7f,0x7d, - 0x00,0x83,0x83,0x80,0x00,0xb3,0xb2,0xae,0x00,0xad,0xab,0xa8,0x00,0xa9,0xa6, - 0xa3,0x00,0xab,0xa7,0xa4,0x00,0xab,0xa9,0xa4,0x00,0xaf,0xad,0xa8,0x00,0xb9, - 0xb6,0xb2,0x00,0xc0,0xbd,0xb6,0x00,0x8c,0x00,0x00,0x00,0x00,0x92,0xff,0xff, - 0xff,0x00,0x03,0xff,0xff,0xff,0x75,0xff,0xff,0xff,0x90,0xff,0xff,0xff,0x26, - 0x8e,0xff,0xff,0xff,0x00,0x01,0xff,0xff,0xff,0x05,0x8d,0xff,0xff,0xff,0x00, - 0x03,0xff,0xff,0xff,0x26,0xff,0xff,0xff,0x90,0xff,0xff,0xff,0x75,0x83,0xff, - 0xff,0xff,0x00,0x15,0xfc,0xfc,0xfc,0x00,0xfb,0xfb,0xfb,0x00,0xf6,0xf6,0xf6, - 0x01,0xf8,0xf8,0xf8,0x02,0xec,0xeb,0xe9,0x00,0xec,0xeb,0xe9,0x01,0xc6,0xc4, - 0xc4,0x0c,0xa7,0xa3,0xa5,0x1b,0xca,0xc9,0xcb,0x0d,0xaa,0xab,0xa7,0x00,0x94, - 0x94,0x96,0x31,0x55,0x58,0x5d,0x62,0x72,0x71,0x73,0x40,0x00,0x00,0x00,0x20, - 0x6f,0x70,0x6f,0x8e,0x49,0x4f,0x57,0xff,0x72,0x76,0x78,0xc6,0x0d,0x0d,0x0d, - 0x2d,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x03,0x82,0xbc, - 0xbc,0xbc,0x00,0x03,0xcd,0xce,0xcd,0x00,0xde,0xdd,0xdd,0x00,0xde,0xdf,0xde, - 0x00,0x82,0xd8,0xda,0xd9,0x00,0x03,0xd5,0xd7,0xd5,0x00,0xd6,0xd8,0xd5,0x00, - 0xd0,0xd2,0xcf,0x00,0x83,0xda,0xda,0xda,0x00,0x17,0xcf,0xd0,0xcf,0x00,0xcf, - 0xd1,0xce,0x00,0xce,0xce,0xcc,0x00,0x93,0x93,0x91,0x00,0x6d,0x6d,0x6c,0x00, - 0x69,0x69,0x69,0x00,0x67,0x67,0x66,0x00,0x75,0x75,0x75,0x00,0x71,0x71,0x6f, - 0x00,0x8b,0x8b,0x8b,0x00,0x72,0x72,0x70,0x00,0x69,0x68,0x67,0x00,0x73,0x73, - 0x71,0x00,0x7f,0x7f,0x7d,0x00,0x83,0x83,0x80,0x00,0xb3,0xb2,0xae,0x00,0xad, - 0xab,0xa8,0x00,0xa9,0xa6,0xa3,0x00,0xab,0xa7,0xa4,0x00,0xab,0xa9,0xa4,0x00, - 0xaf,0xad,0xa8,0x00,0xb9,0xb6,0xb2,0x00,0xc0,0xbd,0xb6,0x00,0x8c,0x00,0x00, - 0x00,0x00,0x91,0xff,0xff,0xff,0x00,0x03,0xff,0xff,0xff,0x75,0xff,0xff,0xff, - 0x86,0xff,0xff,0xff,0x0f,0x9e,0xff,0xff,0xff,0x00,0x03,0xff,0xff,0xff,0x0f, - 0xff,0xff,0xff,0x86,0xff,0xff,0xff,0x75,0x82,0xff,0xff,0xff,0x00,0x1a,0xfa, - 0xfa,0xfa,0x00,0xfb,0xfb,0xfb,0x00,0xef,0xef,0xef,0x01,0xe9,0xe9,0xe9,0x04, - 0xf3,0xf3,0xf3,0x02,0xec,0xeb,0xe9,0x00,0xcf,0xcd,0xcd,0x06,0x8e,0x8c,0x8f, - 0x1e,0x93,0x93,0x95,0x20,0xd2,0xd2,0xd2,0x03,0xa1,0xa1,0xa1,0x11,0x65,0x68, - 0x6a,0x58,0x54,0x58,0x5c,0x67,0x44,0x44,0x43,0x2e,0x2f,0x30,0x2f,0x3f,0x66, - 0x69,0x6c,0xe3,0x49,0x51,0x5a,0xff,0x73,0x73,0x77,0x8c,0x00,0x00,0x00,0x26, - 0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x01,0xdb,0xdf,0xdd, - 0x00,0xcd,0xce,0xcd,0x00,0xde,0xdd,0xdd,0x00,0xde,0xdf,0xde,0x00,0x82,0xd8, - 0xda,0xd9,0x00,0x03,0xd5,0xd7,0xd5,0x00,0xd6,0xd8,0xd5,0x00,0xd0,0xd2,0xcf, - 0x00,0x83,0xda,0xda,0xda,0x00,0x17,0xcf,0xd0,0xcf,0x00,0xcf,0xd1,0xce,0x00, - 0xce,0xce,0xcc,0x00,0x93,0x93,0x91,0x00,0x6d,0x6d,0x6c,0x00,0x69,0x69,0x69, - 0x00,0x67,0x67,0x66,0x00,0x75,0x75,0x75,0x00,0x71,0x71,0x6f,0x00,0x8b,0x8b, - 0x8b,0x00,0x72,0x72,0x70,0x00,0x69,0x68,0x67,0x00,0x73,0x73,0x71,0x00,0x7f, - 0x7f,0x7d,0x00,0x83,0x83,0x80,0x00,0xb3,0xb2,0xae,0x00,0xad,0xab,0xa8,0x00, - 0xa9,0xa6,0xa3,0x00,0xab,0xa7,0xa4,0x00,0xab,0xa9,0xa4,0x00,0xaf,0xad,0xa8, - 0x00,0xb9,0xb6,0xb2,0x00,0xc0,0xbd,0xb6,0x00,0x8c,0x00,0x00,0x00,0x00,0x90, - 0xff,0xff,0xff,0x00,0x03,0xff,0xff,0xff,0x66,0xff,0xff,0xff,0x90,0xff,0xff, - 0xff,0x0f,0xa0,0xff,0xff,0xff,0x00,0x1e,0xff,0xff,0xff,0x0f,0xff,0xff,0xff, - 0x90,0xff,0xff,0xff,0x66,0xff,0xff,0xff,0x00,0xf3,0xf3,0xf3,0x00,0xeb,0xeb, - 0xeb,0x00,0xeb,0xeb,0xeb,0x01,0xd3,0xd2,0xd2,0x06,0xd6,0xd5,0xd6,0x07,0xec, - 0xeb,0xe9,0x00,0xaa,0xab,0xa7,0x00,0x98,0x98,0x9a,0x18,0x5a,0x5e,0x63,0x2f, - 0x9c,0x9b,0x9d,0x19,0xa5,0xa6,0xa5,0x00,0x86,0x87,0x86,0x2f,0x44,0x49,0x50, - 0x6b,0x5f,0x62,0x64,0x58,0x04,0x04,0x04,0x2a,0x5b,0x5c,0x5b,0x77,0x51,0x58, - 0x5f,0xfe,0x60,0x66,0x6b,0xed,0x3c,0x3d,0x3d,0x48,0x00,0x00,0x00,0x1f,0x00, - 0x00,0x00,0x0e,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x01,0xcd,0xce,0xcd,0x00, - 0xde,0xdd,0xdd,0x00,0xde,0xdf,0xde,0x00,0x82,0xd8,0xda,0xd9,0x00,0x03,0xd5, - 0xd7,0xd5,0x00,0xd6,0xd8,0xd5,0x00,0xd0,0xd2,0xcf,0x00,0x83,0xda,0xda,0xda, - 0x00,0x17,0xcf,0xd0,0xcf,0x00,0xcf,0xd1,0xce,0x00,0xce,0xce,0xcc,0x00,0x93, - 0x93,0x91,0x00,0x6d,0x6d,0x6c,0x00,0x69,0x69,0x69,0x00,0x67,0x67,0x66,0x00, - 0x75,0x75,0x75,0x00,0x71,0x71,0x6f,0x00,0x8b,0x8b,0x8b,0x00,0x72,0x72,0x70, - 0x00,0x69,0x68,0x67,0x00,0x73,0x73,0x71,0x00,0x7f,0x7f,0x7d,0x00,0x83,0x83, - 0x80,0x00,0xb3,0xb2,0xae,0x00,0xad,0xab,0xa8,0x00,0xa9,0xa6,0xa3,0x00,0xab, - 0xa7,0xa4,0x00,0xab,0xa9,0xa4,0x00,0xaf,0xad,0xa8,0x00,0xb9,0xb6,0xb2,0x00, - 0xc0,0xbd,0xb6,0x00,0x8c,0x00,0x00,0x00,0x00,0x8f,0xff,0xff,0xff,0x00,0x03, - 0xff,0xff,0xff,0x48,0xff,0xff,0xff,0x9c,0xff,0xff,0xff,0x26,0xa2,0xff,0xff, - 0xff,0x00,0x04,0xff,0xff,0xff,0x26,0xff,0xff,0xff,0x9c,0xff,0xff,0xff,0x48, - 0xec,0xec,0xea,0x00,0x82,0xec,0xeb,0xe9,0x00,0x17,0xc6,0xc4,0xc4,0x06,0xa7, - 0xa3,0xa5,0x0e,0xca,0xc9,0xcb,0x06,0xa9,0xaa,0xa9,0x00,0xac,0xac,0xac,0x08, - 0x68,0x6c,0x6e,0x2b,0x5f,0x64,0x69,0x30,0xaf,0xaf,0xad,0x0a,0x8f,0x90,0x8f, - 0x08,0x65,0x68,0x6b,0x5a,0x40,0x47,0x4f,0x6e,0x51,0x51,0x54,0x46,0x00,0x00, - 0x00,0x35,0x69,0x6e,0x6e,0xc0,0x3f,0x4e,0x53,0xff,0x6e,0x73,0x76,0xc2,0x04, - 0x04,0x04,0x2e,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x02, - 0xcd,0xce,0xcd,0x00,0xde,0xdd,0xdd,0x00,0xde,0xdf,0xde,0x00,0x82,0xd8,0xda, - 0xd9,0x00,0x02,0xd5,0xd7,0xd5,0x00,0xd6,0xd8,0xd5,0x00,0x87,0x00,0x00,0x00, - 0x01,0x14,0x93,0x93,0x91,0x00,0x6d,0x6d,0x6c,0x00,0x69,0x69,0x69,0x00,0x67, - 0x67,0x66,0x00,0x75,0x75,0x75,0x00,0x71,0x71,0x6f,0x00,0x8b,0x8b,0x8b,0x00, - 0x72,0x72,0x70,0x00,0x69,0x68,0x67,0x00,0x73,0x73,0x71,0x00,0x7f,0x7f,0x7d, - 0x00,0x83,0x83,0x80,0x00,0xb3,0xb2,0xae,0x00,0xad,0xab,0xa8,0x00,0xa9,0xa6, - 0xa3,0x00,0xab,0xa7,0xa4,0x00,0xab,0xa9,0xa4,0x00,0xaf,0xad,0xa8,0x00,0xb9, - 0xb6,0xb2,0x00,0xc0,0xbd,0xb6,0x00,0x8c,0x00,0x00,0x00,0x00,0x8e,0xff,0xff, - 0xff,0x00,0x03,0xff,0xff,0xff,0x1b,0xff,0xff,0xff,0x9f,0xff,0xff,0xff,0x4d, - 0xa4,0xff,0xff,0xff,0x00,0x03,0xff,0xff,0xff,0x4d,0xff,0xff,0xff,0x9f,0xff, - 0xff,0xff,0x1b,0x82,0xec,0xeb,0xe9,0x00,0x14,0xcf,0xcd,0xcd,0x03,0x8e,0x8c, - 0x8f,0x0f,0x93,0x93,0x95,0x10,0xd2,0xd2,0xd2,0x01,0x00,0x00,0x00,0x00,0x8a, - 0x8b,0x8a,0x17,0x49,0x4f,0x57,0x33,0x7c,0x81,0x83,0x24,0xbc,0xbc,0xbc,0x01, - 0x8a,0x8b,0x89,0x1f,0x4a,0x51,0x58,0x6b,0x4f,0x53,0x58,0x69,0x1e,0x1e,0x1e, - 0x31,0x3b,0x3c,0x3b,0x58,0x65,0x6a,0x6d,0xef,0x4f,0x56,0x62,0xff,0x6b,0x6c, - 0x6f,0x82,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x06,0x83, - 0x00,0x00,0x00,0x01,0x0f,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x04,0x00,0x00, - 0x00,0x06,0x52,0x53,0x52,0x0d,0x7b,0x7c,0x7b,0x16,0xa1,0xa1,0xa1,0x26,0xa0, - 0xa0,0xa0,0x29,0x96,0x96,0x96,0x23,0x86,0x87,0x86,0x1f,0x71,0x72,0x70,0x16, - 0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x06,0x00,0x00,0x00, - 0x04,0x00,0x00,0x00,0x03,0x82,0x00,0x00,0x00,0x01,0x0e,0x8b,0x8b,0x8b,0x00, - 0x72,0x72,0x70,0x00,0x69,0x68,0x67,0x00,0x73,0x73,0x71,0x00,0x7f,0x7f,0x7d, - 0x00,0x83,0x83,0x80,0x00,0xb3,0xb2,0xae,0x00,0xad,0xab,0xa8,0x00,0xa9,0xa6, - 0xa3,0x00,0xab,0xa7,0xa4,0x00,0xab,0xa9,0xa4,0x00,0xaf,0xad,0xa8,0x00,0xb9, - 0xb6,0xb2,0x00,0xc0,0xbd,0xb6,0x00,0x8c,0x00,0x00,0x00,0x00,0x8e,0xff,0xff, - 0xff,0x00,0x02,0xff,0xff,0xff,0x7c,0xff,0xff,0xff,0x83,0xa6,0xff,0xff,0xff, - 0x00,0x02,0xff,0xff,0xff,0x83,0xff,0xff,0xff,0x7c,0x82,0xec,0xeb,0xe9,0x00, - 0x28,0xa9,0xaa,0xa9,0x00,0x98,0x98,0x9a,0x0c,0x5a,0x5e,0x63,0x18,0x9c,0x9b, - 0x9d,0x0d,0x00,0x00,0x00,0x00,0xa5,0xa6,0xa5,0x04,0x6b,0x6e,0x71,0x2b,0x49, - 0x51,0x5a,0x33,0x96,0x97,0x9b,0x15,0x00,0x00,0x00,0x01,0x75,0x7a,0x7a,0x45, - 0x38,0x45,0x4a,0x6d,0x55,0x59,0x5c,0x59,0x00,0x00,0x00,0x32,0x66,0x66,0x68, - 0x8c,0x5a,0x63,0x68,0xff,0x5e,0x66,0x6b,0xf0,0x50,0x51,0x54,0x56,0x00,0x00, - 0x00,0x20,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x09,0xa2,0xa1,0xa1,0x1a,0xc0, - 0xc0,0xc0,0x42,0xc5,0xc7,0xc6,0x6a,0xcc,0xce,0xcd,0x95,0xcd,0xcf,0xcd,0xba, - 0xcb,0xcd,0xca,0xd3,0xcb,0xcd,0xca,0xeb,0xca,0xcd,0xc9,0xf5,0xcb,0xcd,0xca, - 0xf6,0xc9,0xcb,0xc8,0xf5,0xc8,0xc9,0xc7,0xf4,0xcb,0xcd,0xca,0xe7,0xc1,0xc1, - 0xbf,0xb0,0x8a,0x8a,0x88,0xae,0x61,0x61,0x61,0x7b,0x3e,0x3e,0x3e,0x27,0x00, - 0x00,0x00,0x0c,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x04,0x82,0x00,0x00,0x00, - 0x01,0x0c,0x69,0x68,0x67,0x00,0x73,0x73,0x71,0x00,0x7f,0x7f,0x7d,0x00,0x83, - 0x83,0x80,0x00,0xb3,0xb2,0xae,0x00,0xad,0xab,0xa8,0x00,0xa9,0xa6,0xa3,0x00, - 0xab,0xa7,0xa4,0x00,0xab,0xa9,0xa4,0x00,0xaf,0xad,0xa8,0x00,0xb9,0xb6,0xb2, - 0x00,0xc0,0xbd,0xb6,0x00,0x8c,0x00,0x00,0x00,0x00,0x8d,0xff,0xff,0xff,0x00, - 0x03,0xff,0xff,0xff,0x37,0xff,0xff,0xff,0xad,0xff,0xff,0xff,0x23,0xa6,0xff, - 0xff,0xff,0x00,0x3a,0xff,0xff,0xff,0x23,0xff,0xff,0xff,0xad,0xff,0xff,0xff, - 0x37,0xac,0xac,0xac,0x00,0xa5,0xa5,0xa3,0x00,0xac,0xac,0xac,0x04,0x68,0x6c, - 0x6e,0x16,0x5f,0x64,0x69,0x18,0xaf,0xaf,0xad,0x05,0x00,0x00,0x00,0x00,0x8f, - 0x90,0x8e,0x0f,0x51,0x58,0x5f,0x33,0x62,0x68,0x6e,0x2e,0xad,0xae,0xae,0x05, - 0x91,0x93,0x90,0x0e,0x62,0x65,0x68,0x61,0x43,0x49,0x53,0x70,0x45,0x45,0x47, - 0x45,0x03,0x03,0x03,0x3b,0x6b,0x71,0x71,0xbf,0x4a,0x56,0x5d,0xff,0x64,0x68, - 0x70,0xda,0x52,0x53,0x53,0x49,0xa7,0xab,0xa9,0x66,0xbd,0xbe,0xbd,0x99,0xc5, - 0xc6,0xc4,0xee,0xca,0xcd,0xca,0xfd,0xc9,0xce,0xcb,0xff,0xd4,0xd9,0xd4,0xff, - 0xe1,0xe4,0xdf,0xff,0xe6,0xe8,0xe3,0xff,0xd8,0xda,0xd7,0xff,0xd0,0xd1,0xcf, - 0xff,0xcd,0xd1,0xce,0xff,0xcf,0xd0,0xcd,0xff,0xcf,0xd2,0xce,0xff,0xcc,0xce, - 0xcb,0xff,0xcf,0xd2,0xce,0xff,0xdc,0xdf,0xdb,0xff,0xb1,0xb0,0xaf,0xff,0x6c, - 0x6c,0x6a,0xea,0x59,0x59,0x58,0x88,0x29,0x29,0x29,0x22,0x00,0x00,0x00,0x0e, - 0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x73,0x73,0x71, - 0x00,0x7f,0x7f,0x7d,0x00,0x83,0x83,0x80,0x00,0xb3,0xb2,0xae,0x00,0xad,0xab, - 0xa8,0x00,0xa9,0xa6,0xa3,0x00,0xab,0xa7,0xa4,0x00,0xab,0xa9,0xa4,0x00,0xaf, - 0xad,0xa8,0x00,0xb9,0xb6,0xb2,0x00,0xc0,0xbd,0xb6,0x00,0x8c,0x00,0x00,0x00, - 0x00,0x8d,0xff,0xff,0xff,0x00,0x02,0xff,0xff,0xff,0x8c,0xff,0xff,0xff,0x73, - 0x8f,0xff,0xff,0xff,0x00,0x03,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x01,0x00, - 0x00,0x00,0x02,0x82,0x00,0x00,0x00,0x01,0x94,0xff,0xff,0xff,0x00,0x39,0xff, - 0xff,0xff,0x73,0xff,0xff,0xff,0x8c,0xac,0xac,0xac,0x00,0x9f,0x9f,0x9e,0x00, - 0xa5,0xa6,0xa5,0x00,0x8a,0x8b,0x8a,0x0c,0x49,0x4f,0x57,0x1a,0x7c,0x81,0x83, - 0x13,0x00,0x00,0x00,0x00,0xce,0xce,0xce,0x00,0x79,0x7e,0x7e,0x21,0x3f,0x4e, - 0x53,0x33,0x7c,0x81,0x84,0x23,0xe1,0xe1,0xe0,0x00,0x8c,0x8c,0x8e,0x28,0x53, - 0x5b,0x5f,0x6b,0x4b,0x52,0x56,0x6d,0x26,0x26,0x28,0x3b,0x08,0x08,0x08,0x44, - 0x79,0x7c,0x7d,0xdd,0x95,0x9d,0x9e,0xff,0xc3,0xc8,0xc6,0xf7,0xc8,0xce,0xcc, - 0xff,0xcd,0xd0,0xd0,0xff,0xd6,0xd8,0xd3,0xff,0xb4,0xb6,0xb0,0xff,0xba,0xbc, - 0xb4,0xff,0x93,0x98,0x93,0xff,0x80,0x85,0x85,0xff,0xa2,0xa6,0xa6,0xff,0xe4, - 0xeb,0xe6,0xff,0xf0,0xf5,0xee,0xff,0xdf,0xe1,0xdc,0xff,0xd4,0xd6,0xd4,0xff, - 0xd4,0xd6,0xd3,0xff,0xd5,0xd7,0xd4,0xff,0xd1,0xd3,0xd0,0xff,0xd3,0xd5,0xd2, - 0xff,0xe6,0xe9,0xe5,0xff,0xe9,0xe9,0xe8,0xff,0x9d,0x9e,0x9c,0xff,0x62,0x62, - 0x61,0xdc,0x5e,0x5e,0x5c,0x7d,0x25,0x25,0x25,0x1e,0x00,0x00,0x00,0x0d,0x00, - 0x00,0x00,0x07,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x83,0x83,0x80,0x00, - 0xb3,0xb2,0xae,0x00,0xad,0xab,0xa8,0x00,0xa9,0xa6,0xa3,0x00,0xab,0xa7,0xa4, - 0x00,0xab,0xa9,0xa4,0x00,0xaf,0xad,0xa8,0x00,0xb9,0xb6,0xb2,0x00,0xc0,0xbd, - 0xb6,0x00,0x8c,0x00,0x00,0x00,0x00,0x8c,0xff,0xff,0xff,0x00,0x03,0xff,0xff, - 0xff,0x2c,0xff,0xff,0xff,0xb9,0xff,0xff,0xff,0x20,0x8e,0xff,0xff,0xff,0x00, - 0x01,0x00,0x00,0x00,0x01,0x82,0x00,0x00,0x00,0xff,0x04,0x00,0x00,0x00,0x06, - 0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x02,0x93,0xff,0xff, - 0xff,0x00,0x39,0xff,0xff,0xff,0x20,0xff,0xff,0xff,0xb9,0xff,0xff,0xff,0x2c, - 0xa7,0xa8,0xa5,0x00,0x9d,0x9f,0x9c,0x00,0xa5,0xa6,0xa5,0x02,0x6b,0x6e,0x71, - 0x16,0x49,0x51,0x5a,0x1a,0x96,0x97,0x9b,0x0b,0xd2,0xd2,0xd2,0x00,0x9d,0x9f, - 0x9c,0x07,0x68,0x6d,0x70,0x2e,0x4f,0x56,0x62,0x33,0x96,0x97,0x9c,0x13,0x00, - 0x00,0x00,0x01,0x74,0x78,0x79,0x46,0x3f,0x4a,0x4f,0x70,0x4c,0x4f,0x55,0x6c, - 0x98,0x98,0x97,0x8a,0xc2,0xc7,0xc3,0xf1,0xd9,0xdc,0xd8,0xff,0xc8,0xce,0xca, - 0xff,0xc7,0xcb,0xca,0xff,0xc9,0xcb,0xca,0xff,0xfe,0xff,0xfe,0xff,0xda,0xdb, - 0xd5,0xff,0x6c,0x6d,0x6d,0xff,0x4f,0x50,0x56,0xff,0x39,0x3c,0x46,0xff,0x25, - 0x28,0x30,0xff,0x4d,0x52,0x56,0xff,0x9d,0xa2,0xa0,0xff,0xee,0xef,0xec,0xff, - 0xea,0xec,0xe7,0xff,0xd6,0xd8,0xd5,0xff,0xd8,0xda,0xd7,0xff,0xd7,0xd9,0xd6, - 0xff,0xd5,0xd7,0xd4,0xff,0xd4,0xd6,0xd3,0xff,0xd6,0xd8,0xd4,0xff,0xf3,0xf7, - 0xf4,0xff,0xc6,0xc6,0xc4,0xff,0x5b,0x59,0x57,0xff,0x65,0x64,0x64,0xd1,0x51, - 0x51,0x4f,0x56,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x05, - 0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x01,0xad,0xab,0xa8,0x00,0xa9,0xa6,0xa3, - 0x00,0xab,0xa7,0xa4,0x00,0xab,0xa9,0xa4,0x00,0xaf,0xad,0xa8,0x00,0xb9,0xb6, - 0xb2,0x00,0xc0,0xbd,0xb6,0x00,0x8c,0x00,0x00,0x00,0x00,0x8c,0xff,0xff,0xff, - 0x00,0x02,0xff,0xff,0xff,0x74,0xff,0xff,0xff,0x8b,0x8d,0xff,0xff,0xff,0x00, - 0x0a,0x00,0x00,0x00,0x66,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x02,0x00,0x00, - 0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x0d,0x00, - 0x00,0x00,0x0a,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x02,0x92,0xff,0xff,0xff, - 0x00,0x04,0xa5,0xa5,0xa3,0x00,0xff,0xff,0xff,0x8b,0xff,0xff,0xff,0x74,0xaa, - 0xab,0xab,0x00,0x82,0x9d,0x9f,0x9c,0x00,0x1e,0x8f,0x90,0x8e,0x08,0x51,0x58, - 0x5f,0x1a,0x62,0x68,0x6e,0x18,0xad,0xae,0xae,0x03,0xbc,0xbc,0xbb,0x00,0x94, - 0x95,0x97,0x13,0x5a,0x63,0x68,0x33,0x60,0x68,0x6e,0x2f,0x8f,0x91,0x95,0x0a, - 0x18,0x18,0x18,0x07,0x95,0x97,0x97,0x7a,0xbc,0xbe,0xbe,0xdb,0xc4,0xc8,0xc4, - 0xff,0xc8,0xce,0xcb,0xff,0xc6,0xcb,0xc8,0xff,0xc7,0xcc,0xc8,0xff,0xc7,0xcb, - 0xcd,0xff,0xc4,0xc5,0xc5,0xff,0xe5,0xe8,0xe2,0xff,0x8a,0x8a,0x8a,0xff,0x1c, - 0x1b,0x20,0xff,0x77,0x78,0x7e,0xff,0x9c,0x9f,0xa3,0xff,0x82,0x81,0x85,0xff, - 0x67,0x67,0x6c,0xff,0x51,0x55,0x5c,0xff,0x30,0x32,0x2f,0xff,0xd8,0xd9,0xd5, - 0xff,0xe6,0xe8,0xe5,0xff,0xd5,0xd7,0xd4,0xff,0x84,0xd7,0xd9,0xd6,0xff,0x11, - 0xd3,0xd5,0xd2,0xff,0xf0,0xef,0xee,0xff,0xd7,0xd6,0xd4,0xff,0x49,0x4a,0x47, - 0xff,0x66,0x66,0x64,0xfb,0x5c,0x5b,0x5a,0x9f,0x31,0x31,0x30,0x2d,0x00,0x00, - 0x00,0x11,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0xa9, - 0xa6,0xa3,0x00,0xab,0xa7,0xa4,0x00,0xab,0xa9,0xa4,0x00,0xaf,0xad,0xa8,0x00, - 0xb9,0xb6,0xb2,0x00,0xc0,0xbd,0xb6,0x00,0x8c,0x00,0x00,0x00,0x00,0x8c,0xff, - 0xff,0xff,0x00,0x02,0xff,0xff,0xff,0xb7,0xff,0xff,0xff,0x48,0x8d,0xff,0xff, - 0xff,0x00,0x82,0x00,0x00,0x00,0x66,0x02,0x00,0x00,0x00,0x04,0x00,0x00,0x00, - 0xff,0x82,0xff,0xff,0xff,0xff,0x05,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x15, - 0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x02,0x91,0xff,0xff, - 0xff,0x00,0x04,0x9f,0x9f,0x9e,0x00,0xff,0xff,0xff,0x48,0xff,0xff,0xff,0xb7, - 0xb0,0xb1,0xb3,0x00,0x82,0xab,0xac,0xae,0x00,0x1f,0xd2,0xd2,0xd2,0x00,0x79, - 0x7e,0x7e,0x11,0x3f,0x4e,0x53,0x1a,0x7c,0x81,0x84,0x12,0xbc,0xbc,0xbb,0x00, - 0xc5,0xc5,0xc5,0x00,0x79,0x7e,0x7f,0x22,0x46,0x52,0x58,0x35,0x61,0x65,0x6c, - 0x30,0xc5,0xc6,0xc5,0x8b,0xc6,0xc9,0xc7,0xf6,0xc3,0xc9,0xc4,0xff,0xc6,0xcc, - 0xc7,0xff,0xc7,0xca,0xc9,0xff,0xc9,0xcd,0xcd,0xff,0xc7,0xcb,0xca,0xff,0xc5, - 0xc9,0xc8,0xff,0xc8,0xcc,0xc9,0xff,0xc0,0xc6,0xc3,0xff,0xda,0xdf,0xd9,0xff, - 0xaf,0xaf,0xad,0xff,0x48,0x49,0x4a,0xff,0x59,0x58,0x5f,0xff,0x77,0x76,0x7c, - 0xff,0x50,0x4f,0x58,0xff,0x65,0x62,0x6a,0xff,0x20,0x20,0x1f,0xff,0x7f,0x87, - 0x7f,0xff,0xdf,0xe1,0xe0,0xff,0xdf,0xe1,0xde,0xff,0xd7,0xd9,0xd6,0xff,0x82, - 0xda,0xdc,0xd9,0xff,0x12,0xd9,0xdb,0xd8,0xff,0xd9,0xda,0xd6,0xff,0xd7,0xd6, - 0xd5,0xff,0xff,0xff,0xff,0xff,0x83,0x83,0x81,0xff,0x50,0x51,0x4f,0xff,0x68, - 0x68,0x63,0xff,0x5d,0x5e,0x5b,0xe1,0x5f,0x5f,0x5d,0x66,0x00,0x00,0x00,0x17, - 0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x01,0xab,0xa9,0xa4,0x00,0xaf,0xad,0xa8,0x00,0xb9,0xb6,0xb2,0x00,0xc0,0xbd, - 0xb6,0x00,0x8c,0x00,0x00,0x00,0x00,0x8b,0xff,0xff,0xff,0x00,0x03,0xff,0xff, - 0xff,0x2f,0xff,0xff,0xff,0xc9,0xff,0xff,0xff,0x08,0x8a,0xff,0xff,0xff,0x00, - 0x01,0x00,0x00,0x00,0x33,0x82,0xff,0xff,0xff,0x00,0x04,0x00,0x00,0x00,0x66, - 0xff,0xff,0xff,0x66,0x00,0x00,0x00,0x69,0x00,0x00,0x00,0xff,0x83,0xff,0xff, - 0xff,0xff,0x05,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x0e, - 0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x02,0x90,0xff,0xff,0xff,0x00,0x14,0xa7, - 0xa8,0xa5,0x00,0xff,0xff,0xff,0x08,0xff,0xff,0xff,0xc9,0xff,0xff,0xff,0x2f, - 0xbc,0xbc,0xbb,0x00,0xc5,0xc5,0xc5,0x00,0xd1,0xd1,0xd1,0x00,0x9d,0x9f,0x9c, - 0x03,0x68,0x6d,0x70,0x18,0x4f,0x56,0x62,0x1a,0x96,0x97,0x9c,0x09,0x00,0x00, - 0x00,0x01,0x2c,0x2c,0x2c,0x04,0x9f,0xa1,0xa1,0x4c,0xc6,0xc8,0xc7,0xc9,0xc1, - 0xc4,0xc3,0xff,0xc4,0xca,0xc6,0xff,0xc8,0xcd,0xc9,0xff,0xc7,0xcc,0xc8,0xff, - 0xc9,0xcd,0xcb,0xff,0x83,0xc7,0xcb,0xca,0xff,0x22,0xc7,0xcd,0xcb,0xff,0xc5, - 0xcb,0xc9,0xff,0xc7,0xc8,0xc8,0xff,0xdf,0xe1,0xe0,0xff,0xe4,0xe7,0xe7,0xff, - 0xb0,0xb3,0xb3,0xff,0x97,0x9b,0x9a,0xff,0x86,0x8a,0x8a,0xff,0x97,0x95,0x95, - 0xff,0xa5,0xa5,0xa3,0xff,0xa9,0xae,0xaa,0xff,0xd2,0xd4,0xd1,0xff,0xde,0xe1, - 0xde,0xff,0xd7,0xd9,0xd6,0xff,0xd9,0xdb,0xd8,0xff,0xd8,0xda,0xd7,0xff,0xdd, - 0xe0,0xdd,0xff,0xe6,0xe7,0xe6,0xff,0xe7,0xe7,0xe5,0xff,0xc6,0xc6,0xc4,0xff, - 0x79,0x79,0x77,0xff,0x77,0x75,0x74,0xff,0x70,0x72,0x6e,0xff,0x66,0x66,0x63, - 0xff,0x5e,0x5e,0x5c,0xfc,0x6f,0x6f,0x6d,0x9b,0x40,0x40,0x3e,0x2a,0x00,0x00, - 0x00,0x11,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0xaf, - 0xad,0xa8,0x00,0xb9,0xb6,0xb2,0x00,0xc0,0xbd,0xb6,0x00,0x8c,0x00,0x00,0x00, - 0x00,0x8b,0xff,0xff,0xff,0x00,0x02,0xff,0xff,0xff,0x61,0xff,0xff,0xff,0x9e, - 0x8b,0xff,0xff,0xff,0x00,0x82,0x00,0x00,0x00,0x33,0x05,0xff,0xff,0xff,0x00, - 0x00,0x00,0x00,0x66,0xff,0xff,0xff,0x66,0xef,0xef,0xef,0x6a,0x00,0x00,0x00, - 0xff,0x84,0xff,0xff,0xff,0xff,0x05,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x1a, - 0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x02,0x8f,0xff,0xff, - 0xff,0x00,0x39,0xaa,0xab,0xab,0x00,0x9d,0x9f,0x9c,0x00,0xff,0xff,0xff,0x9e, - 0xff,0xff,0xff,0x61,0xbc,0xbc,0xbb,0x00,0xc5,0xc5,0xc5,0x00,0xd1,0xd1,0xd1, - 0x00,0x00,0x00,0x00,0x00,0x94,0x95,0x97,0x0a,0x5a,0x63,0x68,0x1a,0x60,0x68, - 0x6e,0x18,0x7a,0x7b,0x7e,0x07,0xb7,0xb9,0xb7,0x41,0xcc,0xcd,0xca,0xdc,0xc2, - 0xc3,0xc3,0xff,0xc4,0xc9,0xc8,0xff,0xc8,0xcc,0xca,0xff,0xc7,0xcc,0xc8,0xff, - 0xc7,0xcc,0xc7,0xff,0xc5,0xcb,0xcb,0xff,0xc7,0xc9,0xca,0xff,0xc7,0xcb,0xca, - 0xff,0xc9,0xcd,0xcc,0xff,0xc7,0xcc,0xca,0xff,0xc9,0xcc,0xcc,0xff,0xca,0xcc, - 0xcb,0xff,0xc7,0xcc,0xcb,0xff,0xca,0xce,0xce,0xff,0xd9,0xdf,0xd9,0xff,0xdd, - 0xe3,0xde,0xff,0xe1,0xe7,0xe2,0xff,0xec,0xf2,0xeb,0xff,0xe5,0xe9,0xe6,0xff, - 0xb0,0xb0,0xab,0xff,0xb9,0xba,0xb5,0xff,0xe5,0xe4,0xe4,0xff,0xdf,0xe2,0xdf, - 0xff,0xde,0xdf,0xdc,0xff,0xe3,0xe4,0xe2,0xff,0xd9,0xda,0xd7,0xff,0xc4,0xc4, - 0xc1,0xff,0xa2,0xa1,0x9d,0xff,0x8f,0x8d,0x8d,0xff,0x9d,0x9e,0x99,0xff,0x97, - 0x96,0x94,0xff,0x8c,0x8c,0x86,0xff,0x81,0x81,0x7d,0xff,0x71,0x73,0x6e,0xff, - 0x57,0x57,0x56,0xff,0x68,0x68,0x65,0xd3,0x58,0x57,0x56,0x3f,0x00,0x00,0x00, - 0x14,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x01,0xb9,0xb6, - 0xb2,0x00,0xc0,0xbd,0xb6,0x00,0x8c,0x00,0x00,0x00,0x00,0x8b,0xff,0xff,0xff, - 0x00,0x02,0xff,0xff,0xff,0x8e,0xff,0xff,0xff,0x71,0x88,0xff,0xff,0xff,0x00, - 0x01,0x00,0x00,0x00,0x1a,0x82,0xff,0xff,0xff,0x00,0x07,0x00,0x00,0x00,0x33, - 0xff,0xff,0xff,0x33,0x00,0x00,0x00,0x33,0x00,0x00,0x00,0x66,0xff,0xff,0xff, - 0x66,0xef,0xef,0xef,0x6a,0x00,0x00,0x00,0xff,0x85,0xff,0xff,0xff,0xff,0x05, - 0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x0e,0x00,0x00,0x00, - 0x06,0x00,0x00,0x00,0x02,0x8e,0xff,0xff,0xff,0x00,0x07,0xb0,0xb1,0xb3,0x00, - 0xab,0xac,0xae,0x00,0xff,0xff,0xff,0x71,0xff,0xff,0xff,0x8e,0xbc,0xbc,0xbb, - 0x00,0xc5,0xc5,0xc5,0x00,0xd1,0xd1,0xd1,0x00,0x82,0x00,0x00,0x00,0x00,0x30, - 0x76,0x7b,0x7c,0x12,0x7e,0x84,0x88,0x2a,0xb6,0xb8,0xb8,0x76,0xaf,0xb2,0xaf, - 0xb1,0xb5,0xb9,0xb5,0xff,0xdf,0xe4,0xe2,0xff,0xc7,0xcc,0xcd,0xff,0xc6,0xc9, - 0xc7,0xff,0xc6,0xcc,0xc7,0xff,0xc9,0xcc,0xc9,0xff,0xc7,0xca,0xca,0xff,0xc7, - 0xcb,0xcc,0xff,0xc7,0xcb,0xc9,0xff,0xc8,0xcc,0xcb,0xff,0xc6,0xca,0xc9,0xff, - 0xc8,0xcc,0xcb,0xff,0xc7,0xcc,0xcb,0xff,0xca,0xcd,0xcb,0xff,0xc9,0xcd,0xc9, - 0xff,0xc6,0xcb,0xc7,0xff,0xc8,0xcd,0xc7,0xff,0xc3,0xc8,0xc4,0xff,0xc2,0xc9, - 0xc5,0xff,0xcc,0xcf,0xcc,0xff,0xe8,0xe8,0xe6,0xff,0xbe,0xbd,0xb9,0xff,0xa7, - 0xa9,0xa3,0xff,0xd1,0xd2,0xcc,0xff,0xca,0xc9,0xc5,0xff,0xbd,0xbd,0xb7,0xff, - 0xbb,0xba,0xb5,0xff,0xb8,0xb8,0xb2,0xff,0xc2,0xc1,0xbd,0xff,0xc1,0xc1,0xbb, - 0xff,0xbd,0xbe,0xb8,0xff,0xb9,0xb8,0xb3,0xff,0xaf,0xad,0xa9,0xff,0xa4,0xa4, - 0xa0,0xff,0x93,0x93,0x8d,0xff,0x82,0x82,0x7e,0xff,0x63,0x63,0x5f,0xff,0x68, - 0x68,0x64,0xe5,0x67,0x66,0x64,0x55,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x0c, - 0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x01,0xc0,0xbd,0xb6,0x00,0x8c,0x00,0x00, - 0x00,0x00,0x8b,0xff,0xff,0xff,0x00,0x02,0xff,0xff,0xff,0xb4,0xff,0xff,0xff, - 0x4b,0x88,0xff,0xff,0xff,0x00,0x82,0x00,0x00,0x00,0x1a,0x02,0xff,0xff,0xff, - 0x00,0x00,0x00,0x00,0x33,0x82,0xff,0xff,0xff,0x33,0x04,0x00,0x00,0x00,0x85, - 0xff,0xff,0xff,0x66,0xef,0xef,0xef,0x6a,0x00,0x00,0x00,0xff,0x86,0xff,0xff, - 0xff,0xff,0x05,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x0e, - 0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x02,0x8d,0xff,0xff,0xff,0x00,0x07,0xce, - 0xce,0xce,0x00,0xbc,0xbc,0xbb,0x00,0xff,0xff,0xff,0x4b,0xff,0xff,0xff,0xb4, - 0xbc,0xbc,0xbb,0x00,0xc5,0xc5,0xc5,0x00,0xd1,0xd1,0xd1,0x00,0x82,0x00,0x00, - 0x00,0x00,0x30,0xbd,0xbf,0xbe,0x17,0xbc,0xbd,0xbc,0x7f,0xa2,0xa2,0xa2,0xdd, - 0x6b,0x6c,0x69,0xfe,0x36,0x34,0x33,0xff,0x9d,0x9f,0x9d,0xff,0xeb,0xee,0xee, - 0xff,0xcf,0xd4,0xd2,0xff,0xc7,0xcb,0xc7,0xff,0xc7,0xcd,0xc9,0xff,0xca,0xcd, - 0xcd,0xff,0xc8,0xce,0xca,0xff,0xc8,0xcc,0xcb,0xff,0xca,0xce,0xcd,0xff,0xca, - 0xce,0xcc,0xff,0xc9,0xcd,0xcc,0xff,0xca,0xcd,0xcc,0xff,0xc7,0xcd,0xcc,0xff, - 0xc7,0xcc,0xca,0xff,0xc7,0xcc,0xc9,0xff,0xca,0xcf,0xcb,0xff,0xd2,0xd6,0xd3, - 0xff,0xd4,0xda,0xd6,0xff,0xc9,0xcf,0xcc,0xff,0xb7,0xb7,0xb5,0xff,0xc1,0xc0, - 0xbc,0xff,0xb2,0xb3,0xae,0xff,0xbe,0xbf,0xba,0xff,0xc5,0xc7,0xc2,0xff,0xcc, - 0xcc,0xc7,0xff,0xd1,0xd1,0xcc,0xff,0xd4,0xd4,0xcf,0xff,0xd4,0xd3,0xcf,0xff, - 0xd4,0xd5,0xcf,0xff,0xd5,0xd4,0xd2,0xff,0xd2,0xd1,0xce,0xff,0xce,0xcc,0xc8, - 0xff,0xc1,0xc2,0xbe,0xff,0xb8,0xb7,0xb2,0xff,0xa4,0xa6,0xa0,0xff,0x92,0x93, - 0x8c,0xff,0x70,0x71,0x6a,0xff,0x66,0x67,0x62,0xf1,0x6d,0x6b,0x69,0x61,0x00, - 0x00,0x00,0x1a,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x01, - 0x8c,0x00,0x00,0x00,0x00,0x8b,0xff,0xff,0xff,0x00,0x02,0xff,0xff,0xff,0xd3, - 0xff,0xff,0xff,0x2c,0x88,0xff,0xff,0xff,0x00,0x04,0x00,0x00,0x00,0x1a,0xff, - 0xff,0xff,0x1a,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x33,0x82,0xff,0xff,0xff, - 0x33,0x04,0x3b,0x3b,0x3b,0x85,0xc4,0xc4,0xc4,0x85,0xef,0xef,0xef,0x6a,0x00, - 0x00,0x00,0xff,0x87,0xff,0xff,0xff,0xff,0x05,0x00,0x00,0x00,0xff,0x00,0x00, - 0x00,0x1a,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x02,0x8c, - 0xff,0xff,0xff,0x00,0x3a,0xd2,0xd2,0xd2,0x00,0xbc,0xbc,0xbb,0x00,0xff,0xff, - 0xff,0x2c,0xff,0xff,0xff,0xd3,0xbc,0xbc,0xbb,0x00,0xc5,0xc5,0xc5,0x00,0xd2, - 0xd2,0xd2,0x00,0xe1,0xe1,0xe0,0x04,0xc9,0xca,0xc9,0x26,0xb4,0xb7,0xb5,0x56, - 0x9c,0x9e,0x9b,0xb6,0x82,0x82,0x7f,0xff,0xb2,0xb2,0xb0,0xff,0x86,0x86,0x86, - 0xff,0x34,0x35,0x31,0xff,0x74,0x77,0x73,0xff,0xe7,0xe7,0xe4,0xff,0xe4,0xe6, - 0xe5,0xff,0xcd,0xd1,0xd1,0xff,0xc8,0xce,0xcb,0xff,0xc7,0xcd,0xcc,0xff,0xc8, - 0xcc,0xcb,0xff,0xc9,0xcc,0xcb,0xff,0xc8,0xcc,0xcd,0xff,0xc8,0xcc,0xcb,0xff, - 0xc6,0xcc,0xcb,0xff,0xca,0xcd,0xd0,0xff,0xd5,0xd8,0xda,0xff,0xda,0xde,0xdd, - 0xff,0xd6,0xda,0xd5,0xff,0xbc,0xbf,0xbf,0xff,0x95,0x96,0x92,0xff,0x94,0x94, - 0x90,0xff,0xac,0xad,0xa7,0xff,0xb9,0xba,0xb5,0xff,0xc5,0xc6,0xc1,0xff,0xc9, - 0xca,0xc5,0xff,0xcf,0xd0,0xcb,0xff,0xd1,0xcf,0xcb,0xff,0xcf,0xce,0xca,0xff, - 0xd5,0xd4,0xd0,0xff,0xd6,0xd5,0xd1,0xff,0xdb,0xda,0xd6,0xff,0xdc,0xdb,0xd6, - 0xff,0xdd,0xdc,0xd9,0xff,0xdb,0xda,0xd5,0xff,0xd8,0xd8,0xd4,0xff,0xd1,0xd1, - 0xcd,0xff,0xc8,0xc8,0xc5,0xff,0xb4,0xb4,0xaf,0xff,0x9d,0x9d,0x98,0xff,0x7b, - 0x7d,0x75,0xff,0x6b,0x6b,0x67,0xf4,0x6a,0x69,0x66,0x61,0x00,0x00,0x00,0x1c, - 0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x01,0x8b,0x00,0x00, - 0x00,0x00,0x8b,0xff,0xff,0xff,0x00,0x02,0xff,0xff,0xff,0xeb,0xff,0xff,0xff, - 0x14,0x88,0xff,0xff,0xff,0x00,0x01,0x00,0x00,0x00,0x1a,0x82,0xff,0xff,0xff, - 0x1a,0x01,0x00,0x00,0x00,0x48,0x82,0xff,0xff,0xff,0x33,0x04,0x3b,0x3b,0x3b, - 0x85,0xff,0xff,0xff,0x85,0xbb,0xbb,0xbb,0x88,0x00,0x00,0x00,0xff,0x88,0xff, - 0xff,0xff,0xff,0x05,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x1a,0x00,0x00,0x00, - 0x0e,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x02,0x8b,0xff,0xff,0xff,0x00,0x2e, - 0xd1,0xd1,0xd1,0x00,0xbc,0xbc,0xbb,0x00,0xff,0xff,0xff,0x14,0xff,0xff,0xff, - 0xeb,0xbc,0xbc,0xbb,0x00,0xc5,0xc5,0xc5,0x00,0xbb,0xbb,0xba,0x00,0xd2,0xd2, - 0xd0,0x2b,0xad,0xad,0xad,0x75,0x7d,0x7f,0x7c,0x94,0x5c,0x5c,0x5c,0xc5,0x53, - 0x53,0x51,0xff,0x89,0x89,0x87,0xff,0xb3,0xb3,0xb3,0xff,0xa6,0xa6,0xa4,0xff, - 0x5c,0x5c,0x5a,0xff,0x57,0x56,0x56,0xff,0xa2,0xa2,0xa4,0xff,0xdf,0xe3,0xe2, - 0xff,0xe0,0xe5,0xe4,0xff,0xda,0xde,0xdd,0xff,0xd3,0xd7,0xd7,0xff,0xcf,0xd6, - 0xd3,0xff,0xd1,0xd6,0xd5,0xff,0xd8,0xdd,0xdc,0xff,0xd9,0xde,0xdf,0xff,0xd7, - 0xdc,0xda,0xff,0xb7,0xbb,0xba,0xff,0x97,0x9b,0x93,0xff,0x80,0x80,0x7c,0xff, - 0x7a,0x7c,0x76,0xff,0x98,0x98,0x93,0xff,0xb1,0xb1,0xae,0xff,0xbb,0xbc,0xb7, - 0xff,0xbd,0xbe,0xb9,0xff,0xc2,0xc3,0xbe,0xff,0xc6,0xc7,0xc2,0xff,0xc7,0xcb, - 0xc4,0xff,0xcf,0xce,0xca,0xff,0xd4,0xd3,0xcf,0xff,0xd7,0xd6,0xd2,0xff,0xda, - 0xd9,0xd5,0xff,0xd9,0xd8,0xd4,0xff,0xe0,0xdf,0xdb,0xff,0xdf,0xde,0xd9,0xff, - 0xdf,0xde,0xda,0xff,0x82,0xde,0xdd,0xd9,0xff,0x0b,0xdb,0xdb,0xd5,0xff,0xce, - 0xce,0xcb,0xff,0xbe,0xbd,0xb7,0xff,0xa6,0xa2,0xa0,0xff,0x7f,0x81,0x7a,0xff, - 0x74,0x74,0x6e,0xf1,0x66,0x64,0x62,0x5d,0x00,0x00,0x00,0x1c,0x00,0x00,0x00, - 0x0c,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x01,0x8a,0x00,0x00,0x00,0x00,0x82, - 0xff,0xff,0xff,0x00,0x89,0xff,0xff,0xff,0xb3,0x02,0xff,0xff,0xff,0xfe,0xff, - 0xff,0xff,0xb4,0x84,0xff,0xff,0xff,0xb3,0x01,0xff,0xff,0xff,0x05,0x83,0xff, - 0xff,0xff,0x00,0x01,0x00,0x00,0x00,0x1a,0x82,0xff,0xff,0xff,0x1a,0x07,0x4a, - 0x4a,0x4a,0x48,0xb5,0xb5,0xb5,0x48,0xff,0xff,0xff,0x33,0x3b,0x3b,0x3b,0x85, - 0xff,0xff,0xff,0x85,0xf3,0xf3,0xf3,0x88,0x00,0x00,0x00,0xff,0x89,0xff,0xff, - 0xff,0xff,0x05,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x0e, - 0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x02,0x87,0xff,0xff,0xff,0x00,0x01,0xff, - 0xff,0xff,0x05,0x84,0xff,0xff,0xff,0xb3,0x25,0xff,0xff,0xff,0xb4,0xff,0xff, - 0xff,0xfe,0xff,0xff,0xff,0xb3,0xfe,0xfe,0xfe,0xb4,0xf6,0xf6,0xf6,0xbb,0xd9, - 0xda,0xd9,0xcf,0xbd,0xbe,0xbc,0xdf,0xc5,0xc6,0xc4,0xe1,0x95,0x95,0x95,0xed, - 0x27,0x28,0x27,0xff,0x54,0x54,0x52,0xff,0x81,0x81,0x7f,0xff,0xae,0xb2,0xaf, - 0xff,0xb4,0xb5,0xb2,0xff,0x8c,0x8d,0x89,0xff,0x63,0x66,0x62,0xff,0x76,0x77, - 0x74,0xff,0xa2,0xa4,0xa0,0xff,0xb6,0xb8,0xb5,0xff,0xbd,0xbf,0xbc,0xff,0xc1, - 0xc3,0xbf,0xff,0xc0,0xc1,0xbf,0xff,0xb5,0xb4,0xb2,0xff,0x9d,0x9c,0x99,0xff, - 0x82,0x82,0x81,0xff,0x6d,0x70,0x6b,0xff,0x72,0x74,0x72,0xff,0x87,0x85,0x85, - 0xff,0x99,0x9b,0x95,0xff,0xa5,0xa9,0xa3,0xff,0xb1,0xb3,0xad,0xff,0xba,0xbb, - 0xb6,0xff,0xc0,0xc1,0xbc,0xff,0xc3,0xc4,0xbf,0xff,0xc9,0xc9,0xc4,0xff,0xcb, - 0xc9,0xc6,0xff,0xd1,0xd0,0xca,0xff,0x82,0xd4,0xd3,0xcf,0xff,0x03,0xdb,0xda, - 0xd6,0xff,0xdd,0xdc,0xd8,0xff,0xde,0xdd,0xd9,0xff,0x82,0xe1,0xe0,0xdc,0xff, - 0x0e,0xe4,0xe3,0xdf,0xff,0xe0,0xdf,0xdb,0xff,0xdf,0xde,0xda,0xff,0xdc,0xdd, - 0xd7,0xff,0xd3,0xd1,0xce,0xff,0xc2,0xc1,0xbc,0xff,0xa7,0xa7,0xa0,0xff,0x81, - 0x82,0x7b,0xff,0x7d,0x7b,0x78,0xeb,0x4b,0x4a,0x48,0x48,0x00,0x00,0x00,0x1a, - 0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x89,0x00,0x00, - 0x00,0x00,0x8b,0xff,0xff,0xff,0x00,0x02,0xff,0xff,0xff,0xfa,0xff,0xff,0xff, - 0x05,0x88,0xff,0xff,0xff,0x00,0x01,0x00,0x00,0x00,0x1a,0x82,0xff,0xff,0xff, - 0x1a,0x07,0x4a,0x4a,0x4a,0x48,0xff,0xff,0xff,0x48,0xb5,0xb5,0xb5,0x48,0x3b, - 0x3b,0x3b,0x85,0xff,0xff,0xff,0x85,0xf3,0xf3,0xf3,0x88,0x00,0x00,0x00,0xff, - 0x86,0xff,0xff,0xff,0xff,0x85,0x00,0x00,0x00,0xff,0x04,0x00,0x00,0x00,0x1a, - 0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x02,0x89,0xff,0xff, - 0xff,0x00,0x3d,0xd1,0xd1,0xd1,0x00,0xbc,0xbc,0xbb,0x00,0xff,0xff,0xff,0x05, - 0xff,0xff,0xff,0xfa,0xce,0xce,0xcd,0x04,0xaf,0xaf,0xaf,0x30,0x84,0x86,0x83, - 0x47,0x61,0x62,0x60,0x67,0x6b,0x6c,0x6a,0x92,0x94,0x95,0x93,0x97,0x91,0x91, - 0x91,0xa3,0x4c,0x4c,0x4c,0xe8,0x1b,0x1b,0x19,0xff,0x55,0x55,0x53,0xff,0x7c, - 0x7c,0x78,0xff,0xb2,0xb3,0xb0,0xff,0xb5,0xb8,0xb5,0xff,0xa5,0xa7,0xa4,0xff, - 0x96,0x98,0x95,0xff,0x86,0x87,0x84,0xff,0x87,0x86,0x85,0xff,0x84,0x83,0x82, - 0xff,0x87,0x86,0x83,0xff,0x85,0x85,0x83,0xff,0x7e,0x7c,0x7c,0xff,0x7e,0x7e, - 0x7c,0xff,0x88,0x89,0x86,0xff,0x86,0x8a,0x85,0xff,0x8a,0x8a,0x8a,0xff,0x91, - 0x92,0x8f,0xff,0x9d,0x9f,0x9b,0xff,0xa9,0xad,0xa8,0xff,0xb3,0xb5,0xaf,0xff, - 0xbc,0xbd,0xb8,0xff,0xc1,0xc2,0xbd,0xff,0xc4,0xc5,0xc0,0xff,0xc7,0xc8,0xc4, - 0xff,0xcb,0xca,0xc6,0xff,0xd0,0xcf,0xcb,0xff,0xd2,0xd1,0xcd,0xff,0xd7,0xd6, - 0xd2,0xff,0xd9,0xd8,0xd4,0xff,0xdd,0xdc,0xd8,0xff,0xde,0xdd,0xd9,0xff,0xe2, - 0xe1,0xdd,0xff,0xe4,0xe3,0xdf,0xff,0xe3,0xe2,0xde,0xff,0xe5,0xe4,0xe0,0xff, - 0xe7,0xe4,0xe0,0xff,0xe2,0xe1,0xdd,0xff,0xe1,0xe0,0xda,0xff,0xd7,0xd3,0xd0, - 0xff,0xc2,0xc1,0xbe,0xff,0xab,0xa9,0xa4,0xff,0x81,0x81,0x7c,0xff,0x83,0x82, - 0x7f,0xc7,0x10,0x10,0x0f,0x31,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x0a,0x00, - 0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x88,0x00,0x00,0x00,0x00,0x8b,0xff,0xff, - 0xff,0x00,0x02,0xff,0xff,0xff,0xeb,0xff,0xff,0xff,0x14,0x88,0xff,0xff,0xff, - 0x00,0x01,0x00,0x00,0x00,0x1a,0x82,0xff,0xff,0xff,0x1a,0x01,0x4a,0x4a,0x4a, - 0x48,0x82,0xff,0xff,0xff,0x48,0x04,0x36,0x36,0x36,0x91,0xff,0xff,0xff,0x85, - 0xf3,0xf3,0xf3,0x88,0x00,0x00,0x00,0xff,0x83,0xff,0xff,0xff,0xff,0x01,0x00, - 0x00,0x00,0xff,0x82,0xff,0xff,0xff,0xff,0x0a,0x00,0x00,0x00,0xff,0x00,0x00, - 0x00,0x5f,0x00,0x00,0x00,0x56,0x00,0x00,0x00,0x48,0x00,0x00,0x00,0x36,0x00, - 0x00,0x00,0x25,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x04, - 0x00,0x00,0x00,0x01,0x88,0xff,0xff,0xff,0x00,0x28,0xd1,0xd1,0xd1,0x00,0xbc, - 0xbc,0xbb,0x00,0xff,0xff,0xff,0x14,0xff,0xff,0xff,0xeb,0xb2,0xb2,0xb0,0x21, - 0x95,0x95,0x93,0x48,0xb7,0xb9,0xb7,0x48,0x90,0x91,0x91,0x62,0x38,0x39,0x37, - 0x92,0x5c,0x5d,0x5a,0x97,0x7a,0x7a,0x79,0xa0,0x73,0x74,0x72,0xbb,0x25,0x25, - 0x22,0xfd,0x21,0x22,0x22,0xff,0x53,0x53,0x52,0xff,0x7a,0x79,0x77,0xff,0xb4, - 0xb5,0xb3,0xff,0xbb,0xbe,0xba,0xff,0xa4,0xa6,0xa3,0xff,0xa1,0xa4,0xa1,0xff, - 0x9f,0x9f,0x9e,0xff,0x9c,0x9e,0x9b,0xff,0x97,0x9c,0x97,0xff,0x95,0x97,0x94, - 0xff,0x97,0x98,0x95,0xff,0x93,0x94,0x93,0xff,0x91,0x93,0x90,0xff,0x8f,0x90, - 0x8e,0xff,0x90,0x92,0x8f,0xff,0x93,0x95,0x93,0xff,0x9d,0x9f,0x9b,0xff,0xab, - 0xad,0xa8,0xff,0xb2,0xb4,0xae,0xff,0xb8,0xb9,0xb4,0xff,0xb9,0xba,0xb5,0xff, - 0xbf,0xc0,0xbb,0xff,0xc6,0xc5,0xc1,0xff,0xcc,0xc8,0xc6,0xff,0xcd,0xcc,0xc8, - 0xff,0xd3,0xd2,0xce,0xff,0x82,0xd6,0xd5,0xd1,0xff,0x13,0xdc,0xdb,0xd7,0xff, - 0xdf,0xde,0xda,0xff,0xe0,0xdf,0xdb,0xff,0xe2,0xe1,0xdd,0xff,0xe5,0xe4,0xdf, - 0xff,0xe6,0xe5,0xe3,0xff,0xe9,0xe9,0xe4,0xff,0xe7,0xe6,0xe2,0xff,0xe5,0xe4, - 0xe0,0xff,0xde,0xdf,0xdb,0xff,0xd4,0xd5,0xd1,0xff,0xc5,0xc3,0xc0,0xff,0xaa, - 0xac,0xa5,0xff,0x80,0x82,0x7d,0xff,0x7f,0x7e,0x7a,0xa3,0x00,0x00,0x00,0x29, - 0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x01,0x88,0x00,0x00, - 0x00,0x00,0x8b,0xff,0xff,0xff,0x00,0x02,0xff,0xff,0xff,0xd3,0xff,0xff,0xff, - 0x2c,0x88,0xff,0xff,0xff,0x00,0x01,0x00,0x00,0x00,0x1a,0x82,0xff,0xff,0xff, - 0x1a,0x01,0x4a,0x4a,0x4a,0x48,0x82,0xff,0xff,0xff,0x48,0x04,0x4c,0x4c,0x4c, - 0x91,0xe9,0xe9,0xe9,0x91,0xf3,0xf3,0xf3,0x88,0x00,0x00,0x00,0xff,0x82,0xff, - 0xff,0xff,0xff,0x82,0x00,0x00,0x00,0xff,0x82,0xff,0xff,0xff,0xff,0x0a,0x00, - 0x00,0x00,0xff,0x00,0x00,0x00,0xa0,0x00,0x00,0x00,0x59,0x00,0x00,0x00,0x4c, - 0x00,0x00,0x00,0x3c,0x00,0x00,0x00,0x2b,0x00,0x00,0x00,0x1a,0x00,0x00,0x00, - 0x0d,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x01,0x88,0xff,0xff,0xff,0x00,0x27, - 0xd2,0xd2,0xd2,0x00,0xbc,0xbc,0xbb,0x00,0xfc,0xfc,0xfc,0x2e,0xfc,0xfc,0xfc, - 0xd4,0x8f,0x91,0x8f,0x2d,0x7c,0x7d,0x7b,0x48,0x9b,0x9c,0x9b,0x48,0xb8,0xb8, - 0xb8,0x49,0x6f,0x70,0x6f,0x7d,0x35,0x35,0x33,0x95,0x4f,0x4f,0x4e,0x9d,0x5e, - 0x5e,0x5c,0xa9,0x5d,0x5d,0x5b,0xd8,0x13,0x14,0x13,0xff,0x27,0x27,0x27,0xff, - 0x59,0x59,0x59,0xff,0x7c,0x7c,0x7b,0xff,0xba,0xb9,0xb8,0xff,0xc3,0xc6,0xc2, - 0xff,0xad,0xae,0xac,0xff,0xa5,0xa9,0xa4,0xff,0xa2,0xa4,0xa1,0xff,0x9f,0xa2, - 0x9f,0xff,0x9e,0xa2,0x9d,0xff,0x9f,0xa1,0x9f,0xff,0x9c,0xa0,0x9b,0xff,0x99, - 0x9b,0x98,0xff,0x96,0x98,0x95,0xff,0x93,0x95,0x92,0xff,0x96,0x98,0x93,0xff, - 0x9c,0x9e,0x9a,0xff,0xa5,0xa7,0xa2,0xff,0xaf,0xb2,0xab,0xff,0xb5,0xb4,0xaf, - 0xff,0xb9,0xba,0xb5,0xff,0xba,0xbc,0xb6,0xff,0xbe,0xbe,0xba,0xff,0xc5,0xc5, - 0xc1,0xff,0xca,0xca,0xc5,0xff,0x82,0xd0,0xd0,0xcc,0xff,0x04,0xd7,0xd6,0xd2, - 0xff,0xd8,0xd7,0xd3,0xff,0xde,0xdd,0xd9,0xff,0xe0,0xdf,0xdb,0xff,0x82,0xe2, - 0xe1,0xdd,0xff,0x0f,0xe8,0xe7,0xe2,0xff,0xec,0xeb,0xe6,0xff,0xea,0xe9,0xe7, - 0xff,0xe6,0xe5,0xe2,0xff,0xe5,0xe3,0xe1,0xff,0xe0,0xde,0xda,0xff,0xd5,0xd6, - 0xd0,0xff,0xc6,0xc4,0xc1,0xff,0xa2,0xa3,0x9d,0xff,0x87,0x87,0x82,0xf9,0x53, - 0x51,0x4f,0x5e,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x05, - 0x00,0x00,0x00,0x01,0x87,0x00,0x00,0x00,0x00,0x8b,0xff,0xff,0xff,0x00,0x02, - 0xff,0xff,0xff,0xb4,0xff,0xff,0xff,0x4b,0x88,0xff,0xff,0xff,0x00,0x01,0x00, - 0x00,0x00,0x1a,0x82,0xff,0xff,0xff,0x1a,0x01,0x4a,0x4a,0x4a,0x48,0x82,0xff, - 0xff,0xff,0x48,0x09,0x4c,0x4c,0x4c,0x91,0xff,0xff,0xff,0x91,0xe0,0xe0,0xe0, - 0x94,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0x86,0x86, - 0x86,0xac,0x00,0x00,0x00,0xb2,0x00,0x00,0x00,0xff,0x82,0xff,0xff,0xff,0xff, - 0x09,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x56,0x00,0x00,0x00,0x4a,0x00,0x00, - 0x00,0x3a,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x0e,0x00, - 0x00,0x00,0x06,0x00,0x00,0x00,0x02,0x88,0xff,0xff,0xff,0x00,0x21,0xbb,0xbb, - 0xba,0x00,0xbc,0xbc,0xbb,0x01,0xec,0xec,0xec,0x57,0xeb,0xeb,0xea,0xbc,0x56, - 0x55,0x54,0x2a,0x49,0x4b,0x49,0x48,0x80,0x81,0x7f,0x48,0x98,0x99,0x97,0x48, - 0xa3,0xa5,0xa3,0x56,0x4e,0x4f,0x4c,0x91,0x3e,0x40,0x3f,0x99,0x48,0x49,0x48, - 0xa3,0x4c,0x4c,0x4a,0xb0,0x44,0x42,0x42,0xea,0x13,0x12,0x15,0xff,0x2a,0x2a, - 0x2b,0xff,0x5b,0x5b,0x5b,0xff,0x7f,0x7f,0x7c,0xff,0xbe,0xbf,0xbc,0xff,0xcd, - 0xd0,0xcc,0xff,0xb6,0xb6,0xb5,0xff,0xab,0xae,0xab,0xff,0xab,0xb0,0xad,0xff, - 0xaa,0xaf,0xaa,0xff,0xac,0xad,0xaa,0xff,0xa7,0xa8,0xa5,0xff,0xa1,0xa3,0xa0, - 0xff,0x9e,0xa0,0x9d,0xff,0x95,0x97,0x97,0xff,0x94,0x96,0x92,0xff,0x9a,0x9c, - 0x96,0xff,0x9f,0xa1,0x9e,0xff,0xaa,0xad,0xa8,0xff,0x82,0xb2,0xb3,0xae,0xff, - 0x07,0xbb,0xbc,0xb7,0xff,0xbc,0xbd,0xb8,0xff,0xc3,0xc4,0xbf,0xff,0xc5,0xc7, - 0xc2,0xff,0xcd,0xcf,0xc9,0xff,0xcf,0xcf,0xcb,0xff,0xd4,0xd3,0xcf,0xff,0x82, - 0xda,0xd9,0xd5,0xff,0x12,0xe0,0xdf,0xdb,0xff,0xe6,0xe5,0xe1,0xff,0xe3,0xe2, - 0xde,0xff,0xe7,0xe6,0xe2,0xff,0xeb,0xea,0xe7,0xff,0xe9,0xe8,0xe6,0xff,0xea, - 0xe9,0xe7,0xff,0xe8,0xe7,0xe5,0xff,0xe7,0xe6,0xe1,0xff,0xe0,0xdf,0xdb,0xff, - 0xd8,0xd5,0xd1,0xff,0xc5,0xc2,0xbd,0xff,0x98,0x99,0x92,0xff,0x84,0x84,0x81, - 0xd0,0x0a,0x0a,0x0a,0x36,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x0a,0x00,0x00, - 0x00,0x03,0x87,0x00,0x00,0x00,0x00,0x8b,0xff,0xff,0xff,0x00,0x02,0xff,0xff, - 0xff,0x8e,0xff,0xff,0xff,0x71,0x88,0xff,0xff,0xff,0x00,0x01,0x00,0x00,0x00, - 0x1a,0x82,0xff,0xff,0xff,0x1a,0x01,0x4a,0x4a,0x4a,0x48,0x82,0xff,0xff,0xff, - 0x48,0x03,0x4c,0x4c,0x4c,0x91,0xff,0xff,0xff,0x91,0xf4,0xf4,0xf4,0x94,0x82, - 0x00,0x00,0x00,0xff,0x04,0x79,0x79,0x79,0xa3,0x6a,0x6a,0x6a,0xaa,0x00,0x00, - 0x00,0xaf,0x00,0x00,0x00,0xff,0x82,0xff,0xff,0xff,0xff,0x09,0x00,0x00,0x00, - 0xff,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x41,0x00,0x00,0x00,0x30,0x00,0x00, - 0x00,0x20,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x04,0x00, - 0x00,0x00,0x01,0x88,0xff,0xff,0xff,0x00,0x2c,0x97,0x97,0x96,0x00,0x8a,0x8a, - 0x89,0x08,0xe6,0xe6,0xe5,0x7f,0xf2,0xf2,0xf2,0x9a,0x86,0x86,0x86,0x1b,0x3e, - 0x3e,0x3d,0x3a,0x35,0x36,0x33,0x48,0x7f,0x7f,0x7d,0x48,0x9a,0x9b,0x98,0x48, - 0x8c,0x8d,0x8b,0x6a,0x40,0x42,0x40,0x95,0x43,0x43,0x43,0x9c,0x4d,0x4d,0x4d, - 0xa7,0x4d,0x4d,0x4d,0xbd,0x30,0x30,0x30,0xf6,0x1e,0x1e,0x20,0xff,0x31,0x31, - 0x30,0xff,0x60,0x60,0x5e,0xff,0x84,0x84,0x83,0xff,0xbe,0xbf,0xbb,0xff,0xd4, - 0xd6,0xd4,0xff,0xb9,0xba,0xb9,0xff,0xb4,0xb6,0xb2,0xff,0xb5,0xb6,0xb5,0xff, - 0xb5,0xb7,0xb6,0xff,0xb0,0xb2,0xb2,0xff,0xa9,0xab,0xa9,0xff,0xa3,0xa5,0xa2, - 0xff,0x9d,0x9f,0x9a,0xff,0x97,0x99,0x96,0xff,0x98,0x9a,0x94,0xff,0x9a,0x9e, - 0x99,0xff,0xa0,0xa2,0x9d,0xff,0xab,0xaa,0xa5,0xff,0xaf,0xb1,0xac,0xff,0xb4, - 0xb5,0xb0,0xff,0xb8,0xb9,0xb4,0xff,0xbf,0xc0,0xbb,0xff,0xc4,0xc5,0xc0,0xff, - 0xc7,0xc8,0xc3,0xff,0xcc,0xca,0xc7,0xff,0xd1,0xd0,0xcc,0xff,0xd9,0xd8,0xd4, - 0xff,0xda,0xd9,0xd5,0xff,0x82,0xe2,0xe1,0xdd,0xff,0x03,0xe6,0xe5,0xe1,0xff, - 0xe9,0xe8,0xe3,0xff,0xeb,0xea,0xe9,0xff,0x82,0xea,0xe9,0xe7,0xff,0x0c,0xec, - 0xeb,0xea,0xff,0xe5,0xe4,0xe1,0xff,0xe2,0xdf,0xdb,0xff,0xdf,0xdd,0xdb,0xff, - 0xd7,0xd3,0xcf,0xff,0xbe,0xba,0xb5,0xff,0x8f,0x90,0x89,0xff,0x67,0x65,0x62, - 0x83,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x06,0x00,0x00, - 0x00,0x01,0x86,0x00,0x00,0x00,0x00,0x8b,0xff,0xff,0xff,0x00,0x02,0xff,0xff, - 0xff,0x61,0xff,0xff,0xff,0x9e,0x88,0xff,0xff,0xff,0x00,0x01,0x00,0x00,0x00, - 0x1a,0x82,0xff,0xff,0xff,0x1a,0x01,0x4a,0x4a,0x4a,0x48,0x82,0xff,0xff,0xff, - 0x48,0x0a,0x4c,0x4c,0x4c,0x91,0xc9,0xc9,0xc9,0x91,0x49,0x49,0x49,0x94,0x00, - 0x00,0x00,0xff,0x00,0x00,0x00,0x62,0x00,0x00,0x00,0x89,0x83,0x83,0x83,0x90, - 0x7b,0x7b,0x7b,0x94,0x00,0x00,0x00,0x96,0x00,0x00,0x00,0xff,0x82,0xff,0xff, - 0xff,0xff,0x08,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x3c,0x00,0x00,0x00,0x2a, - 0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x06,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x01,0x88,0xff,0xff,0xff,0x00,0x40,0x7c,0x7c,0x7c,0x00, - 0x63,0x64,0x64,0x0b,0xe5,0xe5,0xe4,0xa8,0xe4,0xe4,0xe3,0x71,0xb3,0xb3,0xb3, - 0x1a,0x97,0x96,0x95,0x22,0x35,0x35,0x32,0x46,0x31,0x31,0x31,0x48,0x6a,0x6a, - 0x6a,0x48,0x94,0x95,0x93,0x49,0x74,0x74,0x73,0x7c,0x41,0x41,0x42,0x97,0x42, - 0x42,0x42,0x9f,0x4b,0x4b,0x4b,0xab,0x4e,0x4e,0x4d,0xcb,0x20,0x1f,0x1f,0xfe, - 0x2a,0x2a,0x2f,0xff,0x33,0x33,0x33,0xff,0x69,0x69,0x6b,0xff,0x89,0x8c,0x87, - 0xff,0xc6,0xca,0xc9,0xff,0xd3,0xd5,0xd3,0xff,0xbe,0xbf,0xbc,0xff,0xbb,0xbd, - 0xb9,0xff,0xba,0xbc,0xbb,0xff,0xb7,0xb9,0xb8,0xff,0xb2,0xb4,0xb2,0xff,0xad, - 0xaf,0xab,0xff,0xa1,0xa3,0xa1,0xff,0x97,0x99,0x94,0xff,0x92,0x94,0x92,0xff, - 0x96,0x98,0x93,0xff,0x9b,0x9c,0x97,0xff,0xa5,0xa6,0xa1,0xff,0xab,0xac,0xa7, - 0xff,0xaf,0xb0,0xab,0xff,0xb3,0xb4,0xaf,0xff,0xb8,0xb9,0xb2,0xff,0xbb,0xbc, - 0xb7,0xff,0xc4,0xc6,0xbf,0xff,0xc8,0xc8,0xc3,0xff,0xcf,0xce,0xca,0xff,0xd2, - 0xd1,0xcd,0xff,0xda,0xd9,0xd5,0xff,0xdc,0xdb,0xd7,0xff,0xde,0xdd,0xd9,0xff, - 0xe5,0xe4,0xe0,0xff,0xea,0xe9,0xe4,0xff,0xe9,0xe8,0xe5,0xff,0xed,0xec,0xea, - 0xff,0xea,0xe9,0xe8,0xff,0xeb,0xea,0xe7,0xff,0xe5,0xe6,0xe2,0xff,0xe0,0xdf, - 0xdb,0xff,0xe4,0xe3,0xdf,0xff,0xdb,0xdb,0xd7,0xff,0xd1,0xcd,0xc8,0xff,0xab, - 0xa8,0xa4,0xff,0x88,0x88,0x83,0xe2,0x26,0x25,0x24,0x49,0x00,0x00,0x00,0x20, - 0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x85,0x00,0x00, - 0x00,0x00,0x8b,0xff,0xff,0xff,0x00,0x03,0xff,0xff,0xff,0x2f,0xff,0xff,0xff, - 0xc9,0xff,0xff,0xff,0x08,0x87,0xff,0xff,0xff,0x00,0x01,0x00,0x00,0x00,0x1a, - 0x82,0xff,0xff,0xff,0x1a,0x01,0x4a,0x4a,0x4a,0x48,0x82,0xff,0xff,0xff,0x48, - 0x0a,0x16,0x16,0x16,0x91,0x00,0x00,0x00,0x91,0xa8,0xa8,0xa8,0x4c,0x91,0x91, - 0x91,0x54,0x00,0x00,0x00,0x5f,0x00,0x00,0x00,0x91,0x93,0x93,0x93,0x89,0x90, - 0x90,0x90,0x8a,0x00,0x00,0x00,0x8b,0x00,0x00,0x00,0xff,0x82,0xff,0xff,0xff, - 0xff,0x07,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x3c,0x00,0x00,0x00,0x29,0x00, - 0x00,0x00,0x17,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x01, - 0x89,0xff,0xff,0xff,0x00,0x40,0xa8,0xa9,0xa8,0x00,0xbc,0xbc,0xbc,0x11,0xe4, - 0xe4,0xe4,0xcf,0xbd,0xbd,0xbd,0x44,0x81,0x81,0x7f,0x1a,0xae,0xb2,0xaf,0x1a, - 0x89,0x89,0x87,0x2e,0x36,0x37,0x35,0x48,0x38,0x39,0x38,0x48,0x61,0x62,0x61, - 0x48,0x82,0x82,0x80,0x50,0x5a,0x5a,0x59,0x89,0x45,0x45,0x46,0x99,0x42,0x42, - 0x41,0xa2,0x47,0x48,0x46,0xaf,0x44,0x43,0x43,0xdb,0x1d,0x1d,0x20,0xff,0x31, - 0x31,0x33,0xff,0x35,0x35,0x38,0xff,0x70,0x6f,0x6e,0xff,0x8f,0x90,0x8a,0xff, - 0xc9,0xcb,0xca,0xff,0xc9,0xce,0xcc,0xff,0xbf,0xc3,0xc2,0xff,0xbe,0xc0,0xc1, - 0xff,0xbd,0xbf,0xbe,0xff,0xb7,0xb9,0xb7,0xff,0xb3,0xb5,0xb2,0xff,0xa4,0xa6, - 0xa3,0xff,0x94,0x96,0x94,0xff,0x92,0x94,0x8e,0xff,0x96,0x98,0x93,0xff,0x96, - 0x97,0x92,0xff,0x9b,0x9c,0x97,0xff,0xa3,0xa4,0x9f,0xff,0xac,0xad,0xa8,0xff, - 0xae,0xaf,0xa9,0xff,0xb3,0xb4,0xae,0xff,0xb9,0xba,0xb3,0xff,0xbb,0xbc,0xb6, - 0xff,0xc4,0xc4,0xbf,0xff,0xcc,0xca,0xc7,0xff,0xcc,0xcb,0xc7,0xff,0xd1,0xd0, - 0xcc,0xff,0xda,0xd9,0xd5,0xff,0xdd,0xdc,0xd8,0xff,0xe1,0xe0,0xdc,0xff,0xe5, - 0xe4,0xe0,0xff,0xe7,0xe6,0xe2,0xff,0xea,0xe9,0xe4,0xff,0xe9,0xe8,0xe3,0xff, - 0xe9,0xe8,0xe4,0xff,0xec,0xeb,0xe7,0xff,0xe8,0xe7,0xe3,0xff,0xe1,0xe1,0xdb, - 0xff,0xe1,0xe2,0xdc,0xff,0xda,0xd8,0xd5,0xff,0xc9,0xc3,0xbe,0xff,0x92,0x94, - 0x8d,0xff,0x70,0x6f,0x6b,0xa1,0x00,0x00,0x00,0x2f,0x00,0x00,0x00,0x16,0x00, - 0x00,0x00,0x08,0x00,0x00,0x00,0x01,0x85,0x00,0x00,0x00,0x00,0x8c,0xff,0xff, - 0xff,0x00,0x02,0xff,0xff,0xff,0xb7,0xff,0xff,0xff,0x48,0x87,0xff,0xff,0xff, - 0x00,0x01,0x00,0x00,0x00,0x1a,0x82,0xff,0xff,0xff,0x1a,0x0e,0x4a,0x4a,0x4a, - 0x48,0xb5,0xb5,0xb5,0x48,0x4a,0x4a,0x4a,0x48,0x23,0x23,0x23,0x76,0x00,0x00, - 0x00,0x1a,0x00,0x00,0x00,0x37,0xc6,0xc6,0xc6,0x3e,0xa3,0xa3,0xa3,0x48,0x00, - 0x00,0x00,0x52,0x00,0x00,0x00,0x7f,0xab,0xab,0xab,0x7f,0xa9,0xa9,0xa9,0x80, - 0x00,0x00,0x00,0x83,0x00,0x00,0x00,0xff,0x82,0xff,0xff,0xff,0xff,0x05,0x00, - 0x00,0x00,0xff,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x0b, - 0x00,0x00,0x00,0x03,0x8a,0xff,0xff,0xff,0x00,0x40,0x8b,0x8b,0x8b,0x00,0xfe, - 0xfe,0xfe,0x49,0xec,0xec,0xec,0xbc,0x1b,0x1b,0x19,0x1a,0x55,0x55,0x53,0x1a, - 0x7c,0x7c,0x78,0x1a,0xb2,0xb3,0xb0,0x1a,0x6d,0x6d,0x6c,0x39,0x3d,0x3d,0x3e, - 0x48,0x49,0x4a,0x4a,0x48,0x67,0x68,0x67,0x48,0x74,0x74,0x72,0x5c,0x45,0x45, - 0x44,0x94,0x48,0x48,0x4b,0x9b,0x3d,0x3d,0x3d,0xa5,0x44,0x44,0x45,0xb3,0x38, - 0x37,0x37,0xeb,0x21,0x21,0x23,0xff,0x34,0x34,0x37,0xff,0x3e,0x3e,0x3e,0xff, - 0x77,0x77,0x72,0xff,0x96,0x99,0x95,0xff,0xc9,0xcc,0xcb,0xff,0xcd,0xcf,0xce, - 0xff,0xc4,0xc6,0xc5,0xff,0xc5,0xc7,0xc7,0xff,0xbe,0xc0,0xbe,0xff,0xb7,0xb9, - 0xb6,0xff,0xa6,0xa8,0xa5,0xff,0x97,0x99,0x94,0xff,0x8c,0x8e,0x89,0xff,0x8e, - 0x90,0x8b,0xff,0x94,0x95,0x90,0xff,0x97,0x98,0x93,0xff,0x9e,0x9f,0x9a,0xff, - 0xa2,0xa3,0x9c,0xff,0xa6,0xa7,0xa2,0xff,0xaf,0xb0,0xa8,0xff,0xb3,0xb4,0xaf, - 0xff,0xb9,0xba,0xb4,0xff,0xba,0xbb,0xb5,0xff,0xc5,0xc4,0xc0,0xff,0xc9,0xc8, - 0xc4,0xff,0xce,0xcd,0xc9,0xff,0xd6,0xd5,0xd1,0xff,0xdb,0xda,0xd6,0xff,0xe0, - 0xdf,0xdb,0xff,0xe6,0xe5,0xe1,0xff,0xe5,0xe4,0xe0,0xff,0xe7,0xe6,0xe2,0xff, - 0xea,0xe9,0xe5,0xff,0xe7,0xe6,0xe2,0xff,0xeb,0xea,0xe6,0xff,0xec,0xea,0xe6, - 0xff,0xe8,0xe9,0xe5,0xff,0xe4,0xe4,0xe1,0xff,0xdf,0xde,0xda,0xff,0xd9,0xd4, - 0xcf,0xff,0xb0,0xae,0xa8,0xff,0x85,0x85,0x80,0xde,0x05,0x05,0x05,0x41,0x00, - 0x00,0x00,0x21,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x03,0x85,0x00,0x00,0x00, - 0x00,0x8c,0xff,0xff,0xff,0x00,0x02,0xff,0xff,0xff,0x74,0xff,0xff,0xff,0x8b, - 0x87,0xff,0xff,0xff,0x00,0x01,0x00,0x00,0x00,0x1a,0x82,0xff,0xff,0xff,0x1a, - 0x82,0x00,0x00,0x00,0x48,0x82,0xff,0xff,0xff,0x1a,0x0a,0x00,0x00,0x00,0x1a, - 0x00,0x00,0x00,0x36,0xd3,0xd3,0xd3,0x3b,0xba,0xba,0xba,0x41,0x00,0x00,0x00, - 0x48,0x00,0x00,0x00,0x76,0xcb,0xcb,0xcb,0x74,0xc8,0xc8,0xc8,0x75,0x00,0x00, - 0x00,0x79,0x00,0x00,0x00,0xff,0x82,0xff,0xff,0xff,0xff,0x06,0x00,0x00,0x00, - 0xff,0x00,0x00,0x00,0x37,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x12,0x00,0x00, - 0x00,0x06,0x00,0x00,0x00,0x01,0x89,0xff,0xff,0xff,0x00,0x41,0x79,0x78,0x77, - 0x00,0xff,0xff,0xff,0x8b,0xfa,0xfa,0xfa,0x76,0x24,0x24,0x21,0x19,0x21,0x22, - 0x22,0x1a,0x53,0x53,0x52,0x1a,0x7a,0x79,0x77,0x1a,0xa9,0xaa,0xa8,0x1e,0x5d, - 0x5e,0x5d,0x41,0x45,0x45,0x46,0x48,0x52,0x52,0x51,0x48,0x71,0x71,0x70,0x49, - 0x6a,0x69,0x68,0x6b,0x44,0x44,0x44,0x96,0x4c,0x4d,0x4d,0x9d,0x3c,0x3d,0x3d, - 0xa9,0x49,0x48,0x48,0xbe,0x30,0x2f,0x2f,0xf7,0x29,0x2b,0x2c,0xff,0x36,0x34, - 0x39,0xff,0x46,0x44,0x46,0xff,0x7f,0x7f,0x7e,0xff,0xa7,0xa8,0xa4,0xff,0xd2, - 0xd1,0xce,0xff,0xcc,0xcc,0xcc,0xff,0xc8,0xca,0xc8,0xff,0xc3,0xc5,0xc2,0xff, - 0xbb,0xbd,0xba,0xff,0xa7,0xa9,0xa6,0xff,0x98,0x9a,0x98,0xff,0x8b,0x8d,0x87, - 0xff,0x87,0x89,0x83,0xff,0x8a,0x8d,0x87,0xff,0x8e,0x8f,0x8a,0xff,0x95,0x96, - 0x91,0xff,0x9b,0x9c,0x98,0xff,0xa2,0xa3,0x9c,0xff,0xa7,0xa8,0xa2,0xff,0xae, - 0xaf,0xa8,0xff,0xb2,0xb3,0xac,0xff,0xb9,0xb7,0xb3,0xff,0xbc,0xbb,0xb7,0xff, - 0xc5,0xc4,0xc0,0xff,0xc9,0xc8,0xc4,0xff,0xcd,0xcc,0xc8,0xff,0xd6,0xd5,0xd1, - 0xff,0xd9,0xd8,0xd4,0xff,0xe1,0xe0,0xdc,0xff,0xe5,0xe4,0xe0,0xff,0xe8,0xe7, - 0xe3,0xff,0xea,0xe9,0xe5,0xff,0xe8,0xe7,0xe3,0xff,0xea,0xe8,0xe4,0xff,0xe9, - 0xea,0xe6,0xff,0xe9,0xe9,0xe5,0xff,0xe6,0xe9,0xe3,0xff,0xe4,0xdf,0xdc,0xff, - 0xd9,0xd7,0xd1,0xff,0xc8,0xc2,0xbd,0xff,0x97,0x95,0x91,0xfd,0x44,0x43,0x40, - 0x75,0x00,0x00,0x00,0x2f,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x06,0x00,0x00, - 0x00,0x01,0x84,0x00,0x00,0x00,0x00,0x8c,0xff,0xff,0xff,0x00,0x03,0xff,0xff, - 0xff,0x2c,0xff,0xff,0xff,0xb9,0xff,0xff,0xff,0x20,0x86,0xff,0xff,0xff,0x00, - 0x06,0x00,0x00,0x00,0x1a,0xff,0xff,0xff,0x1a,0x00,0x00,0x00,0x1a,0x00,0x00, - 0x00,0x33,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x1a,0x82,0xff,0xff,0xff,0x1a, - 0x09,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x38,0xd3,0xd3,0xd3,0x3b,0xc6,0xc6, - 0xc6,0x3e,0x00,0x00,0x00,0x3d,0x00,0x00,0x00,0x6d,0xe4,0xe4,0xe4,0x6d,0xd2, - 0xd2,0xd2,0x72,0x00,0x00,0x00,0x7c,0x82,0x00,0x00,0x00,0xff,0x06,0x00,0x00, - 0x00,0x48,0x00,0x00,0x00,0x3e,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x17,0x00, - 0x00,0x00,0x09,0x00,0x00,0x00,0x02,0x89,0xff,0xff,0xff,0x00,0x41,0xff,0xff, - 0xff,0x20,0xff,0xff,0xff,0xb9,0xff,0xff,0xff,0x2c,0x5b,0x5a,0x58,0x0b,0x13, - 0x14,0x13,0x1a,0x27,0x27,0x27,0x1a,0x59,0x59,0x59,0x1a,0x7c,0x7c,0x7b,0x1a, - 0x9d,0x9d,0x9b,0x25,0x4f,0x4f,0x4e,0x47,0x50,0x50,0x53,0x48,0x54,0x55,0x54, - 0x48,0x79,0x79,0x7a,0x49,0x59,0x59,0x58,0x7d,0x47,0x48,0x49,0x97,0x4a,0x4b, - 0x4c,0xa0,0x3e,0x3e,0x3e,0xac,0x4b,0x4c,0x4a,0xcb,0x21,0x22,0x22,0xff,0x35, - 0x35,0x38,0xff,0x39,0x39,0x3a,0xff,0x4f,0x50,0x4f,0xff,0x8b,0x8b,0x8a,0xff, - 0xb2,0xb2,0xb0,0xff,0xd4,0xd7,0xd3,0xff,0xcc,0xce,0xca,0xff,0xca,0xcc,0xc9, - 0xff,0xbc,0xbf,0xbb,0xff,0xae,0xae,0xad,0xff,0x99,0x9a,0x97,0xff,0x8b,0x8a, - 0x89,0xff,0x87,0x86,0x85,0xff,0x83,0x84,0x82,0xff,0x87,0x8a,0x83,0xff,0x8c, - 0x8d,0x88,0xff,0x95,0x96,0x91,0xff,0x99,0x9a,0x95,0xff,0xa1,0xa2,0x9d,0xff, - 0xa6,0xa7,0xa2,0xff,0xaf,0xb0,0xab,0xff,0xb4,0xb4,0xb1,0xff,0xb8,0xb7,0xb3, - 0xff,0xbe,0xbd,0xb9,0xff,0xc2,0xc1,0xbd,0xff,0xcb,0xca,0xc6,0xff,0xd0,0xcf, - 0xcb,0xff,0xd8,0xd7,0xd3,0xff,0xdc,0xdb,0xd7,0xff,0xe2,0xe1,0xdd,0xff,0xe4, - 0xe3,0xdf,0xff,0xe8,0xe7,0xe3,0xff,0xe9,0xe8,0xe4,0xff,0xe6,0xe5,0xe1,0xff, - 0xea,0xe8,0xe4,0xff,0xea,0xe9,0xe5,0xff,0xe7,0xe6,0xe2,0xff,0xe3,0xe0,0xdc, - 0xff,0xdc,0xd7,0xd4,0xff,0xd0,0xcb,0xc8,0xff,0xa5,0xa2,0x9d,0xff,0x73,0x70, - 0x6c,0xbf,0x00,0x00,0x00,0x3d,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x0b,0x00, - 0x00,0x00,0x02,0x84,0x00,0x00,0x00,0x00,0x8d,0xff,0xff,0xff,0x00,0x02,0xff, - 0xff,0xff,0x8c,0xff,0xff,0xff,0x73,0x86,0xff,0xff,0xff,0x00,0x82,0x00,0x00, - 0x00,0x1a,0x83,0xff,0xff,0xff,0x00,0x01,0x00,0x00,0x00,0x1a,0x82,0xff,0xff, - 0xff,0x1a,0x11,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x35,0xed,0xed,0xed,0x36, - 0xe3,0xe3,0xe3,0x38,0x00,0x00,0x00,0x37,0x00,0x00,0x00,0x68,0xf3,0xf3,0xf3, - 0x69,0xe4,0xe4,0xe4,0x6d,0x00,0x00,0x00,0x75,0x00,0x00,0x00,0x2c,0x00,0x00, - 0x00,0x3c,0x00,0x00,0x00,0x43,0x00,0x00,0x00,0x3d,0x00,0x00,0x00,0x2d,0x00, - 0x00,0x00,0x1a,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x03,0x89,0xff,0xff,0xff, - 0x00,0x42,0xff,0xff,0xff,0x73,0xff,0xff,0xff,0x8c,0x6c,0x6b,0x6a,0x00,0x70, - 0x6d,0x6c,0x00,0x40,0x3e,0x3e,0x12,0x13,0x12,0x15,0x1a,0x2a,0x2a,0x2b,0x1a, - 0x5b,0x5b,0x5b,0x1a,0x7f,0x7f,0x7c,0x1a,0x83,0x83,0x81,0x2f,0x50,0x51,0x52, - 0x48,0x58,0x58,0x59,0x48,0x57,0x58,0x59,0x48,0x7c,0x7d,0x7b,0x51,0x4f,0x4f, - 0x4d,0x8a,0x4c,0x4d,0x4d,0x99,0x47,0x47,0x49,0xa3,0x3e,0x3e,0x3e,0xaf,0x4a, - 0x4a,0x49,0xdb,0x21,0x21,0x21,0xff,0x39,0x3b,0x3d,0xff,0x37,0x34,0x37,0xff, - 0x5e,0x5f,0x5c,0xff,0x96,0x96,0x94,0xff,0xbf,0xc2,0xbf,0xff,0xd3,0xd6,0xd2, - 0xff,0xcc,0xce,0xcb,0xff,0xc4,0xc6,0xc3,0xff,0xb1,0xb4,0xb1,0xff,0xa0,0xa1, - 0x9d,0xff,0x8a,0x89,0x87,0xff,0x80,0x82,0x80,0xff,0x80,0x81,0x7c,0xff,0x83, - 0x84,0x7f,0xff,0x85,0x86,0x81,0xff,0x8d,0x8e,0x89,0xff,0x8f,0x90,0x8b,0xff, - 0x95,0x96,0x91,0xff,0x9f,0xa0,0x9b,0xff,0xa2,0xa3,0xa0,0xff,0xab,0xab,0xa5, - 0xff,0xb2,0xb1,0xad,0xff,0xb8,0xb7,0xb3,0xff,0xbe,0xbd,0xb9,0xff,0xc4,0xc3, - 0xbf,0xff,0xcb,0xca,0xc6,0xff,0xd3,0xd2,0xce,0xff,0xdb,0xda,0xd6,0xff,0xdc, - 0xdb,0xd7,0xff,0xe2,0xe1,0xdd,0xff,0xe6,0xe5,0xe1,0xff,0xe7,0xe6,0xe2,0xff, - 0xe4,0xe3,0xdf,0xff,0xec,0xeb,0xe7,0xff,0xe9,0xe8,0xe4,0xff,0xe3,0xe2,0xde, - 0xff,0xe4,0xe1,0xdd,0xff,0xe1,0xdf,0xd9,0xff,0xd4,0xd2,0xcc,0xff,0xb8,0xb1, - 0xae,0xff,0x8a,0x88,0x83,0xf4,0x25,0x24,0x23,0x5c,0x00,0x00,0x00,0x2a,0x00, - 0x00,0x00,0x12,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x01,0x83,0x00,0x00,0x00, - 0x00,0x8d,0xff,0xff,0xff,0x00,0x03,0xff,0xff,0xff,0x37,0xff,0xff,0xff,0xad, - 0xff,0xff,0xff,0x23,0x85,0xff,0xff,0xff,0x00,0x01,0x00,0x00,0x00,0x1a,0x85, - 0xff,0xff,0xff,0x00,0x01,0x00,0x00,0x00,0x1a,0x82,0xff,0xff,0xff,0x1a,0x10, - 0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x34,0xf3,0xf3,0xf3,0x35,0xf9,0xf9,0xf9, - 0x34,0x00,0x00,0x00,0x34,0x00,0x00,0x00,0x67,0x00,0x00,0x00,0x6a,0x00,0x00, - 0x00,0x11,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x38,0x00, - 0x00,0x00,0x36,0x00,0x00,0x00,0x29,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x0b, - 0x00,0x00,0x00,0x03,0x88,0xff,0xff,0xff,0x00,0x38,0xff,0xff,0xff,0x23,0xff, - 0xff,0xff,0xad,0xff,0xff,0xff,0x37,0x75,0x74,0x72,0x00,0x6d,0x6c,0x6b,0x00, - 0x75,0x74,0x72,0x03,0x2d,0x2d,0x2d,0x16,0x1e,0x1e,0x20,0x1a,0x31,0x31,0x30, - 0x1a,0x60,0x60,0x5e,0x1a,0x84,0x84,0x83,0x1a,0x6d,0x6d,0x6b,0x39,0x55,0x56, - 0x56,0x48,0x5b,0x5b,0x5d,0x48,0x60,0x61,0x60,0x48,0x7a,0x79,0x76,0x5c,0x42, - 0x43,0x42,0x94,0x51,0x52,0x53,0x9b,0x46,0x46,0x46,0xa6,0x3f,0x3f,0x3f,0xb3, - 0x3f,0x3e,0x3e,0xec,0x23,0x25,0x27,0xff,0x3f,0x3f,0x45,0xff,0x37,0x35,0x3a, - 0xff,0x6e,0x6f,0x6b,0xff,0xa1,0xa1,0x9f,0xff,0xc9,0xc9,0xc7,0xff,0xd4,0xd4, - 0xd2,0xff,0xce,0xcc,0xca,0xff,0xc0,0xbf,0xbe,0xff,0xa7,0xa9,0xa7,0xff,0x8f, - 0x91,0x8f,0xff,0x82,0x81,0x80,0xff,0x7d,0x7e,0x7a,0xff,0x7c,0x7d,0x78,0xff, - 0x7e,0x7f,0x7a,0xff,0x84,0x85,0x80,0xff,0x89,0x8a,0x85,0xff,0x8f,0x90,0x8b, - 0xff,0x96,0x97,0x92,0xff,0x99,0x9a,0x96,0xff,0xa6,0xa6,0xa3,0xff,0xac,0xaa, - 0xa7,0xff,0xb5,0xb4,0xb0,0xff,0xbb,0xb8,0xb6,0xff,0xbf,0xbe,0xba,0xff,0xc7, - 0xc6,0xc2,0xff,0xcb,0xca,0xc6,0xff,0xd3,0xd2,0xce,0xff,0xd7,0xd6,0xd2,0xff, - 0xde,0xdd,0xd9,0xff,0xe2,0xe1,0xdd,0xff,0xe2,0xe2,0xde,0xff,0xe2,0xe1,0xdd, - 0xff,0xe3,0xe2,0xde,0xff,0xe7,0xe6,0xe2,0xff,0x82,0xe6,0xe3,0xdf,0xff,0x09, - 0xe1,0xdc,0xd7,0xff,0xd8,0xd6,0xd1,0xff,0xc1,0xba,0xb5,0xff,0x91,0x91,0x8b, - 0xff,0x55,0x54,0x50,0x95,0x00,0x00,0x00,0x36,0x00,0x00,0x00,0x1a,0x00,0x00, - 0x00,0x08,0x00,0x00,0x00,0x01,0x83,0x00,0x00,0x00,0x00,0x8e,0xff,0xff,0xff, - 0x00,0x02,0xff,0xff,0xff,0x7c,0xff,0xff,0xff,0x83,0x8b,0xff,0xff,0xff,0x00, - 0x01,0x00,0x00,0x00,0x1a,0x82,0xff,0xff,0xff,0x1a,0x02,0x00,0x00,0x00,0x1a, - 0x00,0x00,0x00,0x33,0x82,0xff,0xff,0xff,0x33,0x0c,0x00,0x00,0x00,0x33,0xff, - 0xff,0xff,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x15, - 0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x27,0x00,0x00,0x00, - 0x1e,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x02,0x88,0xff, - 0xff,0xff,0x00,0x02,0xff,0xff,0xff,0x83,0xff,0xff,0xff,0x7c,0x82,0x75,0x74, - 0x72,0x00,0x3f,0x70,0x6b,0x69,0x00,0x6d,0x6b,0x6a,0x00,0x61,0x62,0x5e,0x06, - 0x1f,0x1e,0x1e,0x19,0x2a,0x2a,0x2f,0x1a,0x33,0x33,0x33,0x1a,0x69,0x69,0x6b, - 0x1a,0x83,0x85,0x81,0x1e,0x61,0x61,0x62,0x42,0x5a,0x5c,0x5c,0x48,0x5d,0x5c, - 0x5f,0x48,0x67,0x66,0x66,0x49,0x72,0x72,0x71,0x6b,0x44,0x44,0x44,0x96,0x52, - 0x53,0x53,0x9e,0x41,0x40,0x40,0xa9,0x45,0x47,0x45,0xbf,0x33,0x32,0x32,0xf9, - 0x2e,0x2e,0x2f,0xff,0x3e,0x3e,0x41,0xff,0x3c,0x3c,0x3e,0xff,0x81,0x81,0x7e, - 0xff,0xac,0xac,0xaa,0xff,0xcc,0xcb,0xc9,0xff,0xd4,0xd3,0xd1,0xff,0xca,0xc9, - 0xc7,0xff,0xb6,0xb4,0xb2,0xff,0x9b,0x9b,0x97,0xff,0x82,0x81,0x80,0xff,0x78, - 0x7a,0x75,0xff,0x75,0x76,0x71,0xff,0x76,0x77,0x72,0xff,0x7d,0x7e,0x79,0xff, - 0x84,0x83,0x80,0xff,0x88,0x89,0x84,0xff,0x8f,0x90,0x8c,0xff,0x97,0x98,0x8d, - 0xff,0x9d,0x9e,0x9a,0xff,0xa5,0xa7,0xa2,0xff,0xaf,0xae,0xa9,0xff,0xb1,0xb4, - 0xac,0xff,0xbb,0xba,0xb6,0xff,0xbf,0xbe,0xbb,0xff,0xc4,0xc3,0xbf,0xff,0xcc, - 0xca,0xc7,0xff,0xd1,0xd1,0xcd,0xff,0xdb,0xda,0xd6,0xff,0xdc,0xda,0xd8,0xff, - 0xde,0xdb,0xd6,0xff,0xde,0xdc,0xd8,0xff,0xe3,0xe2,0xde,0xff,0xe4,0xe2,0xde, - 0xff,0xe2,0xe3,0xdf,0xff,0xe2,0xe1,0xdc,0xff,0xde,0xdc,0xd7,0xff,0xd8,0xd4, - 0xd1,0xff,0xc7,0xc1,0xbc,0xff,0x95,0x94,0x8d,0xff,0x6b,0x6a,0x65,0xc3,0x00, - 0x00,0x00,0x43,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x02, - 0x83,0x00,0x00,0x00,0x00,0x8e,0xff,0xff,0xff,0x00,0x03,0xff,0xff,0xff,0x1b, - 0xff,0xff,0xff,0x9f,0xff,0xff,0xff,0x4d,0x8b,0xff,0xff,0xff,0x00,0x01,0x00, - 0x00,0x00,0x1a,0x82,0xff,0xff,0xff,0x1a,0x01,0x00,0x00,0x00,0x1a,0x82,0x00, - 0x00,0x00,0x33,0x82,0xff,0xff,0xff,0x00,0x04,0x00,0x00,0x00,0x01,0x00,0x00, - 0x00,0x05,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x12,0x82,0x00,0x00,0x00,0x17, - 0x04,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x05,0x00,0x00, - 0x00,0x01,0x87,0xff,0xff,0xff,0x00,0x03,0xff,0xff,0xff,0x4d,0xff,0xff,0xff, - 0x9f,0xff,0xff,0xff,0x1b,0x82,0x75,0x74,0x72,0x00,0x01,0x7a,0x78,0x77,0x00, - 0x82,0x6d,0x6b,0x6a,0x00,0x3c,0x46,0x44,0x44,0x0c,0x1d,0x1d,0x20,0x1a,0x31, - 0x31,0x33,0x1a,0x35,0x35,0x38,0x1a,0x70,0x6f,0x6e,0x1a,0x80,0x80,0x7c,0x25, - 0x52,0x53,0x53,0x48,0x60,0x61,0x63,0x48,0x60,0x61,0x61,0x48,0x6e,0x70,0x6f, - 0x49,0x61,0x60,0x60,0x7e,0x46,0x48,0x48,0x97,0x52,0x52,0x54,0xa0,0x3d,0x3d, - 0x3e,0xac,0x4c,0x4c,0x4a,0xcf,0x23,0x24,0x21,0xff,0x38,0x38,0x3a,0xff,0x3c, - 0x3c,0x3d,0xff,0x46,0x46,0x45,0xff,0x8f,0x8f,0x8b,0xff,0xb2,0xb1,0xaf,0xff, - 0xce,0xcd,0xcb,0xff,0xd3,0xd0,0xcd,0xff,0xc4,0xc3,0xc0,0xff,0xaa,0xaa,0xa5, - 0xff,0x8d,0x8d,0x8c,0xff,0x79,0x7a,0x77,0xff,0x72,0x73,0x6f,0xff,0x73,0x74, - 0x6f,0xff,0x77,0x78,0x73,0xff,0x7b,0x7a,0x77,0xff,0x7f,0x80,0x7c,0xff,0x87, - 0x88,0x81,0xff,0x90,0x8f,0x89,0xff,0x93,0x96,0x90,0xff,0x9e,0x9f,0x9b,0xff, - 0xa3,0xa4,0x9e,0xff,0xaa,0xac,0xa5,0xff,0xb5,0xb3,0xb0,0xff,0xb7,0xb6,0xb1, - 0xff,0xba,0xb9,0xb4,0xff,0xc3,0xc2,0xbc,0xff,0xcc,0xcc,0xc9,0xff,0xd3,0xd4, - 0xce,0xff,0xda,0xd8,0xd4,0xff,0xd9,0xd5,0xd2,0xff,0xe0,0xdd,0xd8,0xff,0xe0, - 0xde,0xda,0xff,0xdf,0xe0,0xdc,0xff,0xdf,0xe2,0xdc,0xff,0xde,0xdf,0xda,0xff, - 0xdd,0xde,0xd8,0xff,0xd7,0xd3,0xd0,0xff,0xca,0xc4,0xbf,0xff,0x9d,0x9a,0x95, - 0xff,0x77,0x77,0x71,0xe4,0x08,0x08,0x08,0x52,0x00,0x00,0x00,0x2b,0x00,0x00, - 0x00,0x13,0x00,0x00,0x00,0x04,0x83,0x00,0x00,0x00,0x00,0x8f,0xff,0xff,0xff, - 0x00,0x03,0xff,0xff,0xff,0x48,0xff,0xff,0xff,0x9c,0xff,0xff,0xff,0x26,0x8a, - 0xff,0xff,0xff,0x00,0x01,0x00,0x00,0x00,0x1a,0x82,0xff,0xff,0xff,0x1a,0x01, - 0x00,0x00,0x00,0x1a,0x85,0xff,0xff,0xff,0x00,0x03,0x00,0x00,0x00,0x02,0x00, - 0x00,0x00,0x05,0x00,0x00,0x00,0x08,0x82,0x00,0x00,0x00,0x0a,0x03,0x00,0x00, - 0x00,0x08,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x02,0x87,0xff,0xff,0xff,0x00, - 0x04,0xff,0xff,0xff,0x26,0xff,0xff,0xff,0x9c,0xff,0xff,0xff,0x48,0x6d,0x6c, - 0x6b,0x00,0x82,0x6d,0x6b,0x6a,0x00,0x01,0x7f,0x7d,0x7e,0x00,0x82,0x6d,0x6b, - 0x6a,0x00,0x1b,0x76,0x75,0x73,0x00,0x36,0x35,0x35,0x12,0x21,0x21,0x23,0x1a, - 0x34,0x34,0x37,0x1a,0x3e,0x3e,0x3e,0x1a,0x77,0x77,0x72,0x1a,0x75,0x76,0x74, - 0x2f,0x52,0x53,0x52,0x48,0x64,0x66,0x67,0x48,0x60,0x5e,0x60,0x48,0x79,0x7a, - 0x78,0x51,0x54,0x53,0x52,0x8c,0x4e,0x4e,0x4e,0x99,0x4d,0x4d,0x4d,0xa3,0x3a, - 0x3a,0x3a,0xb0,0x46,0x46,0x44,0xe5,0x26,0x26,0x25,0xff,0x3d,0x3d,0x41,0xff, - 0x3a,0x3a,0x3b,0xff,0x5c,0x5c,0x5d,0xff,0x9d,0x9b,0x9b,0xff,0xb6,0xb6,0xb2, - 0xff,0xd0,0xcd,0xcb,0xff,0xd1,0xd0,0xca,0xff,0xb9,0xb8,0xb3,0xff,0x97,0x97, - 0x96,0xff,0x7b,0x7d,0x7a,0xff,0x82,0x6f,0x70,0x6c,0xff,0x20,0x70,0x71,0x6a, - 0xff,0x73,0x74,0x6f,0xff,0x78,0x7c,0x74,0xff,0x7e,0x7e,0x7c,0xff,0x86,0x86, - 0x86,0xff,0x8a,0x8b,0x86,0xff,0x93,0x94,0x8e,0xff,0x98,0x99,0x93,0xff,0x9e, - 0xa0,0x9a,0xff,0xad,0xab,0xa5,0xff,0xb1,0xb0,0xa8,0xff,0xb1,0xb0,0xaa,0xff, - 0xbd,0xbc,0xb6,0xff,0xc3,0xc4,0xbe,0xff,0xc8,0xc9,0xc5,0xff,0xd2,0xd0,0xcc, - 0xff,0xda,0xd3,0xd0,0xff,0xda,0xd8,0xd4,0xff,0xda,0xd7,0xd1,0xff,0xdb,0xda, - 0xd5,0xff,0xdd,0xdc,0xd6,0xff,0xdc,0xdc,0xd7,0xff,0xdf,0xd9,0xd6,0xff,0xda, - 0xd5,0xd1,0xff,0xc8,0xc2,0xbf,0xff,0xa7,0xa2,0x9c,0xff,0x7e,0x7e,0x79,0xfa, - 0x24,0x24,0x22,0x70,0x00,0x00,0x00,0x34,0x00,0x00,0x00,0x18,0x00,0x00,0x00, - 0x07,0x00,0x00,0x00,0x01,0x82,0x00,0x00,0x00,0x00,0x90,0xff,0xff,0xff,0x00, - 0x03,0xff,0xff,0xff,0x66,0xff,0xff,0xff,0x90,0xff,0xff,0xff,0x0f,0x8a,0xff, - 0xff,0xff,0x00,0x82,0x00,0x00,0x00,0x1a,0x87,0xff,0xff,0xff,0x00,0x02,0x00, - 0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x82,0x00,0x00,0x00,0x03,0x02,0x00,0x00, - 0x00,0x02,0x00,0x00,0x00,0x01,0x87,0xff,0xff,0xff,0x00,0x05,0xff,0xff,0xff, - 0x0f,0xff,0xff,0xff,0x90,0xff,0xff,0xff,0x66,0xff,0xff,0xff,0x00,0x70,0x6b, - 0x69,0x00,0x82,0x6d,0x6b,0x6a,0x00,0x01,0x7f,0x79,0x79,0x00,0x82,0x6d,0x6b, - 0x6a,0x00,0x3d,0x7d,0x7b,0x79,0x00,0x66,0x62,0x61,0x03,0x2e,0x2d,0x2e,0x16, - 0x29,0x2b,0x2c,0x1a,0x36,0x34,0x39,0x1a,0x46,0x44,0x46,0x1a,0x7f,0x7f,0x7e, - 0x1a,0x66,0x66,0x65,0x39,0x56,0x57,0x57,0x48,0x68,0x68,0x6c,0x48,0x61,0x60, - 0x63,0x48,0x77,0x77,0x74,0x60,0x46,0x47,0x44,0x95,0x52,0x52,0x52,0x9b,0x47, - 0x47,0x47,0xa6,0x3c,0x3c,0x3a,0xb7,0x35,0x35,0x33,0xf5,0x2f,0x31,0x32,0xff, - 0x3f,0x3f,0x44,0xff,0x39,0x39,0x3a,0xff,0x72,0x70,0x71,0xff,0xa1,0xa0,0x9e, - 0xff,0xbb,0xbc,0xb7,0xff,0xd0,0xcf,0xcb,0xff,0xc9,0xc8,0xc3,0xff,0xa8,0xa8, - 0xa4,0xff,0x89,0x8a,0x85,0xff,0x73,0x75,0x72,0xff,0x69,0x6b,0x68,0xff,0x6a, - 0x6c,0x67,0xff,0x71,0x70,0x6b,0xff,0x74,0x76,0x71,0xff,0x79,0x79,0x78,0xff, - 0x7e,0x7e,0x7d,0xff,0x80,0x82,0x7e,0xff,0x8b,0x8b,0x86,0xff,0x96,0x96,0x91, - 0xff,0x9b,0x9b,0x96,0xff,0xa1,0xa2,0x99,0xff,0xa5,0xa6,0x9e,0xff,0xad,0xac, - 0xa5,0xff,0xb1,0xb3,0xa9,0xff,0xbc,0xbb,0xb6,0xff,0xc5,0xc3,0xc1,0xff,0xc7, - 0xc6,0xc1,0xff,0xce,0xca,0xca,0xff,0xd0,0xcc,0xc8,0xff,0xd6,0xd2,0xce,0xff, - 0xdc,0xd9,0xd3,0xff,0xd7,0xd4,0xd1,0xff,0xda,0xd7,0xd2,0xff,0xda,0xd6,0xd2, - 0xff,0xd6,0xd1,0xcd,0xff,0xc6,0xc3,0xbe,0xff,0xa8,0xa4,0x9c,0xff,0x7e,0x7e, - 0x78,0xff,0x42,0x41,0x3f,0x96,0x00,0x00,0x00,0x3d,0x00,0x00,0x00,0x1e,0x00, - 0x00,0x00,0x0a,0x00,0x00,0x00,0x01,0x82,0x00,0x00,0x00,0x00,0x91,0xff,0xff, - 0xff,0x00,0x03,0xff,0xff,0xff,0x75,0xff,0xff,0xff,0x86,0xff,0xff,0xff,0x0f, - 0x9e,0xff,0xff,0xff,0x00,0x03,0xff,0xff,0xff,0x0f,0xff,0xff,0xff,0x86,0xff, - 0xff,0xff,0x75,0x82,0xff,0xff,0xff,0x00,0x01,0x7a,0x78,0x77,0x00,0x82,0x6d, - 0x6b,0x6a,0x00,0x01,0x76,0x75,0x73,0x00,0x82,0x6c,0x6b,0x69,0x00,0x3d,0x7e, - 0x7b,0x7a,0x00,0x00,0x00,0x00,0x00,0x60,0x5f,0x5e,0x06,0x21,0x22,0x22,0x1a, - 0x35,0x35,0x38,0x1a,0x39,0x39,0x3a,0x1a,0x4f,0x50,0x4f,0x1a,0x86,0x86,0x84, - 0x1e,0x5b,0x5a,0x59,0x42,0x5e,0x5f,0x5f,0x48,0x67,0x68,0x69,0x48,0x64,0x65, - 0x65,0x49,0x69,0x69,0x66,0x76,0x47,0x47,0x46,0x97,0x50,0x50,0x52,0x9e,0x41, - 0x40,0x40,0xaa,0x42,0x42,0x42,0xcc,0x27,0x27,0x28,0xfe,0x38,0x38,0x3a,0xff, - 0x40,0x40,0x42,0xff,0x45,0x45,0x46,0xff,0x83,0x86,0x84,0xff,0xa6,0xa6,0xa3, - 0xff,0xbc,0xbb,0xb7,0xff,0xc9,0xc8,0xc6,0xff,0xba,0xb7,0xb4,0xff,0x97,0x98, - 0x95,0xff,0x80,0x7d,0x7c,0xff,0x6c,0x6d,0x6c,0xff,0x68,0x68,0x69,0xff,0x68, - 0x68,0x68,0xff,0x6e,0x6e,0x6c,0xff,0x74,0x73,0x72,0xff,0x76,0x78,0x71,0xff, - 0x7b,0x7b,0x77,0xff,0x83,0x81,0x7d,0xff,0x8c,0x8b,0x87,0xff,0x8f,0x8e,0x8a, - 0xff,0x9a,0x99,0x96,0xff,0x9f,0x9e,0x99,0xff,0xaa,0xa8,0xa2,0xff,0xaf,0xab, - 0xa3,0xff,0xb7,0xb4,0xb1,0xff,0xc0,0xbb,0xb7,0xff,0xc2,0xbf,0xbc,0xff,0xc7, - 0xc5,0xc1,0xff,0xcc,0xc6,0xc2,0xff,0xcc,0xc7,0xc3,0xff,0xd3,0xcd,0xca,0xff, - 0xd8,0xd2,0xce,0xff,0xd5,0xcf,0xcb,0xff,0xd2,0xcf,0xcb,0xff,0xd0,0xcd,0xc7, - 0xff,0xc7,0xc1,0xbf,0xff,0xa9,0xa3,0x9b,0xff,0x7d,0x7c,0x76,0xff,0x55,0x53, - 0x50,0xb8,0x00,0x00,0x00,0x46,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x0e,0x00, - 0x00,0x00,0x02,0x82,0x00,0x00,0x00,0x00,0x92,0xff,0xff,0xff,0x00,0x03,0xff, - 0xff,0xff,0x75,0xff,0xff,0xff,0x90,0xff,0xff,0xff,0x26,0x8e,0xff,0xff,0xff, - 0x00,0x01,0xff,0xff,0xff,0x05,0x8d,0xff,0xff,0xff,0x00,0x03,0xff,0xff,0xff, - 0x26,0xff,0xff,0xff,0x90,0xff,0xff,0xff,0x75,0x83,0xff,0xff,0xff,0x00,0x01, - 0x7f,0x7d,0x7e,0x00,0x82,0x6d,0x6b,0x6a,0x00,0x01,0x7d,0x7b,0x79,0x00,0x82, - 0x6c,0x6b,0x69,0x00,0x01,0x6c,0x6a,0x69,0x00,0x82,0x00,0x00,0x00,0x00,0x3a, - 0x53,0x52,0x51,0x0c,0x21,0x21,0x21,0x1a,0x39,0x3b,0x3d,0x1a,0x37,0x34,0x37, - 0x1a,0x5e,0x5f,0x5c,0x1a,0x81,0x80,0x7e,0x27,0x50,0x52,0x4f,0x48,0x65,0x66, - 0x66,0x48,0x66,0x66,0x66,0x48,0x6a,0x6a,0x69,0x4c,0x54,0x54,0x52,0x88,0x4b, - 0x4c,0x4c,0x98,0x4d,0x4c,0x4e,0xa1,0x3a,0x3a,0x3a,0xae,0x42,0x41,0x40,0xe3, - 0x28,0x28,0x29,0xff,0x3b,0x3d,0x40,0xff,0x3f,0x3d,0x3f,0xff,0x53,0x51,0x54, - 0xff,0x93,0x93,0x90,0xff,0xa7,0xa5,0xa0,0xff,0xbd,0xbc,0xba,0xff,0xc0,0xbf, - 0xbb,0xff,0xa8,0xa7,0xa4,0xff,0x89,0x8a,0x86,0xff,0x74,0x73,0x72,0xff,0x68, - 0x66,0x67,0xff,0x67,0x67,0x66,0xff,0x66,0x66,0x63,0xff,0x6b,0x6d,0x6b,0xff, - 0x73,0x76,0x70,0xff,0x77,0x77,0x73,0xff,0x7c,0x7b,0x77,0xff,0x7e,0x7d,0x79, - 0xff,0x89,0x85,0x84,0xff,0x8f,0x90,0x8a,0xff,0x94,0x95,0x90,0xff,0x9e,0x9e, - 0x98,0xff,0xa5,0xa1,0x9a,0xff,0xac,0xa9,0xa3,0xff,0xb3,0xb0,0xac,0xff,0xbd, - 0xb9,0xb3,0xff,0xc0,0xbe,0xb7,0xff,0xc7,0xc1,0xbe,0xff,0xc5,0xc0,0xbc,0xff, - 0xca,0xc5,0xc1,0xff,0xcc,0xc7,0xc3,0xff,0xce,0xcb,0xc7,0xff,0xce,0xc9,0xc5, - 0xff,0xcd,0xc8,0xc4,0xff,0xc1,0xbd,0xb7,0xff,0xa8,0xa3,0x9b,0xff,0x7d,0x7c, - 0x76,0xff,0x5d,0x5c,0x58,0xcf,0x00,0x00,0x00,0x4f,0x00,0x00,0x00,0x2b,0x00, - 0x00,0x00,0x12,0x00,0x00,0x00,0x03,0x82,0x00,0x00,0x00,0x00,0x93,0xff,0xff, - 0xff,0x00,0x03,0xff,0xff,0xff,0x66,0xff,0xff,0xff,0x9c,0xff,0xff,0xff,0x4d, - 0x8d,0xff,0xff,0xff,0x00,0x01,0xff,0xff,0xff,0xb3,0x8c,0xff,0xff,0xff,0x00, - 0x03,0xff,0xff,0xff,0x4d,0xff,0xff,0xff,0x9c,0xff,0xff,0xff,0x66,0x84,0xff, - 0xff,0xff,0x00,0x01,0x7f,0x79,0x79,0x00,0x82,0x6d,0x6b,0x6a,0x00,0x01,0x7e, - 0x7b,0x7a,0x00,0x82,0x6c,0x6b,0x69,0x00,0x01,0x60,0x60,0x60,0x00,0x82,0x00, - 0x00,0x00,0x00,0x3a,0x63,0x62,0x5f,0x00,0x3e,0x3d,0x3d,0x12,0x23,0x25,0x27, - 0x1a,0x3f,0x3f,0x45,0x1a,0x37,0x35,0x3a,0x1a,0x6e,0x6f,0x6b,0x1a,0x6f,0x6f, - 0x6d,0x35,0x55,0x55,0x54,0x48,0x69,0x69,0x6b,0x48,0x65,0x64,0x64,0x48,0x6a, - 0x6a,0x6a,0x5d,0x48,0x48,0x48,0x93,0x4d,0x4d,0x4e,0x9b,0x48,0x47,0x48,0xa5, - 0x3e,0x3e,0x3d,0xba,0x2e,0x2e,0x2d,0xf9,0x2e,0x31,0x2f,0xff,0x3f,0x3f,0x42, - 0xff,0x3f,0x3f,0x41,0xff,0x68,0x69,0x6b,0xff,0x9d,0x9c,0x99,0xff,0xa7,0xa6, - 0xa1,0xff,0xb9,0xb4,0xb0,0xff,0xb2,0xb2,0xae,0xff,0x9c,0x99,0x94,0xff,0x81, - 0x7e,0x7c,0xff,0x6c,0x6d,0x6c,0xff,0x65,0x66,0x64,0xff,0x65,0x65,0x64,0xff, - 0x66,0x65,0x63,0xff,0x6c,0x6e,0x67,0xff,0x71,0x70,0x6d,0xff,0x76,0x75,0x71, - 0xff,0x7c,0x7b,0x77,0xff,0x7d,0x7c,0x78,0xff,0x85,0x86,0x83,0xff,0x8f,0x8b, - 0x88,0xff,0x95,0x93,0x8c,0xff,0x9f,0x9b,0x93,0xff,0xa4,0xa0,0x99,0xff,0xa9, - 0xa5,0x9e,0xff,0xb2,0xb0,0xa7,0xff,0xb9,0xb6,0xaf,0xff,0xbc,0xb8,0xb4,0xff, - 0xc1,0xbc,0xbb,0xff,0xc3,0xbf,0xbb,0xff,0xc4,0xc0,0xbb,0xff,0xc5,0xc0,0xbc, - 0xff,0xc7,0xc2,0xbf,0xff,0xc7,0xc2,0xbe,0xff,0xbe,0xb8,0xb5,0xff,0xa5,0x9c, - 0x96,0xff,0x7b,0x78,0x72,0xff,0x65,0x65,0x60,0xdf,0x04,0x04,0x04,0x58,0x00, - 0x00,0x00,0x30,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x06,0x82,0x00,0x00,0x00, - 0x00,0x94,0xff,0xff,0xff,0x00,0x04,0xff,0xff,0xff,0x48,0xff,0xff,0xff,0x9f, - 0xff,0xff,0xff,0x83,0xff,0xff,0xff,0x23,0x8b,0xff,0xff,0xff,0x00,0x01,0xff, - 0xff,0xff,0xb3,0x8a,0xff,0xff,0xff,0x00,0x04,0xff,0xff,0xff,0x23,0xff,0xff, - 0xff,0x83,0xff,0xff,0xff,0x9f,0xff,0xff,0xff,0x48,0x85,0xff,0xff,0xff,0x00, - 0x01,0x76,0x75,0x73,0x00,0x82,0x6c,0x6b,0x69,0x00,0x01,0x6c,0x6a,0x69,0x00, - 0x82,0x66,0x65,0x64,0x00,0x01,0x69,0x67,0x65,0x00,0x82,0x00,0x00,0x00,0x00, - 0x3a,0x63,0x63,0x61,0x00,0x6c,0x6b,0x69,0x03,0x32,0x31,0x31,0x17,0x2e,0x2e, - 0x2f,0x1a,0x3e,0x3e,0x41,0x1a,0x3c,0x3c,0x3e,0x1a,0x7f,0x7f,0x7c,0x1c,0x5c, - 0x5c,0x5a,0x41,0x5d,0x5e,0x5e,0x48,0x6a,0x6a,0x6d,0x48,0x62,0x62,0x62,0x49, - 0x62,0x60,0x5f,0x74,0x44,0x44,0x44,0x96,0x4a,0x4c,0x4d,0x9e,0x41,0x40,0x40, - 0xaa,0x41,0x41,0x40,0xd0,0x25,0x26,0x25,0xff,0x37,0x36,0x38,0xff,0x40,0x40, - 0x43,0xff,0x45,0x44,0x46,0xff,0x7a,0x77,0x79,0xff,0xa1,0x9e,0x9b,0xff,0xa3, - 0xa3,0x9e,0xff,0xac,0xa9,0xa7,0xff,0xa6,0xa5,0xa0,0xff,0x8e,0x8c,0x88,0xff, - 0x78,0x77,0x77,0xff,0x6a,0x69,0x6a,0xff,0x64,0x64,0x63,0xff,0x63,0x63,0x63, - 0xff,0x65,0x65,0x61,0xff,0x69,0x68,0x67,0xff,0x72,0x71,0x6d,0xff,0x75,0x74, - 0x6e,0xff,0x7b,0x7a,0x74,0xff,0x7f,0x7d,0x7a,0xff,0x87,0x86,0x80,0xff,0x8b, - 0x8a,0x87,0xff,0x90,0x8f,0x8b,0xff,0x98,0x99,0x8f,0xff,0x9d,0x9e,0x96,0xff, - 0xa6,0xa6,0x9d,0xff,0xb0,0xac,0xa4,0xff,0xb2,0xb0,0xa5,0xff,0xb7,0xb4,0xac, - 0xff,0xc0,0xba,0xb5,0xff,0xbe,0xb4,0xb3,0xff,0xbf,0xb9,0xb3,0xff,0xbf,0xba, - 0xb3,0xff,0xbe,0xb9,0xb4,0xff,0xb8,0xb1,0xad,0xff,0xa0,0x9c,0x94,0xff,0x7d, - 0x7a,0x74,0xff,0x6a,0x6a,0x65,0xef,0x0c,0x0b,0x0b,0x63,0x00,0x00,0x00,0x36, - 0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x07,0x82,0x00,0x00,0x00,0x00,0x95,0xff, - 0xff,0xff,0x00,0x05,0xff,0xff,0xff,0x1b,0xff,0xff,0xff,0x7c,0xff,0xff,0xff, - 0xad,0xff,0xff,0xff,0x73,0xff,0xff,0xff,0x20,0x89,0xff,0xff,0xff,0x00,0x01, - 0xff,0xff,0xff,0xb3,0x88,0xff,0xff,0xff,0x00,0x05,0xff,0xff,0xff,0x20,0xff, - 0xff,0xff,0x73,0xff,0xff,0xff,0xad,0xff,0xff,0xff,0x7c,0xff,0xff,0xff,0x1b, - 0x86,0xff,0xff,0xff,0x00,0x01,0x7d,0x7b,0x79,0x00,0x82,0x6c,0x6b,0x69,0x00, - 0x01,0x60,0x60,0x60,0x00,0x82,0x66,0x65,0x64,0x00,0x01,0x63,0x62,0x5f,0x00, - 0x82,0x00,0x00,0x00,0x00,0x3c,0x65,0x63,0x63,0x00,0x5c,0x5a,0x58,0x00,0x5f, - 0x5d,0x5c,0x08,0x23,0x24,0x21,0x1a,0x38,0x38,0x3a,0x1a,0x3c,0x3c,0x3d,0x1a, - 0x46,0x46,0x45,0x1a,0x78,0x78,0x75,0x27,0x50,0x50,0x50,0x47,0x64,0x63,0x64, - 0x48,0x6b,0x6a,0x6a,0x48,0x67,0x67,0x66,0x4f,0x4a,0x4b,0x4a,0x8d,0x47,0x49, - 0x47,0x98,0x48,0x48,0x48,0xa2,0x3b,0x3a,0x3b,0xaf,0x3b,0x3a,0x39,0xe9,0x25, - 0x26,0x25,0xff,0x3b,0x3c,0x3e,0xff,0x45,0x45,0x47,0xff,0x50,0x50,0x53,0xff, - 0x8f,0x8f,0x8f,0xff,0x9b,0x9c,0x98,0xff,0xa4,0xa1,0x9d,0xff,0xa6,0xa2,0x9d, - 0xff,0x95,0x93,0x90,0xff,0x86,0x85,0x85,0xff,0x77,0x76,0x73,0xff,0x68,0x67, - 0x66,0xff,0x61,0x5f,0x5e,0xff,0x60,0x5f,0x61,0xff,0x64,0x62,0x60,0xff,0x6a, - 0x69,0x67,0xff,0x70,0x6f,0x6a,0xff,0x71,0x70,0x6c,0xff,0x79,0x78,0x74,0xff, - 0x7d,0x7c,0x78,0xff,0x81,0x80,0x7c,0xff,0x88,0x86,0x85,0xff,0x90,0x90,0x89, - 0xff,0x93,0x94,0x8e,0xff,0x9a,0x98,0x94,0xff,0xa2,0x9f,0x99,0xff,0xa5,0xa2, - 0x9b,0xff,0xad,0xa8,0xa1,0xff,0xb2,0xad,0xa7,0xff,0xb0,0xa9,0xa5,0xff,0xb7, - 0xb2,0xac,0xff,0xb6,0xb1,0xad,0xff,0xb5,0xb0,0xa9,0xff,0xb0,0xab,0xa5,0xff, - 0x9c,0x97,0x8d,0xff,0x7a,0x75,0x6f,0xff,0x6c,0x6d,0x67,0xfa,0x1c,0x1c,0x1a, - 0x74,0x00,0x00,0x00,0x3b,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x09,0x00,0x00, - 0x00,0x01,0x00,0x00,0x00,0x00,0x97,0xff,0xff,0xff,0x00,0x06,0xff,0xff,0xff, - 0x37,0xff,0xff,0xff,0x8c,0xff,0xff,0xff,0xb9,0xff,0xff,0xff,0x8b,0xff,0xff, - 0xff,0x48,0xff,0xff,0xff,0x08,0x86,0xff,0xff,0xff,0x00,0x01,0xff,0xff,0xff, - 0xb3,0x85,0xff,0xff,0xff,0x00,0x06,0xff,0xff,0xff,0x08,0xff,0xff,0xff,0x48, - 0xff,0xff,0xff,0x8b,0xff,0xff,0xff,0xb9,0xff,0xff,0xff,0x8c,0xff,0xff,0xff, - 0x37,0x88,0xff,0xff,0xff,0x00,0x01,0x7e,0x7b,0x7a,0x00,0x82,0x6c,0x6b,0x69, - 0x00,0x01,0x69,0x67,0x65,0x00,0x82,0x66,0x65,0x64,0x00,0x01,0x63,0x63,0x61, - 0x00,0x82,0x00,0x00,0x00,0x00,0x01,0x64,0x63,0x61,0x00,0x82,0x5b,0x5a,0x58, - 0x00,0x39,0x49,0x48,0x46,0x0f,0x26,0x26,0x25,0x1a,0x3d,0x3d,0x41,0x1a,0x3a, - 0x3a,0x3b,0x1a,0x5c,0x5c,0x5d,0x1a,0x6c,0x6a,0x69,0x34,0x51,0x51,0x51,0x48, - 0x66,0x67,0x68,0x48,0x69,0x68,0x67,0x48,0x63,0x62,0x62,0x61,0x42,0x42,0x41, - 0x95,0x48,0x47,0x47,0x9b,0x42,0x42,0x44,0xa6,0x3c,0x3b,0x3b,0xbb,0x2e,0x2e, - 0x2c,0xf8,0x2e,0x2f,0x2e,0xff,0x41,0x43,0x44,0xff,0x48,0x49,0x4c,0xff,0x59, - 0x5b,0x5e,0xff,0x98,0x96,0x95,0xff,0x9a,0x99,0x94,0xff,0x9e,0x9c,0x98,0xff, - 0x9c,0x99,0x98,0xff,0x8f,0x8e,0x8e,0xff,0x7f,0x7e,0x7c,0xff,0x70,0x6f,0x6f, - 0xff,0x64,0x64,0x62,0xff,0x5f,0x5e,0x5c,0xff,0x60,0x5d,0x60,0xff,0x63,0x61, - 0x63,0xff,0x68,0x67,0x67,0xff,0x6a,0x69,0x68,0xff,0x70,0x6e,0x69,0xff,0x76, - 0x75,0x71,0xff,0x7e,0x7c,0x78,0xff,0x80,0x7e,0x7a,0xff,0x86,0x86,0x83,0xff, - 0x8c,0x8b,0x85,0xff,0x94,0x90,0x8e,0xff,0x9a,0x97,0x93,0xff,0x9a,0x9a,0x96, - 0xff,0x9e,0x9c,0x97,0xff,0xa6,0xa2,0x9b,0xff,0xa8,0xa4,0x9b,0xff,0xaa,0xa6, - 0x9f,0xff,0xa9,0xa4,0x9e,0xff,0xac,0xa4,0x9e,0xff,0xa5,0xa2,0x9a,0xff,0x95, - 0x90,0x88,0xff,0x76,0x6f,0x6b,0xff,0x6d,0x6d,0x68,0xff,0x38,0x36,0x35,0x8e, - 0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x00,0x99,0xff,0xff,0xff,0x00,0x14,0xff,0xff,0xff,0x2c, - 0xff,0xff,0xff,0x74,0xff,0xff,0xff,0xb7,0xff,0xff,0xff,0xc9,0xff,0xff,0xff, - 0x9e,0xff,0xff,0xff,0x71,0xff,0xff,0xff,0x4b,0xff,0xff,0xff,0x2c,0xff,0xff, - 0xff,0x14,0xff,0xff,0xff,0x05,0xff,0xff,0xff,0xb4,0xff,0xff,0xff,0x14,0xff, - 0xff,0xff,0x2c,0xff,0xff,0xff,0x4b,0xff,0xff,0xff,0x71,0xff,0xff,0xff,0x9e, - 0xff,0xff,0xff,0xc9,0xff,0xff,0xff,0xb7,0xff,0xff,0xff,0x74,0xff,0xff,0xff, - 0x2c,0x8a,0xff,0xff,0xff,0x00,0x01,0x6c,0x6a,0x69,0x00,0x82,0x66,0x65,0x64, - 0x00,0x01,0x63,0x62,0x5f,0x00,0x82,0x5c,0x5a,0x58,0x00,0x01,0x65,0x63,0x63, - 0x00,0x82,0x00,0x00,0x00,0x00,0x01,0x58,0x57,0x55,0x00,0x82,0x5b,0x5a,0x58, - 0x00,0x39,0x66,0x65,0x64,0x01,0x33,0x33,0x32,0x16,0x2f,0x31,0x32,0x1a,0x3f, - 0x3f,0x44,0x1a,0x39,0x39,0x3a,0x1a,0x6f,0x6d,0x6e,0x1e,0x51,0x51,0x50,0x43, - 0x57,0x59,0x56,0x48,0x69,0x69,0x6a,0x48,0x66,0x66,0x66,0x49,0x56,0x55,0x55, - 0x7a,0x40,0x41,0x3f,0x97,0x46,0x46,0x46,0x9e,0x40,0x3f,0x3f,0xaa,0x3a,0x3a, - 0x3a,0xce,0x28,0x27,0x27,0xff,0x37,0x34,0x37,0xff,0x44,0x46,0x48,0xff,0x4a, - 0x4a,0x4e,0xff,0x6f,0x6c,0x6e,0xff,0x9b,0x99,0x99,0xff,0x98,0x98,0x91,0xff, - 0x96,0x96,0x93,0xff,0x92,0x93,0x91,0xff,0x88,0x86,0x84,0xff,0x79,0x78,0x75, - 0xff,0x6c,0x6d,0x6b,0xff,0x62,0x61,0x60,0xff,0x5e,0x5e,0x5c,0xff,0x5f,0x5c, - 0x5d,0xff,0x63,0x61,0x5f,0xff,0x66,0x63,0x63,0xff,0x68,0x69,0x67,0xff,0x6e, - 0x6f,0x6d,0xff,0x71,0x71,0x6e,0xff,0x78,0x78,0x74,0xff,0x7e,0x7f,0x79,0xff, - 0x83,0x81,0x7e,0xff,0x89,0x89,0x85,0xff,0x90,0x8d,0x88,0xff,0x92,0x91,0x89, - 0xff,0x95,0x95,0x90,0xff,0x99,0x96,0x8d,0xff,0x9b,0x94,0x8d,0xff,0x9c,0x98, - 0x90,0xff,0x9b,0x99,0x92,0xff,0x9d,0x99,0x94,0xff,0x9a,0x96,0x8e,0xff,0x8b, - 0x85,0x81,0xff,0x71,0x6c,0x68,0xff,0x6c,0x6b,0x68,0xff,0x3a,0x38,0x37,0x93, - 0x00,0x00,0x00,0x44,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x0d,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x00,0x9c,0xff,0xff,0xff,0x00,0x0e,0xff,0xff,0xff,0x2f, - 0xff,0xff,0xff,0x61,0xff,0xff,0xff,0x8e,0xff,0xff,0xff,0xb4,0xff,0xff,0xff, - 0xd3,0xff,0xff,0xff,0xeb,0xff,0xff,0xff,0xfa,0xff,0xff,0xff,0xfe,0xff,0xff, - 0xff,0xeb,0xff,0xff,0xff,0xd3,0xff,0xff,0xff,0xb4,0xff,0xff,0xff,0x8e,0xff, - 0xff,0xff,0x61,0xff,0xff,0xff,0x2f,0x8d,0xff,0xff,0xff,0x00,0x01,0x60,0x60, - 0x60,0x00,0x82,0x66,0x65,0x64,0x00,0x01,0x63,0x63,0x61,0x00,0x82,0x5c,0x5a, - 0x58,0x00,0x01,0x64,0x63,0x61,0x00,0x82,0x00,0x00,0x00,0x00,0x01,0x57,0x56, - 0x54,0x00,0x83,0x5b,0x5a,0x58,0x00,0x38,0x4f,0x4f,0x4f,0x07,0x27,0x27,0x28, - 0x1a,0x38,0x38,0x3a,0x1a,0x40,0x40,0x42,0x1a,0x45,0x45,0x46,0x1a,0x6d,0x6f, - 0x6d,0x29,0x4a,0x4b,0x4a,0x48,0x5e,0x5d,0x5d,0x48,0x68,0x67,0x69,0x48,0x64, - 0x63,0x63,0x50,0x47,0x47,0x45,0x8c,0x44,0x44,0x43,0x99,0x43,0x45,0x44,0xa2, - 0x3a,0x3a,0x3b,0xaf,0x35,0x35,0x35,0xe3,0x29,0x29,0x2a,0xff,0x3d,0x3c,0x3e, - 0xff,0x4d,0x4d,0x4e,0xff,0x50,0x4e,0x55,0xff,0x7a,0x7d,0x79,0xff,0x9a,0x99, - 0x95,0xff,0x94,0x94,0x94,0xff,0x91,0x91,0x8f,0xff,0x8b,0x8a,0x88,0xff,0x83, - 0x80,0x80,0xff,0x75,0x74,0x71,0xff,0x69,0x68,0x67,0xff,0x60,0x5f,0x5e,0xff, - 0x5e,0x5b,0x5c,0xff,0x5e,0x5b,0x5a,0xff,0x60,0x60,0x5f,0xff,0x64,0x65,0x64, - 0xff,0x66,0x64,0x64,0xff,0x6c,0x6d,0x69,0xff,0x6f,0x71,0x6b,0xff,0x76,0x76, - 0x72,0xff,0x7b,0x7a,0x76,0xff,0x7e,0x7d,0x77,0xff,0x85,0x86,0x80,0xff,0x89, - 0x89,0x81,0xff,0x89,0x88,0x80,0xff,0x90,0x8c,0x86,0xff,0x93,0x91,0x8a,0xff, - 0x92,0x8d,0x8a,0xff,0x93,0x90,0x89,0xff,0x8e,0x8b,0x84,0xff,0x8c,0x8a,0x82, - 0xff,0x82,0x7d,0x79,0xff,0x69,0x68,0x64,0xff,0x6d,0x6c,0x67,0xfe,0x30,0x2f, - 0x2d,0x90,0x00,0x00,0x00,0x48,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x0e,0x00, - 0x00,0x00,0x02,0x00,0x00,0x00,0x00,0xa3,0xff,0xff,0xff,0x00,0x01,0xff,0xff, - 0xff,0xb3,0x93,0xff,0xff,0xff,0x00,0x01,0x69,0x67,0x65,0x00,0x82,0x66,0x65, - 0x64,0x00,0x01,0x65,0x63,0x63,0x00,0x82,0x5c,0x5a,0x58,0x00,0x01,0x58,0x57, - 0x55,0x00,0x82,0x00,0x00,0x00,0x00,0x01,0x5b,0x5a,0x58,0x00,0x84,0x59,0x57, - 0x55,0x00,0x37,0x45,0x43,0x42,0x0f,0x28,0x28,0x29,0x1a,0x3b,0x3d,0x40,0x1a, - 0x3f,0x3d,0x3f,0x1a,0x53,0x51,0x54,0x1a,0x60,0x5f,0x5d,0x38,0x4b,0x4b,0x49, - 0x48,0x61,0x61,0x62,0x48,0x69,0x68,0x69,0x48,0x5d,0x5c,0x5d,0x5f,0x42,0x42, - 0x41,0x95,0x46,0x43,0x45,0x9b,0x42,0x42,0x42,0xa6,0x36,0x35,0x37,0xb6,0x30, - 0x2f,0x2f,0xf3,0x2c,0x2d,0x32,0xff,0x43,0x42,0x44,0xff,0x4d,0x4e,0x51,0xff, - 0x56,0x56,0x56,0xff,0x89,0x89,0x8b,0xff,0x98,0x98,0x98,0xff,0x90,0x91,0x90, - 0xff,0x8e,0x8d,0x8b,0xff,0x87,0x86,0x84,0xff,0x7e,0x7d,0x7b,0xff,0x72,0x71, - 0x71,0xff,0x67,0x66,0x63,0xff,0x5f,0x5d,0x5d,0xff,0x5c,0x5c,0x5b,0xff,0x5d, - 0x5c,0x5b,0xff,0x5e,0x5e,0x5d,0xff,0x61,0x62,0x5f,0xff,0x65,0x63,0x62,0xff, - 0x6b,0x6d,0x67,0xff,0x73,0x71,0x6d,0xff,0x72,0x71,0x6d,0xff,0x76,0x75,0x72, - 0xff,0x7f,0x7d,0x78,0xff,0x7b,0x80,0x79,0xff,0x81,0x81,0x7c,0xff,0x85,0x84, - 0x7f,0xff,0x89,0x87,0x82,0xff,0x88,0x84,0x7e,0xff,0x88,0x85,0x7d,0xff,0x89, - 0x85,0x7e,0xff,0x82,0x7f,0x78,0xff,0x74,0x72,0x6d,0xff,0x64,0x63,0x5f,0xff, - 0x6e,0x6e,0x69,0xfd,0x27,0x27,0x26,0x8c,0x00,0x00,0x00,0x4a,0x00,0x00,0x00, - 0x27,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0xa3,0xff, - 0xff,0xff,0x00,0x01,0xff,0xff,0xff,0xb3,0x93,0xff,0xff,0xff,0x00,0x01,0x63, - 0x62,0x5f,0x00,0x82,0x5c,0x5a,0x58,0x00,0x01,0x64,0x63,0x61,0x00,0x82,0x5b, - 0x5a,0x58,0x00,0x01,0x57,0x56,0x54,0x00,0x82,0x00,0x00,0x00,0x00,0x01,0x54, - 0x52,0x50,0x00,0x84,0x59,0x57,0x55,0x00,0x37,0x5c,0x5a,0x58,0x02,0x2d,0x2d, - 0x2c,0x18,0x2e,0x31,0x2f,0x1a,0x3f,0x3f,0x42,0x1a,0x3f,0x3f,0x41,0x1a,0x66, - 0x66,0x68,0x1e,0x51,0x50,0x4e,0x42,0x51,0x52,0x4f,0x48,0x64,0x64,0x63,0x48, - 0x66,0x66,0x67,0x49,0x4f,0x4f,0x50,0x74,0x43,0x42,0x43,0x96,0x45,0x44,0x44, - 0x9e,0x40,0x40,0x40,0xaa,0x38,0x37,0x38,0xc6,0x2b,0x2b,0x2b,0xfc,0x31,0x33, - 0x36,0xff,0x45,0x45,0x47,0xff,0x51,0x4f,0x56,0xff,0x5b,0x5b,0x5e,0xff,0x90, - 0x90,0x8f,0xff,0x97,0x97,0x92,0xff,0x90,0x8f,0x8d,0xff,0x8b,0x8a,0x88,0xff, - 0x82,0x81,0x81,0xff,0x7b,0x78,0x77,0xff,0x70,0x6f,0x6e,0xff,0x66,0x64,0x66, - 0xff,0x5f,0x5d,0x5d,0xff,0x5f,0x5c,0x5a,0xff,0x5d,0x5c,0x5b,0xff,0x5e,0x5d, - 0x5e,0xff,0x62,0x61,0x5e,0xff,0x65,0x65,0x62,0xff,0x68,0x69,0x64,0xff,0x6f, - 0x6e,0x6a,0xff,0x6f,0x6e,0x68,0xff,0x74,0x73,0x70,0xff,0x75,0x73,0x6e,0xff, - 0x78,0x79,0x76,0xff,0x80,0x7e,0x79,0xff,0x7d,0x7e,0x79,0xff,0x80,0x7e,0x77, - 0xff,0x81,0x7f,0x79,0xff,0x7f,0x7d,0x76,0xff,0x7c,0x78,0x75,0xff,0x70,0x6f, - 0x6b,0xff,0x61,0x62,0x5e,0xff,0x70,0x6f,0x6a,0xfb,0x1c,0x1c,0x1b,0x83,0x00, - 0x00,0x00,0x49,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x02, - 0x00,0x00,0x00,0x00,0xa3,0xff,0xff,0xff,0x00,0x01,0xff,0xff,0xff,0xb3,0x93, - 0xff,0xff,0xff,0x00,0x01,0x63,0x63,0x61,0x00,0x82,0x5c,0x5a,0x58,0x00,0x01, - 0x58,0x57,0x55,0x00,0x83,0x5b,0x5a,0x58,0x00,0x82,0x00,0x00,0x00,0x00,0x01, - 0x6b,0x6a,0x68,0x00,0x85,0x59,0x57,0x55,0x00,0x36,0x4f,0x4f,0x4d,0x08,0x25, - 0x26,0x25,0x1a,0x37,0x36,0x38,0x1a,0x40,0x40,0x43,0x1a,0x45,0x44,0x46,0x1a, - 0x66,0x64,0x65,0x27,0x4b,0x4a,0x49,0x48,0x56,0x54,0x55,0x48,0x62,0x63,0x64, - 0x48,0x64,0x63,0x65,0x4a,0x47,0x45,0x45,0x86,0x42,0x42,0x46,0x98,0x44,0x43, - 0x44,0xa1,0x3b,0x3b,0x3c,0xad,0x3a,0x3a,0x3a,0xd3,0x23,0x23,0x24,0xff,0x38, - 0x38,0x39,0xff,0x48,0x48,0x49,0xff,0x51,0x51,0x52,0xff,0x66,0x66,0x66,0xff, - 0x94,0x94,0x91,0xff,0x96,0x94,0x92,0xff,0x8f,0x8e,0x8c,0xff,0x8a,0x89,0x87, - 0xff,0x83,0x83,0x80,0xff,0x7a,0x77,0x76,0xff,0x6c,0x6b,0x6c,0xff,0x66,0x62, - 0x64,0xff,0x5f,0x5f,0x5f,0xff,0x60,0x5e,0x5e,0xff,0x5c,0x5a,0x5a,0xff,0x61, - 0x5e,0x5d,0xff,0x5e,0x5d,0x5d,0xff,0x64,0x63,0x60,0xff,0x69,0x68,0x66,0xff, - 0x69,0x68,0x64,0xff,0x6c,0x6b,0x67,0xff,0x72,0x71,0x6b,0xff,0x75,0x73,0x6e, - 0xff,0x74,0x73,0x6f,0xff,0x79,0x78,0x72,0xff,0x7a,0x79,0x76,0xff,0x74,0x75, - 0x72,0xff,0x75,0x75,0x72,0xff,0x70,0x71,0x6d,0xff,0x6b,0x6a,0x66,0xff,0x65, - 0x64,0x60,0xff,0x6b,0x6a,0x66,0xef,0x08,0x07,0x07,0x75,0x00,0x00,0x00,0x47, - 0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x00,0xa3,0xff,0xff,0xff,0x00,0x01,0xff,0xff,0xff,0xb3,0x93,0xff,0xff,0xff, - 0x00,0x01,0x65,0x63,0x63,0x00,0x82,0x5c,0x5a,0x58,0x00,0x01,0x57,0x56,0x54, - 0x00,0x82,0x5b,0x5a,0x58,0x00,0x01,0x54,0x52,0x50,0x00,0x82,0x00,0x00,0x00, - 0x00,0x01,0x6a,0x69,0x67,0x00,0x86,0x59,0x57,0x55,0x00,0x22,0x3c,0x3b,0x3a, - 0x11,0x25,0x26,0x25,0x1a,0x3b,0x3c,0x3e,0x1a,0x45,0x45,0x47,0x1a,0x50,0x50, - 0x53,0x1a,0x5f,0x5e,0x5e,0x34,0x4a,0x4a,0x4a,0x48,0x5b,0x59,0x5a,0x48,0x67, - 0x66,0x65,0x48,0x5c,0x5a,0x5c,0x57,0x41,0x42,0x41,0x91,0x43,0x44,0x44,0x9a, - 0x41,0x41,0x41,0xa4,0x38,0x37,0x39,0xb1,0x3b,0x3a,0x39,0xdc,0x23,0x23,0x24, - 0xff,0x38,0x3a,0x39,0xff,0x4b,0x4a,0x4d,0xff,0x53,0x53,0x56,0xff,0x6a,0x6a, - 0x6a,0xff,0x97,0x97,0x94,0xff,0x94,0x93,0x90,0xff,0x8e,0x8d,0x8d,0xff,0x88, - 0x87,0x85,0xff,0x84,0x83,0x80,0xff,0x78,0x75,0x78,0xff,0x6e,0x6c,0x6c,0xff, - 0x67,0x66,0x67,0xff,0x61,0x5f,0x5f,0xff,0x5f,0x5d,0x5d,0xff,0x5c,0x5c,0x5a, - 0xff,0x5f,0x5c,0x5c,0xff,0x5f,0x5c,0x5f,0xff,0x60,0x60,0x60,0xff,0x82,0x67, - 0x66,0x63,0xff,0x11,0x6a,0x69,0x66,0xff,0x70,0x6f,0x6a,0xff,0x6e,0x6d,0x6c, - 0xff,0x71,0x70,0x6d,0xff,0x72,0x70,0x6c,0xff,0x71,0x73,0x6d,0xff,0x71,0x72, - 0x6d,0xff,0x6b,0x6c,0x67,0xff,0x67,0x68,0x62,0xff,0x6e,0x6b,0x68,0xff,0x61, - 0x60,0x5d,0xda,0x00,0x00,0x00,0x6c,0x00,0x00,0x00,0x43,0x00,0x00,0x00,0x22, - 0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0xa3,0xff,0xff, - 0xff,0x00,0x01,0xff,0xff,0xff,0xb3,0x93,0xff,0xff,0xff,0x00,0x01,0x64,0x63, - 0x61,0x00,0x83,0x5b,0x5a,0x58,0x00,0x82,0x59,0x57,0x55,0x00,0x01,0x6b,0x6a, - 0x68,0x00,0x82,0x00,0x00,0x00,0x00,0x01,0x69,0x68,0x66,0x00,0x86,0x59,0x57, - 0x55,0x00,0x35,0x5b,0x5a,0x58,0x03,0x2d,0x2d,0x2b,0x17,0x2e,0x2f,0x2e,0x1a, - 0x41,0x43,0x44,0x1a,0x48,0x49,0x4c,0x1a,0x59,0x5b,0x5e,0x1b,0x54,0x53,0x53, - 0x3f,0x4c,0x4c,0x4e,0x48,0x5d,0x5c,0x5c,0x48,0x63,0x63,0x65,0x49,0x5a,0x59, - 0x59,0x62,0x3c,0x3c,0x3d,0x94,0x44,0x44,0x45,0x9c,0x3e,0x3e,0x3e,0xa7,0x34, - 0x34,0x34,0xb4,0x34,0x33,0x32,0xde,0x27,0x27,0x27,0xff,0x39,0x38,0x3b,0xff, - 0x4d,0x4d,0x4f,0xff,0x52,0x52,0x55,0xff,0x6e,0x6d,0x6e,0xff,0x93,0x96,0x95, - 0xff,0x93,0x91,0x91,0xff,0x90,0x8f,0x8c,0xff,0x8a,0x89,0x86,0xff,0x83,0x82, - 0x80,0xff,0x7a,0x7a,0x77,0xff,0x72,0x71,0x70,0xff,0x6a,0x6a,0x67,0xff,0x65, - 0x63,0x61,0xff,0x61,0x60,0x61,0xff,0x5e,0x5e,0x5c,0xff,0x5f,0x5d,0x5e,0xff, - 0x60,0x5e,0x5f,0xff,0x60,0x60,0x60,0xff,0x64,0x63,0x62,0xff,0x68,0x67,0x67, - 0xff,0x69,0x68,0x66,0xff,0x6c,0x6b,0x69,0xff,0x6a,0x69,0x68,0xff,0x6e,0x6f, - 0x6a,0xff,0x6c,0x6d,0x68,0xff,0x6b,0x6e,0x67,0xff,0x69,0x6a,0x65,0xff,0x6b, - 0x6b,0x67,0xff,0x78,0x75,0x6f,0xff,0x55,0x54,0x51,0xc6,0x00,0x00,0x00,0x67, - 0x00,0x00,0x00,0x3e,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x0a,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x00,0xa3,0xff,0xff,0xff,0x00,0x01,0xff,0xff,0xff,0xb3, - 0x93,0xff,0xff,0xff,0x00,0x01,0x58,0x57,0x55,0x00,0x82,0x5b,0x5a,0x58,0x00, - 0x01,0x54,0x52,0x50,0x00,0x82,0x59,0x57,0x55,0x00,0x01,0x6a,0x69,0x67,0x00, - 0x82,0x00,0x00,0x00,0x00,0x01,0x69,0x68,0x66,0x00,0x87,0x59,0x57,0x55,0x00, - 0x32,0x46,0x45,0x44,0x08,0x28,0x27,0x27,0x1a,0x37,0x34,0x37,0x1a,0x44,0x46, - 0x48,0x1a,0x4a,0x4a,0x4e,0x1a,0x64,0x61,0x62,0x23,0x4d,0x4d,0x4d,0x45,0x4f, - 0x50,0x50,0x48,0x5d,0x5d,0x5d,0x48,0x63,0x62,0x66,0x49,0x56,0x55,0x56,0x6b, - 0x3c,0x3c,0x3d,0x97,0x41,0x42,0x41,0x9e,0x3e,0x3d,0x3e,0xa9,0x33,0x33,0x34, - 0xb5,0x36,0x35,0x35,0xdd,0x25,0x27,0x27,0xff,0x3a,0x3a,0x3d,0xff,0x4e,0x4e, - 0x50,0xff,0x52,0x52,0x52,0xff,0x6b,0x6b,0x6c,0xff,0x92,0x93,0x90,0xff,0x94, - 0x92,0x8e,0xff,0x8f,0x8e,0x8c,0xff,0x8a,0x89,0x87,0xff,0x86,0x85,0x82,0xff, - 0x7e,0x7d,0x7a,0xff,0x74,0x74,0x71,0xff,0x6e,0x6c,0x6d,0xff,0x6a,0x67,0x69, - 0xff,0x65,0x63,0x64,0xff,0x63,0x61,0x62,0xff,0x62,0x60,0x61,0xff,0x63,0x61, - 0x61,0xff,0x64,0x63,0x64,0xff,0x66,0x65,0x65,0xff,0x69,0x68,0x65,0xff,0x6b, - 0x6a,0x68,0xff,0x6c,0x6b,0x6a,0xff,0x6b,0x69,0x67,0xff,0x6b,0x6c,0x67,0xff, - 0x6c,0x6f,0x68,0xff,0x6d,0x6e,0x6a,0xff,0x6c,0x70,0x6e,0xff,0x7e,0x7d,0x79, - 0xff,0x39,0x39,0x37,0xaa,0x00,0x00,0x00,0x5f,0x00,0x00,0x00,0x37,0x00,0x00, - 0x00,0x1a,0x00,0x00,0x00,0x08,0x82,0x00,0x00,0x00,0x00,0xa3,0xff,0xff,0xff, - 0x00,0x01,0xff,0xff,0xff,0xb3,0x93,0xff,0xff,0xff,0x00,0x01,0x57,0x56,0x54, - 0x00,0x82,0x5b,0x5a,0x58,0x00,0x01,0x6b,0x6a,0x68,0x00,0x82,0x59,0x57,0x55, - 0x00,0x01,0x69,0x68,0x66,0x00,0x82,0x00,0x00,0x00,0x00,0x01,0x69,0x68,0x66, - 0x00,0x88,0x59,0x57,0x55,0x00,0x31,0x38,0x37,0x37,0x0f,0x29,0x29,0x2a,0x1a, - 0x3d,0x3c,0x3e,0x1a,0x4d,0x4d,0x4e,0x1a,0x50,0x4e,0x55,0x1a,0x66,0x68,0x65, - 0x29,0x46,0x45,0x45,0x48,0x53,0x53,0x53,0x48,0x5d,0x5d,0x5d,0x48,0x61,0x61, - 0x61,0x49,0x51,0x51,0x50,0x6e,0x3e,0x3e,0x3d,0x97,0x41,0x40,0x41,0x9f,0x3c, - 0x3c,0x3d,0xaa,0x32,0x32,0x33,0xb6,0x3d,0x3c,0x3b,0xd9,0x28,0x28,0x28,0xfe, - 0x37,0x37,0x38,0xff,0x4b,0x4b,0x4a,0xff,0x4d,0x4d,0x4d,0xff,0x6c,0x6a,0x6a, - 0xff,0x91,0x92,0x91,0xff,0x91,0x90,0x90,0xff,0x90,0x8e,0x8d,0xff,0x8b,0x89, - 0x8c,0xff,0x8a,0x87,0x86,0xff,0x81,0x80,0x7e,0xff,0x7b,0x79,0x79,0xff,0x72, - 0x70,0x73,0xff,0x6f,0x6d,0x6d,0xff,0x6c,0x6a,0x6b,0xff,0x66,0x64,0x65,0xff, - 0x69,0x66,0x65,0xff,0x68,0x68,0x67,0xff,0x68,0x67,0x64,0xff,0x69,0x68,0x66, - 0xff,0x6b,0x6a,0x69,0xff,0x6e,0x6d,0x68,0xff,0x6d,0x6c,0x67,0xff,0x6e,0x6e, - 0x6a,0xff,0x72,0x70,0x6d,0xff,0x74,0x73,0x70,0xff,0x76,0x79,0x76,0xff,0x74, - 0x74,0x72,0xef,0x0b,0x0b,0x0b,0x7d,0x00,0x00,0x00,0x53,0x00,0x00,0x00,0x2f, - 0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x06,0x82,0x00,0x00,0x00,0x00 -}; - -static const GdkPixdata cursor_shadow_pixdata = { - 0x47646b50, /* Pixbuf magic: 'GdkP' */ - 24 + 18163, /* header length + pixel_data length */ - 0x2010002, /* pixdata_type */ - 496, /* rowstride */ - 124, /* width */ - 60, /* height */ - cursor_shadow_pixdata_pixel_data /* pixel_data */ -}; diff --git a/src/image_data/png_to_c_header.sh b/src/image_data/png_to_c_header.sh index 65f7c26..75837c8 100755 --- a/src/image_data/png_to_c_header.sh +++ b/src/image_data/png_to_c_header.sh @@ -20,6 +20,8 @@ ## errors/warnings are tied to the right line ## number. +set -e + ## ## Make sure we were given a png file. @@ -79,7 +81,8 @@ TMP_CODE="$0.code.c" echo "Generating conversion code '$TMP_CODE' ..." -let L="`wc -l < $0` - 3" # Skip the first three lines! +L=`wc -l < $0` +L=`expr $L - 3` # Skip the first three lines! echo "#include \"$INC_HEADER\"" > $TMP_CODE echo "" >> $TMP_CODE echo "/""**" >> $TMP_CODE @@ -135,7 +138,8 @@ REGEXP="s/^ \(.*\/\* pixel_data.*\)/ ${NAME}_pixel_data \/\* pixel_data \*\//" # gdk-pixbuf-csource adds 2 extra empty lines so get rid of one of them. L=`wc -l < $TMP_HEADER` -let L="$L - `tail -n 2 $TMP_HEADER | grep '^[ \t]*$' | wc -l`" +M=`tail -n 2 $TMP_HEADER | grep '^[ \t]*$' | wc -l` +L=`expr $L - $M` head -n $L $TMP_HEADER | \ sed -n '/^ \"/ !p' | \ diff --git a/src/image_data/tv.png b/src/image_data/tv.png Binary files differdeleted file mode 100644 index d8ea6b7..0000000 --- a/src/image_data/tv.png +++ /dev/null diff --git a/src/image_data/tv_pixdata.h b/src/image_data/tv_pixdata.h deleted file mode 100644 index e79a88f..0000000 --- a/src/image_data/tv_pixdata.h +++ /dev/null @@ -1,738 +0,0 @@ -/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ - -static guint8 tv_pixdata_pixel_data[] = { - 0xff,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0xff,0x00,0xdf,0xff,0xff,0xff,0x00, - 0x8f,0x00,0x00,0x00,0x00,0x02,0x4a,0x49,0x57,0x00,0x47,0x47,0x54,0x00,0x82, - 0x49,0x49,0x56,0x00,0x24,0x4b,0x4b,0x5a,0x00,0x4c,0x4b,0x59,0x00,0x4a,0x4a, - 0x58,0x00,0x4c,0x4c,0x5a,0x00,0x47,0x47,0x55,0x00,0x48,0x48,0x56,0x00,0x45, - 0x46,0x51,0x00,0x45,0x45,0x53,0x00,0x47,0x47,0x55,0x00,0x48,0x48,0x55,0x00, - 0x47,0x48,0x55,0x00,0x47,0x47,0x54,0x00,0x48,0x48,0x55,0x00,0x43,0x43,0x50, - 0x00,0x46,0x47,0x52,0x00,0x45,0x45,0x50,0x0a,0x3e,0x3e,0x4b,0x1e,0x40,0x40, - 0x4c,0x3c,0x42,0x42,0x4e,0x55,0x42,0x43,0x4e,0x70,0x42,0x42,0x4e,0x8c,0x42, - 0x42,0x4f,0xab,0x44,0x43,0x4f,0xc3,0x42,0x44,0x50,0xd8,0x43,0x43,0x50,0xf0, - 0x44,0x44,0x51,0xf9,0x43,0x43,0x50,0xe2,0x43,0x42,0x4e,0xce,0x42,0x42,0x4f, - 0xb8,0x43,0x43,0x50,0x9b,0x44,0x44,0x50,0x7d,0x42,0x42,0x4f,0x60,0x46,0x45, - 0x54,0x4a,0x4c,0x4b,0x59,0x2c,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x01,0x82, - 0x00,0x00,0x00,0x00,0x01,0xff,0xff,0xff,0x00,0x8f,0x00,0x00,0x00,0x00,0x02, - 0x4a,0x49,0x57,0x00,0x46,0x47,0x53,0x00,0x82,0x49,0x49,0x56,0x00,0x12,0x4b, - 0x4b,0x5a,0x00,0x4c,0x4b,0x59,0x00,0x4a,0x4a,0x58,0x00,0x4c,0x4c,0x5a,0x00, - 0x46,0x46,0x54,0x00,0x48,0x48,0x56,0x02,0x44,0x45,0x51,0x0f,0x42,0x42,0x50, - 0x27,0x45,0x45,0x53,0x44,0x46,0x46,0x52,0x5f,0x45,0x46,0x53,0x79,0x45,0x45, - 0x52,0x98,0x46,0x46,0x53,0xb5,0x47,0x47,0x55,0xcb,0x48,0x48,0x56,0xdf,0x48, - 0x49,0x57,0xf3,0x49,0x49,0x56,0xff,0x49,0x49,0x57,0xff,0x82,0x48,0x48,0x57, - 0xff,0x13,0x4a,0x49,0x58,0xff,0x4b,0x4b,0x59,0xff,0x4d,0x4c,0x5b,0xff,0x4f, - 0x4f,0x60,0xff,0x55,0x55,0x63,0xff,0x59,0x59,0x69,0xff,0x60,0x5f,0x6e,0xff, - 0x66,0x65,0x74,0xff,0x6a,0x6b,0x7b,0xff,0x71,0x71,0x80,0xff,0x76,0x76,0x86, - 0xff,0x7c,0x7c,0x8a,0xff,0x7f,0x81,0x90,0xff,0x7f,0x7f,0x8d,0xf7,0x43,0x43, - 0x4b,0x17,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0xff, - 0xff,0xff,0x00,0x8f,0x00,0x00,0x00,0x00,0x0b,0x49,0x48,0x56,0x07,0x46,0x46, - 0x53,0x17,0x47,0x47,0x53,0x32,0x47,0x47,0x54,0x4d,0x49,0x49,0x58,0x69,0x4a, - 0x49,0x57,0x84,0x48,0x48,0x56,0xa4,0x4a,0x4a,0x58,0xbf,0x4c,0x4c,0x5a,0xd3, - 0x4c,0x4c,0x5a,0xe7,0x4d,0x4d,0x5b,0xfb,0x83,0x4d,0x4d,0x5b,0xff,0x13,0x4d, - 0x4d,0x5d,0xff,0x4f,0x4f,0x5e,0xff,0x50,0x50,0x5f,0xff,0x51,0x51,0x60,0xff, - 0x53,0x54,0x63,0xff,0x57,0x56,0x66,0xff,0x5c,0x5b,0x6b,0xff,0x60,0x60,0x6f, - 0xff,0x65,0x65,0x75,0xff,0x6a,0x6a,0x7a,0xff,0x6f,0x6f,0x7f,0xff,0x73,0x73, - 0x84,0xff,0x78,0x78,0x87,0xff,0x7b,0x7b,0x8a,0xff,0x7e,0x7e,0x8c,0xff,0x7e, - 0x7e,0x8d,0xff,0x7f,0x80,0x8d,0xff,0x80,0x80,0x8e,0xff,0x7f,0x7f,0x8d,0xff, - 0x82,0x7d,0x7d,0x8b,0xff,0x08,0x7d,0x7c,0x8a,0xff,0x7a,0x7b,0x87,0xff,0x79, - 0x79,0x86,0xf3,0x21,0x21,0x25,0x26,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x04, - 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x89,0x00,0x00,0x00,0x00,0x23,0x4c, - 0x4d,0x5a,0x4a,0x54,0x54,0x62,0x78,0x4f,0x4f,0x5d,0x90,0x4d,0x4d,0x5e,0xaf, - 0x4f,0x4f,0x5e,0xc7,0x50,0x50,0x5f,0xd9,0x50,0x50,0x5f,0xee,0x51,0x51,0x60, - 0xff,0x4f,0x4f,0x5e,0xff,0x4f,0x50,0x5f,0xff,0x50,0x50,0x5e,0xff,0x50,0x4f, - 0x5f,0xff,0x51,0x51,0x61,0xff,0x52,0x53,0x62,0xff,0x54,0x54,0x63,0xff,0x56, - 0x56,0x65,0xff,0x58,0x58,0x6a,0xff,0x5c,0x5d,0x6d,0xff,0x60,0x60,0x71,0xff, - 0x65,0x65,0x75,0xff,0x68,0x68,0x7a,0xff,0x6d,0x6d,0x7d,0xff,0x70,0x70,0x80, - 0xff,0x73,0x73,0x84,0xff,0x76,0x76,0x85,0xff,0x78,0x77,0x87,0xff,0x78,0x79, - 0x88,0xff,0x7a,0x79,0x88,0xff,0x78,0x78,0x86,0xff,0x77,0x78,0x86,0xff,0x77, - 0x77,0x85,0xff,0x77,0x77,0x84,0xff,0x76,0x76,0x84,0xff,0x76,0x75,0x83,0xff, - 0x76,0x75,0x82,0xff,0x83,0x76,0x75,0x83,0xff,0x0b,0x76,0x75,0x82,0xff,0x75, - 0x75,0x81,0xff,0x75,0x75,0x82,0xff,0x71,0x71,0x7d,0xff,0x74,0x75,0x82,0xff, - 0x78,0x78,0x84,0xf1,0x13,0x13,0x15,0x37,0x00,0x00,0x00,0x18,0x00,0x00,0x00, - 0x08,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x88,0x00,0x00,0x00,0x00,0x11, - 0x00,0x00,0x00,0x02,0x49,0x49,0x56,0xe7,0x55,0x55,0x64,0xff,0x56,0x57,0x67, - 0xff,0x5a,0x59,0x69,0xff,0x5b,0x5a,0x6c,0xff,0x5d,0x5d,0x6f,0xff,0x60,0x60, - 0x71,0xff,0x5a,0x59,0x6b,0xff,0x60,0x60,0x70,0xff,0x63,0x63,0x73,0xff,0x65, - 0x65,0x76,0xff,0x68,0x67,0x7a,0xff,0x6c,0x6a,0x7c,0xff,0x6e,0x6e,0x7e,0xff, - 0x70,0x70,0x80,0xff,0x71,0x71,0x81,0xff,0x83,0x72,0x72,0x81,0xff,0x05,0x72, - 0x72,0x80,0xff,0x71,0x71,0x7e,0xff,0x6f,0x6f,0x7d,0xff,0x6f,0x6f,0x7c,0xff, - 0x6e,0x6e,0x7c,0xff,0x82,0x6f,0x6f,0x7c,0xff,0x17,0x6f,0x6f,0x7d,0xff,0x70, - 0x70,0x7e,0xff,0x72,0x72,0x7f,0xff,0x73,0x71,0x7f,0xff,0x72,0x72,0x7e,0xff, - 0x71,0x6f,0x7a,0xff,0x6f,0x6d,0x76,0xff,0x6b,0x68,0x6f,0xff,0x68,0x65,0x66, - 0xff,0x62,0x5d,0x5d,0xff,0x5d,0x5a,0x55,0xff,0x58,0x55,0x4f,0xff,0x54,0x53, - 0x50,0xff,0x5c,0x5a,0x5a,0xff,0x4b,0x4b,0x51,0xff,0x50,0x50,0x5e,0xff,0x75, - 0x75,0x84,0xff,0x77,0x77,0x83,0xf2,0x0e,0x0e,0x0f,0x43,0x00,0x00,0x00,0x1f, - 0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x88,0x00,0x00, - 0x00,0x00,0x02,0x00,0x00,0x00,0x04,0x44,0x44,0x51,0xd9,0x82,0x4e,0x4e,0x5d, - 0xff,0x0a,0x50,0x50,0x5f,0xff,0x52,0x52,0x61,0xff,0x55,0x55,0x65,0xff,0x56, - 0x56,0x65,0xff,0x50,0x50,0x5f,0xff,0x73,0x73,0x86,0xff,0x6f,0x70,0x7f,0xff, - 0x6c,0x6c,0x7c,0xff,0x6d,0x6d,0x7b,0xff,0x6c,0x6c,0x7a,0xff,0x84,0x6b,0x6b, - 0x78,0xff,0x03,0x6b,0x6b,0x79,0xff,0x6c,0x6c,0x7a,0xff,0x6e,0x6d,0x7c,0xff, - 0x82,0x6f,0x6f,0x7d,0xff,0x1b,0x6f,0x6f,0x7c,0xff,0x6d,0x6b,0x79,0xff,0x69, - 0x67,0x72,0xff,0x65,0x61,0x6a,0xff,0x5e,0x59,0x60,0xff,0x56,0x50,0x53,0xff, - 0x4f,0x4a,0x4a,0xff,0x4a,0x45,0x44,0xff,0x47,0x43,0x41,0xff,0x43,0x42,0x43, - 0xff,0x43,0x46,0x4c,0xff,0x58,0x5c,0x69,0xff,0x72,0x77,0x8b,0xff,0x8d,0x91, - 0xa7,0xff,0x99,0x9d,0xb4,0xff,0xa2,0xa6,0xba,0xff,0xc7,0xc7,0xd1,0xff,0xc2, - 0xc1,0xc1,0xff,0x32,0x30,0x2c,0xff,0x52,0x51,0x5f,0xff,0x76,0x76,0x83,0xff, - 0x75,0x75,0x81,0xf1,0x0b,0x0b,0x0c,0x47,0x00,0x00,0x00,0x22,0x00,0x00,0x00, - 0x0c,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x88,0x00,0x00,0x00,0x00,0x32, - 0x00,0x00,0x00,0x07,0x41,0x41,0x4c,0xd5,0x4f,0x4f,0x5e,0xff,0x4e,0x4f,0x5d, - 0xff,0x50,0x50,0x5f,0xff,0x51,0x51,0x60,0xff,0x54,0x54,0x63,0xff,0x52,0x52, - 0x5f,0xff,0x4d,0x4d,0x5b,0xff,0x6b,0x6b,0x7b,0xff,0x66,0x66,0x75,0xff,0x65, - 0x65,0x73,0xff,0x6b,0x6b,0x79,0xff,0x6e,0x6e,0x7c,0xff,0x6d,0x6d,0x79,0xff, - 0x6c,0x6c,0x77,0xff,0x6b,0x69,0x74,0xff,0x67,0x64,0x6e,0xff,0x63,0x5f,0x65, - 0xff,0x59,0x55,0x5a,0xff,0x54,0x50,0x54,0xff,0x54,0x4f,0x50,0xff,0x5c,0x56, - 0x56,0xff,0x65,0x61,0x60,0xff,0x70,0x6e,0x70,0xff,0x82,0x82,0x84,0xff,0x92, - 0x94,0x99,0xff,0x9b,0x9f,0xaa,0xff,0x9d,0xa6,0xb7,0xff,0x9b,0xa7,0xc0,0xff, - 0x98,0xa6,0xc8,0xff,0x96,0xa6,0xcf,0xff,0x9b,0xab,0xd2,0xff,0x8d,0xa2,0xd0, - 0xff,0x7f,0x95,0xcc,0xff,0x6f,0x88,0xc5,0xff,0x5f,0x7b,0xc0,0xff,0x51,0x70, - 0xba,0xff,0x48,0x6b,0xb8,0xff,0x40,0x64,0xb5,0xff,0x3e,0x63,0xb6,0xff,0x31, - 0x2e,0x26,0xff,0x55,0x54,0x61,0xff,0x76,0x76,0x84,0xff,0x73,0x73,0x7f,0xf1, - 0x0b,0x0b,0x0c,0x47,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, - 0x00,0xff,0xff,0xff,0x00,0x83,0x00,0x00,0x00,0x00,0x07,0x39,0x39,0x44,0x02, - 0x4d,0x4f,0x5b,0x13,0x4b,0x4b,0x57,0x3d,0x47,0x46,0x53,0x73,0x3f,0x3f,0x4c, - 0xab,0x37,0x37,0x41,0xd7,0x45,0x45,0x51,0xff,0x82,0x4e,0x4e,0x5d,0xff,0x5c, - 0x50,0x50,0x5e,0xff,0x51,0x51,0x5f,0xff,0x53,0x53,0x63,0xff,0x4f,0x51,0x60, - 0xff,0x4c,0x4c,0x5b,0xff,0x6a,0x6a,0x7b,0xff,0x68,0x68,0x75,0xff,0x69,0x69, - 0x78,0xff,0x52,0x50,0x58,0xff,0x3f,0x3c,0x3d,0xff,0x3e,0x3b,0x3e,0xff,0x3c, - 0x3c,0x41,0xff,0x40,0x42,0x49,0xff,0x4c,0x51,0x5a,0xff,0x5f,0x65,0x6f,0xff, - 0x78,0x81,0x90,0xff,0x82,0x90,0xa7,0xff,0x9f,0xac,0xc0,0xff,0xaf,0xb9,0xce, - 0xff,0xac,0xb7,0xcf,0xff,0xa6,0xb2,0xcb,0xff,0x9c,0xaa,0xc7,0xff,0x91,0xa0, - 0xc3,0xff,0x85,0x96,0xbf,0xff,0x7b,0x8f,0xbb,0xff,0x71,0x89,0xb9,0xff,0x6a, - 0x82,0xb8,0xff,0x61,0x7b,0xb6,0xff,0x57,0x72,0xb4,0xff,0x4f,0x6d,0xb5,0xff, - 0x4d,0x6b,0xb6,0xff,0x4a,0x6c,0xb6,0xff,0x48,0x6d,0xb8,0xff,0x4a,0x6c,0xb8, - 0xff,0x47,0x69,0xb7,0xff,0x48,0x6b,0xb7,0xff,0x49,0x6b,0xb7,0xff,0x2f,0x2d, - 0x26,0xff,0x54,0x53,0x62,0xff,0x77,0x77,0x84,0xff,0x6f,0x6f,0x7c,0xf0,0x07, - 0x07,0x07,0x46,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x00, - 0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x3d,0x3d,0x46,0x7a,0x45,0x43,0x51, - 0xc1,0x32,0x32,0x3a,0xe4,0x36,0x36,0x3f,0xfe,0x37,0x38,0x42,0xff,0x36,0x36, - 0x3f,0xff,0x33,0x33,0x3c,0xff,0x31,0x31,0x3a,0xff,0x44,0x44,0x50,0xff,0x4e, - 0x4e,0x5c,0xff,0x4e,0x4d,0x5b,0xff,0x4f,0x4f,0x5d,0xff,0x50,0x50,0x5e,0xff, - 0x53,0x53,0x62,0xff,0x51,0x51,0x5f,0xff,0x4c,0x4c,0x5b,0xff,0x69,0x69,0x79, - 0xff,0x76,0x76,0x84,0xff,0x76,0x74,0x7c,0xff,0x4e,0x55,0x6b,0xff,0xc6,0xae, - 0xb0,0xff,0xa5,0xab,0xb6,0xff,0x9f,0xa6,0xb8,0xff,0x95,0xa0,0xb6,0xff,0x82, - 0x91,0xb1,0xff,0x70,0x81,0xa4,0xff,0x64,0x78,0xa2,0xff,0x57,0x6f,0x9f,0xff, - 0x4a,0x62,0x9a,0xff,0x40,0x58,0x97,0xff,0x35,0x52,0x94,0xff,0x30,0x4f,0x94, - 0xff,0x30,0x4f,0x95,0xff,0x32,0x51,0x98,0xff,0x36,0x54,0x9b,0xff,0x39,0x57, - 0x9e,0xff,0x3c,0x5b,0xa2,0xff,0x40,0x5e,0xa5,0xff,0x44,0x61,0xa7,0xff,0x47, - 0x66,0xab,0xff,0x49,0x66,0xae,0xff,0x49,0x69,0xb1,0xff,0x4b,0x6b,0xb4,0xff, - 0x4b,0x6b,0xb6,0xff,0x4c,0x6e,0xb7,0xff,0x82,0x49,0x6a,0xb5,0xff,0x2d,0x4a, - 0x6b,0xb6,0xff,0x2c,0x2b,0x22,0xff,0x55,0x55,0x62,0xff,0x74,0x74,0x82,0xff, - 0x70,0x6f,0x7b,0xef,0x06,0x06,0x07,0x45,0x00,0x00,0x00,0x21,0x00,0x00,0x00, - 0x0b,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x02,0x37,0x36, - 0x40,0xf7,0x3d,0x3d,0x48,0xff,0x2b,0x2b,0x33,0xff,0x34,0x34,0x3d,0xff,0x37, - 0x37,0x41,0xff,0x37,0x38,0x40,0xff,0x36,0x36,0x40,0xff,0x32,0x33,0x3a,0xff, - 0x43,0x43,0x4e,0xff,0x4e,0x4e,0x5c,0xff,0x4c,0x4c,0x5b,0xff,0x4e,0x4e,0x5c, - 0xff,0x4f,0x4f,0x5d,0xff,0x52,0x52,0x61,0xff,0x51,0x50,0x5f,0xff,0x4b,0x4b, - 0x5a,0xff,0x68,0x68,0x78,0xff,0x77,0x77,0x86,0xff,0x74,0x70,0x75,0xff,0x69, - 0x77,0x9b,0xff,0x25,0x48,0x86,0xff,0x2d,0x48,0x87,0xff,0x2d,0x45,0x87,0xff, - 0x36,0x51,0x8e,0xff,0x3f,0x59,0x92,0xff,0x45,0x5c,0x98,0xff,0x47,0x61,0x9a, - 0xff,0x4d,0x65,0x9c,0xff,0x4e,0x68,0x9f,0xff,0x51,0x69,0xa0,0xff,0x53,0x6c, - 0xa3,0xff,0x55,0x6f,0xa6,0xff,0x55,0x6c,0xa5,0xff,0x54,0x6e,0xa7,0xff,0x82, - 0x52,0x6b,0xa8,0xff,0x09,0x4f,0x6a,0xa9,0xff,0x4e,0x6a,0xaa,0xff,0x4d,0x69, - 0xab,0xff,0x4c,0x67,0xac,0xff,0x4b,0x69,0xad,0xff,0x4b,0x6b,0xad,0xff,0x4a, - 0x6a,0xaf,0xff,0x49,0x68,0xb2,0xff,0x49,0x6a,0xb3,0xff,0x82,0x46,0x67,0xb2, - 0xff,0x72,0x45,0x66,0xb2,0xff,0x2b,0x2a,0x22,0xff,0x55,0x55,0x62,0xff,0x73, - 0x73,0x80,0xff,0x6e,0x6e,0x7c,0xee,0x04,0x04,0x05,0x44,0x00,0x00,0x00,0x21, - 0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00, - 0x05,0x34,0x34,0x3e,0xde,0x3b,0x3b,0x46,0xff,0x2b,0x2b,0x32,0xff,0x33,0x33, - 0x3c,0xff,0x37,0x37,0x42,0xff,0x36,0x36,0x3f,0xff,0x35,0x35,0x3e,0xff,0x32, - 0x32,0x3b,0xff,0x42,0x42,0x4d,0xff,0x4d,0x4d,0x5b,0xff,0x4c,0x4c,0x5a,0xff, - 0x4d,0x4c,0x5b,0xff,0x4e,0x4e,0x5c,0xff,0x51,0x51,0x60,0xff,0x50,0x50,0x5e, - 0xff,0x4b,0x4b,0x59,0xff,0x65,0x65,0x77,0xff,0x79,0x79,0x85,0xff,0x75,0x73, - 0x77,0xff,0x66,0x73,0x92,0xff,0x39,0x51,0x89,0xff,0x3d,0x54,0x8c,0xff,0x76, - 0x8b,0xad,0xff,0x4e,0x66,0x99,0xff,0x4f,0x65,0x9a,0xff,0x54,0x68,0x9d,0xff, - 0x57,0x6d,0xa0,0xff,0x58,0x6f,0xa3,0xff,0x59,0x72,0xa5,0xff,0x5a,0x71,0xa5, - 0xff,0x5a,0x73,0xa7,0xff,0x5a,0x74,0xa9,0xff,0x58,0x72,0xa9,0xff,0x57,0x71, - 0xaa,0xff,0x55,0x6f,0xaa,0xff,0x54,0x6e,0xaa,0xff,0x51,0x6e,0xab,0xff,0x51, - 0x6c,0xac,0xff,0x4f,0x6c,0xad,0xff,0x50,0x6d,0xaf,0xff,0x4f,0x6c,0xb0,0xff, - 0x4d,0x6e,0xb2,0xff,0x4d,0x6d,0xb4,0xff,0x4f,0x6f,0xb6,0xff,0x4f,0x6f,0xb8, - 0xff,0x4a,0x6b,0xb6,0xff,0x49,0x6a,0xb5,0xff,0x4a,0x6b,0xb4,0xff,0x2b,0x28, - 0x23,0xff,0x55,0x54,0x61,0xff,0x72,0x72,0x80,0xff,0x6d,0x6d,0x7a,0xed,0x00, - 0x00,0x00,0x41,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x00, - 0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x08,0x32,0x32,0x3b,0xd8,0x3b,0x3b,0x45, - 0xff,0x2a,0x2a,0x31,0xff,0x32,0x32,0x3b,0xff,0x36,0x36,0x41,0xff,0x35,0x35, - 0x3e,0xff,0x33,0x33,0x3c,0xff,0x30,0x30,0x37,0xff,0x41,0x41,0x4c,0xff,0x4d, - 0x4d,0x5b,0xff,0x4b,0x4b,0x5a,0xff,0x4c,0x4c,0x5a,0xff,0x4d,0x4d,0x5b,0xff, - 0x50,0x50,0x5f,0xff,0x4f,0x4f,0x5d,0xff,0x4a,0x4a,0x59,0xff,0x63,0x64,0x75, - 0xff,0x79,0x79,0x86,0xff,0x78,0x74,0x78,0xff,0x67,0x72,0x8d,0xff,0x3f,0x56, - 0x8b,0xff,0x3e,0x55,0x8b,0xff,0xc9,0xd7,0xdd,0xff,0x5d,0x72,0x9f,0xff,0x50, - 0x68,0x9a,0xff,0x56,0x6c,0x9e,0xff,0x59,0x6f,0xa1,0xff,0x5b,0x70,0xa4,0xff, - 0x5c,0x72,0xa5,0xff,0x5c,0x74,0xa6,0xff,0x5d,0x74,0xa6,0xff,0x5b,0x74,0xa8, - 0xff,0x5a,0x74,0xaa,0xff,0x59,0x73,0xab,0xff,0x57,0x72,0xab,0xff,0x55,0x71, - 0xac,0xff,0x53,0x6e,0xab,0xff,0x51,0x6d,0xac,0xff,0x50,0x6d,0xad,0xff,0x50, - 0x6d,0xaf,0xff,0x4e,0x6d,0xb0,0xff,0x4f,0x6e,0xb3,0xff,0x50,0x6f,0xb5,0xff, - 0x50,0x72,0xb6,0xff,0x52,0x72,0xb7,0xff,0x83,0x4b,0x6c,0xb7,0xff,0x4e,0x2a, - 0x28,0x23,0xff,0x54,0x54,0x60,0xff,0x73,0x73,0x80,0xff,0x6b,0x6b,0x78,0xeb, - 0x00,0x00,0x00,0x41,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x0a,0x00,0x00,0x00, - 0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x0a,0x32,0x32,0x3b,0xd6,0x3b,0x3b, - 0x45,0xff,0x2a,0x29,0x32,0xff,0x30,0x31,0x39,0xff,0x37,0x37,0x40,0xff,0x34, - 0x34,0x3e,0xff,0x33,0x33,0x3d,0xff,0x2f,0x2f,0x36,0xff,0x3f,0x40,0x4a,0xff, - 0x4d,0x4d,0x5a,0xff,0x4b,0x4b,0x59,0xff,0x4b,0x4b,0x5a,0xff,0x4d,0x4d,0x5b, - 0xff,0x4f,0x50,0x5e,0xff,0x4e,0x4d,0x5c,0xff,0x4a,0x4a,0x58,0xff,0x63,0x63, - 0x73,0xff,0x7b,0x7b,0x87,0xff,0x79,0x76,0x7b,0xff,0x69,0x72,0x8b,0xff,0x3f, - 0x57,0x8b,0xff,0x4b,0x61,0x91,0xff,0x5b,0x6b,0x90,0xff,0x55,0x69,0x97,0xff, - 0x56,0x6b,0x9c,0xff,0x59,0x6e,0x9f,0xff,0x5c,0x71,0xa3,0xff,0x5e,0x73,0xa5, - 0xff,0x5f,0x74,0xa6,0xff,0x5f,0x76,0xa7,0xff,0x5f,0x76,0xa8,0xff,0x5e,0x77, - 0xaa,0xff,0x5d,0x76,0xaa,0xff,0x5c,0x74,0xaa,0xff,0x59,0x72,0xab,0xff,0x56, - 0x70,0xab,0xff,0x54,0x6e,0xab,0xff,0x53,0x6f,0xab,0xff,0x51,0x6e,0xac,0xff, - 0x4f,0x6c,0xad,0xff,0x50,0x6d,0xb0,0xff,0x4f,0x6d,0xb1,0xff,0x4e,0x6d,0xb2, - 0xff,0x4f,0x6f,0xb4,0xff,0x4f,0x6f,0xb7,0xff,0x4a,0x6b,0xb6,0xff,0x48,0x69, - 0xb4,0xff,0x45,0x66,0xb1,0xff,0x2a,0x27,0x24,0xff,0x54,0x54,0x60,0xff,0x73, - 0x73,0x80,0xff,0x65,0x64,0x70,0xeb,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x20, - 0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00, - 0x0a,0x30,0x30,0x38,0xd1,0x3a,0x3a,0x46,0xff,0x2a,0x2b,0x32,0xff,0x30,0x31, - 0x38,0xff,0x35,0x35,0x3e,0xff,0x34,0x34,0x3e,0xff,0x33,0x33,0x3c,0xff,0x2f, - 0x2e,0x36,0xff,0x3f,0x3f,0x4a,0xff,0x4c,0x4d,0x5a,0xff,0x82,0x4b,0x4b,0x59, - 0xff,0x4b,0x4d,0x4d,0x5b,0xff,0x4f,0x4f,0x5d,0xff,0x4d,0x4d,0x5b,0xff,0x49, - 0x49,0x57,0xff,0x62,0x62,0x72,0xff,0x7b,0x7b,0x87,0xff,0x79,0x76,0x7d,0xff, - 0x6a,0x72,0x8b,0xff,0x40,0x59,0x8c,0xff,0x47,0x5d,0x90,0xff,0x48,0x5f,0x93, - 0xff,0x50,0x66,0x99,0xff,0x58,0x6c,0x9d,0xff,0x5c,0x70,0xa0,0xff,0x5f,0x74, - 0xa3,0xff,0x60,0x75,0xa5,0xff,0x61,0x76,0xa6,0xff,0x62,0x77,0xa7,0xff,0x60, - 0x77,0xa8,0xff,0x60,0x77,0xaa,0xff,0x5e,0x77,0xab,0xff,0x5c,0x75,0xaa,0xff, - 0x59,0x73,0xaa,0xff,0x57,0x72,0xaa,0xff,0x55,0x6f,0xac,0xff,0x53,0x6d,0xab, - 0xff,0x50,0x6e,0xac,0xff,0x50,0x6c,0xad,0xff,0x4d,0x6a,0xac,0xff,0x4a,0x65, - 0xab,0xff,0x48,0x68,0xad,0xff,0x46,0x67,0xaf,0xff,0x47,0x67,0xb1,0xff,0x43, - 0x65,0xb0,0xff,0x46,0x67,0xb2,0xff,0x49,0x6a,0xb5,0xff,0x29,0x27,0x24,0xff, - 0x55,0x54,0x60,0xff,0x71,0x72,0x7f,0xff,0x68,0x69,0x73,0xea,0x00,0x00,0x00, - 0x40,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x00,0xff,0xff, - 0xff,0x00,0x00,0x00,0x00,0x09,0x2e,0x2e,0x37,0xc9,0x3a,0x3a,0x44,0xff,0x2c, - 0x2c,0x33,0xff,0x2f,0x2f,0x38,0xff,0x36,0x34,0x3f,0xff,0x33,0x33,0x3c,0xff, - 0x32,0x32,0x3b,0xff,0x2f,0x2f,0x36,0xff,0x3e,0x3e,0x49,0xff,0x4c,0x4c,0x5a, - 0xff,0x4b,0x4b,0x58,0xff,0x4b,0x4b,0x5a,0xff,0x4c,0x4c,0x5a,0xff,0x4f,0x4e, - 0x5d,0xff,0x4d,0x4d,0x5c,0xff,0x49,0x4a,0x57,0xff,0x61,0x60,0x70,0xff,0x7b, - 0x7c,0x88,0xff,0x7a,0x78,0x7f,0xff,0x6b,0x73,0x89,0xff,0x42,0x59,0x8c,0xff, - 0x48,0x5e,0x91,0xff,0x47,0x5f,0x94,0xff,0x52,0x67,0x99,0xff,0x59,0x6c,0x9c, - 0xff,0x5d,0x70,0x9f,0xff,0x5f,0x75,0xa1,0xff,0x61,0x76,0xa4,0xff,0x61,0x76, - 0xa5,0xff,0x82,0x62,0x77,0xa6,0xff,0x62,0x60,0x76,0xa7,0xff,0x5f,0x74,0xa8, - 0xff,0x5c,0x76,0xaa,0xff,0x5a,0x74,0xab,0xff,0x58,0x72,0xaa,0xff,0x55,0x70, - 0xaa,0xff,0x53,0x6e,0xab,0xff,0x4f,0x6c,0xa9,0xff,0x4d,0x68,0xa8,0xff,0x4a, - 0x67,0xa8,0xff,0x4a,0x64,0xaa,0xff,0x4a,0x69,0xad,0xff,0x4a,0x6a,0xb0,0xff, - 0x4c,0x6b,0xb2,0xff,0x47,0x68,0xb4,0xff,0x4c,0x6d,0xb8,0xff,0x4d,0x6e,0xb9, - 0xff,0x29,0x26,0x23,0xff,0x54,0x54,0x60,0xff,0x6f,0x6f,0x7c,0xff,0x6a,0x6a, - 0x76,0xe9,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x0a,0x00, - 0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x09,0x2d,0x2e,0x35,0xc3, - 0x3a,0x3a,0x43,0xff,0x2b,0x2b,0x33,0xff,0x2f,0x2f,0x38,0xff,0x34,0x36,0x3f, - 0xff,0x32,0x32,0x3b,0xff,0x32,0x32,0x3a,0xff,0x2e,0x2e,0x37,0xff,0x3d,0x3d, - 0x48,0xff,0x4c,0x4c,0x5a,0xff,0x4b,0x4b,0x58,0xff,0x4b,0x4b,0x59,0xff,0x4c, - 0x4c,0x5a,0xff,0x4e,0x4e,0x5d,0xff,0x4e,0x4e,0x5c,0xff,0x48,0x48,0x56,0xff, - 0x60,0x5f,0x6f,0xff,0x7b,0x7b,0x88,0xff,0x7c,0x79,0x80,0xff,0x6a,0x72,0x86, - 0xff,0x42,0x58,0x87,0xff,0x42,0x58,0x8c,0xff,0xe6,0xcb,0x9c,0xff,0x62,0x72, - 0x97,0xff,0x56,0x6a,0x9b,0xff,0x5c,0x6f,0x9e,0xff,0x60,0x75,0xa1,0xff,0x62, - 0x77,0xa4,0xff,0x63,0x78,0xa5,0xff,0x63,0x77,0xa6,0xff,0x63,0x77,0xa7,0xff, - 0x60,0x76,0xa6,0xff,0x5d,0x73,0xa5,0xff,0x5d,0x73,0xa7,0xff,0x5b,0x74,0xa9, - 0xff,0x59,0x71,0xaa,0xff,0x54,0x6e,0xa8,0xff,0x51,0x6a,0xa7,0xff,0x4e,0x67, - 0xa5,0xff,0x4a,0x65,0xa4,0xff,0x4a,0x66,0xa7,0xff,0x4c,0x69,0xac,0xff,0x4b, - 0x6d,0xaf,0xff,0x4c,0x6d,0xb1,0xff,0x4e,0x6c,0xb2,0xff,0x46,0x66,0xb2,0xff, - 0x45,0x66,0xb1,0xff,0x46,0x67,0xb2,0xff,0x27,0x25,0x21,0xff,0x54,0x54,0x60, - 0xff,0x6e,0x6f,0x7b,0xff,0x66,0x66,0x73,0xe8,0x00,0x00,0x00,0x40,0x00,0x00, - 0x00,0x1f,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00, - 0x00,0x00,0x09,0x2e,0x2f,0x37,0xbd,0x39,0x39,0x43,0xff,0x2a,0x2b,0x32,0xff, - 0x2e,0x2e,0x37,0xff,0x36,0x37,0x40,0xff,0x33,0x33,0x3c,0xff,0x31,0x31,0x39, - 0xff,0x2b,0x2d,0x33,0xff,0x3c,0x3c,0x46,0xff,0x4b,0x4b,0x5b,0xff,0x4a,0x4a, - 0x57,0xff,0x4b,0x4b,0x59,0xff,0x4c,0x4c,0x5a,0xff,0x82,0x4e,0x4e,0x5c,0xff, - 0x38,0x48,0x48,0x56,0xff,0x5e,0x5e,0x6d,0xff,0x7b,0x7b,0x87,0xff,0x7c,0x7b, - 0x81,0xff,0x6a,0x6f,0x83,0xff,0x42,0x58,0x87,0xff,0x44,0x5e,0x91,0xff,0xa3, - 0x8f,0x80,0xff,0x61,0x71,0x97,0xff,0x58,0x6d,0x9e,0xff,0x5d,0x72,0xa0,0xff, - 0x61,0x74,0xa2,0xff,0x63,0x77,0xa5,0xff,0x62,0x78,0xa6,0xff,0x64,0x77,0xa7, - 0xff,0x63,0x77,0xa8,0xff,0x62,0x78,0xa9,0xff,0x5f,0x75,0xa7,0xff,0x5b,0x71, - 0xa4,0xff,0x59,0x70,0xa5,0xff,0x55,0x6f,0xa7,0xff,0x51,0x69,0xa4,0xff,0x4f, - 0x68,0xa4,0xff,0x4c,0x65,0xa5,0xff,0x4c,0x67,0xa6,0xff,0x4c,0x6b,0xaa,0xff, - 0x4c,0x69,0xac,0xff,0x4a,0x68,0xb0,0xff,0x4b,0x69,0xae,0xff,0x48,0x67,0xad, - 0xff,0x41,0x63,0xab,0xff,0x45,0x66,0xb1,0xff,0x49,0x6a,0xb7,0xff,0x26,0x25, - 0x21,0xff,0x55,0x54,0x61,0xff,0x6e,0x6e,0x7b,0xff,0x63,0x63,0x6f,0xe7,0x00, - 0x00,0x00,0x40,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x00, - 0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x08,0x2c,0x2c,0x33,0xba,0x39,0x38,0x44, - 0xff,0x2a,0x2a,0x32,0xff,0x2d,0x2c,0x34,0xff,0x35,0x33,0x3c,0xff,0x33,0x33, - 0x3b,0xff,0x32,0x32,0x3a,0xff,0x2e,0x2e,0x36,0xff,0x3b,0x3b,0x46,0xff,0x4b, - 0x4b,0x59,0xff,0x4a,0x4a,0x57,0xff,0x4b,0x4b,0x59,0xff,0x4b,0x4b,0x5a,0xff, - 0x82,0x4d,0x4d,0x5b,0xff,0x38,0x49,0x49,0x55,0xff,0x5d,0x5d,0x6c,0xff,0x7a, - 0x7a,0x86,0xff,0x7d,0x7a,0x82,0xff,0x69,0x6d,0x7f,0xff,0x3f,0x55,0x87,0xff, - 0x54,0x68,0x94,0xff,0x47,0x60,0x94,0xff,0x58,0x6e,0x98,0xff,0x5b,0x6e,0x9c, - 0xff,0x5c,0x70,0x9e,0xff,0x60,0x73,0xa1,0xff,0x61,0x75,0xa4,0xff,0x62,0x76, - 0xa5,0xff,0x62,0x76,0xa6,0xff,0x62,0x77,0xa7,0xff,0x61,0x76,0xa7,0xff,0x5d, - 0x74,0xa6,0xff,0x5a,0x6f,0xa4,0xff,0x55,0x6c,0xa2,0xff,0x53,0x6a,0xa2,0xff, - 0x50,0x68,0xa3,0xff,0x4e,0x68,0xa4,0xff,0x4d,0x67,0xa5,0xff,0x4b,0x67,0xa7, - 0xff,0x4c,0x68,0xa9,0xff,0x49,0x66,0xa9,0xff,0x47,0x61,0xa7,0xff,0x45,0x63, - 0xa6,0xff,0x49,0x68,0xad,0xff,0x46,0x67,0xb0,0xff,0x48,0x69,0xb2,0xff,0x4c, - 0x6d,0xb6,0xff,0x24,0x23,0x20,0xff,0x56,0x54,0x61,0xff,0x6d,0x6d,0x7a,0xff, - 0x64,0x65,0x70,0xe6,0x00,0x00,0x00,0x3f,0x00,0x00,0x00,0x1f,0x00,0x00,0x00, - 0x0a,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x08,0x29,0x28, - 0x2f,0xb2,0x38,0x38,0x44,0xff,0x2b,0x2b,0x33,0xff,0x2d,0x2e,0x34,0xff,0x35, - 0x35,0x3e,0xff,0x32,0x32,0x3b,0xff,0x31,0x31,0x39,0xff,0x2e,0x2e,0x36,0xff, - 0x3a,0x3a,0x45,0xff,0x4a,0x4a,0x58,0xff,0x4a,0x4a,0x57,0xff,0x4b,0x4b,0x58, - 0xff,0x4b,0x4b,0x59,0xff,0x82,0x4d,0x4d,0x5b,0xff,0x2d,0x48,0x48,0x54,0xff, - 0x5c,0x5c,0x6b,0xff,0x7a,0x7a,0x85,0xff,0x7c,0x7a,0x80,0xff,0x66,0x6b,0x7b, - 0xff,0x3e,0x53,0x83,0xff,0x44,0x58,0x87,0xff,0x4c,0x60,0x8f,0xff,0x4d,0x63, - 0x93,0xff,0x55,0x69,0x98,0xff,0x59,0x6d,0x9c,0xff,0x5c,0x70,0x9e,0xff,0x5f, - 0x73,0xa1,0xff,0x5f,0x73,0xa2,0xff,0x60,0x75,0xa4,0xff,0x5f,0x74,0xa4,0xff, - 0x5d,0x72,0xa3,0xff,0x5a,0x70,0xa1,0xff,0x57,0x6d,0xa1,0xff,0x54,0x6a,0xa0, - 0xff,0x52,0x69,0xa0,0xff,0x50,0x69,0xa4,0xff,0x4e,0x6b,0xa6,0xff,0x4c,0x6a, - 0xa6,0xff,0x4b,0x68,0xa6,0xff,0x48,0x63,0xa4,0xff,0x45,0x62,0xa5,0xff,0x45, - 0x62,0xa4,0xff,0x47,0x65,0xaa,0xff,0x4a,0x69,0xaf,0xff,0x44,0x65,0xae,0xff, - 0x47,0x67,0xb0,0xff,0x44,0x66,0xb0,0xff,0x26,0x23,0x20,0xff,0x56,0x55,0x61, - 0xff,0x6c,0x6c,0x79,0xff,0x65,0x65,0x71,0xe5,0x00,0x00,0x00,0x3f,0x00,0x00, - 0x00,0x1f,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00, - 0x00,0x00,0x08,0x28,0x28,0x2f,0xaa,0x38,0x38,0x43,0xff,0x82,0x2b,0x2b,0x33, - 0xff,0x7f,0x35,0x35,0x3e,0xff,0x32,0x32,0x3b,0xff,0x31,0x31,0x3a,0xff,0x2e, - 0x2d,0x36,0xff,0x3a,0x3a,0x44,0xff,0x4a,0x4a,0x57,0xff,0x49,0x49,0x57,0xff, - 0x4b,0x4b,0x58,0xff,0x4b,0x4b,0x59,0xff,0x4d,0x4d,0x5b,0xff,0x4c,0x4c,0x5a, - 0xff,0x48,0x47,0x54,0xff,0x5b,0x59,0x6a,0xff,0x78,0x77,0x84,0xff,0x7b,0x79, - 0x80,0xff,0x64,0x67,0x75,0xff,0x3c,0x51,0x81,0xff,0x3a,0x4f,0x81,0xff,0xc9, - 0xcb,0xc9,0xff,0x57,0x6a,0x92,0xff,0x50,0x63,0x91,0xff,0x56,0x6a,0x97,0xff, - 0x59,0x6d,0x98,0xff,0x59,0x6e,0x9c,0xff,0x5b,0x70,0x9d,0xff,0x5b,0x70,0x9f, - 0xff,0x5b,0x6f,0x9f,0xff,0x59,0x6e,0x9e,0xff,0x57,0x6d,0x9f,0xff,0x54,0x6b, - 0x9f,0xff,0x52,0x68,0x9e,0xff,0x50,0x67,0x9f,0xff,0x4e,0x69,0xa2,0xff,0x4d, - 0x67,0xa6,0xff,0x4a,0x67,0xa3,0xff,0x47,0x62,0xa1,0xff,0x46,0x60,0xa0,0xff, - 0x44,0x60,0xa3,0xff,0x47,0x64,0xa8,0xff,0x45,0x67,0xab,0xff,0x47,0x67,0xac, - 0xff,0x44,0x64,0xac,0xff,0x46,0x66,0xaf,0xff,0x43,0x62,0xad,0xff,0x25,0x23, - 0x20,0xff,0x56,0x54,0x61,0xff,0x6b,0x6b,0x78,0xff,0x64,0x64,0x70,0xe5,0x00, - 0x00,0x00,0x3f,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x00, - 0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x08,0x2b,0x2b,0x32,0xa4,0x38,0x38,0x41, - 0xff,0x2d,0x2d,0x33,0xff,0x2b,0x2b,0x32,0xff,0x35,0x35,0x3e,0xff,0x32,0x32, - 0x3a,0xff,0x31,0x31,0x3a,0xff,0x2d,0x2e,0x35,0xff,0x38,0x37,0x41,0xff,0x4a, - 0x4a,0x57,0xff,0x49,0x49,0x56,0xff,0x4a,0x4a,0x57,0xff,0x4b,0x4b,0x59,0xff, - 0x4c,0x4c,0x5b,0xff,0x4c,0x4c,0x5a,0xff,0x47,0x47,0x53,0xff,0x58,0x58,0x69, - 0xff,0x76,0x76,0x83,0xff,0x79,0x78,0x7f,0xff,0x62,0x64,0x72,0xff,0x3b,0x51, - 0x84,0xff,0x39,0x51,0x83,0xff,0x7d,0x87,0x9f,0xff,0x4e,0x61,0x8d,0xff,0x4d, - 0x61,0x8f,0xff,0x52,0x66,0x94,0xff,0x56,0x6a,0x96,0xff,0x57,0x6c,0x98,0xff, - 0x56,0x6b,0x99,0xff,0x57,0x6b,0x9a,0xff,0x56,0x6b,0x9b,0xff,0x55,0x6b,0x9b, - 0xff,0x54,0x6b,0x9d,0xff,0x52,0x68,0x9e,0xff,0x4f,0x66,0x9d,0xff,0x4c,0x64, - 0x9c,0xff,0x4b,0x64,0x9d,0xff,0x49,0x64,0x9f,0xff,0x46,0x60,0x9f,0xff,0x44, - 0x5f,0x9e,0xff,0x43,0x5f,0x9f,0xff,0x44,0x62,0xa4,0xff,0x44,0x62,0xa7,0xff, - 0x46,0x63,0xa9,0xff,0x45,0x65,0xab,0xff,0x42,0x62,0xaa,0xff,0x41,0x62,0xaa, - 0xff,0x43,0x63,0xac,0xff,0x25,0x23,0x1f,0xff,0x57,0x57,0x61,0xff,0x6a,0x6a, - 0x78,0xff,0x62,0x61,0x6e,0xe3,0x00,0x00,0x00,0x3f,0x00,0x00,0x00,0x1e,0x00, - 0x00,0x00,0x0a,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x07, - 0x29,0x2a,0x2e,0x9e,0x38,0x38,0x42,0xff,0x2d,0x2d,0x33,0xff,0x2a,0x2a,0x31, - 0xff,0x35,0x35,0x3e,0xff,0x32,0x32,0x3a,0xff,0x31,0x31,0x39,0xff,0x2e,0x2e, - 0x35,0xff,0x38,0x38,0x40,0xff,0x49,0x49,0x56,0xff,0x48,0x48,0x56,0xff,0x4a, - 0x4a,0x57,0xff,0x4b,0x4b,0x58,0xff,0x4c,0x4c,0x5a,0xff,0x4c,0x4b,0x5a,0xff, - 0x6e,0x46,0x46,0x53,0xff,0x57,0x57,0x67,0xff,0x74,0x74,0x81,0xff,0x77,0x75, - 0x7e,0xff,0x5f,0x61,0x6e,0xff,0x39,0x50,0x82,0xff,0x3c,0x53,0x83,0xff,0x49, - 0x5d,0x8a,0xff,0x45,0x5b,0x8d,0xff,0x48,0x61,0x8f,0xff,0x4d,0x65,0x94,0xff, - 0x50,0x65,0x93,0xff,0x52,0x66,0x95,0xff,0x51,0x66,0x96,0xff,0x52,0x68,0x97, - 0xff,0x52,0x67,0x99,0xff,0x50,0x67,0x99,0xff,0x4f,0x66,0x99,0xff,0x4d,0x65, - 0x99,0xff,0x4b,0x63,0x9b,0xff,0x49,0x60,0x9a,0xff,0x46,0x5f,0x9a,0xff,0x44, - 0x5f,0x9b,0xff,0x42,0x5d,0x9a,0xff,0x41,0x5d,0x9b,0xff,0x41,0x5d,0x9e,0xff, - 0x43,0x64,0xa4,0xff,0x43,0x61,0xa5,0xff,0x44,0x63,0xa7,0xff,0x45,0x65,0xa9, - 0xff,0x41,0x61,0xa9,0xff,0x3f,0x5c,0xa7,0xff,0x3f,0x61,0xab,0xff,0x24,0x22, - 0x1c,0xff,0x58,0x57,0x63,0xff,0x6a,0x6a,0x77,0xff,0x5f,0x5f,0x6b,0xe3,0x00, - 0x00,0x00,0x3f,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x00, - 0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x07,0x25,0x25,0x2b,0x95,0x39,0x38,0x42, - 0xff,0x2d,0x2d,0x35,0xff,0x2a,0x29,0x31,0xff,0x34,0x34,0x3d,0xff,0x32,0x31, - 0x3b,0xff,0x31,0x31,0x39,0xff,0x2e,0x2e,0x35,0xff,0x36,0x36,0x40,0xff,0x49, - 0x49,0x55,0xff,0x48,0x48,0x55,0xff,0x49,0x49,0x57,0xff,0x4b,0x4b,0x58,0xff, - 0x4b,0x4b,0x5a,0xff,0x4c,0x4c,0x5a,0xff,0x46,0x46,0x54,0xff,0x56,0x56,0x65, - 0xff,0x72,0x72,0x7f,0xff,0x75,0x75,0x7d,0xff,0x5d,0x5f,0x6a,0xff,0x35,0x4c, - 0x7f,0xff,0x39,0x50,0x82,0xff,0x38,0x50,0x85,0xff,0x40,0x57,0x8a,0xff,0x46, - 0x5b,0x8c,0xff,0x49,0x5d,0x8c,0xff,0x4c,0x5f,0x8e,0xff,0x4c,0x61,0x92,0xff, - 0x4d,0x62,0x93,0xff,0x4c,0x63,0x94,0xff,0x4c,0x62,0x94,0xff,0x4c,0x63,0x96, - 0xff,0x4b,0x62,0x96,0xff,0x49,0x61,0x96,0xff,0x47,0x60,0x97,0xff,0x46,0x5d, - 0x97,0xff,0x45,0x5d,0x97,0xff,0x41,0x5b,0x98,0xff,0x40,0x5a,0x98,0xff,0x40, - 0x5b,0x9b,0xff,0x42,0x5e,0xa1,0xff,0x41,0x60,0xa2,0xff,0x41,0x5f,0xa4,0xff, - 0x42,0x60,0xa4,0xff,0x41,0x62,0xa6,0xff,0x3c,0x5d,0xa5,0xff,0x3e,0x5d,0xa5, - 0xff,0x40,0x60,0xa8,0xff,0x24,0x21,0x1a,0xff,0x58,0x58,0x64,0xff,0x6a,0x6a, - 0x77,0xff,0x5b,0x5b,0x67,0xe1,0x00,0x00,0x00,0x3e,0x00,0x00,0x00,0x1e,0x00, - 0x00,0x00,0x0a,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x06, - 0x23,0x23,0x29,0x8a,0x37,0x39,0x42,0xff,0x2e,0x2e,0x35,0xff,0x2a,0x29,0x30, - 0xff,0x34,0x34,0x3d,0xff,0x32,0x32,0x3a,0xff,0x31,0x31,0x39,0xff,0x2e,0x2e, - 0x36,0xff,0x35,0x35,0x3f,0xff,0x82,0x48,0x48,0x55,0xff,0x34,0x49,0x49,0x56, - 0xff,0x4a,0x4a,0x57,0xff,0x4b,0x4b,0x59,0xff,0x4c,0x4b,0x59,0xff,0x46,0x46, - 0x53,0xff,0x55,0x55,0x64,0xff,0x70,0x71,0x7d,0xff,0x74,0x73,0x7b,0xff,0x5b, - 0x5c,0x68,0xff,0x31,0x49,0x7d,0xff,0x35,0x4c,0x7f,0xff,0x39,0x50,0x82,0xff, - 0x3d,0x53,0x82,0xff,0x40,0x55,0x84,0xff,0x43,0x56,0x87,0xff,0x46,0x5a,0x8b, - 0xff,0x46,0x5c,0x8c,0xff,0x47,0x5d,0x8e,0xff,0x49,0x5f,0x90,0xff,0x48,0x5e, - 0x92,0xff,0x49,0x61,0x95,0xff,0x48,0x60,0x98,0xff,0x46,0x5e,0x94,0xff,0x43, - 0x5c,0x93,0xff,0x42,0x5a,0x94,0xff,0x40,0x58,0x94,0xff,0x40,0x59,0x95,0xff, - 0x3d,0x57,0x98,0xff,0x3e,0x59,0x98,0xff,0x40,0x5f,0x9f,0xff,0x3f,0x5d,0xa0, - 0xff,0x3f,0x5d,0xa1,0xff,0x40,0x5e,0xa3,0xff,0x41,0x60,0xa7,0xff,0x3b,0x5a, - 0xa1,0xff,0x3d,0x5c,0xa4,0xff,0x3f,0x5d,0xa7,0xff,0x24,0x21,0x19,0xff,0x58, - 0x58,0x65,0xff,0x69,0x69,0x76,0xff,0x5b,0x5b,0x66,0xe0,0x00,0x00,0x00,0x3e, - 0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x00,0xff,0xff,0xff, - 0x00,0x00,0x00,0x00,0x06,0x27,0x28,0x2e,0x84,0x37,0x36,0x41,0xff,0x2e,0x2e, - 0x36,0xff,0x29,0x29,0x30,0xff,0x34,0x34,0x3d,0xff,0x82,0x31,0x31,0x39,0xff, - 0x02,0x2e,0x2d,0x36,0xff,0x37,0x37,0x3f,0xff,0x82,0x47,0x47,0x54,0xff,0x7f, - 0x48,0x48,0x56,0xff,0x49,0x49,0x57,0xff,0x4b,0x4b,0x58,0xff,0x4b,0x4b,0x59, - 0xff,0x45,0x45,0x51,0xff,0x53,0x54,0x63,0xff,0x6d,0x6e,0x7b,0xff,0x72,0x70, - 0x78,0xff,0x5a,0x5a,0x64,0xff,0x2e,0x46,0x7b,0xff,0x31,0x48,0x7b,0xff,0x33, - 0x49,0x7b,0xff,0x37,0x4c,0x7d,0xff,0x39,0x50,0x80,0xff,0x3e,0x52,0x83,0xff, - 0x3f,0x55,0x86,0xff,0x41,0x57,0x88,0xff,0x42,0x59,0x8a,0xff,0x43,0x59,0x8d, - 0xff,0x44,0x5b,0x91,0xff,0x45,0x5c,0x93,0xff,0x43,0x5b,0x92,0xff,0x40,0x59, - 0x91,0xff,0x3f,0x58,0x91,0xff,0x3e,0x57,0x91,0xff,0x3b,0x55,0x91,0xff,0x3b, - 0x56,0x93,0xff,0x3b,0x55,0x93,0xff,0x3b,0x57,0x96,0xff,0x3d,0x5a,0x9e,0xff, - 0x3c,0x5a,0xa0,0xff,0x3e,0x5d,0x9f,0xff,0x3d,0x5c,0xa1,0xff,0x3d,0x5b,0xa0, - 0xff,0x39,0x58,0x9f,0xff,0x3c,0x5b,0xa2,0xff,0x3e,0x5c,0xa6,0xff,0x25,0x23, - 0x1a,0xff,0x5a,0x59,0x67,0xff,0x68,0x68,0x75,0xff,0x5b,0x5b,0x66,0xdf,0x00, - 0x00,0x00,0x3e,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x00, - 0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x06,0x26,0x26,0x2d,0x7d,0x36,0x36,0x3f, - 0xff,0x2e,0x2e,0x36,0xff,0x28,0x28,0x2f,0xff,0x34,0x34,0x3d,0xff,0x32,0x32, - 0x3a,0xff,0x30,0x30,0x38,0xff,0x2e,0x2e,0x36,0xff,0x35,0x34,0x3e,0xff,0x46, - 0x46,0x54,0xff,0x47,0x47,0x53,0xff,0x48,0x48,0x55,0xff,0x49,0x49,0x57,0xff, - 0x4b,0x4a,0x58,0xff,0x4a,0x4a,0x57,0xff,0x45,0x45,0x52,0xff,0x52,0x52,0x62, - 0xff,0x6b,0x6b,0x78,0xff,0x6f,0x6d,0x76,0xff,0x57,0x58,0x60,0xff,0x29,0x40, - 0x74,0xff,0x2b,0x42,0x76,0xff,0x2e,0x45,0x78,0xff,0x30,0x46,0x79,0xff,0x35, - 0x4b,0x7c,0xff,0x37,0x4d,0x7f,0xff,0x38,0x4f,0x82,0xff,0x3c,0x51,0x85,0xff, - 0x3c,0x53,0x88,0xff,0x3e,0x55,0x8c,0xff,0x40,0x56,0x8e,0xff,0x3f,0x57,0x8d, - 0xff,0x3c,0x54,0x8b,0xff,0x3c,0x55,0x8c,0xff,0x3a,0x56,0x8c,0xff,0x39,0x53, - 0x8d,0xff,0x38,0x52,0x8e,0xff,0x37,0x52,0x8f,0xff,0x38,0x52,0x90,0xff,0x38, - 0x53,0x93,0xff,0x39,0x54,0x97,0xff,0x3b,0x58,0x9b,0xff,0x3c,0x5c,0x9e,0xff, - 0x3c,0x5e,0xa2,0xff,0x3a,0x54,0x9c,0xff,0x38,0x57,0x9e,0xff,0x3b,0x5a,0xa1, - 0xff,0x3c,0x5c,0xa4,0xff,0x26,0x24,0x1d,0xff,0x5a,0x59,0x67,0xff,0x67,0x67, - 0x74,0xff,0x57,0x57,0x62,0xdd,0x00,0x00,0x00,0x3e,0x00,0x00,0x00,0x1e,0x00, - 0x00,0x00,0x09,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x05, - 0x21,0x22,0x27,0x76,0x37,0x36,0x3f,0xff,0x2e,0x2e,0x36,0xff,0x28,0x28,0x2f, - 0xff,0x34,0x34,0x3c,0xff,0x31,0x31,0x3a,0xff,0x31,0x31,0x38,0xff,0x2d,0x2e, - 0x36,0xff,0x34,0x34,0x3d,0xff,0x46,0x46,0x53,0xff,0x46,0x47,0x53,0xff,0x47, - 0x48,0x54,0xff,0x48,0x48,0x56,0xff,0x4a,0x4a,0x57,0xff,0x49,0x49,0x56,0xff, - 0x44,0x45,0x51,0xff,0x52,0x51,0x61,0xff,0x69,0x69,0x77,0xff,0x6c,0x6c,0x74, - 0xff,0x54,0x55,0x5d,0xff,0x26,0x3d,0x72,0xff,0x28,0x3f,0x73,0xff,0x0b,0x2a, - 0x41,0x75,0xff,0x2c,0x43,0x76,0xff,0x2e,0x45,0x78,0xff,0x31,0x47,0x7a,0xff, - 0x33,0x4a,0x7e,0xff,0x36,0x4d,0x82,0xff,0x37,0x50,0x86,0xff,0x39,0x50,0x88, - 0xff,0x39,0x50,0x87,0xff,0x39,0x51,0x86,0xff,0x36,0x4f,0x87,0xff,0x82,0x36, - 0x50,0x88,0xff,0x35,0x35,0x4f,0x8a,0xff,0x34,0x4f,0x8b,0xff,0x34,0x4f,0x8d, - 0xff,0x35,0x4f,0x8e,0xff,0x35,0x50,0x90,0xff,0x35,0x52,0x92,0xff,0x36,0x53, - 0x94,0xff,0x39,0x55,0x9a,0xff,0x38,0x54,0x99,0xff,0x39,0x55,0x9b,0xff,0x37, - 0x55,0x9c,0xff,0x39,0x58,0x9f,0xff,0x3c,0x5b,0xa3,0xff,0x28,0x25,0x1d,0xff, - 0x5a,0x5a,0x68,0xff,0x66,0x66,0x73,0xff,0x55,0x54,0x5f,0xdb,0x00,0x00,0x00, - 0x3e,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x00,0xff,0xff, - 0xff,0x00,0x00,0x00,0x00,0x05,0x21,0x21,0x28,0x6a,0x36,0x36,0x3f,0xff,0x2f, - 0x2f,0x37,0xff,0x28,0x28,0x2e,0xff,0x34,0x34,0x3c,0xff,0x31,0x31,0x3a,0xff, - 0x30,0x31,0x39,0xff,0x2e,0x2e,0x36,0xff,0x34,0x33,0x3d,0xff,0x45,0x45,0x52, - 0xff,0x46,0x46,0x52,0xff,0x47,0x47,0x54,0xff,0x48,0x48,0x55,0xff,0x49,0x49, - 0x57,0xff,0x49,0x49,0x56,0xff,0x44,0x44,0x51,0xff,0x51,0x51,0x5f,0xff,0x67, - 0x67,0x75,0xff,0x6c,0x6a,0x73,0xff,0x54,0x53,0x5a,0xff,0x22,0x3a,0x70,0xff, - 0x24,0x3b,0x71,0xff,0x26,0x3d,0x72,0xff,0x28,0x3f,0x73,0xff,0x29,0x40,0x74, - 0xff,0x2a,0x41,0x74,0xff,0x2e,0x46,0x7b,0xff,0x2f,0x48,0x81,0xff,0x31,0x49, - 0x81,0xff,0x32,0x4a,0x81,0xff,0x82,0x32,0x4b,0x81,0xff,0x7f,0x33,0x4c,0x83, - 0xff,0x31,0x4c,0x85,0xff,0x31,0x4b,0x85,0xff,0x31,0x4c,0x87,0xff,0x31,0x4c, - 0x88,0xff,0x31,0x4c,0x8a,0xff,0x33,0x4d,0x8c,0xff,0x32,0x4e,0x8e,0xff,0x32, - 0x4d,0x90,0xff,0x33,0x4f,0x93,0xff,0x35,0x51,0x96,0xff,0x36,0x52,0x97,0xff, - 0x36,0x56,0x9a,0xff,0x36,0x54,0x9b,0xff,0x39,0x55,0x9e,0xff,0x3a,0x59,0xa1, - 0xff,0x27,0x24,0x1d,0xff,0x5b,0x5a,0x68,0xff,0x67,0x67,0x74,0xff,0x4e,0x4e, - 0x58,0xd9,0x00,0x00,0x00,0x3d,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x09,0x00, - 0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x05,0x27,0x27,0x2d,0x65, - 0x35,0x35,0x3e,0xff,0x30,0x2f,0x38,0xff,0x28,0x28,0x2e,0xff,0x32,0x34,0x3c, - 0xff,0x31,0x31,0x3a,0xff,0x30,0x31,0x39,0xff,0x2e,0x2e,0x37,0xff,0x34,0x33, - 0x3d,0xff,0x45,0x45,0x51,0xff,0x45,0x45,0x52,0xff,0x47,0x47,0x53,0xff,0x48, - 0x48,0x55,0xff,0x49,0x49,0x57,0xff,0x49,0x49,0x56,0xff,0x44,0x43,0x50,0xff, - 0x50,0x50,0x5e,0xff,0x65,0x65,0x73,0xff,0x69,0x67,0x71,0xff,0x51,0x50,0x57, - 0xff,0x1e,0x36,0x6d,0xff,0x21,0x39,0x6f,0xff,0x22,0x3a,0x70,0xff,0x24,0x3b, - 0x71,0xff,0x25,0x3c,0x72,0xff,0x27,0x3f,0x76,0xff,0x27,0x41,0x79,0xff,0x2a, - 0x45,0x7c,0xff,0x2b,0x44,0x7b,0xff,0x2d,0x44,0x7b,0xff,0x2d,0x46,0x7d,0xff, - 0x2c,0x46,0x7f,0xff,0x2d,0x47,0x81,0xff,0x2f,0x47,0x82,0xff,0x2e,0x47,0x83, - 0xff,0x2e,0x48,0x85,0xff,0x2d,0x49,0x86,0xff,0x2e,0x4a,0x88,0xff,0x2f,0x4b, - 0x8a,0xff,0x30,0x4b,0x8b,0xff,0x31,0x4d,0x8e,0xff,0x31,0x4d,0x92,0xff,0x31, - 0x4f,0x93,0xff,0x33,0x51,0x96,0xff,0x35,0x52,0x97,0xff,0x35,0x52,0x99,0xff, - 0x37,0x55,0x9c,0xff,0x3a,0x57,0x9f,0xff,0x2a,0x28,0x20,0xff,0x5b,0x5b,0x69, - 0xff,0x66,0x66,0x73,0xff,0x51,0x51,0x5a,0xd8,0x00,0x00,0x00,0x3d,0x00,0x00, - 0x00,0x1d,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00, - 0x00,0x00,0x04,0x27,0x27,0x2d,0x60,0x35,0x35,0x3d,0xff,0x30,0x30,0x38,0xff, - 0x27,0x27,0x2e,0xff,0x34,0x34,0x3c,0xff,0x31,0x31,0x3a,0xff,0x31,0x31,0x39, - 0xff,0x2e,0x2e,0x37,0xff,0x33,0x33,0x3c,0xff,0x44,0x44,0x50,0xff,0x44,0x44, - 0x51,0xff,0x46,0x46,0x53,0xff,0x47,0x47,0x54,0xff,0x48,0x48,0x56,0xff,0x49, - 0x49,0x56,0xff,0x43,0x43,0x51,0xff,0x4f,0x4f,0x5d,0xff,0x63,0x63,0x70,0xff, - 0x67,0x66,0x6f,0xff,0x50,0x4f,0x55,0xff,0x1b,0x34,0x6b,0xff,0x1d,0x35,0x6c, - 0xff,0x1e,0x36,0x6d,0xff,0x20,0x39,0x6f,0xff,0x22,0x3c,0x73,0xff,0x22,0x3c, - 0x74,0xff,0x24,0x3d,0x75,0xff,0x25,0x3e,0x76,0xff,0x25,0x3e,0x75,0xff,0x27, - 0x3f,0x77,0xff,0x29,0x41,0x79,0xff,0x29,0x43,0x7b,0xff,0x27,0x44,0x7d,0xff, - 0x2a,0x43,0x7f,0xff,0x2a,0x45,0x80,0xff,0x2b,0x46,0x84,0xff,0x2d,0x48,0x87, - 0xff,0x2d,0x49,0x88,0xff,0x2b,0x47,0x84,0xff,0x2e,0x49,0x8a,0xff,0x2d,0x4a, - 0x8c,0xff,0x2f,0x4c,0x8e,0xff,0x30,0x4d,0x92,0xff,0x7f,0x32,0x51,0x94,0xff, - 0x34,0x50,0x97,0xff,0x34,0x52,0x99,0xff,0x35,0x53,0x9b,0xff,0x39,0x56,0x9e, - 0xff,0x2c,0x29,0x21,0xff,0x5b,0x5b,0x69,0xff,0x65,0x64,0x71,0xff,0x53,0x53, - 0x5f,0xd6,0x00,0x00,0x00,0x3c,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x09,0x00, - 0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x04,0x23,0x23,0x2a,0x5a, - 0x34,0x35,0x3d,0xff,0x30,0x30,0x38,0xff,0x27,0x26,0x2d,0xff,0x34,0x34,0x3c, - 0xff,0x31,0x32,0x3a,0xff,0x31,0x31,0x3a,0xff,0x2f,0x2f,0x37,0xff,0x33,0x33, - 0x3c,0xff,0x44,0x44,0x50,0xff,0x44,0x44,0x51,0xff,0x46,0x46,0x52,0xff,0x47, - 0x47,0x54,0xff,0x48,0x48,0x55,0xff,0x48,0x48,0x56,0xff,0x43,0x43,0x4f,0xff, - 0x4e,0x4e,0x5d,0xff,0x60,0x60,0x6e,0xff,0x65,0x64,0x6f,0xff,0x4e,0x4e,0x52, - 0xff,0x18,0x31,0x69,0xff,0x1a,0x33,0x6b,0xff,0x1c,0x35,0x6d,0xff,0x1d,0x37, - 0x71,0xff,0x1e,0x38,0x71,0xff,0x1f,0x39,0x72,0xff,0x1f,0x37,0x6f,0xff,0x20, - 0x38,0x6f,0xff,0x21,0x3a,0x72,0xff,0x23,0x3c,0x74,0xff,0x25,0x3d,0x75,0xff, - 0x26,0x3e,0x78,0xff,0x26,0x41,0x79,0xff,0x28,0x42,0x7c,0xff,0x26,0x41,0x7d, - 0xff,0x28,0x42,0x82,0xff,0x2c,0x47,0x89,0xff,0x2c,0x48,0x87,0xff,0x2b,0x47, - 0x87,0xff,0x2c,0x47,0x87,0xff,0x2e,0x4a,0x8a,0xff,0x2d,0x49,0x8c,0xff,0x28, - 0x44,0x8b,0xff,0x20,0x3e,0x89,0xff,0x11,0x35,0x85,0xff,0x14,0x38,0x8a,0xff, - 0x2a,0x4c,0x98,0xff,0x45,0x61,0xa0,0xff,0x2d,0x2b,0x22,0xff,0x5b,0x5b,0x6a, - 0xff,0x64,0x64,0x71,0xff,0x51,0x51,0x5b,0xd4,0x00,0x00,0x00,0x3c,0x00,0x00, - 0x00,0x1d,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00, - 0x00,0x00,0x04,0x1e,0x1e,0x23,0x57,0x33,0x33,0x3d,0xff,0x31,0x31,0x39,0xff, - 0x26,0x27,0x2d,0xff,0x33,0x33,0x3d,0xff,0x31,0x32,0x3a,0xff,0x32,0x31,0x3a, - 0xff,0x2f,0x2f,0x37,0xff,0x32,0x32,0x3c,0xff,0x44,0x44,0x4f,0xff,0x44,0x44, - 0x50,0xff,0x45,0x45,0x52,0xff,0x47,0x47,0x53,0xff,0x47,0x47,0x55,0xff,0x48, - 0x48,0x55,0xff,0x42,0x42,0x4f,0xff,0x4d,0x4d,0x5b,0xff,0x60,0x5f,0x6c,0xff, - 0x64,0x62,0x6e,0xff,0x4e,0x4d,0x4f,0xff,0x16,0x2f,0x68,0xff,0x18,0x31,0x6b, - 0xff,0x19,0x34,0x6e,0xff,0x1a,0x35,0x6f,0xff,0x1b,0x36,0x6f,0xff,0x1a,0x34, - 0x6c,0xff,0x1a,0x33,0x6b,0xff,0x1b,0x34,0x6c,0xff,0x1c,0x35,0x6d,0xff,0x1e, - 0x38,0x70,0xff,0x21,0x3a,0x73,0xff,0x21,0x3b,0x74,0xff,0x23,0x3e,0x77,0xff, - 0x25,0x3f,0x79,0xff,0x26,0x40,0x7d,0xff,0x28,0x44,0x82,0xff,0x25,0x41,0x83, - 0xff,0x1d,0x3b,0x81,0xff,0x11,0x32,0x7c,0xff,0x05,0x24,0x74,0xff,0x04,0x25, - 0x74,0xff,0x1e,0x3b,0x84,0xff,0x48,0x5f,0x99,0xff,0x78,0x89,0xb2,0xff,0x98, - 0xa6,0xc2,0xff,0x87,0x95,0xc7,0xff,0x5f,0x71,0xcb,0xff,0x23,0x2c,0x58,0xff, - 0x2b,0x29,0x25,0xff,0x59,0x59,0x68,0xff,0x64,0x64,0x70,0xff,0x4d,0x4d,0x57, - 0xd4,0x00,0x00,0x00,0x3c,0x00,0x00,0x00,0x1c,0x1a,0x00,0x00,0x00,0x08,0x00, - 0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x03,0x13,0x13,0x16,0x33, - 0x2c,0x2c,0x35,0xfe,0x31,0x31,0x3b,0xff,0x26,0x26,0x2d,0xff,0x33,0x33,0x3c, - 0xff,0x32,0x32,0x3a,0xff,0x31,0x31,0x3a,0xff,0x30,0x30,0x37,0xff,0x32,0x32, - 0x3b,0xff,0x42,0x42,0x4e,0xff,0x43,0x43,0x4f,0xff,0x44,0x44,0x51,0xff,0x46, - 0x46,0x53,0xff,0x47,0x47,0x54,0xff,0x47,0x47,0x55,0xff,0x42,0x42,0x4e,0xff, - 0x4d,0x4c,0x5a,0xff,0x5e,0x5d,0x6c,0xff,0x63,0x61,0x6d,0xff,0x4d,0x4c,0x4d, - 0xff,0x16,0x30,0x6b,0xff,0x17,0x32,0x6c,0xff,0x82,0x17,0x32,0x6d,0xff,0x01, - 0x17,0x30,0x6b,0xff,0x82,0x17,0x30,0x69,0xff,0x7f,0x18,0x31,0x69,0xff,0x18, - 0x32,0x6b,0xff,0x1c,0x35,0x6f,0xff,0x1c,0x36,0x70,0xff,0x1a,0x35,0x6f,0xff, - 0x11,0x2c,0x6b,0xff,0x09,0x23,0x67,0xff,0x08,0x1e,0x66,0xff,0x07,0x29,0x6f, - 0xff,0x21,0x3e,0x7f,0xff,0x45,0x5e,0x94,0xff,0x67,0x7a,0xa7,0xff,0x7f,0x8f, - 0xb2,0xff,0x7f,0x91,0xc4,0xff,0x6a,0x7e,0xd9,0xff,0x4d,0x62,0xd0,0xff,0x3d, - 0x4c,0x9f,0xff,0x36,0x40,0x78,0xff,0x2e,0x34,0x56,0xff,0x2c,0x2c,0x34,0xff, - 0x2a,0x29,0x24,0xff,0x4b,0x4b,0x57,0xff,0x5d,0x5d,0x6a,0xff,0x63,0x63,0x6e, - 0xff,0x4d,0x4f,0x59,0xd2,0x00,0x00,0x00,0x3c,0x00,0x00,0x00,0x1c,0x00,0x00, - 0x00,0x08,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x03,0x00, - 0x00,0x00,0x10,0x0e,0x0e,0x11,0x56,0x2b,0x2b,0x34,0xf8,0x23,0x23,0x29,0xff, - 0x33,0x33,0x3c,0xff,0x32,0x32,0x3a,0xff,0x31,0x31,0x3a,0xff,0x30,0x30,0x39, - 0xff,0x32,0x31,0x3a,0xff,0x42,0x41,0x4d,0xff,0x43,0x43,0x4f,0xff,0x44,0x44, - 0x50,0xff,0x45,0x45,0x52,0xff,0x46,0x46,0x53,0xff,0x47,0x47,0x54,0xff,0x42, - 0x41,0x4d,0xff,0x4b,0x4c,0x59,0xff,0x5c,0x5c,0x6a,0xff,0x62,0x60,0x6a,0xff, - 0x4d,0x4c,0x4b,0xff,0x14,0x2f,0x6b,0xff,0x15,0x30,0x6b,0xff,0x16,0x31,0x6c, - 0xff,0x15,0x2e,0x68,0xff,0x16,0x2f,0x68,0xff,0x15,0x2e,0x67,0xff,0x12,0x2b, - 0x66,0xff,0x0c,0x25,0x62,0xff,0x09,0x1e,0x5c,0xff,0x09,0x1d,0x5c,0xff,0x0b, - 0x26,0x63,0xff,0x1f,0x38,0x73,0xff,0x37,0x4f,0x81,0xff,0x46,0x5b,0x8b,0xff, - 0x56,0x68,0x98,0xff,0x61,0x76,0xb2,0xff,0x68,0x7c,0xd1,0xff,0x5d,0x74,0xd8, - 0xff,0x5a,0x6b,0xc2,0xff,0x54,0x60,0x9e,0xff,0x3f,0x46,0x6c,0xff,0x2e,0x2f, - 0x3a,0xff,0x31,0x2e,0x29,0xff,0x39,0x37,0x2e,0xff,0x48,0x45,0x41,0xff,0x59, - 0x57,0x5a,0xff,0x67,0x66,0x70,0xff,0x6f,0x6f,0x80,0xff,0x6e,0x6e,0x7c,0xff, - 0x66,0x66,0x72,0xff,0x60,0x60,0x6c,0xff,0x50,0x50,0x59,0xd0,0x00,0x00,0x00, - 0x3b,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0xff,0xff, - 0xff,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x1f,0x0a, - 0x0b,0x0d,0x5e,0x1d,0x1d,0x20,0xfc,0x34,0x34,0x3e,0xff,0x32,0x32,0x3a,0xff, - 0x31,0x31,0x3a,0xff,0x30,0x30,0x39,0xff,0x31,0x31,0x39,0xff,0x41,0x41,0x4c, - 0xff,0x43,0x43,0x4e,0xff,0x44,0x44,0x50,0xff,0x44,0x44,0x52,0xff,0x46,0x46, - 0x52,0xff,0x47,0x47,0x53,0xff,0x41,0x41,0x4d,0xff,0x4b,0x4b,0x58,0xff,0x5b, - 0x5b,0x69,0xff,0x60,0x5f,0x69,0xff,0x4d,0x4c,0x4a,0xff,0x12,0x2e,0x6a,0xff, - 0x12,0x2d,0x6a,0xff,0x0f,0x29,0x64,0xff,0x0d,0x27,0x62,0xff,0x0c,0x26,0x62, - 0xff,0x11,0x2a,0x63,0xff,0x17,0x30,0x68,0xff,0x1b,0x33,0x6b,0xff,0x24,0x35, - 0x6b,0xff,0x2b,0x3f,0x72,0xff,0x45,0x5a,0x96,0xff,0x60,0x74,0xbc,0xff,0x63, - 0x77,0xc7,0xff,0x77,0x87,0xcd,0xff,0x70,0x7d,0xbb,0xff,0x4d,0x57,0x88,0xff, - 0x33,0x37,0x51,0xff,0x33,0x33,0x36,0xff,0x19,0x38,0x35,0x2e,0xff,0x41,0x3d, - 0x35,0xff,0x4c,0x4a,0x48,0xff,0x59,0x57,0x5d,0xff,0x63,0x63,0x70,0xff,0x6c, - 0x6c,0x7b,0xff,0x70,0x70,0x7f,0xff,0x62,0x62,0x6d,0xff,0x56,0x56,0x5e,0xff, - 0x4e,0x4e,0x52,0xff,0x44,0x44,0x45,0xff,0x55,0x55,0x5c,0xff,0x5f,0x5f,0x6d, - 0xff,0x4e,0x4e,0x56,0xcf,0x00,0x00,0x00,0x3b,0x00,0x00,0x00,0x1c,0x00,0x00, - 0x00,0x08,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x01,0x00, - 0x00,0x00,0x06,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x2a,0x13,0x14,0x15,0x88, - 0x34,0x33,0x3e,0xff,0x82,0x31,0x31,0x3a,0xff,0x38,0x30,0x31,0x39,0xff,0x30, - 0x30,0x39,0xff,0x40,0x40,0x4b,0xff,0x42,0x42,0x4d,0xff,0x43,0x43,0x4f,0xff, - 0x44,0x44,0x50,0xff,0x45,0x46,0x52,0xff,0x47,0x47,0x53,0xff,0x41,0x41,0x4c, - 0xff,0x4a,0x4a,0x58,0xff,0x5a,0x5a,0x68,0xff,0x60,0x5e,0x69,0xff,0x4d,0x4a, - 0x4a,0xff,0x0f,0x2b,0x68,0xff,0x09,0x23,0x61,0xff,0x04,0x1a,0x58,0xff,0x05, - 0x17,0x59,0xff,0x0e,0x27,0x62,0xff,0x34,0x49,0x81,0xff,0x5c,0x6f,0xa4,0xff, - 0x78,0x86,0xbb,0xff,0x91,0x9d,0xcd,0xff,0x7f,0x8c,0xc7,0xff,0x55,0x60,0x9b, - 0xff,0x38,0x3f,0x65,0xff,0x37,0x38,0x42,0xff,0x39,0x36,0x32,0xff,0x3d,0x3b, - 0x31,0xff,0x48,0x46,0x41,0xff,0x53,0x51,0x54,0xff,0x5e,0x5d,0x64,0xff,0x66, - 0x66,0x73,0xff,0x6c,0x6c,0x7a,0xff,0x6f,0x6f,0x7f,0xff,0x71,0x71,0x80,0xff, - 0x6f,0x6f,0x7c,0xff,0x6b,0x6b,0x7d,0xff,0x61,0x61,0x6e,0xff,0x30,0x31,0x2f, - 0xff,0x38,0x38,0x36,0xff,0x3f,0x3f,0x3e,0xff,0x3f,0x3f,0x3b,0xff,0x50,0x50, - 0x57,0xff,0x5e,0x5e,0x6b,0xff,0x4b,0x4b,0x55,0xce,0x00,0x00,0x00,0x3b,0x00, - 0x00,0x00,0x1c,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x08,0x00,0x00,0x00, - 0x17,0x0d,0x0d,0x0f,0x44,0x33,0x33,0x3c,0xff,0x82,0x31,0x31,0x39,0xff,0x32, - 0x2e,0x30,0x38,0xff,0x30,0x30,0x39,0xff,0x40,0x40,0x4b,0xff,0x41,0x41,0x4d, - 0xff,0x43,0x43,0x4f,0xff,0x44,0x44,0x50,0xff,0x45,0x45,0x52,0xff,0x47,0x47, - 0x53,0xff,0x40,0x40,0x4c,0xff,0x48,0x48,0x56,0xff,0x59,0x59,0x67,0xff,0x5e, - 0x5d,0x68,0xff,0x4d,0x4b,0x47,0xff,0x2c,0x41,0x73,0xff,0x61,0x72,0x97,0xff, - 0x8e,0x9a,0xb3,0xff,0xa1,0xab,0xcb,0xff,0x82,0x8e,0xc8,0xff,0x56,0x62,0xa1, - 0xff,0x3b,0x42,0x6e,0xff,0x3a,0x3d,0x4c,0xff,0x3c,0x3b,0x3a,0xff,0x41,0x3f, - 0x36,0xff,0x48,0x43,0x3d,0xff,0x51,0x4e,0x4c,0xff,0x59,0x57,0x5d,0xff,0x61, - 0x5f,0x6c,0xff,0x67,0x67,0x76,0xff,0x6c,0x6c,0x7a,0xff,0x6e,0x6e,0x7d,0xff, - 0x6e,0x6e,0x7c,0xff,0x6b,0x6b,0x79,0xff,0x68,0x68,0x77,0xff,0x65,0x65,0x72, - 0xff,0x61,0x62,0x70,0xff,0x5f,0x5f,0x6d,0xff,0x78,0x79,0x6e,0xff,0x5a,0x5a, - 0x5e,0xff,0x36,0x37,0x37,0xff,0x42,0x42,0x41,0xff,0x42,0x42,0x43,0xff,0x42, - 0x42,0x44,0xff,0x50,0x50,0x59,0xff,0x5e,0x5e,0x6a,0xff,0x45,0x45,0x4f,0xcd, - 0x00,0x00,0x00,0x3a,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x08,0x00,0x00,0x00, - 0x00,0xff,0xff,0xff,0x00,0x82,0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x02, - 0x00,0x00,0x00,0x0a,0x16,0x16,0x1a,0x3c,0x36,0x37,0x41,0xff,0x34,0x35,0x3d, - 0xff,0x31,0x30,0x39,0xff,0x82,0x30,0x30,0x38,0xff,0x30,0x3f,0x3f,0x4b,0xff, - 0x40,0x41,0x4c,0xff,0x42,0x42,0x4e,0xff,0x43,0x43,0x4f,0xff,0x44,0x44,0x51, - 0xff,0x46,0x46,0x52,0xff,0x40,0x40,0x4b,0xff,0x47,0x47,0x55,0xff,0x57,0x57, - 0x66,0xff,0x5a,0x5a,0x68,0xff,0x50,0x50,0x51,0xff,0x3b,0x43,0x6a,0xff,0x41, - 0x4a,0x80,0xff,0x40,0x42,0x57,0xff,0x41,0x41,0x40,0xff,0x44,0x42,0x39,0xff, - 0x49,0x47,0x3d,0xff,0x50,0x4e,0x4c,0xff,0x58,0x56,0x5a,0xff,0x5d,0x5d,0x67, - 0xff,0x64,0x64,0x71,0xff,0x67,0x68,0x77,0xff,0x6b,0x69,0x7a,0xff,0x6a,0x6c, - 0x7a,0xff,0x69,0x69,0x77,0xff,0x67,0x66,0x75,0xff,0x63,0x63,0x71,0xff,0x61, - 0x61,0x6e,0xff,0x5e,0x5e,0x6b,0xff,0x5c,0x5c,0x69,0xff,0x5b,0x5b,0x67,0xff, - 0x58,0x58,0x65,0xff,0x54,0x54,0x61,0xff,0x54,0x54,0x5e,0xff,0x8b,0x8f,0x60, - 0xff,0x56,0x57,0x57,0xff,0x44,0x44,0x4c,0xff,0x4e,0x4e,0x56,0xff,0x52,0x52, - 0x5d,0xff,0x55,0x55,0x61,0xff,0x59,0x58,0x65,0xff,0x63,0x64,0x70,0xff,0x3d, - 0x3d,0x45,0xd4,0x00,0x00,0x00,0x3a,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x08, - 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x83,0x00,0x00,0x00,0x00,0x37,0x00, - 0x00,0x00,0x04,0x0e,0x0e,0x10,0x25,0x2d,0x2d,0x34,0xee,0x38,0x38,0x41,0xff, - 0x37,0x37,0x42,0xff,0x32,0x32,0x3b,0xff,0x31,0x31,0x39,0xff,0x3e,0x3f,0x4a, - 0xff,0x40,0x40,0x4c,0xff,0x42,0x42,0x4d,0xff,0x43,0x43,0x4e,0xff,0x44,0x44, - 0x50,0xff,0x45,0x45,0x52,0xff,0x3f,0x40,0x4b,0xff,0x47,0x46,0x54,0xff,0x55, - 0x54,0x62,0xff,0x58,0x58,0x66,0xff,0x55,0x55,0x62,0xff,0x4d,0x4a,0x4a,0xff, - 0x4f,0x4c,0x4b,0xff,0x56,0x54,0x59,0xff,0x5a,0x58,0x62,0xff,0x5c,0x5c,0x6a, - 0xff,0x62,0x63,0x72,0xff,0x66,0x66,0x76,0xff,0x67,0x67,0x75,0xff,0x66,0x66, - 0x75,0xff,0x63,0x63,0x73,0xff,0x61,0x61,0x6f,0xff,0x5f,0x5f,0x6c,0xff,0x5c, - 0x5b,0x69,0xff,0x5a,0x5b,0x67,0xff,0x59,0x59,0x65,0xff,0x57,0x57,0x64,0xff, - 0x54,0x54,0x60,0xff,0x51,0x52,0x5e,0xff,0x4f,0x4f,0x5b,0xff,0x4e,0x4e,0x59, - 0xff,0x4e,0x4e,0x58,0xff,0x4e,0x4f,0x59,0xff,0x4f,0x50,0x5c,0xff,0x4b,0x4a, - 0x5e,0xff,0x55,0x55,0x63,0xff,0x58,0x57,0x63,0xff,0x4e,0x4e,0x5b,0xff,0x4a, - 0x4a,0x56,0xff,0x4e,0x4c,0x59,0xff,0x56,0x55,0x61,0xff,0x40,0x40,0x49,0xf0, - 0x18,0x18,0x1a,0xa1,0x00,0x00,0x00,0x39,0x00,0x00,0x00,0x1b,0x00,0x00,0x00, - 0x08,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x83,0x00,0x00,0x00,0x00,0x37, - 0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0d,0x02,0x02,0x02,0x37,0x0c,0x0b,0x0d, - 0x99,0x1b,0x1a,0x1f,0xda,0x29,0x2a,0x2f,0xf9,0x31,0x31,0x39,0xff,0x3e,0x3e, - 0x4a,0xff,0x40,0x40,0x4c,0xff,0x41,0x41,0x4c,0xff,0x43,0x43,0x4e,0xff,0x43, - 0x44,0x4f,0xff,0x44,0x44,0x51,0xff,0x3f,0x3f,0x4a,0xff,0x46,0x47,0x53,0xff, - 0x53,0x53,0x62,0xff,0x54,0x54,0x61,0xff,0x57,0x57,0x64,0xff,0x5e,0x5e,0x6c, - 0xff,0x55,0x55,0x65,0xff,0x51,0x51,0x61,0xff,0x56,0x56,0x67,0xff,0x64,0x64, - 0x73,0xff,0x5d,0x5d,0x6c,0xff,0x5a,0x5a,0x68,0xff,0x59,0x59,0x66,0xff,0x57, - 0x57,0x65,0xff,0x57,0x57,0x64,0xff,0x55,0x55,0x62,0xff,0x54,0x54,0x60,0xff, - 0x51,0x51,0x5c,0xff,0x4e,0x4e,0x5a,0xff,0x4d,0x4d,0x58,0xff,0x4b,0x4c,0x57, - 0xff,0x4c,0x4c,0x57,0xff,0x4e,0x4e,0x59,0xff,0x52,0x52,0x5d,0xff,0x56,0x56, - 0x62,0xff,0x5b,0x5b,0x68,0xff,0x5e,0x5e,0x6c,0xff,0x61,0x61,0x6e,0xff,0x60, - 0x5f,0x6c,0xff,0x5a,0x5a,0x64,0xff,0x5c,0x5d,0x69,0xff,0x8a,0x8a,0x92,0xff, - 0x98,0x98,0xa0,0xff,0xd4,0xd4,0xda,0xff,0x60,0x60,0x63,0xd6,0x00,0x00,0x00, - 0x6e,0x00,0x00,0x00,0x4c,0x00,0x00,0x00,0x33,0x00,0x00,0x00,0x17,0x00,0x00, - 0x00,0x07,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x83,0x00,0x00,0x00,0x00, - 0x08,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x1a,0x00,0x00, - 0x00,0x36,0x00,0x00,0x00,0x5e,0x01,0x01,0x00,0x89,0x0f,0x0f,0x12,0xcb,0x3f, - 0x3f,0x4a,0xff,0x82,0x40,0x40,0x4c,0xff,0x06,0x42,0x42,0x4d,0xff,0x43,0x43, - 0x4f,0xff,0x44,0x44,0x50,0xff,0x3f,0x3f,0x4a,0xff,0x46,0x44,0x53,0xff,0x52, - 0x53,0x60,0xff,0x82,0x53,0x53,0x60,0xff,0x25,0x59,0x59,0x69,0xff,0x91,0x91, - 0x9e,0xff,0xb5,0xb5,0xbd,0xff,0xe6,0xe6,0xe8,0xff,0xf8,0xf8,0xfa,0xff,0x5c, - 0x5c,0x68,0xff,0x49,0x49,0x57,0xff,0x4e,0x4e,0x5a,0xff,0x4c,0x4c,0x58,0xff, - 0x4b,0x4b,0x55,0xff,0x48,0x4a,0x55,0xff,0x4b,0x4b,0x57,0xff,0x4d,0x4d,0x58, - 0xff,0x4f,0x4f,0x5a,0xff,0x52,0x51,0x5d,0xff,0x58,0x58,0x64,0xff,0x5c,0x5d, - 0x69,0xff,0x60,0x60,0x6d,0xff,0x62,0x62,0x6e,0xff,0x5d,0x5d,0x6b,0xff,0x52, - 0x52,0x5d,0xff,0x40,0x40,0x4a,0xf4,0x31,0x31,0x36,0xe2,0x1d,0x1c,0x20,0xcc, - 0x12,0x12,0x14,0xbf,0x2d,0x2d,0x30,0xc6,0x64,0x64,0x65,0xc7,0x54,0x54,0x54, - 0xbe,0x2c,0x2c,0x2b,0xa5,0x02,0x02,0x02,0x71,0x00,0x00,0x00,0x51,0x00,0x00, - 0x00,0x3d,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x04,0x00, - 0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x84,0x00,0x00,0x00,0x00,0x2a,0x00,0x00, - 0x00,0x05,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x2f,0x00, - 0x00,0x00,0x47,0x06,0x06,0x05,0x96,0x3c,0x3c,0x48,0xff,0x40,0x40,0x4c,0xff, - 0x40,0x40,0x4b,0xff,0x41,0x41,0x4c,0xff,0x43,0x43,0x4e,0xff,0x44,0x43,0x50, - 0xff,0x3e,0x3e,0x49,0xff,0x45,0x45,0x52,0xff,0x52,0x52,0x5f,0xff,0x53,0x52, - 0x5f,0xff,0x4e,0x4e,0x5b,0xff,0x50,0x50,0x60,0xff,0xfe,0xfe,0xff,0xff,0xff, - 0xff,0xff,0xff,0xef,0xef,0xf0,0xff,0xba,0xba,0xbe,0xff,0x47,0x48,0x53,0xff, - 0x41,0x42,0x4c,0xff,0x47,0x47,0x51,0xff,0x4d,0x4b,0x57,0xff,0x51,0x50,0x5d, - 0xff,0x55,0x55,0x62,0xff,0x5a,0x5a,0x67,0xff,0x5d,0x5d,0x6a,0xff,0x60,0x60, - 0x6e,0xff,0x61,0x60,0x6f,0xff,0x59,0x59,0x67,0xff,0x4d,0x4d,0x59,0xff,0x3a, - 0x38,0x42,0xf1,0x23,0x23,0x28,0xd8,0x10,0x0f,0x12,0xba,0x04,0x04,0x04,0x9d, - 0x00,0x00,0x00,0x83,0x00,0x00,0x00,0x6f,0x00,0x00,0x00,0x63,0x00,0x00,0x00, - 0x5d,0x82,0x00,0x00,0x00,0x5b,0x0a,0x00,0x00,0x00,0x57,0x00,0x00,0x00,0x4f, - 0x00,0x00,0x00,0x47,0x00,0x00,0x00,0x39,0x00,0x00,0x00,0x26,0x00,0x00,0x00, - 0x15,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0xff,0xff, - 0xff,0x00,0x84,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x06,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x27,0x08,0x08, - 0x08,0x84,0x3d,0x3e,0x49,0xff,0x82,0x3f,0x3f,0x4c,0xff,0x2a,0x40,0x40,0x4c, - 0xff,0x42,0x42,0x4d,0xff,0x43,0x43,0x50,0xff,0x3e,0x3e,0x4a,0xff,0x43,0x43, - 0x51,0xff,0x51,0x51,0x5e,0xff,0x52,0x52,0x5e,0xff,0x4f,0x4f,0x5b,0xff,0x47, - 0x46,0x52,0xff,0x6e,0x6e,0x77,0xff,0x57,0x57,0x62,0xff,0x3c,0x3b,0x46,0xff, - 0x35,0x36,0x41,0xff,0x46,0x47,0x53,0xff,0x52,0x52,0x5e,0xff,0x59,0x59,0x66, - 0xff,0x5d,0x5d,0x6b,0xff,0x60,0x60,0x6d,0xff,0x5f,0x5f,0x6c,0xff,0x57,0x57, - 0x64,0xff,0x4b,0x4a,0x55,0xfd,0x35,0x35,0x3d,0xee,0x1f,0x1f,0x24,0xd3,0x0d, - 0x0d,0x0d,0xb3,0x02,0x02,0x02,0x95,0x00,0x00,0x00,0x7d,0x00,0x00,0x00,0x6a, - 0x00,0x00,0x00,0x5f,0x00,0x00,0x00,0x54,0x00,0x00,0x00,0x4d,0x00,0x00,0x00, - 0x46,0x00,0x00,0x00,0x41,0x00,0x00,0x00,0x3c,0x00,0x00,0x00,0x39,0x00,0x00, - 0x00,0x38,0x00,0x00,0x00,0x37,0x00,0x00,0x00,0x34,0x00,0x00,0x00,0x2b,0x00, - 0x00,0x00,0x1f,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x02, - 0x82,0x00,0x00,0x00,0x00,0x01,0xff,0xff,0xff,0x00,0x85,0x00,0x00,0x00,0x00, - 0x31,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x07,0x01,0x01, - 0x01,0x15,0x06,0x06,0x06,0x68,0x39,0x39,0x43,0xff,0x44,0x44,0x50,0xff,0x44, - 0x44,0x51,0xff,0x42,0x42,0x4f,0xff,0x42,0x42,0x4e,0xff,0x43,0x43,0x4f,0xff, - 0x3d,0x3d,0x48,0xff,0x43,0x42,0x50,0xff,0x50,0x50,0x5d,0xff,0x50,0x50,0x5c, - 0xff,0x4f,0x4f,0x5b,0xff,0x47,0x47,0x53,0xff,0x3f,0x3f,0x4c,0xff,0x43,0x44, - 0x50,0xff,0x4d,0x4d,0x5b,0xff,0x57,0x57,0x65,0xff,0x5c,0x5c,0x6a,0xff,0x5b, - 0x5b,0x68,0xff,0x53,0x53,0x5f,0xff,0x44,0x44,0x4d,0xfa,0x30,0x30,0x36,0xe8, - 0x19,0x19,0x1d,0xcb,0x0a,0x0a,0x0a,0xae,0x01,0x01,0x01,0x92,0x00,0x00,0x00, - 0x7a,0x00,0x00,0x00,0x69,0x00,0x00,0x00,0x5c,0x00,0x00,0x00,0x52,0x00,0x00, - 0x00,0x4b,0x00,0x00,0x00,0x44,0x00,0x00,0x00,0x3e,0x00,0x00,0x00,0x36,0x00, - 0x00,0x00,0x2f,0x00,0x00,0x00,0x29,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x1e, - 0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x1a,0x00,0x00,0x00, - 0x18,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x05,0x00,0x00, - 0x00,0x02,0x83,0x00,0x00,0x00,0x00,0x01,0xff,0xff,0xff,0x00,0x87,0x00,0x00, - 0x00,0x00,0x28,0x00,0x00,0x00,0x01,0x02,0x02,0x02,0x09,0x03,0x03,0x02,0x34, - 0x06,0x06,0x06,0x86,0x14,0x14,0x17,0xc6,0x29,0x29,0x31,0xee,0x3e,0x3d,0x48, - 0xff,0x45,0x46,0x52,0xff,0x3f,0x3f,0x4b,0xff,0x39,0x39,0x44,0xff,0x42,0x43, - 0x4e,0xff,0x4f,0x4f,0x5b,0xff,0x4e,0x4e,0x5a,0xff,0x4b,0x4b,0x57,0xff,0x44, - 0x45,0x53,0xff,0x49,0x4a,0x57,0xff,0x55,0x55,0x62,0xff,0x4f,0x4f,0x5a,0xff, - 0x3f,0x3f,0x49,0xf8,0x2a,0x2a,0x30,0xe4,0x14,0x14,0x18,0xc5,0x07,0x06,0x07, - 0xa8,0x01,0x01,0x00,0x8c,0x00,0x00,0x00,0x76,0x00,0x00,0x00,0x65,0x00,0x00, - 0x00,0x5b,0x00,0x00,0x00,0x51,0x00,0x00,0x00,0x4b,0x00,0x00,0x00,0x44,0x00, - 0x00,0x00,0x3c,0x00,0x00,0x00,0x35,0x00,0x00,0x00,0x2e,0x00,0x00,0x00,0x27, - 0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x15,0x00,0x00,0x00, - 0x10,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x09,0x82,0x00, - 0x00,0x00,0x08,0x04,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x04,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x01,0x84,0x00,0x00,0x00,0x00,0x01,0xff,0xff,0xff,0x00, - 0x88,0x00,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x04,0x02,0x02,0x02,0x16,0x01, - 0x01,0x01,0x32,0x00,0x00,0x00,0x57,0x00,0x00,0x00,0x79,0x03,0x03,0x03,0xa2, - 0x11,0x12,0x15,0xd2,0x64,0x64,0x6d,0xff,0x4f,0x50,0x5a,0xff,0x3c,0x3c,0x49, - 0xff,0x46,0x45,0x53,0xff,0x40,0x40,0x4e,0xff,0x4c,0x4c,0x59,0xff,0x7d,0x7d, - 0x86,0xff,0x71,0x71,0x7a,0xff,0x10,0x10,0x12,0xc2,0x05,0x05,0x04,0xa2,0x00, - 0x00,0x00,0x87,0x00,0x00,0x00,0x74,0x00,0x00,0x00,0x62,0x00,0x00,0x00,0x58, - 0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x48,0x00,0x00,0x00,0x42,0x00,0x00,0x00, - 0x3b,0x00,0x00,0x00,0x34,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x26,0x00,0x00, - 0x00,0x1f,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x0f,0x00, - 0x00,0x00,0x0b,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x03, - 0x00,0x00,0x00,0x02,0x82,0x00,0x00,0x00,0x01,0x8a,0x00,0x00,0x00,0x00,0x01, - 0xff,0xff,0xff,0x00,0x88,0x00,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x02,0x00, - 0x00,0x00,0x0a,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x3d, - 0x00,0x00,0x00,0x53,0x00,0x00,0x00,0x79,0x6a,0x6a,0x6b,0xd4,0xa3,0xa3,0xac, - 0xfc,0x58,0x58,0x62,0xff,0x7b,0x7b,0x86,0xff,0xcf,0xcf,0xd4,0xff,0xc8,0xc7, - 0xce,0xff,0xcc,0xca,0xcd,0xf2,0x3a,0x3a,0x3b,0xb3,0x00,0x00,0x00,0x68,0x00, - 0x00,0x00,0x5a,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x47,0x00,0x00,0x00,0x41, - 0x00,0x00,0x00,0x39,0x00,0x00,0x00,0x32,0x00,0x00,0x00,0x2b,0x00,0x00,0x00, - 0x24,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x13,0x00,0x00, - 0x00,0x0e,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x04,0x00, - 0x00,0x00,0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x01,0x83,0x06,0x06,0x06, - 0x00,0x01,0x02,0x02,0x02,0x00,0x8b,0x00,0x00,0x00,0x00,0x01,0xff,0xff,0xff, - 0x00,0x88,0x00,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x04, - 0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x1f,0x00,0x00,0x00, - 0x29,0x01,0x01,0x01,0x42,0x00,0x00,0x00,0x63,0x05,0x05,0x05,0x8a,0x0d,0x0d, - 0x0e,0xa1,0x1c,0x1c,0x1d,0xa8,0x24,0x24,0x24,0xa4,0x10,0x10,0x0f,0x97,0x00, - 0x00,0x00,0x7b,0x00,0x00,0x00,0x5d,0x00,0x00,0x00,0x4b,0x00,0x00,0x00,0x3e, - 0x00,0x00,0x00,0x33,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x23,0x00,0x00,0x00, - 0x1c,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x0e,0x00,0x00, - 0x00,0x0a,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x02,0x82, - 0x00,0x00,0x00,0x01,0x82,0x06,0x06,0x06,0x00,0x02,0x02,0x02,0x02,0x00,0x04, - 0x04,0x04,0x00,0x83,0x06,0x06,0x06,0x00,0x01,0x02,0x02,0x02,0x00,0x8b,0x00, - 0x00,0x00,0x00,0x01,0xff,0xff,0xff,0x00,0x89,0x00,0x00,0x00,0x00,0x19,0x00, - 0x00,0x00,0x01,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x0b, - 0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x1c,0x01,0x01,0x01,0x2e,0x00,0x00,0x00, - 0x41,0x00,0x00,0x00,0x4e,0x00,0x00,0x00,0x53,0x00,0x00,0x00,0x54,0x00,0x00, - 0x00,0x51,0x00,0x00,0x00,0x4a,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x32,0x00, - 0x00,0x00,0x23,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x0d, - 0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x04,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x01,0x05,0x05,0x05,0x00,0x82,0x06,0x06,0x06,0x00,0x02, - 0x03,0x03,0x03,0x00,0x05,0x05,0x05,0x00,0x82,0x06,0x06,0x06,0x00,0x02,0x02, - 0x02,0x02,0x00,0x04,0x04,0x04,0x00,0x83,0x06,0x06,0x06,0x00,0x01,0x02,0x02, - 0x02,0x00,0x8b,0x00,0x00,0x00,0x00,0x01,0xff,0xff,0xff,0x00,0x8b,0x00,0x00, - 0x00,0x00,0x12,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x05, - 0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x1f,0x00,0x00,0x00, - 0x29,0x00,0x00,0x00,0x2f,0x00,0x00,0x00,0x31,0x00,0x00,0x00,0x30,0x00,0x00, - 0x00,0x2b,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x0e,0x00, - 0x00,0x00,0x07,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x01, - 0x9c,0x00,0x00,0x00,0x00,0x01,0xff,0xff,0xff,0x00,0x8d,0x00,0x00,0x00,0x00, - 0x0d,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x07,0x00,0x00, - 0x00,0x0c,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x16,0x00, - 0x00,0x00,0x15,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x08, - 0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x01,0x9f,0x00,0x00,0x00,0x00,0x01,0xff, - 0xff,0xff,0x00,0x8f,0x00,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x01,0x00,0x00, - 0x00,0x03,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x06,0x00, - 0x00,0x00,0x05,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x01, - 0xa1,0x00,0x00,0x00,0x00,0x01,0xff,0xff,0xff,0x00,0xb9,0x00,0x00,0x00,0x00, - 0x01,0xff,0xff,0xff,0x00,0xb9,0x00,0x00,0x00,0x00 -}; - -static const GdkPixdata tv_pixdata = { - 0x47646b50, /* Pixbuf magic: 'GdkP' */ - 24 + 10855, /* header length + pixel_data length */ - 0x2010002, /* pixdata_type */ - 232, /* rowstride */ - 58, /* width */ - 60, /* height */ - tv_pixdata_pixel_data /* pixel_data */ -}; diff --git a/src/image_data/vdpau.png b/src/image_data/vdpau.png Binary files differnew file mode 100644 index 0000000..25aee50 --- /dev/null +++ b/src/image_data/vdpau.png diff --git a/src/image_data/vdpau_pixdata.h b/src/image_data/vdpau_pixdata.h new file mode 100644 index 0000000..4100eff --- /dev/null +++ b/src/image_data/vdpau_pixdata.h @@ -0,0 +1,878 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +static guint8 vdpau_pixdata_pixel_data[] = { + 0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00, + 0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00, + 0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00, + 0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00, + 0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00, + 0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00, + 0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00, + 0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xa1,0x00,0x00,0x00,0x00, + 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x04,0xeb,0xeb,0xec,0x7b,0xed,0xee, + 0xee,0xbe,0xea,0xeb,0xec,0xbf,0xe8,0xe9,0xea,0xbf,0xe6,0xe8,0xe8,0xbf,0xe4, + 0xe5,0xe6,0xbf,0xe3,0xe5,0xe5,0xbe,0xde,0xe0,0xe0,0x7b,0x00,0x00,0x00,0x04, + 0x00,0x00,0x00,0x01,0x8c,0x00,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x01,0x00, + 0x00,0x00,0x04,0xbc,0xc0,0xc2,0x7b,0xbd,0xc1,0xc2,0xbe,0xba,0xbf,0xc0,0xbf, + 0xb9,0xbd,0xbf,0xbf,0xb7,0xbb,0xbd,0xbf,0xb6,0xba,0xbb,0xbf,0xb5,0xba,0xbc, + 0xbe,0xb0,0xb6,0xb7,0x7b,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x04,0xac,0xaf, + 0xb2,0x6b,0xaf,0xb4,0xb6,0xbe,0xaf,0xb3,0xb5,0xbf,0xae,0xb2,0xb5,0xbf,0xad, + 0xb2,0xb4,0xbf,0x85,0xad,0xb1,0xb3,0xbf,0x82,0xae,0xb2,0xb4,0xbf,0x82,0xaf, + 0xb3,0xb5,0xbf,0x0d,0xaf,0xb4,0xb6,0xbf,0xb0,0xb5,0xb7,0xbf,0xb1,0xb6,0xb8, + 0xbf,0xb2,0xb7,0xb8,0xbf,0xb3,0xb8,0xb9,0xbf,0xb5,0xba,0xbb,0xbf,0xb7,0xbb, + 0xbc,0xbf,0xb8,0xbc,0xbe,0xbf,0xb9,0xbe,0xc0,0xbf,0xb8,0xbc,0xbe,0x8d,0xba, + 0xbe,0xc0,0x7b,0xb2,0xb6,0xb8,0x25,0x00,0x00,0x00,0x01,0x84,0x00,0x00,0x00, + 0x00,0x1a,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x04,0xcc,0xcf,0xd0,0x8c,0xd1, + 0xd4,0xd5,0xbe,0xd2,0xd5,0xd6,0xbf,0xd4,0xd6,0xd8,0xbf,0xd7,0xd9,0xda,0xbf, + 0xd8,0xdb,0xdb,0xbf,0xdb,0xdc,0xdd,0xbf,0xdd,0xde,0xdf,0xbf,0xde,0xe0,0xe1, + 0xbf,0xe1,0xe1,0xe1,0xbf,0xe1,0xe3,0xe3,0xbf,0xe3,0xe5,0xe5,0xbf,0xe6,0xe6, + 0xe7,0xbf,0xe7,0xe8,0xe8,0xbf,0xe8,0xe9,0xea,0xbf,0xea,0xeb,0xec,0xbf,0xec, + 0xed,0xed,0xbf,0xed,0xee,0xef,0xbf,0xef,0xf0,0xf0,0xbf,0xf1,0xf1,0xf2,0xbf, + 0xef,0xef,0xf0,0x9e,0xf0,0xf0,0xf1,0x7b,0xe4,0xe4,0xe5,0x25,0x00,0x00,0x00, + 0x01,0x8c,0x00,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x02,0xdb,0xdb,0xdb,0x27, + 0xf7,0xf7,0xf7,0xbe,0xf5,0xf5,0xf6,0xbf,0xf4,0xf5,0xf4,0xbf,0xf3,0xf3,0xf3, + 0xbf,0xf2,0xf2,0xf3,0xbf,0xf1,0xf1,0xf2,0xbf,0xef,0xf0,0xf0,0xbf,0xef,0xf0, + 0xef,0xbe,0xeb,0xec,0xec,0x9d,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x01,0x89, + 0x00,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x04,0xd0,0xd3, + 0xd4,0x8c,0xd1,0xd4,0xd5,0xbe,0xcf,0xd1,0xd3,0xbf,0xcd,0xcf,0xd1,0xbf,0xca, + 0xcd,0xce,0xbf,0xca,0xcd,0xce,0xbe,0xc8,0xcb,0xcd,0xbe,0x00,0x00,0x00,0x05, + 0x00,0x00,0x00,0x01,0x8a,0x00,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x01,0x00, + 0x00,0x00,0x05,0xb1,0xb6,0xb7,0xbe,0xb0,0xb4,0xb6,0xbe,0xaf,0xb3,0xb4,0xbf, + 0xae,0xb1,0xb3,0xbf,0xad,0xb1,0xb3,0xbf,0xad,0xb2,0xb4,0xbe,0xab,0xb0,0xb2, + 0x8c,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x01,0x85,0x00,0x00,0x00,0x00,0x0d, + 0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0a,0x93,0x94,0x94,0x38,0xee,0xee,0xef, + 0xf1,0xee,0xef,0xf0,0xff,0xed,0xed,0xef,0xff,0xeb,0xec,0xec,0xff,0xe8,0xea, + 0xea,0xff,0xe6,0xe8,0xe9,0xff,0xe4,0xe6,0xe7,0xff,0xb4,0xb6,0xb6,0x40,0x00, + 0x00,0x00,0x05,0x00,0x00,0x00,0x01,0x8b,0x00,0x00,0x00,0x00,0x0d,0x00,0x00, + 0x00,0x05,0x8a,0x8d,0x8e,0x30,0xc1,0xc5,0xc7,0xff,0xbf,0xc3,0xc5,0xff,0xbd, + 0xc2,0xc4,0xff,0xbc,0xc0,0xc2,0xff,0xba,0xbf,0xc1,0xff,0xb9,0xbe,0xbf,0xff, + 0xb6,0xbb,0xbc,0xf1,0x6f,0x72,0x72,0x38,0x00,0x00,0x00,0x0d,0x00,0x00,0x00, + 0x0e,0xa0,0xa5,0xa6,0x85,0x82,0xb2,0xb7,0xb9,0xff,0x02,0xb1,0xb6,0xb7,0xff, + 0xb1,0xb6,0xb8,0xff,0x84,0xb0,0xb5,0xb7,0xff,0x83,0xb1,0xb6,0xb8,0xff,0x2f, + 0xb2,0xb7,0xb9,0xff,0xb3,0xb8,0xb9,0xff,0xb3,0xb8,0xbb,0xff,0xb4,0xb9,0xbb, + 0xff,0xb6,0xba,0xbc,0xff,0xb7,0xbc,0xbd,0xff,0xb8,0xbd,0xbf,0xff,0xb9,0xbe, + 0xbf,0xff,0xbb,0xbf,0xc1,0xff,0xbd,0xc1,0xc2,0xff,0xbe,0xc2,0xc3,0xff,0xc0, + 0xc4,0xc5,0xff,0xc2,0xc5,0xc7,0xff,0xc4,0xc7,0xc9,0xff,0xc1,0xc5,0xc6,0xbf, + 0xb2,0xb5,0xb6,0x39,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x0e,0xcb,0xce,0xcf,0xc3,0xd5,0xd8, + 0xd8,0xff,0xd7,0xda,0xda,0xff,0xd9,0xdc,0xdc,0xff,0xdb,0xdd,0xde,0xff,0xdd, + 0xe0,0xe0,0xff,0xdf,0xe1,0xe2,0xff,0xe1,0xe4,0xe4,0xff,0xe4,0xe5,0xe6,0xff, + 0xe6,0xe7,0xe8,0xff,0xe7,0xe9,0xea,0xff,0xea,0xea,0xeb,0xff,0xeb,0xec,0xec, + 0xff,0xed,0xee,0xef,0xff,0xef,0xef,0xf0,0xff,0xf0,0xf1,0xf1,0xff,0xf2,0xf3, + 0xf2,0xff,0xf3,0xf4,0xf4,0xff,0xf5,0xf5,0xf5,0xff,0xf6,0xf7,0xf7,0xff,0xf8, + 0xf8,0xf8,0xff,0xf8,0xf9,0xf9,0xff,0xf9,0xfa,0xfa,0xff,0xf1,0xf1,0xf0,0x9f, + 0xba,0xb9,0xba,0x17,0x00,0x00,0x00,0x01,0x89,0x00,0x00,0x00,0x00,0x0f,0x00, + 0x00,0x00,0x02,0x00,0x00,0x00,0x0a,0xf2,0xf2,0xf2,0xc2,0xfb,0xfb,0xfa,0xff, + 0xfa,0xfa,0xfa,0xff,0xf9,0xf9,0xf9,0xff,0xf7,0xf8,0xf8,0xff,0xf6,0xf6,0xf7, + 0xff,0xf5,0xf6,0xf6,0xff,0xf4,0xf4,0xf4,0xff,0xf2,0xf3,0xf3,0xff,0xf1,0xf1, + 0xf2,0xff,0xc9,0xc9,0xca,0x51,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x01,0x88, + 0x00,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x0e,0xce,0xd0, + 0xd0,0xc3,0xd4,0xd6,0xd7,0xff,0xd1,0xd4,0xd6,0xff,0xcf,0xd3,0xd4,0xff,0xce, + 0xd1,0xd2,0xff,0xcc,0xcf,0xd0,0xff,0xca,0xcd,0xcf,0xff,0x00,0x00,0x00,0x11, + 0x00,0x00,0x00,0x06,0x8a,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x06,0x00, + 0x00,0x00,0x11,0xb3,0xb7,0xb9,0xff,0xb2,0xb7,0xb9,0xff,0xb1,0xb6,0xb8,0xff, + 0x83,0xb0,0xb5,0xb7,0xff,0x03,0xa9,0xae,0xb0,0xc3,0x00,0x00,0x00,0x0e,0x00, + 0x00,0x00,0x04,0x85,0x00,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x04,0x00,0x00, + 0x00,0x13,0x00,0x00,0x00,0x2e,0xb2,0xb3,0xb4,0xa0,0xed,0xee,0xf0,0xff,0xec, + 0xed,0xed,0xff,0xea,0xeb,0xeb,0xff,0xe8,0xe9,0xea,0xff,0xe6,0xe7,0xe8,0xff, + 0xe3,0xe6,0xe6,0xff,0xd5,0xd7,0xd7,0xc6,0x00,0x00,0x00,0x10,0x00,0x00,0x00, + 0x03,0x8a,0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0f, + 0xb7,0xbb,0xbc,0xc6,0xc1,0xc4,0xc6,0xff,0xbf,0xc2,0xc4,0xff,0xbd,0xc1,0xc3, + 0xff,0xbc,0xc0,0xc1,0xff,0xba,0xbe,0xc0,0xff,0xb8,0xbd,0xbf,0xff,0x88,0x8c, + 0x8e,0xa0,0x00,0x00,0x00,0x2e,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x1c,0x90, + 0x94,0x95,0x94,0xb2,0xb7,0xb8,0xff,0x83,0xb1,0xb6,0xb8,0xff,0x83,0xb0,0xb5, + 0xb7,0xff,0x82,0xb1,0xb5,0xb8,0xff,0x01,0xb1,0xb6,0xb8,0xff,0x82,0xb2,0xb7, + 0xb9,0xff,0x2f,0xb3,0xb8,0xba,0xff,0xb4,0xb9,0xbb,0xff,0xb5,0xba,0xbb,0xff, + 0xb7,0xbb,0xbd,0xff,0xb7,0xbc,0xbd,0xff,0xb8,0xbd,0xbf,0xff,0xba,0xbf,0xc1, + 0xff,0xbb,0xbf,0xc2,0xff,0xbd,0xc1,0xc3,0xff,0xbf,0xc3,0xc5,0xff,0xc1,0xc4, + 0xc6,0xff,0xc2,0xc6,0xc8,0xff,0xc5,0xc8,0xc9,0xff,0xc6,0xc9,0xcb,0xff,0xc6, + 0xca,0xca,0xf0,0xa8,0xac,0xad,0x3d,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x01, + 0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x1e,0xc3,0xc5,0xc6,0xcc,0xd5,0xd8,0xd9, + 0xff,0xd8,0xda,0xdb,0xff,0xda,0xdd,0xde,0xff,0xdc,0xde,0xdf,0xff,0xde,0xe0, + 0xe1,0xff,0xe0,0xe2,0xe3,0xff,0xe3,0xe4,0xe5,0xff,0xe5,0xe6,0xe7,0xff,0xe6, + 0xe8,0xe8,0xff,0xe9,0xe9,0xea,0xff,0xea,0xec,0xeb,0xff,0xec,0xed,0xee,0xff, + 0xed,0xef,0xef,0xff,0xef,0xf0,0xf0,0xff,0xf1,0xf2,0xf2,0xff,0xf3,0xf3,0xf4, + 0xff,0xf4,0xf5,0xf5,0xff,0xf5,0xf6,0xf6,0xff,0xf7,0xf7,0xf8,0xff,0xf8,0xf8, + 0xf8,0xff,0xf9,0xfa,0xf9,0xff,0xfa,0xfb,0xfa,0xff,0xfb,0xfb,0xfb,0xff,0xf3, + 0xf3,0xf3,0xc2,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x02,0x87,0x00,0x00,0x00, + 0x00,0x10,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x08,0xc0,0xc1,0xc1,0x59,0xfa, + 0xfb,0xfb,0xff,0xfa,0xfb,0xfa,0xff,0xf9,0xf9,0xfa,0xff,0xf8,0xf8,0xf8,0xff, + 0xf7,0xf7,0xf8,0xff,0xf5,0xf6,0xf6,0xff,0xf5,0xf4,0xf5,0xff,0xf3,0xf3,0xf4, + 0xff,0xf1,0xf3,0xf3,0xff,0xf0,0xf1,0xf1,0xff,0xe4,0xe6,0xe6,0xd5,0x00,0x00, + 0x00,0x12,0x00,0x00,0x00,0x03,0x88,0x00,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, + 0x09,0x00,0x00,0x00,0x1e,0xc3,0xc5,0xc6,0xcc,0xd3,0xd5,0xd6,0xff,0xd1,0xd4, + 0xd5,0xff,0xcf,0xd2,0xd3,0xff,0xcd,0xd0,0xd2,0xff,0xcb,0xce,0xd0,0xff,0xc9, + 0xcd,0xce,0xff,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x0c,0x8a,0x00,0x00,0x00, + 0x00,0x05,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x25,0xb2,0xb7,0xb9,0xff,0xb2, + 0xb6,0xb9,0xff,0xb1,0xb5,0xb8,0xff,0x83,0xb0,0xb5,0xb7,0xff,0x03,0xa2,0xa6, + 0xa8,0xcc,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x09,0x85,0x00,0x00,0x00,0x00, + 0x0e,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x34,0x26,0x27, + 0x27,0x6b,0xe5,0xe6,0xe6,0xf7,0xeb,0xec,0xec,0xff,0xe9,0xea,0xeb,0xff,0xe6, + 0xe8,0xe9,0xff,0xe5,0xe6,0xe7,0xff,0xe2,0xe4,0xe5,0xff,0xe0,0xe3,0xe3,0xff, + 0xa4,0xa5,0xa7,0x5c,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x01,0x88,0x00,0x00, + 0x00,0x00,0x13,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x08,0x92,0x95,0x96,0x5b, + 0xc2,0xc5,0xc7,0xff,0xbf,0xc4,0xc5,0xff,0xbe,0xc2,0xc4,0xff,0xbc,0xc0,0xc2, + 0xff,0xbb,0xbf,0xc1,0xff,0xb9,0xbe,0xbf,0xff,0xb2,0xb5,0xb7,0xf7,0x1d,0x1e, + 0x1e,0x6b,0x00,0x00,0x00,0x34,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x25,0x83, + 0x87,0x88,0xa1,0xb2,0xb6,0xb8,0xff,0xb1,0xb6,0xb8,0xff,0xb0,0xb6,0xb8,0xff, + 0xb1,0xb6,0xb7,0xff,0x83,0xb0,0xb5,0xb7,0xff,0x01,0xb0,0xb6,0xb7,0xff,0x82, + 0xb1,0xb6,0xb8,0xff,0x32,0xb2,0xb6,0xb9,0xff,0xb2,0xb7,0xba,0xff,0xb4,0xb8, + 0xbb,0xff,0xb5,0xb9,0xbb,0xff,0xb5,0xba,0xbc,0xff,0xb7,0xbc,0xbe,0xff,0xb8, + 0xbc,0xbe,0xff,0xb9,0xbe,0xbf,0xff,0xbb,0xbf,0xc1,0xff,0xbd,0xc1,0xc2,0xff, + 0xbe,0xc2,0xc3,0xff,0xbf,0xc4,0xc6,0xff,0xc1,0xc6,0xc7,0xff,0xc4,0xc7,0xc8, + 0xff,0xc5,0xc9,0xca,0xff,0xc7,0xca,0xcb,0xff,0xc8,0xcc,0xce,0xff,0xc5,0xc9, + 0xca,0xe2,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x0d,0x00, + 0x00,0x00,0x2b,0xbd,0xbf,0xc0,0xd3,0xd7,0xd9,0xdb,0xff,0xd9,0xdb,0xdd,0xff, + 0xdb,0xde,0xde,0xff,0xdd,0xdf,0xe0,0xff,0xdf,0xe1,0xe2,0xff,0xe1,0xe3,0xe4, + 0xff,0xe3,0xe4,0xe6,0xff,0xe5,0xe6,0xe7,0xff,0xe7,0xe8,0xe9,0xff,0xe9,0xeb, + 0xeb,0xff,0xeb,0xec,0xed,0xff,0xed,0xee,0xee,0xff,0xee,0xf0,0xf0,0xff,0xf0, + 0xf1,0xf1,0xff,0xf2,0xf3,0xf2,0xff,0xf3,0xf4,0xf4,0xff,0xf5,0xf5,0xf5,0xff, + 0xf6,0xf6,0xf7,0xff,0xf7,0xf8,0xf8,0xff,0xf9,0xf8,0xf9,0xff,0xfa,0xfa,0xfa, + 0xff,0xfa,0xfb,0xfa,0xff,0xfb,0xfb,0xfb,0xff,0xfc,0xfc,0xfc,0xff,0xda,0xda, + 0xd9,0x76,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x01,0x86,0x00,0x00,0x00,0x00, + 0x11,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x13,0xe9,0xea,0xea,0xc9,0xfa,0xfa, + 0xfb,0xff,0xf9,0xfa,0xfa,0xff,0xf9,0xf9,0xfa,0xff,0xf8,0xf8,0xf8,0xff,0xf6, + 0xf7,0xf7,0xff,0xf5,0xf5,0xf6,0xff,0xf4,0xf5,0xf5,0xff,0xf2,0xf4,0xf4,0xff, + 0xf1,0xf2,0xf2,0xff,0xef,0xf0,0xf0,0xff,0xee,0xef,0xef,0xff,0xc2,0xc3,0xc4, + 0x7c,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x01,0x87,0x00,0x00,0x00,0x00,0x0b, + 0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x2b,0xbc,0xbf,0xbf,0xd3,0xd2,0xd4,0xd6, + 0xff,0xd0,0xd3,0xd4,0xff,0xce,0xd1,0xd2,0xff,0xcc,0xcf,0xd1,0xff,0xcb,0xce, + 0xcf,0xff,0xc8,0xcc,0xcd,0xff,0x00,0x00,0x00,0x35,0x00,0x00,0x00,0x12,0x8a, + 0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x35,0xb2,0xb7, + 0xb9,0xff,0xb2,0xb6,0xb8,0xff,0x84,0xb0,0xb5,0xb7,0xff,0x03,0x9c,0xa0,0xa2, + 0xd3,0x00,0x00,0x00,0x2b,0x00,0x00,0x00,0x0d,0x85,0x00,0x00,0x00,0x00,0x0e, + 0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x2b,0x00,0x00,0x00, + 0x5b,0x90,0x91,0x92,0xc2,0xea,0xeb,0xec,0xff,0xe8,0xe9,0xea,0xff,0xe6,0xe7, + 0xe8,0xff,0xe4,0xe5,0xe6,0xff,0xe2,0xe4,0xe4,0xff,0xe0,0xe2,0xe2,0xff,0xcd, + 0xcf,0xd0,0xca,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x03,0x88,0x00,0x00,0x00, + 0x00,0x12,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x13,0xb4,0xb7,0xb9,0xca,0xc1, + 0xc5,0xc7,0xff,0xbe,0xc3,0xc5,0xff,0xbe,0xc1,0xc3,0xff,0xbb,0xc0,0xc1,0xff, + 0xba,0xbe,0xc0,0xff,0xb8,0xbd,0xbe,0xff,0x70,0x73,0x74,0xc2,0x00,0x00,0x00, + 0x5b,0x00,0x00,0x00,0x2b,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x2a,0x7e,0x82, + 0x83,0xa8,0xb1,0xb6,0xb8,0xff,0xb0,0xb6,0xb8,0xff,0xb1,0xb5,0xb8,0xff,0x83, + 0xb0,0xb5,0xb7,0xff,0x04,0xb0,0xb6,0xb7,0xff,0xb1,0xb5,0xb7,0xff,0xb1,0xb6, + 0xb7,0xff,0xb2,0xb6,0xb8,0xff,0x82,0xb3,0xb7,0xb9,0xff,0x2b,0xb4,0xb9,0xbb, + 0xff,0xb5,0xba,0xbc,0xff,0xb6,0xbb,0xbd,0xff,0xb7,0xbc,0xbe,0xff,0xb8,0xbd, + 0xbf,0xff,0xba,0xbe,0xc0,0xff,0xbb,0xc0,0xc1,0xff,0xbd,0xc1,0xc3,0xff,0xbf, + 0xc3,0xc4,0xff,0xc1,0xc4,0xc6,0xff,0xc2,0xc6,0xc7,0xff,0xc4,0xc8,0xc9,0xff, + 0xc5,0xc9,0xca,0xff,0xc7,0xcb,0xcc,0xff,0xca,0xcd,0xcf,0xff,0xcb,0xce,0xd0, + 0xff,0x8a,0x8b,0x8d,0x4c,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x10,0x00,0x00, + 0x00,0x33,0xb9,0xbb,0xbc,0xd8,0xd8,0xda,0xdc,0xff,0xda,0xdc,0xdd,0xff,0xdc, + 0xde,0xdf,0xff,0xde,0xe0,0xe0,0xff,0xe0,0xe2,0xe2,0xff,0xe3,0xe4,0xe4,0xff, + 0xe4,0xe5,0xe6,0xff,0xe6,0xe8,0xe9,0xff,0xe8,0xe9,0xea,0xff,0xea,0xeb,0xec, + 0xff,0xeb,0xed,0xed,0xff,0xee,0xee,0xef,0xff,0xef,0xf0,0xf0,0xff,0xf1,0xf2, + 0xf1,0xff,0xf3,0xf3,0xf3,0xff,0xf4,0xf5,0xf4,0xff,0xf5,0xf5,0xf6,0xff,0xf6, + 0xf7,0xf7,0xff,0xf8,0xf8,0xf9,0xff,0xf9,0xf9,0xfa,0xff,0xfa,0xfb,0xfa,0xff, + 0xfb,0xfb,0xfb,0xff,0x82,0xfc,0xfc,0xfc,0xff,0x03,0xf0,0xf0,0xf0,0xd7,0x00, + 0x00,0x00,0x14,0x00,0x00,0x00,0x04,0x85,0x00,0x00,0x00,0x00,0x12,0x00,0x00, + 0x00,0x01,0x00,0x00,0x00,0x09,0xc4,0xc5,0xc5,0x6d,0xfb,0xfb,0xfb,0xff,0xfa, + 0xfa,0xfa,0xff,0xf9,0xf9,0xf9,0xff,0xf9,0xf9,0xf8,0xff,0xf7,0xf7,0xf8,0xff, + 0xf6,0xf7,0xf7,0xff,0xf4,0xf5,0xf5,0xff,0xf3,0xf4,0xf4,0xff,0xf1,0xf2,0xf3, + 0xff,0xf0,0xf1,0xf2,0xff,0xee,0xf0,0xf0,0xff,0xed,0xee,0xee,0xff,0xe3,0xe5, + 0xe5,0xe5,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x04,0x87,0x00,0x00,0x00,0x00, + 0x0b,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x33,0xb7,0xb9,0xba,0xd8,0xd1,0xd4, + 0xd5,0xff,0xcf,0xd2,0xd3,0xff,0xcd,0xd0,0xd1,0xff,0xcb,0xce,0xd0,0xff,0xca, + 0xcd,0xcf,0xff,0xc8,0xcb,0xcd,0xff,0x00,0x00,0x00,0x3d,0x00,0x00,0x00,0x14, + 0x8a,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x3d,0xb1, + 0xb6,0xb8,0xff,0xb1,0xb6,0xb7,0xff,0xb1,0xb5,0xb7,0xff,0x83,0xb0,0xb5,0xb7, + 0xff,0x03,0x99,0x9d,0x9f,0xd8,0x00,0x00,0x00,0x33,0x00,0x00,0x00,0x0f,0x86, + 0x00,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x1b,0x00,0x00, + 0x00,0x44,0x1e,0x1f,0x1f,0x82,0xd3,0xd5,0xd6,0xf3,0xe7,0xe9,0xea,0xff,0xe5, + 0xe7,0xe7,0xff,0xe3,0xe5,0xe5,0xff,0xe1,0xe3,0xe4,0xff,0xdf,0xe1,0xe1,0xff, + 0xdc,0xdf,0xdf,0xff,0x9e,0x9f,0xa0,0x5e,0x00,0x00,0x00,0x09,0x00,0x00,0x00, + 0x01,0x86,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x09, + 0x8d,0x90,0x91,0x5e,0xc1,0xc5,0xc7,0xff,0xc0,0xc4,0xc6,0xff,0xbe,0xc2,0xc4, + 0xff,0xbc,0xc1,0xc2,0xff,0xbb,0xc0,0xc1,0xff,0xba,0xbe,0xbf,0xff,0xa7,0xac, + 0xae,0xf3,0x18,0x18,0x18,0x82,0x00,0x00,0x00,0x44,0x00,0x00,0x00,0x1b,0x00, + 0x00,0x00,0x10,0x00,0x00,0x00,0x29,0x7d,0x80,0x83,0xa9,0x82,0xb1,0xb6,0xb8, + 0xff,0x84,0xb0,0xb5,0xb7,0xff,0x01,0x94,0x99,0x9a,0xf2,0x82,0x8c,0x8f,0x91, + 0xed,0x14,0x8d,0x90,0x93,0xec,0x8d,0x92,0x93,0xec,0x8e,0x92,0x93,0xec,0x8f, + 0x93,0x94,0xec,0x8f,0x93,0x95,0xec,0x90,0x94,0x96,0xec,0x92,0x96,0x97,0xec, + 0x93,0x97,0x97,0xec,0x93,0x97,0x99,0xec,0x94,0x97,0x99,0xed,0x96,0x99,0x9a, + 0xed,0xbf,0xc4,0xc5,0xff,0xc1,0xc5,0xc7,0xff,0xc3,0xc6,0xc8,0xff,0xc4,0xc8, + 0xca,0xff,0xc6,0xca,0xcb,0xff,0xc9,0xcc,0xce,0xff,0xca,0xce,0xcf,0xff,0xcc, + 0xd0,0xd0,0xff,0xa6,0xa9,0xaa,0x93,0x82,0x00,0x00,0x00,0x12,0x1d,0x00,0x00, + 0x00,0x34,0xba,0xbc,0xbd,0xd8,0xd9,0xdc,0xdc,0xff,0xdb,0xdd,0xdd,0xff,0xdd, + 0xde,0xdf,0xff,0xdf,0xe1,0xe2,0xff,0xe1,0xe3,0xe4,0xff,0xe3,0xe5,0xe6,0xff, + 0xb5,0xb5,0xb6,0xed,0xb6,0xb8,0xb8,0xed,0xb8,0xb9,0xba,0xec,0xba,0xbb,0xbb, + 0xec,0xbb,0xbd,0xbd,0xec,0xbd,0xbd,0xbd,0xec,0xbe,0xbe,0xbf,0xec,0xc0,0xc0, + 0xc0,0xec,0xc0,0xc0,0xc1,0xec,0xc0,0xc1,0xc1,0xed,0xc2,0xc3,0xc2,0xed,0xea, + 0xea,0xea,0xfb,0xf9,0xf9,0xf9,0xff,0xf9,0xf9,0xfa,0xff,0xfa,0xfb,0xfb,0xff, + 0xfb,0xfb,0xfb,0xff,0xfc,0xfc,0xfc,0xff,0xfc,0xfd,0xfd,0xff,0xfd,0xfd,0xfe, + 0xff,0x00,0x00,0x00,0x24,0x00,0x00,0x00,0x09,0x85,0x00,0x00,0x00,0x00,0x13, + 0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x15,0xf2,0xf2,0xf3,0xe5,0xfb,0xfb,0xfb, + 0xff,0xf9,0xfa,0xfa,0xff,0xf9,0xf9,0xf9,0xff,0xf7,0xf8,0xf8,0xff,0xf7,0xf7, + 0xf7,0xff,0xf5,0xf6,0xf6,0xff,0xc0,0xc0,0xc1,0xed,0xf2,0xf3,0xf4,0xff,0xf1, + 0xf2,0xf2,0xff,0xf0,0xf0,0xf1,0xff,0xee,0xf0,0xef,0xff,0xed,0xed,0xee,0xff, + 0xeb,0xec,0xec,0xff,0xc6,0xc7,0xc7,0x8c,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, + 0x01,0x86,0x00,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x34, + 0xb6,0xb8,0xb9,0xd8,0xd0,0xd3,0xd5,0xff,0xce,0xd1,0xd3,0xff,0xcc,0xd0,0xd0, + 0xff,0xcb,0xce,0xcf,0xff,0xc9,0xcc,0xcd,0xff,0xc7,0xca,0xcb,0xff,0x00,0x00, + 0x00,0x40,0x00,0x00,0x00,0x15,0x8a,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x15,0x00,0x00,0x00,0x40,0xb1,0xb6,0xb8,0xff,0xb1,0xb5,0xb8,0xff,0x84,0xb0, + 0xb5,0xb7,0xff,0x03,0x99,0x9d,0x9f,0xd8,0x00,0x00,0x00,0x34,0x00,0x00,0x00, + 0x10,0x86,0x00,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0d, + 0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x5e,0x7e,0x7f,0x7f,0xbc,0xe6,0xe7,0xe8, + 0xff,0xe4,0xe6,0xe6,0xff,0xe2,0xe4,0xe5,0xff,0xe0,0xe2,0xe2,0xff,0xde,0xe0, + 0xe0,0xff,0xdb,0xde,0xdf,0xff,0xc9,0xcc,0xcd,0xca,0x00,0x00,0x00,0x13,0x00, + 0x00,0x00,0x03,0x86,0x00,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x03,0x00,0x00, + 0x00,0x13,0xb4,0xb7,0xb9,0xca,0xc0,0xc5,0xc6,0xff,0xbf,0xc3,0xc5,0xff,0xbd, + 0xc1,0xc3,0xff,0xbc,0xc0,0xc2,0xff,0xba,0xbf,0xc0,0xff,0xb9,0xbd,0xbf,0xff, + 0x64,0x66,0x67,0xbc,0x00,0x00,0x00,0x5e,0x00,0x00,0x00,0x2c,0x00,0x00,0x00, + 0x0d,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x29,0x7d,0x81,0x83,0xa9,0xb1,0xb5, + 0xb8,0xff,0xb0,0xb6,0xb8,0xff,0xb0,0xb5,0xb8,0xff,0x83,0xb0,0xb5,0xb7,0xff, + 0x03,0x3d,0x3f,0x40,0xc2,0x00,0x00,0x00,0xa4,0x00,0x00,0x00,0xa0,0x89,0x00, + 0x00,0x00,0x9f,0x17,0x00,0x00,0x00,0xa0,0x00,0x00,0x00,0xa5,0x12,0x13,0x13, + 0xb2,0xa5,0xa8,0xaa,0xf0,0xc4,0xc8,0xc8,0xff,0xc6,0xc9,0xcb,0xff,0xc7,0xcb, + 0xcc,0xff,0xca,0xcd,0xce,0xff,0xcb,0xce,0xd0,0xff,0xce,0xd0,0xd1,0xff,0xb4, + 0xb8,0xb8,0xc3,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x34, + 0xba,0xbd,0xbe,0xd8,0xd9,0xdc,0xdc,0xff,0xdc,0xde,0xdf,0xff,0xdd,0xdf,0xe0, + 0xff,0xe0,0xe2,0xe2,0xff,0xe2,0xe4,0xe4,0xff,0xe4,0xe5,0xe6,0xff,0x00,0x00, + 0x00,0xa9,0x00,0x00,0x00,0xa2,0x87,0x00,0x00,0x00,0x9f,0x0d,0x00,0x00,0x00, + 0xa0,0x00,0x00,0x00,0xa4,0x17,0x17,0x17,0xb2,0xe1,0xe1,0xe1,0xf5,0xfa,0xfa, + 0xfb,0xff,0xfb,0xfb,0xfb,0xff,0xfb,0xfc,0xfc,0xff,0xfd,0xfc,0xfd,0xff,0xfd, + 0xfd,0xfe,0xff,0xfd,0xfe,0xfd,0xff,0xa3,0xa3,0xa3,0x6a,0x00,0x00,0x00,0x10, + 0x00,0x00,0x00,0x01,0x83,0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x01,0x00, + 0x00,0x00,0x0a,0xce,0xce,0xcd,0x7d,0xfb,0xfc,0xfb,0xff,0xfa,0xfa,0xfa,0xff, + 0xf9,0xfa,0xfa,0xff,0xf8,0xf9,0xf9,0xff,0xf7,0xf7,0xf8,0xff,0xf6,0xf7,0xf6, + 0xff,0xa6,0xa6,0xa7,0xe1,0x2c,0x2c,0x2c,0xbd,0xf1,0xf3,0xf3,0xff,0xf1,0xf1, + 0xf2,0xff,0xef,0xf0,0xf1,0xff,0xed,0xee,0xee,0xff,0xeb,0xed,0xed,0xff,0xea, + 0xeb,0xeb,0xff,0xe5,0xe5,0xe6,0xf2,0x62,0x62,0x63,0x28,0x00,0x00,0x00,0x04, + 0x86,0x00,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x34,0xb6, + 0xb9,0xb9,0xd8,0xd0,0xd2,0xd3,0xff,0xcd,0xd0,0xd2,0xff,0xcb,0xcf,0xd1,0xff, + 0xca,0xcd,0xcf,0xff,0xc8,0xcc,0xcd,0xff,0xc6,0xc9,0xcb,0xff,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x15,0x8a,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x15, + 0x00,0x00,0x00,0x40,0xb0,0xb6,0xb8,0xff,0x85,0xb0,0xb5,0xb7,0xff,0x03,0x99, + 0x9d,0x9f,0xd8,0x00,0x00,0x00,0x34,0x00,0x00,0x00,0x10,0x87,0x00,0x00,0x00, + 0x00,0x0e,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x42,0x00, + 0x00,0x00,0x76,0xd0,0xd2,0xd2,0xf3,0xe3,0xe4,0xe5,0xff,0xe1,0xe3,0xe4,0xff, + 0xdf,0xe1,0xe2,0xff,0xdd,0xdf,0xe0,0xff,0xdb,0xdd,0xdd,0xff,0xd8,0xdb,0xdc, + 0xff,0xa7,0xa9,0xaa,0x6d,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x01,0x84,0x00, + 0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x09,0x99,0x9c,0x9c, + 0x6d,0xc2,0xc6,0xc7,0xff,0xc0,0xc4,0xc5,0xff,0xbf,0xc3,0xc4,0xff,0xbc,0xc0, + 0xc3,0xff,0xbb,0xbf,0xc1,0xff,0xba,0xbe,0xc0,0xff,0xa7,0xac,0xae,0xf3,0x00, + 0x00,0x00,0x76,0x00,0x00,0x00,0x42,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x05, + 0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x29,0x7d,0x80,0x82,0xa9,0xb0,0xb6,0xb7, + 0xff,0xb0,0xb5,0xb8,0xff,0x84,0xb0,0xb5,0xb7,0xff,0x03,0x45,0x47,0x48,0xae, + 0x00,0x00,0x00,0x7c,0x00,0x00,0x00,0x70,0x89,0x00,0x00,0x00,0x6e,0x17,0x00, + 0x00,0x00,0x70,0x00,0x00,0x00,0x79,0x00,0x00,0x00,0x8b,0x56,0x58,0x59,0xc0, + 0xc5,0xc8,0xc9,0xff,0xc6,0xca,0xcb,0xff,0xc8,0xcc,0xcd,0xff,0xcb,0xce,0xcf, + 0xff,0xcc,0xcf,0xd1,0xff,0xce,0xd1,0xd2,0xff,0xb8,0xbb,0xbc,0xd3,0x00,0x00, + 0x00,0x27,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x34,0xbb,0xbe,0xbf,0xd8,0xda, + 0xdd,0xde,0xff,0xdc,0xdf,0xdf,0xff,0xdf,0xe1,0xe2,0xff,0xe1,0xe3,0xe3,0xff, + 0xe3,0xe4,0xe5,0xff,0xe4,0xe6,0xe7,0xff,0x00,0x00,0x00,0x89,0x00,0x00,0x00, + 0x77,0x87,0x00,0x00,0x00,0x6e,0x0d,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x79, + 0x00,0x00,0x00,0x8b,0x91,0x91,0x91,0xcd,0xfa,0xfb,0xfa,0xff,0xfb,0xfc,0xfb, + 0xff,0xfc,0xfc,0xfc,0xff,0xfc,0xfd,0xfc,0xff,0xfd,0xfe,0xfd,0xff,0xfe,0xfd, + 0xfe,0xff,0x96,0x96,0x96,0x73,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x02,0x83, + 0x00,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x04,0x71,0x71,0x71,0x26,0xf3,0xf3, + 0xf3,0xe5,0xfb,0xfb,0xfb,0xff,0xfa,0xfa,0xfb,0xff,0xf9,0xf9,0xf9,0xff,0xf8, + 0xf8,0xf9,0xff,0xf7,0xf7,0xf7,0xff,0xf5,0xf6,0xf6,0xff,0x30,0x30,0x30,0xae, + 0x00,0x00,0x00,0x9c,0x9a,0x9a,0x9a,0xd6,0xf0,0xf0,0xf1,0xff,0xee,0xef,0xef, + 0xff,0xec,0xee,0xee,0xff,0xeb,0xec,0xec,0xff,0xe9,0xea,0xeb,0xff,0xe7,0xe9, + 0xea,0xff,0xc7,0xc8,0xc9,0x9d,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x01,0x85, + 0x00,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x34,0xb4,0xb7, + 0xb8,0xd8,0xcf,0xd1,0xd3,0xff,0xcc,0xd0,0xd1,0xff,0xca,0xce,0xd0,0xff,0xc9, + 0xcc,0xce,0xff,0xc7,0xca,0xcc,0xff,0xc5,0xc9,0xca,0xff,0x00,0x00,0x00,0x40, + 0x00,0x00,0x00,0x15,0x8a,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x15,0x00, + 0x00,0x00,0x40,0xb1,0xb5,0xb8,0xff,0x85,0xb0,0xb5,0xb7,0xff,0x03,0x99,0x9d, + 0x9f,0xd8,0x00,0x00,0x00,0x34,0x00,0x00,0x00,0x10,0x87,0x00,0x00,0x00,0x00, + 0x0e,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x2a,0x00,0x00, + 0x00,0x5a,0x6c,0x6d,0x6d,0xb3,0xe2,0xe4,0xe5,0xff,0xe0,0xe2,0xe3,0xff,0xde, + 0xe0,0xe1,0xff,0xdc,0xde,0xdf,0xff,0xda,0xdc,0xdd,0xff,0xd7,0xda,0xdb,0xff, + 0xcd,0xd0,0xd1,0xe5,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x04,0x84,0x00,0x00, + 0x00,0x00,0x12,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x15,0xbc,0xc0,0xc2,0xe5, + 0xc1,0xc5,0xc7,0xff,0xbf,0xc4,0xc5,0xff,0xbd,0xc2,0xc4,0xff,0xbc,0xc0,0xc2, + 0xff,0xba,0xbe,0xc1,0xff,0xb9,0xbe,0xbf,0xff,0x57,0x59,0x5a,0xb3,0x00,0x00, + 0x00,0x5a,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x01,0x00, + 0x00,0x00,0x0a,0x00,0x00,0x00,0x29,0x7d,0x80,0x82,0xa9,0xb1,0xb6,0xb8,0xff, + 0x84,0xb0,0xb5,0xb7,0xff,0x04,0xb0,0xb5,0xb8,0xff,0x51,0x53,0x53,0x95,0x00, + 0x00,0x00,0x4c,0x00,0x00,0x00,0x38,0x89,0x00,0x00,0x00,0x34,0x17,0x00,0x00, + 0x00,0x36,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x5a,0x00,0x00,0x00,0x7f,0xc5, + 0xc9,0xcb,0xff,0xc7,0xcb,0xcc,0xff,0xc9,0xcd,0xce,0xff,0xcb,0xce,0xd0,0xff, + 0xcd,0xd1,0xd1,0xff,0xd0,0xd2,0xd3,0xff,0xb7,0xb9,0xbb,0xd6,0x00,0x00,0x00, + 0x2e,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x34,0xbc,0xbf,0xbf,0xd8,0xdc,0xde, + 0xdf,0xff,0xde,0xe0,0xe1,0xff,0xe0,0xe2,0xe2,0xff,0xe2,0xe4,0xe4,0xff,0xe4, + 0xe5,0xe6,0xff,0xe5,0xe7,0xe8,0xff,0x00,0x00,0x00,0x63,0x00,0x00,0x00,0x43, + 0x87,0x00,0x00,0x00,0x34,0x07,0x00,0x00,0x00,0x36,0x00,0x00,0x00,0x42,0x00, + 0x00,0x00,0x5e,0x9d,0x9d,0x9d,0xbe,0xfa,0xfb,0xfb,0xff,0xfc,0xfb,0xfc,0xff, + 0xfc,0xfd,0xfd,0xff,0x82,0xfd,0xfd,0xfd,0xff,0x04,0xfe,0xfe,0xfe,0xff,0x8d, + 0x8d,0x8d,0x7a,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x04,0x82,0x00,0x00,0x00, + 0x00,0x16,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0c,0xd4,0xd4,0xd4,0x8d,0xfb, + 0xfb,0xfb,0xff,0xfa,0xfb,0xfa,0xff,0xfa,0xf9,0xfa,0xff,0xf8,0xf9,0xf9,0xff, + 0xf7,0xf8,0xf8,0xff,0xf6,0xf7,0xf7,0xff,0xae,0xaf,0xaf,0xd7,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x77,0x1b,0x1b,0x1c,0x93,0xe5,0xe7,0xe6,0xf9,0xed,0xef, + 0xef,0xff,0xec,0xed,0xed,0xff,0xea,0xeb,0xec,0xff,0xe8,0xea,0xea,0xff,0xe6, + 0xe9,0xe9,0xff,0xe5,0xe7,0xe8,0xff,0x85,0x85,0x86,0x3a,0x00,0x00,0x00,0x06, + 0x85,0x00,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x34,0xb3, + 0xb6,0xb8,0xd8,0xcd,0xd1,0xd2,0xff,0xcc,0xcf,0xd0,0xff,0xca,0xce,0xcf,0xff, + 0xc8,0xcc,0xcd,0xff,0xc6,0xca,0xcb,0xff,0xc4,0xc8,0xca,0xff,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x15,0x8a,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x15, + 0x00,0x00,0x00,0x40,0xb0,0xb6,0xb7,0xff,0x85,0xb0,0xb5,0xb7,0xff,0x03,0x99, + 0x9d,0x9f,0xd8,0x00,0x00,0x00,0x34,0x00,0x00,0x00,0x10,0x88,0x00,0x00,0x00, + 0x00,0x0e,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x3e,0x00, + 0x00,0x00,0x73,0xb8,0xb9,0xba,0xe5,0xdf,0xe1,0xe2,0xff,0xdd,0xdf,0xe0,0xff, + 0xdb,0xdd,0xde,0xff,0xd8,0xdb,0xdc,0xff,0xd7,0xd9,0xda,0xff,0xd4,0xd7,0xd9, + 0xff,0xab,0xae,0xaf,0x7d,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x01,0x82,0x00, + 0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0a,0xa0,0xa3,0xa5, + 0x7d,0xc2,0xc6,0xc7,0xff,0xc0,0xc5,0xc6,0xff,0xbf,0xc2,0xc5,0xff,0xbc,0xc1, + 0xc2,0xff,0xbb,0xbf,0xc1,0xff,0xba,0xbe,0xc0,0xff,0x97,0x9a,0x9c,0xe5,0x00, + 0x00,0x00,0x73,0x00,0x00,0x00,0x3e,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x04, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x29,0x7c,0x80,0x82, + 0xa9,0xb0,0xb5,0xb7,0xff,0xb0,0xb5,0xb8,0xff,0x82,0xb0,0xb5,0xb7,0xff,0x05, + 0xb1,0xb5,0xb7,0xff,0xb1,0xb5,0xb8,0xff,0x5a,0x5c,0x5d,0x86,0x00,0x00,0x00, + 0x2e,0x00,0x00,0x00,0x15,0x8a,0x00,0x00,0x00,0x10,0x16,0x00,0x00,0x00,0x18, + 0x00,0x00,0x00,0x34,0x00,0x00,0x00,0x60,0xc6,0xca,0xcb,0xff,0xc8,0xcb,0xcd, + 0xff,0xca,0xcd,0xcf,0xff,0xcc,0xcf,0xd0,0xff,0xce,0xd1,0xd3,0xff,0xd0,0xd3, + 0xd4,0xff,0xb6,0xb9,0xb9,0xd8,0x00,0x00,0x00,0x32,0x00,0x00,0x00,0x1e,0x00, + 0x00,0x00,0x34,0xbe,0xc0,0xc0,0xd8,0xdc,0xdf,0xdf,0xff,0xde,0xe0,0xe1,0xff, + 0xe0,0xe2,0xe3,0xff,0xe2,0xe4,0xe4,0xff,0xe5,0xe6,0xe6,0xff,0xe6,0xe8,0xe9, + 0xff,0x00,0x00,0x00,0x4a,0x00,0x00,0x00,0x23,0x88,0x00,0x00,0x00,0x10,0x05, + 0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x3d,0xb5,0xb5,0xb6,0xbb,0xfb,0xfb,0xfb, + 0xff,0xfc,0xfc,0xfc,0xff,0x82,0xfd,0xfd,0xfd,0xff,0x05,0xfe,0xfd,0xfe,0xff, + 0xfd,0xfe,0xfe,0xff,0x89,0x89,0x89,0x7e,0x00,0x00,0x00,0x1f,0x00,0x00,0x00, + 0x05,0x82,0x00,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x05,0x6b,0x6b,0x6b,0x28, + 0xf7,0xf8,0xf8,0xf2,0xfb,0xfb,0xfb,0xff,0xfa,0xfb,0xfb,0xff,0xf9,0xf9,0xfa, + 0xff,0xf8,0xf8,0xf9,0xff,0xf6,0xf7,0xf7,0xff,0xf6,0xf6,0xf7,0xff,0x39,0x39, + 0x39,0x93,0x00,0x00,0x00,0x5a,0x00,0x00,0x00,0x4f,0x00,0x00,0x00,0x69,0x8f, + 0x8f,0x90,0xc6,0xed,0xee,0xee,0xff,0xeb,0xec,0xed,0xff,0xea,0xeb,0xeb,0xff, + 0xe8,0xe9,0xe9,0xff,0xe6,0xe7,0xe8,0xff,0xe4,0xe5,0xe7,0xff,0xc9,0xca,0xcc, + 0xac,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x02,0x84,0x00,0x00,0x00,0x00,0x0b, + 0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x34,0xb3,0xb6,0xb7,0xd8,0xcd,0xd0,0xd1, + 0xff,0xca,0xce,0xcf,0xff,0xc9,0xcc,0xce,0xff,0xc7,0xca,0xcc,0xff,0xc6,0xc9, + 0xcb,0xff,0xc4,0xc7,0xc9,0xff,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x15,0x8a, + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x40,0x86,0xb0, + 0xb5,0xb7,0xff,0x03,0x99,0x9d,0x9f,0xd8,0x00,0x00,0x00,0x34,0x00,0x00,0x00, + 0x10,0x88,0x00,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0a, + 0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x56,0x5a,0x5b,0x5c,0xa8,0xde,0xe0,0xe1, + 0xff,0xdc,0xdf,0xdf,0xff,0xda,0xdc,0xdd,0xff,0xd8,0xda,0xdb,0xff,0xd6,0xd8, + 0xd9,0xff,0xd3,0xd6,0xd8,0xff,0xca,0xcd,0xcd,0xe5,0x00,0x00,0x00,0x16,0x00, + 0x00,0x00,0x04,0x82,0x00,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x04,0x00,0x00, + 0x00,0x16,0xbc,0xc0,0xc2,0xe5,0xc1,0xc5,0xc7,0xff,0xc0,0xc4,0xc5,0xff,0xbe, + 0xc2,0xc4,0xff,0xbc,0xc0,0xc2,0xff,0xbb,0xbf,0xc0,0xff,0xb9,0xbe,0xbf,0xff, + 0x4a,0x4c,0x4d,0xa8,0x00,0x00,0x00,0x56,0x00,0x00,0x00,0x27,0x00,0x00,0x00, + 0x0a,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x00,0x00, + 0x00,0x29,0x7d,0x80,0x81,0xa9,0x84,0xb0,0xb5,0xb7,0xff,0x05,0xb1,0xb5,0xb8, + 0xff,0xb1,0xb6,0xb8,0xff,0x5f,0x62,0x63,0x7f,0x00,0x00,0x00,0x20,0x00,0x00, + 0x00,0x06,0x8a,0x00,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x1c,0x00,0x00,0x00,0x4a,0xc7,0xcb,0xcc,0xff,0xc9,0xcc,0xce,0xff,0xca,0xce, + 0xd0,0xff,0xcd,0xd0,0xd1,0xff,0xcf,0xd2,0xd3,0xff,0xd1,0xd4,0xd5,0xff,0xb7, + 0xb9,0xba,0xd8,0x00,0x00,0x00,0x34,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x34, + 0xbf,0xc0,0xc1,0xd8,0xdd,0xdf,0xe0,0xff,0xe0,0xe1,0xe2,0xff,0xe2,0xe3,0xe4, + 0xff,0xe3,0xe6,0xe6,0xff,0xe6,0xe6,0xe7,0xff,0xe7,0xe9,0xe9,0xff,0x00,0x00, + 0x00,0x40,0x00,0x00,0x00,0x15,0x87,0x00,0x00,0x00,0x00,0x27,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0d,0xa4,0xa5,0xa5,0x67,0xf4,0xf4,0xf4,0xf5,0xfb,0xfb, + 0xfc,0xff,0xfd,0xfc,0xfc,0xff,0xfd,0xfd,0xfd,0xff,0xfd,0xfe,0xfd,0xff,0xfe, + 0xfe,0xfd,0xff,0xfe,0xfd,0xfe,0xff,0x88,0x88,0x88,0x7f,0x00,0x00,0x00,0x20, + 0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x0d,0xd3,0xd3,0xd3,0x8e,0xfb,0xfb,0xfb,0xff,0xfa,0xfb,0xfb,0xff,0xf9,0xfa, + 0xfa,0xff,0xf9,0xf9,0xf9,0xff,0xf8,0xf7,0xf8,0xff,0xf7,0xf7,0xf7,0xff,0xb0, + 0xb1,0xb1,0xd5,0x00,0x00,0x00,0x66,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x2b, + 0x00,0x00,0x00,0x48,0x1f,0x1f,0x1f,0x83,0xe2,0xe3,0xe3,0xf9,0xea,0xeb,0xec, + 0xff,0xe9,0xea,0xeb,0xff,0xe7,0xe8,0xe9,0xff,0xe5,0xe7,0xe8,0xff,0xe4,0xe5, + 0xe6,0xff,0xe1,0xe3,0xe3,0xff,0xa7,0xa9,0xa9,0x5b,0x00,0x00,0x00,0x07,0x00, + 0x00,0x00,0x01,0x83,0x00,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x10,0x00,0x00, + 0x00,0x34,0xb3,0xb5,0xb6,0xd8,0xcc,0xcf,0xd1,0xff,0xca,0xcd,0xcf,0xff,0xc8, + 0xcc,0xcd,0xff,0xc6,0xca,0xcb,0xff,0xc5,0xc9,0xca,0xff,0xc3,0xc6,0xc9,0xff, + 0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x15,0x8a,0x00,0x00,0x00,0x00,0x02,0x00, + 0x00,0x00,0x15,0x00,0x00,0x00,0x40,0x86,0xb0,0xb5,0xb7,0xff,0x03,0x99,0x9d, + 0x9f,0xd8,0x00,0x00,0x00,0x34,0x00,0x00,0x00,0x10,0x89,0x00,0x00,0x00,0x00, + 0x0d,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x3a,0x00,0x00, + 0x00,0x6e,0xb4,0xb6,0xb7,0xe5,0xdb,0xdd,0xdf,0xff,0xd9,0xdc,0xdc,0xff,0xd7, + 0xd9,0xda,0xff,0xd5,0xd7,0xd9,0xff,0xd2,0xd6,0xd7,0xff,0xd1,0xd4,0xd5,0xff, + 0xa6,0xaa,0xaa,0x7e,0x00,0x00,0x00,0x0b,0x82,0x00,0x00,0x00,0x01,0x0d,0x00, + 0x00,0x00,0x0b,0x9e,0xa2,0xa2,0x7e,0xc2,0xc6,0xc8,0xff,0xc0,0xc5,0xc6,0xff, + 0xbf,0xc3,0xc4,0xff,0xbd,0xc1,0xc3,0xff,0xbc,0xc0,0xc2,0xff,0xba,0xbe,0xc0, + 0xff,0x97,0x9a,0x9b,0xe5,0x00,0x00,0x00,0x6e,0x00,0x00,0x00,0x3a,0x00,0x00, + 0x00,0x15,0x00,0x00,0x00,0x04,0x82,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x0a,0x00,0x00,0x00,0x29,0x7c,0x80,0x81,0xa9,0x85,0xb0,0xb5,0xb7,0xff,0x04, + 0xb1,0xb6,0xb8,0xff,0x60,0x62,0x63,0x7f,0x00,0x00,0x00,0x20,0x00,0x00,0x00, + 0x06,0x8a,0x00,0x00,0x00,0x00,0x44,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x16, + 0x00,0x00,0x00,0x42,0xc8,0xcb,0xcc,0xff,0xc9,0xcd,0xcf,0xff,0xcc,0xcf,0xd0, + 0xff,0xce,0xd1,0xd2,0xff,0xcf,0xd3,0xd4,0xff,0xd1,0xd4,0xd6,0xff,0xb7,0xba, + 0xba,0xd8,0x00,0x00,0x00,0x34,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x34,0xbf, + 0xc1,0xc1,0xd8,0xde,0xe0,0xe1,0xff,0xe0,0xe2,0xe3,0xff,0xe2,0xe4,0xe5,0xff, + 0xe4,0xe6,0xe6,0xff,0xe7,0xe8,0xe9,0xff,0xe8,0xea,0xea,0xff,0xea,0xeb,0xec, + 0xff,0xec,0xed,0xed,0xff,0xed,0xef,0xef,0xff,0xef,0xf0,0xf0,0xff,0xf0,0xf1, + 0xf2,0xff,0xf2,0xf4,0xf3,0xff,0xf4,0xf4,0xf5,0xff,0xf5,0xf6,0xf6,0xff,0xf6, + 0xf7,0xf7,0xff,0xf8,0xf8,0xf9,0xff,0xf9,0xfa,0xfa,0xff,0xfa,0xfa,0xfa,0xff, + 0xfb,0xfc,0xfc,0xff,0xfc,0xfc,0xfc,0xff,0xfd,0xfc,0xfd,0xff,0xfd,0xfd,0xfd, + 0xff,0xfe,0xfe,0xfd,0xff,0xfd,0xfd,0xfe,0xff,0xfe,0xfe,0xfe,0xff,0x00,0x00, + 0x00,0x4f,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x06,0x97,0x96,0x97,0x39,0xfb,0xfc,0xfc,0xff,0xfb,0xfb,0xfb,0xff, + 0xfa,0xfb,0xfb,0xff,0xf9,0xf9,0xf9,0xff,0xf8,0xf8,0xf8,0xff,0xf7,0xf7,0xf7, + 0xff,0xf6,0xf7,0xf7,0xff,0x39,0x3a,0x3a,0x91,0x00,0x00,0x00,0x4b,0x00,0x00, + 0x00,0x20,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x2e,0x00,0x00,0x00,0x5f,0x8f, + 0x8f,0x90,0xc4,0xe9,0xeb,0xec,0xff,0xe8,0xe9,0xea,0xff,0xe6,0xe7,0xe8,0xff, + 0xe4,0xe6,0xe6,0xff,0xe2,0xe4,0xe5,0xff,0xe1,0xe3,0xe3,0xff,0xce,0xd0,0xd1, + 0xc9,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x03,0x83,0x00,0x00,0x00,0x00,0x0b, + 0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x34,0xb2,0xb4,0xb5,0xd8,0xcb,0xce,0xcf, + 0xff,0xc9,0xcd,0xce,0xff,0xc7,0xcb,0xcd,0xff,0xc5,0xc9,0xcb,0xff,0xc4,0xc8, + 0xc9,0xff,0xc2,0xc6,0xc8,0xff,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x15,0x8a, + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x40,0x86,0xb0, + 0xb5,0xb7,0xff,0x03,0x99,0x9d,0x9f,0xd8,0x00,0x00,0x00,0x34,0x00,0x00,0x00, + 0x10,0x89,0x00,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x09, + 0x00,0x00,0x00,0x24,0x00,0x00,0x00,0x52,0x47,0x47,0x48,0x9e,0xda,0xdd,0xdd, + 0xff,0xd8,0xdb,0xdb,0xff,0xd6,0xd8,0xd9,0xff,0xd4,0xd6,0xd8,0xff,0xd1,0xd5, + 0xd5,0xff,0xd0,0xd3,0xd4,0xff,0xc6,0xca,0xcb,0xe5,0x00,0x00,0x00,0x17,0x82, + 0x00,0x00,0x00,0x04,0x0d,0x00,0x00,0x00,0x17,0xbc,0xc0,0xc2,0xe5,0xc1,0xc5, + 0xc7,0xff,0xc0,0xc4,0xc5,0xff,0xbe,0xc2,0xc4,0xff,0xbc,0xc1,0xc2,0xff,0xbb, + 0xbf,0xc1,0xff,0xb9,0xbd,0xbf,0xff,0x3b,0x3d,0x3d,0x9e,0x00,0x00,0x00,0x52, + 0x00,0x00,0x00,0x24,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x01,0x82,0x00,0x00, + 0x00,0x00,0x03,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x29,0x7c,0x80,0x81,0xa9, + 0x83,0xb0,0xb5,0xb7,0xff,0x06,0xb1,0xb5,0xb7,0xff,0xb1,0xb6,0xb7,0xff,0xb2, + 0xb7,0xb8,0xff,0x5f,0x62,0x63,0x7f,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x06, + 0x8b,0x00,0x00,0x00,0x00,0x44,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x40,0xc8, + 0xcc,0xce,0xff,0xca,0xce,0xcf,0xff,0xcc,0xd0,0xd1,0xff,0xcf,0xd2,0xd3,0xff, + 0xd1,0xd3,0xd5,0xff,0xd2,0xd6,0xd6,0xff,0xb9,0xbb,0xbc,0xd8,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x34,0xc0,0xc1,0xc2,0xd8,0xe0,0xe1, + 0xe1,0xff,0xe1,0xe2,0xe4,0xff,0xe4,0xe5,0xe6,0xff,0xe5,0xe7,0xe7,0xff,0xe6, + 0xe8,0xe9,0xff,0xe9,0xea,0xeb,0xff,0xeb,0xec,0xed,0xff,0xed,0xee,0xee,0xff, + 0xee,0xef,0xf0,0xff,0xf0,0xf1,0xf1,0xff,0xf2,0xf2,0xf2,0xff,0xf3,0xf3,0xf4, + 0xff,0xf4,0xf5,0xf6,0xff,0xf6,0xf7,0xf6,0xff,0xf7,0xf7,0xf8,0xff,0xf9,0xf9, + 0xf9,0xff,0xfa,0xf9,0xfa,0xff,0xfb,0xfa,0xfb,0xff,0xfb,0xfb,0xfc,0xff,0xfc, + 0xfd,0xfd,0xff,0xfd,0xfd,0xfc,0xff,0xfe,0xfe,0xfe,0xff,0xfe,0xfe,0xfd,0xff, + 0xfe,0xfe,0xfe,0xff,0xe0,0xe0,0xe0,0xe7,0x00,0x00,0x00,0x4a,0x00,0x00,0x00, + 0x1c,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0f,0xe0,0xe0, + 0xe0,0xac,0xfb,0xfc,0xfc,0xff,0xfa,0xfb,0xfb,0xff,0xf9,0xfa,0xfa,0xff,0xf9, + 0xf9,0xf9,0xff,0xf8,0xf8,0xf9,0xff,0xf6,0xf7,0xf7,0xff,0xb0,0xb1,0xb1,0xd5, + 0x00,0x00,0x00,0x66,0x00,0x00,0x00,0x32,0x00,0x00,0x00,0x10,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x44,0x1e,0x1e,0x1f,0x82,0xdf,0xe1, + 0xe0,0xf9,0xe7,0xe9,0xe9,0xff,0xe5,0xe6,0xe8,0xff,0xe3,0xe5,0xe6,0xff,0xe2, + 0xe4,0xe4,0xff,0xe0,0xe2,0xe3,0xff,0xdd,0xe0,0xe1,0xff,0xa0,0xa2,0xa3,0x5d, + 0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x01,0x82,0x00,0x00,0x00,0x00,0x0b,0x00, + 0x00,0x00,0x10,0x00,0x00,0x00,0x34,0xb1,0xb4,0xb4,0xd8,0xca,0xcd,0xcf,0xff, + 0xc8,0xcc,0xcd,0xff,0xc7,0xca,0xcc,0xff,0xc5,0xc9,0xca,0xff,0xc3,0xc7,0xc8, + 0xff,0xc1,0xc5,0xc7,0xff,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x15,0x8a,0x00, + 0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x40,0x86,0xb0,0xb5, + 0xb7,0xff,0x03,0x99,0x9d,0x9f,0xd8,0x00,0x00,0x00,0x34,0x00,0x00,0x00,0x10, + 0x8a,0x00,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x13,0x00, + 0x00,0x00,0x37,0x00,0x00,0x00,0x6b,0x9a,0x9c,0x9d,0xd7,0xd7,0xd9,0xdb,0xff, + 0xd5,0xd7,0xd8,0xff,0xd3,0xd6,0xd6,0xff,0xd0,0xd3,0xd5,0xff,0xce,0xd2,0xd3, + 0xff,0xcc,0xcf,0xd1,0xff,0xaa,0xad,0xaf,0x8d,0x82,0x00,0x00,0x00,0x0d,0x0c, + 0xa6,0xa9,0xa9,0x8d,0xc2,0xc6,0xc8,0xff,0xc1,0xc5,0xc7,0xff,0xbf,0xc2,0xc4, + 0xff,0xbd,0xc1,0xc3,0xff,0xbc,0xbf,0xc2,0xff,0xba,0xbe,0xc0,0xff,0x83,0x86, + 0x88,0xd7,0x00,0x00,0x00,0x6b,0x00,0x00,0x00,0x37,0x00,0x00,0x00,0x13,0x00, + 0x00,0x00,0x03,0x83,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x0a,0x00,0x00, + 0x00,0x29,0x7c,0x80,0x81,0xa9,0x83,0xb0,0xb5,0xb7,0xff,0x06,0xb1,0xb5,0xb7, + 0xff,0xb0,0xb5,0xb8,0xff,0xb2,0xb7,0xb9,0xff,0x60,0x62,0x63,0x7f,0x00,0x00, + 0x00,0x20,0x00,0x00,0x00,0x06,0x8b,0x00,0x00,0x00,0x00,0x44,0x00,0x00,0x00, + 0x15,0x00,0x00,0x00,0x40,0xca,0xcd,0xce,0xff,0xcb,0xcf,0xd0,0xff,0xce,0xd0, + 0xd2,0xff,0xcf,0xd3,0xd3,0xff,0xd2,0xd4,0xd5,0xff,0xd3,0xd6,0xd7,0xff,0xb9, + 0xbb,0xbc,0xd8,0x00,0x00,0x00,0x34,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x34, + 0xc0,0xc1,0xc2,0xd8,0xe0,0xe2,0xe3,0xff,0xe2,0xe3,0xe4,0xff,0xe4,0xe6,0xe7, + 0xff,0xe5,0xe8,0xe8,0xff,0xe7,0xea,0xe9,0xff,0xea,0xeb,0xeb,0xff,0xeb,0xed, + 0xee,0xff,0xee,0xef,0xee,0xff,0xef,0xf0,0xf0,0xff,0xf1,0xf1,0xf2,0xff,0xf2, + 0xf3,0xf3,0xff,0xf4,0xf5,0xf5,0xff,0xf5,0xf6,0xf5,0xff,0xf7,0xf7,0xf7,0xff, + 0xf7,0xf8,0xf9,0xff,0xf8,0xf9,0xf9,0xff,0xfa,0xfa,0xfa,0xff,0xfb,0xfb,0xfb, + 0xff,0xfc,0xfc,0xfb,0xff,0xfd,0xfc,0xfd,0xff,0xfd,0xfd,0xfd,0xff,0xfd,0xfe, + 0xfe,0xff,0xfe,0xfd,0xfe,0xff,0xfe,0xfe,0xfe,0xff,0x93,0x93,0x93,0xb0,0x00, + 0x00,0x00,0x40,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x07, + 0x8f,0x8f,0x8f,0x3c,0xfc,0xfc,0xfc,0xff,0xfb,0xfb,0xfb,0xff,0xfa,0xfb,0xfb, + 0xff,0xf9,0xf9,0xf9,0xff,0xf8,0xf9,0xf8,0xff,0xf7,0xf8,0xf8,0xff,0xf5,0xf6, + 0xf7,0xff,0xb4,0xb5,0xb5,0xcf,0xb0,0xb1,0xb1,0xa4,0xcd,0xcf,0xcf,0x8c,0xdf, + 0xe1,0xe1,0x80,0xe4,0xe4,0xe5,0x7d,0xd8,0xd9,0xd9,0x83,0xbd,0xbf,0xbf,0x94, + 0xa0,0xa0,0xa1,0xae,0xca,0xcc,0xcc,0xea,0xe6,0xe8,0xe8,0xff,0xe4,0xe6,0xe7, + 0xff,0xe3,0xe4,0xe5,0xff,0xe0,0xe2,0xe3,0xff,0xdf,0xe1,0xe2,0xff,0xdd,0xdf, + 0xdf,0xff,0xd3,0xd5,0xd6,0xe5,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x03,0x82, + 0x00,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x34,0xb0,0xb3, + 0xb4,0xd8,0xca,0xcc,0xce,0xff,0xc8,0xcb,0xcd,0xff,0xc6,0xc9,0xcb,0xff,0xc4, + 0xc8,0xc9,0xff,0xc2,0xc6,0xc8,0xff,0xc1,0xc5,0xc6,0xff,0x00,0x00,0x00,0x40, + 0x00,0x00,0x00,0x15,0x8a,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x15,0x00, + 0x00,0x00,0x40,0x86,0xb0,0xb5,0xb7,0xff,0x03,0x99,0x9d,0x9f,0xd8,0x00,0x00, + 0x00,0x34,0x00,0x00,0x00,0x10,0x8a,0x00,0x00,0x00,0x00,0x1a,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x4f,0x32,0x32, + 0x33,0x93,0xd6,0xd9,0xda,0xff,0xd4,0xd7,0xd8,0xff,0xd2,0xd5,0xd6,0xff,0xd0, + 0xd2,0xd4,0xff,0xcd,0xd1,0xd2,0xff,0xcb,0xcf,0xd0,0xff,0xc7,0xca,0xcb,0xf2, + 0x4e,0x4f,0x4f,0x2c,0x4d,0x4e,0x4f,0x2c,0xc0,0xc4,0xc6,0xf2,0xc1,0xc5,0xc7, + 0xff,0xc0,0xc4,0xc6,0xff,0xbe,0xc3,0xc4,0xff,0xbd,0xc0,0xc2,0xff,0xbb,0xbf, + 0xc1,0xff,0xb9,0xbe,0xc0,0xff,0x2b,0x2c,0x2c,0x93,0x00,0x00,0x00,0x4f,0x00, + 0x00,0x00,0x22,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x01,0x83,0x00,0x00,0x00, + 0x00,0x03,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x29,0x7c,0x80,0x81,0xa9,0x83, + 0xb0,0xb5,0xb7,0xff,0x01,0xb1,0xb6,0xb7,0xff,0x82,0xb2,0xb6,0xb8,0xff,0x03, + 0x60,0x63,0x63,0x7f,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x06,0x8a,0x00,0x00, + 0x00,0x00,0x52,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x16,0x63,0x65,0x65,0x67, + 0xca,0xcd,0xcf,0xff,0xcc,0xd0,0xd1,0xff,0xce,0xd2,0xd2,0xff,0xd0,0xd3,0xd5, + 0xff,0xd2,0xd5,0xd6,0xff,0xd4,0xd7,0xd8,0xff,0xba,0xbc,0xbd,0xd8,0x00,0x00, + 0x00,0x34,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x34,0xc1,0xc3,0xc3,0xd8,0xe1, + 0xe3,0xe4,0xff,0xe3,0xe5,0xe5,0xff,0xe5,0xe7,0xe7,0xff,0xe7,0xe8,0xe9,0xff, + 0xe8,0xea,0xea,0xff,0xeb,0xec,0xec,0xff,0xec,0xed,0xee,0xff,0xee,0xef,0xef, + 0xff,0xf0,0xf0,0xf1,0xff,0xf1,0xf2,0xf2,0xff,0xf3,0xf4,0xf4,0xff,0xf4,0xf5, + 0xf5,0xff,0xf6,0xf7,0xf6,0xff,0xf7,0xf8,0xf8,0xff,0xf8,0xf8,0xf9,0xff,0xfa, + 0xf9,0xfa,0xff,0xfa,0xfb,0xfb,0xff,0xfb,0xfc,0xfb,0xff,0xfc,0xfc,0xfc,0xff, + 0xfd,0xfc,0xfd,0xff,0xfd,0xfd,0xfd,0xff,0xfe,0xfe,0xfd,0xff,0xfe,0xfe,0xfe, + 0xff,0xb4,0xb4,0xb4,0xd7,0x00,0x00,0x00,0x6b,0x00,0x00,0x00,0x34,0x00,0x00, + 0x00,0x10,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x10,0xeb,0xeb,0xea,0xc9,0xfb, + 0xfc,0xfc,0xff,0xfa,0xfb,0xfb,0xff,0xfa,0xfa,0xfa,0xff,0xf9,0xf9,0xfa,0xff, + 0xf8,0xf9,0xf8,0xff,0xf7,0xf7,0xf7,0xff,0xf5,0xf6,0xf7,0xff,0xf4,0xf5,0xf5, + 0xff,0xf2,0xf3,0xf3,0xff,0xf1,0xf2,0xf2,0xff,0xf0,0xf1,0xf1,0xff,0xee,0xef, + 0xf0,0xff,0xec,0xed,0xee,0xff,0xeb,0xec,0xec,0xff,0xe9,0xea,0xeb,0xff,0xe7, + 0xe9,0xea,0xff,0xe6,0xe7,0xe8,0xff,0xe3,0xe6,0xe6,0xff,0xe1,0xe4,0xe4,0xff, + 0xe0,0xe2,0xe3,0xff,0xde,0xe0,0xe0,0xff,0xdc,0xde,0xdf,0xff,0xda,0xdc,0xdd, + 0xff,0xb2,0xb4,0xb4,0x7c,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x01,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x34,0xb0,0xb2,0xb3,0xd8,0xc9, + 0xcc,0xcd,0xff,0xc7,0xcb,0xcc,0xff,0xc5,0xc8,0xca,0xff,0xc4,0xc7,0xc9,0xff, + 0xc1,0xc5,0xc7,0xff,0xbf,0xc4,0xc5,0xff,0x48,0x4a,0x4b,0x59,0x00,0x00,0x00, + 0x16,0x8a,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x16,0x43,0x45,0x46,0x59, + 0x86,0xb0,0xb5,0xb7,0xff,0x03,0x99,0x9d,0x9f,0xd8,0x00,0x00,0x00,0x34,0x00, + 0x00,0x00,0x10,0x8b,0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x03,0x00,0x00, + 0x00,0x11,0x00,0x00,0x00,0x34,0x00,0x00,0x00,0x67,0x8c,0x8e,0x8f,0xce,0xd3, + 0xd5,0xd7,0xff,0xd1,0xd3,0xd4,0xff,0xcf,0xd1,0xd3,0xff,0xcc,0xd0,0xd1,0xff, + 0xca,0xce,0xd0,0xff,0xc8,0xcc,0xcd,0xff,0x9e,0xa2,0xa3,0x95,0x9d,0xa0,0xa1, + 0x95,0xc3,0xc7,0xc8,0xff,0xc1,0xc5,0xc6,0xff,0xbf,0xc3,0xc5,0xff,0xbe,0xc1, + 0xc3,0xff,0xbb,0xc0,0xc1,0xff,0xba,0xbf,0xc1,0xff,0x7a,0x7d,0x7e,0xce,0x00, + 0x00,0x00,0x67,0x00,0x00,0x00,0x34,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x03, + 0x84,0x00,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x29,0x7c, + 0x80,0x81,0xa9,0xb0,0xb5,0xb7,0xff,0xb0,0xb5,0xb8,0xff,0xb0,0xb5,0xb7,0xff, + 0xb1,0xb6,0xb8,0xff,0xb1,0xb7,0xb8,0xff,0xb2,0xb7,0xb9,0xff,0x60,0x63,0x63, + 0x7f,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x06,0x8a,0x00,0x00,0x00,0x00,0x22, + 0x00,0x00,0x00,0x03,0x51,0x52,0x53,0x2a,0xb6,0xba,0xbb,0xce,0xcb,0xce,0xd0, + 0xff,0xcd,0xd1,0xd2,0xff,0xd0,0xd2,0xd3,0xff,0xd2,0xd4,0xd5,0xff,0xd3,0xd6, + 0xd7,0xff,0xd6,0xd8,0xda,0xff,0xb1,0xb3,0xb3,0xcf,0x00,0x00,0x00,0x34,0x00, + 0x00,0x00,0x1f,0x00,0x00,0x00,0x34,0xc2,0xc4,0xc5,0xd8,0xe1,0xe4,0xe4,0xff, + 0xe4,0xe6,0xe6,0xff,0xe6,0xe8,0xe8,0xff,0xe7,0xea,0xe9,0xff,0xe9,0xea,0xeb, + 0xff,0xec,0xec,0xed,0xff,0xed,0xee,0xef,0xff,0xee,0xf0,0xf0,0xff,0xf1,0xf1, + 0xf1,0xff,0xf2,0xf3,0xf3,0xff,0xf3,0xf4,0xf5,0xff,0xf5,0xf5,0xf6,0xff,0xf6, + 0xf7,0xf7,0xff,0xf7,0xf8,0xf8,0xff,0xf9,0xf9,0xf9,0xff,0xfa,0xfa,0xfa,0xff, + 0xfb,0xfb,0xfb,0xff,0xfb,0xfb,0xfc,0xff,0xfd,0xfc,0xfc,0xff,0xfd,0xfd,0xfd, + 0xff,0x82,0xfd,0xfd,0xfe,0xff,0x08,0xa1,0xa1,0xa1,0xd6,0x1f,0x1f,0x1f,0x8b, + 0x00,0x00,0x00,0x52,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x0a,0x00,0x00,0x00, + 0x08,0xba,0xbb,0xbb,0x5c,0xfc,0xfc,0xfc,0xff,0x82,0xfb,0xfb,0xfb,0xff,0x25, + 0xf9,0xfa,0xfa,0xff,0xf8,0xf8,0xf9,0xff,0xf7,0xf7,0xf7,0xff,0xf6,0xf6,0xf7, + 0xff,0xf5,0xf5,0xf6,0xff,0xf3,0xf4,0xf5,0xff,0xf2,0xf3,0xf3,0xff,0xf0,0xf1, + 0xf2,0xff,0xef,0xf0,0xf0,0xff,0xed,0xef,0xef,0xff,0xec,0xed,0xed,0xff,0xea, + 0xeb,0xec,0xff,0xe8,0xea,0xea,0xff,0xe6,0xe8,0xe9,0xff,0xe5,0xe7,0xe7,0xff, + 0xe3,0xe4,0xe5,0xff,0xe1,0xe3,0xe4,0xff,0xdf,0xe1,0xe2,0xff,0xdd,0xdf,0xe0, + 0xff,0xdb,0xdd,0xdf,0xff,0xd9,0xdb,0xdd,0xff,0xcf,0xd2,0xd3,0xe5,0x5f,0x61, + 0x61,0x26,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x00, + 0x00,0x00,0x34,0x9c,0x9f,0xa0,0xc5,0xc8,0xcb,0xcd,0xff,0xc6,0xca,0xcb,0xff, + 0xc5,0xc8,0xca,0xff,0xc2,0xc7,0xc8,0xff,0xc0,0xc5,0xc6,0xff,0xbf,0xc3,0xc5, + 0xff,0x98,0x9b,0x9d,0xa9,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x02,0x88,0x00, + 0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x19,0x8d,0x92,0x93, + 0xa9,0x86,0xb0,0xb5,0xb7,0xff,0x03,0x89,0x8d,0x8e,0xc5,0x00,0x00,0x00,0x34, + 0x00,0x00,0x00,0x0f,0x8b,0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x01,0x00, + 0x00,0x00,0x07,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x4a,0x1b,0x1b,0x1b,0x88, + 0xc9,0xcc,0xcc,0xf9,0xd0,0xd3,0xd4,0xff,0xce,0xd1,0xd3,0xff,0xcc,0xcf,0xd0, + 0xff,0xc9,0xcd,0xcf,0xff,0xc8,0xcb,0xcc,0xff,0xc1,0xc4,0xc6,0xf4,0xbf,0xc3, + 0xc4,0xf4,0xc2,0xc5,0xc7,0xff,0xc0,0xc4,0xc6,0xff,0xbe,0xc2,0xc4,0xff,0xbd, + 0xc1,0xc3,0xff,0xbb,0xbf,0xc2,0xff,0xb1,0xb6,0xb8,0xf9,0x17,0x18,0x18,0x88, + 0x00,0x00,0x00,0x4a,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x01,0x84,0x00,0x00,0x00,0x00,0x34,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x29, + 0x7c,0x80,0x81,0xa9,0xb0,0xb5,0xb7,0xff,0xb1,0xb5,0xb7,0xff,0xb0,0xb5,0xb7, + 0xff,0xb1,0xb6,0xb8,0xff,0xb2,0xb6,0xb8,0xff,0xb3,0xb7,0xba,0xff,0x94,0x97, + 0x98,0xbb,0x99,0x9d,0x9f,0x8c,0xaa,0xae,0xaf,0x7f,0xb0,0xb4,0xb6,0x7c,0xb1, + 0xb5,0xb7,0x7c,0xb2,0xb6,0xb8,0x7c,0xb3,0xb7,0xb9,0x7c,0xb5,0xb8,0xba,0x7c, + 0xb7,0xba,0xbb,0x7c,0xb8,0xbc,0xbd,0x7c,0xb9,0xbd,0xbe,0x7c,0xba,0xbe,0xc0, + 0x7c,0xbc,0xbf,0xc0,0x7d,0xbc,0xc0,0xc1,0xa1,0xc3,0xc6,0xc7,0xe3,0xca,0xcd, + 0xcf,0xff,0xcd,0xd0,0xd0,0xff,0xce,0xd1,0xd3,0xff,0xd0,0xd3,0xd4,0xff,0xd2, + 0xd5,0xd6,0xff,0xd4,0xd6,0xd8,0xff,0xd7,0xd9,0xda,0xff,0x92,0x93,0x94,0xb1, + 0x00,0x00,0x00,0x32,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x34,0xc3,0xc5,0xc5, + 0xd8,0xe2,0xe4,0xe5,0xff,0xe4,0xe6,0xe7,0xff,0xe6,0xe8,0xe8,0xff,0xe8,0xea, + 0xea,0xff,0xea,0xeb,0xec,0xff,0xec,0xed,0xee,0xff,0x83,0x83,0x83,0xd9,0x84, + 0x84,0x84,0xd8,0x85,0x86,0x86,0xd7,0x86,0x86,0x87,0xd7,0x87,0x87,0x87,0xd7, + 0x87,0x88,0x88,0xd7,0x89,0x89,0x89,0xd7,0x89,0x8a,0x8a,0xd7,0x8a,0x8a,0x8a, + 0xd7,0x8a,0x8b,0x8b,0xd7,0x82,0x8b,0x8b,0x8b,0xd7,0x82,0x8d,0x8d,0x8d,0xd6, + 0x0a,0x6c,0x6c,0x6b,0xc9,0x1a,0x1a,0x1a,0xa7,0x00,0x00,0x00,0x87,0x00,0x00, + 0x00,0x5f,0x00,0x00,0x00,0x33,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x07,0x00, + 0x00,0x00,0x13,0xe9,0xea,0xe9,0xca,0xfc,0xfb,0xfc,0xff,0x82,0xfa,0xfa,0xfb, + 0xff,0x2f,0xf9,0xfa,0xf9,0xff,0xf8,0xf8,0xf8,0xff,0xf7,0xf7,0xf7,0xff,0xf5, + 0xf6,0xf6,0xff,0xf4,0xf5,0xf5,0xff,0xf3,0xf3,0xf4,0xff,0xf1,0xf2,0xf3,0xff, + 0xf0,0xf0,0xf1,0xff,0xee,0xef,0xf0,0xff,0xec,0xee,0xee,0xff,0xeb,0xec,0xed, + 0xff,0xe9,0xea,0xeb,0xff,0xe7,0xe9,0xea,0xff,0xe6,0xe7,0xe8,0xff,0xe4,0xe6, + 0xe6,0xff,0xe2,0xe4,0xe4,0xff,0xe0,0xe2,0xe3,0xff,0xde,0xe0,0xe1,0xff,0xdc, + 0xde,0xdf,0xff,0xda,0xdd,0xde,0xff,0xd8,0xdb,0xdc,0xff,0xd7,0xd9,0xda,0xff, + 0xb3,0xb5,0xb6,0x8d,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x0e,0x00,0x00,0x00,0x32,0x87,0x8a,0x8b,0xb0,0xc7,0xca,0xcc,0xff,0xc6,0xc9, + 0xca,0xff,0xc3,0xc8,0xc9,0xff,0xc2,0xc6,0xc7,0xff,0xc0,0xc4,0xc6,0xff,0xbf, + 0xc2,0xc4,0xff,0xbd,0xc1,0xc2,0xff,0xb1,0xb5,0xb6,0xc6,0xac,0xb0,0xb2,0x6e, + 0xac,0xb0,0xb2,0x49,0xad,0xb1,0xb4,0x48,0xac,0xb0,0xb3,0x48,0xab,0xaf,0xb1, + 0x48,0xaa,0xae,0xb0,0x48,0xa8,0xad,0xaf,0x48,0xa7,0xac,0xae,0x48,0xa5,0xa9, + 0xac,0x49,0xa3,0xa8,0xa9,0x6e,0xa6,0xab,0xad,0xc6,0x87,0xb0,0xb5,0xb7,0xff, + 0x03,0x77,0x7a,0x7b,0xb0,0x00,0x00,0x00,0x32,0x00,0x00,0x00,0x0e,0x8c,0x00, + 0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0f,0x00,0x00,0x00, + 0x30,0x00,0x00,0x00,0x62,0x7e,0x80,0x81,0xc5,0xcf,0xd1,0xd3,0xff,0xcd,0xd0, + 0xd1,0xff,0xcb,0xce,0xd0,0xff,0xc9,0xcc,0xce,0xff,0xc7,0xca,0xcc,0xff,0xc5, + 0xc9,0xca,0xff,0xc3,0xc6,0xc8,0xff,0xc1,0xc5,0xc6,0xff,0xbf,0xc3,0xc4,0xff, + 0xbe,0xc2,0xc3,0xff,0xbc,0xc1,0xc2,0xff,0xbb,0xbf,0xc0,0xff,0x70,0x73,0x73, + 0xc5,0x00,0x00,0x00,0x62,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x0f,0x00,0x00, + 0x00,0x02,0x85,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x0a,0x00,0x00,0x00, + 0x29,0x7c,0x80,0x81,0xa9,0x82,0xb0,0xb5,0xb7,0xff,0x27,0xb1,0xb6,0xb7,0xff, + 0xb1,0xb7,0xb8,0xff,0xb2,0xb6,0xb9,0xff,0xb2,0xb7,0xb9,0xff,0xb3,0xb8,0xba, + 0xff,0xb5,0xba,0xbb,0xff,0xb6,0xbb,0xbc,0xff,0xb7,0xbb,0xbd,0xff,0xb8,0xbd, + 0xbe,0xff,0xb9,0xbe,0xc0,0xff,0xbb,0xbf,0xc1,0xff,0xbd,0xc0,0xc2,0xff,0xbe, + 0xc2,0xc4,0xff,0xc0,0xc3,0xc5,0xff,0xc1,0xc5,0xc7,0xff,0xc4,0xc8,0xc9,0xff, + 0xc6,0xc9,0xca,0xff,0xc7,0xcb,0xcc,0xff,0xc9,0xcc,0xce,0xff,0xcb,0xce,0xd0, + 0xff,0xcd,0xd0,0xd1,0xff,0xcf,0xd2,0xd3,0xff,0xd1,0xd4,0xd5,0xff,0xd3,0xd6, + 0xd7,0xff,0xd6,0xd8,0xd9,0xff,0xd7,0xd9,0xdb,0xff,0x78,0x79,0x7a,0x9a,0x00, + 0x00,0x00,0x2f,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x34,0xc4,0xc5,0xc6,0xd8, + 0xe3,0xe5,0xe6,0xff,0xe5,0xe7,0xe8,0xff,0xe7,0xe9,0xea,0xff,0xe9,0xeb,0xeb, + 0xff,0xeb,0xec,0xed,0xff,0xec,0xed,0xee,0xff,0x00,0x00,0x00,0xa2,0x00,0x00, + 0x00,0x98,0x8b,0x00,0x00,0x00,0x93,0x0a,0x00,0x00,0x00,0x90,0x00,0x00,0x00, + 0x88,0x00,0x00,0x00,0x76,0x00,0x00,0x00,0x59,0x00,0x00,0x00,0x37,0x00,0x00, + 0x00,0x19,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x0a,0xc5,0xc5,0xc5,0x6d,0xfb, + 0xfc,0xfc,0xff,0x82,0xfb,0xfb,0xfb,0xff,0x2e,0xfa,0xf9,0xfa,0xff,0xf8,0xf9, + 0xf9,0xff,0xf8,0xf8,0xf8,0xff,0xf6,0xf6,0xf7,0xff,0xf5,0xf6,0xf6,0xff,0xf3, + 0xf5,0xf5,0xff,0xf2,0xf3,0xf3,0xff,0xf1,0xf1,0xf1,0xff,0xef,0xf0,0xf0,0xff, + 0xed,0xef,0xef,0xff,0xec,0xed,0xed,0xff,0xea,0xec,0xec,0xff,0xe9,0xea,0xea, + 0xff,0xe7,0xe8,0xe8,0xff,0xe5,0xe7,0xe7,0xff,0xe3,0xe5,0xe5,0xff,0xe1,0xe2, + 0xe4,0xff,0xdf,0xe1,0xe2,0xff,0xdd,0xe0,0xe0,0xff,0xdb,0xdd,0xde,0xff,0xd9, + 0xdc,0xdc,0xff,0xd8,0xda,0xdb,0xff,0xd6,0xd8,0xd9,0xff,0xd0,0xd3,0xd5,0xf2, + 0x59,0x5a,0x5a,0x28,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x0d,0x00,0x00,0x00, + 0x2e,0x60,0x62,0x62,0x8d,0xc6,0xca,0xcb,0xff,0xc4,0xc9,0xca,0xff,0xc3,0xc7, + 0xc8,0xff,0xc1,0xc5,0xc6,0xff,0xbf,0xc4,0xc5,0xff,0xbd,0xc2,0xc4,0xff,0xbc, + 0xc1,0xc2,0xff,0xbb,0xbf,0xc1,0xff,0xb9,0xbe,0xbf,0xff,0xb7,0xbc,0xbe,0xff, + 0xb6,0xbb,0xbd,0xff,0xb5,0xba,0xbb,0xff,0xb4,0xb9,0xbb,0xff,0xb3,0xb8,0xba, + 0xff,0xb2,0xb6,0xb9,0xff,0xb1,0xb6,0xb8,0xff,0xb0,0xb5,0xb8,0xff,0x89,0xb0, + 0xb5,0xb7,0xff,0x03,0x55,0x57,0x58,0x8d,0x00,0x00,0x00,0x2e,0x00,0x00,0x00, + 0x0c,0x8d,0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x1c, + 0x00,0x00,0x00,0x46,0x1b,0x1b,0x1b,0x83,0xc5,0xc8,0xc9,0xf9,0xcc,0xcf,0xd0, + 0xff,0xc9,0xcd,0xce,0xff,0xc7,0xcb,0xcd,0xff,0xc6,0xca,0xcb,0xff,0xc4,0xc8, + 0xc9,0xff,0xc3,0xc6,0xc7,0xff,0xc0,0xc4,0xc6,0xff,0xbf,0xc2,0xc4,0xff,0xbd, + 0xc1,0xc3,0xff,0xbb,0xbf,0xc1,0xff,0xb2,0xb6,0xb8,0xf9,0x18,0x18,0x19,0x83, + 0x00,0x00,0x00,0x46,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x06,0x86,0x00,0x00, + 0x00,0x00,0x2c,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x29,0x7c,0x80,0x81,0xa9, + 0xb0,0xb5,0xb8,0xff,0xb1,0xb6,0xb7,0xff,0xb1,0xb6,0xb8,0xff,0xb2,0xb6,0xb8, + 0xff,0xb3,0xb8,0xb9,0xff,0xb3,0xb8,0xba,0xff,0xb4,0xb9,0xbb,0xff,0xb5,0xba, + 0xbc,0xff,0xb6,0xbb,0xbd,0xff,0xb8,0xbc,0xbd,0xff,0xb8,0xbd,0xbf,0xff,0xba, + 0xbf,0xc0,0xff,0xbc,0xc0,0xc1,0xff,0xbd,0xc2,0xc3,0xff,0xbe,0xc3,0xc5,0xff, + 0xc0,0xc5,0xc6,0xff,0xc3,0xc6,0xc8,0xff,0xc5,0xc8,0xca,0xff,0xc6,0xc9,0xcb, + 0xff,0xc8,0xcb,0xcc,0xff,0xca,0xcd,0xcf,0xff,0xcc,0xd0,0xd1,0xff,0xce,0xd1, + 0xd3,0xff,0xd0,0xd3,0xd4,0xff,0xd2,0xd5,0xd6,0xff,0xd4,0xd7,0xd8,0xff,0xd6, + 0xd9,0xd9,0xff,0xd0,0xd1,0xd2,0xf8,0x24,0x24,0x24,0x68,0x00,0x00,0x00,0x29, + 0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x34,0xc5,0xc6,0xc6,0xd8,0xe5,0xe6,0xe6, + 0xff,0xe6,0xe8,0xe9,0xff,0xe8,0xe9,0xea,0xff,0xea,0xeb,0xec,0xff,0xeb,0xed, + 0xed,0xff,0xed,0xef,0xef,0xff,0x00,0x00,0x00,0x7e,0x00,0x00,0x00,0x68,0x8a, + 0x00,0x00,0x00,0x5e,0x3a,0x00,0x00,0x00,0x5c,0x00,0x00,0x00,0x58,0x00,0x00, + 0x00,0x4f,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x2b,0x00,0x00,0x00,0x16,0x00, + 0x00,0x00,0x08,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x15,0xf3,0xf4,0xf3,0xe5, + 0xfb,0xfc,0xfc,0xff,0xfb,0xfb,0xfb,0xff,0xfa,0xfb,0xfb,0xff,0xf9,0xf9,0xfa, + 0xff,0xf8,0xf8,0xf9,0xff,0xf6,0xf7,0xf8,0xff,0x97,0x97,0x97,0xdd,0x54,0x54, + 0x54,0xc7,0x54,0x54,0x54,0xc5,0x53,0x54,0x54,0xc4,0x53,0x53,0x53,0xc4,0x52, + 0x52,0x53,0xc4,0x52,0x52,0x52,0xc4,0x51,0x52,0x51,0xc4,0x50,0x51,0x51,0xc4, + 0x50,0x50,0x51,0xc4,0x4f,0x50,0x50,0xc4,0x4f,0x4f,0x4f,0xc4,0x4e,0x4f,0x4f, + 0xc6,0x4c,0x4d,0x4d,0xc7,0xb1,0xb1,0xb2,0xec,0xdc,0xdf,0xdf,0xff,0xdb,0xdc, + 0xdd,0xff,0xd8,0xdb,0xdc,0xff,0xd7,0xd9,0xda,0xff,0xd5,0xd7,0xd8,0xff,0xd3, + 0xd5,0xd6,0xff,0xb5,0xb7,0xb8,0x9d,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x0a, + 0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x5b,0xb6,0xb9,0xba,0xf0,0xc4,0xc7,0xc9, + 0xff,0xc2,0xc6,0xc8,0xff,0xc0,0xc4,0xc6,0xff,0xbe,0xc3,0xc4,0xff,0xbd,0xc1, + 0xc3,0xff,0xbc,0xbf,0xc2,0xff,0xba,0xbe,0xc0,0xff,0xb8,0xbd,0xbf,0xff,0xb7, + 0xbc,0xbe,0xff,0xb6,0xba,0xbd,0xff,0xb4,0xb9,0xbb,0xff,0xb4,0xb9,0xba,0xff, + 0xb3,0xb7,0xb9,0xff,0xb2,0xb6,0xb8,0xff,0xb1,0xb6,0xb7,0xff,0x89,0xb0,0xb5, + 0xb7,0xff,0x04,0xa2,0xa7,0xa9,0xf0,0x00,0x00,0x00,0x5b,0x00,0x00,0x00,0x27, + 0x00,0x00,0x00,0x09,0x8d,0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x01,0x00, + 0x00,0x00,0x0d,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x5f,0x7d,0x7e,0x80,0xc4, + 0xcb,0xce,0xd0,0xff,0xc9,0xcc,0xcd,0xff,0xc7,0xca,0xcc,0xff,0xc5,0xc9,0xca, + 0xff,0xc3,0xc7,0xc8,0xff,0xc2,0xc5,0xc6,0xff,0xc0,0xc3,0xc5,0xff,0xbe,0xc2, + 0xc4,0xff,0xbc,0xc0,0xc2,0xff,0xba,0xbf,0xc1,0xff,0x70,0x73,0x74,0xc4,0x00, + 0x00,0x00,0x5f,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x01, + 0x86,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x29,0x7c, + 0x80,0x82,0xa9,0xb1,0xb5,0xb7,0xff,0x82,0xb1,0xb6,0xb8,0xff,0x26,0xb2,0xb7, + 0xb9,0xff,0xb3,0xb7,0xb9,0xff,0xb3,0xb9,0xbb,0xff,0xb4,0xba,0xbb,0xff,0xb6, + 0xba,0xbc,0xff,0xb7,0xbb,0xbd,0xff,0xb8,0xbd,0xbf,0xff,0xb9,0xbe,0xbf,0xff, + 0xbb,0xbf,0xc0,0xff,0xbc,0xc1,0xc2,0xff,0xbe,0xc2,0xc3,0xff,0xc0,0xc3,0xc5, + 0xff,0xc1,0xc6,0xc7,0xff,0xc3,0xc7,0xc9,0xff,0xc5,0xc9,0xca,0xff,0xc7,0xcb, + 0xcc,0xff,0xc9,0xcc,0xcd,0xff,0xcb,0xce,0xcf,0xff,0xcc,0xd0,0xd1,0xff,0xcf, + 0xd2,0xd3,0xff,0xd0,0xd4,0xd5,0xff,0xd3,0xd6,0xd7,0xff,0xd5,0xd7,0xd9,0xff, + 0xd7,0xda,0xdb,0xff,0x79,0x7b,0x7b,0xb7,0x00,0x00,0x00,0x4f,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x34,0xc5,0xc6,0xc7,0xd8,0xe5,0xe7, + 0xe8,0xff,0xe8,0xe9,0xea,0xff,0xe9,0xea,0xeb,0xff,0xeb,0xec,0xec,0xff,0xed, + 0xee,0xee,0xff,0xee,0xef,0xf0,0xff,0x00,0x00,0x00,0x5b,0x00,0x00,0x00,0x3a, + 0x8a,0x00,0x00,0x00,0x29,0x13,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x26,0x00, + 0x00,0x00,0x20,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x06, + 0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0a,0xce,0xce,0xce,0x7d,0xfc,0xfc,0xfc, + 0xff,0xfc,0xfb,0xfb,0xff,0xfb,0xfb,0xfa,0xff,0xf9,0xfa,0xfa,0xff,0xf9,0xf9, + 0xf9,0xff,0xf7,0xf8,0xf8,0xff,0xea,0xeb,0xeb,0xfa,0x19,0x19,0x19,0xa6,0x00, + 0x00,0x00,0x93,0x00,0x00,0x00,0x8c,0x89,0x00,0x00,0x00,0x8b,0x1e,0x00,0x00, + 0x00,0x8e,0x00,0x00,0x00,0x96,0x50,0x50,0x51,0xbd,0xdc,0xde,0xde,0xff,0xda, + 0xdc,0xdd,0xff,0xd8,0xdb,0xdb,0xff,0xd6,0xd8,0xd9,0xff,0xd3,0xd7,0xd8,0xff, + 0xd2,0xd5,0xd6,0xff,0xd0,0xd3,0xd4,0xff,0x78,0x7a,0x7b,0x3a,0x00,0x00,0x00, + 0x0b,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x4b,0x40,0x41,0x41,0x9c,0xc2,0xc7, + 0xc9,0xff,0xc1,0xc5,0xc6,0xff,0xbf,0xc3,0xc5,0xff,0xbd,0xc2,0xc4,0xff,0xbc, + 0xc1,0xc3,0xff,0xbb,0xbf,0xc1,0xff,0xb9,0xbe,0xbf,0xff,0xb8,0xbc,0xbe,0xff, + 0xb6,0xbc,0xbd,0xff,0xb6,0xba,0xbc,0xff,0xb4,0xb9,0xbb,0xff,0xb3,0xb8,0xb9, + 0xff,0xb2,0xb7,0xb8,0xff,0xb1,0xb7,0xb8,0xff,0xb0,0xb5,0xb8,0xff,0x89,0xb0, + 0xb5,0xb7,0xff,0x04,0x39,0x3b,0x3c,0x9c,0x00,0x00,0x00,0x4b,0x00,0x00,0x00, + 0x1e,0x00,0x00,0x00,0x05,0x8e,0x00,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x05, + 0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x43,0x00,0x00,0x00,0x79,0xb9,0xbb,0xbc, + 0xf3,0xc8,0xcc,0xcd,0xff,0xc6,0xca,0xcb,0xff,0xc4,0xc8,0xc9,0xff,0xc3,0xc6, + 0xc8,0xff,0xc0,0xc4,0xc6,0xff,0xbf,0xc3,0xc4,0xff,0xbd,0xc1,0xc3,0xff,0xbc, + 0xc0,0xc1,0xff,0xa9,0xad,0xaf,0xf3,0x00,0x00,0x00,0x79,0x00,0x00,0x00,0x43, + 0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x05,0x87,0x00,0x00,0x00,0x00,0x2c,0x00, + 0x00,0x00,0x0a,0x00,0x00,0x00,0x29,0x7c,0x80,0x81,0xa9,0xb0,0xb6,0xb7,0xff, + 0xb1,0xb6,0xb8,0xff,0xb2,0xb6,0xb8,0xff,0xb2,0xb7,0xb9,0xff,0xb3,0xb8,0xba, + 0xff,0xb4,0xb8,0xbb,0xff,0xb5,0xba,0xbb,0xff,0xb6,0xbb,0xbc,0xff,0xb7,0xbc, + 0xbe,0xff,0xb9,0xbd,0xbf,0xff,0xba,0xbf,0xc1,0xff,0xbc,0xc0,0xc2,0xff,0xbe, + 0xc1,0xc2,0xff,0xbe,0xc3,0xc5,0xff,0xc0,0xc4,0xc6,0xff,0xc2,0xc6,0xc8,0xff, + 0xc4,0xc7,0xc9,0xff,0xc6,0xc9,0xcb,0xff,0xc7,0xcb,0xcd,0xff,0xca,0xcd,0xcf, + 0xff,0xcc,0xcf,0xd1,0xff,0xce,0xd1,0xd2,0xff,0xd0,0xd3,0xd3,0xff,0xd1,0xd4, + 0xd6,0xff,0xd3,0xd7,0xd8,0xff,0xcc,0xcf,0xcf,0xfa,0x71,0x72,0x72,0xc4,0x00, + 0x00,0x00,0x72,0x00,0x00,0x00,0x3d,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x13, + 0x00,0x00,0x00,0x34,0xc6,0xc6,0xc7,0xd8,0xe6,0xe7,0xe8,0xff,0xe8,0xea,0xea, + 0xff,0xea,0xec,0xec,0xff,0xec,0xed,0xed,0xff,0xed,0xee,0xef,0xff,0xef,0xf0, + 0xf0,0xff,0x00,0x00,0x00,0x46,0x00,0x00,0x00,0x1e,0x8b,0x00,0x00,0x00,0x0a, + 0x12,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x03,0x00,0x00, + 0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x71,0x71,0x71,0x26,0xf3, + 0xf3,0xf4,0xe5,0xfc,0xfc,0xfb,0xff,0xfb,0xfc,0xfb,0xff,0xfa,0xfa,0xfa,0xff, + 0xf9,0xf9,0xf9,0xff,0xf8,0xf8,0xf9,0xff,0xf7,0xf7,0xf7,0xff,0x91,0x92,0x92, + 0xc9,0x00,0x00,0x00,0x79,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x53,0x89,0x00, + 0x00,0x00,0x51,0x1d,0x00,0x00,0x00,0x55,0x00,0x00,0x00,0x65,0x00,0x00,0x00, + 0x82,0xb2,0xb3,0xb4,0xe6,0xd8,0xdb,0xdc,0xff,0xd7,0xd9,0xda,0xff,0xd4,0xd7, + 0xd9,0xff,0xd3,0xd6,0xd7,0xff,0xd1,0xd4,0xd5,0xff,0xcf,0xd2,0xd3,0xff,0xbb, + 0xbe,0xc0,0xba,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x37, + 0x00,0x00,0x00,0x6c,0x58,0x5a,0x5b,0xbb,0xb7,0xbc,0xbd,0xfa,0xbf,0xc3,0xc4, + 0xff,0xbe,0xc2,0xc3,0xff,0xbb,0xc0,0xc1,0xff,0xba,0xbe,0xc0,0xff,0xb8,0xbd, + 0xbf,0xff,0xb7,0xbb,0xbe,0xff,0xb6,0xbb,0xbd,0xff,0xb5,0xb9,0xbb,0xff,0xb3, + 0xb8,0xba,0xff,0xb2,0xb7,0xba,0xff,0xb2,0xb7,0xb8,0xff,0xb1,0xb6,0xb8,0xff, + 0x88,0xb0,0xb5,0xb7,0xff,0x06,0xa8,0xac,0xae,0xfa,0x50,0x52,0x53,0xbb,0x00, + 0x00,0x00,0x6c,0x00,0x00,0x00,0x37,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x02, + 0x8e,0x00,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0d,0x00, + 0x00,0x00,0x2b,0x00,0x00,0x00,0x5d,0x6d,0x6e,0x6f,0xbc,0xc7,0xcb,0xcd,0xff, + 0xc5,0xc9,0xca,0xff,0xc3,0xc7,0xc8,0xff,0xc1,0xc5,0xc7,0xff,0xbf,0xc4,0xc5, + 0xff,0xbe,0xc2,0xc3,0xff,0xbc,0xc1,0xc2,0xff,0xbb,0xbf,0xc1,0xff,0x64,0x67, + 0x67,0xbc,0x00,0x00,0x00,0x5d,0x00,0x00,0x00,0x2b,0x00,0x00,0x00,0x0d,0x00, + 0x00,0x00,0x01,0x87,0x00,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x0a,0x00,0x00, + 0x00,0x29,0x7c,0x80,0x81,0xa9,0xb1,0xb5,0xb8,0xff,0xb1,0xb6,0xb8,0xff,0xb2, + 0xb7,0xb9,0xff,0xb2,0xb8,0xb9,0xff,0xb3,0xb8,0xba,0xff,0xb4,0xb9,0xbb,0xff, + 0xb6,0xba,0xbc,0xff,0xb6,0xbc,0xbe,0xff,0xb8,0xbc,0xbe,0xff,0xba,0xbe,0xbf, + 0xff,0xbb,0xbf,0xc1,0xff,0xbc,0xc1,0xc2,0xff,0xbe,0xc2,0xc4,0xff,0xbf,0xc3, + 0xc5,0xff,0xc1,0xc6,0xc7,0xff,0xc3,0xc7,0xc8,0xff,0xc5,0xc8,0xca,0xff,0xc7, + 0xcb,0xcb,0xff,0xc8,0xcc,0xce,0xff,0xca,0xce,0xd0,0xff,0xcc,0xd0,0xd1,0xff, + 0xba,0xbc,0xbd,0xf6,0xa5,0xa8,0xa8,0xec,0x8e,0x91,0x91,0xe1,0x69,0x6b,0x6b, + 0xce,0x16,0x16,0x16,0xa4,0x00,0x00,0x00,0x7e,0x00,0x00,0x00,0x52,0x00,0x00, + 0x00,0x26,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x34,0xc6, + 0xc8,0xc8,0xd8,0xe7,0xe9,0xe9,0xff,0xe9,0xea,0xeb,0xff,0xeb,0xec,0xec,0xff, + 0xed,0xed,0xee,0xff,0xee,0xef,0xef,0xff,0xf0,0xf1,0xf1,0xff,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x15,0x8f,0x00,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x01, + 0x00,0x00,0x00,0x0c,0xd5,0xd5,0xd4,0x8d,0xfc,0xfc,0xfc,0xff,0xfc,0xfb,0xfc, + 0xff,0xfb,0xfb,0xfb,0xff,0xfa,0xfa,0xfb,0xff,0xf9,0xf9,0xf9,0xff,0xf7,0xf8, + 0xf8,0xff,0xe0,0xe1,0xe1,0xf3,0x00,0x00,0x00,0x7d,0x00,0x00,0x00,0x4f,0x00, + 0x00,0x00,0x30,0x00,0x00,0x00,0x22,0x89,0x00,0x00,0x00,0x20,0x1d,0x00,0x00, + 0x00,0x25,0x00,0x00,0x00,0x37,0x00,0x00,0x00,0x5b,0x31,0x31,0x31,0x98,0xd8, + 0xda,0xdc,0xff,0xd6,0xd9,0xd9,0xff,0xd4,0xd7,0xd8,0xff,0xd2,0xd5,0xd6,0xff, + 0xd0,0xd3,0xd4,0xff,0xcf,0xd2,0xd3,0xff,0xcc,0xd0,0xd0,0xff,0x97,0x9a,0x9b, + 0x5b,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x4b,0x00,0x00, + 0x00,0x79,0x14,0x15,0x15,0xa3,0x6b,0x6d,0x6e,0xd4,0x95,0x99,0x9a,0xec,0xbb, + 0xbf,0xc1,0xff,0xb9,0xbe,0xbf,0xff,0xb8,0xbd,0xbe,0xff,0xb6,0xbb,0xbd,0xff, + 0xb6,0xba,0xbc,0xff,0xb4,0xb9,0xbb,0xff,0xb3,0xb8,0xba,0xff,0xb2,0xb7,0xb9, + 0xff,0xb1,0xb7,0xb8,0xff,0xb1,0xb5,0xb8,0xff,0x86,0xb0,0xb5,0xb7,0xff,0x08, + 0x8b,0x8f,0x91,0xec,0x63,0x66,0x67,0xd4,0x13,0x13,0x13,0xa3,0x00,0x00,0x00, + 0x79,0x00,0x00,0x00,0x4b,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x09,0x00,0x00, + 0x00,0x01,0x8f,0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x19,0x00,0x00,0x00,0x3f,0x00,0x00,0x00,0x6f,0x00,0x00,0x00,0x94,0x00,0x00, + 0x00,0xa6,0x84,0x00,0x00,0x00,0xaa,0x06,0x00,0x00,0x00,0xa6,0x00,0x00,0x00, + 0x94,0x00,0x00,0x00,0x6f,0x00,0x00,0x00,0x3f,0x00,0x00,0x00,0x19,0x00,0x00, + 0x00,0x04,0x88,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x09,0x00,0x00,0x00, + 0x25,0x00,0x00,0x00,0x53,0x00,0x00,0x00,0x83,0x00,0x00,0x00,0xa0,0x91,0x00, + 0x00,0x00,0xaa,0x10,0x00,0x00,0x00,0xa9,0x00,0x00,0x00,0xa7,0x00,0x00,0x00, + 0xa3,0x00,0x00,0x00,0x9e,0x00,0x00,0x00,0x97,0x00,0x00,0x00,0x8a,0x00,0x00, + 0x00,0x73,0x00,0x00,0x00,0x52,0x00,0x00,0x00,0x2e,0x00,0x00,0x00,0x13,0x00, + 0x00,0x00,0x04,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x2e,0x00,0x00,0x00,0x62, + 0x00,0x00,0x00,0x8d,0x00,0x00,0x00,0xa5,0x82,0x00,0x00,0x00,0xaa,0x04,0x00, + 0x00,0x00,0x97,0x00,0x00,0x00,0x71,0x00,0x00,0x00,0x39,0x00,0x00,0x00,0x13, + 0x8f,0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x12,0x00, + 0x00,0x00,0x34,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x8c,0x00,0x00,0x00,0xa3, + 0x82,0x00,0x00,0x00,0xa9,0x06,0x00,0x00,0x00,0xa0,0x00,0x00,0x00,0x85,0x00, + 0x00,0x00,0x59,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x07, + 0x89,0x00,0x00,0x00,0x06,0x0c,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x16,0x00, + 0x00,0x00,0x37,0x00,0x00,0x00,0x66,0x00,0x00,0x00,0x8e,0x00,0x00,0x00,0xa4, + 0x00,0x00,0x00,0xaa,0x00,0x00,0x00,0xa9,0x00,0x00,0x00,0x9f,0x00,0x00,0x00, + 0x83,0x00,0x00,0x00,0x57,0x00,0x00,0x00,0x2a,0x82,0x00,0x00,0x00,0x10,0x07, + 0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x4f,0x00,0x00,0x00,0x72,0x00,0x00,0x00, + 0x8d,0x00,0x00,0x00,0x9d,0x00,0x00,0x00,0xa6,0x11,0x12,0x12,0xb1,0x82,0x3f, + 0x41,0x41,0xc4,0x82,0x3e,0x40,0x41,0xc4,0x01,0x3e,0x3f,0x40,0xc4,0x82,0x3d, + 0x3f,0x3f,0xc4,0x85,0x3d,0x3e,0x3f,0xc4,0x09,0x11,0x11,0x11,0xb1,0x00,0x00, + 0x00,0xa6,0x00,0x00,0x00,0x9d,0x00,0x00,0x00,0x8d,0x00,0x00,0x00,0x72,0x00, + 0x00,0x00,0x4f,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x03, + 0x90,0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0b,0x00, + 0x00,0x00,0x24,0x00,0x00,0x00,0x49,0x00,0x00,0x00,0x69,0x00,0x00,0x00,0x7a, + 0x84,0x00,0x00,0x00,0x7f,0x06,0x00,0x00,0x00,0x7a,0x00,0x00,0x00,0x69,0x00, + 0x00,0x00,0x49,0x00,0x00,0x00,0x24,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x01, + 0x88,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x1c,0x00, + 0x00,0x00,0x3e,0x00,0x00,0x00,0x62,0x00,0x00,0x00,0x78,0x92,0x00,0x00,0x00, + 0x7f,0x0f,0x00,0x00,0x00,0x7c,0x00,0x00,0x00,0x76,0x00,0x00,0x00,0x6e,0x00, + 0x00,0x00,0x63,0x00,0x00,0x00,0x53,0x00,0x00,0x00,0x3e,0x00,0x00,0x00,0x27, + 0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x0a,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x49,0x00,0x00,0x00,0x6a,0x00,0x00, + 0x00,0x7c,0x82,0x00,0x00,0x00,0x7f,0x04,0x00,0x00,0x00,0x71,0x00,0x00,0x00, + 0x55,0x00,0x00,0x00,0x2b,0x00,0x00,0x00,0x0e,0x8f,0x00,0x00,0x00,0x00,0x06, + 0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x31,0x00,0x00,0x00, + 0x56,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x7d,0x82,0x00,0x00,0x00,0x7f,0x05, + 0x00,0x00,0x00,0x73,0x00,0x00,0x00,0x5b,0x00,0x00,0x00,0x36,0x00,0x00,0x00, + 0x16,0x00,0x00,0x00,0x04,0x8a,0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x01, + 0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x40,0x00,0x00,0x00, + 0x63,0x00,0x00,0x00,0x78,0x82,0x00,0x00,0x00,0x7f,0x0f,0x00,0x00,0x00,0x7c, + 0x00,0x00,0x00,0x6c,0x00,0x00,0x00,0x4e,0x00,0x00,0x00,0x29,0x00,0x00,0x00, + 0x0f,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x25,0x00,0x00, + 0x00,0x3f,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x6c,0x00,0x00,0x00,0x79,0x00, + 0x00,0x00,0x82,0x00,0x00,0x00,0x88,0x00,0x00,0x00,0x8a,0x88,0x00,0x00,0x00, + 0x8b,0x0b,0x00,0x00,0x00,0x8a,0x00,0x00,0x00,0x88,0x00,0x00,0x00,0x82,0x00, + 0x00,0x00,0x79,0x00,0x00,0x00,0x6c,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x3f, + 0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x05,0x00,0x00,0x00, + 0x01,0x91,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x0f, + 0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x32,0x00,0x00,0x00,0x3d,0x84,0x00,0x00, + 0x00,0x40,0x05,0x00,0x00,0x00,0x3d,0x00,0x00,0x00,0x32,0x00,0x00,0x00,0x21, + 0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x04,0x89,0x00,0x00,0x00,0x00,0x05,0x00, + 0x00,0x00,0x03,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x31, + 0x00,0x00,0x00,0x3c,0x91,0x00,0x00,0x00,0x40,0x10,0x00,0x00,0x00,0x3f,0x00, + 0x00,0x00,0x3d,0x00,0x00,0x00,0x3a,0x00,0x00,0x00,0x34,0x00,0x00,0x00,0x2d, + 0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x0c,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x00, + 0x00,0x11,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x35,0x00,0x00,0x00,0x3d,0x82, + 0x00,0x00,0x00,0x40,0x04,0x00,0x00,0x00,0x39,0x00,0x00,0x00,0x2b,0x00,0x00, + 0x00,0x15,0x00,0x00,0x00,0x07,0x8f,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x2e,0x00,0x00, + 0x00,0x3a,0x82,0x00,0x00,0x00,0x40,0x06,0x00,0x00,0x00,0x3f,0x00,0x00,0x00, + 0x38,0x00,0x00,0x00,0x2b,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x09,0x00,0x00, + 0x00,0x01,0x8b,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x0c,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x2f,0x00,0x00,0x00,0x3b,0x82,0x00, + 0x00,0x00,0x40,0x0f,0x00,0x00,0x00,0x3f,0x00,0x00,0x00,0x38,0x00,0x00,0x00, + 0x2b,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x02,0x00,0x00, + 0x00,0x04,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x26,0x00, + 0x00,0x00,0x33,0x00,0x00,0x00,0x3d,0x00,0x00,0x00,0x46,0x00,0x00,0x00,0x4c, + 0x00,0x00,0x00,0x4f,0x88,0x00,0x00,0x00,0x51,0x0a,0x00,0x00,0x00,0x4f,0x00, + 0x00,0x00,0x4c,0x00,0x00,0x00,0x46,0x00,0x00,0x00,0x3d,0x00,0x00,0x00,0x33, + 0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x0c,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x01,0x92,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x01, + 0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x10,0x00,0x00,0x00, + 0x13,0x84,0x00,0x00,0x00,0x15,0x05,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x10, + 0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x01,0x89,0x00,0x00, + 0x00,0x00,0x05,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x0a, + 0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x14,0x92,0x00,0x00,0x00,0x15,0x07,0x00, + 0x00,0x00,0x14,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x0c, + 0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x01,0x83,0x00,0x00, + 0x00,0x00,0x05,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x0c, + 0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x14,0x82,0x00,0x00,0x00,0x15,0x04,0x00, + 0x00,0x00,0x13,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x02, + 0x8f,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x04,0x00, + 0x00,0x00,0x0a,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x14,0x83,0x00,0x00,0x00, + 0x15,0x04,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x07,0x00, + 0x00,0x00,0x02,0x8d,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x03,0x00,0x00, + 0x00,0x08,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x13,0x83,0x00,0x00,0x00,0x15, + 0x0e,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x09,0x00,0x00, + 0x00,0x04,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00, + 0x00,0x00,0x04,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x14, + 0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x1f,0x88,0x00,0x00, + 0x00,0x20,0x08,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x19, + 0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x0a,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x01,0xff,0x00,0x00,0x00,0x00,0x91,0x00,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x04,0x00,0x00, + 0x00,0x05,0x88,0x00,0x00,0x00,0x06,0x04,0x00,0x00,0x00,0x05,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x01,0xff,0x00,0x00,0x00,0x00,0xff, + 0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff, + 0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff, + 0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff, + 0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff, + 0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff, + 0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xba, + 0x00,0x00,0x00,0x00 +}; + +static const GdkPixdata vdpau_pixdata = { + 0x47646b50, /* Pixbuf magic: 'GdkP' */ + 24 + 12949, /* header length + pixel_data length */ + 0x2010002, /* pixdata_type */ + 656, /* rowstride */ + 164, /* width */ + 60, /* height */ + vdpau_pixdata_pixel_data /* pixel_data */ +}; diff --git a/src/jansson/dump.c b/src/jansson/dump.c new file mode 100644 index 0000000..2c7dee9 --- /dev/null +++ b/src/jansson/dump.c @@ -0,0 +1,450 @@ +/* + * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org> + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#define _GNU_SOURCE +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#include "jansson.h" +#include "jansson_private.h" +#include "strbuffer.h" +#include "utf.h" + +#define MAX_INTEGER_STR_LENGTH 100 +#define MAX_REAL_STR_LENGTH 100 + +struct object_key { + size_t serial; + const char *key; +}; + +static int dump_to_strbuffer(const char *buffer, size_t size, void *data) +{ + return strbuffer_append_bytes((strbuffer_t *)data, buffer, size); +} + +static int dump_to_file(const char *buffer, size_t size, void *data) +{ + FILE *dest = (FILE *)data; + if(fwrite(buffer, size, 1, dest) != 1) + return -1; + return 0; +} + +/* 32 spaces (the maximum indentation size) */ +static char whitespace[] = " "; + +static int dump_indent(size_t flags, int depth, int space, json_dump_callback_t dump, void *data) +{ + if(JSON_INDENT(flags) > 0) + { + int i, ws_count = JSON_INDENT(flags); + + if(dump("\n", 1, data)) + return -1; + + for(i = 0; i < depth; i++) + { + if(dump(whitespace, ws_count, data)) + return -1; + } + } + else if(space && !(flags & JSON_COMPACT)) + { + return dump(" ", 1, data); + } + return 0; +} + +static int dump_string(const char *str, json_dump_callback_t dump, void *data, size_t flags) +{ + const char *pos, *end; + int32_t codepoint; + + if(dump("\"", 1, data)) + return -1; + + end = pos = str; + while(1) + { + const char *text; + char seq[13]; + int length; + + while(*end) + { + end = utf8_iterate(pos, &codepoint); + if(!end) + return -1; + + /* mandatory escape or control char */ + if(codepoint == '\\' || codepoint == '"' || codepoint < 0x20) + break; + + /* slash */ + if((flags & JSON_ESCAPE_SLASH) && codepoint == '/') + break; + + /* non-ASCII */ + if((flags & JSON_ENSURE_ASCII) && codepoint > 0x7F) + break; + + pos = end; + } + + if(pos != str) { + if(dump(str, pos - str, data)) + return -1; + } + + if(end == pos) + break; + + /* handle \, /, ", and control codes */ + length = 2; + switch(codepoint) + { + case '\\': text = "\\\\"; break; + case '\"': text = "\\\""; break; + case '\b': text = "\\b"; break; + case '\f': text = "\\f"; break; + case '\n': text = "\\n"; break; + case '\r': text = "\\r"; break; + case '\t': text = "\\t"; break; + case '/': text = "\\/"; break; + default: + { + /* codepoint is in BMP */ + if(codepoint < 0x10000) + { + sprintf(seq, "\\u%04x", codepoint); + length = 6; + } + + /* not in BMP -> construct a UTF-16 surrogate pair */ + else + { + int32_t first, last; + + codepoint -= 0x10000; + first = 0xD800 | ((codepoint & 0xffc00) >> 10); + last = 0xDC00 | (codepoint & 0x003ff); + + sprintf(seq, "\\u%04x\\u%04x", first, last); + length = 12; + } + + text = seq; + break; + } + } + + if(dump(text, length, data)) + return -1; + + str = pos = end; + } + + return dump("\"", 1, data); +} + +static int object_key_compare_keys(const void *key1, const void *key2) +{ + return strcmp(((const struct object_key *)key1)->key, + ((const struct object_key *)key2)->key); +} + +static int object_key_compare_serials(const void *key1, const void *key2) +{ + size_t a = ((const struct object_key *)key1)->serial; + size_t b = ((const struct object_key *)key2)->serial; + + return a < b ? -1 : a == b ? 0 : 1; +} + +static int do_dump(const json_t *json, size_t flags, int depth, + json_dump_callback_t dump, void *data) +{ + switch(json_typeof(json)) { + case JSON_NULL: + return dump("null", 4, data); + + case JSON_TRUE: + return dump("true", 4, data); + + case JSON_FALSE: + return dump("false", 5, data); + + case JSON_INTEGER: + { + char buffer[MAX_INTEGER_STR_LENGTH]; + int size; + + size = snprintf(buffer, MAX_INTEGER_STR_LENGTH, + "%" JSON_INTEGER_FORMAT, + json_integer_value(json)); + if(size < 0 || size >= MAX_INTEGER_STR_LENGTH) + return -1; + + return dump(buffer, size, data); + } + + case JSON_REAL: + { + char buffer[MAX_REAL_STR_LENGTH]; + int size; + double value = json_real_value(json); + + size = jsonp_dtostr(buffer, MAX_REAL_STR_LENGTH, value); + if(size < 0) + return -1; + + return dump(buffer, size, data); + } + + case JSON_STRING: + return dump_string(json_string_value(json), dump, data, flags); + + case JSON_ARRAY: + { + int i; + int n; + json_array_t *array; + + /* detect circular references */ + array = json_to_array(json); + if(array->visited) + goto array_error; + array->visited = 1; + + n = json_array_size(json); + + if(dump("[", 1, data)) + goto array_error; + if(n == 0) { + array->visited = 0; + return dump("]", 1, data); + } + if(dump_indent(flags, depth + 1, 0, dump, data)) + goto array_error; + + for(i = 0; i < n; ++i) { + if(do_dump(json_array_get(json, i), flags, depth + 1, + dump, data)) + goto array_error; + + if(i < n - 1) + { + if(dump(",", 1, data) || + dump_indent(flags, depth + 1, 1, dump, data)) + goto array_error; + } + else + { + if(dump_indent(flags, depth, 0, dump, data)) + goto array_error; + } + } + + array->visited = 0; + return dump("]", 1, data); + + array_error: + array->visited = 0; + return -1; + } + + case JSON_OBJECT: + { + json_object_t *object; + void *iter; + const char *separator; + int separator_length; + + if(flags & JSON_COMPACT) { + separator = ":"; + separator_length = 1; + } + else { + separator = ": "; + separator_length = 2; + } + + /* detect circular references */ + object = json_to_object(json); + if(object->visited) + goto object_error; + object->visited = 1; + + iter = json_object_iter((json_t *)json); + + if(dump("{", 1, data)) + goto object_error; + if(!iter) { + object->visited = 0; + return dump("}", 1, data); + } + if(dump_indent(flags, depth + 1, 0, dump, data)) + goto object_error; + + if(flags & JSON_SORT_KEYS || flags & JSON_PRESERVE_ORDER) + { + struct object_key *keys; + size_t size, i; + int (*cmp_func)(const void *, const void *); + + size = json_object_size(json); + keys = jsonp_malloc(size * sizeof(struct object_key)); + if(!keys) + goto object_error; + + i = 0; + while(iter) + { + keys[i].serial = hashtable_iter_serial(iter); + keys[i].key = json_object_iter_key(iter); + iter = json_object_iter_next((json_t *)json, iter); + i++; + } + assert(i == size); + + if(flags & JSON_SORT_KEYS) + cmp_func = object_key_compare_keys; + else + cmp_func = object_key_compare_serials; + + qsort(keys, size, sizeof(struct object_key), cmp_func); + + for(i = 0; i < size; i++) + { + const char *key; + json_t *value; + + key = keys[i].key; + value = json_object_get(json, key); + assert(value); + + dump_string(key, dump, data, flags); + if(dump(separator, separator_length, data) || + do_dump(value, flags, depth + 1, dump, data)) + { + jsonp_free(keys); + goto object_error; + } + + if(i < size - 1) + { + if(dump(",", 1, data) || + dump_indent(flags, depth + 1, 1, dump, data)) + { + jsonp_free(keys); + goto object_error; + } + } + else + { + if(dump_indent(flags, depth, 0, dump, data)) + { + jsonp_free(keys); + goto object_error; + } + } + } + + jsonp_free(keys); + } + else + { + /* Don't sort keys */ + + while(iter) + { + void *next = json_object_iter_next((json_t *)json, iter); + + dump_string(json_object_iter_key(iter), dump, data, flags); + if(dump(separator, separator_length, data) || + do_dump(json_object_iter_value(iter), flags, depth + 1, + dump, data)) + goto object_error; + + if(next) + { + if(dump(",", 1, data) || + dump_indent(flags, depth + 1, 1, dump, data)) + goto object_error; + } + else + { + if(dump_indent(flags, depth, 0, dump, data)) + goto object_error; + } + + iter = next; + } + } + + object->visited = 0; + return dump("}", 1, data); + + object_error: + object->visited = 0; + return -1; + } + + default: + /* not reached */ + return -1; + } +} + +char *json_dumps(const json_t *json, size_t flags) +{ + strbuffer_t strbuff; + char *result; + + if(strbuffer_init(&strbuff)) + return NULL; + + if(json_dump_callback(json, dump_to_strbuffer, (void *)&strbuff, flags)) + result = NULL; + else + result = jsonp_strdup(strbuffer_value(&strbuff)); + + strbuffer_close(&strbuff); + return result; +} + +int json_dumpf(const json_t *json, FILE *output, size_t flags) +{ + return json_dump_callback(json, dump_to_file, (void *)output, flags); +} + +int json_dump_file(const json_t *json, const char *path, size_t flags) +{ + int result; + + FILE *output = fopen(path, "w"); + if(!output) + return -1; + + result = json_dumpf(json, output, flags); + + fclose(output); + return result; +} + +int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags) +{ + if(!(flags & JSON_ENCODE_ANY)) { + if(!json_is_array(json) && !json_is_object(json)) + return -1; + } + + return do_dump(json, flags, 0, callback, data); +} diff --git a/src/jansson/error.c b/src/jansson/error.c new file mode 100644 index 0000000..a544a59 --- /dev/null +++ b/src/jansson/error.c @@ -0,0 +1,63 @@ +#include <string.h> +#include "jansson_private.h" + +void jsonp_error_init(json_error_t *error, const char *source) +{ + if(error) + { + error->text[0] = '\0'; + error->line = -1; + error->column = -1; + error->position = 0; + if(source) + jsonp_error_set_source(error, source); + else + error->source[0] = '\0'; + } +} + +void jsonp_error_set_source(json_error_t *error, const char *source) +{ + size_t length; + + if(!error || !source) + return; + + length = strlen(source); + if(length < JSON_ERROR_SOURCE_LENGTH) + strcpy(error->source, source); + else { + size_t extra = length - JSON_ERROR_SOURCE_LENGTH + 4; + strcpy(error->source, "..."); + strcpy(error->source + 3, source + extra); + } +} + +void jsonp_error_set(json_error_t *error, int line, int column, + size_t position, const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + jsonp_error_vset(error, line, column, position, msg, ap); + va_end(ap); +} + +void jsonp_error_vset(json_error_t *error, int line, int column, + size_t position, const char *msg, va_list ap) +{ + if(!error) + return; + + if(error->text[0] != '\0') { + /* error already set */ + return; + } + + error->line = line; + error->column = column; + error->position = position; + + vsnprintf(error->text, JSON_ERROR_TEXT_LENGTH, msg, ap); + error->text[JSON_ERROR_TEXT_LENGTH - 1] = '\0'; +} diff --git a/src/jansson/hashtable.c b/src/jansson/hashtable.c new file mode 100644 index 0000000..bcbaa8c --- /dev/null +++ b/src/jansson/hashtable.c @@ -0,0 +1,360 @@ +/* + * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org> + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include <stdlib.h> +#include <string.h> +#include <jansson_config.h> /* for JSON_INLINE */ +#include "jansson_private.h" /* for container_of() */ +#include "hashtable.h" + +typedef struct hashtable_list list_t; +typedef struct hashtable_pair pair_t; +typedef struct hashtable_bucket bucket_t; + +#define list_to_pair(list_) container_of(list_, pair_t, list) + +/* From http://www.cse.yorku.ca/~oz/hash.html */ +static size_t hash_str(const void *ptr) +{ + const char *str = (const char *)ptr; + + size_t hash = 5381; + size_t c; + + while((c = (size_t)*str)) + { + hash = ((hash << 5) + hash) + c; + str++; + } + + return hash; +} + +static JSON_INLINE void list_init(list_t *list) +{ + list->next = list; + list->prev = list; +} + +static JSON_INLINE void list_insert(list_t *list, list_t *node) +{ + node->next = list; + node->prev = list->prev; + list->prev->next = node; + list->prev = node; +} + +static JSON_INLINE void list_remove(list_t *list) +{ + list->prev->next = list->next; + list->next->prev = list->prev; +} + +static JSON_INLINE int bucket_is_empty(hashtable_t *hashtable, bucket_t *bucket) +{ + return bucket->first == &hashtable->list && bucket->first == bucket->last; +} + +static void insert_to_bucket(hashtable_t *hashtable, bucket_t *bucket, + list_t *list) +{ + if(bucket_is_empty(hashtable, bucket)) + { + list_insert(&hashtable->list, list); + bucket->first = bucket->last = list; + } + else + { + list_insert(bucket->first, list); + bucket->first = list; + } +} + +static const size_t primes[] = { + 5, 13, 23, 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593, + 49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469, + 12582917, 25165843, 50331653, 100663319, 201326611, 402653189, + 805306457, 1610612741 +}; + +static JSON_INLINE size_t num_buckets(hashtable_t *hashtable) +{ + return primes[hashtable->num_buckets]; +} + + +static pair_t *hashtable_find_pair(hashtable_t *hashtable, bucket_t *bucket, + const char *key, size_t hash) +{ + list_t *list; + pair_t *pair; + + if(bucket_is_empty(hashtable, bucket)) + return NULL; + + list = bucket->first; + while(1) + { + pair = list_to_pair(list); + if(pair->hash == hash && strcmp(pair->key, key) == 0) + return pair; + + if(list == bucket->last) + break; + + list = list->next; + } + + return NULL; +} + +/* returns 0 on success, -1 if key was not found */ +static int hashtable_do_del(hashtable_t *hashtable, + const char *key, size_t hash) +{ + pair_t *pair; + bucket_t *bucket; + size_t index; + + index = hash % num_buckets(hashtable); + bucket = &hashtable->buckets[index]; + + pair = hashtable_find_pair(hashtable, bucket, key, hash); + if(!pair) + return -1; + + if(&pair->list == bucket->first && &pair->list == bucket->last) + bucket->first = bucket->last = &hashtable->list; + + else if(&pair->list == bucket->first) + bucket->first = pair->list.next; + + else if(&pair->list == bucket->last) + bucket->last = pair->list.prev; + + list_remove(&pair->list); + json_decref(pair->value); + + jsonp_free(pair); + hashtable->size--; + + return 0; +} + +static void hashtable_do_clear(hashtable_t *hashtable) +{ + list_t *list, *next; + pair_t *pair; + + for(list = hashtable->list.next; list != &hashtable->list; list = next) + { + next = list->next; + pair = list_to_pair(list); + json_decref(pair->value); + jsonp_free(pair); + } +} + +static int hashtable_do_rehash(hashtable_t *hashtable) +{ + list_t *list, *next; + pair_t *pair; + size_t i, index, new_size; + + jsonp_free(hashtable->buckets); + + hashtable->num_buckets++; + new_size = num_buckets(hashtable); + + hashtable->buckets = jsonp_malloc(new_size * sizeof(bucket_t)); + if(!hashtable->buckets) + return -1; + + for(i = 0; i < num_buckets(hashtable); i++) + { + hashtable->buckets[i].first = hashtable->buckets[i].last = + &hashtable->list; + } + + list = hashtable->list.next; + list_init(&hashtable->list); + + for(; list != &hashtable->list; list = next) { + next = list->next; + pair = list_to_pair(list); + index = pair->hash % new_size; + insert_to_bucket(hashtable, &hashtable->buckets[index], &pair->list); + } + + return 0; +} + + +int hashtable_init(hashtable_t *hashtable) +{ + size_t i; + + hashtable->size = 0; + hashtable->num_buckets = 0; /* index to primes[] */ + hashtable->buckets = jsonp_malloc(num_buckets(hashtable) * sizeof(bucket_t)); + if(!hashtable->buckets) + return -1; + + list_init(&hashtable->list); + + for(i = 0; i < num_buckets(hashtable); i++) + { + hashtable->buckets[i].first = hashtable->buckets[i].last = + &hashtable->list; + } + + return 0; +} + +void hashtable_close(hashtable_t *hashtable) +{ + hashtable_do_clear(hashtable); + jsonp_free(hashtable->buckets); +} + +int hashtable_set(hashtable_t *hashtable, + const char *key, size_t serial, + json_t *value) +{ + pair_t *pair; + bucket_t *bucket; + size_t hash, index; + + /* rehash if the load ratio exceeds 1 */ + if(hashtable->size >= num_buckets(hashtable)) + if(hashtable_do_rehash(hashtable)) + return -1; + + hash = hash_str(key); + index = hash % num_buckets(hashtable); + bucket = &hashtable->buckets[index]; + pair = hashtable_find_pair(hashtable, bucket, key, hash); + + if(pair) + { + json_decref(pair->value); + pair->value = value; + } + else + { + /* offsetof(...) returns the size of pair_t without the last, + flexible member. This way, the correct amount is + allocated. */ + pair = jsonp_malloc(offsetof(pair_t, key) + strlen(key) + 1); + if(!pair) + return -1; + + pair->hash = hash; + pair->serial = serial; + strcpy(pair->key, key); + pair->value = value; + list_init(&pair->list); + + insert_to_bucket(hashtable, bucket, &pair->list); + + hashtable->size++; + } + return 0; +} + +void *hashtable_get(hashtable_t *hashtable, const char *key) +{ + pair_t *pair; + size_t hash; + bucket_t *bucket; + + hash = hash_str(key); + bucket = &hashtable->buckets[hash % num_buckets(hashtable)]; + + pair = hashtable_find_pair(hashtable, bucket, key, hash); + if(!pair) + return NULL; + + return pair->value; +} + +int hashtable_del(hashtable_t *hashtable, const char *key) +{ + size_t hash = hash_str(key); + return hashtable_do_del(hashtable, key, hash); +} + +void hashtable_clear(hashtable_t *hashtable) +{ + size_t i; + + hashtable_do_clear(hashtable); + + for(i = 0; i < num_buckets(hashtable); i++) + { + hashtable->buckets[i].first = hashtable->buckets[i].last = + &hashtable->list; + } + + list_init(&hashtable->list); + hashtable->size = 0; +} + +void *hashtable_iter(hashtable_t *hashtable) +{ + return hashtable_iter_next(hashtable, &hashtable->list); +} + +void *hashtable_iter_at(hashtable_t *hashtable, const char *key) +{ + pair_t *pair; + size_t hash; + bucket_t *bucket; + + hash = hash_str(key); + bucket = &hashtable->buckets[hash % num_buckets(hashtable)]; + + pair = hashtable_find_pair(hashtable, bucket, key, hash); + if(!pair) + return NULL; + + return &pair->list; +} + +void *hashtable_iter_next(hashtable_t *hashtable, void *iter) +{ + list_t *list = (list_t *)iter; + if(list->next == &hashtable->list) + return NULL; + return list->next; +} + +void *hashtable_iter_key(void *iter) +{ + pair_t *pair = list_to_pair((list_t *)iter); + return pair->key; +} + +size_t hashtable_iter_serial(void *iter) +{ + pair_t *pair = list_to_pair((list_t *)iter); + return pair->serial; +} + +void *hashtable_iter_value(void *iter) +{ + pair_t *pair = list_to_pair((list_t *)iter); + return pair->value; +} + +void hashtable_iter_set(void *iter, json_t *value) +{ + pair_t *pair = list_to_pair((list_t *)iter); + + json_decref(pair->value); + pair->value = value; +} diff --git a/src/jansson/hashtable.h b/src/jansson/hashtable.h new file mode 100644 index 0000000..de1df26 --- /dev/null +++ b/src/jansson/hashtable.h @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org> + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef HASHTABLE_H +#define HASHTABLE_H + +struct hashtable_list { + struct hashtable_list *prev; + struct hashtable_list *next; +}; + +/* "pair" may be a bit confusing a name, but think of it as a + key-value pair. In this case, it just encodes some extra data, + too */ +struct hashtable_pair { + size_t hash; + struct hashtable_list list; + json_t *value; + size_t serial; + char key[1]; +}; + +struct hashtable_bucket { + struct hashtable_list *first; + struct hashtable_list *last; +}; + +typedef struct hashtable { + size_t size; + struct hashtable_bucket *buckets; + size_t num_buckets; /* index to primes[] */ + struct hashtable_list list; +} hashtable_t; + + +#define hashtable_key_to_iter(key_) \ + (&(container_of(key_, struct hashtable_pair, key)->list)) + +/** + * hashtable_init - Initialize a hashtable object + * + * @hashtable: The (statically allocated) hashtable object + * + * Initializes a statically allocated hashtable object. The object + * should be cleared with hashtable_close when it's no longer used. + * + * Returns 0 on success, -1 on error (out of memory). + */ +int hashtable_init(hashtable_t *hashtable); + +/** + * hashtable_close - Release all resources used by a hashtable object + * + * @hashtable: The hashtable + * + * Destroys a statically allocated hashtable object. + */ +void hashtable_close(hashtable_t *hashtable); + +/** + * hashtable_set - Add/modify value in hashtable + * + * @hashtable: The hashtable object + * @key: The key + * @serial: For addition order of keys + * @value: The value + * + * If a value with the given key already exists, its value is replaced + * with the new value. Value is "stealed" in the sense that hashtable + * doesn't increment its refcount but decreases the refcount when the + * value is no longer needed. + * + * Returns 0 on success, -1 on failure (out of memory). + */ +int hashtable_set(hashtable_t *hashtable, + const char *key, size_t serial, + json_t *value); + +/** + * hashtable_get - Get a value associated with a key + * + * @hashtable: The hashtable object + * @key: The key + * + * Returns value if it is found, or NULL otherwise. + */ +void *hashtable_get(hashtable_t *hashtable, const char *key); + +/** + * hashtable_del - Remove a value from the hashtable + * + * @hashtable: The hashtable object + * @key: The key + * + * Returns 0 on success, or -1 if the key was not found. + */ +int hashtable_del(hashtable_t *hashtable, const char *key); + +/** + * hashtable_clear - Clear hashtable + * + * @hashtable: The hashtable object + * + * Removes all items from the hashtable. + */ +void hashtable_clear(hashtable_t *hashtable); + +/** + * hashtable_iter - Iterate over hashtable + * + * @hashtable: The hashtable object + * + * Returns an opaque iterator to the first element in the hashtable. + * The iterator should be passed to hashtable_iter_* functions. + * The hashtable items are not iterated over in any particular order. + * + * There's no need to free the iterator in any way. The iterator is + * valid as long as the item that is referenced by the iterator is not + * deleted. Other values may be added or deleted. In particular, + * hashtable_iter_next() may be called on an iterator, and after that + * the key/value pair pointed by the old iterator may be deleted. + */ +void *hashtable_iter(hashtable_t *hashtable); + +/** + * hashtable_iter_at - Return an iterator at a specific key + * + * @hashtable: The hashtable object + * @key: The key that the iterator should point to + * + * Like hashtable_iter() but returns an iterator pointing to a + * specific key. + */ +void *hashtable_iter_at(hashtable_t *hashtable, const char *key); + +/** + * hashtable_iter_next - Advance an iterator + * + * @hashtable: The hashtable object + * @iter: The iterator + * + * Returns a new iterator pointing to the next element in the + * hashtable or NULL if the whole hastable has been iterated over. + */ +void *hashtable_iter_next(hashtable_t *hashtable, void *iter); + +/** + * hashtable_iter_key - Retrieve the key pointed by an iterator + * + * @iter: The iterator + */ +void *hashtable_iter_key(void *iter); + +/** + * hashtable_iter_serial - Retrieve the serial number pointed to by an iterator + * + * @iter: The iterator + */ +size_t hashtable_iter_serial(void *iter); + +/** + * hashtable_iter_value - Retrieve the value pointed by an iterator + * + * @iter: The iterator + */ +void *hashtable_iter_value(void *iter); + +/** + * hashtable_iter_set - Set the value pointed by an iterator + * + * @iter: The iterator + * @value: The value to set + */ +void hashtable_iter_set(void *iter, json_t *value); + +#endif diff --git a/src/jansson/jansson.h b/src/jansson/jansson.h new file mode 100644 index 0000000..352c6ce --- /dev/null +++ b/src/jansson/jansson.h @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org> + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef JANSSON_H +#define JANSSON_H + +#include <stdio.h> +#include <stdlib.h> /* for size_t */ +#include <stdarg.h> + +#include <jansson_config.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* version */ + +#define JANSSON_MAJOR_VERSION 2 +#define JANSSON_MINOR_VERSION 4 +#define JANSSON_MICRO_VERSION 0 + +/* Micro version is omitted if it's 0 */ +#define JANSSON_VERSION "2.4" + +/* Version as a 3-byte hex number, e.g. 0x010201 == 1.2.1. Use this + for numeric comparisons, e.g. #if JANSSON_VERSION_HEX >= ... */ +#define JANSSON_VERSION_HEX ((JANSSON_MAJOR_VERSION << 16) | \ + (JANSSON_MINOR_VERSION << 8) | \ + (JANSSON_MICRO_VERSION << 0)) + + +/* types */ + +typedef enum { + JSON_OBJECT, + JSON_ARRAY, + JSON_STRING, + JSON_INTEGER, + JSON_REAL, + JSON_TRUE, + JSON_FALSE, + JSON_NULL +} json_type; + +typedef struct { + json_type type; + size_t refcount; +} json_t; + +#if JSON_INTEGER_IS_LONG_LONG +#ifdef _WIN32 +#define JSON_INTEGER_FORMAT "I64d" +#else +#define JSON_INTEGER_FORMAT "lld" +#endif +typedef long long json_int_t; +#else +#define JSON_INTEGER_FORMAT "ld" +typedef long json_int_t; +#endif /* JSON_INTEGER_IS_LONG_LONG */ + +#define json_typeof(json) ((json)->type) +#define json_is_object(json) (json && json_typeof(json) == JSON_OBJECT) +#define json_is_array(json) (json && json_typeof(json) == JSON_ARRAY) +#define json_is_string(json) (json && json_typeof(json) == JSON_STRING) +#define json_is_integer(json) (json && json_typeof(json) == JSON_INTEGER) +#define json_is_real(json) (json && json_typeof(json) == JSON_REAL) +#define json_is_number(json) (json_is_integer(json) || json_is_real(json)) +#define json_is_true(json) (json && json_typeof(json) == JSON_TRUE) +#define json_is_false(json) (json && json_typeof(json) == JSON_FALSE) +#define json_is_boolean(json) (json_is_true(json) || json_is_false(json)) +#define json_is_null(json) (json && json_typeof(json) == JSON_NULL) + +/* construction, destruction, reference counting */ + +json_t *json_object(void); +json_t *json_array(void); +json_t *json_string(const char *value); +json_t *json_string_nocheck(const char *value); +json_t *json_integer(json_int_t value); +json_t *json_real(double value); +json_t *json_true(void); +json_t *json_false(void); +#define json_boolean(val) ((val) ? json_true() : json_false()) +json_t *json_null(void); + +static JSON_INLINE +json_t *json_incref(json_t *json) +{ + if(json && json->refcount != (size_t)-1) + ++json->refcount; + return json; +} + +/* do not call json_delete directly */ +void json_delete(json_t *json); + +static JSON_INLINE +void json_decref(json_t *json) +{ + if(json && json->refcount != (size_t)-1 && --json->refcount == 0) + json_delete(json); +} + + +/* error reporting */ + +#define JSON_ERROR_TEXT_LENGTH 160 +#define JSON_ERROR_SOURCE_LENGTH 80 + +typedef struct { + int line; + int column; + int position; + char source[JSON_ERROR_SOURCE_LENGTH]; + char text[JSON_ERROR_TEXT_LENGTH]; +} json_error_t; + + +/* getters, setters, manipulation */ + +size_t json_object_size(const json_t *object); +json_t *json_object_get(const json_t *object, const char *key); +int json_object_set_new(json_t *object, const char *key, json_t *value); +int json_object_set_new_nocheck(json_t *object, const char *key, json_t *value); +int json_object_del(json_t *object, const char *key); +int json_object_clear(json_t *object); +int json_object_update(json_t *object, json_t *other); +int json_object_update_existing(json_t *object, json_t *other); +int json_object_update_missing(json_t *object, json_t *other); +void *json_object_iter(json_t *object); +void *json_object_iter_at(json_t *object, const char *key); +void *json_object_key_to_iter(const char *key); +void *json_object_iter_next(json_t *object, void *iter); +const char *json_object_iter_key(void *iter); +json_t *json_object_iter_value(void *iter); +int json_object_iter_set_new(json_t *object, void *iter, json_t *value); + +#define json_object_foreach(object, key, value) \ + for(key = json_object_iter_key(json_object_iter(object)); \ + key && (value = json_object_iter_value(json_object_key_to_iter(key))); \ + key = json_object_iter_key(json_object_iter_next(object, json_object_key_to_iter(key)))) + +static JSON_INLINE +int json_object_set(json_t *object, const char *key, json_t *value) +{ + return json_object_set_new(object, key, json_incref(value)); +} + +static JSON_INLINE +int json_object_set_nocheck(json_t *object, const char *key, json_t *value) +{ + return json_object_set_new_nocheck(object, key, json_incref(value)); +} + +static JSON_INLINE +int json_object_iter_set(json_t *object, void *iter, json_t *value) +{ + return json_object_iter_set_new(object, iter, json_incref(value)); +} + +size_t json_array_size(const json_t *array); +json_t *json_array_get(const json_t *array, size_t index); +int json_array_set_new(json_t *array, size_t index, json_t *value); +int json_array_append_new(json_t *array, json_t *value); +int json_array_insert_new(json_t *array, size_t index, json_t *value); +int json_array_remove(json_t *array, size_t index); +int json_array_clear(json_t *array); +int json_array_extend(json_t *array, json_t *other); + +static JSON_INLINE +int json_array_set(json_t *array, size_t index, json_t *value) +{ + return json_array_set_new(array, index, json_incref(value)); +} + +static JSON_INLINE +int json_array_append(json_t *array, json_t *value) +{ + return json_array_append_new(array, json_incref(value)); +} + +static JSON_INLINE +int json_array_insert(json_t *array, size_t index, json_t *value) +{ + return json_array_insert_new(array, index, json_incref(value)); +} + +const char *json_string_value(const json_t *string); +json_int_t json_integer_value(const json_t *integer); +double json_real_value(const json_t *real); +double json_number_value(const json_t *json); + +int json_string_set(json_t *string, const char *value); +int json_string_set_nocheck(json_t *string, const char *value); +int json_integer_set(json_t *integer, json_int_t value); +int json_real_set(json_t *real, double value); + + +/* pack, unpack */ + +json_t *json_pack(const char *fmt, ...); +json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...); +json_t *json_vpack_ex(json_error_t *error, size_t flags, const char *fmt, va_list ap); + +#define JSON_VALIDATE_ONLY 0x1 +#define JSON_STRICT 0x2 + +int json_unpack(json_t *root, const char *fmt, ...); +int json_unpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, ...); +int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, va_list ap); + + +/* equality */ + +int json_equal(json_t *value1, json_t *value2); + + +/* copying */ + +json_t *json_copy(json_t *value); +json_t *json_deep_copy(json_t *value); + + +/* decoding */ + +#define JSON_REJECT_DUPLICATES 0x1 +#define JSON_DISABLE_EOF_CHECK 0x2 +#define JSON_DECODE_ANY 0x4 + +typedef size_t (*json_load_callback_t)(void *buffer, size_t buflen, void *data); + +json_t *json_loads(const char *input, size_t flags, json_error_t *error); +json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error); +json_t *json_loadf(FILE *input, size_t flags, json_error_t *error); +json_t *json_load_file(const char *path, size_t flags, json_error_t *error); +json_t *json_load_callback(json_load_callback_t callback, void *data, size_t flags, json_error_t *error); + + +/* encoding */ + +#define JSON_INDENT(n) (n & 0x1F) +#define JSON_COMPACT 0x20 +#define JSON_ENSURE_ASCII 0x40 +#define JSON_SORT_KEYS 0x80 +#define JSON_PRESERVE_ORDER 0x100 +#define JSON_ENCODE_ANY 0x200 +#define JSON_ESCAPE_SLASH 0x400 + +typedef int (*json_dump_callback_t)(const char *buffer, size_t size, void *data); + +char *json_dumps(const json_t *json, size_t flags); +int json_dumpf(const json_t *json, FILE *output, size_t flags); +int json_dump_file(const json_t *json, const char *path, size_t flags); +int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags); + +/* custom memory allocation */ + +typedef void *(*json_malloc_t)(size_t); +typedef void (*json_free_t)(void *); + +void json_set_alloc_funcs(json_malloc_t malloc_fn, json_free_t free_fn); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/jansson/jansson_config.h b/src/jansson/jansson_config.h new file mode 100644 index 0000000..e576cd1 --- /dev/null +++ b/src/jansson/jansson_config.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2010-2012 Petri Lehtinen <petri@digip.org> + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + * + * + * This file specifies a part of the site-specific configuration for + * Jansson, namely those things that affect the public API in + * jansson.h. + * + * The configure script copies this file to jansson_config.h and + * replaces @var@ substitutions by values that fit your system. If you + * cannot run the configure script, you can do the value substitution + * by hand. + */ + +#ifndef JANSSON_CONFIG_H +#define JANSSON_CONFIG_H + +/* If your compiler supports the inline keyword in C, JSON_INLINE is + defined to `inline', otherwise empty. In C++, the inline is always + supported. */ +#ifdef __cplusplus +#define JSON_INLINE inline +#else +#define JSON_INLINE inline +#endif + +/* If your compiler supports the `long long` type and the strtoll() + library function, JSON_INTEGER_IS_LONG_LONG is defined to 1, + otherwise to 0. */ +#define JSON_INTEGER_IS_LONG_LONG 1 + +/* If locale.h and localeconv() are available, define to 1, + otherwise to 0. */ +#define JSON_HAVE_LOCALECONV 1 + +#endif diff --git a/src/jansson/jansson_private.h b/src/jansson/jansson_private.h new file mode 100644 index 0000000..7d6d09d --- /dev/null +++ b/src/jansson/jansson_private.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org> + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef JANSSON_PRIVATE_H +#define JANSSON_PRIVATE_H + +#include <stddef.h> +#include "jansson.h" +#include "hashtable.h" +#include "strbuffer.h" + +#define container_of(ptr_, type_, member_) \ + ((type_ *)((char *)ptr_ - offsetof(type_, member_))) + +/* On some platforms, max() may already be defined */ +#ifndef max +#define max(a, b) ((a) > (b) ? (a) : (b)) +#endif + +/* va_copy is a C99 feature. In C89 implementations, it's sometimes + available as __va_copy. If not, memcpy() should do the trick. */ +#ifndef va_copy +#ifdef __va_copy +#define va_copy __va_copy +#else +#define va_copy(a, b) memcpy(&(a), &(b), sizeof(va_list)) +#endif +#endif + +typedef struct { + json_t json; + hashtable_t hashtable; + size_t serial; + int visited; +} json_object_t; + +typedef struct { + json_t json; + size_t size; + size_t entries; + json_t **table; + int visited; +} json_array_t; + +typedef struct { + json_t json; + char *value; +} json_string_t; + +typedef struct { + json_t json; + double value; +} json_real_t; + +typedef struct { + json_t json; + json_int_t value; +} json_integer_t; + +#define json_to_object(json_) container_of(json_, json_object_t, json) +#define json_to_array(json_) container_of(json_, json_array_t, json) +#define json_to_string(json_) container_of(json_, json_string_t, json) +#define json_to_real(json_) container_of(json_, json_real_t, json) +#define json_to_integer(json_) container_of(json_, json_integer_t, json) + +void jsonp_error_init(json_error_t *error, const char *source); +void jsonp_error_set_source(json_error_t *error, const char *source); +void jsonp_error_set(json_error_t *error, int line, int column, + size_t position, const char *msg, ...); +void jsonp_error_vset(json_error_t *error, int line, int column, + size_t position, const char *msg, va_list ap); + +/* Locale independent string<->double conversions */ +int jsonp_strtod(strbuffer_t *strbuffer, double *out); +int jsonp_dtostr(char *buffer, size_t size, double value); + +/* Wrappers for custom memory functions */ +void* jsonp_malloc(size_t size); +void jsonp_free(void *ptr); +char *jsonp_strdup(const char *str); + +/* Windows compatibility */ +#ifdef _WIN32 +#define snprintf _snprintf +#define vsnprintf _vsnprintf +#endif + +#endif diff --git a/src/jansson/load.c b/src/jansson/load.c new file mode 100644 index 0000000..d88a704 --- /dev/null +++ b/src/jansson/load.c @@ -0,0 +1,1054 @@ +/* + * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org> + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#define _GNU_SOURCE +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#include "jansson.h" +#include "jansson_private.h" +#include "strbuffer.h" +#include "utf.h" + +#define STREAM_STATE_OK 0 +#define STREAM_STATE_EOF -1 +#define STREAM_STATE_ERROR -2 + +#define TOKEN_INVALID -1 +#define TOKEN_EOF 0 +#define TOKEN_STRING 256 +#define TOKEN_INTEGER 257 +#define TOKEN_REAL 258 +#define TOKEN_TRUE 259 +#define TOKEN_FALSE 260 +#define TOKEN_NULL 261 + +/* Locale independent versions of isxxx() functions */ +#define l_isupper(c) ('A' <= (c) && (c) <= 'Z') +#define l_islower(c) ('a' <= (c) && (c) <= 'z') +#define l_isalpha(c) (l_isupper(c) || l_islower(c)) +#define l_isdigit(c) ('0' <= (c) && (c) <= '9') +#define l_isxdigit(c) \ + (l_isdigit(c) || 'A' <= (c) || (c) <= 'F' || 'a' <= (c) || (c) <= 'f') + +/* Read one byte from stream, convert to unsigned char, then int, and + return. return EOF on end of file. This corresponds to the + behaviour of fgetc(). */ +typedef int (*get_func)(void *data); + +typedef struct { + get_func get; + void *data; + char buffer[5]; + size_t buffer_pos; + int state; + int line; + int column, last_column; + size_t position; +} stream_t; + +typedef struct { + stream_t stream; + strbuffer_t saved_text; + int token; + union { + char *string; + json_int_t integer; + double real; + } value; +} lex_t; + +#define stream_to_lex(stream) container_of(stream, lex_t, stream) + + +/*** error reporting ***/ + +static void error_set(json_error_t *error, const lex_t *lex, + const char *msg, ...) +{ + va_list ap; + char msg_text[JSON_ERROR_TEXT_LENGTH]; + char msg_with_context[JSON_ERROR_TEXT_LENGTH]; + + int line = -1, col = -1; + size_t pos = 0; + const char *result = msg_text; + + if(!error) + return; + + va_start(ap, msg); + vsnprintf(msg_text, JSON_ERROR_TEXT_LENGTH, msg, ap); + msg_text[JSON_ERROR_TEXT_LENGTH - 1] = '\0'; + va_end(ap); + + if(lex) + { + const char *saved_text = strbuffer_value(&lex->saved_text); + + line = lex->stream.line; + col = lex->stream.column; + pos = lex->stream.position; + + if(saved_text && saved_text[0]) + { + if(lex->saved_text.length <= 20) { + snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH, + "%s near '%s'", msg_text, saved_text); + msg_with_context[JSON_ERROR_TEXT_LENGTH - 1] = '\0'; + result = msg_with_context; + } + } + else + { + if(lex->stream.state == STREAM_STATE_ERROR) { + /* No context for UTF-8 decoding errors */ + result = msg_text; + } + else { + snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH, + "%s near end of file", msg_text); + msg_with_context[JSON_ERROR_TEXT_LENGTH - 1] = '\0'; + result = msg_with_context; + } + } + } + + jsonp_error_set(error, line, col, pos, "%s", result); +} + + +/*** lexical analyzer ***/ + +static void +stream_init(stream_t *stream, get_func get, void *data) +{ + stream->get = get; + stream->data = data; + stream->buffer[0] = '\0'; + stream->buffer_pos = 0; + + stream->state = STREAM_STATE_OK; + stream->line = 1; + stream->column = 0; + stream->position = 0; +} + +static int stream_get(stream_t *stream, json_error_t *error) +{ + int c; + + if(stream->state != STREAM_STATE_OK) + return stream->state; + + if(!stream->buffer[stream->buffer_pos]) + { + c = stream->get(stream->data); + if(c == EOF) { + stream->state = STREAM_STATE_EOF; + return STREAM_STATE_EOF; + } + + stream->buffer[0] = c; + stream->buffer_pos = 0; + + if(0x80 <= c && c <= 0xFF) + { + /* multi-byte UTF-8 sequence */ + int i, count; + + count = utf8_check_first(c); + if(!count) + goto out; + + assert(count >= 2); + + for(i = 1; i < count; i++) + stream->buffer[i] = stream->get(stream->data); + + if(!utf8_check_full(stream->buffer, count, NULL)) + goto out; + + stream->buffer[count] = '\0'; + } + else + stream->buffer[1] = '\0'; + } + + c = stream->buffer[stream->buffer_pos++]; + + stream->position++; + if(c == '\n') { + stream->line++; + stream->last_column = stream->column; + stream->column = 0; + } + else if(utf8_check_first(c)) { + /* track the Unicode character column, so increment only if + this is the first character of a UTF-8 sequence */ + stream->column++; + } + + return c; + +out: + stream->state = STREAM_STATE_ERROR; + error_set(error, stream_to_lex(stream), "unable to decode byte 0x%x", c); + return STREAM_STATE_ERROR; +} + +static void stream_unget(stream_t *stream, int c) +{ + if(c == STREAM_STATE_EOF || c == STREAM_STATE_ERROR) + return; + + stream->position--; + if(c == '\n') { + stream->line--; + stream->column = stream->last_column; + } + else if(utf8_check_first(c)) + stream->column--; + + assert(stream->buffer_pos > 0); + stream->buffer_pos--; + assert(stream->buffer[stream->buffer_pos] == c); +} + + +static int lex_get(lex_t *lex, json_error_t *error) +{ + return stream_get(&lex->stream, error); +} + +static void lex_save(lex_t *lex, int c) +{ + strbuffer_append_byte(&lex->saved_text, c); +} + +static int lex_get_save(lex_t *lex, json_error_t *error) +{ + int c = stream_get(&lex->stream, error); + if(c != STREAM_STATE_EOF && c != STREAM_STATE_ERROR) + lex_save(lex, c); + return c; +} + +static void lex_unget(lex_t *lex, int c) +{ + stream_unget(&lex->stream, c); +} + +static void lex_unget_unsave(lex_t *lex, int c) +{ + if(c != STREAM_STATE_EOF && c != STREAM_STATE_ERROR) { + char d; + stream_unget(&lex->stream, c); + d = strbuffer_pop(&lex->saved_text); + assert(c == d); + } +} + +static void lex_save_cached(lex_t *lex) +{ + while(lex->stream.buffer[lex->stream.buffer_pos] != '\0') + { + lex_save(lex, lex->stream.buffer[lex->stream.buffer_pos]); + lex->stream.buffer_pos++; + lex->stream.position++; + } +} + +/* assumes that str points to 'u' plus at least 4 valid hex digits */ +static int32_t decode_unicode_escape(const char *str) +{ + int i; + int32_t value = 0; + + assert(str[0] == 'u'); + + for(i = 1; i <= 4; i++) { + char c = str[i]; + value <<= 4; + if(l_isdigit(c)) + value += c - '0'; + else if(l_islower(c)) + value += c - 'a' + 10; + else if(l_isupper(c)) + value += c - 'A' + 10; + else + assert(0); + } + + return value; +} + +static void lex_scan_string(lex_t *lex, json_error_t *error) +{ + int c; + const char *p; + char *t; + int i; + + lex->value.string = NULL; + lex->token = TOKEN_INVALID; + + c = lex_get_save(lex, error); + + while(c != '"') { + if(c == STREAM_STATE_ERROR) + goto out; + + else if(c == STREAM_STATE_EOF) { + error_set(error, lex, "premature end of input"); + goto out; + } + + else if(0 <= c && c <= 0x1F) { + /* control character */ + lex_unget_unsave(lex, c); + if(c == '\n') + error_set(error, lex, "unexpected newline", c); + else + error_set(error, lex, "control character 0x%x", c); + goto out; + } + + else if(c == '\\') { + c = lex_get_save(lex, error); + if(c == 'u') { + c = lex_get_save(lex, error); + for(i = 0; i < 4; i++) { + if(!l_isxdigit(c)) { + error_set(error, lex, "invalid escape"); + goto out; + } + c = lex_get_save(lex, error); + } + } + else if(c == '"' || c == '\\' || c == '/' || c == 'b' || + c == 'f' || c == 'n' || c == 'r' || c == 't') + c = lex_get_save(lex, error); + else { + error_set(error, lex, "invalid escape"); + goto out; + } + } + else + c = lex_get_save(lex, error); + } + + /* the actual value is at most of the same length as the source + string, because: + - shortcut escapes (e.g. "\t") (length 2) are converted to 1 byte + - a single \uXXXX escape (length 6) is converted to at most 3 bytes + - two \uXXXX escapes (length 12) forming an UTF-16 surrogate pair + are converted to 4 bytes + */ + lex->value.string = jsonp_malloc(lex->saved_text.length + 1); + if(!lex->value.string) { + /* this is not very nice, since TOKEN_INVALID is returned */ + goto out; + } + + /* the target */ + t = lex->value.string; + + /* + 1 to skip the " */ + p = strbuffer_value(&lex->saved_text) + 1; + + while(*p != '"') { + if(*p == '\\') { + p++; + if(*p == 'u') { + char buffer[4]; + int length; + int32_t value; + + value = decode_unicode_escape(p); + p += 5; + + if(0xD800 <= value && value <= 0xDBFF) { + /* surrogate pair */ + if(*p == '\\' && *(p + 1) == 'u') { + int32_t value2 = decode_unicode_escape(++p); + p += 5; + + if(0xDC00 <= value2 && value2 <= 0xDFFF) { + /* valid second surrogate */ + value = + ((value - 0xD800) << 10) + + (value2 - 0xDC00) + + 0x10000; + } + else { + /* invalid second surrogate */ + error_set(error, lex, + "invalid Unicode '\\u%04X\\u%04X'", + value, value2); + goto out; + } + } + else { + /* no second surrogate */ + error_set(error, lex, "invalid Unicode '\\u%04X'", + value); + goto out; + } + } + else if(0xDC00 <= value && value <= 0xDFFF) { + error_set(error, lex, "invalid Unicode '\\u%04X'", value); + goto out; + } + else if(value == 0) + { + error_set(error, lex, "\\u0000 is not allowed"); + goto out; + } + + if(utf8_encode(value, buffer, &length)) + assert(0); + + memcpy(t, buffer, length); + t += length; + } + else { + switch(*p) { + case '"': case '\\': case '/': + *t = *p; break; + case 'b': *t = '\b'; break; + case 'f': *t = '\f'; break; + case 'n': *t = '\n'; break; + case 'r': *t = '\r'; break; + case 't': *t = '\t'; break; + default: assert(0); + } + t++; + p++; + } + } + else + *(t++) = *(p++); + } + *t = '\0'; + lex->token = TOKEN_STRING; + return; + +out: + jsonp_free(lex->value.string); +} + +#if JSON_INTEGER_IS_LONG_LONG +#ifdef _MSC_VER // Microsoft Visual Studio +#define json_strtoint _strtoi64 +#else +#define json_strtoint strtoll +#endif +#else +#define json_strtoint strtol +#endif + +static int lex_scan_number(lex_t *lex, int c, json_error_t *error) +{ + const char *saved_text; + char *end; + double value; + + lex->token = TOKEN_INVALID; + + if(c == '-') + c = lex_get_save(lex, error); + + if(c == '0') { + c = lex_get_save(lex, error); + if(l_isdigit(c)) { + lex_unget_unsave(lex, c); + goto out; + } + } + else if(l_isdigit(c)) { + c = lex_get_save(lex, error); + while(l_isdigit(c)) + c = lex_get_save(lex, error); + } + else { + lex_unget_unsave(lex, c); + goto out; + } + + if(c != '.' && c != 'E' && c != 'e') { + json_int_t value; + + lex_unget_unsave(lex, c); + + saved_text = strbuffer_value(&lex->saved_text); + + errno = 0; + value = json_strtoint(saved_text, &end, 10); + if(errno == ERANGE) { + if(value < 0) + error_set(error, lex, "too big negative integer"); + else + error_set(error, lex, "too big integer"); + goto out; + } + + assert(end == saved_text + lex->saved_text.length); + + lex->token = TOKEN_INTEGER; + lex->value.integer = value; + return 0; + } + + if(c == '.') { + c = lex_get(lex, error); + if(!l_isdigit(c)) { + lex_unget(lex, c); + goto out; + } + lex_save(lex, c); + + c = lex_get_save(lex, error); + while(l_isdigit(c)) + c = lex_get_save(lex, error); + } + + if(c == 'E' || c == 'e') { + c = lex_get_save(lex, error); + if(c == '+' || c == '-') + c = lex_get_save(lex, error); + + if(!l_isdigit(c)) { + lex_unget_unsave(lex, c); + goto out; + } + + c = lex_get_save(lex, error); + while(l_isdigit(c)) + c = lex_get_save(lex, error); + } + + lex_unget_unsave(lex, c); + + if(jsonp_strtod(&lex->saved_text, &value)) { + error_set(error, lex, "real number overflow"); + goto out; + } + + lex->token = TOKEN_REAL; + lex->value.real = value; + return 0; + +out: + return -1; +} + +static int lex_scan(lex_t *lex, json_error_t *error) +{ + int c; + + strbuffer_clear(&lex->saved_text); + + if(lex->token == TOKEN_STRING) { + jsonp_free(lex->value.string); + lex->value.string = NULL; + } + + c = lex_get(lex, error); + while(c == ' ' || c == '\t' || c == '\n' || c == '\r') + c = lex_get(lex, error); + + if(c == STREAM_STATE_EOF) { + lex->token = TOKEN_EOF; + goto out; + } + + if(c == STREAM_STATE_ERROR) { + lex->token = TOKEN_INVALID; + goto out; + } + + lex_save(lex, c); + + if(c == '{' || c == '}' || c == '[' || c == ']' || c == ':' || c == ',') + lex->token = c; + + else if(c == '"') + lex_scan_string(lex, error); + + else if(l_isdigit(c) || c == '-') { + if(lex_scan_number(lex, c, error)) + goto out; + } + + else if(l_isalpha(c)) { + /* eat up the whole identifier for clearer error messages */ + const char *saved_text; + + c = lex_get_save(lex, error); + while(l_isalpha(c)) + c = lex_get_save(lex, error); + lex_unget_unsave(lex, c); + + saved_text = strbuffer_value(&lex->saved_text); + + if(strcmp(saved_text, "true") == 0) + lex->token = TOKEN_TRUE; + else if(strcmp(saved_text, "false") == 0) + lex->token = TOKEN_FALSE; + else if(strcmp(saved_text, "null") == 0) + lex->token = TOKEN_NULL; + else + lex->token = TOKEN_INVALID; + } + + else { + /* save the rest of the input UTF-8 sequence to get an error + message of valid UTF-8 */ + lex_save_cached(lex); + lex->token = TOKEN_INVALID; + } + +out: + return lex->token; +} + +static char *lex_steal_string(lex_t *lex) +{ + char *result = NULL; + if(lex->token == TOKEN_STRING) + { + result = lex->value.string; + lex->value.string = NULL; + } + return result; +} + +static int lex_init(lex_t *lex, get_func get, void *data) +{ + stream_init(&lex->stream, get, data); + if(strbuffer_init(&lex->saved_text)) + return -1; + + lex->token = TOKEN_INVALID; + return 0; +} + +static void lex_close(lex_t *lex) +{ + if(lex->token == TOKEN_STRING) + jsonp_free(lex->value.string); + strbuffer_close(&lex->saved_text); +} + + +/*** parser ***/ + +static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error); + +static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error) +{ + json_t *object = json_object(); + if(!object) + return NULL; + + lex_scan(lex, error); + if(lex->token == '}') + return object; + + while(1) { + char *key; + json_t *value; + + if(lex->token != TOKEN_STRING) { + error_set(error, lex, "string or '}' expected"); + goto error; + } + + key = lex_steal_string(lex); + if(!key) + return NULL; + + if(flags & JSON_REJECT_DUPLICATES) { + if(json_object_get(object, key)) { + jsonp_free(key); + error_set(error, lex, "duplicate object key"); + goto error; + } + } + + lex_scan(lex, error); + if(lex->token != ':') { + jsonp_free(key); + error_set(error, lex, "':' expected"); + goto error; + } + + lex_scan(lex, error); + value = parse_value(lex, flags, error); + if(!value) { + jsonp_free(key); + goto error; + } + + if(json_object_set_nocheck(object, key, value)) { + jsonp_free(key); + json_decref(value); + goto error; + } + + json_decref(value); + jsonp_free(key); + + lex_scan(lex, error); + if(lex->token != ',') + break; + + lex_scan(lex, error); + } + + if(lex->token != '}') { + error_set(error, lex, "'}' expected"); + goto error; + } + + return object; + +error: + json_decref(object); + return NULL; +} + +static json_t *parse_array(lex_t *lex, size_t flags, json_error_t *error) +{ + json_t *array = json_array(); + if(!array) + return NULL; + + lex_scan(lex, error); + if(lex->token == ']') + return array; + + while(lex->token) { + json_t *elem = parse_value(lex, flags, error); + if(!elem) + goto error; + + if(json_array_append(array, elem)) { + json_decref(elem); + goto error; + } + json_decref(elem); + + lex_scan(lex, error); + if(lex->token != ',') + break; + + lex_scan(lex, error); + } + + if(lex->token != ']') { + error_set(error, lex, "']' expected"); + goto error; + } + + return array; + +error: + json_decref(array); + return NULL; +} + +static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error) +{ + json_t *json; + + switch(lex->token) { + case TOKEN_STRING: { + json = json_string_nocheck(lex->value.string); + break; + } + + case TOKEN_INTEGER: { + json = json_integer(lex->value.integer); + break; + } + + case TOKEN_REAL: { + json = json_real(lex->value.real); + break; + } + + case TOKEN_TRUE: + json = json_true(); + break; + + case TOKEN_FALSE: + json = json_false(); + break; + + case TOKEN_NULL: + json = json_null(); + break; + + case '{': + json = parse_object(lex, flags, error); + break; + + case '[': + json = parse_array(lex, flags, error); + break; + + case TOKEN_INVALID: + error_set(error, lex, "invalid token"); + return NULL; + + default: + error_set(error, lex, "unexpected token"); + return NULL; + } + + if(!json) + return NULL; + + return json; +} + +static json_t *parse_json(lex_t *lex, size_t flags, json_error_t *error) +{ + json_t *result; + + lex_scan(lex, error); + if(!(flags & JSON_DECODE_ANY)) { + if(lex->token != '[' && lex->token != '{') { + error_set(error, lex, "'[' or '{' expected"); + return NULL; + } + } + + result = parse_value(lex, flags, error); + if(!result) + return NULL; + + if(!(flags & JSON_DISABLE_EOF_CHECK)) { + lex_scan(lex, error); + if(lex->token != TOKEN_EOF) { + error_set(error, lex, "end of file expected"); + json_decref(result); + return NULL; + } + } + + if(error) { + /* Save the position even though there was no error */ + error->position = lex->stream.position; + } + + return result; +} + +typedef struct +{ + const char *data; + int pos; +} string_data_t; + +static int string_get(void *data) +{ + char c; + string_data_t *stream = (string_data_t *)data; + c = stream->data[stream->pos]; + if(c == '\0') + return EOF; + else + { + stream->pos++; + return (unsigned char)c; + } +} + +json_t *json_loads(const char *string, size_t flags, json_error_t *error) +{ + lex_t lex; + json_t *result; + string_data_t stream_data; + + jsonp_error_init(error, "<string>"); + + if (string == NULL) { + error_set(error, NULL, "wrong arguments"); + return NULL; + } + + stream_data.data = string; + stream_data.pos = 0; + + if(lex_init(&lex, string_get, (void *)&stream_data)) + return NULL; + + result = parse_json(&lex, flags, error); + + lex_close(&lex); + return result; +} + +typedef struct +{ + const char *data; + size_t len; + size_t pos; +} buffer_data_t; + +static int buffer_get(void *data) +{ + char c; + buffer_data_t *stream = data; + if(stream->pos >= stream->len) + return EOF; + + c = stream->data[stream->pos]; + stream->pos++; + return (unsigned char)c; +} + +json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error) +{ + lex_t lex; + json_t *result; + buffer_data_t stream_data; + + jsonp_error_init(error, "<buffer>"); + + if (buffer == NULL) { + error_set(error, NULL, "wrong arguments"); + return NULL; + } + + stream_data.data = buffer; + stream_data.pos = 0; + stream_data.len = buflen; + + if(lex_init(&lex, buffer_get, (void *)&stream_data)) + return NULL; + + result = parse_json(&lex, flags, error); + + lex_close(&lex); + return result; +} + +json_t *json_loadf(FILE *input, size_t flags, json_error_t *error) +{ + lex_t lex; + const char *source; + json_t *result; + + if(input == stdin) + source = "<stdin>"; + else + source = "<stream>"; + + jsonp_error_init(error, source); + + if (input == NULL) { + error_set(error, NULL, "wrong arguments"); + return NULL; + } + + if(lex_init(&lex, (get_func)fgetc, input)) + return NULL; + + result = parse_json(&lex, flags, error); + + lex_close(&lex); + return result; +} + +json_t *json_load_file(const char *path, size_t flags, json_error_t *error) +{ + json_t *result; + FILE *fp; + + jsonp_error_init(error, path); + + if (path == NULL) { + error_set(error, NULL, "wrong arguments"); + return NULL; + } + + fp = fopen(path, "rb"); + if(!fp) + { + error_set(error, NULL, "unable to open %s: %s", + path, strerror(errno)); + return NULL; + } + + result = json_loadf(fp, flags, error); + + fclose(fp); + return result; +} + +#define MAX_BUF_LEN 1024 + +typedef struct +{ + char data[MAX_BUF_LEN]; + size_t len; + size_t pos; + json_load_callback_t callback; + void *arg; +} callback_data_t; + +static int callback_get(void *data) +{ + char c; + callback_data_t *stream = data; + + if(stream->pos >= stream->len) { + stream->pos = 0; + stream->len = stream->callback(stream->data, MAX_BUF_LEN, stream->arg); + if(stream->len == 0 || stream->len == (size_t)-1) + return EOF; + } + + c = stream->data[stream->pos]; + stream->pos++; + return (unsigned char)c; +} + +json_t *json_load_callback(json_load_callback_t callback, void *arg, size_t flags, json_error_t *error) +{ + lex_t lex; + json_t *result; + + callback_data_t stream_data; + + memset(&stream_data, 0, sizeof(stream_data)); + stream_data.callback = callback; + stream_data.arg = arg; + + jsonp_error_init(error, "<callback>"); + + if (callback == NULL) { + error_set(error, NULL, "wrong arguments"); + return NULL; + } + + if(lex_init(&lex, (get_func)callback_get, &stream_data)) + return NULL; + + result = parse_json(&lex, flags, error); + + lex_close(&lex); + return result; +} diff --git a/src/jansson/memory.c b/src/jansson/memory.c new file mode 100644 index 0000000..543ecc4 --- /dev/null +++ b/src/jansson/memory.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org> + * Copyright (c) 2011-2012 Basile Starynkevitch <basile@starynkevitch.net> + * + * Jansson is free software; you can redistribute it and/or modify it + * under the terms of the MIT license. See LICENSE for details. + */ + +#include <stdlib.h> +#include <string.h> + +#include "jansson.h" +#include "jansson_private.h" + +/* memory function pointers */ +static json_malloc_t do_malloc = malloc; +static json_free_t do_free = free; + +void *jsonp_malloc(size_t size) +{ + if(!size) + return NULL; + + return (*do_malloc)(size); +} + +void jsonp_free(void *ptr) +{ + if(!ptr) + return; + + (*do_free)(ptr); +} + +char *jsonp_strdup(const char *str) +{ + char *new_str; + + new_str = jsonp_malloc(strlen(str) + 1); + if(!new_str) + return NULL; + + strcpy(new_str, str); + return new_str; +} + +void json_set_alloc_funcs(json_malloc_t malloc_fn, json_free_t free_fn) +{ + do_malloc = malloc_fn; + do_free = free_fn; +} diff --git a/src/jansson/pack_unpack.c b/src/jansson/pack_unpack.c new file mode 100644 index 0000000..39db9b8 --- /dev/null +++ b/src/jansson/pack_unpack.c @@ -0,0 +1,647 @@ +/* + * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org> + * Copyright (c) 2011-2012 Graeme Smecher <graeme.smecher@mail.mcgill.ca> + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include <string.h> +#include "jansson.h" +#include "jansson_private.h" +#include "utf.h" + +typedef struct { + const char *start; + const char *fmt; + char token; + json_error_t *error; + size_t flags; + int line; + int column; +} scanner_t; + +static const char *type_names[] = { + "object", + "array", + "string", + "integer", + "real", + "true", + "false", + "null" +}; + +#define type_name(x) type_names[json_typeof(x)] + +static const char *unpack_value_starters = "{[siIbfFOon"; + + +static void scanner_init(scanner_t *s, json_error_t *error, + size_t flags, const char *fmt) +{ + s->error = error; + s->flags = flags; + s->fmt = s->start = fmt; + s->line = 1; + s->column = 0; +} + +static void next_token(scanner_t *s) +{ + const char *t = s->fmt; + s->column++; + + /* skip space and ignored chars */ + while(*t == ' ' || *t == '\t' || *t == '\n' || *t == ',' || *t == ':') { + if(*t == '\n') { + s->line++; + s->column = 1; + } + else + s->column++; + + t++; + } + + s->token = *t; + + t++; + s->fmt = t; +} + +static void set_error(scanner_t *s, const char *source, const char *fmt, ...) +{ + va_list ap; + size_t pos; + va_start(ap, fmt); + + pos = (size_t)(s->fmt - s->start); + jsonp_error_vset(s->error, s->line, s->column, pos, fmt, ap); + + jsonp_error_set_source(s->error, source); + + va_end(ap); +} + +static json_t *pack(scanner_t *s, va_list *ap); + +static json_t *pack_object(scanner_t *s, va_list *ap) +{ + json_t *object = json_object(); + next_token(s); + + while(s->token != '}') { + const char *key; + json_t *value; + + if(!s->token) { + set_error(s, "<format>", "Unexpected end of format string"); + goto error; + } + + if(s->token != 's') { + set_error(s, "<format>", "Expected format 's', got '%c'", s->token); + goto error; + } + + key = va_arg(*ap, const char *); + if(!key) { + set_error(s, "<args>", "NULL object key"); + goto error; + } + + if(!utf8_check_string(key, -1)) { + set_error(s, "<args>", "Invalid UTF-8 in object key"); + goto error; + } + + next_token(s); + + value = pack(s, ap); + if(!value) + goto error; + + if(json_object_set_new_nocheck(object, key, value)) { + set_error(s, "<internal>", "Unable to add key \"%s\"", key); + goto error; + } + + next_token(s); + } + + return object; + +error: + json_decref(object); + return NULL; +} + +static json_t *pack_array(scanner_t *s, va_list *ap) +{ + json_t *array = json_array(); + next_token(s); + + while(s->token != ']') { + json_t *value; + + if(!s->token) { + set_error(s, "<format>", "Unexpected end of format string"); + goto error; + } + + value = pack(s, ap); + if(!value) + goto error; + + if(json_array_append_new(array, value)) { + set_error(s, "<internal>", "Unable to append to array"); + goto error; + } + + next_token(s); + } + return array; + +error: + json_decref(array); + return NULL; +} + +static json_t *pack(scanner_t *s, va_list *ap) +{ + switch(s->token) { + case '{': + return pack_object(s, ap); + + case '[': + return pack_array(s, ap); + + case 's': /* string */ + { + const char *str = va_arg(*ap, const char *); + if(!str) { + set_error(s, "<args>", "NULL string argument"); + return NULL; + } + if(!utf8_check_string(str, -1)) { + set_error(s, "<args>", "Invalid UTF-8 string"); + return NULL; + } + return json_string_nocheck(str); + } + + case 'n': /* null */ + return json_null(); + + case 'b': /* boolean */ + return va_arg(*ap, int) ? json_true() : json_false(); + + case 'i': /* integer from int */ + return json_integer(va_arg(*ap, int)); + + case 'I': /* integer from json_int_t */ + return json_integer(va_arg(*ap, json_int_t)); + + case 'f': /* real */ + return json_real(va_arg(*ap, double)); + + case 'O': /* a json_t object; increments refcount */ + return json_incref(va_arg(*ap, json_t *)); + + case 'o': /* a json_t object; doesn't increment refcount */ + return va_arg(*ap, json_t *); + + default: + set_error(s, "<format>", "Unexpected format character '%c'", + s->token); + return NULL; + } +} + +static int unpack(scanner_t *s, json_t *root, va_list *ap); + +static int unpack_object(scanner_t *s, json_t *root, va_list *ap) +{ + int ret = -1; + int strict = 0; + + /* Use a set (emulated by a hashtable) to check that all object + keys are accessed. Checking that the correct number of keys + were accessed is not enough, as the same key can be unpacked + multiple times. + */ + hashtable_t key_set; + + if(hashtable_init(&key_set)) { + set_error(s, "<internal>", "Out of memory"); + return -1; + } + + if(root && !json_is_object(root)) { + set_error(s, "<validation>", "Expected object, got %s", + type_name(root)); + goto out; + } + next_token(s); + + while(s->token != '}') { + const char *key; + json_t *value; + int opt = 0; + + if(strict != 0) { + set_error(s, "<format>", "Expected '}' after '%c', got '%c'", + (strict == 1 ? '!' : '*'), s->token); + goto out; + } + + if(!s->token) { + set_error(s, "<format>", "Unexpected end of format string"); + goto out; + } + + if(s->token == '!' || s->token == '*') { + strict = (s->token == '!' ? 1 : -1); + next_token(s); + continue; + } + + if(s->token != 's') { + set_error(s, "<format>", "Expected format 's', got '%c'", s->token); + goto out; + } + + key = va_arg(*ap, const char *); + if(!key) { + set_error(s, "<args>", "NULL object key"); + goto out; + } + + next_token(s); + + if(s->token == '?') { + opt = 1; + next_token(s); + } + + if(!root) { + /* skipping */ + value = NULL; + } + else { + value = json_object_get(root, key); + if(!value && !opt) { + set_error(s, "<validation>", "Object item not found: %s", key); + goto out; + } + } + + if(unpack(s, value, ap)) + goto out; + + hashtable_set(&key_set, key, 0, json_null()); + next_token(s); + } + + if(strict == 0 && (s->flags & JSON_STRICT)) + strict = 1; + + if(root && strict == 1 && key_set.size != json_object_size(root)) { + long diff = (long)json_object_size(root) - (long)key_set.size; + set_error(s, "<validation>", "%li object item(s) left unpacked", diff); + goto out; + } + + ret = 0; + +out: + hashtable_close(&key_set); + return ret; +} + +static int unpack_array(scanner_t *s, json_t *root, va_list *ap) +{ + size_t i = 0; + int strict = 0; + + if(root && !json_is_array(root)) { + set_error(s, "<validation>", "Expected array, got %s", type_name(root)); + return -1; + } + next_token(s); + + while(s->token != ']') { + json_t *value; + + if(strict != 0) { + set_error(s, "<format>", "Expected ']' after '%c', got '%c'", + (strict == 1 ? '!' : '*'), + s->token); + return -1; + } + + if(!s->token) { + set_error(s, "<format>", "Unexpected end of format string"); + return -1; + } + + if(s->token == '!' || s->token == '*') { + strict = (s->token == '!' ? 1 : -1); + next_token(s); + continue; + } + + if(!strchr(unpack_value_starters, s->token)) { + set_error(s, "<format>", "Unexpected format character '%c'", + s->token); + return -1; + } + + if(!root) { + /* skipping */ + value = NULL; + } + else { + value = json_array_get(root, i); + if(!value) { + set_error(s, "<validation>", "Array index %lu out of range", + (unsigned long)i); + return -1; + } + } + + if(unpack(s, value, ap)) + return -1; + + next_token(s); + i++; + } + + if(strict == 0 && (s->flags & JSON_STRICT)) + strict = 1; + + if(root && strict == 1 && i != json_array_size(root)) { + long diff = (long)json_array_size(root) - (long)i; + set_error(s, "<validation>", "%li array item(s) left unpacked", diff); + return -1; + } + + return 0; +} + +static int unpack(scanner_t *s, json_t *root, va_list *ap) +{ + switch(s->token) + { + case '{': + return unpack_object(s, root, ap); + + case '[': + return unpack_array(s, root, ap); + + case 's': + if(root && !json_is_string(root)) { + set_error(s, "<validation>", "Expected string, got %s", + type_name(root)); + return -1; + } + + if(!(s->flags & JSON_VALIDATE_ONLY)) { + const char **target; + + target = va_arg(*ap, const char **); + if(!target) { + set_error(s, "<args>", "NULL string argument"); + return -1; + } + + if(root) + *target = json_string_value(root); + } + return 0; + + case 'i': + if(root && !json_is_integer(root)) { + set_error(s, "<validation>", "Expected integer, got %s", + type_name(root)); + return -1; + } + + if(!(s->flags & JSON_VALIDATE_ONLY)) { + int *target = va_arg(*ap, int*); + if(root) + *target = (int)json_integer_value(root); + } + + return 0; + + case 'I': + if(root && !json_is_integer(root)) { + set_error(s, "<validation>", "Expected integer, got %s", + type_name(root)); + return -1; + } + + if(!(s->flags & JSON_VALIDATE_ONLY)) { + json_int_t *target = va_arg(*ap, json_int_t*); + if(root) + *target = json_integer_value(root); + } + + return 0; + + case 'b': + if(root && !json_is_boolean(root)) { + set_error(s, "<validation>", "Expected true or false, got %s", + type_name(root)); + return -1; + } + + if(!(s->flags & JSON_VALIDATE_ONLY)) { + int *target = va_arg(*ap, int*); + if(root) + *target = json_is_true(root); + } + + return 0; + + case 'f': + if(root && !json_is_real(root)) { + set_error(s, "<validation>", "Expected real, got %s", + type_name(root)); + return -1; + } + + if(!(s->flags & JSON_VALIDATE_ONLY)) { + double *target = va_arg(*ap, double*); + if(root) + *target = json_real_value(root); + } + + return 0; + + case 'F': + if(root && !json_is_number(root)) { + set_error(s, "<validation>", "Expected real or integer, got %s", + type_name(root)); + return -1; + } + + if(!(s->flags & JSON_VALIDATE_ONLY)) { + double *target = va_arg(*ap, double*); + if(root) + *target = json_number_value(root); + } + + return 0; + + case 'O': + if(root && !(s->flags & JSON_VALIDATE_ONLY)) + json_incref(root); + /* Fall through */ + + case 'o': + if(!(s->flags & JSON_VALIDATE_ONLY)) { + json_t **target = va_arg(*ap, json_t**); + if(root) + *target = root; + } + + return 0; + + case 'n': + /* Never assign, just validate */ + if(root && !json_is_null(root)) { + set_error(s, "<validation>", "Expected null, got %s", + type_name(root)); + return -1; + } + return 0; + + default: + set_error(s, "<format>", "Unexpected format character '%c'", + s->token); + return -1; + } +} + +json_t *json_vpack_ex(json_error_t *error, size_t flags, + const char *fmt, va_list ap) +{ + scanner_t s; + va_list ap_copy; + json_t *value; + + if(!fmt || !*fmt) { + jsonp_error_init(error, "<format>"); + jsonp_error_set(error, -1, -1, 0, "NULL or empty format string"); + return NULL; + } + jsonp_error_init(error, NULL); + + scanner_init(&s, error, flags, fmt); + next_token(&s); + + va_copy(ap_copy, ap); + value = pack(&s, &ap_copy); + va_end(ap_copy); + + if(!value) + return NULL; + + next_token(&s); + if(s.token) { + json_decref(value); + set_error(&s, "<format>", "Garbage after format string"); + return NULL; + } + + return value; +} + +json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...) +{ + json_t *value; + va_list ap; + + va_start(ap, fmt); + value = json_vpack_ex(error, flags, fmt, ap); + va_end(ap); + + return value; +} + +json_t *json_pack(const char *fmt, ...) +{ + json_t *value; + va_list ap; + + va_start(ap, fmt); + value = json_vpack_ex(NULL, 0, fmt, ap); + va_end(ap); + + return value; +} + +int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags, + const char *fmt, va_list ap) +{ + scanner_t s; + va_list ap_copy; + + if(!root) { + jsonp_error_init(error, "<root>"); + jsonp_error_set(error, -1, -1, 0, "NULL root value"); + return -1; + } + + if(!fmt || !*fmt) { + jsonp_error_init(error, "<format>"); + jsonp_error_set(error, -1, -1, 0, "NULL or empty format string"); + return -1; + } + jsonp_error_init(error, NULL); + + scanner_init(&s, error, flags, fmt); + next_token(&s); + + va_copy(ap_copy, ap); + if(unpack(&s, root, &ap_copy)) { + va_end(ap_copy); + return -1; + } + va_end(ap_copy); + + next_token(&s); + if(s.token) { + set_error(&s, "<format>", "Garbage after format string"); + return -1; + } + + return 0; +} + +int json_unpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, ...) +{ + int ret; + va_list ap; + + va_start(ap, fmt); + ret = json_vunpack_ex(root, error, flags, fmt, ap); + va_end(ap); + + return ret; +} + +int json_unpack(json_t *root, const char *fmt, ...) +{ + int ret; + va_list ap; + + va_start(ap, fmt); + ret = json_vunpack_ex(root, NULL, 0, fmt, ap); + va_end(ap); + + return ret; +} diff --git a/src/jansson/strbuffer.c b/src/jansson/strbuffer.c new file mode 100644 index 0000000..6d4edd6 --- /dev/null +++ b/src/jansson/strbuffer.c @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org> + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#define _GNU_SOURCE +#include <stdlib.h> +#include <string.h> +#include "jansson_private.h" +#include "strbuffer.h" + +#define STRBUFFER_MIN_SIZE 16 +#define STRBUFFER_FACTOR 2 +#define STRBUFFER_SIZE_MAX ((size_t)-1) + +int strbuffer_init(strbuffer_t *strbuff) +{ + strbuff->size = STRBUFFER_MIN_SIZE; + strbuff->length = 0; + + strbuff->value = jsonp_malloc(strbuff->size); + if(!strbuff->value) + return -1; + + /* initialize to empty */ + strbuff->value[0] = '\0'; + return 0; +} + +void strbuffer_close(strbuffer_t *strbuff) +{ + jsonp_free(strbuff->value); + strbuff->size = 0; + strbuff->length = 0; + strbuff->value = NULL; +} + +void strbuffer_clear(strbuffer_t *strbuff) +{ + strbuff->length = 0; + strbuff->value[0] = '\0'; +} + +const char *strbuffer_value(const strbuffer_t *strbuff) +{ + return strbuff->value; +} + +char *strbuffer_steal_value(strbuffer_t *strbuff) +{ + char *result = strbuff->value; + strbuffer_init(strbuff); + return result; +} + +int strbuffer_append(strbuffer_t *strbuff, const char *string) +{ + return strbuffer_append_bytes(strbuff, string, strlen(string)); +} + +int strbuffer_append_byte(strbuffer_t *strbuff, char byte) +{ + return strbuffer_append_bytes(strbuff, &byte, 1); +} + +int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size) +{ + if(size >= strbuff->size - strbuff->length) + { + size_t new_size; + char *new_value; + + /* avoid integer overflow */ + if (strbuff->size > STRBUFFER_SIZE_MAX / STRBUFFER_FACTOR + || size > STRBUFFER_SIZE_MAX - 1 + || strbuff->length > STRBUFFER_SIZE_MAX - 1 - size) + return -1; + + new_size = max(strbuff->size * STRBUFFER_FACTOR, + strbuff->length + size + 1); + + new_value = jsonp_malloc(new_size); + if(!new_value) + return -1; + + memcpy(new_value, strbuff->value, strbuff->length); + + jsonp_free(strbuff->value); + strbuff->value = new_value; + strbuff->size = new_size; + } + + memcpy(strbuff->value + strbuff->length, data, size); + strbuff->length += size; + strbuff->value[strbuff->length] = '\0'; + + return 0; +} + +char strbuffer_pop(strbuffer_t *strbuff) +{ + if(strbuff->length > 0) { + char c = strbuff->value[--strbuff->length]; + strbuff->value[strbuff->length] = '\0'; + return c; + } + else + return '\0'; +} diff --git a/src/jansson/strbuffer.h b/src/jansson/strbuffer.h new file mode 100644 index 0000000..23f8fff --- /dev/null +++ b/src/jansson/strbuffer.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org> + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef STRBUFFER_H +#define STRBUFFER_H + +typedef struct { + char *value; + size_t length; /* bytes used */ + size_t size; /* bytes allocated */ +} strbuffer_t; + +int strbuffer_init(strbuffer_t *strbuff); +void strbuffer_close(strbuffer_t *strbuff); + +void strbuffer_clear(strbuffer_t *strbuff); + +const char *strbuffer_value(const strbuffer_t *strbuff); +char *strbuffer_steal_value(strbuffer_t *strbuff); + +int strbuffer_append(strbuffer_t *strbuff, const char *string); +int strbuffer_append_byte(strbuffer_t *strbuff, char byte); +int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size); + +char strbuffer_pop(strbuffer_t *strbuff); + +#endif diff --git a/src/jansson/strconv.c b/src/jansson/strconv.c new file mode 100644 index 0000000..caa9ab8 --- /dev/null +++ b/src/jansson/strconv.c @@ -0,0 +1,129 @@ +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include "jansson_private.h" +#include "strbuffer.h" + +#if JSON_HAVE_LOCALECONV +#include <locale.h> + +/* + - This code assumes that the decimal separator is exactly one + character. + + - If setlocale() is called by another thread between the call to + localeconv() and the call to sprintf() or strtod(), the result may + be wrong. setlocale() is not thread-safe and should not be used + this way. Multi-threaded programs should use uselocale() instead. +*/ + +static void to_locale(strbuffer_t *strbuffer) +{ + const char *point; + char *pos; + + point = localeconv()->decimal_point; + if(*point == '.') { + /* No conversion needed */ + return; + } + + pos = strchr(strbuffer->value, '.'); + if(pos) + *pos = *point; +} + +static void from_locale(char *buffer) +{ + const char *point; + char *pos; + + point = localeconv()->decimal_point; + if(*point == '.') { + /* No conversion needed */ + return; + } + + pos = strchr(buffer, *point); + if(pos) + *pos = '.'; +} +#endif + +int jsonp_strtod(strbuffer_t *strbuffer, double *out) +{ + double value; + char *end; + +#if JSON_HAVE_LOCALECONV + to_locale(strbuffer); +#endif + + errno = 0; + value = strtod(strbuffer->value, &end); + assert(end == strbuffer->value + strbuffer->length); + + if(errno == ERANGE && value != 0) { + /* Overflow */ + return -1; + } + + *out = value; + return 0; +} + +int jsonp_dtostr(char *buffer, size_t size, double value) +{ + int ret; + char *start, *end; + size_t length; + + ret = snprintf(buffer, size, "%.17g", value); + if(ret < 0) + return -1; + + length = (size_t)ret; + if(length >= size) + return -1; + +#if JSON_HAVE_LOCALECONV + from_locale(buffer); +#endif + + /* Make sure there's a dot or 'e' in the output. Otherwise + a real is converted to an integer when decoding */ + if(strchr(buffer, '.') == NULL && + strchr(buffer, 'e') == NULL) + { + if(length + 3 >= size) { + /* No space to append ".0" */ + return -1; + } + buffer[length] = '.'; + buffer[length + 1] = '0'; + buffer[length + 2] = '\0'; + length += 2; + } + + /* Remove leading '+' from positive exponent. Also remove leading + zeros from exponents (added by some printf() implementations) */ + start = strchr(buffer, 'e'); + if(start) { + start++; + end = start + 1; + + if(*start == '-') + start++; + + while(*end == '0') + end++; + + if(end != start) { + memmove(start, end, length - (size_t)(end - buffer)); + length -= (size_t)(end - start); + } + } + + return (int)length; +} diff --git a/src/jansson/utf.c b/src/jansson/utf.c new file mode 100644 index 0000000..0359ee2 --- /dev/null +++ b/src/jansson/utf.c @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org> + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include <string.h> +#include "utf.h" + +int utf8_encode(int32_t codepoint, char *buffer, int *size) +{ + if(codepoint < 0) + return -1; + else if(codepoint < 0x80) + { + buffer[0] = (char)codepoint; + *size = 1; + } + else if(codepoint < 0x800) + { + buffer[0] = 0xC0 + ((codepoint & 0x7C0) >> 6); + buffer[1] = 0x80 + ((codepoint & 0x03F)); + *size = 2; + } + else if(codepoint < 0x10000) + { + buffer[0] = 0xE0 + ((codepoint & 0xF000) >> 12); + buffer[1] = 0x80 + ((codepoint & 0x0FC0) >> 6); + buffer[2] = 0x80 + ((codepoint & 0x003F)); + *size = 3; + } + else if(codepoint <= 0x10FFFF) + { + buffer[0] = 0xF0 + ((codepoint & 0x1C0000) >> 18); + buffer[1] = 0x80 + ((codepoint & 0x03F000) >> 12); + buffer[2] = 0x80 + ((codepoint & 0x000FC0) >> 6); + buffer[3] = 0x80 + ((codepoint & 0x00003F)); + *size = 4; + } + else + return -1; + + return 0; +} + +int utf8_check_first(char byte) +{ + unsigned char u = (unsigned char)byte; + + if(u < 0x80) + return 1; + + if(0x80 <= u && u <= 0xBF) { + /* second, third or fourth byte of a multi-byte + sequence, i.e. a "continuation byte" */ + return 0; + } + else if(u == 0xC0 || u == 0xC1) { + /* overlong encoding of an ASCII byte */ + return 0; + } + else if(0xC2 <= u && u <= 0xDF) { + /* 2-byte sequence */ + return 2; + } + + else if(0xE0 <= u && u <= 0xEF) { + /* 3-byte sequence */ + return 3; + } + else if(0xF0 <= u && u <= 0xF4) { + /* 4-byte sequence */ + return 4; + } + else { /* u >= 0xF5 */ + /* Restricted (start of 4-, 5- or 6-byte sequence) or invalid + UTF-8 */ + return 0; + } +} + +int utf8_check_full(const char *buffer, int size, int32_t *codepoint) +{ + int i; + int32_t value = 0; + unsigned char u = (unsigned char)buffer[0]; + + if(size == 2) + { + value = u & 0x1F; + } + else if(size == 3) + { + value = u & 0xF; + } + else if(size == 4) + { + value = u & 0x7; + } + else + return 0; + + for(i = 1; i < size; i++) + { + u = (unsigned char)buffer[i]; + + if(u < 0x80 || u > 0xBF) { + /* not a continuation byte */ + return 0; + } + + value = (value << 6) + (u & 0x3F); + } + + if(value > 0x10FFFF) { + /* not in Unicode range */ + return 0; + } + + else if(0xD800 <= value && value <= 0xDFFF) { + /* invalid code point (UTF-16 surrogate halves) */ + return 0; + } + + else if((size == 2 && value < 0x80) || + (size == 3 && value < 0x800) || + (size == 4 && value < 0x10000)) { + /* overlong encoding */ + return 0; + } + + if(codepoint) + *codepoint = value; + + return 1; +} + +const char *utf8_iterate(const char *buffer, int32_t *codepoint) +{ + int count; + int32_t value; + + if(!*buffer) + return buffer; + + count = utf8_check_first(buffer[0]); + if(count <= 0) + return NULL; + + if(count == 1) + value = (unsigned char)buffer[0]; + else + { + if(!utf8_check_full(buffer, count, &value)) + return NULL; + } + + if(codepoint) + *codepoint = value; + + return buffer + count; +} + +int utf8_check_string(const char *string, int length) +{ + int i; + + if(length == -1) + length = strlen(string); + + for(i = 0; i < length; i++) + { + int count = utf8_check_first(string[i]); + if(count == 0) + return 0; + else if(count > 1) + { + if(i + count > length) + return 0; + + if(!utf8_check_full(&string[i], count, NULL)) + return 0; + + i += count - 1; + } + } + + return 1; +} diff --git a/src/jansson/utf.h b/src/jansson/utf.h new file mode 100644 index 0000000..2495cdd --- /dev/null +++ b/src/jansson/utf.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org> + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef UTF_H +#define UTF_H + +#ifdef HAVE_CONFIG_H +#include <config.h> + +#ifdef HAVE_INTTYPES_H +/* inttypes.h includes stdint.h in a standard environment, so there's +no need to include stdint.h separately. If inttypes.h doesn't define +int32_t, it's defined in config.h. */ +#include <inttypes.h> +#endif /* HAVE_INTTYPES_H */ + +#else /* !HAVE_CONFIG_H */ +#ifdef _WIN32 +typedef int int32_t; +#else /* !_WIN32 */ +/* Assume a standard environment */ +#include <inttypes.h> +#endif /* _WIN32 */ + +#endif /* HAVE_CONFIG_H */ + +int utf8_encode(int codepoint, char *buffer, int *size); + +int utf8_check_first(char byte); +int utf8_check_full(const char *buffer, int size, int32_t *codepoint); +const char *utf8_iterate(const char *buffer, int32_t *codepoint); + +int utf8_check_string(const char *string, int length); + +#endif diff --git a/src/jansson/value.c b/src/jansson/value.c new file mode 100644 index 0000000..1a6d902 --- /dev/null +++ b/src/jansson/value.c @@ -0,0 +1,939 @@ +/* + * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org> + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#define _GNU_SOURCE + +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#include "jansson.h" +#include "hashtable.h" +#include "jansson_private.h" +#include "utf.h" + +/* Work around nonstandard isnan() and isinf() implementations */ +#ifndef isnan +static JSON_INLINE int jansson_isnan(double x) { return x != x; } +# define isnan jansson_isnan +#endif +#ifndef isinf +static JSON_INLINE int jansson_isinf(double x) { return !isnan(x) && isnan(x - x); } +# define isinf jansson_isinf +#endif + +static JSON_INLINE void json_init(json_t *json, json_type type) +{ + json->type = type; + json->refcount = 1; +} + + +/*** object ***/ + +json_t *json_object(void) +{ + json_object_t *object = jsonp_malloc(sizeof(json_object_t)); + if(!object) + return NULL; + json_init(&object->json, JSON_OBJECT); + + if(hashtable_init(&object->hashtable)) + { + jsonp_free(object); + return NULL; + } + + object->serial = 0; + object->visited = 0; + + return &object->json; +} + +static void json_delete_object(json_object_t *object) +{ + hashtable_close(&object->hashtable); + jsonp_free(object); +} + +size_t json_object_size(const json_t *json) +{ + json_object_t *object; + + if(!json_is_object(json)) + return 0; + + object = json_to_object(json); + return object->hashtable.size; +} + +json_t *json_object_get(const json_t *json, const char *key) +{ + json_object_t *object; + + if(!json_is_object(json)) + return NULL; + + object = json_to_object(json); + return hashtable_get(&object->hashtable, key); +} + +int json_object_set_new_nocheck(json_t *json, const char *key, json_t *value) +{ + json_object_t *object; + + if(!value) + return -1; + + if(!key || !json_is_object(json) || json == value) + { + json_decref(value); + return -1; + } + object = json_to_object(json); + + if(hashtable_set(&object->hashtable, key, object->serial++, value)) + { + json_decref(value); + return -1; + } + + return 0; +} + +int json_object_set_new(json_t *json, const char *key, json_t *value) +{ + if(!key || !utf8_check_string(key, -1)) + { + json_decref(value); + return -1; + } + + return json_object_set_new_nocheck(json, key, value); +} + +int json_object_del(json_t *json, const char *key) +{ + json_object_t *object; + + if(!json_is_object(json)) + return -1; + + object = json_to_object(json); + return hashtable_del(&object->hashtable, key); +} + +int json_object_clear(json_t *json) +{ + json_object_t *object; + + if(!json_is_object(json)) + return -1; + + object = json_to_object(json); + + hashtable_clear(&object->hashtable); + object->serial = 0; + + return 0; +} + +int json_object_update(json_t *object, json_t *other) +{ + const char *key; + json_t *value; + + if(!json_is_object(object) || !json_is_object(other)) + return -1; + + json_object_foreach(other, key, value) { + if(json_object_set_nocheck(object, key, value)) + return -1; + } + + return 0; +} + +int json_object_update_existing(json_t *object, json_t *other) +{ + const char *key; + json_t *value; + + if(!json_is_object(object) || !json_is_object(other)) + return -1; + + json_object_foreach(other, key, value) { + if(json_object_get(object, key)) + json_object_set_nocheck(object, key, value); + } + + return 0; +} + +int json_object_update_missing(json_t *object, json_t *other) +{ + const char *key; + json_t *value; + + if(!json_is_object(object) || !json_is_object(other)) + return -1; + + json_object_foreach(other, key, value) { + if(!json_object_get(object, key)) + json_object_set_nocheck(object, key, value); + } + + return 0; +} + +void *json_object_iter(json_t *json) +{ + json_object_t *object; + + if(!json_is_object(json)) + return NULL; + + object = json_to_object(json); + return hashtable_iter(&object->hashtable); +} + +void *json_object_iter_at(json_t *json, const char *key) +{ + json_object_t *object; + + if(!key || !json_is_object(json)) + return NULL; + + object = json_to_object(json); + return hashtable_iter_at(&object->hashtable, key); +} + +void *json_object_iter_next(json_t *json, void *iter) +{ + json_object_t *object; + + if(!json_is_object(json) || iter == NULL) + return NULL; + + object = json_to_object(json); + return hashtable_iter_next(&object->hashtable, iter); +} + +const char *json_object_iter_key(void *iter) +{ + if(!iter) + return NULL; + + return hashtable_iter_key(iter); +} + +json_t *json_object_iter_value(void *iter) +{ + if(!iter) + return NULL; + + return (json_t *)hashtable_iter_value(iter); +} + +int json_object_iter_set_new(json_t *json, void *iter, json_t *value) +{ + if(!json_is_object(json) || !iter || !value) + return -1; + + hashtable_iter_set(iter, value); + return 0; +} + +void *json_object_key_to_iter(const char *key) +{ + if(!key) + return NULL; + + return hashtable_key_to_iter(key); +} + +static int json_object_equal(json_t *object1, json_t *object2) +{ + const char *key; + json_t *value1, *value2; + + if(json_object_size(object1) != json_object_size(object2)) + return 0; + + json_object_foreach(object1, key, value1) { + value2 = json_object_get(object2, key); + + if(!json_equal(value1, value2)) + return 0; + } + + return 1; +} + +static json_t *json_object_copy(json_t *object) +{ + json_t *result; + + const char *key; + json_t *value; + + result = json_object(); + if(!result) + return NULL; + + json_object_foreach(object, key, value) + json_object_set_nocheck(result, key, value); + + return result; +} + +static json_t *json_object_deep_copy(json_t *object) +{ + json_t *result; + + const char *key; + json_t *value; + + result = json_object(); + if(!result) + return NULL; + + json_object_foreach(object, key, value) + json_object_set_new_nocheck(result, key, json_deep_copy(value)); + + return result; +} + + +/*** array ***/ + +json_t *json_array(void) +{ + json_array_t *array = jsonp_malloc(sizeof(json_array_t)); + if(!array) + return NULL; + json_init(&array->json, JSON_ARRAY); + + array->entries = 0; + array->size = 8; + + array->table = jsonp_malloc(array->size * sizeof(json_t *)); + if(!array->table) { + jsonp_free(array); + return NULL; + } + + array->visited = 0; + + return &array->json; +} + +static void json_delete_array(json_array_t *array) +{ + size_t i; + + for(i = 0; i < array->entries; i++) + json_decref(array->table[i]); + + jsonp_free(array->table); + jsonp_free(array); +} + +size_t json_array_size(const json_t *json) +{ + if(!json_is_array(json)) + return 0; + + return json_to_array(json)->entries; +} + +json_t *json_array_get(const json_t *json, size_t index) +{ + json_array_t *array; + if(!json_is_array(json)) + return NULL; + array = json_to_array(json); + + if(index >= array->entries) + return NULL; + + return array->table[index]; +} + +int json_array_set_new(json_t *json, size_t index, json_t *value) +{ + json_array_t *array; + + if(!value) + return -1; + + if(!json_is_array(json) || json == value) + { + json_decref(value); + return -1; + } + array = json_to_array(json); + + if(index >= array->entries) + { + json_decref(value); + return -1; + } + + json_decref(array->table[index]); + array->table[index] = value; + + return 0; +} + +static void array_move(json_array_t *array, size_t dest, + size_t src, size_t count) +{ + memmove(&array->table[dest], &array->table[src], count * sizeof(json_t *)); +} + +static void array_copy(json_t **dest, size_t dpos, + json_t **src, size_t spos, + size_t count) +{ + memcpy(&dest[dpos], &src[spos], count * sizeof(json_t *)); +} + +static json_t **json_array_grow(json_array_t *array, + size_t amount, + int copy) +{ + size_t new_size; + json_t **old_table, **new_table; + + if(array->entries + amount <= array->size) + return array->table; + + old_table = array->table; + + new_size = max(array->size + amount, array->size * 2); + new_table = jsonp_malloc(new_size * sizeof(json_t *)); + if(!new_table) + return NULL; + + array->size = new_size; + array->table = new_table; + + if(copy) { + array_copy(array->table, 0, old_table, 0, array->entries); + jsonp_free(old_table); + return array->table; + } + + return old_table; +} + +int json_array_append_new(json_t *json, json_t *value) +{ + json_array_t *array; + + if(!value) + return -1; + + if(!json_is_array(json) || json == value) + { + json_decref(value); + return -1; + } + array = json_to_array(json); + + if(!json_array_grow(array, 1, 1)) { + json_decref(value); + return -1; + } + + array->table[array->entries] = value; + array->entries++; + + return 0; +} + +int json_array_insert_new(json_t *json, size_t index, json_t *value) +{ + json_array_t *array; + json_t **old_table; + + if(!value) + return -1; + + if(!json_is_array(json) || json == value) { + json_decref(value); + return -1; + } + array = json_to_array(json); + + if(index > array->entries) { + json_decref(value); + return -1; + } + + old_table = json_array_grow(array, 1, 0); + if(!old_table) { + json_decref(value); + return -1; + } + + if(old_table != array->table) { + array_copy(array->table, 0, old_table, 0, index); + array_copy(array->table, index + 1, old_table, index, + array->entries - index); + jsonp_free(old_table); + } + else + array_move(array, index + 1, index, array->entries - index); + + array->table[index] = value; + array->entries++; + + return 0; +} + +int json_array_remove(json_t *json, size_t index) +{ + json_array_t *array; + + if(!json_is_array(json)) + return -1; + array = json_to_array(json); + + if(index >= array->entries) + return -1; + + json_decref(array->table[index]); + + array_move(array, index, index + 1, array->entries - index); + array->entries--; + + return 0; +} + +int json_array_clear(json_t *json) +{ + json_array_t *array; + size_t i; + + if(!json_is_array(json)) + return -1; + array = json_to_array(json); + + for(i = 0; i < array->entries; i++) + json_decref(array->table[i]); + + array->entries = 0; + return 0; +} + +int json_array_extend(json_t *json, json_t *other_json) +{ + json_array_t *array, *other; + size_t i; + + if(!json_is_array(json) || !json_is_array(other_json)) + return -1; + array = json_to_array(json); + other = json_to_array(other_json); + + if(!json_array_grow(array, other->entries, 1)) + return -1; + + for(i = 0; i < other->entries; i++) + json_incref(other->table[i]); + + array_copy(array->table, array->entries, other->table, 0, other->entries); + + array->entries += other->entries; + return 0; +} + +static int json_array_equal(json_t *array1, json_t *array2) +{ + size_t i, size; + + size = json_array_size(array1); + if(size != json_array_size(array2)) + return 0; + + for(i = 0; i < size; i++) + { + json_t *value1, *value2; + + value1 = json_array_get(array1, i); + value2 = json_array_get(array2, i); + + if(!json_equal(value1, value2)) + return 0; + } + + return 1; +} + +static json_t *json_array_copy(json_t *array) +{ + json_t *result; + size_t i; + + result = json_array(); + if(!result) + return NULL; + + for(i = 0; i < json_array_size(array); i++) + json_array_append(result, json_array_get(array, i)); + + return result; +} + +static json_t *json_array_deep_copy(json_t *array) +{ + json_t *result; + size_t i; + + result = json_array(); + if(!result) + return NULL; + + for(i = 0; i < json_array_size(array); i++) + json_array_append_new(result, json_deep_copy(json_array_get(array, i))); + + return result; +} + +/*** string ***/ + +json_t *json_string_nocheck(const char *value) +{ + json_string_t *string; + + if(!value) + return NULL; + + string = jsonp_malloc(sizeof(json_string_t)); + if(!string) + return NULL; + json_init(&string->json, JSON_STRING); + + string->value = jsonp_strdup(value); + if(!string->value) { + jsonp_free(string); + return NULL; + } + + return &string->json; +} + +json_t *json_string(const char *value) +{ + if(!value || !utf8_check_string(value, -1)) + return NULL; + + return json_string_nocheck(value); +} + +const char *json_string_value(const json_t *json) +{ + if(!json_is_string(json)) + return NULL; + + return json_to_string(json)->value; +} + +int json_string_set_nocheck(json_t *json, const char *value) +{ + char *dup; + json_string_t *string; + + if(!json_is_string(json) || !value) + return -1; + + dup = jsonp_strdup(value); + if(!dup) + return -1; + + string = json_to_string(json); + jsonp_free(string->value); + string->value = dup; + + return 0; +} + +int json_string_set(json_t *json, const char *value) +{ + if(!value || !utf8_check_string(value, -1)) + return -1; + + return json_string_set_nocheck(json, value); +} + +static void json_delete_string(json_string_t *string) +{ + jsonp_free(string->value); + jsonp_free(string); +} + +static int json_string_equal(json_t *string1, json_t *string2) +{ + return strcmp(json_string_value(string1), json_string_value(string2)) == 0; +} + +static json_t *json_string_copy(json_t *string) +{ + return json_string_nocheck(json_string_value(string)); +} + + +/*** integer ***/ + +json_t *json_integer(json_int_t value) +{ + json_integer_t *integer = jsonp_malloc(sizeof(json_integer_t)); + if(!integer) + return NULL; + json_init(&integer->json, JSON_INTEGER); + + integer->value = value; + return &integer->json; +} + +json_int_t json_integer_value(const json_t *json) +{ + if(!json_is_integer(json)) + return 0; + + return json_to_integer(json)->value; +} + +int json_integer_set(json_t *json, json_int_t value) +{ + if(!json_is_integer(json)) + return -1; + + json_to_integer(json)->value = value; + + return 0; +} + +static void json_delete_integer(json_integer_t *integer) +{ + jsonp_free(integer); +} + +static int json_integer_equal(json_t *integer1, json_t *integer2) +{ + return json_integer_value(integer1) == json_integer_value(integer2); +} + +static json_t *json_integer_copy(json_t *integer) +{ + return json_integer(json_integer_value(integer)); +} + + +/*** real ***/ + +json_t *json_real(double value) +{ + json_real_t *real; + + if(isnan(value) || isinf(value)) + return NULL; + + real = jsonp_malloc(sizeof(json_real_t)); + if(!real) + return NULL; + json_init(&real->json, JSON_REAL); + + real->value = value; + return &real->json; +} + +double json_real_value(const json_t *json) +{ + if(!json_is_real(json)) + return 0; + + return json_to_real(json)->value; +} + +int json_real_set(json_t *json, double value) +{ + if(!json_is_real(json) || isnan(value) || isinf(value)) + return -1; + + json_to_real(json)->value = value; + + return 0; +} + +static void json_delete_real(json_real_t *real) +{ + jsonp_free(real); +} + +static int json_real_equal(json_t *real1, json_t *real2) +{ + return json_real_value(real1) == json_real_value(real2); +} + +static json_t *json_real_copy(json_t *real) +{ + return json_real(json_real_value(real)); +} + + +/*** number ***/ + +double json_number_value(const json_t *json) +{ + if(json_is_integer(json)) + return (double)json_integer_value(json); + else if(json_is_real(json)) + return json_real_value(json); + else + return 0.0; +} + + +/*** simple values ***/ + +json_t *json_true(void) +{ + static json_t the_true = {JSON_TRUE, (size_t)-1}; + return &the_true; +} + + +json_t *json_false(void) +{ + static json_t the_false = {JSON_FALSE, (size_t)-1}; + return &the_false; +} + + +json_t *json_null(void) +{ + static json_t the_null = {JSON_NULL, (size_t)-1}; + return &the_null; +} + + +/*** deletion ***/ + +void json_delete(json_t *json) +{ + if(json_is_object(json)) + json_delete_object(json_to_object(json)); + + else if(json_is_array(json)) + json_delete_array(json_to_array(json)); + + else if(json_is_string(json)) + json_delete_string(json_to_string(json)); + + else if(json_is_integer(json)) + json_delete_integer(json_to_integer(json)); + + else if(json_is_real(json)) + json_delete_real(json_to_real(json)); + + /* json_delete is not called for true, false or null */ +} + + +/*** equality ***/ + +int json_equal(json_t *json1, json_t *json2) +{ + if(!json1 || !json2) + return 0; + + if(json_typeof(json1) != json_typeof(json2)) + return 0; + + /* this covers true, false and null as they are singletons */ + if(json1 == json2) + return 1; + + if(json_is_object(json1)) + return json_object_equal(json1, json2); + + if(json_is_array(json1)) + return json_array_equal(json1, json2); + + if(json_is_string(json1)) + return json_string_equal(json1, json2); + + if(json_is_integer(json1)) + return json_integer_equal(json1, json2); + + if(json_is_real(json1)) + return json_real_equal(json1, json2); + + return 0; +} + + +/*** copying ***/ + +json_t *json_copy(json_t *json) +{ + if(!json) + return NULL; + + if(json_is_object(json)) + return json_object_copy(json); + + if(json_is_array(json)) + return json_array_copy(json); + + if(json_is_string(json)) + return json_string_copy(json); + + if(json_is_integer(json)) + return json_integer_copy(json); + + if(json_is_real(json)) + return json_real_copy(json); + + if(json_is_true(json) || json_is_false(json) || json_is_null(json)) + return json; + + return NULL; +} + +json_t *json_deep_copy(json_t *json) +{ + if(!json) + return NULL; + + if(json_is_object(json)) + return json_object_deep_copy(json); + + if(json_is_array(json)) + return json_array_deep_copy(json); + + /* for the rest of the types, deep copying doesn't differ from + shallow copying */ + + if(json_is_string(json)) + return json_string_copy(json); + + if(json_is_integer(json)) + return json_integer_copy(json); + + if(json_is_real(json)) + return json_real_copy(json); + + if(json_is_true(json) || json_is_false(json) || json_is_null(json)) + return json; + + return NULL; +} diff --git a/src/libXNVCtrl/NVCtrl.h b/src/libXNVCtrl/NVCtrl.h index 53b49ae..4933f49 100644 --- a/src/libXNVCtrl/NVCtrl.h +++ b/src/libXNVCtrl/NVCtrl.h @@ -721,18 +721,22 @@ #define NV_CTRL_TEXTURE_CLAMPING_SPEC 1 +/* + * The NV_CTRL_CURSOR_SHADOW attributes are no longer supported; use + * an ARGB cursor instead. + */ -#define NV_CTRL_CURSOR_SHADOW 43 /* RW- */ +#define NV_CTRL_CURSOR_SHADOW 43 /* --- */ #define NV_CTRL_CURSOR_SHADOW_DISABLE 0 #define NV_CTRL_CURSOR_SHADOW_ENABLE 1 -#define NV_CTRL_CURSOR_SHADOW_ALPHA 44 /* RW- */ -#define NV_CTRL_CURSOR_SHADOW_RED 45 /* RW- */ -#define NV_CTRL_CURSOR_SHADOW_GREEN 46 /* RW- */ -#define NV_CTRL_CURSOR_SHADOW_BLUE 47 /* RW- */ +#define NV_CTRL_CURSOR_SHADOW_ALPHA 44 /* --- */ +#define NV_CTRL_CURSOR_SHADOW_RED 45 /* --- */ +#define NV_CTRL_CURSOR_SHADOW_GREEN 46 /* --- */ +#define NV_CTRL_CURSOR_SHADOW_BLUE 47 /* --- */ -#define NV_CTRL_CURSOR_SHADOW_X_OFFSET 48 /* RW- */ -#define NV_CTRL_CURSOR_SHADOW_Y_OFFSET 49 /* RW- */ +#define NV_CTRL_CURSOR_SHADOW_X_OFFSET 48 /* --- */ +#define NV_CTRL_CURSOR_SHADOW_Y_OFFSET 49 /* --- */ @@ -3606,15 +3610,16 @@ /* - * NV_CTRL_STRING_XINERAMA_SCREEN_INFO - returns the physical X Screen's - * initial position and size (in absolute coordinates) within the Xinerama + * NV_CTRL_STRING_SCREEN_RECTANGLE - returns the physical X Screen's + * initial position and size (in absolute coordinates) within the * desktop as the "token=value" string: "x=#, y=#, width=#, height=#" * - * Querying this attribute returns FALSE if NV_CTRL_XINERAMA is not - * NV_CTRL_XINERAMA_ON. + * Querying this attribute returns success only when Xinerama is enabled + * or the X server ABI is greater than equal to 12. */ -#define NV_CTRL_STRING_XINERAMA_SCREEN_INFO 26 /* R--- */ +#define NV_CTRL_STRING_XINERAMA_SCREEN_INFO 26 /* renamed */ +#define NV_CTRL_STRING_SCREEN_RECTANGLE 26 /* R--- */ /* @@ -4329,6 +4334,8 @@ #define NV_CTRL_BINARY_DATA_GPU_FLAGS_STEREO_DISPLAY_TRANSFORM_EXCLUSIVE 0 /* Overlay and display composition transformations are mutually exclusive. */ #define NV_CTRL_BINARY_DATA_GPU_FLAGS_OVERLAY_DISPLAY_TRANSFORM_EXCLUSIVE 1 +/* Depth 8 and display composition transformations are mutually exclusive. */ +#define NV_CTRL_BINARY_DATA_GPU_FLAGS_DEPTH_8_DISPLAY_TRANSFORM_EXCLUSIVE 2 #define NV_CTRL_BINARY_DATA_LAST_ATTRIBUTE NV_CTRL_BINARY_DATA_GPU_FLAGS diff --git a/src/libXNVCtrlAttributes/NvCtrlAttributes.c b/src/libXNVCtrlAttributes/NvCtrlAttributes.c index e168a20..eb3e350 100644 --- a/src/libXNVCtrlAttributes/NvCtrlAttributes.c +++ b/src/libXNVCtrlAttributes/NvCtrlAttributes.c @@ -22,6 +22,7 @@ #include "NVCtrlLib.h" +#include "common-utils.h" #include "msg.h" #include "parse.h" @@ -45,11 +46,7 @@ NvCtrlAttributeHandle *NvCtrlAttributeInit(Display *dpy, int target_type, { NvCtrlAttributePrivateHandle *h = NULL; - h = calloc(1, sizeof (NvCtrlAttributePrivateHandle)); - if (!h) { - nv_error_msg("Memory allocation failure!"); - goto failed; - } + h = nvalloc(sizeof (NvCtrlAttributePrivateHandle)); /* initialize the display and screen to the parameter values */ @@ -589,33 +586,13 @@ NvCtrlSetDisplayAttribute(NvCtrlAttributeHandle *handle, NvCtrlAttributePrivateHandle *h; h = (NvCtrlAttributePrivateHandle *) handle; - + if ((attr >= 0) && (attr <= NV_CTRL_LAST_ATTRIBUTE)) { if (!h->nv) return NvCtrlMissingExtension; return NvCtrlNvControlSetAttribute(h, display_mask, attr, val); } return NvCtrlNoAttribute; - -} /* NvCtrlSetDisplayAttribute() */ - - -ReturnStatus -NvCtrlSetDisplayAttributeWithReply(NvCtrlAttributeHandle *handle, - unsigned int display_mask, - int attr, int val) -{ - NvCtrlAttributePrivateHandle *h; - - h = (NvCtrlAttributePrivateHandle *) handle; - - if ((attr >= 0) && (attr <= NV_CTRL_LAST_ATTRIBUTE)) { - if (!h->nv) return NvCtrlMissingExtension; - return NvCtrlNvControlSetAttributeWithReply(h, display_mask, - attr, val); - } - - return NvCtrlNoAttribute; } diff --git a/src/libXNVCtrlAttributes/NvCtrlAttributes.h b/src/libXNVCtrlAttributes/NvCtrlAttributes.h index 72c3c51..fdd7498 100644 --- a/src/libXNVCtrlAttributes/NvCtrlAttributes.h +++ b/src/libXNVCtrlAttributes/NvCtrlAttributes.h @@ -412,11 +412,6 @@ NvCtrlGetDisplayAttribute64 (NvCtrlAttributeHandle *handle, unsigned int display_mask, int attr, int64_t *val); ReturnStatus -NvCtrlSetDisplayAttributeWithReply (NvCtrlAttributeHandle *handle, - unsigned int display_mask, - int attr, int val); - -ReturnStatus NvCtrlGetVoidDisplayAttribute (NvCtrlAttributeHandle *handle, unsigned int display_mask, int attr, void **val); diff --git a/src/libXNVCtrlAttributes/NvCtrlAttributesGlx.c b/src/libXNVCtrlAttributes/NvCtrlAttributesGlx.c index 97ba082..7603cb8 100644 --- a/src/libXNVCtrlAttributes/NvCtrlAttributesGlx.c +++ b/src/libXNVCtrlAttributes/NvCtrlAttributesGlx.c @@ -22,6 +22,7 @@ #include "NVCtrlLib.h" +#include "common-utils.h" #include "msg.h" #include "parse.h" @@ -130,13 +131,9 @@ static Bool open_libgl(void) /* Initialize bookkeeping structure */ if ( !__libGL ) { - __libGL = calloc(1, sizeof(__libGLInfo)); - if ( !__libGL ) { - error_str = "Could not allocate memory."; - goto fail; - } + __libGL = nvalloc(sizeof(__libGLInfo)); } - + /* Library was already opened */ if ( __libGL->handle ) { @@ -365,10 +362,7 @@ get_fbconfig_attribs(NvCtrlAttributePrivateHandle *h) } /* Allocate to hold the fbconfig attributes */ - fbcas = calloc(nfbconfigs + 1, sizeof(GLXFBConfigAttr)); - if ( fbcas == NULL ) { - goto fail; - } + fbcas = nvalloc((nfbconfigs + 1) * sizeof(GLXFBConfigAttr)); /* Query each fbconfig's attributes and populate the attrib array */ for ( i = 0; i < nfbconfigs; i++ ) { diff --git a/src/libXNVCtrlAttributes/NvCtrlAttributesNvControl.c b/src/libXNVCtrlAttributes/NvCtrlAttributesNvControl.c index 1695fd6..04f9a7f 100644 --- a/src/libXNVCtrlAttributes/NvCtrlAttributesNvControl.c +++ b/src/libXNVCtrlAttributes/NvCtrlAttributesNvControl.c @@ -22,6 +22,7 @@ #include "NVCtrlLib.h" +#include "common-utils.h" #include "msg.h" #include <stdlib.h> @@ -67,8 +68,8 @@ NvCtrlInitNvControlAttributes (NvCtrlAttributePrivateHandle *h) return NULL; } } - - nv = calloc(1, sizeof(NvCtrlNvControlAttributes)); + + nv = nvalloc(sizeof(NvCtrlNvControlAttributes)); ret = XNVCtrlSelectTargetNotify(h->dpy, h->target_type, h->target_id, TARGET_ATTRIBUTE_CHANGED_EVENT, True); @@ -210,37 +211,7 @@ ReturnStatus NvCtrlNvControlSetAttribute (NvCtrlAttributePrivateHandle *h, } return NvCtrlNoAttribute; - -} /* NvCtrlNvControlSetAttribute() */ - - -ReturnStatus -NvCtrlNvControlSetAttributeWithReply(NvCtrlAttributePrivateHandle *h, - unsigned int display_mask, - int attr, int val) -{ - Bool bRet; - - /* XNVCTRLSetAttributeAndGetStatus() only works on X screens */ - - if (h->target_type != NV_CTRL_TARGET_TYPE_X_SCREEN) { - return NvCtrlError; - } - - if (attr <= NV_CTRL_LAST_ATTRIBUTE) { - bRet = XNVCTRLSetAttributeAndGetStatus(h->dpy, h->target_id, - display_mask, attr, val); - if (bRet) { - return NvCtrlSuccess; - } else { - return NvCtrlError; - } - } - - return NvCtrlNoAttribute; - -} /* NvCtrlNvControlSetAttributeWithReply() */ - +} ReturnStatus NvCtrlNvControlGetValidAttributeValues @@ -340,17 +311,29 @@ NvCtrlNvControlSetStringAttribute (NvCtrlAttributePrivateHandle *h, { int tmp_int; /* Temp storage if ret is not specified */ - if (h->target_type != NV_CTRL_TARGET_TYPE_X_SCREEN) { - return NvCtrlBadHandle; - } - if (attr <= NV_CTRL_LAST_ATTRIBUTE) { if ( !ret ) { ret = &tmp_int; } - *ret = - XNVCTRLSetStringAttribute (h->dpy, h->target_id, display_mask, - attr, ptr); + + /* NV-CONTROL 1.19 and above has support for setting string attributes + * on targets other than X screens. + */ + if (h->nv->major_version > 1 || + (h->nv->major_version == 1 && h->nv->minor_version >= 19)) { + *ret = + XNVCTRLSetTargetStringAttribute(h->dpy, h->target_type, + h->target_id, display_mask, + attr, ptr); + } else { + if (h->target_type != NV_CTRL_TARGET_TYPE_X_SCREEN) { + return NvCtrlBadHandle; + } + *ret = + XNVCTRLSetStringAttribute(h->dpy, h->target_id, display_mask, + attr, ptr); + } + if ( *ret ) { return NvCtrlSuccess; } else { diff --git a/src/libXNVCtrlAttributes/NvCtrlAttributesVidMode.c b/src/libXNVCtrlAttributes/NvCtrlAttributesVidMode.c index f16db84..0949ceb 100644 --- a/src/libXNVCtrlAttributes/NvCtrlAttributesVidMode.c +++ b/src/libXNVCtrlAttributes/NvCtrlAttributesVidMode.c @@ -20,6 +20,7 @@ #include "NvCtrlAttributes.h" #include "NvCtrlAttributesPrivate.h" +#include "common-utils.h" #include "msg.h" #include <X11/extensions/xf86vmode.h> @@ -43,7 +44,7 @@ NvCtrlInitVidModeAttributes(NvCtrlAttributePrivateHandle *h) ret = XF86VidModeQueryExtension(h->dpy, &event, &error); if (!ret) goto failed; - vm = calloc(1, sizeof(NvCtrlVidModeAttributes)); + vm = nvalloc(sizeof(NvCtrlVidModeAttributes)); ret = XF86VidModeQueryVersion(h->dpy, &(vm->major_version), &(vm->minor_version)); if (!ret) goto failed; @@ -76,15 +77,9 @@ NvCtrlInitVidModeAttributes(NvCtrlAttributePrivateHandle *h) if (!ret) goto failed; - vm->lut[RED_CHANNEL_INDEX] = malloc(sizeof(unsigned short) * size); - vm->lut[GREEN_CHANNEL_INDEX] = malloc(sizeof(unsigned short) * size); - vm->lut[BLUE_CHANNEL_INDEX] = malloc(sizeof(unsigned short) * size); - - if ((vm->lut[RED_CHANNEL_INDEX] == NULL) || - (vm->lut[GREEN_CHANNEL_INDEX] == NULL) || - (vm->lut[BLUE_CHANNEL_INDEX] == NULL)) { - goto failed; - } + vm->lut[RED_CHANNEL_INDEX] = nvalloc(sizeof(unsigned short) * size); + vm->lut[GREEN_CHANNEL_INDEX] = nvalloc(sizeof(unsigned short) * size); + vm->lut[BLUE_CHANNEL_INDEX] = nvalloc(sizeof(unsigned short) * size); vm->gammaRampSize = size; diff --git a/src/libXNVCtrlAttributes/NvCtrlAttributesXrandr.c b/src/libXNVCtrlAttributes/NvCtrlAttributesXrandr.c index 9a77b94..79f95d5 100644 --- a/src/libXNVCtrlAttributes/NvCtrlAttributesXrandr.c +++ b/src/libXNVCtrlAttributes/NvCtrlAttributesXrandr.c @@ -35,6 +35,7 @@ #include "NvCtrlAttributesPrivate.h" #include "NVCtrlLib.h" +#include "common-utils.h" #include "msg.h" #include "parse.h" @@ -89,13 +90,9 @@ static Bool open_libxrandr(void) /* Initialize bookkeeping structure */ if ( !__libXrandr ) { - __libXrandr = calloc(1, sizeof(__libXrandrInfo)); - if ( !__libXrandr ) { - error_str = "Could not allocate memory."; - goto fail; - } + __libXrandr = nvalloc(sizeof(__libXrandrInfo)); } - + /* Library was already opened */ if ( __libXrandr->handle ) { __libXrandr->ref_count++; @@ -295,12 +292,8 @@ NvCtrlInitXrandrAttributes (NvCtrlAttributePrivateHandle *h) /* Create storage for XRandR attributes */ - xrandr = - calloc(1, sizeof(NvCtrlXrandrAttributes)); - if ( !xrandr ) { - goto fail; - } - + xrandr = nvalloc(sizeof(NvCtrlXrandrAttributes)); + /* Verify server support of XRandR extension */ if ( !__libXrandr->XRRQueryExtension(h->dpy, diff --git a/src/libXNVCtrlAttributes/NvCtrlAttributesXv.c b/src/libXNVCtrlAttributes/NvCtrlAttributesXv.c index d8902b5..ee56907 100644 --- a/src/libXNVCtrlAttributes/NvCtrlAttributesXv.c +++ b/src/libXNVCtrlAttributes/NvCtrlAttributesXv.c @@ -26,6 +26,7 @@ #include <string.h> #include <dlfcn.h> +#include "common-utils.h" #include "msg.h" typedef struct __libXvInfoRec { @@ -57,13 +58,9 @@ static Bool open_libxv(void) /* Initialize bookkeeping structure */ if ( !__libXv ) { - __libXv = calloc(1, sizeof(__libXvInfo)); - if ( !__libXv ) { - error_str = "Could not allocate memory."; - goto fail; - } + __libXv = nvalloc(sizeof(__libXvInfo)); } - + /* Library was already opened */ if ( __libXv->handle ) { @@ -171,11 +168,7 @@ NvCtrlXvAttributes * NvCtrlInitXvAttributes(NvCtrlAttributePrivateHandle *h) /* Allocate the attributes structure */ - xv = calloc(1, sizeof(NvCtrlXvAttributes)); - if ( xv == NULL ) { - error_str = "Out of memory."; - goto fail; - } + xv = nvalloc(sizeof(NvCtrlXvAttributes)); /* Verify server support of Xv extension */ @@ -166,7 +166,7 @@ static void format(FILE *stream, const char *prefix, if (prefix) { prefix_len = strlen(prefix); - local_prefix = malloc(prefix_len+1); + local_prefix = nvalloc(prefix_len+1); strcpy(local_prefix, prefix); } else { prefix_len = 0; @@ -216,7 +216,7 @@ static void format(FILE *stream, const char *prefix, */ len = b-a; - line = malloc(len+1); + line = nvalloc(len+1); strncpy(line, a, len); line[len] = '\0'; if (local_prefix) { @@ -23,10 +23,12 @@ #include <stdarg.h> #include <stdio.h> -void nv_error_msg(const char*, ...); -void nv_warning_msg(const char*, ...); -void nv_info_msg(const char*, const char*, ...); -void nv_msg(const char*, const char*, ...); -void nv_msg_preserve_whitespace(const char*, const char*, ...); +#include "common-utils.h" + +void nv_error_msg(const char*, ...) NV_ATTRIBUTE_PRINTF(1, 2); +void nv_warning_msg(const char*, ...) NV_ATTRIBUTE_PRINTF(1, 2); +void nv_info_msg(const char*, const char*, ...) NV_ATTRIBUTE_PRINTF(2, 3); +void nv_msg(const char*, const char*, ...) NV_ATTRIBUTE_PRINTF(2, 3); +void nv_msg_preserve_whitespace(const char*, const char*, ...) NV_ATTRIBUTE_PRINTF(2, 3); #endif /* __MSG_H__ */ diff --git a/src/nvidia-settings.c b/src/nvidia-settings.c index 1b90f0f..acfa685 100644 --- a/src/nvidia-settings.c +++ b/src/nvidia-settings.c @@ -34,11 +34,15 @@ int main(int argc, char **argv) ConfigProperties conf; ParsedAttribute *p; CtrlHandles *h; + CtrlHandlesArray handles_array; Options *op; int ret; char *dpy = NULL; int gui = 0; + handles_array.n = 0; + handles_array.array = NULL; + /* * initialize the ui * @@ -58,7 +62,7 @@ int main(int argc, char **argv) /* parse the commandline */ - op = parse_command_line(argc, argv, dpy); + op = parse_command_line(argc, argv, dpy, &handles_array); /* quit here if we don't have a ctrl_display - TY 2005-05-27 */ @@ -68,10 +72,15 @@ int main(int argc, char **argv) return 1; } + /* Allocate handle for ctrl_display */ + + nv_alloc_ctrl_handles_and_add_to_array(op->ctrl_display, &handles_array); + /* process any query or assignment commandline options */ if (op->num_assignments || op->num_queries) { - ret = nv_process_assignments_and_queries(op); + ret = nv_process_assignments_and_queries(op, &handles_array); + nv_free_ctrl_handles_array(&handles_array); return ret ? 0 : 1; } @@ -90,10 +99,10 @@ int main(int argc, char **argv) if (op->rewrite) { nv_parsed_attribute_clean(p); - h = nv_alloc_ctrl_handles(op->ctrl_display); + h = nv_get_ctrl_handles(op->ctrl_display, &handles_array); if(!h || !h->dpy) return 1; ret = nv_write_config_file(op->config, h, p, &conf); - nv_free_ctrl_handles(h); + nv_free_ctrl_handles_array(&handles_array); nv_parsed_attribute_free(p); free(op); op = NULL; @@ -103,7 +112,8 @@ int main(int argc, char **argv) /* upload the data from the config file */ if (!op->no_load) { - ret = nv_read_config_file(op->config, op->ctrl_display, p, &conf); + ret = nv_read_config_file(op->config, op->ctrl_display, + p, &conf, &handles_array); } else { ret = 1; } @@ -128,9 +138,9 @@ int main(int argc, char **argv) return 1; } - /* allocate the CtrlHandles for this X screen */ + /* Get the CtrlHandles for this X screen */ - h = nv_alloc_ctrl_handles(op->ctrl_display); + h = nv_get_ctrl_handles(op->ctrl_display, &handles_array); if (!h || !h->dpy) { return 1; @@ -146,7 +156,7 @@ int main(int argc, char **argv) /* cleanup */ - nv_free_ctrl_handles(h); + nv_free_ctrl_handles_array(&handles_array); nv_parsed_attribute_free(p); return 0; diff --git a/src/parse.c b/src/parse.c index 4b480a2..528549a 100644 --- a/src/parse.c +++ b/src/parse.c @@ -27,14 +27,13 @@ #include "parse.h" #include "NvCtrlAttributes.h" +#include "query-assign.h" #include "common-utils.h" /* local helper functions */ -static int nv_parse_display_and_target(char *start, char *end, - ParsedAttribute *a); static char **nv_strtok(char *s, char c, int *n); static void nv_free_strtoks(char **s, int n); static int ctoi(const char c); @@ -64,8 +63,8 @@ static int count_number_of_chars(char *o, char d); #define M NV_PARSER_TYPE_SDI_CSC #define T NV_PARSER_TYPE_HIJACK_DISPLAY_DEVICE -AttributeTableEntry attributeTable[] = { - +const AttributeTableEntry attributeTable[] = { + /* name constant flags description */ /* Version information */ @@ -78,6 +77,7 @@ AttributeTableEntry attributeTable[] = { { "XRandRVersion", NV_CTRL_STRING_XRANDR_VERSION, S|N, "The X RandR version." }, { "XF86VidModeVersion", NV_CTRL_STRING_XF86VIDMODE_VERSION, S|N, "The XF86 Video Mode X extension version." }, { "XvVersion", NV_CTRL_STRING_XV_VERSION, S|N, "The Xv X extension version." }, + { "ScreenPosition", NV_CTRL_STRING_SCREEN_RECTANGLE, S|N, "Returns the physical X Screen's initial position and size (in absolute coordinates) within the desktop as the \"token=value \" string: \"x=#, y=#, width=#, height=#\"." }, /* X screen */ { "Ubb", NV_CTRL_UBB, 0, "Is UBB enabled for the specified X screen." }, @@ -86,13 +86,6 @@ AttributeTableEntry attributeTable[] = { { "TwinView", NV_CTRL_TWINVIEW, 0, "Is TwinView enabled for the specified X screen." }, { "ConnectedDisplays", NV_CTRL_CONNECTED_DISPLAYS, D, "Display mask indicating the last cached state of the display devices connected to the GPU." }, { "EnabledDisplays", NV_CTRL_ENABLED_DISPLAYS, D, "Display mask indicating what display devices are enabled for use on the specified X screen or GPU." }, - { "CursorShadow", NV_CTRL_CURSOR_SHADOW, 0, "Hardware cursor shadow." }, - { "CursorShadowAlpha", NV_CTRL_CURSOR_SHADOW_ALPHA, 0, "Hardware cursor shadow alpha (transparency) value." }, - { "CursorShadowRed", NV_CTRL_CURSOR_SHADOW_RED, 0, "Hardware cursor shadow red color." }, - { "CursorShadowGreen", NV_CTRL_CURSOR_SHADOW_GREEN, 0, "Hardware cursor shadow green color." }, - { "CursorShadowBlue", NV_CTRL_CURSOR_SHADOW_BLUE, 0, "Hardware cursor shadow blue color." }, - { "CursorShadowXOffset", NV_CTRL_CURSOR_SHADOW_X_OFFSET, 0, "Hardware cursor shadow X offset." }, - { "CursorShadowYOffset", NV_CTRL_CURSOR_SHADOW_Y_OFFSET, 0, "Hardware cursor shadow Y offset." }, { "AssociatedDisplays", NV_CTRL_ASSOCIATED_DISPLAY_DEVICES, N|D, "Display device mask indicating which display devices are \"associated\" with the specified X screen (i.e., are available for displaying the desktop)." }, { "ProbeDisplays", NV_CTRL_PROBE_DISPLAYS, A|D, "When this attribute is queried, the X driver re-probes the hardware to detect which display devices are connected to the GPU or DPU driving the specified X screen. Returns a display mask of the currently connected display devices." }, { "InitialPixmapPlacement", NV_CTRL_INITIAL_PIXMAP_PLACEMENT, N, "Controls where X pixmaps are initially created." }, @@ -386,7 +379,7 @@ AttributeTableEntry attributeTable[] = { * for each attribute target type. */ -TargetTypeEntry targetTypeTable[] = { +const TargetTypeEntry targetTypeTable[] = { { "X Screen", /* name */ "screen", /* parsed_name */ @@ -395,7 +388,7 @@ TargetTypeEntry targetTypeTable[] = { ATTRIBUTE_TYPE_X_SCREEN, /* permission_bit */ NV_TRUE, /* uses_display_devices */ 1, 6 }, /* required major,minor protocol rev */ - + { "GPU", /* name */ "gpu", /* parsed_name */ GPU_TARGET, /* target_index */ @@ -403,7 +396,7 @@ TargetTypeEntry targetTypeTable[] = { ATTRIBUTE_TYPE_GPU, /* permission_bit */ NV_TRUE, /* uses_display_devices */ 1, 10 }, /* required major,minor protocol rev */ - + { "Frame Lock Device", /* name */ "framelock", /* parsed_name */ FRAMELOCK_TARGET, /* target_index */ @@ -435,7 +428,7 @@ TargetTypeEntry targetTypeTable[] = { ATTRIBUTE_TYPE_COOLER, /* permission_bit */ NV_FALSE, /* uses_display_devices */ 1, 20 }, /* required major,minor protocol rev */ - + { "Thermal Sensor", /* name */ "thermalsensor", /* parsed_name */ THERMAL_SENSOR_TARGET, /* target_index */ @@ -459,36 +452,30 @@ TargetTypeEntry targetTypeTable[] = { ATTRIBUTE_TYPE_DISPLAY, /* permission_bit */ NV_FALSE, /* uses_display_devices */ 1, 27 }, /* required major,minor protocol rev */ - - { NULL, NULL, 0, 0, 0, 0, 0, 0 }, }; -TargetTypeEntry *nv_get_target_type_entry_by_nvctrl(int nvctrl) -{ - TargetTypeEntry *targetTypeEntry; +const int targetTypeTableLen = ARRAY_LEN(targetTypeTable); - for (targetTypeEntry = targetTypeTable; - targetTypeEntry->name; - targetTypeEntry++) { +const TargetTypeEntry *nv_get_target_type_entry_by_nvctrl(int nvctrl) +{ + int i; - if (targetTypeEntry->nvctrl == nvctrl) { - return targetTypeEntry; + for (i = 0; i < targetTypeTableLen; i++) { + if (targetTypeTable[i].nvctrl == nvctrl) { + return &targetTypeTable[i]; } } return NULL; } -TargetTypeEntry *nv_get_target_type_entry_by_name(const char *name) +const TargetTypeEntry *nv_get_target_type_entry_by_name(const char *name) { - TargetTypeEntry *targetTypeEntry; - - for (targetTypeEntry = targetTypeTable; - targetTypeEntry->name; - targetTypeEntry++) { + int i; - if (nv_strcasecmp(targetTypeEntry->parsed_name, name)) { - return targetTypeEntry; + for (i = 0; i < targetTypeTableLen; i++) { + if (nv_strcasecmp(targetTypeTable[i].parsed_name, name)) { + return &targetTypeTable[i]; } } @@ -539,15 +526,199 @@ const float * nv_get_sdi_csc_matrix(char *s) +/*! + * Return whether the string defined by 'start' and 'end' is a simple numerical + * value; and if so, assigns the value to where 'val' points. + * + * \param[in] start Start of the string to parse. + * \param[in] end End of the string to parse, or NULL if the string is NULL- + * terminated. + * \param[out] val Points to the integer that should hold the parsed value, + * if the string is a numeric. + * + * \return Return NV_TRUE if the string was a simple numerical value and + * 'val' was assigned; else, returns NV_FALSE and 'val' is not + * modified. + */ + +int nv_parse_numerical(const char *start, const char *end, int *val) +{ + int num = 0; + const char *s; + + for (s = start; + *s && (!end || (s < end)); + s++) { + if (!isdigit(*s)) { + return NV_FALSE; + } + num = (num * 10) + ctoi(*s); + } + + *val = num; + return NV_TRUE; +} + + + +/*! + * Parse the string as a (special case) X screen number. + * + * Return whether the string defined by 'start' and 'end' is a simple numerical + * value that was applied to the ParsedAttribute 'a' as an X screen target + * type/id. + * + * \param[in] start Start of the string to parse. + * \param[in] end End of the string to parse, or NULL if the string is NULL- + * terminated. + * \param[out] a ParsedAttribute to set as an X screen target if the string + * is found to be a simple numeric. + * + * \return Return NV_TRUE if the string was a simple numerical value and 'a' + * was modified; else, return NV_FALSE. + */ + +static int nv_parse_special_xscreen_target(ParsedAttribute *a, + const char *start, + const char *end) +{ + if (!nv_parse_numerical(start, end, &(a->target_id))) { + return FALSE; + } + + a->flags |= NV_PARSER_HAS_TARGET; + a->target_type = NV_CTRL_TARGET_TYPE_X_SCREEN; + + return NV_TRUE; +} + + + +/*! + * Parse the string as one of either: an X Display name, just an X screen, and/ + * or a target specification in which the string can be in one of the following + * formats: + * + * {screen} + * {host}:{display}.{screen} + * {host}:{display}[{target-type}:{target-id} + * + * This is a helper for nv_parse_attribute_string() for parsing all the text + * before the DISPLAY_NAME_SEPARATOR. + * + * \param[in] start Start of the string to parse. + * \param[in] end End of the the string to parse. + * \param[out] a ParsedAttribute to be modified with the X Display and/or + * target type + target id or generic specification + * information. + * + * \return Return NV_PARSER_STATUS_SUCCESS if the string was successfully + * parsed; Else, one of the NV_PARSER_STATUS_XXX errors that describes + * the parsing error encountered. + */ + +static int nv_parse_display_and_target(const char *start, + const char *end, /* exclusive */ + ParsedAttribute *a) +{ + int len; + const char *s, *pOpen, *pClose; + const char *startDisplayName; + const char *endDisplayName; + + /* Set target specification related defaults */ + + a->display = NULL; + a->target_id = -1; + a->target_type = -1; + a->target_name = NULL; + a->target_specification = NULL; + a->flags &= ~(NV_PARSER_HAS_X_DISPLAY | + NV_PARSER_HAS_TARGET); + + /* + * If the string consists of digits only, then this is a special case where + * the X screen number is being specified. + */ + + if (nv_parse_special_xscreen_target(a, start, end)) { + return NV_PARSER_STATUS_SUCCESS; + } + + /* + * If we get here, then there are non-digit characters; look for a pair of + * brackets, and treat the contents as a target specification. + */ + + pOpen = pClose = NULL; + + for (s = start; s < end; s++) { + if (*s == '[') pOpen = s; + if (*s == ']') pClose = s; + } + + startDisplayName = start; + endDisplayName = end; + + if (pOpen && pClose && (pClose > pOpen) && ((pClose - pOpen) > 1)) { + + /* + * check that there is no stray text between the closing bracket and the + * end of our parsable string. + */ + + if ((end - pClose) > 1) { + return NV_PARSER_STATUS_TARGET_SPEC_TRAILING_GARBAGE; + } + + /* + * Save everything between the pair of brackets as the target + * specification to be parsed (against the list of targets on the + * specified/default X Display) later. + */ + + len = pClose - pOpen - 1; + + a->target_specification = nvstrndup(pOpen + 1, len); + + /* + * The X Display name should end on the opening bracket of the target + * specification. + */ + + endDisplayName = pOpen; + } + + /* treat everything between start and end as an X Display name */ + + if (startDisplayName < endDisplayName) { + + a->display = nvstrndup(startDisplayName, + endDisplayName - startDisplayName); + a->flags |= NV_PARSER_HAS_X_DISPLAY; + + /* + * this will attempt to parse out any screen number from the + * display name + */ + + nv_assign_default_display(a, NULL); + } + + return NV_PARSER_STATUS_SUCCESS; +} + + + /* * nv_parse_attribute_string() - see comments in parse.h */ int nv_parse_attribute_string(const char *str, int query, ParsedAttribute *a) { - char *s, *tmp, *name, *start, *display_device_name, *no_spaces = NULL; + char *s, *tmp, *name, *start, *display_device_name, *equal_sign, *no_spaces = NULL; char tmpname[NV_PARSER_MAX_NAME_LEN]; - AttributeTableEntry *t; + const AttributeTableEntry *t; int len, ret; #define stop(x) { if (no_spaces) free(no_spaces); return (x); } @@ -556,14 +727,29 @@ int nv_parse_attribute_string(const char *str, int query, ParsedAttribute *a) /* clear the ParsedAttribute struct */ memset((void *) a, 0, sizeof(ParsedAttribute)); + a->target_id = -1; + a->target_type = -1; /* remove any white space from the string, to simplify parsing */ no_spaces = remove_spaces(str); if (!no_spaces) stop(NV_PARSER_STATUS_EMPTY_STRING); - + + /* + * temporarily terminate the string at the equal sign, so that the + * DISPLAY_NAME_SEPARATOR search does not extend too far + */ + equal_sign = NULL; + if (query == NV_PARSER_ASSIGNMENT) { + equal_sign = strchr(no_spaces, '='); + } + + if (equal_sign) { + *equal_sign = '\0'; + } + /* - * get the display name... ie: everything before the + * get the display name... i.e.,: everything before the * DISPLAY_NAME_SEPARATOR */ @@ -576,14 +762,18 @@ int nv_parse_attribute_string(const char *str, int query, ParsedAttribute *a) */ if ((s) && (s != no_spaces)) { - + ret = nv_parse_display_and_target(no_spaces, s, a); if (ret != NV_PARSER_STATUS_SUCCESS) { stop(ret); } } - + + if (equal_sign) { + *equal_sign = '='; + } + /* move past the DISPLAY_NAME_SEPARATOR */ if (s) s++; @@ -710,202 +900,12 @@ int nv_parse_attribute_string(const char *str, int query, ParsedAttribute *a) /* - * nv_parse_display_and_target() - helper function for - * nv_parse_attribute_string() to parse all the text before the - * DISPLAY_NAME_SEPARATOR. This text is expected to be an X Display - * name, just an X screen, and/or a target specification. - */ - -static int nv_parse_display_and_target(char *start, - char *end, /* exclusive */ - ParsedAttribute *a) -{ - int digits_only, target_id, len; - char *tmp, *s, *pOpen, *pClose, *colon; - TargetTypeEntry *targetTypeEntry; - - /* - * are all characters numeric? compute the target_id integer as we - * scan the string to check - */ - - digits_only = NV_TRUE; - target_id = 0; - - for (s = start; s < end; s++) { - if (!isdigit(*s)) { - digits_only = NV_FALSE; - break; - } - target_id = (target_id * 10) + ctoi(*s); - } - - /* - * if all characters are numeric, assume the target type is - * X_SCREEN, and build the target_id; we have no X Display name in - * this case. - */ - - if (digits_only) { - a->display = NULL; - a->flags &= ~NV_PARSER_HAS_X_DISPLAY; - a->flags |= NV_PARSER_HAS_TARGET; - a->target_id = target_id; - a->target_type = NV_CTRL_TARGET_TYPE_X_SCREEN; - - /* we are done */ - - return NV_PARSER_STATUS_SUCCESS; - } - - /* - * if we get here, then there are non-digit characters; look for a - * pair of brackets, and treat the contents as a target - * specification. - */ - - pOpen = pClose = NULL; - - for (s = start; s < end; s++) { - if (*s == '[') pOpen = s; - if (*s == ']') pClose = s; - } - - if (pOpen && pClose && (pClose > pOpen) && ((pClose - pOpen) > 1)) { - - /* - * we have a pair of brackets and something inside the - * brackets, pull that into a temporary string. - */ - - len = pClose - pOpen - 1; - - tmp = nvstrndup(pOpen + 1, len); - - /* find the colon within the temp string */ - - colon = strchr(tmp, ':'); - - /* no colon? give up */ - - if (!colon) { - free(tmp); - return NV_PARSER_STATUS_TARGET_SPEC_NO_COLON; - } - - /* - * check that what is between the opening bracket and the - * colon is a target type name - */ - - *colon = '\0'; - - targetTypeEntry = nv_get_target_type_entry_by_name(tmp); - - *colon = ':'; - - /* if we did not find a matching target name, give up */ - - if (!targetTypeEntry) { - free(tmp); - return NV_PARSER_STATUS_TARGET_SPEC_BAD_TARGET; - } - - /* - * check that we have something between the colon and the end - * of the temp string - */ - - if ((colon + 1 - tmp) >= len) { - free(tmp); - return NV_PARSER_STATUS_TARGET_SPEC_NO_TARGET_ID; - } - - /* - * are all characters numeric? compute the target_id integer as we scan - * the string to check - */ - - digits_only = NV_TRUE; - target_id = 0; - - for (s = colon +1; *s; s++) { - if (!isdigit(*s)) { - digits_only = NV_FALSE; - break; - } - target_id = (target_id * 10) + ctoi(*s); - } - - /* - * if all characters are numeric, this is the target_id, otherwise, - * this is a target type-specific name. - */ - - a->target_type = targetTypeEntry->nvctrl; - - if (digits_only) { - a->target_id = target_id; - a->target_name = NULL; - } else { - a->target_id = -1; - a->target_name = strdup(colon +1); - } - - a->flags |= NV_PARSER_HAS_TARGET; - - /* we're finally done with the temp string */ - - free(tmp); - - /* - * check that there is no stray text between the closing - * bracket and the end of our parsable string - */ - - if ((end - pClose) > 1) { - return NV_PARSER_STATUS_TARGET_SPEC_TRAILING_GARBAGE; - } - - /* - * make end now point at the start of the bracketed target - * info for the X Display name processing below - */ - - end = pOpen; - } - - - /* treat everything between start and end as an X Display name */ - - if (start < end) { - - a->display = nvstrndup(start, end - start); - a->flags |= NV_PARSER_HAS_X_DISPLAY; - - /* - * this will attempt to parse out any screen number from the - * display name - */ - - nv_assign_default_display(a, NULL); - } - - /* done */ - - return NV_PARSER_STATUS_SUCCESS; - -} /* nv_parse_display_and_target() */ - - - -/* * nv_parse_strerror() - given the error status returned by * nv_parse_attribute_string(), return a string describing the * error. */ -char *nv_parse_strerror(int status) +const char *nv_parse_strerror(int status) { switch (status) { case NV_PARSER_STATUS_SUCCESS : @@ -940,7 +940,9 @@ char *nv_parse_strerror(int status) return "Bad target ID in target specification"; break; case NV_PARSER_STATUS_TARGET_SPEC_TRAILING_GARBAGE: return "Trailing garbage after target specification"; break; - + case NV_PARSER_STATUS_TARGET_SPEC_NO_TARGETS: + return "No targets match target specification"; break; + default: return "Unknown error"; break; } @@ -1100,7 +1102,7 @@ char *display_device_mask_to_display_device_name(const uint32 mask) uint32 devcnt, devmask; char *display_device_name_string; - display_device_name_string = malloc(DISPLAY_DEVICE_STRING_LEN); + display_device_name_string = nvalloc(DISPLAY_DEVICE_STRING_LEN); s = display_device_name_string; @@ -1195,8 +1197,7 @@ uint32 expand_display_device_mask_wildcards(const uint32 d, const uint32 e) void nv_assign_default_display(ParsedAttribute *a, const char *display) { - char *colon, *dot, *s; - int digits_only; + char *colon, *dot; if (!(a->flags & NV_PARSER_HAS_X_DISPLAY)) { if (display) a->display = strdup(display); @@ -1209,50 +1210,28 @@ void nv_assign_default_display(ParsedAttribute *a, const char *display) if (colon) { dot = strchr(colon, '.'); if (dot) { - + /* - * if all characters afer the '.' are digits, - * interpret it as a screen number. + * if all characters afer the '.' are digits, interpret it as a + * screen number. */ - - digits_only = NV_FALSE; - a->target_id = 0; - - for (s = dot + 1; *s; s++) { - if (isdigit(*s)) { - digits_only = NV_TRUE; - a->target_id = (a->target_id * 10) + ctoi(*s); - } else { - digits_only = NV_FALSE; - break; - } - } - - if (digits_only) { - a->target_type = NV_CTRL_TARGET_TYPE_X_SCREEN; - a->flags |= NV_PARSER_HAS_TARGET; - } + nv_parse_special_xscreen_target(a, dot + 1, NULL); } } } -} /* nv_assign_default_display() */ +} -/* +/* * nv_parsed_attribute_init() - initialize a ParsedAttribute linked * list */ ParsedAttribute *nv_parsed_attribute_init(void) { - ParsedAttribute *p = calloc(1, sizeof(ParsedAttribute)); - - p->next = NULL; - - return p; - -} /* nv_parsed_attribute_init() */ + return nvalloc(sizeof(ParsedAttribute)); +} @@ -1265,25 +1244,23 @@ void nv_parsed_attribute_add(ParsedAttribute *head, ParsedAttribute *a) { ParsedAttribute *p, *t; - p = calloc(1, sizeof(ParsedAttribute)); + p = nvalloc(sizeof(ParsedAttribute)); - p->next = NULL; - for (t = head; t->next; t = t->next); - + t->next = p; - + if (a->display) t->display = strdup(a->display); else t->display = NULL; - + t->target_type = a->target_type; t->target_id = a->target_id; t->attr = a->attr; t->val = a->val; t->display_device_mask = a->display_device_mask; t->flags = a->flags; - -} /* nv_parsed_attribute_add() */ + t->targets = a->targets; +} @@ -1294,15 +1271,15 @@ void nv_parsed_attribute_add(ParsedAttribute *head, ParsedAttribute *a) void nv_parsed_attribute_free(ParsedAttribute *p) { ParsedAttribute *n; - + while(p) { n = p->next; if (p->display) free(p->display); + nv_target_list_free(p->targets); free(p); p = n; } - -} /* nv_parsed_attribute_free() */ +} @@ -1383,7 +1360,7 @@ char *nv_standardize_screen_name(const char *orig, int screen) if (display_name == colon) { if (uname(&uname_buf) == 0) { len = strlen(display_name) + strlen(uname_buf.nodename) + 1; - tmp = malloc(len); + tmp = nvalloc(len); snprintf(tmp, len, "%s%s", uname_buf.nodename, display_name); free(display_name); display_name = tmp; @@ -1424,7 +1401,7 @@ char *nv_standardize_screen_name(const char *orig, int screen) screen_name = display_name; } else { len = strlen(display_name) + 8; - screen_name = malloc(len); + screen_name = nvalloc(len); snprintf(screen_name, len, "%s.%d", display_name, screen); free(display_name); } @@ -1449,7 +1426,7 @@ char *remove_spaces(const char *o) len = strlen (o); - no_spaces = malloc(len + 1); + no_spaces = nvalloc(len + 1); m = no_spaces; while (*o) { @@ -1483,7 +1460,7 @@ char *replace_characters(const char *o, const char c, const char r) len = strlen(o); - out = malloc(len + 1); + out = nvalloc(len + 1); m = out; while (*o != '\0') { @@ -1493,7 +1470,7 @@ char *replace_characters(const char *o, const char c, const char r) *m = '\0'; len = (m - out + 1); - out = realloc(out, len); + out = nvrealloc(out, len); return out; @@ -1526,7 +1503,7 @@ static char **nv_strtok(char *s, char c, int *n) * dividing character, and the terminating NULL of the string) */ - delims = malloc((count + 1) * sizeof(char *)); + delims = nvalloc((count + 1) * sizeof(char *)); m = s; for (i = 0; i < count; i++) { while (*m != c) m++; @@ -1540,7 +1517,7 @@ static char **nv_strtok(char *s, char c, int *n) * the divisions (the tokens) into the dynamic array of strings */ - tokens = malloc((count + 1) * sizeof(char *)); + tokens = nvalloc((count + 1) * sizeof(char *)); len = delims[0] - s; tokens[0] = nvstrndup(s, len); @@ -1749,10 +1726,7 @@ const char *parse_read_name(const char *str, char **name, char term) str++; } - *name = calloc(1, str - tmp + 1); - if (!(*name)) { - return NULL; - } + *name = nvalloc(str - tmp + 1); strncpy(*name, tmp, str -tmp); if (name_terminated(*str, term)) { str++; diff --git a/src/parse.h b/src/parse.h index bf71447..ea79383 100644 --- a/src/parse.h +++ b/src/parse.h @@ -84,7 +84,7 @@ #define NV_PARSER_STATUS_TARGET_SPEC_NO_TARGET_ID 13 #define NV_PARSER_STATUS_TARGET_SPEC_BAD_TARGET_ID 14 #define NV_PARSER_STATUS_TARGET_SPEC_TRAILING_GARBAGE 15 - +#define NV_PARSER_STATUS_TARGET_SPEC_NO_TARGETS 16 /* * define useful types @@ -115,6 +115,15 @@ typedef struct _AttributeTableEntry { typedef struct _ParsedAttribute { char *display; char *name; + char *target_specification; + /* + * The target_type and target_id here are mostly set by the GUI to store + * target-specific information, as well as the cmd line for handling the + * case where an X screen is specified as part of the display (e.g. + * "localhost:0.1"). Note that if the target_specification is specified, + * the target_type and target_id are ignored when resolving to the list of + * targets that should be operated on. + */ int target_type; int target_id; char *target_name; @@ -128,6 +137,12 @@ typedef struct _ParsedAttribute { uint32 display_device_mask; uint32 flags; struct _ParsedAttribute *next; + /* + * Upon being resolved, the ParsedAttribute's target_type and target_id, + * and/or target_specification get converted into a list of targets to + * which the attribute should be processed. + */ + struct _CtrlHandleTargetNode *targets; } ParsedAttribute; @@ -136,7 +151,7 @@ typedef struct _ParsedAttribute { * Attribute table; defined in parse.c */ -extern AttributeTableEntry attributeTable[]; +extern const AttributeTableEntry attributeTable[]; /* @@ -191,14 +206,15 @@ typedef struct { * TargetType table; defined in parse.c */ -extern TargetTypeEntry targetTypeTable[]; +extern const TargetTypeEntry targetTypeTable[]; +extern const int targetTypeTableLen; /* * accessor functions for getting target type info based on NV-CONTROL * attribute type or by a name. */ -TargetTypeEntry *nv_get_target_type_entry_by_nvctrl(int nvctrl); -TargetTypeEntry *nv_get_target_type_entry_by_name(const char *name); +const TargetTypeEntry *nv_get_target_type_entry_by_nvctrl(int nvctrl); +const TargetTypeEntry *nv_get_target_type_entry_by_name(const char *name); /* nv_get_sdi_csc_matrxi() - Returns an array of floats that specifies @@ -278,7 +294,7 @@ void nv_assign_default_display(ParsedAttribute *a, const char *display); * describing the error. */ -char *nv_parse_strerror(int); +const char *nv_parse_strerror(int); int nv_strcasecmp(const char *, const char *); @@ -333,6 +349,7 @@ char *nv_standardize_screen_name(const char *display_name, int screen); /* General parsing functions */ +int nv_parse_numerical(const char *start, const char *end, int *val); const char *parse_skip_whitespace(const char *str); void parse_chop_whitespace(char *str); const char *parse_skip_integer(const char *str); diff --git a/src/query-assign.c b/src/query-assign.c index 3ac0931..c44f494 100644 --- a/src/query-assign.c +++ b/src/query-assign.c @@ -29,6 +29,9 @@ #include <string.h> #include <inttypes.h> +#include <X11/Xlib.h> +#include "NVCtrlLib.h" + #include "parse.h" #include "msg.h" #include "query-assign.h" @@ -40,14 +43,18 @@ extern int __display_device_string; /* local prototypes */ -static int process_attribute_queries(int, char**, const char *); +static int process_attribute_queries(int, char**, const char *, + CtrlHandlesArray *); -static int process_attribute_assignments(int, char**, const char *); +static int process_attribute_assignments(int, char**, const char *, + CtrlHandlesArray *); -static int query_all(const char *); -static int query_all_targets(const char *display_name, const int target_index); +static int query_all(const char *, CtrlHandlesArray *); +static int query_all_targets(const char *display_name, const int target_index, + CtrlHandlesArray *); -static void print_valid_values(char *, int, uint32, NVCTRLAttributeValidValuesRec); +static void print_valid_values(const char *, int, uint32, + NVCTRLAttributeValidValuesRec); static void print_additional_info(const char *name, int attr, @@ -57,26 +64,33 @@ static void print_additional_info(const char *name, static int validate_value(CtrlHandleTarget *t, ParsedAttribute *a, uint32 d, int target_type, char *whence); +static CtrlHandles *nv_alloc_ctrl_handles(const char *display); + +static void nv_free_ctrl_handles(CtrlHandles *h); + /* * nv_process_assignments_and_queries() - process any assignments or * queries specified on the commandline. If an error occurs, return * NV_FALSE. On success return NV_TRUE. */ -int nv_process_assignments_and_queries(Options *op) +int nv_process_assignments_and_queries(const Options *op, + CtrlHandlesArray *handles_array) { int ret; if (op->num_queries) { ret = process_attribute_queries(op->num_queries, - op->queries, op->ctrl_display); + op->queries, op->ctrl_display, + handles_array); if (!ret) return NV_FALSE; } if (op->num_assignments) { ret = process_attribute_assignments(op->num_assignments, op->assignments, - op->ctrl_display); + op->ctrl_display, + handles_array); if (!ret) return NV_FALSE; } @@ -86,31 +100,129 @@ int nv_process_assignments_and_queries(Options *op) -/* - * query_display_target_names() - retrieves the list of display device names - * for the given target. +/*! + * Queries the NV-CONTROL string attribute and returns the string as a simple + * char *. This is useful to avoid having to track how strings are allocated + * so we can cleanup all strings via nvfree(). + * + * \param[in] t The CtrlHandleTarget to query the string on. + * \param[in] attr The NV-CONTROL string to query. + * + * \return Return a nvalloc()'ed copy of the NV-CONTROL string; else, returns + * NULL. */ -static void query_display_target_names(CtrlHandleTarget *t) + +static char *query_x_name(const CtrlHandleTarget *t, int attr) { - NvCtrlGetStringAttribute(t->h, NV_CTRL_STRING_DISPLAY_NAME_TYPE_BASENAME, - &(t->protoNames[NV_DPY_PROTO_NAME_TYPE_BASENAME])); - NvCtrlGetStringAttribute(t->h, NV_CTRL_STRING_DISPLAY_NAME_TYPE_ID, - &(t->protoNames[NV_DPY_PROTO_NAME_TYPE_ID])); - NvCtrlGetStringAttribute(t->h, NV_CTRL_STRING_DISPLAY_NAME_DP_GUID, - &(t->protoNames[NV_DPY_PROTO_NAME_DP_GUID])); - NvCtrlGetStringAttribute(t->h, NV_CTRL_STRING_DISPLAY_NAME_EDID_HASH, - &(t->protoNames[NV_DPY_PROTO_NAME_EDID_HASH])); - NvCtrlGetStringAttribute(t->h, NV_CTRL_STRING_DISPLAY_NAME_TARGET_INDEX, - &(t->protoNames[NV_DPY_PROTO_NAME_TARGET_INDEX])); - NvCtrlGetStringAttribute(t->h, NV_CTRL_STRING_DISPLAY_NAME_RANDR, - &(t->protoNames[NV_DPY_PROTO_NAME_RANDR])); + ReturnStatus status; + char *x_str; + char *str = NULL; + + status = NvCtrlGetStringAttribute(t->h, attr, &x_str); + if (status == NvCtrlSuccess) { + str = nvstrdup(x_str); + XFree(x_str); + } + + return str; } -NvCtrlAttributeHandle *nv_get_target_handle(CtrlHandles *handles, - int target_type, - int target_id) + + +/*! + * Retrieves and adds all the display device names for the given target. + * + * \param[in] t The CtrlHandleTarget to load names for. + */ + +static void load_display_target_proto_names(CtrlHandleTarget *t) +{ + t->protoNames[NV_DPY_PROTO_NAME_TYPE_BASENAME] = + query_x_name(t, NV_CTRL_STRING_DISPLAY_NAME_TYPE_BASENAME); + + t->protoNames[NV_DPY_PROTO_NAME_TYPE_ID] = + query_x_name(t, NV_CTRL_STRING_DISPLAY_NAME_TYPE_ID); + + t->protoNames[NV_DPY_PROTO_NAME_DP_GUID] = + query_x_name(t, NV_CTRL_STRING_DISPLAY_NAME_DP_GUID); + + t->protoNames[NV_DPY_PROTO_NAME_EDID_HASH] = + query_x_name(t, NV_CTRL_STRING_DISPLAY_NAME_EDID_HASH); + + t->protoNames[NV_DPY_PROTO_NAME_TARGET_INDEX] = + query_x_name(t, NV_CTRL_STRING_DISPLAY_NAME_TARGET_INDEX); + + t->protoNames[NV_DPY_PROTO_NAME_RANDR] = + query_x_name(t, NV_CTRL_STRING_DISPLAY_NAME_RANDR); +} + + + +/*! + * Adds the default names for the given target to the list of protocal names. + * + * \param[in] t The CtrlHandleTarget to load names for. + */ + +static void load_default_target_proto_name(CtrlHandleTarget *t) +{ + const int target_type = NvCtrlGetTargetType(t->h); + const int target_id = NvCtrlGetTargetId(t->h); + + + const TargetTypeEntry *targetTypeEntry = + nv_get_target_type_entry_by_nvctrl(target_type); + + if (targetTypeEntry) { + t->protoNames[0] = nvasprintf("%s-%d", + targetTypeEntry->parsed_name, + target_id); + nvstrtoupper(t->protoNames[0]); + } +} + + + +/*! + * Adds the all the appropriate names for the given target to the list of protocal names. + * + * \param[in] t The CtrlHandleTarget to load names for. + */ + +static void load_target_proto_names(CtrlHandleTarget *t) +{ + const int target_type = NvCtrlGetTargetType(t->h); + + switch (target_type) { + case NV_CTRL_TARGET_TYPE_DISPLAY: + load_display_target_proto_names(t); + break; + + default: + load_default_target_proto_name(t); + break; + } +} + + + +/*! + * Returns the CtrlHandleTarget from CtrlHandles with the given target type/ + * target id. + * + * \param[in] handles Container for all the CtrlHandleTargets to search. + * \param[in] target_type The target type of the CtrlHandleTarget to search. + * \param[in] target_id The target id of the CtrlHandleTarget to search. + * + * \return Returns the matching CtrlHandleTarget from CtrlHandles on success; + * else, returns NULL. + */ + +static CtrlHandleTarget *nv_get_target(const CtrlHandles *handles, + int target_type, + int target_id) { - CtrlHandleTargets *targets; + const CtrlHandleTargets *targets; int i; if (target_type < 0 || target_type >= MAX_TARGET_TYPES) { @@ -121,7 +233,7 @@ NvCtrlAttributeHandle *nv_get_target_handle(CtrlHandles *handles, for (i = 0; i < targets->n; i++) { CtrlHandleTarget *target = targets->t + i; if (NvCtrlGetTargetId(target->h) == target_id) { - return target->h; + return target; } } @@ -129,6 +241,601 @@ NvCtrlAttributeHandle *nv_get_target_handle(CtrlHandles *handles, } + +/*! + * Returns the NvCtrlAttributeHandle from CtrlHandles with the given target + * type/target id. + * + * \param[in] handles Container for all the CtrlHandleTargets to search. + * \param[in] target_type The target type of the CtrlHandleTarget to search. + * \param[in] target_id The target id of the CtrlHandleTarget to search. + * + * \return Returns the NvCtrlAttributeHandle from the matching + * CtrlHandleTarget from CtrlHandles on success; else, returns NULL. + */ + +NvCtrlAttributeHandle *nv_get_target_handle(const CtrlHandles *handles, + int target_type, + int target_id) +{ + CtrlHandleTarget *target; + + target = nv_get_target(handles, target_type, target_id); + if (target) { + return target->h; + } + + return NULL; +} + + + +/*! + * Returns the first node (sentry) for tracking CtrlHandleTarget lists. + * + * \return Returns the sentry node to be used for tracking CtrlHandleTarget + * list. + */ + +static CtrlHandleTargetNode *nv_target_list_init(void) +{ + return nvalloc(sizeof(CtrlHandleTargetNode)); +} + + + +/*! + * Appends the given CtrlHandleTarget 'target' to the end of the + * CtrlHandleTarget list 'head' if 'target' is not already in the list. + * + * \param[in/out] head The first node in the CtrlHandleTarget list, to which + * target should be inserted. + * \param[in] target The CtrlHandleTarget to add to the list. + */ + +static void nv_target_list_add(CtrlHandleTargetNode *head, + CtrlHandleTarget *target) +{ + CtrlHandleTargetNode *t; + + for (t = head; t->next; t = t->next) { + if (t->t == target) { + /* Already in list, ignore */ + return; + } + } + + t->next = nv_target_list_init(); + t->t = target; +} + + + +/*! + * Frees the memory used for tracking a list of CtrlHandleTargets. + * + * \param[in\out] head The first node in the CtrlHandleTarget list that is to + * be freed. + */ + +void nv_target_list_free(CtrlHandleTargetNode *head) +{ + CtrlHandleTargetNode *n; + + while (head) { + n = head->next; + free(head); + head = n; + } +} + + + +/*! + * Adds all the targets (of target type 'targetType') that are both present + * in the CtrlHandle, and known to be associated to 't' by querying the list + * of associated targets to 't' from NV-CONTROL via binary attribute 'attr', + * and possibly reciprocally adding the reverse relationship. + * + * \param[in\out] h The list of all managed targets from + * which associations are to be made to/ + * from. + * \param[in\out] t The target to which association(s) are + * being made. + * \param[in] targetType The target type of the associated + * targets being considered (queried.) + * \param[in] attr The NV-CONTROL binary attribute that + * should be queried to retrieve the list + * of 'targetType' targets that are + * associated to the (CtrlHandleTarget) + * target 't'. + * \param[in] implicit_reciprocal Whether or not to reciprocally add the + * reverse relationship to the matching + * targets. + */ + +static void add_target_relationships(const CtrlHandles *h, CtrlHandleTarget *t, + int targetType, int attr, + int implicit_reciprocal) +{ + ReturnStatus status; + int *pData; + int len; + int i; + + status = + NvCtrlGetBinaryAttribute(t->h, 0, + attr, + (unsigned char **)(&pData), &len); + if ((status != NvCtrlSuccess) || !pData) { + nv_error_msg("Error querying target relations"); + return; + } + + for (i = 0; i < pData[0]; i++) { + int targetId = pData[i+1]; + CtrlHandleTarget *r; + + r = nv_get_target(h, targetType, targetId); + if (r) { + nv_target_list_add(t->relations, r); + + if (implicit_reciprocal == NV_TRUE) { + nv_target_list_add(r->relations, t); + } + } + } + + XFree(pData); +} + + + +/*! + * Adds all associations to/from an X screen target. + * + * \param[in\out] h The list of all managed targets from which associations + * are to be made to/from. + * \param[in\out] t The X screen target to which association(s) are being + * made. + */ + +static void load_screen_target_relationships(CtrlHandles *h, + CtrlHandleTarget *t) +{ + add_target_relationships(h, t, NV_CTRL_TARGET_TYPE_GPU, + NV_CTRL_BINARY_DATA_GPUS_USED_BY_LOGICAL_XSCREEN, + NV_TRUE); + add_target_relationships(h, t, NV_CTRL_TARGET_TYPE_DISPLAY, + NV_CTRL_BINARY_DATA_DISPLAYS_ASSIGNED_TO_XSCREEN, + NV_TRUE); +} + + + +/*! + * Adds all associations to/from a GPU target. + * + * \param[in\out] h The list of all managed targets from which associations + * are to be made to/from. + * \param[in\out] t The GPU target to which association(s) are being made. + */ + +static void load_gpu_target_relationships(CtrlHandles *h, CtrlHandleTarget *t) +{ + add_target_relationships(h, t, NV_CTRL_TARGET_TYPE_FRAMELOCK, + NV_CTRL_BINARY_DATA_FRAMELOCKS_USED_BY_GPU, + NV_FALSE); + add_target_relationships(h, t, NV_CTRL_TARGET_TYPE_VCSC, + NV_CTRL_BINARY_DATA_VCSCS_USED_BY_GPU, + NV_FALSE); + add_target_relationships(h, t, NV_CTRL_TARGET_TYPE_COOLER, + NV_CTRL_BINARY_DATA_COOLERS_USED_BY_GPU, + NV_TRUE); + add_target_relationships(h, t, NV_CTRL_TARGET_TYPE_THERMAL_SENSOR, + NV_CTRL_BINARY_DATA_THERMAL_SENSORS_USED_BY_GPU, + NV_TRUE); + add_target_relationships(h, t, NV_CTRL_TARGET_TYPE_DISPLAY, + NV_CTRL_BINARY_DATA_DISPLAYS_CONNECTED_TO_GPU, + NV_TRUE); +} + + + +/*! + * Adds all associations to/from a FrameLock target. + * + * \param[in\out] h The list of all managed targets from which associations + * are to be made to/from. + * \param[in\out] t The FrameLock target to which association(s) are being + * made. + */ + +static void load_framelock_target_relationships(CtrlHandles *h, + CtrlHandleTarget *t) +{ + add_target_relationships(h, t, NV_CTRL_TARGET_TYPE_GPU, + NV_CTRL_BINARY_DATA_GPUS_USING_FRAMELOCK, + NV_FALSE); +} + + + +/*! + * Adds all associations to/from a VCS target. + * + * \param[in\out] h The list of all managed targets from which associations + * are to be made to/from. + * \param[in\out] t The VCS target to which association(s) are being made. + */ + +static void load_vcs_target_relationships(CtrlHandles *h, CtrlHandleTarget *t) +{ + add_target_relationships(h, t, NV_CTRL_TARGET_TYPE_GPU, + NV_CTRL_BINARY_DATA_GPUS_USING_VCSC, + NV_FALSE); +} + + + +/*! + * Adds all associations to/from a target. + * + * \param[in\out] h The list of all managed targets from which associations + * are to be made to/from. + * \param[in\out] t The target to which association(s) are being made. + */ + +static void load_target_relationships(CtrlHandles *h, CtrlHandleTarget *t) +{ + int target_type = NvCtrlGetTargetType(t->h); + + switch (target_type) { + case NV_CTRL_TARGET_TYPE_X_SCREEN: + load_screen_target_relationships(h, t); + break; + + case NV_CTRL_TARGET_TYPE_GPU: + load_gpu_target_relationships(h, t); + break; + + case NV_CTRL_TARGET_TYPE_FRAMELOCK: + load_framelock_target_relationships(h, t); + break; + + case NV_CTRL_TARGET_TYPE_VCSC: + load_vcs_target_relationships(h, t); + break; + + default: + break; + } +} + + + +/*! + * Determines if the target 't' has the name 'name'. + * + * \param[in] t The target being considered. + * \param[in] name The name to match against. + * + * \return Returns NV_TRUE if the given target 't' has the name 'name'; else + * returns NV_FALSE. + */ + +static int nv_target_has_name(const CtrlHandleTarget *t, const char *name) +{ + int n; + + for (n = 0; n < NV_DPY_PROTO_NAME_MAX; n++) { + if (t->protoNames[n] && + nv_strcasecmp(t->protoNames[n], name)) { + return NV_TRUE; + } + } + + return NV_FALSE; +} + + + +/*! + * Determines if the target 't' matches a given target type, target id, and/or + * target name. + * + * \param[in] t The target being considered. + * \param[in] name The name to match against. + * + * \return Returns NV_TRUE if the given target 't' has the name 'name'; else + * returns NV_FALSE. + */ + +static int target_has_qualification(const CtrlHandleTarget *t, + int matchTargetType, + int matchTargetId, + const char *matchTargetName) +{ + const CtrlHandleTargetNode *n; + + /* If no qualifications given, all targets match */ + if ((matchTargetType < 0) && (matchTargetId < 0) && (!matchTargetName)) { + return NV_TRUE; + } + + /* Look for any matching relationship */ + for (n = t->relations; + n->next; + n = n->next) { + const CtrlHandleTarget *r = n->t; + + if (matchTargetType >= 0 && + (matchTargetType != NvCtrlGetTargetType(r->h))) { + continue; + } + + if ((matchTargetId >= 0) && + matchTargetId != NvCtrlGetTargetId(r->h)) { + continue; + } + + if (matchTargetName && + !nv_target_has_name(r, matchTargetName)) { + continue; + } + + return NV_TRUE; + } + + return NV_FALSE; +} + + + +/*! + * Resolves the two given strings sAAA and sBBB into a target type, target id, + * and/or target name. The following target specifications are supported: + * + * "AAA" (BBB = NULL) + * - All targets of type AAA, if AAA names a target type, or + * - All targets named AAA if AAA does not name a target type. + * + * "BBB:AAA" + * - All targets of type BBB with either target id AAA if AAA is a numerical + * value, or target(s) named AAA otherwise. + * + * \param[in] sAAA If sBBB is NULL, this is either the target name, + * or a target type. + * \param[in] sBBB If not NULL, this is the target type as a string. + * \param[out] targetType Assigned the target type, or -1 for all target types. + * \param[out] targetId Assigned the target id, or -1 for all targets. + * \param[out] targetName Assigned the target name, or NULL for all target + * names. + * + * \return Returns NV_PARSER_STATUS_SUCCESS if sAAA and sBBB were successfuly + * parsed into at target specification; else, returns + * NV_PARSER_STATUS_TARGET_SPEC_BAD_TARGET if a parsing failure + * occured. + */ + +static int parse_single_target_specification(const char *sAAA, + const char *sBBB, + int *targetType, + int *targetId, + const char **targetName) +{ + const TargetTypeEntry *targetTypeEntry; + + *targetType = -1; // Match all target types + *targetId = -1; // Match all target ids + *targetName = NULL; // Match all target names + + if (sBBB) { + + /* If BBB is specified, then it must name a target type */ + targetTypeEntry = nv_get_target_type_entry_by_name(sBBB); + if (!targetTypeEntry) { + return NV_PARSER_STATUS_TARGET_SPEC_BAD_TARGET; + } + *targetType = targetTypeEntry->nvctrl; + + /* AAA can either be a name, or a target id */ + if (!nv_parse_numerical(sAAA, NULL, targetId)) { + *targetName = sAAA; + } + + } else { + + /* If BBB is unspecified, then AAA could possibly name a target type */ + targetTypeEntry = nv_get_target_type_entry_by_name(sAAA); + if (targetTypeEntry) { + *targetType = targetTypeEntry->nvctrl; + } else { + /* If not, then AAA is a target name */ + *targetName = sAAA; + } + } + + return NV_PARSER_STATUS_SUCCESS; +} + + + + +/*! + * Computes the list of targets from the list of CtrlHandles 'h' that match the + * ParsedAttribute's target specification string. + * + * The following target specifications string formats are supported: + * + * "AAA" + * - All targets of type AAA, if AAA names a target type, or + * - All targets named AAA if AAA does not name a target type. + * + * "BBB:AAA" + * - All targets of type BBB with either target id AAA if AAA is a numerical + * value, or target(s) named AAA otherwise. + * + * "CCC.AAA" + * - All target types (and/or names) AAA that have a relation to any target + * types (and/or names) CCC. + * + * "CCC.BBB:AAA" + * - All the targets named (or target id) AAA of the target type BBB that are + * related to target type (and or name) CCC. + * + * "DDD:CCC.AAA" + * - All target types (and/or names) AAA that are related to targets named + * (or target id) CCC of the target type DDD. + * + * "DDD:CCC.BBB:AAA" + * - All the targets named (or target id) AAA of the target type BBB that are + * related to targets named (or target id) CCC of the target type DDD. + * + * \param[in/ou] a The ParsedAttribute whose target specification string + * should be analyzed and converted into a list of targets. + * \param[in] h The list of targets to choose from. + * + * \return Returns NV_PARSER_STATUS_SUCCESS if the ParsedAttribute's target + * specification string was successfully prased into a list of targets + * (though this list could be empty, if no targets are found to + * match!); else, returns one of the other NV_PARSER_STATUS_XXX + * error codes that detail the particular parsing error. + */ + +static int nv_infer_targets_from_specification(ParsedAttribute *a, + CtrlHandles *h) +{ + int ret = NV_PARSER_STATUS_SUCCESS; + + /* specification as: "XXX.YYY" or "XXX" */ + char *sXXX = NULL; // Target1 string + char *sYYY = NULL; // Target2 string + + /* XXX as: "BBB:AAA" or "AAA" */ + char *sAAA = NULL; // Target1 name + char *sBBB = NULL; // Target1 type name + + /* YYY as: "DDD:CCC" or "CCC" */ + char *sCCC = NULL; // Target2 name + char *sDDD = NULL; // Target2type name + + char *s; + char *tmp; + + int i, j; + + int matchTargetType; + int matchTargetId; + const char *matchTargetName; + + int matchQualifierTargetType; + int matchQualifierTargetId; + const char *matchQualifierTargetName; + + + tmp = nvstrdup(a->target_specification); + + /* Parse for 'YYY.XXX' or 'XXX' */ + s = strchr(tmp, '.'); + if (s) { + *s = '\0'; + sXXX = s+1; + sYYY = tmp; + } else { + sXXX = tmp; + } + + /* Parse for 'BBB:AAA' or 'AAA' (in XXX above) */ + s = strchr(sXXX, ':'); + if (s) { + *s = '\0'; + sBBB = sXXX; + sAAA = s+1; + } else { + /* AAA is either a target type name, or a target name */ + sAAA = sXXX; + } + + /* Parse for 'DDD:CCC' or 'CCC' (in YYY above) */ + if (sYYY) { + s = strchr(sYYY, ':'); + if (s) { + *s = '\0'; + sDDD = sYYY; + sCCC = s+1; + } else { + /* CCC is either a target type name, or a target name */ + sCCC = sYYY; + } + } + + /* Get target matching criteria */ + + ret = parse_single_target_specification(sAAA, sBBB, + &matchTargetType, + &matchTargetId, + &matchTargetName); + if (ret != NV_PARSER_STATUS_SUCCESS) { + goto done; + } + + /* Get target qualifier matching criteria */ + + ret = parse_single_target_specification(sCCC, sDDD, + &matchQualifierTargetType, + &matchQualifierTargetId, + &matchQualifierTargetName); + if (ret != NV_PARSER_STATUS_SUCCESS) { + goto done; + } + + /* Iterate over the target types */ + for (i = 0; i < targetTypeTableLen; i++) { + const TargetTypeEntry *targetTypeEntry = &(targetTypeTable[i]); + CtrlHandleTargets *hts; + + if (matchTargetType >= 0 && + (matchTargetType != targetTypeEntry->nvctrl)) { + continue; + } + + /* For each target of this type, match the id and/or name */ + hts = &(h->targets[targetTypeEntry->target_index]); + for (j = 0; j < hts->n; j++) { + CtrlHandleTarget *t = &(hts->t[j]); + + if ((matchTargetId >= 0) && + matchTargetId != NvCtrlGetTargetId(t->h)) { + continue; + } + if (matchTargetName && + !nv_target_has_name(t, matchTargetName)) { + continue; + } + + if (!target_has_qualification(t, + matchQualifierTargetType, + matchQualifierTargetId, + matchQualifierTargetName)) { + continue; + } + + /* Target matches, add it to the list */ + nv_target_list_add(a->targets, t); + a->flags |= NV_PARSER_HAS_TARGET; + } + } + + done: + if (tmp) { + free(tmp); + } + return ret; +} + + + /* * nv_alloc_ctrl_handles() - allocate a new CtrlHandles structure, * connect to the X server identified by display, and initialize an @@ -141,17 +848,13 @@ CtrlHandles *nv_alloc_ctrl_handles(const char *display) ReturnStatus status; CtrlHandles *h, *pQueryHandle = NULL; NvCtrlAttributeHandle *handle; - int i, val, d, c, len; + int i, j, val, d, c, len; char *tmp; int *pData = NULL; - TargetTypeEntry *targetTypeEntry; /* allocate the CtrlHandles struct */ - h = calloc(1, sizeof(CtrlHandles)); - if (!h) { - return NULL; - } + h = nvalloc(sizeof(CtrlHandles)); /* store any given X display name */ @@ -175,10 +878,8 @@ CtrlHandles *nv_alloc_ctrl_handles(const char *display) * information */ - for (targetTypeEntry = targetTypeTable; - targetTypeEntry->name; - targetTypeEntry++) { - + for (j = 0; j < targetTypeTableLen; j++) { + const TargetTypeEntry *targetTypeEntry = &targetTypeTable[j]; int target = targetTypeEntry->target_index; CtrlHandleTargets *targets = &(h->targets[target]); @@ -267,11 +968,7 @@ CtrlHandles *nv_alloc_ctrl_handles(const char *display) /* allocate an array of CtrlHandleTarget's */ - targets->t = calloc(targets->n, sizeof(CtrlHandleTarget)); - if (!targets->t) { - targets->n = 0; - goto next_target_type; - } + targets->t = nvalloc(targets->n * sizeof(CtrlHandleTarget)); /* * loop over all the targets of this type and setup the @@ -279,6 +976,7 @@ CtrlHandles *nv_alloc_ctrl_handles(const char *display) */ for (i = 0; i < targets->n; i++) { + CtrlHandleTarget *t = &(targets->t[i]); int targetId; switch (target) { @@ -304,7 +1002,7 @@ CtrlHandles *nv_alloc_ctrl_handles(const char *display) targetTypeEntry->nvctrl, targetId, NV_CTRL_ATTRIBUTES_ALL_SUBSYSTEMS); - targets->t[i].h = handle; + t->h = handle; /* * silently fail: this might happen if not all X screens @@ -325,24 +1023,24 @@ CtrlHandles *nv_alloc_ctrl_handles(const char *display) tmp = NvCtrlGetDisplayName(handle); if (target == X_SCREEN_TARGET) { - targets->t[i].name = tmp; + t->name = tmp; } else { len = strlen(tmp) + strlen(targetTypeEntry->parsed_name) +16; - targets->t[i].name = malloc(len); + t->name = nvalloc(len); - if (targets->t[i].name) { - snprintf(targets->t[i].name, len, "%s[%s:%d]", - tmp, targetTypeEntry->parsed_name, i); + if (t->name) { + snprintf(t->name, len, "%s[%s:%d]", + tmp, targetTypeEntry->parsed_name, targetId); free(tmp); } else { - targets->t[i].name = tmp; - } - - if (target == DISPLAY_TARGET) { - query_display_target_names(&(targets->t[i])); + t->name = tmp; } } + load_target_proto_names(t); + t->relations = nv_target_list_init(); + + /* * get the enabled display device mask; for X screens and * GPUs we query NV-CONTROL; for anything else @@ -375,8 +1073,8 @@ CtrlHandles *nv_alloc_ctrl_handles(const char *display) c = 0; } - targets->t[i].d = d; - targets->t[i].c = c; + t->d = d; + t->c = c; /* * store this handle so that we can use it to query other @@ -393,25 +1091,65 @@ CtrlHandles *nv_alloc_ctrl_handles(const char *display) } } + + /* Load relationships to other targets */ + + for (i = 0; i < MAX_TARGET_TYPES; i++) { + CtrlHandleTargets *targets = &(h->targets[i]); + for (j = 0; j < targets->n; j++) { + CtrlHandleTarget *t = &(targets->t[j]); + load_target_relationships(h, t); + } + } + return h; } /* nv_alloc_ctrl_handles() */ /* + * nv_alloc_ctrl_handles_and_add_to_array() - if it doesn't exist, allocate a new + * CtrlHandles structure via nv_alloc_ctrl_handles and add it to the + * CtrlHandlesArray given and return the newly allocated handle. If it + * does exist, simply return the existing handle. + */ + +CtrlHandles * + nv_alloc_ctrl_handles_and_add_to_array(const char *display, + CtrlHandlesArray *handles_array) +{ + CtrlHandles *handle = nv_get_ctrl_handles(display, handles_array); + + if (handle == NULL) { + handle = nv_alloc_ctrl_handles(display); + + if (handle) { + handles_array->array = nvrealloc(handles_array->array, + sizeof(CtrlHandles *) + * (handles_array->n + 1)); + handles_array->array[handles_array->n] = handle; + handles_array->n++; + } + } + + return handle; +} + + + +/* * nv_free_ctrl_handles() - free the CtrlHandles structure allocated * by nv_alloc_ctrl_handles() */ -void nv_free_ctrl_handles(CtrlHandles *h) +static void nv_free_ctrl_handles(CtrlHandles *h) { - TargetTypeEntry *targetTypeEntry; - if (!h) return; if (h->display) free(h->display); if (h->dpy) { + int i; /* * XXX It is unfortunate that the display connection needs @@ -424,16 +1162,14 @@ void nv_free_ctrl_handles(CtrlHandles *h) XCloseDisplay(h->dpy); h->dpy = NULL; - for (targetTypeEntry = targetTypeTable; - targetTypeEntry->name; - targetTypeEntry++) { + for (i = 0; i < targetTypeTableLen; i++) { CtrlHandleTargets *targets = - &(h->targets[targetTypeEntry->target_index]); - int i; + &(h->targets[targetTypeTable[i].target_index]); + int j; - for (i = 0; i < targets->n; i++) { - CtrlHandleTarget *t = &(targets->t[i]); + for (j = 0; j < targets->n; j++) { + CtrlHandleTarget *t = &(targets->t[j]); int n; NvCtrlAttributeClose(t->h); @@ -454,6 +1190,209 @@ void nv_free_ctrl_handles(CtrlHandles *h) } /* nv_free_ctrl_handles() */ +/* + * nv_free_ctrl_handles_array() - free an array of CtrlHandles. + */ + +void nv_free_ctrl_handles_array(CtrlHandlesArray *handles_array) +{ + int i; + + for (i = 0; i < handles_array->n; i++) { + nv_free_ctrl_handles(handles_array->array[i]); + } + + if (handles_array->array) { + free(handles_array->array); + } + + handles_array->n = 0; + handles_array->array = NULL; +} + + +/* + * nv_get_ctrl_handles() - return the CtrlHandles matching the given string. + */ +CtrlHandles *nv_get_ctrl_handles(const char *display, + CtrlHandlesArray *handles_array) +{ + int i; + + for (i=0; i < handles_array->n; i++) { + if (nv_strcasecmp(display, handles_array->array[i]->display)) { + return handles_array->array[i]; + } + } + + return NULL; +} + + +/*! + * Adds all the targets of the target type (specified via a target type index) + * to the list of targets to process for the ParsedAttribute. + * + * \param[in/out] a The ParsedAttribute to add targets to. + * \param[in] h The list of targets to add from. + * \param[in] targetIdx The target type index of the targets to add. + */ + +static void include_target_idx_targets(ParsedAttribute *a, const CtrlHandles *h, + int targetIdx) +{ + const CtrlHandleTargets *targets = &(h->targets[targetIdx]); + int i; + + for (i = 0; i < targets->n; i++) { + CtrlHandleTarget *target = &(targets->t[i]); + nv_target_list_add(a->targets, target); + a->flags |= NV_PARSER_HAS_TARGET; + } +} + + +/*! + * Queries the permissions for the given attribute. + * \param[in] a The attribute to query permissions for. + * \param[in] h CtrlHandles used to communicate with the X server. + * \param[out] perms The permissions of the attribute. + * + * \return Returns TRUE if the permissions were queried successfully; else, + * returns FALSE. + */ +static Bool query_attribute_perms(ParsedAttribute *a, CtrlHandles *h, + NVCTRLAttributePermissionsRec *perms) +{ + memset(perms, 0, sizeof(*perms)); + + if (a->flags & NV_PARSER_TYPE_COLOR_ATTRIBUTE) { + /* Allow non NV-CONTROL attributes to be read/written on X screen + * targets + */ + perms->type = ATTRIBUTE_TYPE_INTEGER; + perms->permissions = + ATTRIBUTE_TYPE_READ | + ATTRIBUTE_TYPE_WRITE | + ATTRIBUTE_TYPE_X_SCREEN; + + return NV_TRUE; + } + + if (a->flags & NV_PARSER_TYPE_STRING_ATTRIBUTE) { + return XNVCTRLQueryStringAttributePermissions(h->dpy, a->attr, perms); + } + + return XNVCTRLQueryAttributePermissions(h->dpy, a->attr, perms); +} + + + +/*! + * Converts the ParsedAttribute 'a''s target specification (and/or target type + * + id) into a list of CtrlHandleTarget to operate on. If the ParsedAttribute + * has a target specification set, this is used to generate the list; Otherwise, + * the target type and target id are used. If nothing is specified, all the + * valid targets for the attribute are included. + * + * \param[in/out] a ParsedAttribute to resolve. + * \param[in] h CtrlHandles to resolve the target specification against. + * + * \return Return NV_PARSER_STATUS_SUCCESS if the attribute's target + * specification was successfully parsed into a list of targets to + * operate on; else, returns one of the other NV_PARSER_STATUS_XXX + * error codes that detail the particular parsing error. + */ + +static int resolve_attribute_targets(ParsedAttribute *a, CtrlHandles *h) +{ + NVCTRLAttributePermissionsRec perms; + Bool status; + int ret = NV_PARSER_STATUS_SUCCESS; + int i; + + if (a->targets) { + // Oops already parsed? + // XXX thrown another error here? + return NV_PARSER_STATUS_BAD_ARGUMENT; + } + + a->targets = nv_target_list_init(); + + + + /* If a target specification string was given, use that to determine the + * list of targets to include. + */ + if (a->target_specification) { + ret = nv_infer_targets_from_specification(a, h); + goto done; + } + + + /* If the target type and target id was given, use that. */ + if (a->target_type >= 0 && a->target_id >= 0) { + CtrlHandleTarget *target = nv_get_target(h, a->target_type, + a->target_id); + if (!target) { + return NV_PARSER_STATUS_TARGET_SPEC_NO_TARGETS; + } + + nv_target_list_add(a->targets, target); + a->flags |= NV_PARSER_HAS_TARGET; + goto done; + } + + + /* If a target type was given, but no target id, process all the targets + * of that type. + */ + if (a->target_type >= 0) { + const TargetTypeEntry *targetTypeEntry = + nv_get_target_type_entry_by_nvctrl(a->target_type); + + if (!targetTypeEntry) { + return NV_PARSER_STATUS_TARGET_SPEC_BAD_TARGET; + } + + include_target_idx_targets(a, h, targetTypeEntry->target_index); + goto done; + } + + + /* If no target type was given, assume all the appropriate targets for the + * attribute by querying its permissions. + */ + status = query_attribute_perms(a, h, &perms); + if (!status) { + // XXX Throw other error here...? + return NV_PARSER_STATUS_TARGET_SPEC_NO_TARGETS; + } + + for (i = 0; i < MAX_TARGET_TYPES; i++) { + int permBit = targetTypeTable[i].permission_bit; + + if (!(perms.permissions & permBit)) { + continue; + } + + /* Add all targets of type that are valid for this attribute */ + include_target_idx_targets(a, h, i); + } + + + done: + /* Make sure at least one target was resolved */ + if (ret == NV_PARSER_STATUS_SUCCESS) { + if (!(a->flags & NV_PARSER_HAS_TARGET)) { + return NV_PARSER_STATUS_TARGET_SPEC_NO_TARGETS; + } + } + + return ret; +} + + /* * process_attribute_queries() - parse the list of queries, and call @@ -461,14 +1400,11 @@ void nv_free_ctrl_handles(CtrlHandles *h) * * If any errors are encountered, an error message is printed and * NV_FALSE is returned. Otherwise, NV_TRUE is returned. - * - * XXX rather than call nv_alloc_ctrl_handles()/nv_free_ctrl_handles() - * for every query, we should share the code in - * process_config_file_attributes() to collapse the list of handles. */ static int process_attribute_queries(int num, char **queries, - const char *display_name) + const char *display_name, + CtrlHandlesArray *handles_array) { int query, ret, val; ParsedAttribute a; @@ -487,7 +1423,7 @@ static int process_attribute_queries(int num, char **queries, /* special case the "all" query */ if (nv_strcasecmp(queries[query], "all")) { - query_all(display_name); + query_all(display_name, handles_array); continue; } @@ -495,47 +1431,49 @@ static int process_attribute_queries(int num, char **queries, if (nv_strcasecmp(queries[query], "screens") || nv_strcasecmp(queries[query], "xscreens")) { - query_all_targets(display_name, X_SCREEN_TARGET); + query_all_targets(display_name, X_SCREEN_TARGET, handles_array); continue; } if (nv_strcasecmp(queries[query], "gpus")) { - query_all_targets(display_name, GPU_TARGET); + query_all_targets(display_name, GPU_TARGET, handles_array); continue; } if (nv_strcasecmp(queries[query], "framelocks")) { - query_all_targets(display_name, FRAMELOCK_TARGET); + query_all_targets(display_name, FRAMELOCK_TARGET, handles_array); continue; } if (nv_strcasecmp(queries[query], "vcs")) { - query_all_targets(display_name, VCS_TARGET); + query_all_targets(display_name, VCS_TARGET, handles_array); continue; } if (nv_strcasecmp(queries[query], "gvis")) { - query_all_targets(display_name, GVI_TARGET); + query_all_targets(display_name, GVI_TARGET, handles_array); continue; } if (nv_strcasecmp(queries[query], "fans")) { - query_all_targets(display_name, COOLER_TARGET); + query_all_targets(display_name, COOLER_TARGET, handles_array); continue; } if (nv_strcasecmp(queries[query], "thermalsensors")) { - query_all_targets(display_name, THERMAL_SENSOR_TARGET); + query_all_targets(display_name, THERMAL_SENSOR_TARGET, handles_array); continue; } if (nv_strcasecmp(queries[query], "svps")) { - query_all_targets(display_name, NVIDIA_3D_VISION_PRO_TRANSCEIVER_TARGET); + query_all_targets(display_name, + NVIDIA_3D_VISION_PRO_TRANSCEIVER_TARGET, + handles_array); continue; } if (nv_strcasecmp(queries[query], "dpys")) { - query_all_targets(display_name, DISPLAY_TARGET); + query_all_targets(display_name, DISPLAY_TARGET, handles_array); continue; } @@ -554,7 +1492,7 @@ static int process_attribute_queries(int num, char **queries, /* allocate the CtrlHandles */ - h = nv_alloc_ctrl_handles(a.display); + h = nv_alloc_ctrl_handles_and_add_to_array(a.display, handles_array); if (!h) { goto done; } @@ -563,10 +1501,6 @@ static int process_attribute_queries(int num, char **queries, ret = nv_process_parsed_attribute(&a, h, NV_FALSE, NV_FALSE, "in query '%s'", queries[query]); - /* free the CtrlHandles */ - - nv_free_ctrl_handles(h); - if (ret == NV_FALSE) goto done; /* print a newline at the end */ @@ -591,14 +1525,11 @@ static int process_attribute_queries(int num, char **queries, * * If any errors are encountered, an error message is printed and * NV_FALSE is returned. Otherwise, NV_TRUE is returned. - * - * XXX rather than call nv_alloc_ctrl_handles()/nv_free_ctrl_handles() - * for every assignment, we should share the code in - * process_config_file_attributes() to collapse the list of handles. */ static int process_attribute_assignments(int num, char **assignments, - const char *display_name) + const char *display_name, + CtrlHandlesArray *handles_array) { int assignment, ret, val; ParsedAttribute a; @@ -631,7 +1562,7 @@ static int process_attribute_assignments(int num, char **assignments, /* allocate the CtrlHandles */ - h = nv_alloc_ctrl_handles(a.display); + h = nv_alloc_ctrl_handles_and_add_to_array(a.display, handles_array); if (!h) { goto done; } @@ -641,10 +1572,6 @@ static int process_attribute_assignments(int num, char **assignments, ret = nv_process_parsed_attribute(&a, h, NV_TRUE, NV_TRUE, "in assignment '%s'", assignments[assignment]); - /* free the CtrlHandles */ - - nv_free_ctrl_handles(h); - if (ret == NV_FALSE) goto done; /* print a newline at the end */ @@ -676,7 +1603,7 @@ static int validate_value(CtrlHandleTarget *t, ParsedAttribute *a, uint32 d, ReturnStatus status; char d_str[256]; char *tmp_d_str; - TargetTypeEntry *targetTypeEntry; + const TargetTypeEntry *targetTypeEntry; status = NvCtrlGetValidDisplayAttributeValues(t->h, d, a->attr, &valid); @@ -778,16 +1705,15 @@ static int validate_value(CtrlHandleTarget *t, ParsedAttribute *a, uint32 d, * attribute. */ -static void print_valid_values(char *name, int attr, uint32 flags, +static void print_valid_values(const char *name, int attr, uint32 flags, NVCTRLAttributeValidValuesRec valid) { - int bit, print_bit, last, last2, n; + int bit, print_bit, last, last2, n, i; char str[256]; char *c; char str2[256]; char *c2; char **at; - TargetTypeEntry *targetTypeEntry; /* do not print any valid values information when we are in 'terse' mode */ @@ -899,12 +1825,10 @@ static void print_valid_values(char *name, int attr, uint32 flags, c = str; n = 0; - for (targetTypeEntry = targetTypeTable; - targetTypeEntry->name; - targetTypeEntry++) { - if (valid.permissions & targetTypeEntry->permission_bit) { + for (i = 0; i < targetTypeTableLen; i++) { + if (valid.permissions & targetTypeTable[i].permission_bit) { if (n > 0) c += sprintf(c, ", "); - c += sprintf(c, "%s", targetTypeEntry->name); + c += sprintf(c, "%s", targetTypeTable[i].name); n++; } } @@ -1069,17 +1993,15 @@ static void print_additional_info(const char *name, * returned; if successful, NV_TRUE is returned. */ -static int query_all(const char *display_name) +static int query_all(const char *display_name, CtrlHandlesArray *handles_array) { - int bit, entry, val; + int bit, entry, val, i; uint32 mask; ReturnStatus status; - AttributeTableEntry *a; NVCTRLAttributeValidValuesRec valid; CtrlHandles *h; - TargetTypeEntry *targetTypeEntry; - h = nv_alloc_ctrl_handles(display_name); + h = nv_alloc_ctrl_handles_and_add_to_array(display_name, handles_array); if (!h) { return NV_FALSE; } @@ -1090,17 +2012,14 @@ static int query_all(const char *display_name) * Loop through all target types. */ - for (targetTypeEntry = targetTypeTable; - targetTypeEntry->name; - targetTypeEntry++) { - + for (i = 0; i < targetTypeTableLen; i++) { CtrlHandleTargets *targets = - &(h->targets[targetTypeEntry->target_index]); - int i; + &(h->targets[targetTypeTable[i].target_index]); + int j; - for (i = 0; i < targets->n; i++) { + for (j = 0; j < targets->n; j++) { - CtrlHandleTarget *t = &targets->t[i]; + CtrlHandleTarget *t = &targets->t[j]; if (!t->h) continue; @@ -1109,8 +2028,7 @@ static int query_all(const char *display_name) if (!__terse) nv_msg(NULL, ""); for (entry = 0; attributeTable[entry].name; entry++) { - - a = &attributeTable[entry]; + const AttributeTableEntry *a = &attributeTable[entry]; /* skip the color attributes */ @@ -1129,7 +2047,7 @@ static int query_all(const char *display_name) * display devices), skip to the next bit */ - if (targetTypeEntry->uses_display_devices && + if (targetTypeTable[i].uses_display_devices && ((t->d & mask) == 0x0) && (t->d)) continue; if (a->flags & NV_PARSER_TYPE_STRING_ATTRIBUTE) { @@ -1220,14 +2138,12 @@ exit_bit_loop: } /* entry */ - } /* i (targets) */ + } /* j (targets) */ - } /* target types */ + } /* i (target types) */ #undef INDENT - nv_free_ctrl_handles(h); - return NV_TRUE; } /* query_all() */ @@ -1267,7 +2183,7 @@ static int print_target_display_connections(CtrlHandleTarget *t) if (status != NvCtrlSuccess) name = strdup("Unknown"); tmp_d_str = display_device_mask_to_display_device_name(bit); - nv_msg(" ", "%s (%s: 0x%0.8X)", name, tmp_d_str, bit); + nv_msg(" ", "%s (%s: 0x%.8X)", name, tmp_d_str, bit); free(name); free(tmp_d_str); @@ -1415,19 +2331,20 @@ static int print_target_connections(CtrlHandles *h, * specified type) accessible via the Display connection. */ -static int query_all_targets(const char *display_name, const int target_index) +static int query_all_targets(const char *display_name, const int target_index, + CtrlHandlesArray *handles_array) { CtrlHandles *h; CtrlHandleTarget *t; char *str, *name; int i; - TargetTypeEntry *targetTypeEntry; + const TargetTypeEntry *targetTypeEntry; targetTypeEntry = &(targetTypeTable[target_index]); /* create handles */ - h = nv_alloc_ctrl_handles(display_name); + h = nv_alloc_ctrl_handles_and_add_to_array(display_name, handles_array); if (!h) { return NV_FALSE; } @@ -1441,7 +2358,6 @@ static int query_all_targets(const char *display_name, const int target_index) if (h->targets[target_index].n <= 0) { nv_warning_msg("No %ss on %s", targetTypeEntry->name, str); free(str); - nv_free_ctrl_handles(h); return NV_FALSE; } @@ -1571,8 +2487,6 @@ static int query_all_targets(const char *display_name, const int target_index) } } - nv_free_ctrl_handles(h); - return NV_TRUE; } /* query_all_targets() */ @@ -1747,13 +2661,11 @@ int nv_process_parsed_attribute(ParsedAttribute *a, CtrlHandles *h, int assign, int verbose, char *whence_fmt, ...) { - int i, target, start, end, bit, ret, val; + int bit, ret, val; char *whence, *tmp_d_str0, *tmp_d_str1; - uint32 display_devices, mask; + uint32 display_devices; ReturnStatus status; - NVCTRLAttributeValidValuesRec valid; - CtrlHandleTarget *t; - TargetTypeEntry *targetTypeEntry; + CtrlHandleTargetNode *n; val = NV_FALSE; @@ -1771,126 +2683,34 @@ int nv_process_parsed_attribute(ParsedAttribute *a, CtrlHandles *h, a->name, whence); goto done; } - - /* - * if a target was specified, make sure it is valid, and setup - * the variables 'start', 'end', and 'target'. - */ - - if (a->flags & NV_PARSER_HAS_TARGET) { - char *target_type_name; - - /* - * look up the target index for the target type specified in - * the ParsedAttribute - */ - - targetTypeEntry = nv_get_target_type_entry_by_nvctrl(a->target_type); - if (!targetTypeEntry) { - nv_error_msg("Invalid target specified %s.", whence); - goto done; - } - - target = targetTypeEntry->target_index; - target_type_name = targetTypeEntry->name; - - /* - * If the target was named, match it to one of the probed targets - * to obtain the target_id - */ - - if (a->target_name) { - Bool found = FALSE; - for (i = 0; i < h->targets[target].n; i++) { - int j; - t = &(h->targets[target].t[i]); - for (j = 0; j < NV_DPY_PROTO_NAME_MAX; j++) { - char *name = t->protoNames[j]; - if (!name) continue; - if (strcasecmp(name, a->target_name) == 0) { - a->target_id = NvCtrlGetTargetId(t->h); - found = TRUE; - break; - } - } - if (found) break; - } - - if (!found) { - nv_error_msg("Invalid name '%s' specified for %s " - "specified %s.", a->target_name, - target_type_name, whence); - goto done; - } - } - - /* make sure the target_id is in range */ - - if (a->target_id >= h->targets[target].n) { - - if (h->targets[target].n == 1) { - nv_error_msg("Invalid %s %d specified %s (there is only " - "1 %s on this Display).", - target_type_name, - a->target_id, whence, target_type_name); - } else { - nv_error_msg("Invalid %s %d specified %s " - "(there are only %d %ss on this Display).", - target_type_name, - a->target_id, whence, - h->targets[target].n, - target_type_name); - } - goto done; - } - - /* - * make sure we have a handle for this target; missing a - * handle should only happen for X screens because not all X - * screens will be controlled by NVIDIA - */ - - if (!h->targets[target].t[a->target_id].h) { - nv_warning_msg("Invalid %s %d specified %s (NV-CONTROL extension " - "not supported on %s %d).", - target_type_name, - a->target_id, whence, - target_type_name, a->target_id); - } - - /* - * assign 'start' and 'end' such that the below loop only uses - * this target. - */ - - start = a->target_id; - end = a->target_id + 1; - - } else { - - /* - * no target was specified; assume a target type of - * X_SCREEN_TARGET, and assign 'start' and 'end' such that we - * loop over all the screens; we could potentially store the - * correct default target type for each attribute and default - * to that rather than assume X_SCREEN_TARGET. - */ + /* Resolve any target specifications against the CtrlHandles that were + * allocated. + */ - targetTypeEntry = &(targetTypeTable[X_SCREEN_TARGET]); - target = X_SCREEN_TARGET; - start = 0; - end = h->targets[target].n; + ret = resolve_attribute_targets(a, h); + if (ret != NV_PARSER_STATUS_SUCCESS) { + nv_error_msg("Error resolving target specification '%s' " + "(%s), specified %s.", + a->target_specification ? a->target_specification : "", + nv_parse_strerror(ret), + whence); + goto done; } /* loop over the requested targets */ - - for (i = start; i < end; i++) { - - t = &h->targets[target].t[i]; + + for (n = a->targets; n->next; n = n->next) { + + CtrlHandleTarget *t = n->t; + int target_type; + const TargetTypeEntry *targetTypeEntry; if (!t->h) continue; /* no handle on this target; silently skip */ + target_type = NvCtrlGetTargetType(t->h); + targetTypeEntry = nv_get_target_type_entry_by_nvctrl(target_type); + /* validate any specified display device mask */ if ((a->flags & NV_PARSER_HAS_DISPLAY_DEVICE) && @@ -1933,7 +2753,7 @@ int nv_process_parsed_attribute(ParsedAttribute *a, CtrlHandles *h, } } else { - + /* Just use the display device mask for this target */ display_devices = t->d; } @@ -2060,7 +2880,7 @@ int nv_process_parsed_attribute(ParsedAttribute *a, CtrlHandles *h, */ if ((a->flags & NV_PARSER_TYPE_FRAMELOCK) && - (NvCtrlGetTargetType(t->h) != NV_CTRL_TARGET_TYPE_FRAMELOCK)) { + (target_type != NV_CTRL_TARGET_TYPE_FRAMELOCK)) { int available; status = NvCtrlGetAttribute(t->h, NV_CTRL_FRAMELOCK, &available); @@ -2121,7 +2941,7 @@ int nv_process_parsed_attribute(ParsedAttribute *a, CtrlHandles *h, */ if (a->flags & NV_PARSER_TYPE_SDI && - target != NV_CTRL_TARGET_TYPE_GVI) { + target_type != NV_CTRL_TARGET_TYPE_GVI) { int available; status = NvCtrlGetAttribute(t->h, NV_CTRL_GVO_SUPPORTED, @@ -2198,19 +3018,19 @@ int nv_process_parsed_attribute(ParsedAttribute *a, CtrlHandles *h, nv_msg(INDENT, " Red Green Blue Offset Scale"); nv_msg(INDENT, "----------------------------------------------------"); - nv_msg(INDENT, " Y % -0.6f % -0.6f % -0.6f % -0.6f % -0.6f", + nv_msg(INDENT, " Y % -.6f % -.6f % -.6f % -.6f % -.6f", colorMatrix[0][0], colorMatrix[0][1], colorMatrix[0][2], colorOffset[0], colorScale[0]); - nv_msg(INDENT, "Cr % -0.6f % -0.6f % -0.6f % -0.6f % -0.6f", + nv_msg(INDENT, "Cr % -.6f % -.6f % -.6f % -.6f % -.6f", colorMatrix[1][0], colorMatrix[1][1], colorMatrix[1][2], colorOffset[1], colorScale[1]); - nv_msg(INDENT, "Cb % -0.6f % -0.6f % -0.6f % -0.6f % -0.6f", + nv_msg(INDENT, "Cb % -.6f % -.6f % -.6f % -.6f % -.6f", colorMatrix[2][0], colorMatrix[2][1], colorMatrix[2][2], @@ -2226,6 +3046,9 @@ int nv_process_parsed_attribute(ParsedAttribute *a, CtrlHandles *h, /* loop over the display devices */ for (bit = 0; bit < 24; bit++) { + uint32 mask; + NVCTRLAttributeValidValuesRec valid; + mask = (1 << bit); @@ -2277,11 +3100,12 @@ int nv_process_parsed_attribute(ParsedAttribute *a, CtrlHandles *h, a->name, whence); goto done; } - - ret = process_parsed_attribute_internal(t, a, mask, target, assign, - verbose, whence, valid); + + ret = process_parsed_attribute_internal(t, a, mask, target_type, + assign, verbose, whence, + valid); if (ret == NV_FALSE) goto done; - + /* * if this attribute is not per-display device, or this * target does not know about display devices, or this target @@ -2296,11 +3120,11 @@ int nv_process_parsed_attribute(ParsedAttribute *a, CtrlHandles *h, } } /* bit */ - - } /* i - done looping over requested targets */ - + + } /* done looping over requested targets */ + val = NV_TRUE; - + done: if (whence) free(whence); return val; diff --git a/src/query-assign.h b/src/query-assign.h index bbc6dd3..60ce1e9 100644 --- a/src/query-assign.h +++ b/src/query-assign.h @@ -54,6 +54,8 @@ typedef struct { uint32 c; /* Connected display device mask for target */ char *name; /* Name for this target */ char *protoNames[NV_DPY_PROTO_NAME_MAX]; /* List of valid names for this target */ + + struct _CtrlHandleTargetNode *relations; /* List of associated targets */ } CtrlHandleTarget; typedef struct { @@ -67,17 +69,35 @@ typedef struct { CtrlHandleTargets targets[MAX_TARGET_TYPES]; } CtrlHandles; +/* Used to keep track of lists of targets */ +typedef struct _CtrlHandleTargetNode { + struct _CtrlHandleTargetNode *next; + CtrlHandleTarget *t; +} CtrlHandleTargetNode; + +typedef struct _CtrlHandlesArray { + int n; /* number of CtrlHandles */ + CtrlHandles **array; /* dynamically allocated array of CtrlHandles */ +} CtrlHandlesArray; + +int nv_process_assignments_and_queries(const Options *op, + CtrlHandlesArray *handles_array); + +CtrlHandles * + nv_alloc_ctrl_handles_and_add_to_array(const char *display, + CtrlHandlesArray *handles_array); -int nv_process_assignments_and_queries(Options *op); +void nv_free_ctrl_handles_array(CtrlHandlesArray *handles_array); -CtrlHandles *nv_alloc_ctrl_handles(const char *display); -void nv_free_ctrl_handles(CtrlHandles *h); +CtrlHandles *nv_get_ctrl_handles(const char *display, + CtrlHandlesArray *handles_array); -NvCtrlAttributeHandle *nv_get_target_handle(CtrlHandles *handles, +NvCtrlAttributeHandle *nv_get_target_handle(const CtrlHandles *handles, int target_type, int target_id); int nv_process_parsed_attribute(ParsedAttribute*, CtrlHandles *h, - int, int, char*, ...); + int, int, char*, ...) NV_ATTRIBUTE_PRINTF(5, 6); +void nv_target_list_free(CtrlHandleTargetNode *head); #endif /* __QUERY_ASSIGN_H__ */ @@ -20,6 +20,7 @@ SRC_SRC += msg.c SRC_SRC += nvidia-settings.c SRC_SRC += parse.c SRC_SRC += query-assign.c +SRC_SRC += app-profiles.c SRC_SRC += glxinfo.c NVIDIA_SETTINGS_SRC += $(SRC_SRC) @@ -32,6 +33,7 @@ SRC_EXTRA_DIST += lscf.h SRC_EXTRA_DIST += msg.h SRC_EXTRA_DIST += parse.h SRC_EXTRA_DIST += query-assign.h +SRC_EXTRA_DIST += app-profiles.h SRC_EXTRA_DIST += glxinfo.h SRC_EXTRA_DIST += gen-manpage-opts.c @@ -62,8 +64,6 @@ IMAGE_DATA_EXTRA_DIST += image_data/config.png IMAGE_DATA_EXTRA_DIST += image_data/config_pixdata.h IMAGE_DATA_EXTRA_DIST += image_data/crt.png IMAGE_DATA_EXTRA_DIST += image_data/crt_pixdata.h -IMAGE_DATA_EXTRA_DIST += image_data/cursor_shadow.png -IMAGE_DATA_EXTRA_DIST += image_data/cursor_shadow_pixdata.h IMAGE_DATA_EXTRA_DIST += image_data/dfp.png IMAGE_DATA_EXTRA_DIST += image_data/dfp_pixdata.h IMAGE_DATA_EXTRA_DIST += image_data/display_config.png @@ -113,10 +113,10 @@ IMAGE_DATA_EXTRA_DIST += image_data/svp_3dvp.png IMAGE_DATA_EXTRA_DIST += image_data/svp_3dvp_pixdata.h IMAGE_DATA_EXTRA_DIST += image_data/thermal.png IMAGE_DATA_EXTRA_DIST += image_data/thermal_pixdata.h -IMAGE_DATA_EXTRA_DIST += image_data/tv.png -IMAGE_DATA_EXTRA_DIST += image_data/tv_pixdata.h IMAGE_DATA_EXTRA_DIST += image_data/vcs.png IMAGE_DATA_EXTRA_DIST += image_data/vcs_pixdata.h +IMAGE_DATA_EXTRA_DIST += image_data/vdpau.png +IMAGE_DATA_EXTRA_DIST += image_data/vdpau_pixdata.h IMAGE_DATA_EXTRA_DIST += image_data/x.png IMAGE_DATA_EXTRA_DIST += image_data/x_pixdata.h IMAGE_DATA_EXTRA_DIST += image_data/xvideo.png @@ -173,7 +173,6 @@ NVIDIA_SETTINGS_EXTRA_DIST += $(LIB_XPM_DATA_EXTRA_DIST) # GTK_SRC += gtk+-2.x/ctkxvideo.c -GTK_SRC += gtk+-2.x/ctkcursorshadow.c GTK_SRC += gtk+-2.x/ctkui.c GTK_SRC += gtk+-2.x/ctkframelock.c GTK_SRC += gtk+-2.x/ctkgauge.c @@ -214,13 +213,16 @@ GTK_SRC += gtk+-2.x/ctkpowersavings.c GTK_SRC += gtk+-2.x/ctkgvi.c GTK_SRC += gtk+-2.x/ctklicense.c GTK_SRC += gtk+-2.x/ctkecc.c +GTK_SRC += gtk+-2.x/ctkappprofile.c +GTK_SRC += gtk+-2.x/ctkapcprofilemodel.c +GTK_SRC += gtk+-2.x/ctkapcrulemodel.c GTK_SRC += gtk+-2.x/ctkcolorcontrols.c GTK_SRC += gtk+-2.x/ctk3dvisionpro.c +GTK_SRC += gtk+-2.x/ctkvdpau.c NVIDIA_SETTINGS_SRC += $(GTK_SRC) GTK_EXTRA_DIST += gtk+-2.x/ctkxvideo.h -GTK_EXTRA_DIST += gtk+-2.x/ctkcursorshadow.h GTK_EXTRA_DIST += gtk+-2.x/ctkui.h GTK_EXTRA_DIST += gtk+-2.x/ctkframelock.h GTK_EXTRA_DIST += gtk+-2.x/ctkgauge.h @@ -262,10 +264,39 @@ GTK_EXTRA_DIST += gtk+-2.x/ctkgvo-sync.h GTK_EXTRA_DIST += gtk+-2.x/ctkgvi.h GTK_EXTRA_DIST += gtk+-2.x/ctklicense.h GTK_EXTRA_DIST += gtk+-2.x/ctkecc.h +GTK_EXTRA_DIST += gtk+-2.x/ctkappprofile.h +GTK_EXTRA_DIST += gtk+-2.x/ctkapcprofilemodel.h +GTK_EXTRA_DIST += gtk+-2.x/ctkapcrulemodel.h GTK_EXTRA_DIST += gtk+-2.x/ctkcolorcontrols.h GTK_EXTRA_DIST += gtk+-2.x/ctk3dvisionpro.h +GTK_EXTRA_DIST += gtk+-2.x/ctkvdpau.h NVIDIA_SETTINGS_EXTRA_DIST += $(GTK_EXTRA_DIST) +# +# files in the src/jansson directory of nvidia-settings +# +JANSSON_SRC += jansson/load.c +JANSSON_SRC += jansson/value.c +JANSSON_SRC += jansson/pack_unpack.c +JANSSON_SRC += jansson/utf.c +JANSSON_SRC += jansson/dump.c +JANSSON_SRC += jansson/strconv.c +JANSSON_SRC += jansson/strbuffer.c +JANSSON_SRC += jansson/memory.c +JANSSON_SRC += jansson/error.c +JANSSON_SRC += jansson/hashtable.c + +NVIDIA_SETTINGS_SRC += $(JANSSON_SRC) + +JANSSON_EXTRA_DIST += jansson/utf.h +JANSSON_EXTRA_DIST += jansson/jansson_config.h +JANSSON_EXTRA_DIST += jansson/strbuffer.h +JANSSON_EXTRA_DIST += jansson/jansson.h +JANSSON_EXTRA_DIST += jansson/hashtable.h +JANSSON_EXTRA_DIST += jansson/jansson_private.h + +NVIDIA_SETTINGS_EXTRA_DIST += $(JANSSON_EXTRA_DIST) + NVIDIA_SETTINGS_DIST_FILES += $(NVIDIA_SETTINGS_SRC) NVIDIA_SETTINGS_DIST_FILES += $(NVIDIA_SETTINGS_EXTRA_DIST) diff --git a/src/version.mk b/src/version.mk index d8ad3ea..69efa2d 100644 --- a/src/version.mk +++ b/src/version.mk @@ -1 +1 @@ -NVIDIA_VERSION = 313.30 +NVIDIA_VERSION = 319.12 @@ -29,7 +29,7 @@ CC ?= gcc LD ?= ld # only set these warnings and optimizations if CFLAGS is unset -CFLAGS ?= -Wall -Wno-unused-parameter -O2 +CFLAGS ?= -Wall -O2 # always set these -f CFLAGS CFLAGS += -fno-strict-aliasing -fno-omit-frame-pointer CC_ONLY_CFLAGS ?= @@ -42,6 +42,10 @@ HOST_CFLAGS ?= $(CFLAGS) HOST_LDFLAGS ?= $(LDFLAGS) HOST_BIN_LDFLAGS ?= +# always disable warnings that will break the build +CFLAGS += -Wno-unused-parameter -Wno-format-zero-length +HOST_CFLAGS += -Wno-unused-parameter -Wno-format-zero-length + ifeq ($(DEBUG),1) STRIP_CMD ?= true CFLAGS += -O0 -g @@ -1 +1 @@ -NVIDIA_VERSION = 313.30 +NVIDIA_VERSION = 319.12 |