diff options
Diffstat (limited to 'navit/graphics/opengl/graphics_opengl.c')
-rw-r--r-- | navit/graphics/opengl/graphics_opengl.c | 1489 |
1 files changed, 1489 insertions, 0 deletions
diff --git a/navit/graphics/opengl/graphics_opengl.c b/navit/graphics/opengl/graphics_opengl.c new file mode 100644 index 00000000..c380c7d4 --- /dev/null +++ b/navit/graphics/opengl/graphics_opengl.c @@ -0,0 +1,1489 @@ +/** + * Navit, a modular navigation system. + * Copyright (C) 2005-2010 Navit Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <glib.h> +#include <unistd.h> +#include <math.h> +#include <stdio.h> +#include <FreeImage.h> +#include <time.h> + +#include "item.h" +#include "attr.h" +#include "config.h" +#include "point.h" +#include "graphics.h" +#include "color.h" +#include "plugin.h" +#include "event.h" +#include "debug.h" +#include "callback.h" +#include "keys.h" +#include "window.h" +#include "navit/font/freetype/font_freetype.h" +#include <GL/glc.h> + +#if defined(WINDOWS) || defined(WIN32) +#include <windows.h> +# define sleep(i) Sleep(i * 1000) +#endif + +#ifdef __APPLE__ +#include <GLUT/glut.h> +#else +#include <GL/glut.h> /* glut.h includes gl.h and glu.h */ +#endif + +#define SCREEN_WIDTH 700 +#define SCREEN_HEIGHT 700 + +//#define MIRRORED_VIEW 1 + +struct graphics_gc_priv +{ + struct graphics_priv *gr; + float fr, fg, fb, fa; + float br, bg, bb, ba; + int linewidth; + unsigned char *dash_list; + int dash_count; + int dash_mask; +} graphics_gc_priv; + +struct graphics_priv +{ + int button_timeout; + struct point p; + int width; + int height; + int library_init; + int visible; + int overlay_enabled; + int overlay_autodisabled; + int wraparound; + struct graphics_priv *parent; + struct graphics_priv *overlays; + struct graphics_priv *next; + struct graphics_gc_priv *background_gc; + enum draw_mode_num mode; + void (*resize_callback) (void *data, int w, int h); + void *resize_callback_data; + void (*motion_callback) (void *data, struct point * p); + void *motion_callback_data; + void (*button_callback) (void *data, int press, int button, + struct point * p); + void *button_callback_data; + GLuint DLid; + struct callback_list *cbl; + struct font_freetype_methods freetype_methods; + struct navit *nav; + int timeout; + int delay; + struct window window; + int dirty; //display needs to be redrawn (draw on root graphics or overlay is done) + int force_redraw; //display needs to be redrawn (draw on root graphics or overlay is done) + time_t last_refresh_time; //last display refresh time +}; + +static struct graphics_priv *graphics_priv_root; +struct graphics_image_priv +{ + int w; + int h; + int hot_x; + int hot_y; + unsigned char *data; + char *path; +} graphics_image_priv; + +struct mouse_event_queue_element { + int button; + int state; + int x; + int y; +}; + +static const int mouse_event_queue_size = 100; +static int mouse_event_queue_begin_idx = 0; +static int mouse_event_queue_end_idx = 0; +static struct mouse_event_queue_element mouse_queue[100]; + +//hastable for uncompressed image data +static GHashTable *hImageData; + +/* prototypes */ +void CALLBACK tessBeginCB (GLenum which); +void CALLBACK tessEndCB (); +void CALLBACK tessErrorCB (GLenum errorCode); +void CALLBACK tessVertexCB (const GLvoid * data); +void CALLBACK tessVertexCB2 (const GLvoid * data); +void CALLBACK tessCombineCB (const GLdouble newVertex[3], + const GLdouble * neighborVertex[4], + const GLfloat neighborWeight[4], + GLdouble ** outData); + +static struct graphics_priv *graphics_opengl_new_helper (struct + graphics_methods + *meth); +static void display (void); +static void resize_callback (int w, int h); +static void glut_close (); +const char *getPrimitiveType (GLenum type); + +static void +graphics_destroy (struct graphics_priv *gr) +{ + /*FIXME graphics_destroy is never called*/ + /*TODO add destroy code for image cache(delete entries in hImageData)*/ + g_free (gr); + gr = NULL; +} + +static void +gc_destroy (struct graphics_gc_priv *gc) +{ + g_free (gc); + gc = NULL; +} + +static void +gc_set_linewidth (struct graphics_gc_priv *gc, int w) +{ + gc->linewidth = w; +} + +static void +gc_set_dashes (struct graphics_gc_priv *gc, int width, int offset, + unsigned char *dash_list, int n) +{ + int i; + printf("\n"); + const int cOpenglMaskBits = 16; + gc->dash_count = n; + if (1 == n) + { + gc->dash_mask = 0; + for (i = 0; i < cOpenglMaskBits; ++i) + { + gc->dash_mask <<= 1; + gc->dash_mask |= (i / n) % 2; + } + } + else if (1 < n) + { + unsigned char *curr = dash_list; + int cnt = 0; //dot counter + int dcnt = 0; //dash element counter + int sum_dash = 0; + gc->dash_mask = 0; + + for (i = 0; i < n; ++i) + { + sum_dash += dash_list[i]; + } + + //scale dashlist elements to max size + if (sum_dash > cOpenglMaskBits) + { + int num_error[2] = { 0, 0 }; //count elements rounded to 0 for odd(drawn) and even(masked) for compensation + double factor = (1.0 * cOpenglMaskBits) / sum_dash; + for (i = 0; i < n; ++i) + { //calculate dashlist max and largest common denomiator for scaling + dash_list[i] *= factor; + if (dash_list[i] == 0) + { + ++dash_list[i]; + ++num_error[i % 2]; + } + else if (0 < num_error[i % 2] && 2 < dash_list[i]) + { + ++dash_list[i]; + --num_error[i % 2]; + } + } + } + + //calculate mask + for (i = 0; i < cOpenglMaskBits; ++i) + { + gc->dash_mask <<= 1; + gc->dash_mask |= 1 - dcnt % 2; + ++cnt; + if (cnt == *curr) + { + cnt = 0; + ++curr; + ++dcnt; + if (dcnt == n) + { + curr = dash_list; + } + } + } + } +} + + +static void +gc_set_foreground (struct graphics_gc_priv *gc, struct color *c) +{ + gc->fr = c->r / 65535.0; + gc->fg = c->g / 65535.0; + gc->fb = c->b / 65535.0; + gc->fa = c->a / 65535.0; +} + +static void +gc_set_background (struct graphics_gc_priv *gc, struct color *c) +{ + gc->br = c->r / 65535.0; + gc->bg = c->g / 65535.0; + gc->bb = c->b / 65535.0; + gc->ba = c->a / 65535.0; +} + +static struct graphics_gc_methods gc_methods = { + gc_destroy, + gc_set_linewidth, + gc_set_dashes, + gc_set_foreground, + gc_set_background +}; + +static struct graphics_gc_priv * +gc_new (struct graphics_priv *gr, struct graphics_gc_methods *meth) +{ + struct graphics_gc_priv *gc = g_new0(struct graphics_gc_priv, 1); + + *meth = gc_methods; + gc->gr = gr; + gc->linewidth = 1; + return gc; +} + +static struct graphics_image_priv image_error; + +static struct graphics_image_priv * +image_new (struct graphics_priv *gr, struct graphics_image_methods *meth, + char *path, int *w, int *h, struct point *hot, int rotation) +{ + FIBITMAP *image; + RGBQUAD aPixel; + unsigned char *data; + int width, height, i, j; + struct graphics_image_priv *gi; + + //check if image already exists in hashmap + struct graphics_image_priv*curr_elem = g_hash_table_lookup(hImageData,path); + if(curr_elem == &image_error) { + //found but couldn't be loaded + return NULL; + } + else if(curr_elem) { + //found and OK -> use hastable entry + *w = curr_elem->w; + *h = curr_elem->h; + hot->x = curr_elem->w / 2 - 1; + hot->y = curr_elem->h / 2 - 1; + return curr_elem; + } + else { + if (strlen (path) < 4) + { + g_hash_table_insert(hImageData,g_strdup(path),&image_error); + return NULL; + } + char *ext_str = path + strlen (path) - 3; + if (strstr (ext_str, "png") || strstr (path, "PNG")) + { + if ((image = FreeImage_Load (FIF_PNG, path, 0)) == NULL) + { + g_hash_table_insert(hImageData,g_strdup(path),&image_error); + return NULL; + } + } + else if (strstr (ext_str, "xpm") || strstr (path, "XPM")) + { + if ((image = FreeImage_Load (FIF_XPM, path, 0)) == NULL) + { + g_hash_table_insert(hImageData,g_strdup(path),&image_error); + return NULL; + } + } + else if (strstr (ext_str, "svg") || strstr (path, "SVG")) + { + char path_new[256]; + snprintf (path_new, strlen (path) - 3, "%s", path); + strcat (path_new, "_48_48.png"); + + if ((image = FreeImage_Load (FIF_PNG, path_new, 0)) == NULL) + { + g_hash_table_insert(hImageData,g_strdup(path),&image_error); + return NULL; + } + } + else + { + g_hash_table_insert(hImageData,g_strdup(path),&image_error); + return NULL; + } + gi = g_new0 (struct graphics_image_priv, 1); + + width = FreeImage_GetWidth (image); + height = FreeImage_GetHeight (image); + + data = (unsigned char *) malloc (width * height * 4); + + RGBQUAD *palette = NULL; + if (FreeImage_GetBPP (image) == 8) + { + palette = FreeImage_GetPalette (image); + } + + for (i = 0; i < height; i++) + { + for (j = 0; j < width; j++) + { + unsigned char idx; + if (FreeImage_GetBPP (image) == 8) + { + FreeImage_GetPixelIndex (image, j, height - i - 1, &idx); + data[4 * width * i + 4 * j + 0] = palette[idx].rgbRed; + data[4 * width * i + 4 * j + 1] = palette[idx].rgbGreen; + data[4 * width * i + 4 * j + 2] = palette[idx].rgbBlue; + data[4 * width * i + 4 * j + 3] = 255; + } + else if (FreeImage_GetBPP (image) == 16 + || FreeImage_GetBPP (image) == 24 + || FreeImage_GetBPP (image) == 32) + { + FreeImage_GetPixelColor (image, j, height - i - 1, &aPixel); + int transparent = (aPixel.rgbRed == 0 && aPixel.rgbBlue == 0 + && aPixel.rgbGreen == 0); + data[4 * width * i + 4 * j + 0] = + transparent ? 0 : (aPixel.rgbRed); + data[4 * width * i + 4 * j + 1] = (aPixel.rgbGreen); + data[4 * width * i + 4 * j + 2] = + transparent ? 0 : (aPixel.rgbBlue); + data[4 * width * i + 4 * j + 3] = transparent ? 0 : 255; + + } + else if (FreeImage_GetBPP (image) == 64) + { + //FreeImage_GetPixelColor does not handle 64bits/pixel images correctly + g_hash_table_insert(hImageData,g_strdup(path),&image_error); + return NULL; + } + } + } + + FreeImage_Unload (image); + + *w = width; + *h = height; + gi->w = width; + gi->h = height; + gi->hot_x = width / 2 - 1; + gi->hot_y = height / 2 - 1; + hot->x = width / 2 - 1; + hot->y = height / 2 - 1; + gi->data = data; + gi->path = path; + //add to hashtable + g_hash_table_insert(hImageData,g_strdup(path),gi); + return gi; + } +} + +const char * +getPrimitiveType (GLenum type) +{ + char *ret = ""; + + switch (type) + { + case 0x0000: + ret = "GL_POINTS"; + break; + case 0x0001: + ret = "GL_LINES"; + break; + case 0x0002: + ret = "GL_LINE_LOOP"; + break; + case 0x0003: + ret = "GL_LINE_STRIP"; + break; + case 0x0004: + ret = "GL_TRIANGLES"; + break; + case 0x0005: + ret = "GL_TRIANGLE_STRIP"; + break; + case 0x0006: + ret = "GL_TRIANGLE_FAN"; + break; + case 0x0007: + ret = "GL_QUADS"; + break; + case 0x0008: + ret = "GL_QUAD_STRIP"; + break; + case 0x0009: + ret = "GL_POLYGON"; + break; + } + return ret; +} + +void CALLBACK +tessBeginCB (GLenum which) +{ + glBegin (which); + + dbg (1, "glBegin( %s );\n", getPrimitiveType (which)); +} + + + +void CALLBACK +tessEndCB () +{ + glEnd (); + + dbg (1, "glEnd();\n"); +} + + + +void CALLBACK +tessVertexCB (const GLvoid * data) +{ + // cast back to double type + const GLdouble *ptr = (const GLdouble *) data; + + glVertex3dv (ptr); + + dbg (1, " glVertex3d();\n"); +} + +static void +get_overlay_pos (struct graphics_priv *gr, struct point *point_out) +{ + if (gr->parent == NULL) + { + point_out->x = 0; + point_out->y = 0; + return; + } + point_out->x = gr->p.x; + if (point_out->x < 0) + { + point_out->x += gr->parent->width; + } + + point_out->y = gr->p.y; + if (point_out->y < 0) + { + point_out->y += gr->parent->height; + } +} + +static void +draw_lines (struct graphics_priv *gr, struct graphics_gc_priv *gc, + struct point *p, int count) +{ + if (gr->parent && !gr->parent->overlay_enabled) + { + return; + } + + graphics_priv_root->dirty = 1; + + glColor4f (gc->fr, gc->fg, gc->fb, gc->fa); + glLineWidth (gc->linewidth); + if (!gr->parent && 0 < gc->dash_count) + { + glLineStipple (1, gc->dash_mask); + glEnable (GL_LINE_STIPPLE); + } + glBegin (GL_LINE_STRIP); + int i; + for (i = 0; i < count; i++) + { + struct point p_eff; + p_eff.x = p[i].x; + p_eff.y = p[i].y; + glVertex2f (p_eff.x, p_eff.y); + } + glEnd (); + if (!gr->parent && 0 < gc->dash_count) + { + glDisable (GL_LINE_STIPPLE); + } +} + + +static void +draw_polygon (struct graphics_priv *gr, struct graphics_gc_priv *gc, + struct point *p, int count) +{ + if (gr->parent && !gr->parent->overlay_enabled) + { + return; + } + + graphics_priv_root->dirty = 1; + + int i; + GLUtesselator *tess = gluNewTess (); // create a tessellator + if (!tess) + return; // failed to create tessellation object, return 0 + + GLdouble quad1[count][3]; + for (i = 0; i < count; i++) + { + quad1[i][0] = (GLdouble) (p[i].x); + quad1[i][1] = (GLdouble) (p[i].y); + quad1[i][2] = 0; + } + + + // register callback functions + gluTessCallback (tess, GLU_TESS_BEGIN, (void (*)(void)) tessBeginCB); + gluTessCallback (tess, GLU_TESS_END, (void (*)(void)) tessEndCB); + // gluTessCallback(tess, GLU_TESS_ERROR, (void (*)(void))tessErrorCB); + gluTessCallback (tess, GLU_TESS_VERTEX, (void (*)(void)) tessVertexCB); + + // tessellate and compile a concave quad into display list + // gluTessVertex() takes 3 params: tess object, pointer to vertex coords, + // and pointer to vertex data to be passed to vertex callback. + // The second param is used only to perform tessellation, and the third + // param is the actual vertex data to draw. It is usually same as the second + // param, but It can be more than vertex coord, for example, color, normal + // and UV coords which are needed for actual drawing. + // Here, we are looking at only vertex coods, so the 2nd and 3rd params are + // pointing same address. + glColor4f (gc->fr, gc->fg, gc->fb, gc->fa); + gluTessBeginPolygon (tess, 0); // with NULL data + gluTessBeginContour (tess); + for (i = 0; i < count; i++) + { + gluTessVertex (tess, quad1[i], quad1[i]); + } + gluTessEndContour (tess); + gluTessEndPolygon (tess); + + gluDeleteTess (tess); // delete after tessellation +} + +static void +draw_rectangle (struct graphics_priv *gr, struct graphics_gc_priv *gc, + struct point *p, int w, int h) +{ + if (gr->parent && !gr->parent->overlay_enabled) + { + return; + } + + graphics_priv_root->dirty = 1; + + struct point p_eff; + p_eff.x = p->x; + p_eff.y = p->y; + + glColor4f (gc->fr, gc->fg, gc->fb, gc->fa); + glBegin (GL_POLYGON); + glVertex2f (p_eff.x, p_eff.y); + glVertex2f (p_eff.x + w, p_eff.y); + glVertex2f (p_eff.x + w, p_eff.y + h); + glVertex2f (p_eff.x, p_eff.y + h); + glEnd (); +} + +static void +draw_circle (struct graphics_priv *gr, struct graphics_gc_priv *gc, + struct point *p, int r) +{ + + if (gr->parent && !gr->parent->overlay_enabled) + { + return; + } + + graphics_priv_root->dirty = 1; + + /* FIXME: does not quite match gtk */ + /* hack for osd compass.. why is this needed!? */ + if (gr->parent) + { + r = r / 2; + } + + struct point p_eff; + p_eff.x = p->x; + p_eff.y = p->y; + + GLUquadricObj *quadratic; + quadratic=gluNewQuadric(); + glPushMatrix (); + glTranslatef (p_eff.x, p_eff.y, 0); + glColor4f (gc->fr, gc->fg, gc->fb, gc->fa); + gluDisk(quadratic, r-(gc->linewidth/2)-2,r+(gc->linewidth/2), 10+r/5,10+r/5); + glPopMatrix (); + gluDeleteQuadric(quadratic); + + return; +} + +static void +display_text_draw (struct font_freetype_text *text, struct graphics_priv *gr, + struct graphics_gc_priv *fg, struct graphics_gc_priv *bg, + int color, struct point *p) +{ + int i, x, y, stride; + struct font_freetype_glyph *g, **gp; + unsigned char *shadow, *glyph; + struct color transparent = { 0x0000, 0x0000, 0x0000, 0x0000 }; + struct color black = + { fg->fr * 65535, fg->fg * 65535, fg->fb * 65535, fg->fa * 65535 }; + struct color white = { 0xffff, 0xffff, 0xffff, 0xffff }; + + if (bg) + { + if(COLOR_IS_WHITE(black) && COLOR_IS_BLACK(white)) { + black.r = 65535; + black.g = 65535; + black.b = 65535; + black.a = 65535; + + white.r = 0; + white.g = 0; + white.b = 0; + white.a = 65535; + } + else if(COLOR_IS_BLACK(black) && COLOR_IS_WHITE(white)) { + white.r = 65535; + white.g = 65535; + white.b = 65535; + white.a = 65535; + + black.r = 0; + black.g = 0; + black.b = 0; + black.a = 65535; + } + else { + white.r = bg->fr; + white.g = bg->fg; + white.b = bg->fb; + white.a = bg->fa; + } + } + else { + white.r = 0; + white.g = 0; + white.b = 0; + white.a = 0; + } + + gp = text->glyph; + i = text->glyph_count; + x = p->x << 6; + y = p->y << 6; + while (i-- > 0) + { + g = *gp++; + if (g->w && g->h && bg) + { + stride = (g->w + 2) * 4; + if (color) + { + shadow = g_malloc (stride * (g->h + 2)); + gr->freetype_methods.get_shadow (g, shadow, 32, stride, &white, + &transparent); +#ifdef MIRRORED_VIEW + glPixelZoom (-1.0, -1.0); //mirrored mode +#else + glPixelZoom (1.0, -1.0); +#endif + glRasterPos2d ((x + g->x) >> 6, (y + g->y) >> 6); + glDrawPixels (g->w + 2, g->h + 2, GL_BGRA, GL_UNSIGNED_BYTE, + shadow); + g_free (shadow); + } + } + x += g->dx; + y += g->dy; + } + + x = p->x << 6; + y = p->y << 6; + gp = text->glyph; + i = text->glyph_count; + while (i-- > 0) + { + g = *gp++; + if (g->w && g->h) + { + if (color) + { + stride = g->w; + if (bg) + { + glyph = g_malloc (stride * g->h * 4); + gr->freetype_methods.get_glyph (g, glyph, 32, stride * 4, + &black, &white, &transparent); +#ifdef MIRRORED_VIEW + glPixelZoom (-1.0, -1.0); //mirrored mode +#else + glPixelZoom (1.0, -1.0); +#endif + glRasterPos2d ((x + g->x) >> 6, (y + g->y) >> 6); + glDrawPixels (g->w, g->h, GL_BGRA, GL_UNSIGNED_BYTE, glyph); + + g_free (glyph); + } + stride *= 4; + glyph = g_malloc (stride * g->h); + gr->freetype_methods.get_glyph (g, glyph, 32, stride, &black, + &white, &transparent); + +#ifdef MIRRORED_VIEW + glPixelZoom (-1.0, -1.0); //mirrored mode +#else + glPixelZoom (1.0, -1.0); +#endif + glRasterPos2d ((x + g->x) >> 6, (y + g->y) >> 6); + glDrawPixels (g->w, g->h, GL_BGRA, GL_UNSIGNED_BYTE, glyph); + g_free (glyph); + } + } + x += g->dx; + y += g->dy; + } +} + +static void +draw_text (struct graphics_priv *gr, struct graphics_gc_priv *fg, + struct graphics_gc_priv *bg, struct graphics_font_priv *font, + char *text, struct point *p, int dx, int dy) +{ + if (gr->parent && !gr->parent->overlay_enabled) + { + return; + } + + struct font_freetype_text *t; + int color = 1; + + if (!font) + { + dbg (0, "no font, returning\n"); + return; + } + + graphics_priv_root->dirty = 1; + + t = + gr->freetype_methods.text_new (text, (struct font_freetype_font *) font, + dx, dy); + + struct point p_eff; + p_eff.x = p->x; + p_eff.y = p->y; + + display_text_draw (t, gr, fg, bg, color, &p_eff); + gr->freetype_methods.text_destroy (t); +} + + +static void +draw_image (struct graphics_priv *gr, struct graphics_gc_priv *fg, + struct point *p, struct graphics_image_priv *img) +{ + if (gr->parent && !gr->parent->overlay_enabled) + { + return; + } + + if (!img || !img->data) + { + return; + } + + graphics_priv_root->dirty = 1; + + struct point p_eff; + p_eff.x = p->x + img->hot_x; + p_eff.y = p->y + img->hot_y; + + glEnable (GL_BLEND); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glRasterPos2d (p_eff.x - img->hot_x, p_eff.y - img->hot_y); + glDrawPixels (img->w, img->h, GL_RGBA, GL_UNSIGNED_BYTE, img->data); + +} + +static void +draw_image_warp (struct graphics_priv *gr, struct graphics_gc_priv *fg, + struct point *p, int count, char *data) +{ +} + +static void +draw_restore (struct graphics_priv *gr, struct point *p, int w, int h) +{ +} + +static void +draw_drag (struct graphics_priv *gr, struct point *p) +{ + + if (p) + { + gr->p.x = p->x; + gr->p.y = p->y; + } +} + +static void +background_gc (struct graphics_priv *gr, struct graphics_gc_priv *gc) +{ + gr->background_gc = gc; +} + +static void handle_mouse_queue() +{ + static locked = 0; + if(!locked) { + locked = 1; + } + else { + return; + } + + if(mouse_event_queue_begin_idx < mouse_event_queue_end_idx) { + if (mouse_queue[mouse_event_queue_begin_idx].button == GLUT_LEFT_BUTTON && mouse_queue[mouse_event_queue_begin_idx].state == GLUT_UP) + { + struct point p; + p.x = mouse_queue[mouse_event_queue_begin_idx%mouse_event_queue_size].x; + p.y = mouse_queue[mouse_event_queue_begin_idx%mouse_event_queue_size].y; + graphics_priv_root->force_redraw = 1; + callback_list_call_attr_3 (graphics_priv_root->cbl, attr_button, + (void *) 0, 1, (void *) &p); + } + else if (mouse_queue[mouse_event_queue_begin_idx].button == GLUT_LEFT_BUTTON && mouse_queue[mouse_event_queue_begin_idx].state == GLUT_DOWN) + { + struct point p; + p.x = mouse_queue[mouse_event_queue_begin_idx%mouse_event_queue_size].x; + p.y = mouse_queue[mouse_event_queue_begin_idx%mouse_event_queue_size].y; + graphics_priv_root->force_redraw = 1; + callback_list_call_attr_3 (graphics_priv_root->cbl, attr_button, + (void *) 1, 1, (void *) &p); + } + ++mouse_event_queue_begin_idx; + } + locked = 0; +} + + +/*draws root graphics and its overlays*/ +static int +redraw_screen (struct graphics_priv *gr) +{ + time_t curr_time = time(0); + graphics_priv_root->dirty = 0; + + glCallList (gr->DLid); + //display overlays display list + struct graphics_priv *overlay; + overlay = gr->overlays; + while (gr->overlay_enabled && overlay) + { + glPushMatrix (); + struct point p_eff; + get_overlay_pos (overlay, &p_eff); + glTranslatef (p_eff.x, p_eff.y, 1); + glCallList (overlay->DLid); + glPopMatrix (); + overlay = overlay->next; + } + glutSwapBuffers (); + + return TRUE; +} + + +/*filters call to redraw in overlay enabled(map) mode*/ +static void +redraw_filter (struct graphics_priv *gr) +{ + if(gr->overlay_enabled && gr->dirty) { + redraw_screen(gr); + } +} + + + +static void +draw_mode (struct graphics_priv *gr, enum draw_mode_num mode) +{ + if (gr->parent) + { //overlay + if (mode == draw_mode_begin) + { + glNewList (gr->DLid, GL_COMPILE); + } + + if (mode == draw_mode_end || mode == draw_mode_end_lazy) + { + glEndList (); + } + } + else + { //root graphics + if (mode == draw_mode_begin) + { + glNewList (gr->DLid, GL_COMPILE); + } + + if (mode == draw_mode_end) + { + glEndList (); + gr->force_redraw = 1; + if(!gr->overlay_enabled || gr->force_redraw ) { + redraw_screen (gr); + } + } + } + gr->mode = mode; +} + +static struct graphics_priv *overlay_new (struct graphics_priv *gr, + struct graphics_methods *meth, + struct point *p, int w, int h, + int alpha, int wraparound); + +static int +graphics_opengl_fullscreen (struct window *w, int on) +{ + return 1; +} + +static void +graphics_opengl_disable_suspend (struct window *w) +{ +} + + +static void * +get_data (struct graphics_priv *this, char *type) +{ + /*TODO initialize gtkglext context when type=="gtk_widget" */ + if (!strcmp(type,"gtk_widget")) { + fprintf(stderr, "Currently GTK gui is not yet supported with opengl graphics driver\n"); + exit(-1); + } + if (strcmp (type, "window") == 0) + { + struct window *win; + win = g_new0 (struct window, 1); + win->priv = this; + win->fullscreen = graphics_opengl_fullscreen; + win->disable_suspend = graphics_opengl_disable_suspend; + return win; + } + else + { + return &this->DLid; + } + + +} + +static void +image_free (struct graphics_priv *gr, struct graphics_image_priv *priv) +{ +//TODO free image data in hashtable when graphics is destroyed +//currently graphics destroy is not called !!! +/* + g_free(priv->data); + priv->data = NULL; + g_free(priv); + priv = NULL; +*/ +} + +static void +overlay_disable (struct graphics_priv *gr, int disable) +{ + gr->overlay_enabled = !disable; +} + +static void +overlay_resize (struct graphics_priv *gr, struct point *p, int w, int h, + int alpha, int wraparound) +{ + int changed = 0; + int w2, h2; + + if (w == 0) + { + w2 = 1; + } + else + { + w2 = w; + } + + if (h == 0) + { + h2 = 1; + } + else + { + h2 = h; + } + + gr->p = *p; + if (gr->width != w2) + { + gr->width = w2; + changed = 1; + } + + if (gr->height != h2) + { + gr->height = h2; + changed = 1; + } + + gr->wraparound = wraparound; + + if (changed) + { + if ((w == 0) || (h == 0)) + { + gr->overlay_autodisabled = 1; + } + else + { + gr->overlay_autodisabled = 0; + } + + callback_list_call_attr_2 (gr->cbl, attr_resize, + GINT_TO_POINTER (gr->width), + GINT_TO_POINTER (gr->height)); + } +} + +static struct graphics_methods graphics_methods = { + graphics_destroy, + draw_mode, + draw_lines, + draw_polygon, + draw_rectangle, + draw_circle, + draw_text, + draw_image, + draw_image_warp, + draw_restore, + draw_drag, + NULL, + gc_new, + background_gc, + overlay_new, + image_new, + get_data, + image_free, + NULL, + overlay_disable, + overlay_resize, +}; + +static struct graphics_priv * +graphics_opengl_new_helper (struct graphics_methods *meth) +{ + struct font_priv *(*font_freetype_new) (void *meth); + font_freetype_new = plugin_get_font_type ("freetype"); + + if (!font_freetype_new) + { + return NULL; + } + + struct graphics_priv *this = g_new0 (struct graphics_priv, 1); + + font_freetype_new (&this->freetype_methods); + *meth = graphics_methods; + + meth->font_new = + (struct graphics_font_priv * + (*)(struct graphics_priv *, struct graphics_font_methods *, char *, int, + int)) this->freetype_methods.font_new; + meth->get_text_bbox = this->freetype_methods.get_text_bbox; + + return this; +} + +static struct graphics_priv * +overlay_new (struct graphics_priv *gr, struct graphics_methods *meth, + struct point *p, int w, int h, int alpha, int wraparound) +{ + int w2, h2; + struct graphics_priv *this = graphics_opengl_new_helper (meth); + this->p = *p; + this->width = w; + this->height = h; + this->parent = gr; + + /* If either height or width is 0, we set it to 1 to avoid warnings, and + * disable the overlay. */ + if (h == 0) + { + h2 = 1; + } + else + { + h2 = h; + } + + if (w == 0) + { + w2 = 1; + } + else + { + w2 = w; + } + + if ((w == 0) || (h == 0)) + { + this->overlay_autodisabled = 1; + } + else + { + this->overlay_autodisabled = 0; + } + this->overlay_enabled = 1; + this->overlay_autodisabled = 0; + + this->next = gr->overlays; + gr->overlays = this; + this->DLid = glGenLists (1); + return this; +} + + +static void +click_notify (int button, int state, int x, int y) +{ + mouse_queue[mouse_event_queue_end_idx%mouse_event_queue_size].button = button; + mouse_queue[mouse_event_queue_end_idx%mouse_event_queue_size].state = state; +#ifdef MIRRORED_VIEW + mouse_queue[mouse_event_queue_end_idx%mouse_event_queue_size].x = graphics_priv_root->width-x; +#else + mouse_queue[mouse_event_queue_end_idx%mouse_event_queue_size].x = x; +#endif + mouse_queue[mouse_event_queue_end_idx%mouse_event_queue_size].y = y; + ++mouse_event_queue_end_idx; +} + +static void +motion_notify (int x, int y) +{ + struct point p; +#ifdef MIRRORED_VIEW + p.x = graphics_priv_root->width-x; +#else + p.x = x; +#endif + p.y = y; + callback_list_call_attr_1 (graphics_priv_root->cbl, attr_motion, + (void *) &p); + return; +} + +static gboolean +graphics_opengl_idle (void *data) +{ + static int opengl_init_ok = 0; + if (!opengl_init_ok) + { + callback_list_call_attr_2 (graphics_priv_root->cbl, attr_resize, + GINT_TO_POINTER (graphics_priv_root->width), + GINT_TO_POINTER + (graphics_priv_root->height)); + opengl_init_ok = 1; + } + else + { + glutMainLoopEvent (); + handle_mouse_queue(); + } + return TRUE; +} + +static void +ProcessNormalKeys (unsigned char key_in, int x, int y) +{ + int key = 0; + char keybuf[2]; + + switch (key_in) + { + case 13: + key = NAVIT_KEY_RETURN; + break; + default: + key = key_in; + break; + } + keybuf[0] = key; + keybuf[1] = '\0'; + graphics_priv_root->force_redraw = 1; + callback_list_call_attr_1 (graphics_priv_root->cbl, attr_keypress, + (void *) keybuf); +} + +static void +ProcessSpecialKeys (int key_in, int x, int y) +{ + int key = 0; + char keybuf[2]; + + switch (key_in) + { + case 102: + key = NAVIT_KEY_RIGHT; + break; + case 100: + key = NAVIT_KEY_LEFT; + break; + case 103: + key = NAVIT_KEY_DOWN; + break; + case 101: + key = NAVIT_KEY_UP; + break; + case 104: + key = NAVIT_KEY_ZOOM_OUT; + break; + case 105: + key = NAVIT_KEY_ZOOM_IN; + break; + default: + break; + } //switch + + graphics_priv_root->force_redraw = 1; + keybuf[0] = key; + keybuf[1] = '\0'; + callback_list_call_attr_1 (graphics_priv_root->cbl, attr_keypress, + (void *) keybuf); +} + +static void +resize_callback (int w, int h) +{ + glViewport (0, 0, w, h); + glMatrixMode (GL_PROJECTION); + glLoadIdentity (); +#ifdef MIRRORED_VIEW + gluOrtho2D ( w, 0, h, 0.0); //mirrored mode +#else + gluOrtho2D ( 0, w, h, 0.0); +#endif + + graphics_priv_root->width = w; + graphics_priv_root->height = h; + + callback_list_call_attr_2 (graphics_priv_root->cbl, attr_resize, + GINT_TO_POINTER (w), GINT_TO_POINTER (h)); +} + +static void +display (void) +{ + graphics_priv_root->force_redraw = 1; + redraw_screen(graphics_priv_root); + resize_callback (graphics_priv_root->width, graphics_priv_root->height); +} + +static void +glut_close (void) +{ + callback_list_call_attr_0(graphics_priv_root->cbl, attr_window_closed); +} + + +static struct graphics_priv * +graphics_opengl_new (struct navit *nav, struct graphics_methods *meth, + struct attr **attrs, struct callback_list *cbl) +{ + struct attr *attr; + + if (!event_request_system ("glib", "graphics_opengl_new")) + return NULL; + + struct graphics_priv *this = graphics_opengl_new_helper (meth); + graphics_priv_root = this; + + this->nav = nav; + this->parent = NULL; + this->overlay_enabled = 1; + + this->width = SCREEN_WIDTH; + if ((attr = attr_search (attrs, NULL, attr_w))) + this->width = attr->u.num; + this->height = SCREEN_HEIGHT; + if ((attr = attr_search (attrs, NULL, attr_h))) + this->height = attr->u.num; + this->timeout = 100; + if ((attr = attr_search (attrs, NULL, attr_timeout))) + this->timeout = attr->u.num; + this->delay = 0; + if ((attr = attr_search (attrs, NULL, attr_delay))) + this->delay = attr->u.num; + this->cbl = cbl; + + char *cmdline = ""; + int argc = 0; + glutInit (&argc, &cmdline); + glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGBA); + + glutInitWindowSize (this->width, this->height); + glutInitWindowPosition (0, 0); + glutCreateWindow ("Navit opengl window"); + + glutDisplayFunc (display); + glutReshapeFunc (resize_callback); + resize_callback (this->width, this->height); + + graphics_priv_root->cbl = cbl; + graphics_priv_root->width = this->width; + graphics_priv_root->height = this->height; + + glutMotionFunc (motion_notify); + glutPassiveMotionFunc (motion_notify); + glutMouseFunc (click_notify); + glutKeyboardFunc (ProcessNormalKeys); + glutSpecialFunc (ProcessSpecialKeys); + glutCloseFunc (glut_close); + + this->DLid = glGenLists (1); + + g_timeout_add (G_PRIORITY_DEFAULT + 10, graphics_opengl_idle, NULL); + + /*this will only refresh screen in map(overlay enabled) mode*/ + g_timeout_add (G_PRIORITY_DEFAULT + 1000, redraw_filter, this); + + //create hash table for uncompressed image data + hImageData = g_hash_table_new(g_str_hash, g_str_equal); + return this; +} + + +static void +event_opengl_main_loop_run (void) +{ + dbg (0, "enter\n"); +} + +static void +event_opengl_main_loop_quit (void) +{ + dbg (0, "enter\n"); +} + +static struct event_watch * +event_opengl_add_watch (void *h, enum event_watch_cond cond, + struct callback *cb) +{ + dbg (0, "enter\n"); + return NULL; +} + +static void +event_opengl_remove_watch (struct event_watch *ev) +{ + dbg (0, "enter\n"); +} + + +static struct event_timeout * +event_opengl_add_timeout (int timeout, int multi, struct callback *cb) +{ + dbg (0, "enter\n"); + return NULL; +} + +static void +event_opengl_remove_timeout (struct event_timeout *to) +{ + dbg (0, "enter\n"); +} + + +static struct event_idle * +event_opengl_add_idle (int priority, struct callback *cb) +{ + dbg (0, "enter\n"); + return NULL; +} + +static void +event_opengl_remove_idle (struct event_idle *ev) +{ + dbg (0, "enter\n"); +} + +static void +event_opengl_call_callback (struct callback_list *cb) +{ + dbg (0, "enter\n"); +} + +static struct event_methods event_opengl_methods = { + event_opengl_main_loop_run, + event_opengl_main_loop_quit, + event_opengl_add_watch, + event_opengl_remove_watch, + event_opengl_add_timeout, + event_opengl_remove_timeout, + event_opengl_add_idle, + event_opengl_remove_idle, + event_opengl_call_callback, +}; + +static struct event_priv * +event_opengl_new (struct event_methods *meth) +{ + *meth = event_opengl_methods; + return NULL; +} + +void +plugin_init (void) +{ + plugin_register_graphics_type ("opengl", graphics_opengl_new); + plugin_register_event_type ("opengl", event_opengl_new); +} |