mirror of
https://github.com/libretro/RetroArch
synced 2025-02-20 15:40:44 +00:00
video_layout
This commit is contained in:
parent
ea931428e1
commit
6aaa4df394
@ -842,6 +842,10 @@ static void video_driver_free_internal(void)
|
||||
bool is_threaded = video_driver_is_threaded_internal();
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_VIDEO_LAYOUT
|
||||
video_layout_deinit();
|
||||
#endif
|
||||
|
||||
command_event(CMD_EVENT_OVERLAY_DEINIT, NULL);
|
||||
|
||||
if (!video_driver_is_video_cache_context())
|
||||
@ -1099,6 +1103,15 @@ static bool video_driver_init_internal(bool *video_is_threaded)
|
||||
command_event(CMD_EVENT_OVERLAY_DEINIT, NULL);
|
||||
command_event(CMD_EVENT_OVERLAY_INIT, NULL);
|
||||
|
||||
#ifdef HAVE_VIDEO_LAYOUT
|
||||
if(settings->bools.video_layout_enable)
|
||||
{
|
||||
video_layout_init(video_driver_data, video_driver_layout_render_interface());
|
||||
video_layout_load(settings->paths.path_video_layout);
|
||||
video_layout_view_select(settings->uints.video_layout_selected_view);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!core_is_game_loaded())
|
||||
video_driver_cached_frame_set(&dummy_pixels, 4, 4, 8);
|
||||
|
||||
|
470
gfx/video_layout.c
Normal file
470
gfx/video_layout.c
Normal file
@ -0,0 +1,470 @@
|
||||
#include "video_layout.h"
|
||||
#include "video_layout/view.h"
|
||||
#include "video_driver.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <formats/rxml.h>
|
||||
#include <file/file_path.h>
|
||||
#include <file/archive_file.h>
|
||||
#include <compat/strl.h>
|
||||
#include <verbosity.h>
|
||||
|
||||
bool load(view_array_t *view_array, rxml_document_t *doc);
|
||||
|
||||
typedef struct io
|
||||
{
|
||||
char *name;
|
||||
int base_value;
|
||||
int value;
|
||||
}
|
||||
io_t;
|
||||
|
||||
typedef struct video_layout_state
|
||||
{
|
||||
video_layout_render_info_t render_info;
|
||||
const video_layout_render_interface_t *render;
|
||||
|
||||
view_array_t view_array;
|
||||
|
||||
view_t *view;
|
||||
int view_index;
|
||||
|
||||
io_t *io;
|
||||
int io_count;
|
||||
|
||||
void **images;
|
||||
int images_count;
|
||||
|
||||
char *base_path;
|
||||
|
||||
bool is_archive;
|
||||
bool view_changed;
|
||||
}
|
||||
video_layout_state_t;
|
||||
|
||||
static video_layout_state_t *video_layout_state = NULL;
|
||||
|
||||
void video_layout_init(void *video_driver_data, const video_layout_render_interface_t *render)
|
||||
{
|
||||
if (video_layout_state)
|
||||
video_layout_deinit();
|
||||
|
||||
video_layout_state = (video_layout_state_t*)calloc(1, sizeof(video_layout_state_t));
|
||||
video_layout_state->render_info.video_driver_data = video_driver_data;
|
||||
video_layout_state->render = render;
|
||||
|
||||
vec_size((void**)&video_layout_state->images, sizeof(void*), 1);
|
||||
|
||||
video_layout_state->images[0] = NULL;
|
||||
video_layout_state->images_count = 1;
|
||||
}
|
||||
|
||||
void video_layout_deinit(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!video_layout_state)
|
||||
return;
|
||||
|
||||
free(video_layout_state->base_path);
|
||||
|
||||
for (i = 1; i < video_layout_state->images_count; ++i)
|
||||
{
|
||||
video_layout_state->render->free_image(
|
||||
video_layout_state->render_info.video_driver_data,
|
||||
video_layout_state->images[i]
|
||||
);
|
||||
}
|
||||
|
||||
free(video_layout_state->images);
|
||||
|
||||
for (i = 0; i < video_layout_state->io_count; ++i)
|
||||
free(video_layout_state->io[i].name);
|
||||
|
||||
free(video_layout_state->io);
|
||||
|
||||
view_array_deinit(&video_layout_state->view_array);
|
||||
|
||||
free(video_layout_state);
|
||||
video_layout_state = NULL;
|
||||
}
|
||||
|
||||
int video_layout_io_assign(const char *name, int base_value)
|
||||
{
|
||||
int index;
|
||||
|
||||
index = video_layout_state->io_count;
|
||||
|
||||
vec_size((void**)&video_layout_state->io, sizeof(io_t), ++video_layout_state->io_count);
|
||||
|
||||
video_layout_state->io[index].name = init_string(name);
|
||||
video_layout_state->io[index].base_value = base_value;
|
||||
video_layout_state->io[index].value = base_value;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
int video_layout_io_find(const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < video_layout_state->io_count; ++i)
|
||||
{
|
||||
if (strcmp(video_layout_state->io[i].name, name) == 0)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int video_layout_io_get(int index)
|
||||
{
|
||||
return video_layout_state->io[index].value;
|
||||
}
|
||||
|
||||
void video_layout_io_set(int index, int value)
|
||||
{
|
||||
video_layout_state->io[index].value = value;
|
||||
}
|
||||
|
||||
bool video_layout_load(const char *path)
|
||||
{
|
||||
rxml_document_t *doc;
|
||||
bool result;
|
||||
|
||||
if(!path || !strlen(path))
|
||||
return true;
|
||||
|
||||
video_layout_state->is_archive = path_is_compressed_file(path);
|
||||
|
||||
doc = NULL;
|
||||
|
||||
if(video_layout_state->is_archive)
|
||||
{
|
||||
void *buf;
|
||||
int64_t len;
|
||||
|
||||
char respath[PATH_MAX_LENGTH];
|
||||
strlcpy(respath, path, sizeof(respath));
|
||||
strlcat(respath, "#", sizeof(respath));
|
||||
set_string(&video_layout_state->base_path, respath);
|
||||
|
||||
strlcat(respath, "default.lay", sizeof(respath));
|
||||
if (file_archive_compressed_read(respath, &buf, NULL, &len))
|
||||
{
|
||||
char *str;
|
||||
if ((str = (char*)realloc(buf, (size_t)len + 1)))
|
||||
{
|
||||
str[(size_t)len] = '\0';
|
||||
doc = rxml_load_document_string(str);
|
||||
free(str);
|
||||
}
|
||||
else free(buf);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
char respath[PATH_MAX_LENGTH];
|
||||
fill_pathname_basedir(respath, path, sizeof(respath));
|
||||
set_string(&video_layout_state->base_path, respath);
|
||||
doc = rxml_load_document(path);
|
||||
}
|
||||
|
||||
if (!doc)
|
||||
{
|
||||
RARCH_LOG("video_layout: unable to open file \"%s\"\n", path);
|
||||
return false;
|
||||
}
|
||||
|
||||
result = load(&video_layout_state->view_array, doc);
|
||||
rxml_free_document(doc);
|
||||
|
||||
video_layout_view_select(video_layout_view_index());
|
||||
return result;
|
||||
}
|
||||
|
||||
bool video_layout_valid(void)
|
||||
{
|
||||
return video_layout_state && video_layout_state->view;
|
||||
}
|
||||
|
||||
static int video_layout_load_image(const char *path)
|
||||
{
|
||||
struct texture_image image;
|
||||
void *handle;
|
||||
int index;
|
||||
|
||||
image.supports_rgba = video_driver_supports_rgba();
|
||||
|
||||
if (video_layout_state->is_archive)
|
||||
{
|
||||
void *buf;
|
||||
int64_t len;
|
||||
|
||||
char respath[PATH_MAX_LENGTH];
|
||||
strlcpy(respath, video_layout_state->base_path, sizeof(respath));
|
||||
strlcat(respath, path, sizeof(respath));
|
||||
|
||||
if (!file_archive_compressed_read(respath, &buf, NULL, &len))
|
||||
{
|
||||
RARCH_LOG("video_layout: failed to decompress image: %s\n", respath);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!image_texture_load_buffer(&image, image_texture_get_type(path), buf, (size_t)len))
|
||||
{
|
||||
free(buf);
|
||||
|
||||
RARCH_LOG("video_layout: failed to load image: %s\n", respath);
|
||||
return 0;
|
||||
}
|
||||
|
||||
free(buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
char respath[PATH_MAX_LENGTH];
|
||||
strlcpy(respath, video_layout_state->base_path, sizeof(respath));
|
||||
strlcat(respath, path, sizeof(respath));
|
||||
|
||||
if (!image_texture_load(&image, respath))
|
||||
{
|
||||
RARCH_LOG("video_layout: failed to load image: %s\n", respath);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
handle = video_layout_state->render->take_image(
|
||||
video_layout_state->render_info.video_driver_data, image);
|
||||
|
||||
if (!handle)
|
||||
return 0;
|
||||
|
||||
index = video_layout_state->images_count;
|
||||
|
||||
vec_size((void**)&video_layout_state->images, sizeof(void*), ++video_layout_state->images_count);
|
||||
|
||||
video_layout_state->images[index] = handle;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
int video_layout_view_count(void)
|
||||
{
|
||||
return video_layout_state->view_array.views_count;
|
||||
}
|
||||
|
||||
const char *video_layout_view_name(int index)
|
||||
{
|
||||
return video_layout_state->view_array.views[index].name;
|
||||
}
|
||||
|
||||
int video_layout_view_select(int index)
|
||||
{
|
||||
index = MAX(0, MIN(index, video_layout_state->view_array.views_count - 1));
|
||||
|
||||
video_layout_state->view_index = index;
|
||||
video_layout_state->view = video_layout_state->view_array.views_count ?
|
||||
&video_layout_state->view_array.views[index] : NULL;
|
||||
|
||||
video_layout_view_change();
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
int video_layout_view_cycle(void)
|
||||
{
|
||||
return video_layout_view_select(
|
||||
(video_layout_state->view_index + 1) % video_layout_state->view_array.views_count);
|
||||
}
|
||||
|
||||
int video_layout_view_index(void)
|
||||
{
|
||||
return video_layout_state->view_index;
|
||||
}
|
||||
|
||||
void video_layout_view_change(void)
|
||||
{
|
||||
video_layout_state->view_changed = true;
|
||||
}
|
||||
|
||||
bool video_layout_view_on_change(void)
|
||||
{
|
||||
if (video_layout_state->view_changed)
|
||||
{
|
||||
video_layout_state->view_changed = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void video_layout_view_fit_bounds(video_layout_bounds_t bounds)
|
||||
{
|
||||
view_t *view;
|
||||
float c, dx, dy;
|
||||
int i, j, k;
|
||||
|
||||
view = video_layout_state->view;
|
||||
|
||||
c = MIN(bounds.w / view->bounds.w, bounds.h / view->bounds.h);
|
||||
|
||||
dx = view->bounds.w * c;
|
||||
dy = view->bounds.h * c;
|
||||
|
||||
view->render_bounds.w = dx;
|
||||
view->render_bounds.h = dy;
|
||||
view->render_bounds.x = (bounds.w - dx) / 2.f;
|
||||
view->render_bounds.y = (bounds.h - dy) / 2.f;
|
||||
|
||||
for (i = 0; i < view->layers_count; ++i)
|
||||
{
|
||||
layer_t *layer;
|
||||
layer = &view->layers[i];
|
||||
|
||||
for (j = 0; j < layer->elements_count; ++j)
|
||||
{
|
||||
element_t *elem;
|
||||
elem = &layer->elements[j];
|
||||
|
||||
elem->render_bounds.x = elem->bounds.x * view->render_bounds.w + view->render_bounds.x;
|
||||
elem->render_bounds.y = elem->bounds.y * view->render_bounds.h + view->render_bounds.y;
|
||||
elem->render_bounds.w = elem->bounds.w * view->render_bounds.w;
|
||||
elem->render_bounds.h = elem->bounds.h * view->render_bounds.h;
|
||||
|
||||
for (k = 0; k < elem->components_count; ++k)
|
||||
{
|
||||
component_t *comp;
|
||||
comp = &elem->components[k];
|
||||
|
||||
comp->render_bounds.x = comp->bounds.x * elem->render_bounds.w + elem->render_bounds.x;
|
||||
comp->render_bounds.y = comp->bounds.y * elem->render_bounds.h + elem->render_bounds.y;
|
||||
comp->render_bounds.w = comp->bounds.w * elem->render_bounds.w;
|
||||
comp->render_bounds.h = comp->bounds.h * elem->render_bounds.h;
|
||||
|
||||
if (comp->type == VIDEO_LAYOUT_C_SCREEN)
|
||||
view->screens[comp->attr.screen.index] = comp->render_bounds;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int video_layout_layer_count(void)
|
||||
{
|
||||
return video_layout_state->view->layers_count;
|
||||
}
|
||||
|
||||
void video_layout_layer_render(void *video_driver_frame_data, int index)
|
||||
{
|
||||
video_layout_render_info_t *info;
|
||||
const video_layout_render_interface_t *r;
|
||||
layer_t *layer;
|
||||
int i, j;
|
||||
|
||||
info = &video_layout_state->render_info;
|
||||
r = video_layout_state->render;
|
||||
layer = &video_layout_state->view->layers[index];
|
||||
|
||||
info->video_driver_frame_data = video_driver_frame_data;
|
||||
|
||||
r->layer_begin(info);
|
||||
|
||||
for (i = 0; i < layer->elements_count; ++i)
|
||||
{
|
||||
element_t *elem;
|
||||
elem = &layer->elements[i];
|
||||
|
||||
if (elem->o_bind != -1)
|
||||
elem->state = video_layout_state->io[elem->o_bind].value;
|
||||
|
||||
for (j = 0; j < elem->components_count; ++j)
|
||||
{
|
||||
component_t *comp;
|
||||
comp = &elem->components[j];
|
||||
|
||||
if (comp->enabled_state != -1)
|
||||
{
|
||||
if(comp->enabled_state != elem->state)
|
||||
continue;
|
||||
}
|
||||
|
||||
info->bounds = comp->render_bounds;
|
||||
info->orientation = comp->orientation;
|
||||
info->color = comp->color;
|
||||
|
||||
switch (comp->type)
|
||||
{
|
||||
case VIDEO_LAYOUT_C_UNKNOWN:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_SCREEN:
|
||||
r->screen(info, comp->attr.screen.index);
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_RECT:
|
||||
r->rect(info);
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_DISK:
|
||||
r->ellipse(info);
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_IMAGE:
|
||||
if(!comp->attr.image.loaded)
|
||||
{
|
||||
comp->attr.image.image_idx = video_layout_load_image(comp->attr.image.file);
|
||||
if(comp->attr.image.alpha_file)
|
||||
comp->attr.image.alpha_idx = video_layout_load_image(comp->attr.image.alpha_file);
|
||||
comp->attr.image.loaded = true;
|
||||
}
|
||||
r->image(info,
|
||||
video_layout_state->images[comp->attr.image.image_idx],
|
||||
video_layout_state->images[comp->attr.image.alpha_idx]);
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_TEXT:
|
||||
r->text(info, comp->attr.text.string);
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_COUNTER:
|
||||
r->counter(info, MIN(elem->state, comp->attr.counter.max_state));
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_DOTMATRIX_X1:
|
||||
r->led_dot(info, 1, elem->state);
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_DOTMATRIX_H5:
|
||||
r->led_dot(info, 5, elem->state);
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_DOTMATRIX_H8:
|
||||
r->led_dot(info, 8, elem->state);
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_LED_7:
|
||||
r->led_seg(info, VIDEO_LAYOUT_LED_7, elem->state);
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_LED_8_GTS1:
|
||||
r->led_seg(info, VIDEO_LAYOUT_LED_8_GTS1, elem->state);
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_LED_14:
|
||||
r->led_seg(info, VIDEO_LAYOUT_LED_14, elem->state);
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_LED_14_SC:
|
||||
r->led_seg(info, VIDEO_LAYOUT_LED_14_SC, elem->state);
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_LED_16:
|
||||
r->led_seg(info, VIDEO_LAYOUT_LED_16, elem->state);
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_LED_16_SC:
|
||||
r->led_seg(info, VIDEO_LAYOUT_LED_16_SC, elem->state);
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_REEL:
|
||||
/* not implemented */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r->layer_end(info, layer->blend);
|
||||
}
|
||||
|
||||
const video_layout_bounds_t *video_layout_screen(int index)
|
||||
{
|
||||
return &video_layout_state->view->screens[index];
|
||||
}
|
||||
|
||||
int video_layout_screen_count(void)
|
||||
{
|
||||
return video_layout_state->view->screens_count;
|
||||
}
|
77
gfx/video_layout.h
Normal file
77
gfx/video_layout.h
Normal file
@ -0,0 +1,77 @@
|
||||
#ifndef VIDEO_LAYOUT_H
|
||||
#define VIDEO_LAYOUT_H
|
||||
#include "video_layout/types.h"
|
||||
#include <boolean.h>
|
||||
#include <formats/image.h>
|
||||
|
||||
typedef struct video_layout_render_info
|
||||
{
|
||||
void *video_driver_data;
|
||||
void *video_driver_frame_data;
|
||||
|
||||
video_layout_bounds_t bounds;
|
||||
video_layout_orientation_t orientation;
|
||||
video_layout_color_t color;
|
||||
}
|
||||
video_layout_render_info_t;
|
||||
|
||||
typedef enum video_layout_led
|
||||
{
|
||||
VIDEO_LAYOUT_LED_7, /* digit with . */
|
||||
VIDEO_LAYOUT_LED_8_GTS1, /* digit with vertical split */
|
||||
VIDEO_LAYOUT_LED_14, /* alphanumeric */
|
||||
VIDEO_LAYOUT_LED_14_SC, /* alphanumeric with ., */
|
||||
VIDEO_LAYOUT_LED_16, /* full alphanumeric */
|
||||
VIDEO_LAYOUT_LED_16_SC /* full alphanumeric with ., */
|
||||
}
|
||||
video_layout_led_t;
|
||||
|
||||
typedef struct video_layout_render_interface
|
||||
{
|
||||
void *(*take_image) (void *video_driver_data, struct texture_image image);
|
||||
void (*free_image) (void *video_driver_data, void *image);
|
||||
|
||||
void (*layer_begin) (const video_layout_render_info_t *info);
|
||||
|
||||
void (*screen) (const video_layout_render_info_t *info, int screen_index);
|
||||
void (*image) (const video_layout_render_info_t *info, void *image_handle, void *alpha_handle);
|
||||
void (*text) (const video_layout_render_info_t *info, const char *str);
|
||||
void (*counter) (const video_layout_render_info_t *info, int value);
|
||||
void (*rect) (const video_layout_render_info_t *info);
|
||||
void (*ellipse) (const video_layout_render_info_t *info);
|
||||
void (*led_dot) (const video_layout_render_info_t *info, int dot_count, int dot_mask);
|
||||
void (*led_seg) (const video_layout_render_info_t *info, video_layout_led_t seg_layout, int seg_mask);
|
||||
|
||||
void (*layer_end) (const video_layout_render_info_t *info, video_layout_blend_t blend_type);
|
||||
}
|
||||
video_layout_render_interface_t;
|
||||
|
||||
void video_layout_init (void *video_driver_data, const video_layout_render_interface_t *render);
|
||||
void video_layout_deinit (void);
|
||||
|
||||
int video_layout_io_assign (const char *name, int base_value);
|
||||
int video_layout_io_get (int index);
|
||||
void video_layout_io_set (int index, int value);
|
||||
|
||||
bool video_layout_load (const char *path);
|
||||
bool video_layout_valid (void);
|
||||
|
||||
int video_layout_view_count (void);
|
||||
const char *video_layout_view_name (int index);
|
||||
|
||||
int video_layout_view_select (int index);
|
||||
int video_layout_view_cycle (void);
|
||||
int video_layout_view_index (void);
|
||||
|
||||
void video_layout_view_change (void);
|
||||
bool video_layout_view_on_change (void);
|
||||
void video_layout_view_fit_bounds (video_layout_bounds_t bounds);
|
||||
|
||||
int video_layout_layer_count (void);
|
||||
void video_layout_layer_render (void *video_driver_frame_data, int index);
|
||||
|
||||
const video_layout_bounds_t
|
||||
*video_layout_screen (int index);
|
||||
int video_layout_screen_count (void);
|
||||
|
||||
#endif
|
165
gfx/video_layout/component.c
Normal file
165
gfx/video_layout/component.c
Normal file
@ -0,0 +1,165 @@
|
||||
#include "component.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void component_init(component_t *comp, comp_type_t type)
|
||||
{
|
||||
comp->type = type;
|
||||
comp->bounds = make_bounds();
|
||||
comp->render_bounds = make_bounds_unit();
|
||||
comp->orientation = VIDEO_LAYOUT_ROT0;
|
||||
comp->color = make_color_white();
|
||||
comp->enabled_state = -1;
|
||||
|
||||
switch (comp->type)
|
||||
{
|
||||
case VIDEO_LAYOUT_C_UNKNOWN:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_SCREEN:
|
||||
comp->attr.screen.index = 0;
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_RECT:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_DISK:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_IMAGE:
|
||||
comp->attr.image.file = NULL;
|
||||
comp->attr.image.alpha_file = NULL;
|
||||
comp->attr.image.image_idx = 0;
|
||||
comp->attr.image.alpha_idx = 0;
|
||||
comp->attr.image.loaded = false;
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_TEXT:
|
||||
comp->attr.text.string = NULL;
|
||||
comp->attr.text.align = VIDEO_LAYOUT_TEXT_ALIGN_CENTER;
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_COUNTER:
|
||||
comp->attr.counter.digits = 2;
|
||||
comp->attr.counter.max_state = 999;
|
||||
comp->attr.counter.align = VIDEO_LAYOUT_TEXT_ALIGN_CENTER;
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_DOTMATRIX_X1:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_DOTMATRIX_H5:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_DOTMATRIX_H8:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_LED_7:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_LED_8_GTS1:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_LED_14:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_LED_14_SC:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_LED_16:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_LED_16_SC:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_REEL:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void component_copy(component_t *comp, const component_t *src)
|
||||
{
|
||||
comp->type = src->type;
|
||||
comp->bounds = src->bounds;
|
||||
comp->render_bounds = src->render_bounds;
|
||||
comp->orientation = src->orientation;
|
||||
comp->color = src->color;
|
||||
comp->enabled_state = src->enabled_state;
|
||||
|
||||
switch (comp->type)
|
||||
{
|
||||
case VIDEO_LAYOUT_C_UNKNOWN:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_SCREEN:
|
||||
comp->attr.screen.index = src->attr.screen.index;
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_RECT:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_DISK:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_IMAGE:
|
||||
comp->attr.image.file = init_string(src->attr.image.file);
|
||||
comp->attr.image.alpha_file = init_string(src->attr.image.alpha_file);
|
||||
comp->attr.image.image_idx = src->attr.image.image_idx;
|
||||
comp->attr.image.alpha_idx = src->attr.image.alpha_idx;
|
||||
comp->attr.image.loaded = src->attr.image.loaded;
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_TEXT:
|
||||
comp->attr.text.string = init_string(src->attr.text.string);
|
||||
comp->attr.text.align = src->attr.text.align;
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_COUNTER:
|
||||
comp->attr.counter.digits = src->attr.counter.digits;
|
||||
comp->attr.counter.max_state = src->attr.counter.max_state;
|
||||
comp->attr.counter.align = src->attr.counter.align;
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_DOTMATRIX_X1:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_DOTMATRIX_H5:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_DOTMATRIX_H8:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_LED_7:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_LED_8_GTS1:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_LED_14:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_LED_14_SC:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_LED_16:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_LED_16_SC:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_REEL:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void component_deinit(component_t *comp)
|
||||
{
|
||||
switch (comp->type)
|
||||
{
|
||||
case VIDEO_LAYOUT_C_UNKNOWN:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_SCREEN:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_RECT:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_DISK:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_IMAGE:
|
||||
free(comp->attr.image.file);
|
||||
free(comp->attr.image.alpha_file);
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_TEXT:
|
||||
free(comp->attr.text.string);
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_COUNTER:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_DOTMATRIX_X1:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_DOTMATRIX_H5:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_DOTMATRIX_H8:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_LED_7:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_LED_8_GTS1:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_LED_14:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_LED_14_SC:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_LED_16:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_LED_16_SC:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_REEL:
|
||||
break;
|
||||
}
|
||||
}
|
53
gfx/video_layout/component.h
Normal file
53
gfx/video_layout/component.h
Normal file
@ -0,0 +1,53 @@
|
||||
#ifndef VIDEO_LAYOUT_COMPONENT_H
|
||||
#define VIDEO_LAYOUT_COMPONENT_H
|
||||
|
||||
#include "internal.h"
|
||||
#include "component_attr.h"
|
||||
|
||||
typedef enum comp_type
|
||||
{
|
||||
VIDEO_LAYOUT_C_UNKNOWN,
|
||||
VIDEO_LAYOUT_C_SCREEN,
|
||||
VIDEO_LAYOUT_C_RECT,
|
||||
VIDEO_LAYOUT_C_DISK,
|
||||
VIDEO_LAYOUT_C_IMAGE,
|
||||
VIDEO_LAYOUT_C_TEXT,
|
||||
VIDEO_LAYOUT_C_COUNTER,
|
||||
VIDEO_LAYOUT_C_DOTMATRIX_X1,
|
||||
VIDEO_LAYOUT_C_DOTMATRIX_H5,
|
||||
VIDEO_LAYOUT_C_DOTMATRIX_H8,
|
||||
VIDEO_LAYOUT_C_LED_7,
|
||||
VIDEO_LAYOUT_C_LED_8_GTS1,
|
||||
VIDEO_LAYOUT_C_LED_14,
|
||||
VIDEO_LAYOUT_C_LED_14_SC,
|
||||
VIDEO_LAYOUT_C_LED_16,
|
||||
VIDEO_LAYOUT_C_LED_16_SC,
|
||||
VIDEO_LAYOUT_C_REEL
|
||||
}
|
||||
comp_type_t;
|
||||
|
||||
union comp_attr
|
||||
{
|
||||
c_attr_screen_t screen;
|
||||
c_attr_image_t image;
|
||||
c_attr_text_t text;
|
||||
c_attr_counter_t counter;
|
||||
};
|
||||
|
||||
typedef struct component
|
||||
{
|
||||
comp_type_t type;
|
||||
video_layout_bounds_t bounds;
|
||||
video_layout_bounds_t render_bounds;
|
||||
video_layout_orientation_t orientation;
|
||||
video_layout_color_t color;
|
||||
int enabled_state;
|
||||
union comp_attr attr;
|
||||
}
|
||||
component_t;
|
||||
|
||||
void component_init (component_t *comp, comp_type_t type);
|
||||
void component_copy (component_t *comp, const component_t *src);
|
||||
void component_deinit (component_t *comp);
|
||||
|
||||
#endif
|
35
gfx/video_layout/component_attr.h
Normal file
35
gfx/video_layout/component_attr.h
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef VIDEO_LAYOUT_COMPONENT_ATTR_H
|
||||
#define VIDEO_LAYOUT_COMPONENT_ATTR_H
|
||||
|
||||
typedef struct c_attr_screen
|
||||
{
|
||||
int index;
|
||||
}
|
||||
c_attr_screen_t;
|
||||
|
||||
typedef struct c_attr_image
|
||||
{
|
||||
char *file;
|
||||
char *alpha_file;
|
||||
int image_idx;
|
||||
int alpha_idx;
|
||||
bool loaded;
|
||||
}
|
||||
c_attr_image_t;
|
||||
|
||||
typedef struct c_attr_text
|
||||
{
|
||||
char *string;
|
||||
video_layout_text_align_t align;
|
||||
}
|
||||
c_attr_text_t;
|
||||
|
||||
typedef struct c_attr_counter
|
||||
{
|
||||
int digits;
|
||||
int max_state;
|
||||
video_layout_text_align_t align;
|
||||
}
|
||||
c_attr_counter_t;
|
||||
|
||||
#endif
|
78
gfx/video_layout/element.c
Normal file
78
gfx/video_layout/element.c
Normal file
@ -0,0 +1,78 @@
|
||||
#include "element.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
void element_init(element_t *elem, const char *name, int components_count)
|
||||
{
|
||||
elem->name = init_string(name);
|
||||
elem->state = -1;
|
||||
elem->o_bind = -1;
|
||||
elem->i_bind = -1;
|
||||
elem->i_mask = -1;
|
||||
elem->i_raw = false;
|
||||
|
||||
elem->bounds = make_bounds();
|
||||
elem->render_bounds = make_bounds_unit();
|
||||
|
||||
elem->components = (component_t*)(components_count > 0 ?
|
||||
calloc(components_count, sizeof(component_t)) : NULL);
|
||||
elem->components_count = components_count;
|
||||
}
|
||||
|
||||
void element_copy(element_t *elem, const element_t *src)
|
||||
{
|
||||
int i;
|
||||
|
||||
elem->name = init_string(src->name);
|
||||
elem->state = src->state;
|
||||
|
||||
elem->bounds = src->bounds;
|
||||
elem->render_bounds = src->render_bounds;
|
||||
|
||||
elem->components = (component_t*)(src->components_count > 0 ?
|
||||
calloc(src->components_count, sizeof(component_t)) : NULL);
|
||||
|
||||
for (i = 0; i < src->components_count; ++i)
|
||||
component_copy(&elem->components[i], &src->components[i]);
|
||||
|
||||
elem->components_count = src->components_count;
|
||||
}
|
||||
|
||||
void element_deinit(element_t *elem)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < elem->components_count; ++i)
|
||||
component_deinit(&elem->components[i]);
|
||||
free(elem->components);
|
||||
|
||||
free(elem->name);
|
||||
}
|
||||
|
||||
void element_apply_orientation(element_t *elem, video_layout_orientation_t orientation)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < elem->components_count; ++i)
|
||||
{
|
||||
component_t *comp;
|
||||
comp = &elem->components[i];
|
||||
comp->orientation ^= orientation;
|
||||
|
||||
if (orientation & VIDEO_LAYOUT_SWAP_XY)
|
||||
{
|
||||
video_layout_bounds_t b;
|
||||
b = comp->bounds;
|
||||
|
||||
comp->bounds.x = b.y;
|
||||
comp->bounds.y = b.x;
|
||||
comp->bounds.w = b.h;
|
||||
comp->bounds.h = b.w;
|
||||
}
|
||||
|
||||
if (orientation & VIDEO_LAYOUT_FLIP_X)
|
||||
comp->bounds.x = 1.0f - comp->bounds.x - comp->bounds.w;
|
||||
|
||||
if (orientation & VIDEO_LAYOUT_FLIP_Y)
|
||||
comp->bounds.y = 1.0f - comp->bounds.y - comp->bounds.h;
|
||||
}
|
||||
}
|
28
gfx/video_layout/element.h
Normal file
28
gfx/video_layout/element.h
Normal file
@ -0,0 +1,28 @@
|
||||
#ifndef VIDEO_LAYOUT_ELEMENT_H
|
||||
#define VIDEO_LAYOUT_ELEMENT_H
|
||||
#include "internal.h"
|
||||
#include "component.h"
|
||||
|
||||
typedef struct element
|
||||
{
|
||||
char *name;
|
||||
int state;
|
||||
int o_bind;
|
||||
int i_bind;
|
||||
int i_mask;
|
||||
bool i_raw;
|
||||
|
||||
video_layout_bounds_t bounds;
|
||||
video_layout_bounds_t render_bounds;
|
||||
|
||||
component_t *components;
|
||||
int components_count;
|
||||
}
|
||||
element_t;
|
||||
|
||||
void element_init (element_t *elem, const char *name, int components_count);
|
||||
void element_copy (element_t *elem, const element_t *src);
|
||||
void element_deinit (element_t *elem);
|
||||
void element_apply_orientation (element_t *elem, video_layout_orientation_t orientation);
|
||||
|
||||
#endif
|
181
gfx/video_layout/internal.c
Normal file
181
gfx/video_layout/internal.c
Normal file
@ -0,0 +1,181 @@
|
||||
#include "internal.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <compat/posix_string.h>
|
||||
|
||||
char *init_string(const char *src)
|
||||
{
|
||||
return src ? strdup(src) : NULL;
|
||||
}
|
||||
|
||||
void set_string(char **string, const char *src)
|
||||
{
|
||||
free(*string);
|
||||
*string = src ? strdup(src) : NULL;
|
||||
}
|
||||
|
||||
bool vec_size(void **target, size_t elem_size, int count)
|
||||
{
|
||||
const int seg = 4;
|
||||
|
||||
if (--count % seg == 0)
|
||||
{
|
||||
void *resized = realloc(*target, elem_size * (count + seg));
|
||||
if (!resized)
|
||||
return false;
|
||||
*target = resized;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_decimal(const char *str)
|
||||
{
|
||||
float v;
|
||||
|
||||
v = 0.0f;
|
||||
sscanf(str, "%f", &v);
|
||||
return (v && v != (int)v);
|
||||
}
|
||||
|
||||
int get_int(const char *str)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = 0;
|
||||
|
||||
if (str[0] == '#')
|
||||
++str;
|
||||
|
||||
if (str[0] == '$')
|
||||
{
|
||||
unsigned hex;
|
||||
|
||||
++str;
|
||||
sscanf(str, "%x", &hex);
|
||||
res = (int)hex;
|
||||
}
|
||||
else
|
||||
{
|
||||
sscanf(str, "%i", &res);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
float get_dec(const char *str)
|
||||
{
|
||||
float res;
|
||||
|
||||
res = 0.0f;
|
||||
sscanf(str, "%f", &res);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
video_layout_color_t make_color(void)
|
||||
{
|
||||
video_layout_color_t color;
|
||||
color.r = 0.0f;
|
||||
color.g = 0.0f;
|
||||
color.b = 0.0f;
|
||||
color.a = 0.0f;
|
||||
return color;
|
||||
}
|
||||
|
||||
video_layout_color_t make_color_white(void)
|
||||
{
|
||||
video_layout_color_t color;
|
||||
color.r = 1.0f;
|
||||
color.g = 1.0f;
|
||||
color.b = 1.0f;
|
||||
color.a = 1.0f;
|
||||
return color;
|
||||
}
|
||||
|
||||
video_layout_color_t make_color_v(float v)
|
||||
{
|
||||
video_layout_color_t color;
|
||||
color.r = v;
|
||||
color.g = v;
|
||||
color.b = v;
|
||||
color.a = 1.0f;
|
||||
return color;
|
||||
}
|
||||
|
||||
video_layout_color_t make_color_rgb(float r, float g, float b)
|
||||
{
|
||||
video_layout_color_t color;
|
||||
color.r = r;
|
||||
color.g = g;
|
||||
color.b = b;
|
||||
color.a = 1.0f;
|
||||
return color;
|
||||
}
|
||||
|
||||
video_layout_color_t make_color_rgba(float r, float g, float b, float a)
|
||||
{
|
||||
video_layout_color_t color;
|
||||
color.r = r;
|
||||
color.g = g;
|
||||
color.b = b;
|
||||
color.a = a;
|
||||
return color;
|
||||
}
|
||||
|
||||
void color_mod(video_layout_color_t *dst, const video_layout_color_t *src)
|
||||
{
|
||||
dst->r *= src->r;
|
||||
dst->g *= src->g;
|
||||
dst->b *= src->b;
|
||||
dst->a *= src->a;
|
||||
}
|
||||
|
||||
video_layout_bounds_t make_bounds(void)
|
||||
{
|
||||
video_layout_bounds_t bounds;
|
||||
bounds.x = 0.0f;
|
||||
bounds.y = 0.0f;
|
||||
bounds.w = 0.0f;
|
||||
bounds.h = 0.0f;
|
||||
return bounds;
|
||||
}
|
||||
|
||||
video_layout_bounds_t make_bounds_unit(void)
|
||||
{
|
||||
video_layout_bounds_t bounds;
|
||||
bounds.x = 0.0f;
|
||||
bounds.y = 0.0f;
|
||||
bounds.w = 1.0f;
|
||||
bounds.h = 1.0f;
|
||||
return bounds;
|
||||
}
|
||||
|
||||
video_layout_bounds_t bounds_union(const video_layout_bounds_t *a, const video_layout_bounds_t *b)
|
||||
{
|
||||
video_layout_bounds_t bounds;
|
||||
|
||||
if (!bounds_valid(a)) return *b;
|
||||
if (!bounds_valid(b)) return *a;
|
||||
|
||||
bounds.x = MIN(a->x, b->x);
|
||||
bounds.y = MIN(a->y, b->y);
|
||||
bounds.w = MAX(a->x + a->w, b->x + b->w) - bounds.x;
|
||||
bounds.h = MAX(a->y + a->h, b->y + b->h) - bounds.y;
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
void bounds_scale(video_layout_bounds_t *dst, const video_layout_bounds_t *dim)
|
||||
{
|
||||
dst->x *= dim->w;
|
||||
dst->y *= dim->h;
|
||||
dst->w *= dim->w;
|
||||
dst->h *= dim->h;
|
||||
}
|
||||
|
||||
bool bounds_valid(const video_layout_bounds_t *bounds)
|
||||
{
|
||||
return (bounds->w > 0 && bounds->h > 0);
|
||||
}
|
29
gfx/video_layout/internal.h
Normal file
29
gfx/video_layout/internal.h
Normal file
@ -0,0 +1,29 @@
|
||||
#ifndef VIDEO_LAYOUT_INTERNAL_H
|
||||
#define VIDEO_LAYOUT_INTERNAL_H
|
||||
#include "types.h"
|
||||
#include <retro_miscellaneous.h>
|
||||
#include <boolean.h>
|
||||
#include <stddef.h>
|
||||
|
||||
char *init_string (const char *src);
|
||||
void set_string (char **string, const char *src);
|
||||
bool vec_size (void **target, size_t elem_size, int count);
|
||||
|
||||
bool is_decimal (const char *str);
|
||||
int get_int (const char *str);
|
||||
float get_dec (const char *str);
|
||||
|
||||
video_layout_color_t make_color (void);
|
||||
video_layout_color_t make_color_white (void);
|
||||
video_layout_color_t make_color_v (float v);
|
||||
video_layout_color_t make_color_rgb (float r, float g, float b);
|
||||
video_layout_color_t make_color_rgba (float r, float g, float b, float a);
|
||||
void color_mod (video_layout_color_t *dst, const video_layout_color_t *src);
|
||||
|
||||
video_layout_bounds_t make_bounds (void);
|
||||
video_layout_bounds_t make_bounds_unit (void);
|
||||
video_layout_bounds_t bounds_union (const video_layout_bounds_t *a, const video_layout_bounds_t *b);
|
||||
void bounds_scale (video_layout_bounds_t *dst, const video_layout_bounds_t *dim);
|
||||
bool bounds_valid (const video_layout_bounds_t *bounds);
|
||||
|
||||
#endif
|
762
gfx/video_layout/load.c
Normal file
762
gfx/video_layout/load.c
Normal file
@ -0,0 +1,762 @@
|
||||
#include "internal.h"
|
||||
#include "view.h"
|
||||
#include "scope.h"
|
||||
#include <formats/rxml.h>
|
||||
#include <verbosity.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
int video_layout_io_find(const char *name);
|
||||
|
||||
static const char *const comp_type_str[] = {
|
||||
NULL, /* VIDEO_LAYOUT_C_UNKNOWN */
|
||||
NULL, /* VIDEO_LAYOUT_C_SCREEN */
|
||||
"rect",
|
||||
"disk",
|
||||
"image",
|
||||
"text",
|
||||
"dotmatrixdot",
|
||||
"dotmatrix5dot",
|
||||
"dotmatrix",
|
||||
"led7seg",
|
||||
"led8seg_gts1",
|
||||
"led14seg",
|
||||
"led14segsc",
|
||||
"led16seg",
|
||||
"led16segsc",
|
||||
"simplecounter",
|
||||
"reel"
|
||||
};
|
||||
|
||||
static const char *const video_layout_internal_device_params[] =
|
||||
{
|
||||
"devicetag" , ":",
|
||||
"devicebasetag" , "root",
|
||||
"devicename" , "RetroArch",
|
||||
"deviceshortname" , "libretro"
|
||||
};
|
||||
|
||||
static const char *const video_layout_internal_screen_params[] =
|
||||
{
|
||||
"scr#physicalxaspect" , "1",
|
||||
"scr#physicalyaspect" , "1",
|
||||
"scr#nativexaspect" , "1",
|
||||
"scr#nativeyaspect" , "1",
|
||||
"scr#width" , "1",
|
||||
"scr#height" , "1"
|
||||
};
|
||||
|
||||
static int child_count(rxml_node_t *node)
|
||||
{
|
||||
int res;
|
||||
rxml_node_t *child;
|
||||
|
||||
res = 0;
|
||||
|
||||
for (child = node->children; child; child = child->next)
|
||||
++res;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static comp_type_t comp_type_from_str(const char *s)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 2; i < ARRAY_SIZE(comp_type_str); ++i)
|
||||
{
|
||||
if (strcmp(s, comp_type_str[i]) == 0)
|
||||
return (comp_type_t)(int)i;
|
||||
}
|
||||
|
||||
return VIDEO_LAYOUT_C_UNKNOWN;
|
||||
}
|
||||
|
||||
static void init_device_params(scope_t *scope)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(video_layout_internal_device_params); i += 2)
|
||||
{
|
||||
scope_param(scope, video_layout_internal_device_params[i], video_layout_internal_device_params[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
static void init_screen_params(scope_t *scope, int screen_index)
|
||||
{
|
||||
char buf[64];
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(video_layout_internal_screen_params); i += 2)
|
||||
{
|
||||
strcpy(buf, video_layout_internal_screen_params[i + 1]);
|
||||
buf[3] = '0' + screen_index;
|
||||
|
||||
scope_param(scope, video_layout_internal_screen_params[i], buf);
|
||||
}
|
||||
}
|
||||
|
||||
static video_layout_bounds_t parse_bounds(scope_t *scope, rxml_node_t *node)
|
||||
{
|
||||
video_layout_bounds_t bounds;
|
||||
const char *prop;
|
||||
|
||||
bounds = make_bounds_unit();
|
||||
|
||||
if ((prop = scope_eval(scope, rxml_node_attrib(node, "x")))) bounds.x = get_dec(prop);
|
||||
if ((prop = scope_eval(scope, rxml_node_attrib(node, "y")))) bounds.y = get_dec(prop);
|
||||
if ((prop = scope_eval(scope, rxml_node_attrib(node, "width")))) bounds.w = get_dec(prop);
|
||||
if ((prop = scope_eval(scope, rxml_node_attrib(node, "height")))) bounds.h = get_dec(prop);
|
||||
|
||||
if ((prop = scope_eval(scope, rxml_node_attrib(node, "left")))) bounds.x = get_dec(prop);
|
||||
if ((prop = scope_eval(scope, rxml_node_attrib(node, "top")))) bounds.y = get_dec(prop);
|
||||
if ((prop = scope_eval(scope, rxml_node_attrib(node, "right")))) bounds.w = get_dec(prop) - bounds.x;
|
||||
if ((prop = scope_eval(scope, rxml_node_attrib(node, "bottom")))) bounds.h = get_dec(prop) - bounds.y;
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
static video_layout_color_t parse_color(scope_t *scope, rxml_node_t *node)
|
||||
{
|
||||
video_layout_color_t color;
|
||||
const char *prop;
|
||||
|
||||
color = make_color_white();
|
||||
|
||||
if ((prop = scope_eval(scope, rxml_node_attrib(node, "red")))) color.r = get_dec(prop);
|
||||
if ((prop = scope_eval(scope, rxml_node_attrib(node, "green")))) color.g = get_dec(prop);
|
||||
if ((prop = scope_eval(scope, rxml_node_attrib(node, "blue")))) color.b = get_dec(prop);
|
||||
if ((prop = scope_eval(scope, rxml_node_attrib(node, "alpha")))) color.a = get_dec(prop);
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
static video_layout_orientation_t parse_orientation(scope_t *scope, rxml_node_t *node)
|
||||
{
|
||||
video_layout_orientation_t result;
|
||||
const char *prop;
|
||||
|
||||
result = VIDEO_LAYOUT_ROT0;
|
||||
|
||||
if ((prop = scope_eval(scope, rxml_node_attrib(node, "rotate"))))
|
||||
{
|
||||
if (strcmp(prop, "90") == 0)
|
||||
result = VIDEO_LAYOUT_ROT90;
|
||||
|
||||
else if (strcmp(prop, "180") == 0)
|
||||
result = VIDEO_LAYOUT_ROT180;
|
||||
|
||||
else if (strcmp(prop, "270") == 0)
|
||||
result = VIDEO_LAYOUT_ROT270;
|
||||
}
|
||||
|
||||
if ((prop = scope_eval(scope, rxml_node_attrib(node, "swapxy"))))
|
||||
{
|
||||
if (strcmp(prop, "no") != 0)
|
||||
result ^= VIDEO_LAYOUT_SWAP_XY;
|
||||
}
|
||||
|
||||
if ((prop = scope_eval(scope, rxml_node_attrib(node, "flipx"))))
|
||||
{
|
||||
if (strcmp(prop, "no") != 0)
|
||||
result ^= VIDEO_LAYOUT_FLIP_X;
|
||||
}
|
||||
|
||||
if ((prop = scope_eval(scope, rxml_node_attrib(node, "flipy"))))
|
||||
{
|
||||
if (strcmp(prop, "no") != 0)
|
||||
result ^= VIDEO_LAYOUT_FLIP_Y;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool load_param(scope_t *scope, rxml_node_t *node, bool can_repeat)
|
||||
{
|
||||
const char *name;
|
||||
const char *value;
|
||||
const char *start;
|
||||
|
||||
if (!(name = rxml_node_attrib(node, "name")))
|
||||
{
|
||||
RARCH_LOG("video_layout: <param> is missing 'name' attribute\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
value = rxml_node_attrib(node, "value");
|
||||
start = rxml_node_attrib(node, "start");
|
||||
|
||||
if (can_repeat && start)
|
||||
{
|
||||
const char *inc = rxml_node_attrib(node, "increment");
|
||||
const char *ls = rxml_node_attrib(node, "lshift");
|
||||
const char *rs = rxml_node_attrib(node, "rshift");
|
||||
|
||||
if (inc || ls || rs)
|
||||
{
|
||||
scope_generator(scope, name, start, inc, ls, rs);
|
||||
}
|
||||
else
|
||||
{
|
||||
RARCH_LOG("video_layout: invalid generator <param name=\"%s\" /> missing increment/shift\n",
|
||||
scope_eval(scope, name));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (name && value)
|
||||
{
|
||||
scope_param(scope, name, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
RARCH_LOG("video_layout: invalid parameter <param name=\"%s\" /> missing value\n",
|
||||
scope_eval(scope, name));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool load_component(scope_t *scope, component_t *comp, rxml_node_t *node)
|
||||
{
|
||||
comp_type_t type;
|
||||
bool result;
|
||||
const char *state;
|
||||
const char *attr;
|
||||
rxml_node_t *n;
|
||||
|
||||
type = comp_type_from_str(node->name);
|
||||
result = true;
|
||||
|
||||
if (type == VIDEO_LAYOUT_C_UNKNOWN)
|
||||
{
|
||||
RARCH_LOG("video_layout: invalid component <%s />\n", node->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
component_init(comp, type);
|
||||
|
||||
if ((state = rxml_node_attrib(node, "state")))
|
||||
comp->enabled_state = get_int(scope_eval(scope, state));
|
||||
|
||||
for (n = node->children; n; n = n->next)
|
||||
{
|
||||
if (strcmp(n->name, "bounds") == 0)
|
||||
comp->bounds = parse_bounds(scope, n);
|
||||
|
||||
else if (strcmp(n->name, "color") == 0)
|
||||
comp->color = parse_color(scope, n);
|
||||
}
|
||||
|
||||
switch (comp->type)
|
||||
{
|
||||
case VIDEO_LAYOUT_C_UNKNOWN:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_SCREEN:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_RECT:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_DISK:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_IMAGE:
|
||||
{
|
||||
if (!(attr = rxml_node_attrib(node, "file")))
|
||||
{
|
||||
RARCH_LOG("video_layout: invalid component <%s />, missing 'file' attribute\n", node->name);
|
||||
result = false;
|
||||
}
|
||||
set_string(&comp->attr.image.file, scope_eval(scope, attr));
|
||||
|
||||
if ((attr = rxml_node_attrib(node, "alphafile")))
|
||||
set_string(&comp->attr.image.alpha_file, scope_eval(scope, attr));
|
||||
}
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_TEXT:
|
||||
{
|
||||
if (!(attr = rxml_node_attrib(node, "string")))
|
||||
{
|
||||
RARCH_LOG("video_layout: invalid component <%s />, missing 'string' attribute\n", node->name);
|
||||
result = false;
|
||||
}
|
||||
set_string(&comp->attr.text.string, scope_eval(scope, attr));
|
||||
|
||||
if ((attr = rxml_node_attrib(node, "align")))
|
||||
comp->attr.text.align = (video_layout_text_align_t)get_int(scope_eval(scope, attr));
|
||||
}
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_COUNTER:
|
||||
{
|
||||
if ((attr = rxml_node_attrib(node, "digits")))
|
||||
comp->attr.counter.digits = get_int(scope_eval(scope, attr));
|
||||
|
||||
if ((attr = rxml_node_attrib(node, "maxstate")))
|
||||
comp->attr.counter.max_state = get_int(scope_eval(scope, attr));
|
||||
|
||||
if ((attr = rxml_node_attrib(node, "align")))
|
||||
comp->attr.counter.align = (video_layout_text_align_t)get_int(scope_eval(scope, attr));
|
||||
}
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_DOTMATRIX_X1:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_DOTMATRIX_H5:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_DOTMATRIX_H8:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_LED_7:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_LED_8_GTS1:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_LED_14:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_LED_14_SC:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_LED_16:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_LED_16_SC:
|
||||
break;
|
||||
case VIDEO_LAYOUT_C_REEL:
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool load_element(scope_t *scope, rxml_node_t *node)
|
||||
{
|
||||
const char *name;
|
||||
const char *state;
|
||||
bool result;
|
||||
int i;
|
||||
element_t *elem;
|
||||
rxml_node_t *n;
|
||||
video_layout_bounds_t dim;
|
||||
|
||||
result = true;
|
||||
|
||||
if (!(name = rxml_node_attrib(node, "name")))
|
||||
{
|
||||
RARCH_LOG("video_layout: <element> is missing 'name' attribute\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
elem = scope_add_element(scope);
|
||||
element_init(elem, scope_eval(scope, name), child_count(node));
|
||||
|
||||
if ((state = rxml_node_attrib(node, "defstate")))
|
||||
elem->state = get_int(scope_eval(scope, state));
|
||||
|
||||
i = 0;
|
||||
for (n = node->children; n; n = n->next, ++i)
|
||||
{
|
||||
component_t *comp;
|
||||
comp = &elem->components[i];
|
||||
|
||||
if (load_component(scope, comp, n))
|
||||
elem->bounds = bounds_union(&elem->bounds, &comp->bounds);
|
||||
else
|
||||
result = false;
|
||||
}
|
||||
|
||||
if (bounds_valid(&elem->bounds))
|
||||
{
|
||||
dim.x = elem->bounds.x / elem->bounds.w;
|
||||
dim.y = elem->bounds.y / elem->bounds.h;
|
||||
dim.w = 1.0f / elem->bounds.w;
|
||||
dim.h = 1.0f / elem->bounds.h;
|
||||
}
|
||||
else
|
||||
{
|
||||
dim = make_bounds_unit();
|
||||
}
|
||||
|
||||
for (i = 0; i < elem->components_count; ++i)
|
||||
{
|
||||
component_t *comp;
|
||||
comp = &elem->components[i];
|
||||
|
||||
if (bounds_valid(&comp->bounds))
|
||||
bounds_scale(&comp->bounds, &dim);
|
||||
else
|
||||
comp->bounds = dim;
|
||||
|
||||
comp->bounds.x -= dim.x;
|
||||
comp->bounds.y -= dim.y;
|
||||
}
|
||||
|
||||
elem->bounds = make_bounds_unit();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool load_screen(scope_t *scope, element_t *elem, rxml_node_t *node)
|
||||
{
|
||||
const char *index;
|
||||
component_t *comp;
|
||||
|
||||
index = rxml_node_attrib(node, "index");
|
||||
|
||||
element_init(elem, NULL, 1);
|
||||
comp = &elem->components[0];
|
||||
|
||||
component_init(comp, VIDEO_LAYOUT_C_SCREEN);
|
||||
comp->bounds = make_bounds_unit();
|
||||
comp->attr.screen.index = get_int(scope_eval(scope, index));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void merge_group(scope_t *scope, view_t *view, view_t *group,
|
||||
bool has_bounds, video_layout_bounds_t n_bounds, video_layout_orientation_t n_orient, video_layout_color_t n_color)
|
||||
{
|
||||
bool constrain;
|
||||
int i, j, k;
|
||||
|
||||
constrain = bounds_valid(&n_bounds);
|
||||
|
||||
for (i = 0; i < group->layers_count; ++i)
|
||||
{
|
||||
layer_t *group_layer;
|
||||
layer_t *layer;
|
||||
|
||||
group_layer = &group->layers[i];
|
||||
layer = view_emplace_layer(view, group_layer->name);
|
||||
|
||||
for (j = 0; j < group_layer->elements_count; ++j)
|
||||
{
|
||||
element_t *elem;
|
||||
elem = layer_add_element(layer);
|
||||
|
||||
element_copy(elem, &group_layer->elements[j]);
|
||||
|
||||
for (k = 0; k < elem->components_count; ++k)
|
||||
color_mod(&elem->components->color, &n_color);
|
||||
|
||||
if (n_orient)
|
||||
element_apply_orientation(elem, n_orient);
|
||||
|
||||
if (constrain)
|
||||
{
|
||||
bounds_scale(&elem->bounds, &n_bounds);
|
||||
elem->bounds.x += n_bounds.x;
|
||||
elem->bounds.y += n_bounds.y;
|
||||
}
|
||||
|
||||
if (!has_bounds)
|
||||
view->bounds = bounds_union(&view->bounds, &elem->bounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool load_view(scope_t *scope, view_t *view, rxml_node_t *node, bool is_named)
|
||||
{
|
||||
bool result, has_bounds;
|
||||
rxml_node_t *n;
|
||||
rxml_node_t *o;
|
||||
int i;
|
||||
|
||||
if (is_named)
|
||||
{
|
||||
const char *name;
|
||||
|
||||
if (!(name = rxml_node_attrib(node, "name")))
|
||||
{
|
||||
RARCH_LOG("video_layout: <view> is missing 'name' attribute\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
view_init(view, scope_eval(scope, name));
|
||||
}
|
||||
|
||||
result = true;
|
||||
has_bounds = false;
|
||||
|
||||
for (n = node->children; n; n = n->next)
|
||||
{
|
||||
video_layout_color_t n_color;
|
||||
video_layout_bounds_t n_bounds;
|
||||
video_layout_orientation_t n_orient;
|
||||
|
||||
if (strcmp(n->name, "param") == 0)
|
||||
{
|
||||
if (!load_param(scope, n, true))
|
||||
result = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
else if (strcmp(n->name, "bounds") == 0)
|
||||
{
|
||||
view->bounds = parse_bounds(scope, n);
|
||||
has_bounds = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
n_color = make_color_white();
|
||||
n_bounds = make_bounds();
|
||||
n_orient = VIDEO_LAYOUT_ROT0;
|
||||
|
||||
for (o = n->children; o; o = o->next)
|
||||
{
|
||||
if (strcmp(o->name, "color") == 0)
|
||||
n_color = parse_color(scope, o);
|
||||
|
||||
else if (strcmp(o->name, "bounds") == 0)
|
||||
n_bounds = parse_bounds(scope, o);
|
||||
|
||||
else if (strcmp(o->name, "orientation") == 0)
|
||||
n_orient = parse_orientation(scope, o);
|
||||
}
|
||||
|
||||
if (strcmp(n->name, "group") == 0)
|
||||
{
|
||||
const char *ref;
|
||||
if ((ref = rxml_node_attrib(n, "ref")))
|
||||
{
|
||||
view_t *group;
|
||||
if ((group = scope_find_group(scope, scope_eval(scope, ref))))
|
||||
{
|
||||
merge_group(scope, view, group, has_bounds, n_bounds, n_orient, n_color);
|
||||
}
|
||||
else
|
||||
{
|
||||
RARCH_LOG("video_layout: group \"%s\" is missing\n", scope_eval(scope, ref));
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RARCH_LOG("video_layout: <group> is missing 'ref' attribute\n");
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
else if (strcmp(n->name, "repeat") == 0)
|
||||
{
|
||||
const char *count_s;
|
||||
int count;
|
||||
|
||||
if (!(count_s = rxml_node_attrib(n, "count")))
|
||||
{
|
||||
RARCH_LOG("video_layout: <repeat> is missing 'count' attribute\n");
|
||||
result = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
count = get_int(scope_eval(scope, count_s));
|
||||
|
||||
scope_push(scope);
|
||||
|
||||
for (o = n->children; o; o = o->next)
|
||||
{
|
||||
if (strcmp(o->name, "param") == 0)
|
||||
{
|
||||
if (!load_param(scope, o, true))
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
{
|
||||
view_t rep;
|
||||
view_init(&rep, NULL);
|
||||
|
||||
if (!load_view(scope, &rep, n, false))
|
||||
result = false;
|
||||
|
||||
merge_group(scope, view, &rep, has_bounds, n_bounds, n_orient, n_color);
|
||||
|
||||
view_deinit(&rep);
|
||||
|
||||
scope_repeat(scope);
|
||||
}
|
||||
|
||||
scope_pop(scope);
|
||||
}
|
||||
|
||||
else /* element */
|
||||
{
|
||||
layer_t *layer;
|
||||
element_t *elem;
|
||||
|
||||
layer = view_emplace_layer(view, n->name);
|
||||
elem = layer_add_element(layer);
|
||||
|
||||
if (strcmp(n->name, "screen") == 0)
|
||||
{
|
||||
if (!load_screen(scope, elem, n))
|
||||
result = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *elem_name;
|
||||
const char *attr;
|
||||
|
||||
if ((elem_name = rxml_node_attrib(n, "element")))
|
||||
{
|
||||
element_t *elem_src;
|
||||
if ((elem_src = scope_find_element(scope, elem_name)))
|
||||
{
|
||||
element_copy(elem, elem_src);
|
||||
|
||||
if ((attr = rxml_node_attrib(n, "name")))
|
||||
elem->o_bind = video_layout_io_find(scope_eval(scope, attr));
|
||||
|
||||
if ((attr = rxml_node_attrib(n, "inputtag")))
|
||||
elem->i_bind = video_layout_io_find(scope_eval(scope, attr));
|
||||
|
||||
if ((attr = rxml_node_attrib(n, "inputmask")))
|
||||
elem->i_mask = get_int(scope_eval(scope, attr));
|
||||
|
||||
if ((attr = rxml_node_attrib(n, "inputraw")))
|
||||
elem->i_raw = get_int(scope_eval(scope, attr)) ? true : false;
|
||||
}
|
||||
else
|
||||
{
|
||||
RARCH_LOG("video_layout: element \"%s\" is missing\n", scope_eval(scope, elem_name));
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RARCH_LOG("video_layout: <%s> is missing 'element' attribute\n", n->name);
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < elem->components_count; ++i)
|
||||
color_mod(&elem->components->color, &n_color);
|
||||
|
||||
elem->bounds = n_bounds;
|
||||
|
||||
if (n_orient)
|
||||
element_apply_orientation(elem, n_orient);
|
||||
|
||||
if (!has_bounds)
|
||||
view->bounds = bounds_union(&view->bounds, &elem->bounds);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool load_group(scope_t *scope, rxml_node_t *node)
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
view_t *group = scope_add_group(scope);
|
||||
|
||||
scope_push(scope);
|
||||
|
||||
if (!load_view(scope, group, node, true))
|
||||
result = false;
|
||||
|
||||
scope_pop(scope);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool load_top_level(scope_t *scope, int *view_count, rxml_node_t *root)
|
||||
{
|
||||
bool result;
|
||||
rxml_node_t *node;
|
||||
|
||||
result = true;
|
||||
*view_count = 0;
|
||||
|
||||
for (node = root->children; node; node = node->next)
|
||||
{
|
||||
if (strcmp(node->name, "param") == 0)
|
||||
{
|
||||
if (!load_param(scope, node, false))
|
||||
result = false;
|
||||
}
|
||||
|
||||
else if (strcmp(node->name, "element") == 0)
|
||||
{
|
||||
if (!load_element(scope, node))
|
||||
result = false;
|
||||
}
|
||||
|
||||
else if (strcmp(node->name, "group") == 0)
|
||||
{
|
||||
if (!load_group(scope, node))
|
||||
result = false;
|
||||
}
|
||||
|
||||
else if (strcmp(node->name, "view") == 0)
|
||||
++(*view_count);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool load_views(scope_t *scope, view_array_t *view_array, rxml_node_t *root)
|
||||
{
|
||||
bool result;
|
||||
int i;
|
||||
rxml_node_t *node;
|
||||
|
||||
result = true;
|
||||
i = 0;
|
||||
|
||||
for (node = root->children; node; node = node->next)
|
||||
{
|
||||
if (strcmp(node->name, "view") == 0)
|
||||
{
|
||||
view_t *view;
|
||||
view = &view_array->views[i];
|
||||
|
||||
scope_push(scope);
|
||||
|
||||
if (!load_view(scope, view, node, true))
|
||||
result = false;
|
||||
|
||||
view_sort_layers(view);
|
||||
view_normalize(view);
|
||||
view_count_screens(view);
|
||||
|
||||
scope_pop(scope);
|
||||
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool load(view_array_t *view_array, rxml_document_t *doc)
|
||||
{
|
||||
bool result;
|
||||
rxml_node_t *root;
|
||||
scope_t scope;
|
||||
int view_count;
|
||||
|
||||
root = rxml_root_node(doc);
|
||||
|
||||
if (strcmp(root->name, "mamelayout") ||
|
||||
strcmp(rxml_node_attrib(root, "version"), "2"))
|
||||
{
|
||||
RARCH_LOG("video_layout: invalid MAME Layout file\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
result = false;
|
||||
|
||||
scope_init(&scope);
|
||||
init_device_params(&scope);
|
||||
init_screen_params(&scope, 0);
|
||||
init_screen_params(&scope, 1);
|
||||
|
||||
if (!load_top_level(&scope, &view_count, root))
|
||||
result = false;
|
||||
|
||||
view_array_init(view_array, view_count);
|
||||
|
||||
if (!load_views(&scope, view_array, root))
|
||||
result = false;
|
||||
|
||||
scope_deinit(&scope);
|
||||
|
||||
return result;
|
||||
}
|
339
gfx/video_layout/scope.c
Normal file
339
gfx/video_layout/scope.c
Normal file
@ -0,0 +1,339 @@
|
||||
#include "scope.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
union number
|
||||
{
|
||||
int val_int;
|
||||
float val_dec;
|
||||
};
|
||||
|
||||
typedef struct generator
|
||||
{
|
||||
bool is_decimal;
|
||||
union number value;
|
||||
union number increment;
|
||||
int shift;
|
||||
}
|
||||
generator_t;
|
||||
|
||||
struct param
|
||||
{
|
||||
char *name;
|
||||
char *value;
|
||||
generator_t *generator;
|
||||
param_t *prev;
|
||||
int level;
|
||||
};
|
||||
|
||||
static void param_deinit(param_t *param)
|
||||
{
|
||||
free(param->generator);
|
||||
free(param->value);
|
||||
free(param->name);
|
||||
}
|
||||
|
||||
static param_t *param_find(scope_t *scope, const char *name, int level)
|
||||
{
|
||||
param_t *param;
|
||||
param = scope->param;
|
||||
|
||||
while (param && param->level >= level)
|
||||
{
|
||||
if (strcmp(param->name, name) == 0)
|
||||
{
|
||||
return param;
|
||||
}
|
||||
param = param->prev;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void scope_init(scope_t *scope)
|
||||
{
|
||||
scope->level = 0;
|
||||
|
||||
scope->param = NULL;
|
||||
|
||||
scope->elements = NULL;
|
||||
scope->elements_count = 0;
|
||||
|
||||
scope->groups = NULL;
|
||||
scope->groups_count = 0;
|
||||
}
|
||||
|
||||
void scope_deinit(scope_t *scope)
|
||||
{
|
||||
int i;
|
||||
param_t *param;
|
||||
param_t *prev;
|
||||
|
||||
for (i = 0; i < scope->elements_count; ++i)
|
||||
element_deinit(&scope->elements[i]);
|
||||
free(scope->elements);
|
||||
|
||||
for (i = 0; i < scope->groups_count; ++i)
|
||||
view_deinit(&scope->groups[i]);
|
||||
free(scope->groups);
|
||||
|
||||
for (param = scope->param; param; param = prev)
|
||||
{
|
||||
prev = param->prev;
|
||||
param_deinit(param);
|
||||
free(param);
|
||||
}
|
||||
}
|
||||
|
||||
void scope_push(scope_t *scope)
|
||||
{
|
||||
++scope->level;
|
||||
}
|
||||
|
||||
void scope_pop(scope_t *scope)
|
||||
{
|
||||
param_t *param;
|
||||
|
||||
--scope->level;
|
||||
|
||||
while ((param = scope->param))
|
||||
{
|
||||
if (param->level <= scope->level)
|
||||
break;
|
||||
|
||||
scope->param = param->prev;
|
||||
param_deinit(param);
|
||||
free(param);
|
||||
}
|
||||
}
|
||||
|
||||
void scope_repeat(scope_t *scope)
|
||||
{
|
||||
param_t *param;
|
||||
|
||||
for (param = scope->param; param && param->level >= scope->level; param = param->prev)
|
||||
{
|
||||
generator_t *gen;
|
||||
if ((gen = param->generator))
|
||||
{
|
||||
char tmp[SCOPE_BUFFER_SIZE];
|
||||
tmp[0] = '\0';
|
||||
|
||||
if (gen->is_decimal)
|
||||
{
|
||||
gen->value.val_dec += gen->increment.val_dec;
|
||||
if (gen->shift > 0)
|
||||
gen->value.val_dec = (float)((int)gen->value.val_dec << gen->shift);
|
||||
else if (gen->shift < 0)
|
||||
gen->value.val_dec = (float)((int)gen->value.val_dec >> -gen->shift);
|
||||
sprintf(tmp, "%f", gen->value.val_dec);
|
||||
}
|
||||
else
|
||||
{
|
||||
gen->value.val_int += gen->increment.val_int;
|
||||
if(gen->shift > 0)
|
||||
gen->value.val_int <<= gen->shift;
|
||||
else if (gen->shift < 0)
|
||||
gen->value.val_int >>= -gen->shift;
|
||||
sprintf(tmp, "%d", gen->value.val_int);
|
||||
}
|
||||
|
||||
set_string(¶m->value, tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void scope_param(scope_t *scope, const char *name, const char *value)
|
||||
{
|
||||
char *eval_name;
|
||||
char *eval_value;
|
||||
param_t *param;
|
||||
|
||||
eval_name = init_string(scope_eval(scope, name));
|
||||
eval_value = init_string(scope_eval(scope, value));
|
||||
|
||||
if ((param = param_find(scope, eval_name, scope->level)))
|
||||
{
|
||||
free(param->value);
|
||||
param->value = eval_value;
|
||||
}
|
||||
else
|
||||
{
|
||||
param = (param_t*)malloc(sizeof(param_t));
|
||||
param->name = init_string(name);
|
||||
param->value = eval_value;
|
||||
param->generator = NULL;
|
||||
param->level = scope->level;
|
||||
param->prev = scope->param;
|
||||
scope->param = param;
|
||||
}
|
||||
|
||||
free(eval_name);
|
||||
}
|
||||
|
||||
void scope_generator(scope_t *scope, const char *name, const char *start, const char *increment, const char *lshift, const char *rshift)
|
||||
{
|
||||
char *e_name;
|
||||
char *e_val;
|
||||
char *e_inc;
|
||||
generator_t *gen;
|
||||
param_t *param;
|
||||
|
||||
e_name = init_string(scope_eval(scope, name));
|
||||
|
||||
if (param_find(scope, e_name, scope->level))
|
||||
{
|
||||
free(e_name);
|
||||
return;
|
||||
}
|
||||
|
||||
e_val = init_string(scope_eval(scope, start));
|
||||
e_inc = init_string(scope_eval(scope, increment));
|
||||
|
||||
gen = (generator_t*)malloc(sizeof(generator_t));
|
||||
|
||||
param = (param_t*)malloc(sizeof(param_t));
|
||||
param->name = init_string(e_name);
|
||||
param->value = init_string(e_val);
|
||||
param->generator = gen;
|
||||
param->level = scope->level;
|
||||
param->prev = scope->param;
|
||||
scope->param = param;
|
||||
|
||||
gen->is_decimal = is_decimal(e_val) | is_decimal(e_inc);
|
||||
|
||||
if (gen->is_decimal)
|
||||
{
|
||||
gen->value.val_dec = get_dec(e_val);
|
||||
gen->increment.val_dec = get_dec(e_inc);
|
||||
}
|
||||
else
|
||||
{
|
||||
gen->value.val_int = get_int(e_val);
|
||||
gen->increment.val_int = get_int(e_inc);
|
||||
}
|
||||
|
||||
gen->shift = 0;
|
||||
|
||||
if (lshift)
|
||||
gen->shift += get_int(scope_eval(scope, lshift));
|
||||
|
||||
if (rshift)
|
||||
gen->shift -= get_int(scope_eval(scope, rshift));
|
||||
|
||||
free(e_inc);
|
||||
free(e_val);
|
||||
free(e_name);
|
||||
}
|
||||
|
||||
const char *scope_eval(scope_t *scope, const char *src)
|
||||
{
|
||||
const char* next;
|
||||
bool in_var;
|
||||
|
||||
char tmp[SCOPE_BUFFER_SIZE];
|
||||
|
||||
if (!src)
|
||||
return NULL;
|
||||
|
||||
scope->eval[0] = '\0';
|
||||
next = src;
|
||||
|
||||
while (next[0] != '\0')
|
||||
{
|
||||
const char* cur;
|
||||
cur = next;
|
||||
|
||||
if ((in_var = (next[0] == '~')))
|
||||
++cur;
|
||||
|
||||
next = strchr(cur, '~');
|
||||
|
||||
if (next && next != cur)
|
||||
{
|
||||
size_t len;
|
||||
len = next - cur;
|
||||
|
||||
if (in_var)
|
||||
{
|
||||
param_t *param;
|
||||
|
||||
strncpy(tmp, cur, len);
|
||||
tmp[len] = '\0';
|
||||
|
||||
if ((param = param_find(scope, tmp, 0)))
|
||||
strcat(scope->eval, param->value);
|
||||
else
|
||||
strcat(scope->eval, tmp);
|
||||
|
||||
++next;
|
||||
}
|
||||
else
|
||||
{
|
||||
strncat(scope->eval, cur, len);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (in_var)
|
||||
--cur;
|
||||
strcat(scope->eval, cur);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return scope->eval;
|
||||
}
|
||||
|
||||
element_t *scope_add_element(scope_t *scope)
|
||||
{
|
||||
element_t *elem;
|
||||
|
||||
vec_size((void**)&scope->elements, sizeof(element_t), ++scope->elements_count);
|
||||
|
||||
elem = &scope->elements[scope->elements_count - 1];
|
||||
element_init(elem, NULL, 0);
|
||||
|
||||
return elem;
|
||||
}
|
||||
|
||||
element_t *scope_find_element(scope_t *scope, const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < scope->elements_count; ++i)
|
||||
{
|
||||
if (strcmp(name, scope->elements[i].name) == 0)
|
||||
return &scope->elements[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
view_t *scope_add_group(scope_t *scope)
|
||||
{
|
||||
view_t *group;
|
||||
|
||||
vec_size((void**)&scope->groups, sizeof(view_t), ++scope->groups_count);
|
||||
|
||||
group = &scope->groups[scope->groups_count - 1];
|
||||
view_init(group, NULL);
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
view_t *scope_find_group(scope_t *scope, const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < scope->groups_count; ++i)
|
||||
{
|
||||
if (strcmp(name, scope->groups[i].name) == 0)
|
||||
return &scope->groups[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
42
gfx/video_layout/scope.h
Normal file
42
gfx/video_layout/scope.h
Normal file
@ -0,0 +1,42 @@
|
||||
#ifndef VIDEO_LAYOUT_SCOPE_H
|
||||
#define VIDEO_LAYOUT_SCOPE_H
|
||||
#include "view.h"
|
||||
#include "element.h"
|
||||
|
||||
#define SCOPE_BUFFER_SIZE 256
|
||||
|
||||
typedef struct param param_t;
|
||||
|
||||
typedef struct scope
|
||||
{
|
||||
int level;
|
||||
|
||||
param_t *param;
|
||||
|
||||
element_t *elements;
|
||||
int elements_count;
|
||||
|
||||
view_t *groups;
|
||||
int groups_count;
|
||||
|
||||
char eval[SCOPE_BUFFER_SIZE];
|
||||
}
|
||||
scope_t;
|
||||
|
||||
void scope_init (scope_t *scope);
|
||||
void scope_deinit (scope_t *scope);
|
||||
void scope_push (scope_t *scope);
|
||||
void scope_pop (scope_t *scope);
|
||||
void scope_repeat (scope_t *scope);
|
||||
|
||||
void scope_param (scope_t *scope, const char *name, const char *value);
|
||||
void scope_generator (scope_t *scope, const char *name, const char *start, const char *increment, const char *lshift, const char *rshift);
|
||||
const char *scope_eval (scope_t *scope, const char *src);
|
||||
|
||||
element_t *scope_add_element (scope_t *scope);
|
||||
element_t *scope_find_element (scope_t *scope, const char *name);
|
||||
|
||||
view_t *scope_add_group (scope_t *scope);
|
||||
view_t *scope_find_group (scope_t *scope, const char *name);
|
||||
|
||||
#endif
|
49
gfx/video_layout/types.h
Normal file
49
gfx/video_layout/types.h
Normal file
@ -0,0 +1,49 @@
|
||||
#ifndef VIDEO_LAYOUT_TYPES_H
|
||||
#define VIDEO_LAYOUT_TYPES_H
|
||||
|
||||
typedef unsigned char video_layout_orientation_t;
|
||||
|
||||
#define VIDEO_LAYOUT_FLIP_X 1
|
||||
#define VIDEO_LAYOUT_FLIP_Y 2
|
||||
#define VIDEO_LAYOUT_SWAP_XY 4
|
||||
|
||||
#define VIDEO_LAYOUT_ROT0 0
|
||||
#define VIDEO_LAYOUT_ROT90 VIDEO_LAYOUT_SWAP_XY | VIDEO_LAYOUT_FLIP_X
|
||||
#define VIDEO_LAYOUT_ROT180 VIDEO_LAYOUT_FLIP_X | VIDEO_LAYOUT_FLIP_Y
|
||||
#define VIDEO_LAYOUT_ROT270 VIDEO_LAYOUT_SWAP_XY | VIDEO_LAYOUT_FLIP_Y
|
||||
|
||||
typedef enum video_layout_blend
|
||||
{
|
||||
VIDEO_LAYOUT_BLEND_ALPHA,
|
||||
VIDEO_LAYOUT_BLEND_ADD,
|
||||
VIDEO_LAYOUT_BLEND_MOD
|
||||
}
|
||||
video_layout_blend_t;
|
||||
|
||||
typedef enum video_layout_text_align
|
||||
{
|
||||
VIDEO_LAYOUT_TEXT_ALIGN_CENTER,
|
||||
VIDEO_LAYOUT_TEXT_ALIGN_LEFT,
|
||||
VIDEO_LAYOUT_TEXT_ALIGN_RIGHT
|
||||
}
|
||||
video_layout_text_align_t;
|
||||
|
||||
typedef struct video_layout_color
|
||||
{
|
||||
float r;
|
||||
float g;
|
||||
float b;
|
||||
float a;
|
||||
}
|
||||
video_layout_color_t;
|
||||
|
||||
typedef struct video_layout_bounds
|
||||
{
|
||||
float x;
|
||||
float y;
|
||||
float w;
|
||||
float h;
|
||||
}
|
||||
video_layout_bounds_t;
|
||||
|
||||
#endif
|
259
gfx/video_layout/view.c
Normal file
259
gfx/video_layout/view.c
Normal file
@ -0,0 +1,259 @@
|
||||
#include "view.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void layer_init(layer_t *layer, const char *name)
|
||||
{
|
||||
layer->name = init_string(name);
|
||||
layer->blend = VIDEO_LAYOUT_BLEND_ALPHA;
|
||||
layer->elements = NULL;
|
||||
layer->elements_count = 0;
|
||||
}
|
||||
|
||||
void layer_deinit(layer_t *layer)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < layer->elements_count; ++i)
|
||||
element_deinit(&layer->elements[i]);
|
||||
|
||||
free(layer->elements);
|
||||
free(layer->name);
|
||||
}
|
||||
|
||||
element_t *layer_add_element(layer_t *layer)
|
||||
{
|
||||
element_t *elem;
|
||||
|
||||
vec_size((void**)&layer->elements, sizeof(element_t), ++layer->elements_count);
|
||||
|
||||
elem = &layer->elements[layer->elements_count - 1];
|
||||
element_init(elem, NULL, 0);
|
||||
|
||||
return elem;
|
||||
}
|
||||
|
||||
void view_init(view_t *view, const char *name)
|
||||
{
|
||||
view->name = init_string(name);
|
||||
view->bounds = make_bounds();
|
||||
view->render_bounds = make_bounds_unit();
|
||||
view->layers = NULL;
|
||||
view->layers_count = 0;
|
||||
view->screens = NULL;
|
||||
view->screens_count = 0;
|
||||
}
|
||||
|
||||
void view_deinit(view_t *view)
|
||||
{
|
||||
int i;
|
||||
|
||||
free(view->screens);
|
||||
|
||||
for (i = 0; i < view->layers_count; ++i)
|
||||
layer_deinit(&view->layers[i]);
|
||||
|
||||
free(view->layers);
|
||||
free(view->name);
|
||||
}
|
||||
|
||||
layer_t *view_find_layer(view_t *view, const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < view->layers_count; ++i)
|
||||
{
|
||||
if (strcmp(name, view->layers[i].name) == 0)
|
||||
return &view->layers[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
layer_t *view_emplace_layer(view_t *view, const char *name)
|
||||
{
|
||||
layer_t *layer = view_find_layer(view, name);
|
||||
|
||||
if (!layer)
|
||||
{
|
||||
vec_size((void**)&view->layers, sizeof(layer_t), ++view->layers_count);
|
||||
|
||||
layer = &view->layers[view->layers_count - 1];
|
||||
layer_init(layer, name);
|
||||
}
|
||||
|
||||
return layer;
|
||||
}
|
||||
|
||||
void view_sort_layers(view_t *view)
|
||||
{
|
||||
layer_t sorted[6];
|
||||
layer_t *layer;
|
||||
int i = 0;
|
||||
|
||||
/* retroarch frame *= screen's color */
|
||||
if ((layer = view_find_layer(view, "screen")))
|
||||
{
|
||||
layer->blend = VIDEO_LAYOUT_BLEND_MOD;
|
||||
sorted[i] = *layer;
|
||||
++i;
|
||||
}
|
||||
|
||||
if ((layer = view_find_layer(view, "overlay")))
|
||||
{
|
||||
layer->blend = VIDEO_LAYOUT_BLEND_MOD;
|
||||
sorted[i] = *layer;
|
||||
++i;
|
||||
}
|
||||
|
||||
if ((layer = view_find_layer(view, "backdrop")))
|
||||
{
|
||||
layer->blend = VIDEO_LAYOUT_BLEND_ADD;
|
||||
sorted[i] = *layer;
|
||||
++i;
|
||||
}
|
||||
|
||||
if ((layer = view_find_layer(view, "bezel")))
|
||||
{
|
||||
layer->blend = VIDEO_LAYOUT_BLEND_ALPHA;
|
||||
sorted[i] = *layer;
|
||||
++i;
|
||||
}
|
||||
|
||||
if ((layer = view_find_layer(view, "cpanel")))
|
||||
{
|
||||
layer->blend = VIDEO_LAYOUT_BLEND_ALPHA;
|
||||
sorted[i] = *layer;
|
||||
++i;
|
||||
}
|
||||
|
||||
if ((layer = view_find_layer(view, "marquee")))
|
||||
{
|
||||
layer->blend = VIDEO_LAYOUT_BLEND_ALPHA;
|
||||
sorted[i] = *layer;
|
||||
++i;
|
||||
}
|
||||
|
||||
for (i = 0; i < view->layers_count; ++i)
|
||||
view->layers[i] = sorted[i];
|
||||
}
|
||||
|
||||
void view_normalize(view_t *view)
|
||||
{
|
||||
video_layout_bounds_t dim;
|
||||
int i, j;
|
||||
|
||||
if (bounds_valid(&view->bounds))
|
||||
{
|
||||
dim.x = view->bounds.x / view->bounds.w;
|
||||
dim.y = view->bounds.y / view->bounds.h;
|
||||
dim.w = 1.0f / view->bounds.w;
|
||||
dim.h = 1.0f / view->bounds.h;
|
||||
|
||||
if (view->bounds.w < view->bounds.h)
|
||||
{
|
||||
view->bounds.w = view->bounds.w / view->bounds.h;
|
||||
view->bounds.h = 1.f;
|
||||
}
|
||||
else
|
||||
{
|
||||
view->bounds.h = view->bounds.h / view->bounds.w;
|
||||
view->bounds.w = 1.f;
|
||||
}
|
||||
|
||||
view->bounds.x = 0;
|
||||
view->bounds.y = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
dim = view->bounds = make_bounds_unit();
|
||||
}
|
||||
|
||||
for (i = 0; i < view->layers_count; ++i)
|
||||
{
|
||||
layer_t *layer;
|
||||
layer = &view->layers[i];
|
||||
|
||||
for (j = 0; j < layer->elements_count; ++j)
|
||||
{
|
||||
element_t *elem;
|
||||
elem = &layer->elements[j];
|
||||
|
||||
if (bounds_valid(&elem->bounds))
|
||||
{
|
||||
bounds_scale(&elem->bounds, &dim);
|
||||
}
|
||||
else
|
||||
{
|
||||
elem->bounds = dim;
|
||||
}
|
||||
|
||||
elem->bounds.x -= dim.x;
|
||||
elem->bounds.y -= dim.y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void view_count_screens(view_t *view)
|
||||
{
|
||||
int idx, i, j, k;
|
||||
|
||||
idx = -1;
|
||||
|
||||
for (i = 0; i < view->layers_count; ++i)
|
||||
{
|
||||
layer_t *layer = &view->layers[i];
|
||||
for (j = 0; j < layer->elements_count; ++j)
|
||||
{
|
||||
element_t *elem = &layer->elements[j];
|
||||
for (k = 0; k < elem->components_count; ++k)
|
||||
{
|
||||
component_t *comp = &elem->components[k];
|
||||
if (comp->type == VIDEO_LAYOUT_C_SCREEN)
|
||||
idx = MAX(idx, comp->attr.screen.index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (view->screens_count)
|
||||
{
|
||||
free(view->screens);
|
||||
view->screens_count = 0;
|
||||
}
|
||||
|
||||
if ((++idx))
|
||||
{
|
||||
view->screens = (video_layout_bounds_t*)calloc(idx, sizeof(video_layout_bounds_t));
|
||||
view->screens_count = idx;
|
||||
}
|
||||
}
|
||||
|
||||
void view_array_init(view_array_t *view_array, int views_count)
|
||||
{
|
||||
view_array->views = (view_t*)(views_count > 0 ?
|
||||
calloc(views_count, sizeof(view_t)) : NULL);
|
||||
view_array->views_count = views_count;
|
||||
}
|
||||
|
||||
void view_array_deinit(view_array_t *view_array)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < view_array->views_count; ++i)
|
||||
view_deinit(&view_array->views[i]);
|
||||
free(view_array->views);
|
||||
view_array->views = NULL;
|
||||
view_array->views_count = 0;
|
||||
}
|
||||
|
||||
view_t *view_array_find(view_array_t *view_array, const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < view_array->views_count; ++i)
|
||||
{
|
||||
if (strcmp(name, view_array->views[i].name) == 0)
|
||||
return &view_array->views[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
53
gfx/video_layout/view.h
Normal file
53
gfx/video_layout/view.h
Normal file
@ -0,0 +1,53 @@
|
||||
#ifndef VIDEO_LAYOUT_VIEW_H
|
||||
#define VIDEO_LAYOUT_VIEW_H
|
||||
#include "internal.h"
|
||||
#include "element.h"
|
||||
|
||||
typedef struct layer
|
||||
{
|
||||
char *name;
|
||||
video_layout_blend_t blend;
|
||||
|
||||
element_t *elements;
|
||||
int elements_count;
|
||||
}
|
||||
layer_t;
|
||||
|
||||
typedef struct view
|
||||
{
|
||||
char *name;
|
||||
video_layout_bounds_t bounds;
|
||||
video_layout_bounds_t render_bounds;
|
||||
|
||||
layer_t *layers;
|
||||
int layers_count;
|
||||
|
||||
video_layout_bounds_t *screens;
|
||||
int screens_count;
|
||||
}
|
||||
view_t;
|
||||
|
||||
typedef struct view_array
|
||||
{
|
||||
view_t *views;
|
||||
int views_count;
|
||||
}
|
||||
view_array_t;
|
||||
|
||||
void layer_init (layer_t *layer, const char *name);
|
||||
void layer_deinit (layer_t *layer);
|
||||
element_t *layer_add_element (layer_t *layer);
|
||||
|
||||
void view_init (view_t *view, const char *name);
|
||||
void view_deinit (view_t *view);
|
||||
layer_t *view_find_layer (view_t *view, const char *name);
|
||||
layer_t *view_emplace_layer (view_t *view, const char *name);
|
||||
void view_sort_layers (view_t *view);
|
||||
void view_normalize (view_t *view);
|
||||
void view_count_screens (view_t *view);
|
||||
|
||||
void view_array_init (view_array_t *view_array, int views_count);
|
||||
void view_array_deinit (view_array_t *view_array);
|
||||
view_t *view_array_find (view_array_t *view_array, const char *name);
|
||||
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user