genode/libports/src/app/mupdf/main.cc

343 lines
7.7 KiB
C++

/*
* \brief MuPDF for Genode
* \author Norman Feske
* \date 2012-01-09
*/
/*
* Copyright (C) 2012-2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <framebuffer_session/connection.h>
#include <base/sleep.h>
#include <input_session/connection.h>
#include <input/event.h>
#include <input/keycodes.h>
#include <timer_session/connection.h>
/* MuPDF includes */
extern "C" {
#include <fitz.h>
#include <mupdf.h>
#include <muxps.h>
#include <pdfapp.h>
}
/* libc includes */
#include <unistd.h>
/***************
** Dithering **
***************/
/*
* XXX blatantly copied from 'demo/src/app/backdrop/main.cc'
*
* We should factor-out the dithering support into a separate header file.
* But where is a good place to put it?
*/
enum { DITHER_SIZE = 16, DITHER_MASK = DITHER_SIZE - 1 };
static const int dither_matrix[DITHER_SIZE][DITHER_SIZE] = {
{ 0,192, 48,240, 12,204, 60,252, 3,195, 51,243, 15,207, 63,255 },
{ 128, 64,176,112,140, 76,188,124,131, 67,179,115,143, 79,191,127 },
{ 32,224, 16,208, 44,236, 28,220, 35,227, 19,211, 47,239, 31,223 },
{ 160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95 },
{ 8,200, 56,248, 4,196, 52,244, 11,203, 59,251, 7,199, 55,247 },
{ 136, 72,184,120,132, 68,180,116,139, 75,187,123,135, 71,183,119 },
{ 40,232, 24,216, 36,228, 20,212, 43,235, 27,219, 39,231, 23,215 },
{ 168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87 },
{ 2,194, 50,242, 14,206, 62,254, 1,193, 49,241, 13,205, 61,253 },
{ 130, 66,178,114,142, 78,190,126,129, 65,177,113,141, 77,189,125 },
{ 34,226, 18,210, 46,238, 30,222, 33,225, 17,209, 45,237, 29,221 },
{ 162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93 },
{ 10,202, 58,250, 6,198, 54,246, 9,201, 57,249, 5,197, 53,245 },
{ 138, 74,186,122,134, 70,182,118,137, 73,185,121,133, 69,181,117 },
{ 42,234, 26,218, 38,230, 22,214, 41,233, 25,217, 37,229, 21,213 },
{ 170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85 }
};
static inline uint16_t rgb565(int r, int g, int b)
{
enum {
R_MASK = 0xf800, R_LSHIFT = 8,
G_MASK = 0x07e0, G_LSHIFT = 3,
B_MASK = 0x001f, B_RSHIFT = 3
};
return ((r << R_LSHIFT) & R_MASK)
| ((g << G_LSHIFT) & G_MASK)
| ((b >> B_RSHIFT) & B_MASK);
}
static void convert_line_rgba_to_rgb565(const unsigned char *rgba_src,
uint16_t *dst, int num_pixels, int line)
{
using namespace Genode;
enum { CHANNEL_MAX = 255 };
int const *dm = dither_matrix[line & DITHER_MASK];
for (int i = 0; i < num_pixels; i++) {
int v = dm[i & DITHER_MASK] >> 5;
*dst++ = rgb565(min(v + (int)rgba_src[0], (int)CHANNEL_MAX),
min(v + (int)rgba_src[1], (int)CHANNEL_MAX),
min(v + (int)rgba_src[2], (int)CHANNEL_MAX));
/* we ignore the alpha channel */
rgba_src += 4; /* next pixel */
}
}
/**************
** PDF view **
**************/
class Pdf_view
{
public:
/**
* Exception types
*/
class Non_supported_framebuffer_mode { };
class Invalid_input_file_name { };
class Unexpected_document_color_depth { };
private:
struct _Framebuffer : Framebuffer::Connection
{
typedef uint16_t pixel_t;
Framebuffer::Mode mode;
pixel_t *base;
_Framebuffer()
:
mode(Framebuffer::Connection::mode()),
base(Genode::env()->rm_session()->attach(dataspace()))
{
PDBG("Framebuffer is %dx%d\n", mode.width(), mode.height());
if (mode.format() != Framebuffer::Mode::RGB565) {
PERR("Color modes other than RGB565 are not supported. Exiting.");
throw Non_supported_framebuffer_mode();
}
}
} _framebuffer;
pdfapp_t _pdfapp;
public:
/**
* Constructor
*
* \throw Non_supported_framebuffer_mode
* \throw Invalid_input_file_name
* \throw Unexpected_document_color_depth
*/
Pdf_view(char const *file_name)
{
pdfapp_init(&_pdfapp);
_pdfapp.userdata = this;
_pdfapp.scrw = _framebuffer.mode.width();
_pdfapp.scrh = _framebuffer.mode.height();
_pdfapp.resolution = 75; /* XXX read from config */
_pdfapp.pageno = 0; /* XXX read from config */
int fd = open(file_name, O_BINARY | O_RDONLY, 0666);
if (fd < 0) {
PERR("Could not open input file \"%s\", Exiting.", file_name);
throw Invalid_input_file_name();
}
pdfapp_open(&_pdfapp, (char *)file_name, fd, 0);
if (_pdfapp.image->n != 4) {
PERR("Unexpected color depth, expected 4, got %d, Exiting.",
_pdfapp.image->n);
throw Unexpected_document_color_depth();
}
}
void show();
void handle_key(int ascii)
{
pdfapp_onkey(&_pdfapp, ascii);
}
};
void Pdf_view::show()
{
int const x_max = Genode::min(_framebuffer.mode.width(), _pdfapp.image->w);
int const y_max = Genode::min(_framebuffer.mode.height(), _pdfapp.image->h);
Genode::size_t src_line_bytes = _pdfapp.image->n * _pdfapp.image->w;
unsigned char *src_line = _pdfapp.image->samples;
Genode::size_t dst_line_width = _framebuffer.mode.width(); /* in pixels */
_Framebuffer::pixel_t *dst_line = _framebuffer.base;
for (int y = 0; y < y_max; y++) {
convert_line_rgba_to_rgb565(src_line, dst_line, x_max, y);
src_line += src_line_bytes;
dst_line += dst_line_width;
}
_framebuffer.refresh(0, 0, _framebuffer.mode.width(), _framebuffer.mode.height());
}
extern "C" void _sigprocmask()
{
/* suppress debug message by default "not-implemented" implementation */
}
/**************************
** Called from pdfapp.c **
**************************/
void winrepaint(pdfapp_t *pdfapp)
{
PDBG("called");
Pdf_view *pdf_view = (Pdf_view *)pdfapp->userdata;
pdf_view->show();
}
void winrepaintsearch(pdfapp_t *)
{
PDBG("not implemented");
}
void wincursor(pdfapp_t *, int curs)
{
PDBG("curs=%d - not implemented", curs);
}
void winerror(pdfapp_t *, fz_error error)
{
PDBG("error=%d", error);
Genode::sleep_forever();
}
void winwarn(pdfapp_t *, char *msg)
{
PWRN("MuPDF: %s", msg);
}
void winhelp(pdfapp_t *)
{
PDBG("not implemented");
}
char *winpassword(pdfapp_t *, char *)
{
PDBG("not implemented");
return NULL;
}
void winclose(pdfapp_t *app)
{
PDBG("not implemented");
}
void winreloadfile(pdfapp_t *)
{
PDBG("not implemented");
}
void wintitle(pdfapp_t *app, char *s)
{
PDBG("s=\"%s\" - not implemented", s);
}
void winresize(pdfapp_t *app, int w, int h)
{
PDBG("not implemented, w=%d, h=%d", w, h);
}
/******************
** Main program **
******************/
static int keycode_to_ascii(int code)
{
switch (code) {
case Input::KEY_LEFT: return 'h';
case Input::KEY_RIGHT: return 'l';
case Input::KEY_DOWN: return 'j';
case Input::KEY_UP: return 'k';
case Input::KEY_PAGEDOWN:
case Input::KEY_ENTER: return ' ';
case Input::KEY_PAGEUP:
case Input::KEY_BACKSPACE: return 'b';
default: return 0;
}
}
int main(int, char **)
{
char const *file_name = "test.pdf"; /* XXX read from config */
static Pdf_view pdf_view(file_name);
static Input::Connection input;
static Timer::Connection timer;
Input::Event *ev_buf = Genode::env()->rm_session()->attach(input.dataspace());
int key_cnt = 0;
/*
* Input event loop
*/
for (;;) {
while (!input.is_pending()) timer.msleep(20);
for (int i = 0, num_ev = input.flush(); i < num_ev; i++) {
Input::Event const &ev = ev_buf[i];
if (ev.type() == Input::Event::PRESS) key_cnt++;
if (ev.type() == Input::Event::RELEASE) key_cnt--;
if (ev.type() == Input::Event::PRESS && key_cnt == 1) {
PDBG("key %d pressed", ev.code());
int const ascii = keycode_to_ascii(ev.code());
if (ascii)
pdf_view.handle_key(ascii);
}
}
}
Genode::sleep_forever();
return 0;
}