mirror of
https://github.com/genodelabs/genode.git
synced 2025-05-02 08:42:52 +00:00
275 lines
6.0 KiB
C++
275 lines
6.0 KiB
C++
/*
|
|
* \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 <base/attached_rom_dataspace.h>
|
|
#include <base/component.h>
|
|
#include <event_session/connection.h>
|
|
#include <capture_session/connection.h>
|
|
#include <timer_session/connection.h>
|
|
|
|
/* Linux includes */
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wconversion"
|
|
#include <SDL/SDL.h>
|
|
#pragma GCC diagnostic pop
|
|
|
|
/* local includes */
|
|
#include "convert_keycode.h"
|
|
|
|
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 { };
|
|
|
|
|
|
struct Fb_sdl::Main
|
|
{
|
|
Env &_env;
|
|
|
|
Attached_rom_dataspace _config { _env, "config" };
|
|
|
|
Timer::Connection _timer { _env };
|
|
Event::Connection _event { _env };
|
|
|
|
using Area = Capture::Area;
|
|
using Point = Capture::Area;
|
|
using Pixel = Capture::Pixel;
|
|
using Affected_rects = Capture::Session::Affected_rects;
|
|
using Event_batch = Event::Session_client::Batch;
|
|
|
|
void _init_sdl()
|
|
{
|
|
/*
|
|
* 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);
|
|
}
|
|
|
|
bool const _sdl_initialized = ( _init_sdl(), true );
|
|
|
|
struct Sdl_screen
|
|
{
|
|
Area const size;
|
|
|
|
SDL_Surface &_sdl_surface = _init_sdl_surface();
|
|
|
|
SDL_Surface &_init_sdl_surface()
|
|
{
|
|
unsigned const bpp = 32;
|
|
unsigned const flags = SDL_SWSURFACE | SDL_RESIZABLE;
|
|
|
|
SDL_Surface *surface_ptr = nullptr;
|
|
|
|
if (SDL_VideoModeOK(size.w(), size.h(), bpp, flags))
|
|
surface_ptr = SDL_SetVideoMode(size.w(), size.h(), bpp, flags);
|
|
|
|
if (!surface_ptr) {
|
|
error("SDL_SetVideoMode failed (", Genode::Cstring(SDL_GetError()), ")");
|
|
throw Sdl_setvideomode_failed();
|
|
}
|
|
return *surface_ptr;
|
|
}
|
|
|
|
Sdl_screen(Area size) : size(size) { }
|
|
|
|
template <typename FN>
|
|
void with_surface(FN const &fn)
|
|
{
|
|
Surface<Pixel> surface { (Pixel *)_sdl_surface.pixels, size };
|
|
fn(surface);
|
|
}
|
|
|
|
void flush(Capture::Rect rect)
|
|
{
|
|
SDL_UpdateRect(&_sdl_surface, rect.x1(), rect.y1(), rect.w(), rect.h());
|
|
}
|
|
};
|
|
|
|
Constructible<Sdl_screen> _sdl_screen { };
|
|
|
|
Capture::Connection _capture { _env };
|
|
|
|
Constructible<Capture::Connection::Screen> _captured_screen { };
|
|
|
|
Signal_handler<Main> _timer_handler {
|
|
_env.ep(), *this, &Main::_handle_timer };
|
|
|
|
int _mx = 0, _my = 0;
|
|
|
|
void _handle_sdl_event(Event_batch &, SDL_Event const &);
|
|
void _handle_sdl_events();
|
|
|
|
void _update_sdl_screen_from_capture()
|
|
{
|
|
Affected_rects const affected = _capture.capture_at(Capture::Point(0, 0));
|
|
|
|
_sdl_screen->with_surface([&] (Surface<Pixel> &surface) {
|
|
|
|
_captured_screen->with_texture([&] (Texture<Pixel> const &texture) {
|
|
|
|
affected.for_each_rect([&] (Capture::Rect const rect) {
|
|
|
|
surface.clip(rect);
|
|
|
|
Blit_painter::paint(surface, texture, Capture::Point(0, 0));
|
|
});
|
|
});
|
|
});
|
|
|
|
/* flush pixels in SDL window */
|
|
affected.for_each_rect([&] (Capture::Rect const rect) {
|
|
_sdl_screen->flush(rect); });
|
|
}
|
|
|
|
void _handle_timer()
|
|
{
|
|
_handle_sdl_events();
|
|
|
|
_update_sdl_screen_from_capture();
|
|
}
|
|
|
|
void _resize(Area size)
|
|
{
|
|
|
|
_sdl_screen.construct(size);
|
|
_captured_screen.construct(_capture, _env.rm(), size);
|
|
_update_sdl_screen_from_capture();
|
|
}
|
|
|
|
Main(Env &env) : _env(env)
|
|
{
|
|
_resize(Area(_config.xml().attribute_value("width", 1024U),
|
|
_config.xml().attribute_value("height", 768U)));
|
|
|
|
_timer.sigh(_timer_handler);
|
|
_timer.trigger_periodic(100000000 / 5994); /* 59.94Hz */
|
|
}
|
|
};
|
|
|
|
|
|
void Fb_sdl::Main::_handle_sdl_event(Event_batch &batch, SDL_Event const &event)
|
|
{
|
|
using namespace Input;
|
|
|
|
if (event.type == SDL_VIDEORESIZE) {
|
|
|
|
if (event.resize.w < 0 || event.resize.h < 0) {
|
|
warning("attempt to resize to negative size");
|
|
return;
|
|
}
|
|
|
|
_resize(Area((unsigned)event.resize.w, (unsigned)event.resize.h));
|
|
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;
|
|
|
|
batch.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;
|
|
|
|
batch.submit(Release{keycode});
|
|
return;
|
|
|
|
case SDL_KEYDOWN:
|
|
case SDL_MOUSEBUTTONDOWN:
|
|
|
|
if (event.button.button == SDL_BUTTON_WHEELUP)
|
|
batch.submit(Wheel{0, 1});
|
|
else if (event.button.button == SDL_BUTTON_WHEELDOWN)
|
|
batch.submit(Wheel{0, -1});
|
|
else
|
|
batch.submit(Press{keycode});
|
|
return;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void Fb_sdl::Main::_handle_sdl_events()
|
|
{
|
|
SDL_Event event { };
|
|
|
|
_event.with_batch([&] (Event_batch &batch) {
|
|
|
|
while (SDL_PollEvent(&event))
|
|
_handle_sdl_event(batch, event);
|
|
});
|
|
}
|
|
|
|
|
|
void Component::construct(Genode::Env &env) { static Fb_sdl::Main inst(env); }
|