Skip to content

Commit ed76493

Browse files
committed
vdisp/sdl3: allow setting custom hints
eg. `-d sdl:hint=SDL_VIDEO_WAYLAND_PREFER_LIBDECOR=0`
1 parent 5817eba commit ed76493

File tree

4 files changed

+256
-2
lines changed

4 files changed

+256
-2
lines changed

Makefile.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ COMMON_OBJS = \
146146
src/utils/audio_buffer.o \
147147
src/utils/color_out.o \
148148
src/utils/config_file.o \
149+
src/utils/dictionary.o \
149150
src/utils/fs.o \
150151
src/utils/jpeg_reader.o \
151152
src/utils/list.o \

src/utils/dictionary.c

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
/**
2+
* @file utils/dictionary.c
3+
* @author Martin Pulec <[email protected]>
4+
*
5+
* this file implementents simple key/value dictionary in string
6+
*
7+
* Currenttly simple linked listi is used with linear lookup/insertion.
8+
*/
9+
/*
10+
* Copyright (c) 2026 CESNET, zájmové sdružení právnických osob
11+
* All rights reserved.
12+
*
13+
* Redistribution and use in source and binary forms, with or without
14+
* modification, is permitted provided that the following conditions
15+
* are met:
16+
*
17+
* 1. Redistributions of source code must retain the above copyright
18+
* notice, this list of conditions and the following disclaimer.
19+
*
20+
* 2. Redistributions in binary form must reproduce the above copyright
21+
* notice, this list of conditions and the following disclaimer in the
22+
* documentation and/or other materials provided with the distribution.
23+
*
24+
* 3. Neither the name of CESNET nor the names of its contributors may be
25+
* used to endorse or promote products derived from this software without
26+
* specific prior written permission.
27+
*
28+
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
29+
* "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
30+
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
31+
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
32+
* EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33+
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34+
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36+
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
37+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
38+
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
39+
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40+
*/
41+
42+
#include "dictionary.h"
43+
44+
#include <assert.h> // for assert
45+
#include <stdlib.h> // for calloc, free
46+
#include <string.h> // for strdup, strchr
47+
#include <stdint.h> // for uint32_t
48+
49+
#include "utils//macros.h" // for to_fourcc
50+
51+
#define MAGIC to_fourcc('d', 'i', 'c', 't')
52+
53+
struct item {
54+
char *key;
55+
char *val;
56+
struct item *next;
57+
};
58+
59+
struct dictionary {
60+
uint32_t magic;
61+
struct item *items;
62+
63+
struct item *iterator;
64+
};
65+
66+
struct dictionary *
67+
dictionary_init()
68+
{
69+
struct dictionary *dictionary = calloc(1, sizeof *dictionary);
70+
dictionary->magic = MAGIC;
71+
return dictionary;
72+
}
73+
74+
/**
75+
* insert specified element
76+
*/
77+
void
78+
dictionary_insert(struct dictionary *dictionary, const char *key,
79+
const char *val)
80+
{
81+
struct item **cur_p = &dictionary->items;
82+
while (*cur_p != NULL) {
83+
struct item *cur = *cur_p;
84+
if (strcmp(cur->key, key) == 0) { // replace
85+
free(cur->val);
86+
cur->val = strdup(val);
87+
return;
88+
}
89+
if (strcmp(cur->key, key) > 0) { // prepend
90+
struct item *new = calloc(1, sizeof(struct item));
91+
new->key = strdup(key);
92+
new->val = strdup(val);
93+
new->next = cur;
94+
*cur_p = new;
95+
return;
96+
}
97+
cur_p = &(*cur_p)->next;
98+
}
99+
// append as last item
100+
struct item *new = calloc(1, sizeof(struct item));
101+
new->key = strdup(key);
102+
new->val = strdup(val);
103+
*cur_p = new;
104+
}
105+
106+
/**
107+
* insert an element in format key=vale
108+
*/
109+
void
110+
dictionary_insert2(struct dictionary *dictionary, const char *key_val)
111+
{
112+
assert(strchr(key_val, '=') != NULL);
113+
char *key = strdup(key_val);
114+
char *val = strchr(key, '=');
115+
*val = '\0';
116+
val += 1;
117+
dictionary_insert(dictionary, key, val);
118+
free(key);
119+
}
120+
121+
const char *
122+
dictionary_lookup(struct dictionary *dictionary, const char *key)
123+
{
124+
struct item *cur = dictionary->items;
125+
while (cur != NULL) {
126+
if (strcmp(cur->key, key) == 0) {
127+
return cur->key;
128+
}
129+
if (strcmp(cur->key, key) > 0) {
130+
return NULL;
131+
}
132+
};
133+
return NULL;
134+
}
135+
136+
/**
137+
* create an iterator and return key of first element
138+
*
139+
* @note
140+
* As the iterator state is stored internally, creating the
141+
* iterator (calling this fn) invalidates any previous.
142+
*/
143+
const char *
144+
dictionary_first(struct dictionary *dictionary, const char **key)
145+
{
146+
if (dictionary->items == NULL) {
147+
return NULL;
148+
}
149+
dictionary->iterator = dictionary->items;
150+
*key = dictionary->iterator->key;
151+
return dictionary->iterator->val;
152+
}
153+
154+
/**
155+
* get next element
156+
*
157+
* this must be called after dictionary_first()
158+
*
159+
* When dictionary_next() returns nullptr, no more elements are
160+
* available and the internal iterator is invalidated.
161+
*/
162+
const char *
163+
dictionary_next(struct dictionary *dictionary, const char **key)
164+
{
165+
if (dictionary->iterator->next == NULL) {
166+
return NULL;
167+
}
168+
dictionary->iterator = dictionary->iterator->next;
169+
*key = dictionary->iterator->key;
170+
return dictionary->iterator->val;
171+
}
172+
173+
void
174+
dictionary_destroy(struct dictionary *dictionary)
175+
{
176+
if (dictionary == NULL) {
177+
return;
178+
}
179+
assert(dictionary->magic == MAGIC);
180+
struct item *cur = dictionary->items;
181+
while (cur != NULL) {
182+
struct item *next = cur->next;
183+
free(cur->key);
184+
free(cur->val);
185+
free(cur);
186+
cur = next;
187+
};
188+
free(dictionary);
189+
}

src/utils/dictionary.h

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/**
2+
* @file utils/hash_table.h
3+
* @author Martin Pulec <[email protected]>
4+
*/
5+
/*
6+
* Copyright (c) 2026 CESNET, zájmové sdružení právnických osob
7+
* All rights reserved.
8+
*
9+
* Redistribution and use in source and binary forms, with or without
10+
* modification, is permitted provided that the following conditions
11+
* are met:
12+
*
13+
* 1. Redistributions of source code must retain the above copyright
14+
* notice, this list of conditions and the following disclaimer.
15+
*
16+
* 2. Redistributions in binary form must reproduce the above copyright
17+
* notice, this list of conditions and the following disclaimer in the
18+
* documentation and/or other materials provided with the distribution.
19+
*
20+
* 3. Neither the name of CESNET nor the names of its contributors may be
21+
* used to endorse or promote products derived from this software without
22+
* specific prior written permission.
23+
*
24+
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
25+
* "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
26+
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
27+
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
28+
* EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
29+
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30+
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32+
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
34+
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
35+
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36+
*/
37+
38+
#ifndef DICTIONARY_H_A228804B_D47D_4AE3_85A4_D479101FDAA8
39+
#define DICTIONARY_H_A228804B_D47D_4AE3_85A4_D479101FDAA8
40+
41+
struct dictionary *dictionary_init();
42+
void dictionary_insert(struct dictionary *dictionary, const char *key,
43+
const char *val);
44+
void dictionary_insert2(struct dictionary *dictionary, const char *key_val);
45+
const char *dictionary_lookup(struct dictionary *dictionary, const char *key);
46+
const char *dictionary_first(struct dictionary *dictionary, const char **key);
47+
const char *dictionary_next(struct dictionary *dictionary, const char **key);
48+
void dictionary_destroy(struct dictionary *dictionary);
49+
50+
#endif // defined DICTIONARY_H_A228804B_D47D_4AE3_85A4_D479101FDAA8

src/video_display/sdl3.c

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* @author Martin Pulec <[email protected]>
66
*/
77
/*
8-
* Copyright (c) 2018-2025 CESNET
8+
* Copyright (c) 2018-2026 CESNET, zájmové sdružení právnických osob
99
* All rights reserved.
1010
*
1111
* Redistribution and use in source and binary forms, with or without
@@ -75,6 +75,7 @@
7575
#include "tv.h" // for ts_add_nsec
7676
#include "types.h" // for video_desc, tile, video_frame, device...
7777
#include "utils/color_out.h" // for color_printf, TBOLD, TRED
78+
#include "utils/dictionary.h" // for dictionary
7879
#include "utils/list.h" // for simple_linked_list_append, simple_lin...
7980
#include "utils/macros.h" // for STR_LEN
8081
#include "video.h" // for get_video_desc_from_string
@@ -144,6 +145,7 @@ static const struct fmt_data pf_mapping_template[] = {
144145
struct state_sdl3 {
145146
uint32_t magic;
146147
struct module mod;
148+
struct dictionary *hints;
147149
struct fmt_data supp_fmts[(sizeof pf_mapping_template /
148150
sizeof pf_mapping_template[0]) +
149151
1]; // nul terminated
@@ -540,6 +542,10 @@ show_help(const char *driver, bool full)
540542
if (full) {
541543
color_printf(TBOLD(" blend[=<val>]") " - set alpha blending "
542544
"(default is opaque)\n");
545+
color_printf(TBOLD(
546+
" hint=<key>=<val>") " - set SDL hint (eg. "
547+
"SDL_VIDEO_WAYLAND_PREFER_LIBDECOR="
548+
"0): hint can be used repeatedly\n");
543549
}
544550
if (driver == NULL) {
545551
color_printf(
@@ -994,6 +1000,7 @@ display_sdl3_init(struct module *parent, const char *fmt, unsigned int flags)
9941000
s->display_idx = -1;
9951001
s->x = s->y = SDL_WINDOWPOS_UNDEFINED;
9961002
s->vsync = true;
1003+
s->hints = dictionary_init();
9971004

9981005
if (fmt == NULL) {
9991006
fmt = "";
@@ -1010,7 +1017,6 @@ display_sdl3_init(struct module *parent, const char *fmt, unsigned int flags)
10101017
s->display_idx = atoi(strchr(tok, '=') + 1);
10111018
} else if (IS_KEY_PREFIX(tok, "driver")) {
10121019
driver = strchr(tok, '=') + 1;
1013-
;
10141020
} else if (IS_PREFIX(tok, "fs")) {
10151021
s->fs = true;
10161022
} else if (IS_PREFIX(tok, "help") || strcmp(tok, "fullhelp") == 0) {
@@ -1063,6 +1069,8 @@ display_sdl3_init(struct module *parent, const char *fmt, unsigned int flags)
10631069
s->req_blend_mode = atoi(strchr(tok, '=') + 1);
10641070
} else if (strcmp(tok, "blend") == 0) {
10651071
s->req_blend_mode = SDL_BLENDMODE_BLEND;
1072+
} else if (IS_KEY_PREFIX(tok, "hint") && strchr(tok, '=') != strrchr(tok, '=')) {
1073+
dictionary_insert2(s->hints, strchr(tok, '=') + 1);
10661074
} else {
10671075
MSG(ERROR, "Wrong option: %s\n", tok);
10681076
free(s);
@@ -1083,6 +1091,11 @@ display_sdl3_init(struct module *parent, const char *fmt, unsigned int flags)
10831091
if (driver != NULL) {
10841092
SDL_CHECK(SDL_SetHint(SDL_HINT_VIDEO_DRIVER, driver));
10851093
}
1094+
for (const char *key, *val = dictionary_first(s->hints, &key);
1095+
val != NULL; val = dictionary_next(s->hints, &key)) {
1096+
SDL_CHECK(SDL_SetHint(key, val));
1097+
MSG(INFO, "Setting %s to %s\n", key, val);
1098+
}
10861099
if (!SDL_InitSubSystem(SDL_INIT_VIDEO)) {
10871100
MSG(ERROR, "Unable to initialize SDL3 video: %s\n",
10881101
SDL_GetError());
@@ -1170,6 +1183,7 @@ display_sdl3_done(void *state)
11701183

11711184
module_done(&s->mod);
11721185

1186+
dictionary_destroy(s->hints);
11731187
free(s);
11741188
}
11751189

0 commit comments

Comments
 (0)