+static int getElementFromToken(char *token)
+{
+ char *value = getHashEntry(element_token_hash, token);
+
+ if (value != NULL)
+ return atoi(value);
+
+ Error(ERR_WARN, "unknown element token '%s'", token);
+
+ return EL_UNDEFINED;
+}
+
+void FreeGlobalAnimEventInfo(void)
+{
+ struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
+
+ if (gaei->event_list == NULL)
+ return;
+
+ int i;
+
+ for (i = 0; i < gaei->num_event_lists; i++)
+ {
+ checked_free(gaei->event_list[i]->event_value);
+ checked_free(gaei->event_list[i]);
+ }
+
+ checked_free(gaei->event_list);
+
+ gaei->event_list = NULL;
+ gaei->num_event_lists = 0;
+}
+
+static int AddGlobalAnimEventList(void)
+{
+ struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
+ int list_pos = gaei->num_event_lists++;
+
+ gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
+ sizeof(struct GlobalAnimEventListInfo *));
+
+ gaei->event_list[list_pos] =
+ checked_calloc(sizeof(struct GlobalAnimEventListInfo));
+
+ struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
+
+ gaeli->event_value = NULL;
+ gaeli->num_event_values = 0;
+
+ return list_pos;
+}
+
+static int AddGlobalAnimEventValue(int list_pos, int event_value)
+{
+ // do not add empty global animation events
+ if (event_value == ANIM_EVENT_NONE)
+ return list_pos;
+
+ // if list position is undefined, create new list
+ if (list_pos == ANIM_EVENT_UNDEFINED)
+ list_pos = AddGlobalAnimEventList();
+
+ struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
+ struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
+ int value_pos = gaeli->num_event_values++;
+
+ gaeli->event_value = checked_realloc(gaeli->event_value,
+ gaeli->num_event_values * sizeof(int *));
+
+ gaeli->event_value[value_pos] = event_value;
+
+ return list_pos;
+}
+
+int GetGlobalAnimEventValue(int list_pos, int value_pos)
+{
+ if (list_pos == ANIM_EVENT_UNDEFINED)
+ return ANIM_EVENT_NONE;
+
+ struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
+ struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
+
+ return gaeli->event_value[value_pos];
+}
+
+int GetGlobalAnimEventValueCount(int list_pos)
+{
+ if (list_pos == ANIM_EVENT_UNDEFINED)
+ return 0;
+
+ struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
+ struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
+
+ return gaeli->num_event_values;
+}
+
+// This function checks if a string <s> of the format "string1, string2, ..."
+// exactly contains a string <s_contained>.
+
+static boolean string_has_parameter(char *s, char *s_contained)
+{
+ char *substring;
+
+ if (s == NULL || s_contained == NULL)
+ return FALSE;
+
+ if (strlen(s_contained) > strlen(s))
+ return FALSE;
+
+ if (strncmp(s, s_contained, strlen(s_contained)) == 0)
+ {
+ char next_char = s[strlen(s_contained)];
+
+ // check if next character is delimiter or whitespace
+ return (next_char == ',' || next_char == '\0' ||
+ next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
+ }
+
+ // check if string contains another parameter string after a comma
+ substring = strchr(s, ',');
+ if (substring == NULL) // string does not contain a comma
+ return FALSE;
+
+ // advance string pointer to next character after the comma
+ substring++;
+
+ // skip potential whitespaces after the comma
+ while (*substring == ' ' || *substring == '\t')
+ substring++;
+
+ return string_has_parameter(substring, s_contained);
+}
+
+static int get_anim_parameter_value(char *s)
+{
+ int event_value[] =
+ {
+ ANIM_EVENT_CLICK,
+ ANIM_EVENT_INIT,
+ ANIM_EVENT_START,
+ ANIM_EVENT_END,
+ ANIM_EVENT_POST
+ };
+ char *pattern_1[] =
+ {
+ "click:anim_",
+ "init:anim_",
+ "start:anim_",
+ "end:anim_",
+ "post:anim_"
+ };
+ char *pattern_2 = ".part_";
+ char *matching_char = NULL;
+ char *s_ptr = s;
+ int pattern_1_len = 0;
+ int result = ANIM_EVENT_NONE;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(event_value); i++)
+ {
+ matching_char = strstr(s_ptr, pattern_1[i]);
+ pattern_1_len = strlen(pattern_1[i]);
+ result = event_value[i];
+
+ if (matching_char != NULL)
+ break;
+ }
+
+ if (matching_char == NULL)
+ return ANIM_EVENT_NONE;
+
+ s_ptr = matching_char + pattern_1_len;
+
+ // check for main animation number ("anim_X" or "anim_XX")
+ if (*s_ptr >= '0' && *s_ptr <= '9')
+ {
+ int gic_anim_nr = (*s_ptr++ - '0');
+
+ if (*s_ptr >= '0' && *s_ptr <= '9')
+ gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
+
+ if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
+ return ANIM_EVENT_NONE;
+
+ result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
+ }
+ else
+ {
+ // invalid main animation number specified
+
+ return ANIM_EVENT_NONE;
+ }
+
+ // check for animation part number ("part_X" or "part_XX") (optional)
+ if (strPrefix(s_ptr, pattern_2))
+ {
+ s_ptr += strlen(pattern_2);
+
+ if (*s_ptr >= '0' && *s_ptr <= '9')
+ {
+ int gic_part_nr = (*s_ptr++ - '0');
+
+ if (*s_ptr >= '0' && *s_ptr <= '9')
+ gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
+
+ if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
+ return ANIM_EVENT_NONE;
+
+ result |= gic_part_nr << ANIM_EVENT_PART_BIT;
+ }
+ else
+ {
+ // invalid animation part number specified
+
+ return ANIM_EVENT_NONE;
+ }
+ }
+
+ // discard result if next character is neither delimiter nor whitespace
+ if (!(*s_ptr == ',' || *s_ptr == '\0' ||
+ *s_ptr == ' ' || *s_ptr == '\t'))
+ return ANIM_EVENT_NONE;
+
+ return result;
+}
+
+static int get_anim_parameter_values(char *s)
+{
+ int list_pos = ANIM_EVENT_UNDEFINED;
+ int event_value = ANIM_EVENT_DEFAULT;
+
+ if (string_has_parameter(s, "any"))
+ event_value |= ANIM_EVENT_ANY;
+
+ if (string_has_parameter(s, "click:self") ||
+ string_has_parameter(s, "click") ||
+ string_has_parameter(s, "self"))
+ event_value |= ANIM_EVENT_SELF;
+
+ if (string_has_parameter(s, "unclick:any"))
+ event_value |= ANIM_EVENT_UNCLICK_ANY;
+
+ // if animation event found, add it to global animation event list
+ if (event_value != ANIM_EVENT_NONE)
+ list_pos = AddGlobalAnimEventValue(list_pos, event_value);
+
+ while (s != NULL)
+ {
+ // add optional "click:anim_X" or "click:anim_X.part_X" parameter
+ event_value = get_anim_parameter_value(s);
+
+ // if animation event found, add it to global animation event list
+ if (event_value != ANIM_EVENT_NONE)
+ list_pos = AddGlobalAnimEventValue(list_pos, event_value);
+
+ // continue with next part of the string, starting with next comma
+ s = strchr(s + 1, ',');
+ }
+
+ return list_pos;
+}
+
+static int get_anim_action_parameter_value(char *token)
+{
+ int result = getImageIDFromToken(token);
+
+ if (result == -1)
+ {
+ char *gfx_token = getStringCat2("gfx.", token);
+
+ result = getImageIDFromToken(gfx_token);
+
+ checked_free(gfx_token);
+ }
+
+ if (result == -1)
+ {
+ Key key = getKeyFromX11KeyName(token);
+
+ if (key != KSYM_UNDEFINED)
+ result = -(int)key;
+ }
+
+ if (result == -1)
+ result = ANIM_EVENT_ACTION_NONE;
+
+ return result;
+}
+
+int get_parameter_value(char *value_raw, char *suffix, int type)
+{
+ char *value = getStringToLower(value_raw);
+ int result = 0; // probably a save default value
+
+ if (strEqual(suffix, ".direction"))
+ {
+ result = (strEqual(value, "left") ? MV_LEFT :
+ strEqual(value, "right") ? MV_RIGHT :
+ strEqual(value, "up") ? MV_UP :
+ strEqual(value, "down") ? MV_DOWN : MV_NONE);
+ }
+ else if (strEqual(suffix, ".position"))
+ {
+ result = (strEqual(value, "left") ? POS_LEFT :
+ strEqual(value, "right") ? POS_RIGHT :
+ strEqual(value, "top") ? POS_TOP :
+ strEqual(value, "upper") ? POS_UPPER :
+ strEqual(value, "middle") ? POS_MIDDLE :
+ strEqual(value, "lower") ? POS_LOWER :
+ strEqual(value, "bottom") ? POS_BOTTOM :
+ strEqual(value, "any") ? POS_ANY :
+ strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
+ }
+ else if (strEqual(suffix, ".align"))
+ {
+ result = (strEqual(value, "left") ? ALIGN_LEFT :
+ strEqual(value, "right") ? ALIGN_RIGHT :
+ strEqual(value, "center") ? ALIGN_CENTER :
+ strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
+ }
+ else if (strEqual(suffix, ".valign"))
+ {
+ result = (strEqual(value, "top") ? VALIGN_TOP :
+ strEqual(value, "bottom") ? VALIGN_BOTTOM :
+ strEqual(value, "middle") ? VALIGN_MIDDLE :
+ strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
+ }
+ else if (strEqual(suffix, ".anim_mode"))
+ {
+ result = (string_has_parameter(value, "none") ? ANIM_NONE :
+ string_has_parameter(value, "loop") ? ANIM_LOOP :
+ string_has_parameter(value, "linear") ? ANIM_LINEAR :
+ string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
+ string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
+ string_has_parameter(value, "random") ? ANIM_RANDOM :
+ string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
+ string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
+ string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
+ string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
+ string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
+ string_has_parameter(value, "centered") ? ANIM_CENTERED :
+ string_has_parameter(value, "all") ? ANIM_ALL :
+ ANIM_DEFAULT);
+
+ if (string_has_parameter(value, "once"))
+ result |= ANIM_ONCE;
+
+ if (string_has_parameter(value, "reverse"))
+ result |= ANIM_REVERSE;
+
+ if (string_has_parameter(value, "opaque_player"))
+ result |= ANIM_OPAQUE_PLAYER;
+
+ if (string_has_parameter(value, "static_panel"))
+ result |= ANIM_STATIC_PANEL;
+ }
+ else if (strEqual(suffix, ".init_event") ||
+ strEqual(suffix, ".anim_event"))
+ {
+ result = get_anim_parameter_values(value);
+ }
+ else if (strEqual(suffix, ".init_delay_action") ||
+ strEqual(suffix, ".anim_delay_action") ||
+ strEqual(suffix, ".post_delay_action") ||
+ strEqual(suffix, ".init_event_action") ||
+ strEqual(suffix, ".anim_event_action"))
+ {
+ result = get_anim_action_parameter_value(value_raw);
+ }
+ else if (strEqual(suffix, ".class"))
+ {
+ result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
+ get_hash_from_key(value));
+ }
+ else if (strEqual(suffix, ".style"))
+ {
+ result = STYLE_DEFAULT;
+
+ if (string_has_parameter(value, "accurate_borders"))
+ result |= STYLE_ACCURATE_BORDERS;
+
+ if (string_has_parameter(value, "inner_corners"))
+ result |= STYLE_INNER_CORNERS;
+
+ if (string_has_parameter(value, "reverse"))
+ result |= STYLE_REVERSE;
+
+ if (string_has_parameter(value, "block_clicks"))
+ result |= STYLE_BLOCK;
+
+ if (string_has_parameter(value, "passthrough_clicks"))
+ result |= STYLE_PASSTHROUGH;
+
+ if (string_has_parameter(value, "multiple_actions"))
+ result |= STYLE_MULTIPLE_ACTIONS;
+ }
+ else if (strEqual(suffix, ".fade_mode"))
+ {
+ result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
+ string_has_parameter(value, "fade") ? FADE_MODE_FADE :
+ string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
+ string_has_parameter(value, "melt") ? FADE_MODE_MELT :
+ string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
+ FADE_MODE_DEFAULT);
+ }
+ else if (strEqual(suffix, ".auto_delay_unit"))
+ {
+ result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
+ string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
+ AUTO_DELAY_UNIT_DEFAULT);
+ }
+ else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
+ {
+ result = gfx.get_font_from_token_function(value);
+ }
+ else // generic parameter of type integer or boolean
+ {
+ result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
+ type == TYPE_INTEGER ? get_integer_from_string(value) :
+ type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
+ ARG_UNDEFINED_VALUE);
+ }
+
+ free(value);
+
+ return result;
+}
+
+static int get_token_parameter_value(char *token, char *value_raw)
+{
+ char *suffix;
+
+ if (token == NULL || value_raw == NULL)
+ return ARG_UNDEFINED_VALUE;
+
+ suffix = strrchr(token, '.');
+ if (suffix == NULL)
+ suffix = token;
+
+ if (strEqual(suffix, ".element"))
+ return getElementFromToken(value_raw);
+
+ // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
+ return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
+}
+
+void InitMenuDesignSettings_Static(void)
+{
+ int i;
+
+ // always start with reliable default values from static default config
+ for (i = 0; image_config_vars[i].token != NULL; i++)
+ {
+ char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
+
+ if (value != NULL)
+ *image_config_vars[i].value =
+ get_token_parameter_value(image_config_vars[i].token, value);
+ }
+}
+
+static void InitMenuDesignSettings_SpecialPreProcessing(void)
+{
+ int i;
+
+ // the following initializes hierarchical values from static configuration
+
+ // special case: initialize "ARG_DEFAULT" values in static default config
+ // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
+ titlescreen_initial_first_default.fade_mode =
+ title_initial_first_default.fade_mode;
+ titlescreen_initial_first_default.fade_delay =
+ title_initial_first_default.fade_delay;
+ titlescreen_initial_first_default.post_delay =
+ title_initial_first_default.post_delay;
+ titlescreen_initial_first_default.auto_delay =
+ title_initial_first_default.auto_delay;
+ titlescreen_initial_first_default.auto_delay_unit =
+ title_initial_first_default.auto_delay_unit;
+ titlescreen_first_default.fade_mode = title_first_default.fade_mode;
+ titlescreen_first_default.fade_delay = title_first_default.fade_delay;
+ titlescreen_first_default.post_delay = title_first_default.post_delay;
+ titlescreen_first_default.auto_delay = title_first_default.auto_delay;
+ titlescreen_first_default.auto_delay_unit =
+ title_first_default.auto_delay_unit;
+ titlemessage_initial_first_default.fade_mode =
+ title_initial_first_default.fade_mode;
+ titlemessage_initial_first_default.fade_delay =
+ title_initial_first_default.fade_delay;
+ titlemessage_initial_first_default.post_delay =
+ title_initial_first_default.post_delay;
+ titlemessage_initial_first_default.auto_delay =
+ title_initial_first_default.auto_delay;
+ titlemessage_initial_first_default.auto_delay_unit =
+ title_initial_first_default.auto_delay_unit;
+ titlemessage_first_default.fade_mode = title_first_default.fade_mode;
+ titlemessage_first_default.fade_delay = title_first_default.fade_delay;
+ titlemessage_first_default.post_delay = title_first_default.post_delay;
+ titlemessage_first_default.auto_delay = title_first_default.auto_delay;
+ titlemessage_first_default.auto_delay_unit =
+ title_first_default.auto_delay_unit;
+
+ titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
+ titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
+ titlescreen_initial_default.post_delay = title_initial_default.post_delay;
+ titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
+ titlescreen_initial_default.auto_delay_unit =
+ title_initial_default.auto_delay_unit;
+ titlescreen_default.fade_mode = title_default.fade_mode;
+ titlescreen_default.fade_delay = title_default.fade_delay;
+ titlescreen_default.post_delay = title_default.post_delay;
+ titlescreen_default.auto_delay = title_default.auto_delay;
+ titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
+ titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
+ titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
+ titlemessage_initial_default.post_delay = title_initial_default.post_delay;
+ titlemessage_initial_default.auto_delay_unit =
+ title_initial_default.auto_delay_unit;
+ titlemessage_default.fade_mode = title_default.fade_mode;
+ titlemessage_default.fade_delay = title_default.fade_delay;
+ titlemessage_default.post_delay = title_default.post_delay;
+ titlemessage_default.auto_delay = title_default.auto_delay;
+ titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
+
+ // special case: initialize "ARG_DEFAULT" values in static default config
+ // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
+ for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
+ {
+ titlescreen_initial_first[i] = titlescreen_initial_first_default;
+ titlescreen_first[i] = titlescreen_first_default;
+ titlemessage_initial_first[i] = titlemessage_initial_first_default;
+ titlemessage_first[i] = titlemessage_first_default;
+
+ titlescreen_initial[i] = titlescreen_initial_default;
+ titlescreen[i] = titlescreen_default;
+ titlemessage_initial[i] = titlemessage_initial_default;
+ titlemessage[i] = titlemessage_default;
+ }
+
+ // special case: initialize "ARG_DEFAULT" values in static default config
+ // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
+ for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
+ {
+ if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
+ continue;
+
+ menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
+ menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
+ menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
+ }
+
+ // special case: initialize "ARG_DEFAULT" values in static default config
+ // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
+ for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
+ {
+ viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
+ viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
+ viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
+
+ if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
+ continue;
+
+ viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
+ }
+}
+
+static void InitMenuDesignSettings_SpecialPostProcessing(void)
+{
+ static struct
+ {
+ struct XY *dst, *src;
+ }
+ game_buttons_xy[] =
+ {
+ { &game.button.save, &game.button.stop },
+ { &game.button.pause2, &game.button.pause },
+ { &game.button.load, &game.button.play },
+ { &game.button.undo, &game.button.stop },
+ { &game.button.redo, &game.button.play },
+
+ { NULL, NULL }
+ };
+ int i, j;
+
+ // special case: initialize later added SETUP list size from LEVELS value
+ if (menu.list_size[GAME_MODE_SETUP] == -1)
+ menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
+
+ // set default position for snapshot buttons to stop/pause/play buttons
+ for (i = 0; game_buttons_xy[i].dst != NULL; i++)
+ if ((*game_buttons_xy[i].dst).x == -1 &&
+ (*game_buttons_xy[i].dst).y == -1)
+ *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
+
+ // --------------------------------------------------------------------------
+ // dynamic viewports (including playfield margins, borders and alignments)
+ // --------------------------------------------------------------------------
+
+ // dynamic viewports currently only supported for landscape mode
+ int display_width = MAX(video.display_width, video.display_height);
+ int display_height = MIN(video.display_width, video.display_height);
+
+ for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
+ {
+ struct RectWithBorder *vp_window = &viewport.window[i];
+ struct RectWithBorder *vp_playfield = &viewport.playfield[i];
+ struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
+ struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
+ boolean dynamic_window_width = (vp_window->min_width != -1);
+ boolean dynamic_window_height = (vp_window->min_height != -1);
+ boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
+ boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
+
+ // adjust window size if min/max width/height is specified
+
+ if (vp_window->min_width != -1)
+ {
+ int window_width = display_width;
+
+ // when using static window height, use aspect ratio of display
+ if (vp_window->min_height == -1)
+ window_width = vp_window->height * display_width / display_height;
+
+ vp_window->width = MAX(vp_window->min_width, window_width);
+ }
+
+ if (vp_window->min_height != -1)
+ {
+ int window_height = display_height;
+
+ // when using static window width, use aspect ratio of display
+ if (vp_window->min_width == -1)
+ window_height = vp_window->width * display_height / display_width;
+
+ vp_window->height = MAX(vp_window->min_height, window_height);
+ }
+
+ if (vp_window->max_width != -1)
+ vp_window->width = MIN(vp_window->width, vp_window->max_width);
+
+ if (vp_window->max_height != -1)
+ vp_window->height = MIN(vp_window->height, vp_window->max_height);
+
+ int playfield_width = vp_window->width;
+ int playfield_height = vp_window->height;
+
+ // adjust playfield size and position according to specified margins
+
+ playfield_width -= vp_playfield->margin_left;
+ playfield_width -= vp_playfield->margin_right;
+
+ playfield_height -= vp_playfield->margin_top;
+ playfield_height -= vp_playfield->margin_bottom;
+
+ // adjust playfield size if min/max width/height is specified
+
+ if (vp_playfield->min_width != -1)
+ vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
+
+ if (vp_playfield->min_height != -1)
+ vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
+
+ if (vp_playfield->max_width != -1)
+ vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
+
+ if (vp_playfield->max_height != -1)
+ vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
+
+ // adjust playfield position according to specified alignment
+
+ if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
+ vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
+ else if (vp_playfield->align == ALIGN_CENTER)
+ vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
+ else if (vp_playfield->align == ALIGN_RIGHT)
+ vp_playfield->x += playfield_width - vp_playfield->width;
+
+ if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
+ vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
+ else if (vp_playfield->valign == VALIGN_MIDDLE)
+ vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
+ else if (vp_playfield->valign == VALIGN_BOTTOM)
+ vp_playfield->y += playfield_height - vp_playfield->height;
+
+ vp_playfield->x += vp_playfield->margin_left;
+ vp_playfield->y += vp_playfield->margin_top;
+
+ // adjust individual playfield borders if only default border is specified
+
+ if (vp_playfield->border_left == -1)
+ vp_playfield->border_left = vp_playfield->border_size;
+ if (vp_playfield->border_right == -1)
+ vp_playfield->border_right = vp_playfield->border_size;
+ if (vp_playfield->border_top == -1)
+ vp_playfield->border_top = vp_playfield->border_size;
+ if (vp_playfield->border_bottom == -1)
+ vp_playfield->border_bottom = vp_playfield->border_size;
+
+ // set dynamic playfield borders if borders are specified as undefined
+ // (but only if window size was dynamic and playfield size was static)
+
+ if (dynamic_window_width && !dynamic_playfield_width)
+ {
+ if (vp_playfield->border_left == -1)
+ {
+ vp_playfield->border_left = (vp_playfield->x -
+ vp_playfield->margin_left);
+ vp_playfield->x -= vp_playfield->border_left;
+ vp_playfield->width += vp_playfield->border_left;
+ }
+
+ if (vp_playfield->border_right == -1)
+ {
+ vp_playfield->border_right = (vp_window->width -
+ vp_playfield->x -
+ vp_playfield->width -
+ vp_playfield->margin_right);
+ vp_playfield->width += vp_playfield->border_right;
+ }
+ }
+
+ if (dynamic_window_height && !dynamic_playfield_height)
+ {
+ if (vp_playfield->border_top == -1)
+ {
+ vp_playfield->border_top = (vp_playfield->y -
+ vp_playfield->margin_top);
+ vp_playfield->y -= vp_playfield->border_top;
+ vp_playfield->height += vp_playfield->border_top;
+ }
+
+ if (vp_playfield->border_bottom == -1)
+ {
+ vp_playfield->border_bottom = (vp_window->height -
+ vp_playfield->y -
+ vp_playfield->height -
+ vp_playfield->margin_bottom);
+ vp_playfield->height += vp_playfield->border_bottom;
+ }
+ }