From 6ca8f4c17434e2d18d9a75628c563013bd856f86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josef=20S=C3=B6ntgen?= Date: Fri, 17 Nov 2017 13:03:22 +0100 Subject: [PATCH] sdl: remove deprecated API usage In addition framebuffer resizing is now also supported. Fixes #2583. --- repos/libports/lib/mk/sdlmain.mk | 5 + repos/libports/run/avplay.run | 2 +- repos/libports/run/sdl.run | 2 +- repos/libports/src/app/avplay/target.mk | 2 +- .../src/lib/sdl/SDL_genode_internal.h | 24 +++ .../src/lib/sdl/audio/SDL_genodeaudio.cc | 95 +++++++----- repos/libports/src/lib/sdl/sdl_main.cc | 132 +++++++++++++++++ .../src/lib/sdl/video/SDL_genode_fb_events.cc | 51 ++++++- .../src/lib/sdl/video/SDL_genode_fb_video.cc | 140 +++++++++++++----- repos/libports/src/test/sdl/target.mk | 6 +- repos/ports/run/dosbox.run | 4 +- repos/ports/src/app/dosbox/target.mk | 2 +- 12 files changed, 372 insertions(+), 93 deletions(-) create mode 100644 repos/libports/lib/mk/sdlmain.mk create mode 100644 repos/libports/src/lib/sdl/SDL_genode_internal.h create mode 100644 repos/libports/src/lib/sdl/sdl_main.cc diff --git a/repos/libports/lib/mk/sdlmain.mk b/repos/libports/lib/mk/sdlmain.mk new file mode 100644 index 000000000..bf344e42b --- /dev/null +++ b/repos/libports/lib/mk/sdlmain.mk @@ -0,0 +1,5 @@ +SRC_CC = sdl_main.cc + +LIBS += libc + +vpath sdl_main.cc $(REP_DIR)/src/lib/sdl diff --git a/repos/libports/run/avplay.run b/repos/libports/run/avplay.run index e96fbbe69..e7d59b2c3 100644 --- a/repos/libports/run/avplay.run +++ b/repos/libports/run/avplay.run @@ -115,7 +115,7 @@ append boot_modules { core init timer } [audio_drv_binary] { avplay ld.lib.so libc.lib.so libm.lib.so pthread.lib.so zlib.lib.so sdl.lib.so avfilter.lib.so avutil.lib.so avcodec.lib.so avformat.lib.so swscale.lib.so - avresample.lib.so posix.lib.so + avresample.lib.so mediafile } diff --git a/repos/libports/run/sdl.run b/repos/libports/run/sdl.run index 7597f64f8..874e23932 100644 --- a/repos/libports/run/sdl.run +++ b/repos/libports/run/sdl.run @@ -85,7 +85,7 @@ set boot_modules { core init timer test-sdl - ld.lib.so libc.lib.so libm.lib.so sdl.lib.so pthread.lib.so posix.lib.so + ld.lib.so libc.lib.so libm.lib.so sdl.lib.so pthread.lib.so } # platform-specific modules diff --git a/repos/libports/src/app/avplay/target.mk b/repos/libports/src/app/avplay/target.mk index 62cf1f687..8dcca5363 100644 --- a/repos/libports/src/app/avplay/target.mk +++ b/repos/libports/src/app/avplay/target.mk @@ -3,7 +3,7 @@ include $(REP_DIR)/lib/import/import-av.inc TARGET = avplay SRC_C = avplay.c cmdutils.c libc_dummies.c LIBS += avfilter avformat avcodec avutil avresample swscale -LIBS += sdl posix +LIBS += sdl sdlmain libc libm CC_WARN += -Wno-parentheses -Wno-switch -Wno-uninitialized \ -Wno-format-zero-length -Wno-pointer-sign diff --git a/repos/libports/src/lib/sdl/SDL_genode_internal.h b/repos/libports/src/lib/sdl/SDL_genode_internal.h new file mode 100644 index 000000000..bec66bb2d --- /dev/null +++ b/repos/libports/src/lib/sdl/SDL_genode_internal.h @@ -0,0 +1,24 @@ +/* + * \brief Genode-specific data structures + * \author Josef Soentgen + * \date 2017-11-21 + */ + +/* + * Copyright (C) 2017 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _SDL_GENODE_INTERNAL_H_ +#define _SDL_GENODE_INTERNAL_H_ + +struct Video +{ + bool resize_pending; + int width; + int height; +}; + +#endif /* _SDL_GENODE_INTERNAL_H_ */ diff --git a/repos/libports/src/lib/sdl/audio/SDL_genodeaudio.cc b/repos/libports/src/lib/sdl/audio/SDL_genodeaudio.cc index 6e86e774c..699668c1d 100644 --- a/repos/libports/src/lib/sdl/audio/SDL_genodeaudio.cc +++ b/repos/libports/src/lib/sdl/audio/SDL_genodeaudio.cc @@ -14,6 +14,7 @@ * under the terms of the GNU Affero General Public License version 3. */ +/* Genode includes */ #include #include #include @@ -21,6 +22,13 @@ #include #include +/* local includes */ +#include + + +extern Genode::Env *global_env(); +extern Genode::Lock event_lock; + enum { AUDIO_CHANNELS = 2, @@ -34,7 +42,6 @@ using Genode::Hex; using Genode::Constructible; static const char *channel_names[] = { "front left", "front right" }; -static float volume = 1.0; static Signal_context config_signal_context; extern "C" { @@ -54,48 +61,55 @@ extern "C" { /* The tag name used by Genode audio */ #define GENODEAUD_DRIVER_NAME "genode" +struct Volume_config +{ + Genode::Env &_env; + + Genode::Attached_rom_dataspace _config_rom { _env, "config" }; + + float volume { 1.0f }; + + void _handle_config_update() + { + _config_rom.update(); + + if (!_config_rom.valid()) { return; } + + Genode::Lock_guard guard(event_lock); + + Genode::Xml_node config = _config_rom.xml(); + + try { + unsigned int config_volume; + config.sub_node("sdl_audio_volume").attribute("value") + .value(&config_volume); + volume = (float)config_volume / 100; + } catch (...) { } + + Genode::log("Change SDL audio volume to ", volume * 100); + } + + Genode::Signal_handler _config_handler { + _env.ep(), *this, &Volume_config::_handle_config_update }; + + Volume_config(Genode::Env &env) : _env(env) + { + _config_rom.sigh(_config_handler); + _handle_config_update(); + } +}; + + struct SDL_PrivateAudioData { Uint8 *mixbuf; Uint32 mixlen; + Constructible volume_config; Constructible audio[AUDIO_CHANNELS]; Audio_out::Packet *packet[AUDIO_CHANNELS]; }; -/* - * The first 'Signal_receiver' object in a process creates a signal receiver - * thread. Currently this must not happen before the main program has started - * or else the thread's stack area would get overmapped on Genode/Linux when - * the main program calls 'main_thread_bootstrap()' from '_main()'. - */ -static Signal_receiver *signal_receiver() -{ - static Signal_receiver _signal_receiver; - return &_signal_receiver; -} - - -static void read_config(Genode::Signal_context_capability sigh = - Genode::Signal_context_capability()) -{ - /* read volume from config file */ - try { - unsigned int config_volume; - - Genode::Attached_rom_dataspace config("config"); - if (sigh.valid()) config.sigh(sigh); - else config.update(); - config.xml().sub_node("sdl_audio_volume") - .attribute("value").value(&config_volume); - - volume = (float)config_volume / 100; - } - catch (Genode::Xml_node::Nonexistent_sub_node) { } - catch (Genode::Xml_node::Nonexistent_attribute) { } -} - - /* Audio driver functions */ static int GENODEAUD_OpenAudio(_THIS, SDL_AudioSpec *spec); static void GENODEAUD_WaitAudio(_THIS); @@ -153,7 +167,7 @@ static SDL_AudioDevice *GENODEAUD_CreateDevice(int devindex) /* connect to 'Audio_out' service */ for (int channel = 0; channel < AUDIO_CHANNELS; channel++) { try { - _this->hidden->audio[channel].construct( + _this->hidden->audio[channel].construct(*global_env(), channel_names[channel], false, channel == 0 ? true : false); _this->hidden->audio[channel]->start(); } @@ -167,7 +181,7 @@ static SDL_AudioDevice *GENODEAUD_CreateDevice(int devindex) } } - read_config(signal_receiver()->manage(&config_signal_context)); + _this->hidden->volume_config.construct(*global_env()); return _this; } @@ -200,6 +214,8 @@ static void GENODEAUD_WaitAudio(_THIS) static void GENODEAUD_PlayAudio(_THIS) { + Genode::Lock_guard guard(event_lock); + Audio_out::Connection *c[AUDIO_CHANNELS]; Audio_out::Packet *p[AUDIO_CHANNELS]; for (int channel = 0; channel < AUDIO_CHANNELS; channel++) { @@ -214,6 +230,8 @@ static void GENODEAUD_PlayAudio(_THIS) init = true; } + float const volume = _this->hidden->volume_config->volume; + /* * Get new packet for left channel and use it to synchronize * the right channel @@ -222,11 +240,6 @@ static void GENODEAUD_PlayAudio(_THIS) unsigned ppos = c[0]->stream()->packet_position(p[0]); p[1] = c[1]->stream()->get(ppos); - if (signal_receiver()->pending()) { - signal_receiver()->wait_for_signal(); - read_config(); - } - for (int sample = 0; sample < Audio_out::PERIOD; sample++) for (int channel = 0; channel < AUDIO_CHANNELS; channel++) p[channel]->content()[sample] = diff --git a/repos/libports/src/lib/sdl/sdl_main.cc b/repos/libports/src/lib/sdl/sdl_main.cc new file mode 100644 index 000000000..53a03cf11 --- /dev/null +++ b/repos/libports/src/lib/sdl/sdl_main.cc @@ -0,0 +1,132 @@ +/* + * \brief Entry point for SDL applications with a main() function + * \author Josef Soentgen + * \date 2017-11-21 + */ + +/* + * Copyright (C) 2017 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +/* Genode includes */ +#include +#include + +/* libc includes */ +#include /* 'malloc' and 'exit' */ +#include + +extern char **genode_argv; +extern int genode_argc; +extern char **genode_envp; + +/* initial environment for the FreeBSD libc implementation */ +extern char **environ; + +/* provided by the application */ +extern "C" int main(int argc, char *argv[], char *envp[]); + + +/* provided by our SDL backend */ +extern void sdl_init_genode(Genode::Env &env); + + + +static void* sdl_main(void *) +{ + exit(main(genode_argc, genode_argv, genode_envp)); + return nullptr; +} + + +void Libc::Component::construct(Libc::Env &env) +{ + using Genode::Xml_node; + using Genode::Xml_attribute; + + env.config([&] (Xml_node const &node) { + int argc = 0; + int envc = 0; + char **argv; + char **envp; + + /* count the number of arguments and environment variables */ + node.for_each_sub_node([&] (Xml_node const &node) { + /* check if the 'value' attribute exists */ + if (node.has_type("arg") && node.has_attribute("value")) + ++argc; + else + if (node.has_type("env") && node.has_attribute("key") && node.has_attribute("value")) + ++envc; + }); + + if (argc == 0 && envc == 0) + return; /* from lambda */ + + /* arguments and environment are a contiguous array (but don't count on it) */ + argv = (char**)malloc((argc + envc + 1) * sizeof(char*)); + envp = &argv[argc]; + + /* read the arguments */ + int arg_i = 0; + int env_i = 0; + node.for_each_sub_node([&] (Xml_node const &node) { + /* insert an argument */ + if (node.has_type("arg")) try { + Xml_attribute attr = node.attribute("value"); + + Genode::size_t const arg_len = attr.value_size()+1; + char *arg = argv[arg_i] = (char*)malloc(arg_len); + + attr.value(arg, arg_len); + ++arg_i; + + } catch (Xml_node::Nonexistent_sub_node) { } + + else + + /* insert an environment variable */ + if (node.has_type("env")) try { + Xml_attribute key_attr = node.attribute("key"); + Xml_attribute val_attr = node.attribute("value"); + + Genode::size_t const pair_len = + key_attr.value_size() + + val_attr.value_size() + 1; + char *env = envp[env_i] = (char*)malloc(pair_len); + + Genode::size_t off = 0; + key_attr.value(&env[off], key_attr.value_size()+1); + off = key_attr.value_size(); + env[off++] = '='; + val_attr.value(&env[off], val_attr.value_size()+1); + ++env_i; + + } catch (Xml_node::Nonexistent_sub_node) { } + }); + + envp[env_i] = NULL; + + /* register command-line arguments at Genode's startup code */ + genode_argc = argc; + genode_argv = argv; + genode_envp = environ = envp; + }); + + /* pass env to SDL backend */ + sdl_init_genode(env); + + pthread_attr_t attr; + pthread_t main_thread; + + pthread_attr_init(&attr); + pthread_attr_setstacksize(&attr, 768 * 1024); + + if (pthread_create(&main_thread, &attr, sdl_main, nullptr)) { + Genode::error("failed to create SDL main thread"); + exit(1); + } +} diff --git a/repos/libports/src/lib/sdl/video/SDL_genode_fb_events.cc b/repos/libports/src/lib/sdl/video/SDL_genode_fb_events.cc index 97bdd851a..bbe8d58a0 100644 --- a/repos/libports/src/lib/sdl/video/SDL_genode_fb_events.cc +++ b/repos/libports/src/lib/sdl/video/SDL_genode_fb_events.cc @@ -28,11 +28,41 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ + +/* Genode includes */ #include #include #include #include +/* local includes */ +#include + + +Genode::Lock event_lock; +Video video_events; + + +static Genode::Env *_global_env = nullptr; + + +Genode::Env *global_env() +{ + if (!_global_env) { + Genode::error("sdl_init_genode() not called, aborting"); + throw Genode::Exception(); + } + + return _global_env; +} + + +void sdl_init_genode(Genode::Env &env) +{ + _global_env = &env; +} + + extern "C" { #include @@ -40,7 +70,8 @@ extern "C" { #include "SDL_sysevents.h" #include "SDL_genode_fb_events.h" - static Input::Connection *input = 0; + + static Genode::Constructible input; static const int KEYNUM_MAX = 512; static SDLKey keymap[KEYNUM_MAX]; static int buttonmap[KEYNUM_MAX]; @@ -58,8 +89,17 @@ extern "C" { void Genode_Fb_PumpEvents(SDL_VideoDevice *t) { + Genode::Lock_guard guard(event_lock); + + if (video_events.resize_pending) { + video_events.resize_pending = false; + + SDL_PrivateResize(video_events.width, video_events.height); + } + if (!input->pending()) return; + input->for_each_event([&] (Input::Event const &curr) { SDL_keysym ksym; switch(curr.type()) @@ -104,14 +144,15 @@ extern "C" { void Genode_Fb_InitOSKeymap(SDL_VideoDevice *t) { - using namespace Input; - input = new(Genode::env()->heap()) Connection(); - if(!input->cap().valid()) - { + try { + input.construct(*_global_env); + } catch (...) { Genode::error("no input driver available!"); return; } + using namespace Input; + /* Prepare button mappings */ for (int i=0; i #include +/* local includes */ +#include + + +extern Genode::Env *global_env(); + +extern Genode::Lock event_lock; +extern Video video_events; + + extern "C" { #include @@ -47,10 +57,52 @@ extern "C" { #include "SDL_genode_fb_events.h" #include "SDL_genode_fb_video.h" - static Framebuffer::Connection *framebuffer = 0; + static SDL_Rect df_mode; + + struct Sdl_framebuffer + { + Genode::Env &_env; + + Framebuffer::Mode _mode; + Framebuffer::Connection _fb { _env, _mode }; + + void _handle_mode_change() + { + Genode::Lock_guard guard(event_lock); + + Framebuffer::Mode mode = _fb.mode(); + df_mode.w = mode.width(); + df_mode.h = mode.height(); + + video_events.resize_pending = true; + video_events.width = mode.width(); + video_events.height = mode.height(); + } + + Genode::Signal_handler _mode_handler { + _env.ep(), *this, &Sdl_framebuffer::_handle_mode_change }; + + Sdl_framebuffer(Genode::Env &env) : _env(env) { + _fb.mode_sigh(_mode_handler); } + + bool valid() const { return _fb.cap().valid(); } + + + /************************************ + ** Framebuffer::Session Interface ** + ************************************/ + + Genode::Dataspace_capability dataspace() { return _fb.dataspace(); } + + Framebuffer::Mode mode() const { return _fb.mode(); } + + void refresh(int x, int y, int w, int h) { + _fb.refresh(x, y, w, h); } + }; + + static Genode::Constructible framebuffer; static Framebuffer::Mode scr_mode; static SDL_Rect *modes[2]; - static SDL_Rect df_mode; #if defined(SDL_VIDEO_OPENGL) @@ -216,11 +268,11 @@ extern "C" { static int Genode_Fb_Available(void) { - if (framebuffer == nullptr) { - framebuffer = new (Genode::env()->heap()) Framebuffer::Connection(); + if (!framebuffer.constructed()) { + framebuffer.construct(*global_env()); } - if (!framebuffer->cap().valid()) { + if (!framebuffer->valid()) { Genode::error("could not obtain framebuffer session"); return 0; } @@ -231,12 +283,9 @@ extern "C" { static void Genode_Fb_DeleteDevice(SDL_VideoDevice *device) { - Genode::log("free framebuffer session object"); - if(framebuffer != nullptr) { - Genode::destroy(Genode::env()->heap(), framebuffer); + if (framebuffer.constructed()) { + framebuffer.destruct(); } - - framebuffer = nullptr; } @@ -310,9 +359,8 @@ extern "C" { */ int Genode_Fb_VideoInit(SDL_VideoDevice *t, SDL_PixelFormat *vformat) { - if (framebuffer == 0) - { - Genode::error("framebuffer isn't initialized"); + if (!framebuffer.constructed()) { + Genode::error("framebuffer not initialized"); return -1; } @@ -345,17 +393,30 @@ extern "C" { df_mode.h = scr_mode.height(); modes[1] = 0; - /* Map the buffer */ - Genode::Dataspace_capability fb_ds_cap = framebuffer->dataspace(); - if (!fb_ds_cap.valid()) { - Genode::error("could not request dataspace for frame buffer"); - return -1; - } - t->hidden->buffer = Genode::env()->rm_session()->attach(fb_ds_cap); + t->hidden->buffer = 0; return 0; } + /** + *Note: If we are terminated, this could be called in the middle of + * another SDL video routine -- notably UpdateRects. + */ + void Genode_Fb_VideoQuit(SDL_VideoDevice *t) + { + Genode::log("Quit video device ..."); + + if (t->screen->pixels) { + t->screen->pixels = nullptr; + } + + if (t->hidden->buffer) { + global_env()->rm().detach(t->hidden->buffer); + t->hidden->buffer = nullptr; + } + } + + /** * List the available video modes for the given pixel format, * sorted from largest to smallest. @@ -383,13 +444,32 @@ extern "C" { int width, int height, int bpp, Uint32 flags) { - Genode::log("Set video mode to: " - "width=", width, " " "height=", height, " " "bpp=", bpp); + /* for now we do not support this */ + if (t->hidden->buffer && flags & SDL_OPENGL) { + Genode::error("resizing a OpenGL window not possible"); + return nullptr; + } + + /* Map the buffer */ + Genode::Dataspace_capability fb_ds_cap = framebuffer->dataspace(); + if (!fb_ds_cap.valid()) { + Genode::error("could not request dataspace for frame buffer"); + return nullptr; + } + + if (t->hidden->buffer) { + global_env()->rm().detach(t->hidden->buffer); + } + + t->hidden->buffer = global_env()->rm().attach(fb_ds_cap); if (!t->hidden->buffer) { Genode::error("no buffer for requested mode"); return nullptr; } + + Genode::log("Set video mode to: ", width, "x", height, "@", bpp); + SDL_memset(t->hidden->buffer, 0, width * height * (bpp / 8)); if (!SDL_ReallocFormat(current, bpp, 0, 0, 0, 0) ) { @@ -481,21 +561,6 @@ extern "C" { } - /** - *Note: If we are terminated, this could be called in the middle of - * another SDL video routine -- notably UpdateRects. - */ - void Genode_Fb_VideoQuit(SDL_VideoDevice *t) - { - Genode::log("Quit video device ..."); - if (t->screen->pixels != 0) - { - SDL_free(t->screen->pixels); - t->screen->pixels = 0; - } - } - - int Genode_Fb_GL_MakeCurrent(SDL_VideoDevice *t) { Genode::warning(__func__, ": not yet implemented"); @@ -523,5 +588,4 @@ extern "C" { { return !__mesa ? nullptr : dlsym(__mesa, proc); } - } //extern "C" diff --git a/repos/libports/src/test/sdl/target.mk b/repos/libports/src/test/sdl/target.mk index a14e11b26..fc7f75f89 100644 --- a/repos/libports/src/test/sdl/target.mk +++ b/repos/libports/src/test/sdl/target.mk @@ -1,3 +1,3 @@ -TARGET = test-sdl -LIBS = sdl posix -SRC_CC = main.cc +TARGET := test-sdl +LIBS := libc sdl sdlmain +SRC_CC := main.cc diff --git a/repos/ports/run/dosbox.run b/repos/ports/run/dosbox.run index 6816ada6b..bfef30d36 100644 --- a/repos/ports/run/dosbox.run +++ b/repos/ports/run/dosbox.run @@ -88,7 +88,7 @@ append config { - + @@ -121,7 +121,7 @@ if {![file exists bin/dosbox.tar]} { append boot_modules { core init timer } [audio_drv_binary] { ld.lib.so - libc.lib.so posix.lib.so + libc.lib.so libm.lib.so lwip.lib.so libpng.lib.so stdcxx.lib.so sdl.lib.so sdl_net.lib.so pthread.lib.so zlib.lib.so dosbox dosbox.tar diff --git a/repos/ports/src/app/dosbox/target.mk b/repos/ports/src/app/dosbox/target.mk index 875bfd664..bcdfc2a7a 100644 --- a/repos/ports/src/app/dosbox/target.mk +++ b/repos/ports/src/app/dosbox/target.mk @@ -55,5 +55,5 @@ CC_WARN += -Wno-unused-variable -Wno-unused-function -Wno-switch -Wno-unused-val -Wno-sign-compare -Wno-narrowing -Wno-missing-braces -Wno-array-bounds \ -Wno-parentheses -LIBS += posix libpng sdl sdl_net stdcxx zlib +LIBS += libpng libc sdl sdlmain sdl_net stdcxx zlib LIBS += libc_lwip_nic_dhcp