From 2565928495c44bdd7b1fdf1e910b0e317e99c736 Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Mon, 24 Dec 2018 15:13:08 +0100 Subject: [PATCH] fb_sdl: support the resizing of the SDL window Fixes #3095 --- repos/os/recipes/src/fb_sdl/used_apis | 1 + .../spec/sdl/{input.cc => convert_keycode.h} | 122 +------ .../drivers/framebuffer/spec/sdl/fb_sdl.cc | 213 ----------- .../src/drivers/framebuffer/spec/sdl/input.h | 31 -- .../src/drivers/framebuffer/spec/sdl/main.cc | 331 ++++++++++++++++++ .../drivers/framebuffer/spec/sdl/target.mk | 4 +- 6 files changed, 341 insertions(+), 361 deletions(-) rename repos/os/src/drivers/framebuffer/spec/sdl/{input.cc => convert_keycode.h} (67%) delete mode 100644 repos/os/src/drivers/framebuffer/spec/sdl/fb_sdl.cc delete mode 100644 repos/os/src/drivers/framebuffer/spec/sdl/input.h create mode 100644 repos/os/src/drivers/framebuffer/spec/sdl/main.cc diff --git a/repos/os/recipes/src/fb_sdl/used_apis b/repos/os/recipes/src/fb_sdl/used_apis index 33d9c72d21..4fa949cf7a 100644 --- a/repos/os/recipes/src/fb_sdl/used_apis +++ b/repos/os/recipes/src/fb_sdl/used_apis @@ -4,3 +4,4 @@ os input_session framebuffer_session timer_session +blit diff --git a/repos/os/src/drivers/framebuffer/spec/sdl/input.cc b/repos/os/src/drivers/framebuffer/spec/sdl/convert_keycode.h similarity index 67% rename from repos/os/src/drivers/framebuffer/spec/sdl/input.cc rename to repos/os/src/drivers/framebuffer/spec/sdl/convert_keycode.h index c3f897dad2..599a260c19 100644 --- a/repos/os/src/drivers/framebuffer/spec/sdl/input.cc +++ b/repos/os/src/drivers/framebuffer/spec/sdl/convert_keycode.h @@ -6,27 +6,22 @@ */ /* - * Copyright (C) 2006-2017 Genode Labs GmbH + * Copyright (C) 2006-2018 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. */ -/* Linux includes */ -#include - -/* Genode includes */ -#include -#include - -/* local includes */ -#include "input.h" +#ifndef _DRIVERS__FRAMEBUFFER__SPEC__SDL__CONVERT_KEYCODE_H_ +#define _DRIVERS__FRAMEBUFFER__SPEC__SDL__CONVERT_KEYCODE_H_ +/* Genode include */ +#include /** * Convert SDL keycode to Genode keycode */ -static Input::Keycode convert_keycode(int sdl_keycode) +static inline Input::Keycode convert_keycode(int sdl_keycode) { using namespace Input; @@ -139,107 +134,4 @@ static Input::Keycode convert_keycode(int sdl_keycode) } }; - -static Input::Event wait_for_sdl_event() -{ - using namespace Input; - - SDL_Event event; - static int mx, my; - - SDL_WaitEvent(&event); - - /* query new mouse position */ - if (event.type == SDL_MOUSEMOTION) { - int ox = mx, oy = my; - SDL_GetMouseState(&mx, &my); - - /* drop superficial events */ - if (ox == mx && oy == my) - return Event(); - - return Absolute_motion{mx, my}; - } - - /* determine key code */ - Keycode keycode = KEY_UNKNOWN; - switch (event.type) { - case SDL_KEYUP: - case SDL_KEYDOWN: - - keycode = convert_keycode(event.key.keysym.sym); - break; - - case SDL_MOUSEBUTTONDOWN: - case SDL_MOUSEBUTTONUP: - - switch (event.button.button) { - case SDL_BUTTON_LEFT: keycode = BTN_LEFT; break; - case SDL_BUTTON_MIDDLE: keycode = BTN_MIDDLE; break; - case SDL_BUTTON_RIGHT: keycode = BTN_RIGHT; break; - default: break; - } - } - - /* determine event type */ - switch (event.type) { - - case SDL_KEYUP: - case SDL_MOUSEBUTTONUP: - if (event.button.button == SDL_BUTTON_WHEELUP - || event.button.button == SDL_BUTTON_WHEELDOWN) - /* ignore */ - return Event(); - - return Release{keycode}; - - case SDL_KEYDOWN: - case SDL_MOUSEBUTTONDOWN: - - if (event.button.button == SDL_BUTTON_WHEELUP) - return Wheel{0, 1}; - - else if (event.button.button == SDL_BUTTON_WHEELDOWN) - return Wheel{0, -1}; - - return Press{keycode}; - - default: - break; - } - return Event(); -} - - -namespace Input { struct Backend; } - -struct Input::Backend : Genode::Thread -{ - Handler &handler; - - Backend(Genode::Env &env, Input::Handler &handler) - : - Genode::Thread(env, "input_backend", 4 * 1024 * sizeof(long)), - handler(handler) - { - start(); - } - - void entry() - { - while (true) { - Input::Event e; - - /* prevent flooding of client with invalid events */ - do { e = wait_for_sdl_event(); } while (!e.valid()); - - handler.event(e); - } - } -}; - - -void init_input_backend(Genode::Env &env, Input::Handler &h) -{ - static Input::Backend inst(env, h); -} +#endif /* _DRIVERS__FRAMEBUFFER__SPEC__SDL__CONVERT_KEYCODE_H_ */ diff --git a/repos/os/src/drivers/framebuffer/spec/sdl/fb_sdl.cc b/repos/os/src/drivers/framebuffer/spec/sdl/fb_sdl.cc deleted file mode 100644 index 9a843799e6..0000000000 --- a/repos/os/src/drivers/framebuffer/spec/sdl/fb_sdl.cc +++ /dev/null @@ -1,213 +0,0 @@ -/* - * \brief SDL-based implementation of the Genode framebuffer - * \author Norman Feske - * \author Christian Helmuth - * \date 2006-07-10 - */ - -/* - * Copyright (C) 2006-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 -#include -#include -#include - -/* Linux includes */ -#include - -/* local includes */ -#include "input.h" - -namespace Framebuffer { - class Session_component; - using namespace Genode; -} - - -namespace Fb_sdl { - class Main; - using namespace Genode; -} - - -class Framebuffer::Session_component : public Rpc_object -{ - private: - - /* - * Noncopyable - */ - Session_component(Session_component const &); - Session_component &operator = (Session_component const &); - - SDL_Surface *_screen { nullptr }; - - Mode _mode; - Dataspace_capability _fb_ds_cap; - void *_fb_ds_addr; - - Timer::Connection _timer; - - public: - - /** - * Constructor - */ - Session_component(Env &env, Framebuffer::Mode mode, - Dataspace_capability fb_ds_cap, void *fb_ds_addr) - : - _mode(mode), _fb_ds_cap(fb_ds_cap), _fb_ds_addr(fb_ds_addr), _timer(env) - { } - - void screen(SDL_Surface *screen) { _screen = screen; } - - Dataspace_capability dataspace() override { return _fb_ds_cap; } - - Mode mode() const override { return _mode; } - - void mode_sigh(Signal_context_capability) override { } - - void sync_sigh(Signal_context_capability sigh) override - { - _timer.sigh(sigh); - if (sigh.valid()) - _timer.trigger_periodic(100000000 / 5994); /* 59.94Hz */ - } - - void refresh(int x, int y, int w, int h) override - { - /* clip refresh area to screen boundaries */ - int x1 = max(x, 0); - int y1 = max(y, 0); - int x2 = min(x + w - 1, _mode.width() - 1); - int y2 = min(y + h - 1, _mode.height() - 1); - - if (x1 <= x2 && y1 <= y2) { - - /* copy pixels from shared dataspace to sdl surface */ - const int start_offset = _mode.bytes_per_pixel()*(y1*_mode.width() + x1); - const int line_len = _mode.bytes_per_pixel()*(x2 - x1 + 1); - const int pitch = _mode.bytes_per_pixel()*_mode.width(); - - char *src = (char *)_fb_ds_addr + start_offset; - char *dst = (char *)_screen->pixels + start_offset; - - for (int i = y1; i <= y2; i++, src += pitch, dst += pitch) - Genode::memcpy(dst, src, line_len); - - /* flush pixels in sdl window */ - SDL_UpdateRect(_screen, x1, y1, x2 - x1 + 1, y2 - y1 + 1); - } - } -}; - - -namespace Input { - - struct Handler_rpc : Handler - { - GENODE_RPC(Rpc_event, void, event, Input::Event); - GENODE_RPC_INTERFACE(Rpc_event); - }; - - struct Handler_client : Handler - { - Genode::Capability cap; - - Handler_client(Genode::Capability cap) : cap(cap) { } - - void event(Input::Event ev) override - { - cap.call(ev); - } - }; - - struct Handler_component : Genode::Rpc_object - { - Session_component &session; - - Handler_component(Session_component &session) : session(session) { } - - void event(Input::Event e) override { session.submit(e); } - }; -} - - -struct Fb_sdl::Main -{ - /* fatal exceptions */ - struct Sdl_init_failed : Exception { }; - struct Sdl_videodriver_not_supported : Exception { }; - struct Sdl_setvideomode_failed : Exception { }; - - Env &_env; - - Attached_rom_dataspace _config { _env, "config" }; - - int const _fb_width = _config.xml().attribute_value("width", 1024UL); - int const _fb_height = _config.xml().attribute_value("height", 768UL); - - Framebuffer::Mode _fb_mode { _fb_width, _fb_height, Framebuffer::Mode::RGB565 }; - - Attached_ram_dataspace _fb_ds { _env.ram(), _env.rm(), - _fb_mode.width()*_fb_mode.height()*_fb_mode.bytes_per_pixel() }; - - Framebuffer::Session_component _fb_session { _env, _fb_mode, _fb_ds.cap(), _fb_ds.local_addr() }; - - Static_root _fb_root { _env.ep().manage(_fb_session) }; - - Input::Session_component _input_session { _env, _env.ram() }; - Input::Root_component _input_root { _env.ep().rpc_ep(), _input_session }; - - Input::Handler_component _input_handler_component { _input_session }; - Input::Handler_client _input_handler_client { _env.ep().manage(_input_handler_component) }; - - Main(Env &env) : _env(env) - { - /* - * Initialize libSDL window - */ - if (SDL_Init(SDL_INIT_VIDEO) < 0) { - error("SDL_Init failed (", Genode::Cstring(SDL_GetError()), ")"); - throw Sdl_init_failed(); - } - - /* - * We're testing only X11. - */ - char driver[16] = { 0 }; - SDL_VideoDriverName(driver, sizeof(driver)); - if (::strcmp(driver, "x11") != 0) { - error("fb_sdl works on X11 only. " - "Your SDL backend is ", Genode::Cstring(driver), "."); - throw Sdl_videodriver_not_supported(); - } - - SDL_Surface *screen = SDL_SetVideoMode(_fb_mode.width(), _fb_mode.height(), - _fb_mode.bytes_per_pixel()*8, SDL_SWSURFACE); - if (!screen) { - error("SDL_SetVideoMode failed (", Genode::Cstring(SDL_GetError()), ")"); - throw Sdl_setvideomode_failed(); - } - _fb_session.screen(screen); - - SDL_ShowCursor(0); - - log("creating virtual framebuffer for mode ", _fb_mode); - - _env.parent().announce(env.ep().manage(_fb_root)); - _env.parent().announce(env.ep().manage(_input_root)); - - init_input_backend(_env, _input_handler_client); - } -}; - - -void Component::construct(Genode::Env &env) { static Fb_sdl::Main inst(env); } diff --git a/repos/os/src/drivers/framebuffer/spec/sdl/input.h b/repos/os/src/drivers/framebuffer/spec/sdl/input.h deleted file mode 100644 index 1030c68fcb..0000000000 --- a/repos/os/src/drivers/framebuffer/spec/sdl/input.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * \brief SDL input support - * \author Norman Feske - * \author Christian Helmuth - * \date 2006-08-16 - */ - -/* - * Copyright (C) 2006-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 _DRIVERS__FRAMEBUFFER__SPEC__SDL__INPUT_H_ -#define _DRIVERS__FRAMEBUFFER__SPEC__SDL__INPUT_H_ - -/* Genode include */ -#include -#include - -namespace Input { struct Handler; } - -struct Input::Handler : Genode::Interface -{ - virtual void event(Input::Event) = 0; -}; - -void init_input_backend(Genode::Env &, Input::Handler &); - -#endif /* _DRIVERS__FRAMEBUFFER__SPEC__SDL__INPUT_H_ */ diff --git a/repos/os/src/drivers/framebuffer/spec/sdl/main.cc b/repos/os/src/drivers/framebuffer/spec/sdl/main.cc new file mode 100644 index 0000000000..a830c0fcbb --- /dev/null +++ b/repos/os/src/drivers/framebuffer/spec/sdl/main.cc @@ -0,0 +1,331 @@ +/* + * \brief SDL-based implementation of the Genode framebuffer + * \author Norman Feske + * \author Christian Helmuth + * \date 2006-07-10 + */ + +/* + * Copyright (C) 2006-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 +#include +#include +#include +#include + +/* Linux includes */ +#include + +/* local includes */ +#include "convert_keycode.h" + +namespace Framebuffer { + class Session_component; + using namespace Genode; +} + + +namespace Fb_sdl { + class Main; + using namespace Genode; +} + + +/* fatal exceptions */ +struct Sdl_init_failed : Genode::Exception { }; +struct Sdl_videodriver_not_supported : Genode::Exception { }; +struct Sdl_setvideomode_failed : Genode::Exception { }; + + +class Framebuffer::Session_component : public Rpc_object +{ + private: + + /* + * Noncopyable + */ + Session_component(Session_component const &); + Session_component &operator = (Session_component const &); + + Env &_env; + + Mode _next_mode; + Mode mutable _requested_mode = _next_mode; + Mode _mode = _next_mode; + + SDL_Surface *_screen { nullptr }; + + Constructible _fb_ds { }; + + Signal_context_capability _mode_sigh { }; + Signal_context_capability _sync_sigh { }; + + public: + + void submit_sync() + { + if (_sync_sigh.valid()) + Signal_transmitter(_sync_sigh).submit(); + } + + void submit_mode_change(Mode next_mode) + { + _next_mode = next_mode; + + if (_mode_sigh.valid()) + Signal_transmitter(_mode_sigh).submit(); + } + + /** + * Constructor + */ + Session_component(Env &env, Framebuffer::Mode next_mode) + : + _env(env), _next_mode(next_mode) + { } + + Dataspace_capability dataspace() override + { + unsigned const bpp = _requested_mode.bytes_per_pixel(); + unsigned const flags = SDL_SWSURFACE | SDL_RESIZABLE; + unsigned const w = _requested_mode.width(); + unsigned const h = _requested_mode.height(); + + if (SDL_VideoModeOK(w, h, bpp*8, flags)) + _screen = SDL_SetVideoMode(w, h, bpp*8, flags); + + if (!_screen) { + error("SDL_SetVideoMode failed (", Genode::Cstring(SDL_GetError()), ")"); + throw Sdl_setvideomode_failed(); + } + + /* + * Preserve content of old dataspace in new SDL screen to reduce + * flickering during resize. + * + * Note that flickering cannot fully be avoided because the host + * window is immediately cleared by 'SDL_SetVideoMode'. + */ + refresh(0, 0, w, h); + + _mode = _requested_mode; + + /* + * Allocate new dataspace, and initialize with the original pixels. + */ + _fb_ds.construct(_env.ram(), _env.rm(), w*h*bpp); + + blit(_screen->pixels, _screen->pitch, _fb_ds->local_addr(), bpp*w, + min(w, (unsigned)_screen->w)*bpp, + min(h, (unsigned)_screen->h)); + + return _fb_ds->cap(); + } + + Mode mode() const override + { + _requested_mode = _next_mode; + return _requested_mode; + } + + void mode_sigh(Signal_context_capability sigh) override + { + _mode_sigh = sigh; + } + + void sync_sigh(Signal_context_capability sigh) override + { + _sync_sigh = sigh; + } + + void refresh(int x, int y, int w, int h) override + { + if (!_fb_ds.constructed()) + return; + + /* clip refresh area to screen boundaries */ + int const x1 = max(x, 0); + int const y1 = max(y, 0); + int const x2 = min(x + w - 1, min(_mode.width(), _screen->w) - 1); + int const y2 = min(y + h - 1, min(_mode.height(), _screen->h) - 1); + + if (x1 > x2 || y1 > y2) + return; + + /* blit pixels from framebuffer dataspace to SDL screen */ + unsigned const bpp = _mode.bytes_per_pixel(); + + char const * const src_base = _fb_ds->local_addr(); + unsigned const src_pitch = bpp*_mode.width(); + char const * const src = src_base + y1*src_pitch + bpp*x1; + + unsigned const dst_pitch = _screen->pitch; + char * const dst_base = (char *)_screen->pixels; + char * const dst = dst_base + y1*dst_pitch + bpp*x1; + + blit(src, src_pitch, dst, dst_pitch, bpp*(x2 - x1 + 1), y2 - y1 + 1); + + /* flush pixels in sdl window */ + SDL_UpdateRect(_screen, x1, y1, x2 - x1 + 1, y2 - y1 + 1); + } +}; + + +struct Fb_sdl::Main +{ + Env &_env; + + Attached_rom_dataspace _config { _env, "config" }; + + Timer::Connection _timer { _env }; + + int _fb_width = _config.xml().attribute_value("width", 1024UL); + int _fb_height = _config.xml().attribute_value("height", 768UL); + + Framebuffer::Mode _fb_mode { _fb_width, _fb_height, Framebuffer::Mode::RGB565 }; + + Framebuffer::Session_component _fb_session { _env, _fb_mode }; + + Static_root _fb_root { _env.ep().manage(_fb_session) }; + + Input::Session_component _input_session { _env, _env.ram() }; + Input::Root_component _input_root { _env.ep().rpc_ep(), _input_session }; + + Signal_handler
_timer_handler { + _env.ep(), *this, &Main::_handle_timer }; + + int _mx = 0, _my = 0; + + void _handle_sdl_event(SDL_Event const &event); + void _handle_sdl_events(); + + void _handle_timer() + { + _handle_sdl_events(); + _fb_session.submit_sync(); + } + + Main(Env &env) : _env(env) + { + /* + * Initialize libSDL window + */ + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + error("SDL_Init failed (", Genode::Cstring(SDL_GetError()), ")"); + throw Sdl_init_failed(); + } + + /* + * We're testing only X11. + */ + char driver[16] = { 0 }; + SDL_VideoDriverName(driver, sizeof(driver)); + if (::strcmp(driver, "x11") != 0) { + error("fb_sdl works on X11 only. " + "Your SDL backend is ", Genode::Cstring(driver), "."); + throw Sdl_videodriver_not_supported(); + } + + SDL_ShowCursor(0); + + _env.parent().announce(env.ep().manage(_fb_root)); + _env.parent().announce(env.ep().manage(_input_root)); + + _timer.sigh(_timer_handler); + _timer.trigger_periodic(100000000 / 5994); /* 59.94Hz */ + } +}; + + +void Fb_sdl::Main::_handle_sdl_event(SDL_Event const &event) +{ + using namespace Input; + + if (event.type == SDL_VIDEORESIZE) { + + Framebuffer::Mode const mode(event.resize.w, event.resize.h, + Framebuffer::Mode::RGB565); + + _fb_session.submit_mode_change(mode); + return; + } + + /* query new mouse position */ + if (event.type == SDL_MOUSEMOTION) { + int ox = _mx, oy = _my; + SDL_GetMouseState(&_mx, &_my); + + /* drop superficial events */ + if (ox == _mx && oy == _my) + return; + + _input_session.submit(Absolute_motion{_mx, _my}); + return; + } + + /* determine key code */ + Keycode keycode = KEY_UNKNOWN; + switch (event.type) { + case SDL_KEYUP: + case SDL_KEYDOWN: + + keycode = convert_keycode(event.key.keysym.sym); + break; + + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + + switch (event.button.button) { + case SDL_BUTTON_LEFT: keycode = BTN_LEFT; break; + case SDL_BUTTON_MIDDLE: keycode = BTN_MIDDLE; break; + case SDL_BUTTON_RIGHT: keycode = BTN_RIGHT; break; + default: break; + } + } + + /* determine event type */ + switch (event.type) { + + case SDL_KEYUP: + case SDL_MOUSEBUTTONUP: + if (event.button.button == SDL_BUTTON_WHEELUP + || event.button.button == SDL_BUTTON_WHEELDOWN) + /* ignore */ + return; + + _input_session.submit(Release{keycode}); + return; + + case SDL_KEYDOWN: + case SDL_MOUSEBUTTONDOWN: + + if (event.button.button == SDL_BUTTON_WHEELUP) + _input_session.submit(Wheel{0, 1}); + else if (event.button.button == SDL_BUTTON_WHEELDOWN) + _input_session.submit(Wheel{0, -1}); + else + _input_session.submit(Press{keycode}); + return; + + default: + break; + } +} + + +void Fb_sdl::Main::_handle_sdl_events() +{ + SDL_Event event { }; + while (SDL_PollEvent(&event)) + _handle_sdl_event(event); +} + + +void Component::construct(Genode::Env &env) { static Fb_sdl::Main inst(env); } diff --git a/repos/os/src/drivers/framebuffer/spec/sdl/target.mk b/repos/os/src/drivers/framebuffer/spec/sdl/target.mk index 33f0461701..93199244dc 100644 --- a/repos/os/src/drivers/framebuffer/spec/sdl/target.mk +++ b/repos/os/src/drivers/framebuffer/spec/sdl/target.mk @@ -1,6 +1,6 @@ TARGET = fb_sdl -LIBS = lx_hybrid +LIBS = lx_hybrid blit REQUIRES = linux sdl -SRC_CC = fb_sdl.cc input.cc +SRC_CC = main.cc LX_LIBS = sdl INC_DIR += $(PRG_DIR)