diff options
author | Stefan Wildemann <gta04@metalstrolche.de> | 2017-02-23 19:43:25 +0100 |
---|---|---|
committer | Stefan Wildemann <gta04@metalstrolche.de> | 2017-02-23 19:43:25 +0100 |
commit | f8bfd1c1ae7f0c2d74ea021baeb8828110b5d657 (patch) | |
tree | 0bdd482b55ce4251a3ebd97fb11f46440eeea694 | |
parent | 867fe129d0dfb9fea7e840317669cc04a1e709a9 (diff) | |
parent | 518d1b9b6ea7898926d1a7bccfd27fd4eb35869a (diff) | |
download | navit-f8bfd1c1ae7f0c2d74ea021baeb8828110b5d657.tar.gz |
Merge remote-tracking branch 'upstream/trunk' into sailfish_build
-rwxr-xr-x | CMakeLists.txt | 42 | ||||
-rw-r--r-- | ci/build_linux.sh | 2 | ||||
-rw-r--r-- | ci/pointer-64.png | bin | 0 -> 1733 bytes | |||
-rw-r--r-- | ci/publish.sh | 4 | ||||
-rw-r--r-- | ci/run_linux_tests.sh | 54 | ||||
-rw-r--r-- | ci/xdotools.sh | 94 | ||||
-rw-r--r-- | navit/attr_def.h | 2 | ||||
-rw-r--r-- | navit/graphics/egl/CMakeLists.txt | 2 | ||||
-rw-r--r-- | navit/graphics/egl/graphics_egl.c | 1458 | ||||
-rw-r--r-- | navit/graphics/qt5/CMakeLists.txt | 9 | ||||
-rw-r--r-- | navit/graphics/qt5/QNavitWidget.cpp | 158 | ||||
-rw-r--r-- | navit/graphics/qt5/QNavitWidget.h | 33 | ||||
-rw-r--r-- | navit/graphics/qt5/event_qt5.cpp | 185 | ||||
-rw-r--r-- | navit/graphics/qt5/event_qt5.h | 18 | ||||
-rw-r--r-- | navit/graphics/qt5/graphics_qt5.cpp | 837 | ||||
-rw-r--r-- | navit/graphics/qt5/graphics_qt5.h | 63 | ||||
-rw-r--r-- | navit/vehicle/qt5/CMakeLists.txt | 6 | ||||
-rw-r--r-- | navit/vehicle/qt5/vehicle_qt5.cpp | 300 | ||||
-rw-r--r-- | navit/vehicle/qt5/vehicle_qt5.h | 58 |
19 files changed, 3308 insertions, 17 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index e2b869203..819e7d450 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,7 +85,9 @@ add_module(graphics/gd "FreeType library not found" FALSE) add_module(graphics/gtk_drawing_area "FreeType library not found" FALSE) add_module(graphics/opengl "FreeType library not found" FALSE) add_module(graphics/sdl "Required library not found" FALSE) +add_module(graphics/egl "Required library not found" FALSE) add_module(graphics/qt_qpainter "Qt libraries not found" FALSE) +add_module(graphics/qt5 "Qt5 libraries not found" FALSE) add_module(gui/qml "Qt Declarative not found" FALSE) add_module(gui/gtk "GTK libs not found" FALSE) add_module(vehicle/gpsd "gpsd lib not found" FALSE) @@ -97,6 +99,7 @@ add_module(binding/python "python libraries not found" FALSE) add_module(speech/dbus "dbus-glib-1 not found" FALSE) add_module(speech/cmdline "neither system() nor CreateProcess() found" FALSE) add_module(vehicle/gpsd_dbus "dbus-glib-1 not found" FALSE) +add_module(vehicle/qt5 "Qt5 libraries not found" FALSE) add_module(speech/speech_dispatcher "speech_dispatcher lib not found" FALSE) add_module(autoload/osso "Default" FALSE) add_module(map/garmin "Garmin library not found" FALSE) @@ -136,6 +139,9 @@ find_package(Glib) find_package(Gmodule) find_package(ZLIB) find_package(Freetype) +find_library(SDL2MAIN SDL2) +find_library(SDL2IMAGE SDL2_image) +find_library(GLES2 GLESv2) find_package(SDL) find_package(SDL_ttf) find_package(SDL_image) @@ -151,11 +157,21 @@ find_package(Threads) libfind_pkg_check_modules(FONTCONFIG fontconfig) #Qt detection if (NOT DISABLE_QT) - # Unfortunately, CMake seems to ignore the "OPTIONAL_COMPONENTS" flag, - # and actually requires all components to be installed. Maybe this can - # be fixed later... - find_package(Qt4 4.7 COMPONENTS QtCore OPTIONAL_COMPONENTS QtGui QtXml QtDeclarative QtSvg) + find_package(Qt5Widgets) + find_package(Qt5Svg) + find_package(Qt5DBus) + find_package(Qt5Positioning) + find_package(Qt5Sensors) + #find_package(Qt5Declarative) + if (Qt5Widgets_FOUND) + else (Qt5Widgets_FOUND) + # Unfortunately, CMake seems to ignore the "OPTIONAL_COMPONENTS" flag, + # and actually requires all components to be installed. Maybe this can + # be fixed later... + find_package(Qt4 4.7 COMPONENTS QtCore OPTIONAL_COMPONENTS QtGui QtXml QtDeclarative QtSvg) + endif (Qt5Widgets_FOUND) endif (NOT DISABLE_QT) + #pkg-config based detection find_package(PkgConfig) if(PKG_CONFIG_FOUND) @@ -274,6 +290,7 @@ else(FREETYPE_FOUND) set_with_reason(graphics/gtk_drawing_area "FreeType library not found" FALSE) set_with_reason(graphics/opengl "FreeType library not found" FALSE) set_with_reason(graphics/sdl "FreeType library not found" FALSE) + set_with_reason(graphics/egl "FreeType library not found" FALSE) endif(FREETYPE_FOUND) if(FONTCONFIG_FOUND) @@ -296,7 +313,17 @@ if (QT_FOUND) endif() endif() endif(QT_FOUND) - +if (Qt5Widgets_FOUND) + set_with_reason(graphics/qt5 "Qt5 found" TRUE ${Qt5Widgets_LIBRARIES} + ${Qt5Declarative_LIBRARIES} + ${Qt5Svg_LIBRARIES} + ${Qt5DBus_LIBRARIES}) +endif () +if (Qt5Positioning_FOUND) + set_with_reason(vehicle/qt5 "Qt5 Positioning found" TRUE + ${Qt5Positioning_LIBRARIES} + ${Qt5Sensors_LIBRARIES}) +endif () if(GTK2_FOUND) # Include gtk.h with "SYSTEM" to avoid GCC compiler warning for gtkitemfactory.h. include_directories(SYSTEM ${GTK2_GTK_INCLUDE_DIR}) @@ -346,6 +373,11 @@ if(SDL_FOUND AND SDLIMAGE_FOUND AND FREETYPE_FOUND) set_with_reason(graphics/sdl "SDL/SDL_image libs found" TRUE ${SDL_LIBRARY} ${SDLIMAGE_LIBRARY}) endif(SDL_FOUND AND SDLIMAGE_FOUND AND FREETYPE_FOUND) +if(SDL2MAIN AND SDL2IMAGE AND GLES2) + set_with_reason(graphics/egl "SDL2/SDL2_image libs found" TRUE ${SDL2MAIN} ${SDL2IMAGE} ${GLES2}) + message(STATUS "EGL libs found : ${SDL2MAIN} ${SDL2IMAGE} ${GLES2}") +endif(SDL2_FOUND AND SDL2IMAGE_FOUND AND GLES2) + if (LIBGPS_FOUND) if (LIBGPS_NEW_FOUND) set(VEHICLE_GPSD_REASON "gpsd lib found") diff --git a/ci/build_linux.sh b/ci/build_linux.sh index 3716f47d9..7e26d0685 100644 --- a/ci/build_linux.sh +++ b/ci/build_linux.sh @@ -1,4 +1,4 @@ -sudo apt-get install cmake libpng12-dev librsvg2-bin libfreetype6-dev libdbus-glib-1-dev g++ libgtk2.0-dev +sudo apt-get install cmake libpng12-dev librsvg2-bin libfreetype6-dev libdbus-glib-1-dev g++ libgtk2.0-dev libqt5svg5-dev cmake_opts="-Dgraphics/qt_qpainter:BOOL=FALSE -Dgui/qml:BOOL=FALSE -DSVG2PNG:BOOL=FALSE -DSAMPLE_MAP=n -Dgraphics/gtk_drawing_area:BOOL=TRUE" diff --git a/ci/pointer-64.png b/ci/pointer-64.png Binary files differnew file mode 100644 index 000000000..904f6878d --- /dev/null +++ b/ci/pointer-64.png diff --git a/ci/publish.sh b/ci/publish.sh index 06f5b147b..2f880f339 100644 --- a/ci/publish.sh +++ b/ci/publish.sh @@ -2,11 +2,11 @@ cd ~/ git clone git@github.com:navit-gps/infrastructure-blackbox.git cd infrastructure-blackbox/keyrings/ openssl aes-256-cbc -d -in keystore.gpg -k $KEY > ~/.keystore -openssl aes-256-cbc -d -in client_secrets.gpg -k $KEY > client_secrets.json +openssl aes-256-cbc -d -in client_secrets.gpg -k $KEY > ~/navit/ci/client_secrets.json openssl aes-256-cbc -d -in androidpublisher.gpg -k $KEY > androidpublisher.dat pip install google-api-python-client -jarsigner -storepass $SP $CIRCLE_ARTIFACTS/navit-$CIRCLE_SHA1-${ARCH}-release-unsigned.apk $key_name +/usr/lib/jvm/java-8-openjdk-amd64/bin/jarsigner -storepass $SP $CIRCLE_ARTIFACTS/navit-$CIRCLE_SHA1-${ARCH}-release-unsigned.apk $key_name /usr/local/android-sdk-linux/build-tools/25.0.1/zipalign 4 $CIRCLE_ARTIFACTS/navit-$CIRCLE_SHA1-${ARCH}-release-unsigned.apk $CIRCLE_ARTIFACTS/navit-$CIRCLE_SHA1-${ARCH}-release-signed.apk python ~/navit/ci/basic_upload_apks.py org.navitproject.navit $CIRCLE_ARTIFACTS/navit-$CIRCLE_SHA1-${ARCH}-release-signed.apk diff --git a/ci/run_linux_tests.sh b/ci/run_linux_tests.sh index 0897ec4e4..3f87161ed 100644 --- a/ci/run_linux_tests.sh +++ b/ci/run_linux_tests.sh @@ -1,18 +1,56 @@ #!/bin/bash +linux_test () { + # create logs dir + mkdir $CIRCLE_ARTIFACTS/logs_${1} + + #run instance of navit and remember pid + ./navit >$CIRCLE_ARTIFACTS/logs_${1}/stdout.txt 2>$CIRCLE_ARTIFACTS/logs_${1}/stderr.txt & + pid=$! + + # give navit time to come up + sleep 5 + + # screen shot root window + import -window root $CIRCLE_ARTIFACTS/default.png + + # run tests on X11 + bash ~/navit/ci/xdotools.sh + + # python ~/navit/ci/dbus_tests.py $CIRCLE_TEST_REPORTS/ + # dbus-send --print-reply --session --dest=org.navit_project.navit /org/navit_project/navit/default_navit org.navit_project.navit.navit.quit + + # kill navit instance + kill $pid +} + set -e pushd ~/linux-bin/navit/ + +# prepare environment by getting a map of berkley +cat > maps/berkeley.xml << EOF +<map type="binfile" data="\$NAVIT_SHAREDIR/maps/berkeley.bin" /> +EOF +wget http://sd-55475.dedibox.fr/berkeley.bin -O maps/berkeley.bin + +# back up config +cp navit.xml navit.xml.bak + +# run gtk test sed -i -e 's@name="Local GPS" profilename="car" enabled="yes" active="1"@name="Local GPS" profilename="car" enabled="no" active="0"@' navit.xml -sed -i -e 's@name="Demo" profilename="car" enabled="no" active="yes"@name="Demo" profilename="car" enabled="yes" active="yes" follow="1" refresh="1"@' navit.xml +sed -i -e 's@name="Demo" profilename="car" enabled="no" @name="Demo" profilename="car" enabled="yes" follow="1" refresh="1"@' navit.xml sed -i -e 's@type="internal" enabled@type="internal" fullscreen="1" font_size="350" enabled@' navit.xml sed -i -e 's@libbinding_dbus.so" active="no"@libbinding_dbus.so" active="yes"@' navit.xml +linux_test gtk_drawing_area -./navit & -pid=$! - -import -window root $CIRCLE_ARTIFACTS/default.png +# restore config +cp navit.xml.bak navit.xml +# run qt5 test +sed -i -e 's@graphics type="gtk_drawing_area"@graphics type="qt5" v="792" h="547"@' navit.xml +sed -i -e 's@name="Local GPS" profilename="car" enabled="yes" active="1"@name="Local GPS" profilename="car" enabled="no" active="0"@' navit.xml +sed -i -e 's@name="Demo" profilename="car" enabled="no" @name="Demo" profilename="car" enabled="yes" follow="1" refresh="1"@' navit.xml +sed -i -e 's@type="internal" enabled@type="internal" fullscreen="1" font_size="350" enabled@' navit.xml +sed -i -e 's@libbinding_dbus.so" active="no"@libbinding_dbus.so" active="yes"@' navit.xml +linux_test qt5 -# python ~/navit/ci/dbus_tests.py $CIRCLE_TEST_REPORTS/ -# -# dbus-send --print-reply --session --dest=org.navit_project.navit /org/navit_project/navit/default_navit org.navit_project.navit.navit.quit diff --git a/ci/xdotools.sh b/ci/xdotools.sh new file mode 100644 index 000000000..36331fd9b --- /dev/null +++ b/ci/xdotools.sh @@ -0,0 +1,94 @@ +sudo apt-get install xdotool +# Use xinput test 4 when running x11vnc on the circleci server to find mouse coordinates + +[ -d $CIRCLE_ARTIFACTS/frames/ ] || mkdir $CIRCLE_ARTIFACTS/frames/ + +event=0 + +send_event (){ + file=`printf "%05d\n" $event` + + import -window root $CIRCLE_ARTIFACTS/frames/tmp.png + if [[ "$1" == "mousemove" ]]; then + composite -gravity NorthWest -geometry +$2+$3 ~/navit/ci/pointer-64.png $CIRCLE_ARTIFACTS/frames/tmp.png $CIRCLE_ARTIFACTS/frames/${file}.png + else + mv $CIRCLE_ARTIFACTS/frames/tmp.png $CIRCLE_ARTIFACTS/frames/${file}.png + fi + event=$((event+1)) + xdotool $@ + sleep 1 +} + + +# Center the view +send_event key KP_Enter # Open main menu +send_event key Down # Select 'Actions' +send_event key KP_Enter # Validate +send_event key Down # Scroll to 'Bookmarks' +send_event key Right # Scroll to 'Former destinations' +send_event key Right # Select 'Town' +send_event key KP_Enter # Validate +# Send 'Berk' +send_event key b +send_event key e +send_event key r +send_event key k +send_event key Down # Highlight search area +send_event key Down # Highlight first result +send_event key KP_Enter # Validate + +# Set the position +send_event mousemove 482 318 click 1 # Open main menu, clicking on a somewhat random position on the map +send_event key Down # Select 'Actions' +send_event key KP_Enter # Validate +send_event key Down # Scroll to 'Bookmarks' +send_event key Right # Scroll to 'Former destinations' +send_event key Right # Select current coordinates +send_event key KP_Enter # Validate + +# Set a destination +send_event key KP_Enter # Open main menu +send_event key Down # Select 'Actions' +send_event key KP_Enter # Validate +send_event key Down # Scroll to 'Bookmarks' +send_event key Right # Scroll to 'Former destinations' +send_event key Right # Select 'Town' +send_event key KP_Enter # Validate +# Send 'oakl' +send_event key o +send_event key a +send_event key k +send_event key l +send_event key Down # Highlight search area +send_event key Down # Highlight first result +send_event key KP_Enter # Validate + +# Switch to 3d view +send_event key KP_Enter # Open main menu +send_event key Down # Select 'Actions' +send_event key Right # Select 'Settings' +send_event key KP_Enter # Validate +send_event key Down # Select 'Display' +send_event key KP_Enter # Validate +send_event key Down # Scroll to 'Layout' +send_event key Right # Scroll to 'Fullscreen' +send_event key Right # Select '3d' +send_event key KP_Enter # Validate +# Send 'Berk' + +# capture 5 seconds of usage +for i in `seq 99994 99999`; do + import -window root $CIRCLE_ARTIFACTS/frames/${i}.png + sleep 1 +done + +# Quit +send_event key KP_Enter # Open main menu +send_event key Down # Select 'Actions' +send_event key Down # Select 'Route' +send_event key Right # Select 'About' +send_event key Right # Select 'Quit' +send_event key KP_Enter # Validate + +# Assemble the gif +convert -delay 100 -loop 0 $CIRCLE_ARTIFACTS/frames/*.png $CIRCLE_ARTIFACTS/town_search.gif diff --git a/navit/attr_def.h b/navit/attr_def.h index b3f77163b..2b0bad448 100644 --- a/navit/attr_def.h +++ b/navit/attr_def.h @@ -281,7 +281,7 @@ ATTR_UNUSED ATTR_UNUSED ATTR_UNUSED ATTR(window_title) -ATTR_UNUSED +ATTR(qt5_platform) ATTR_UNUSED /* poi */ ATTR_UNUSED diff --git a/navit/graphics/egl/CMakeLists.txt b/navit/graphics/egl/CMakeLists.txt new file mode 100644 index 000000000..1943f2bc0 --- /dev/null +++ b/navit/graphics/egl/CMakeLists.txt @@ -0,0 +1,2 @@ +module_add_library(graphics_egl graphics_egl.c) + diff --git a/navit/graphics/egl/graphics_egl.c b/navit/graphics/egl/graphics_egl.c new file mode 100644 index 000000000..4d7780ff4 --- /dev/null +++ b/navit/graphics/egl/graphics_egl.c @@ -0,0 +1,1458 @@ +/** + * 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 <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 <SDL2/SDL_image.h> +#include <GLES2/gl2.h> +#include <EGL/egl.h> + +/* + * This is work in progress * + * Remainng issues : + * - SDL mouse cursor sometimes raise an SDL assertion + * - Dashed lines to implement + * - Full Keyboard handling +*/ + +#define SCREEN_WIDTH 800 +#define SCREEN_HEIGHT 600 + +#define glF(x) x +#define glD(x) x +#define GL_F GL_FLOAT +typedef GLfloat GLf; + +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 fill_poly; + int show_overlays; + int button_timeout; + struct point p; + int width; + int height; + int library_init; + int visible; + int overlay_enabled; + int overlay_autodisabled; + int wraparound; + GLuint framebuffer_name; + GLuint overlay_texture; + struct graphics_priv *parent; + struct graphics_priv *overlays; + struct graphics_priv *next; + struct graphics_gc_priv *background_gc; + enum draw_mode_num mode; + GLuint program; + GLint mvp_location, position_location, color_location, texture_position_location, use_texture_location, texture_location; + 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 + struct graphics_opengl_platform *platform; + struct graphics_opengl_platform_methods *platform_methods; +}; + +struct graphics_image_priv { + SDL_Surface *img; +} graphics_image_priv; + + +struct graphics_opengl_platform { + SDL_Window* eglwindow; + SDL_GLContext eglcontext; + EGLDisplay egldisplay; + EGLConfig config[1]; +}; + +struct contour +{ + struct point *p; + unsigned int count; +}; + +static GHashTable *hImageData; +static int USERWANTSTOQUIT = 0; +static struct graphics_priv *graphics_priv_root; +static struct graphics_priv *graphics_opengl_new_helper(struct + graphics_methods + *meth); + +/* + * GLES 2 Compatible vertex and fragment shaders + * Taken from opengl driver + */ +const char vertex_src [] = +" \ + attribute vec2 position; \ + attribute vec2 texture_position; \ + uniform mat4 mvp; \ + varying vec2 v_texture_position; \ + \ + void main() \ + { \ + v_texture_position=texture_position; \ + gl_Position = mvp*vec4(position, 0.0, 1.0); \ + } \ +"; + +const char fragment_src [] = +" \ + uniform lowp vec4 avcolor; \ + uniform sampler2D texture; \ + uniform bool use_texture; \ + varying vec2 v_texture_position; \ + void main() \ + { \ + if (use_texture) { \ + gl_FragColor = texture2D(texture, v_texture_position); \ + } else { \ + gl_FragColor = avcolor; \ + } \ + } \ +"; + +/* +* C conversion of Efficient Polygon Triangulation +* Found at http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml +* Adapted and debugged for this use +*/ +float area(const struct contour* contour) +{ + int p, q; + int n = contour->count - 1; + float A = 0.f; + + for (p=n-1, q=0; q < n; p=q++) + { + A += contour->p[p].x * contour->p[q].y - contour->p[q].x * contour->p[p].y; + } + return A * .5f; +} + +int +inside_triangle(float Ax, float Ay, + float Bx, float By, + float Cx, float Cy, + float Px, float Py) +{ + float ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy; + float cCROSSap, bCROSScp, aCROSSbp; + + ax = Cx - Bx; ay = Cy - By; + bx = Ax - Cx; by = Ay - Cy; + cx = Bx - Ax; cy = By - Ay; + apx= Px - Ax; apy= Py - Ay; + bpx= Px - Bx; bpy= Py - By; + cpx= Px - Cx; cpy= Py - Cy; + + aCROSSbp = ax*bpy - ay*bpx; + cCROSSap = cx*apy - cy*apx; + bCROSScp = bx*cpy - by*cpx; + + return ((aCROSSbp >= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f)); +} + +int +snip(const struct point* pnt,int u,int v,int w,int n,int *V) +{ + int p; + float Ax, Ay, Bx, By, Cx, Cy, Px, Py; + + Ax = pnt[V[u]].x; + Ay = pnt[V[u]].y; + + Bx = pnt[V[v]].x; + By = pnt[V[v]].y; + + Cx = pnt[V[w]].x; + Cy = pnt[V[w]].y; + + if ( (((Bx-Ax)*(Cy-Ay)) - ((By-Ay)*(Cx-Ax))) < 0.f ) + return 0; + + for (p=0;p<n;p++) + { + if( (p == u) || (p == v) || (p == w) ) + continue; + Px = pnt[V[p]].x; + Py = pnt[V[p]].y; + if (inside_triangle(Ax,Ay,Bx,By,Cx,Cy,Px,Py)) + return 0; + } + + return 1; +} + +int +process_triangles(const struct contour* contour, struct contour* result) +{ + int v; + int contour_size = contour->count - 1; + int polygon_temp_size = contour_size * 80; + int final_count = 0; + result->p = malloc(sizeof(struct point) * polygon_temp_size); + + int n = contour_size; + if ( n < 3 ) return 0; + + int *V = alloca(sizeof(int)*n); + + if ( 0.0f < area(contour) ) + for (v=0; v<n; v++) V[v] = v; + else + for(v=0; v<n; v++) V[v] = (n-1)-v; + + int nv = n; + + int count = 2*nv; + + for(v=nv-1; nv>2; ) + { + /* if we loop, it is probably a non-simple polygon */ + if (0 >= (count--)) + { + //** Triangulate: ERROR - probable bad polygon! + break; + } + + /* three consecutive vertices in current polygon, <u,v,w> */ + int u = v ; if (nv <= u) u = 0; /* previous */ + v = u+1; if (nv <= v) v = 0; /* new v */ + int w = v+1; if (nv <= w) w = 0; /* next */ + + if ( snip(contour->p,u,v,w,nv,V) ) + { + int a,b,c,s,t; + + /* true names of the vertices */ + a = V[u]; b = V[v]; c = V[w]; + + /* output Triangle */ + result->p[final_count++] = contour->p[a]; + result->p[final_count++] = contour->p[b]; + result->p[final_count++] = contour->p[c]; + + if (final_count >= polygon_temp_size){ + free(result->p); + return 0; + } + + /* remove v from remaining polygon */ + for(s=v,t=v+1;t<nv;s++,t++) + V[s] = V[t]; + nv--; + + /* resest error detection counter */ + count = 2*nv; + } + } + + result->count = final_count; + + return 1; +} + +// ** Efficient Polygon Triangulation ** + +/* + * Destroys SDL/EGL context + */ +static void +sdl_egl_destroy(struct graphics_opengl_platform *egl) +{ + if (egl->eglwindow){ + SDL_GL_DeleteContext(egl->eglcontext); + SDL_DestroyWindow(egl->eglwindow); + } + g_free(egl); + SDL_Quit(); +} + +/* + * Swap EGL buffer + */ +static void +sdl_egl_swap_buffers(struct graphics_opengl_platform *egl) +{ + SDL_GL_SwapWindow(egl->eglwindow); +} + +/* + * Main graphic destroy + */ +static void +graphics_destroy(struct graphics_priv *gr) +{ + /*FIXME graphics_destroy is never called */ + gr->freetype_methods.destroy(); + g_free(gr); + gr = NULL; + sdl_egl_destroy(gr->platform); + SDL_Quit(); +} + +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; + 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 *name, int *w, int *h, + struct point *hot, int rotation) +{ + struct graphics_image_priv *gi; + + /* FIXME: meth is not used yet.. so gi leaks. at least xpm is small */ + + struct graphics_image_priv *curr_elem = + g_hash_table_lookup(hImageData, name); + + 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->img->w; + *h = curr_elem->img->h; + hot->x = *w / 2; + hot->y = *h / 2; + return curr_elem; + } + + if (strlen(name) < 4) { + g_hash_table_insert(hImageData, g_strdup(name), + &image_error); + return NULL; + } + + gi = g_new0(struct graphics_image_priv, 1); + gi->img = IMG_Load(name); + if(gi->img) + { + *w=gi->img->w; + *h=gi->img->h; + hot->x=*w/2; + hot->y=*h/2; + } + else + { + /* TODO: debug "colour parse errors" on xpm */ + dbg(lvl_error,"image_new on '%s' failed: %s\n", name, IMG_GetError()); + g_free(gi); + gi = NULL; + g_hash_table_insert(hImageData, + g_strdup(name), + &image_error); + return gi; + } + + g_hash_table_insert(hImageData, g_strdup(name), gi); + return gi; +} + +static void +image_free(struct graphics_priv *gr, struct graphics_image_priv * gi) +{ +// SDL_FreeSurface(gi->img); +// g_free(gi); +} + +static void +set_color(struct graphics_priv *gr, struct graphics_gc_priv *gc) +{ + GLfloat col[4]; + col[0]=gc->fr; + col[1]=gc->fg; + col[2]=gc->fb; + col[3]=gc->fa; + glUniform4fv(gr->color_location, 1, col); +} + +static void +draw_array(struct graphics_priv *gr, struct point *p, int count, GLenum mode) +{ + int i; + GLf *x;//[count*2]; + x = alloca(sizeof(GLf)*count*2); + for (i = 0 ; i < count ; i++) { + x[i*2]=glF(p[i].x); + x[i*2+1]=glF(p[i].y); + } + + glVertexAttribPointer (gr->position_location, 2, GL_FLOAT, 0, 0, x ); + glDrawArrays(mode, 0, count); +} + +static void +draw_rectangle_do(struct graphics_priv *gr, struct point *p, int w, int h) +{ + struct point pa[4]; + pa[0]=pa[1]=pa[2]=pa[3]=*p; + pa[0].x+=w; + pa[1].x+=w; + pa[1].y+=h; + pa[3].y+=h; + draw_array(gr, pa, 4, GL_TRIANGLE_STRIP); +} + + +static void +draw_image_es(struct graphics_priv *gr, struct point *p, int w, int h, unsigned char *data) +{ + GLf x[8]; + GLuint texture; + memset(x, 0, sizeof(x)); + + glGenTextures(1, &texture); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + + x[0]+=glF(1); + x[2]+=glF(1); + x[3]+=glF(1); + x[7]+=glF(1); + glUniform1i(gr->use_texture_location, 1); + glEnableVertexAttribArray(gr->texture_position_location); + glVertexAttribPointer (gr->texture_position_location, 2, GL_FLOAT, 0, 0, x ); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + draw_rectangle_do(gr, p, w, h); + + glUniform1i(gr->use_texture_location, 0); + glDisableVertexAttribArray(gr->texture_position_location); + + glDisable(GL_BLEND); + glDeleteTextures(1, &texture); +} + +inline 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; + } +} + +inline void +draw_overlay(struct graphics_priv *gr) +{ + struct point p_eff; + GLf x[8]; + + get_overlay_pos(gr, &p_eff); + + memset(x, 0, 8*sizeof(GLf)); + x[0]+=glF(1); + x[1]+=glF(1); + x[2]+=glF(1); + x[5]+=glF(1); + + glUniform1i(gr->use_texture_location, 1); + glEnableVertexAttribArray(gr->texture_position_location); + glVertexAttribPointer (gr->texture_position_location, 2, GL_FLOAT, 0, 0, x); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBindTexture(GL_TEXTURE_2D, gr->overlay_texture); + + draw_rectangle_do(graphics_priv_root, &p_eff, gr->width, gr->height); + + glUniform1i(gr->use_texture_location, 0); + glDisableVertexAttribArray(gr->texture_position_location); + + glDisable(GL_BLEND); +} + +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) + || (gr->parent && gr->parent->overlay_enabled + && !gr->overlay_enabled)) { + return; + } + + glLineWidth(gc->linewidth); + + set_color(gr, gc); + graphics_priv_root->dirty = 1; + + draw_array(gr, p, count, GL_LINE_STRIP); +} + + +static void +draw_polygon(struct graphics_priv *gr, struct graphics_gc_priv *gc, + struct point *p, int count) +{ + int ok; + if ((gr->parent && !gr->parent->overlay_enabled) + || (gr->parent && gr->parent->overlay_enabled + && !gr->overlay_enabled)) { + return; + } + set_color(gr, gc); + graphics_priv_root->dirty = 1; + struct contour contour, result; + contour.count = count; + contour.p = p; + ok = process_triangles(&contour, &result); + if (ok && gr->fill_poly){ + draw_array(gr, result.p, result.count, GL_TRIANGLES); + free(result.p); + } else { + draw_array(gr, p, count, GL_LINE_STRIP); + } +} + +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) + || (gr->parent && gr->parent->overlay_enabled + && !gr->overlay_enabled)) { + return; + } + set_color(gr, gc); + draw_rectangle_do(gr, p, w, h); + graphics_priv_root->dirty = 1; +} + +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, + stride, + &white, + &transparent); + + struct point p; + p.x=((x + g->x) >> 6)-1; + p.y=((y + g->y) >> 6)-1; + draw_image_es(gr, &p, g->w+2, g->h+2, 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, + stride + * 4, + &black, + &white, + &transparent); + struct point p; + p.x=(x + g->x) >> 6; + p.y=(y + g->y) >> 6; + draw_image_es(gr, &p, g->w, g->h, glyph); + + g_free(glyph); + } + stride *= 4; + glyph = g_malloc(stride * g->h); + gr->freetype_methods.get_glyph(g, glyph, + stride, + &black, + &white, + &transparent); + struct point p; + p.x=(x + g->x) >> 6; + p.y=(y + g->y) >> 6; + draw_image_es(gr, &p, g->w, g->h, 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) + || (gr->parent && gr->parent->overlay_enabled + && !gr->overlay_enabled)) { + return; + } + + struct font_freetype_text *t; + int color = 1; + + if (!font) { + dbg(lvl_error, "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) +{ + draw_image_es(gr, p, img->img->w, img->img->h, img->img->pixels); +} + +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; +} + +/* + * Draws map in background + */ +static void +draw_background(struct graphics_priv *gr) +{ + struct point p_eff; + GLf x[8]; + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glViewport(0, 0, gr->width, gr->height); + //get_overlay_pos(gr, &p_eff); + p_eff.x = gr->p.x; + p_eff.y = gr->p.y; + + memset(x, 0, 8*sizeof(GLf)); + x[0]+=glF(1); + x[1]+=glF(1); + x[2]+=glF(1); + x[5]+=glF(1); + + glUniform1i(gr->use_texture_location, 1); + glEnableVertexAttribArray(gr->texture_position_location); + glVertexAttribPointer (gr->texture_position_location, 2, GL_FLOAT, 0, 0, x); + + glDisable(GL_BLEND); + glBindTexture(GL_TEXTURE_2D, gr->overlay_texture); + + draw_rectangle_do(gr, &p_eff, gr->width, gr->height); + + glUniform1i(gr->use_texture_location, 0); + glDisableVertexAttribArray(gr->texture_position_location); +} + +/* + Drawing method : + Map and overlays are rendered in an offscreen buffer (See render to texture) + and are recomposed altogether at draw_mode_end request for root +*/ +static void +draw_mode(struct graphics_priv *gr, enum draw_mode_num mode) +{ + GLfloat matrix[16]; + struct graphics_priv *overlay = NULL; + int i; + + if (mode == draw_mode_begin){ + // Should not be necessary... + // SDL_GL_MakeCurrent(gr->platform->eglwindow, gr->platform->eglcontext); + + if (gr->parent == NULL){ + // Full redraw, reset drag position + gr->p.x = 0; + gr->p.y = 0; + } + + // Need to setup appropriate projection matrix + for (i = 0; i < 16 ; i++){ + matrix[i] = 0.0; + } + + matrix[0]=2.0 / gr->width; + matrix[5]=-2.0 / gr->height; + matrix[10]=1; + matrix[12]=-1; + matrix[13]=1; + matrix[15]=1; + glUniformMatrix4fv(gr->mvp_location, 1, GL_FALSE, matrix); + + glBindFramebuffer(GL_FRAMEBUFFER, gr->framebuffer_name); + glViewport(0,0,gr->width,gr->height); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + + if (mode == draw_mode_end && gr->parent == NULL) { + overlay = gr->overlays; + draw_background(gr); + while(overlay){ + if (gr->overlay_enabled) + draw_overlay(overlay); + overlay = overlay->next; + } + sdl_egl_swap_buffers(gr->platform); + } + + gr->mode = mode; +} + +static int +graphics_opengl_fullscreen(struct window *w, int on) +{ + return 1; +} + +static void +graphics_opengl_disable_suspend(struct window *w) +{ + // No op +} + + +static GLuint +load_shader(const char *shader_source, GLenum type) +{ + GLuint shader = glCreateShader(type); + + glShaderSource(shader, 1, &shader_source, NULL); + glCompileShader(shader); + + return shader; +} + +static void * +get_data(struct graphics_priv *this, const char *type) +{ + GLuint vertexShader; + GLuint fragmentShader; + struct window* win; + int i; + + if (!strcmp(type, "gtk_widget")) { + fprintf(stderr, + "Currently GTK gui is not yet supported with EGL graphics driver\n"); + return NULL; + } + + if(strcmp(type, "window") == 0) { + SDL_GL_MakeCurrent(this->platform->eglwindow, this->platform->eglcontext); + + glClearColor ( 0 , 0 , 0 , 1); + glClear ( GL_COLOR_BUFFER_BIT ); + + callback_list_call_attr_2(graphics_priv_root->cbl, attr_resize, + GINT_TO_POINTER(this->width), GINT_TO_POINTER(this->height)); + + this->program = glCreateProgram(); + vertexShader = load_shader(vertex_src, GL_VERTEX_SHADER); + fragmentShader = load_shader(fragment_src, GL_FRAGMENT_SHADER); + + glAttachShader(this->program, vertexShader); + glAttachShader(this->program, fragmentShader); + glLinkProgram(this->program); + glUseProgram(this->program); + + this->mvp_location = glGetUniformLocation(this->program, "mvp"); + this->position_location = glGetAttribLocation(this->program, "position"); + glEnableVertexAttribArray(this->position_location); + this->texture_position_location = glGetAttribLocation(this->program, "texture_position"); + this->color_location = glGetUniformLocation(this->program, "avcolor"); + this->texture_location = glGetUniformLocation(this->program, "texture"); + this->use_texture_location = glGetUniformLocation(this->program, "use_texture"); + + glUniform1i(this->use_texture_location, 0); + glUniform1i(this->texture_location, 0); + + win=g_new(struct window, 1); + win->priv=this; + win->disable_suspend=NULL; + win->fullscreen = graphics_opengl_fullscreen; + win->disable_suspend = graphics_opengl_disable_suspend; + return win; + } + + return NULL; + +} + +static void +overlay_disable(struct graphics_priv *gr, int disable) +{ + gr->overlay_enabled = !disable; + gr->force_redraw = 1; + draw_mode(gr, draw_mode_end); +} + +// Need more testing +static void +overlay_resize(struct graphics_priv *gr, struct point *p, int w, int h, + 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; + // Reset overlay texture + glDeleteTextures(1, &gr->overlay_texture); + glGenTextures(1, &gr->overlay_texture); + glBindTexture(GL_TEXTURE_2D, gr->overlay_texture); + glTexImage2D(GL_TEXTURE_2D, 0,GL_RGBA, gr->width, gr->height, 0,GL_RGBA, GL_UNSIGNED_BYTE, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gr->overlay_texture, 0); + } + + callback_list_call_attr_2(gr->cbl, attr_resize, + GINT_TO_POINTER(gr->width), + GINT_TO_POINTER(gr->height)); + } +} + + +static struct graphics_priv *overlay_new(struct graphics_priv *gr, + struct graphics_methods *meth, + struct point *p, int w, int h, + int wraparound); + +static struct graphics_methods graphics_methods = { + graphics_destroy, + draw_mode, + draw_lines, + draw_polygon, + draw_rectangle, + NULL, + draw_text, + draw_image, + NULL, + draw_drag, + NULL, + gc_new, + background_gc, + overlay_new, + image_new, + get_data, + image_free, + NULL, + overlay_disable, + overlay_resize, + NULL, /* set_attr, */ + NULL, /* show_native_keyboard */ + NULL, /* hide_native_keyboard */ +}; + +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_category_font("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 = + (void (*) (struct graphics_priv *, struct graphics_font_priv *, + char *, int, int, struct point*, int)) this->freetype_methods.get_text_bbox; + return this; +} + +static void +create_framebuffer_texture(struct graphics_priv *gr) +{ + GLenum status; + // Prepare a new framebuffer object + glGenFramebuffers(1, &gr->framebuffer_name); + glBindFramebuffer(GL_FRAMEBUFFER, gr->framebuffer_name); + + glGenTextures(1, &gr->overlay_texture); + glBindTexture(GL_TEXTURE_2D, gr->overlay_texture); + glTexImage2D(GL_TEXTURE_2D, 0,GL_RGBA, gr->width, gr->height, 0,GL_RGBA, GL_UNSIGNED_BYTE, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gr->overlay_texture, 0); + + status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + printf("Error creating texture framebuffer for overlay, exiting.\n"); + SDL_Quit(); + exit(1); + } +} + +static struct graphics_priv * +overlay_new(struct graphics_priv *gr, struct graphics_methods *meth, + struct point *p, int w, int h, int wraparound) +{ + struct graphics_priv *this = graphics_opengl_new_helper(meth); + + this->p.x = p->x; + this->p.y = p->y; + + this->width = w; + this->height = h; + this->parent = gr; + this->overlays = NULL; + this->fill_poly = 1; + + // Copy shader locations parameters + this->mvp_location = graphics_priv_root->mvp_location; + this->position_location = graphics_priv_root->position_location; + this->texture_position_location = graphics_priv_root->texture_position_location; + this->color_location = graphics_priv_root->color_location; + this->texture_location = graphics_priv_root->texture_location; + this->use_texture_location = graphics_priv_root->use_texture_location; + + if ((w == 0) || (h == 0)) { + this->overlay_autodisabled = 1; + } else { + this->overlay_autodisabled = 0; + } + this->overlay_enabled = 1; + + this->next = gr->overlays; + gr->overlays = this; + + create_framebuffer_texture(this); + + return this; +} + + +static gboolean graphics_sdl_idle(void *data) +{ + struct graphics_priv *gr = (struct graphics_priv *)data; + struct point p; + SDL_Event ev; + int ret; + char key_mod = 0; + char keybuf[8]; + char keycode; + + // Process SDL events (KEYS + MOUSE) + while(1) + { + ret = SDL_PollEvent(&ev); + + if(!ret) + break; + + switch(ev.type) + { + case SDL_MOUSEMOTION: + { + p.x = ev.motion.x; + p.y = ev.motion.y; + //gr->force_redraw = 1; + callback_list_call_attr_1(gr->cbl, attr_motion, (void *)&p); + break; + } + + case SDL_KEYDOWN: + { + memset(keybuf, 0, sizeof(keybuf)); + switch(ev.key.keysym.sym) + { + case SDLK_F1: + graphics_priv_root->fill_poly = !graphics_priv_root->fill_poly; + break; + case SDLK_F2: + graphics_priv_root->show_overlays = !graphics_priv_root->show_overlays; + break; + case SDLK_LEFT: + { + keybuf[0] = NAVIT_KEY_LEFT; + break; + } + case SDLK_RIGHT: + { + keybuf[0] = NAVIT_KEY_RIGHT; + break; + } + case SDLK_BACKSPACE: + { + keybuf[0] = NAVIT_KEY_BACKSPACE; + break; + } + case SDLK_RETURN: + { + keybuf[0] = NAVIT_KEY_RETURN; + break; + } + case SDLK_DOWN: + { + keybuf[0] = NAVIT_KEY_DOWN; + break; + } + case SDLK_PAGEUP: + { + keybuf[0] = NAVIT_KEY_ZOOM_OUT; + break; + } + case SDLK_UP: + { + keybuf[0] = NAVIT_KEY_UP; + break; + } + case SDLK_PAGEDOWN: + { + keybuf[0] = NAVIT_KEY_ZOOM_IN; + break; + } + case SDLK_ESCAPE: + { + USERWANTSTOQUIT = 1; + break; + } + default: + { + /* return unicode chars when they can be converted to ascii */ + // Need more work... + keycode = ev.key.keysym.sym; + keybuf[0] = keycode <= 127 ? keycode : 0; + break; + } + } + if (keybuf[0]) { + callback_list_call_attr_1(gr->cbl, attr_keypress, (void *)keybuf); + } + break; + } + case SDL_KEYUP: + { + break; + } + case SDL_MOUSEBUTTONDOWN: + { + p.x = ev.button.x; + p.y = ev.button.y; + graphics_priv_root->force_redraw = 1; + callback_list_call_attr_3(gr->cbl, attr_button, GINT_TO_POINTER(1), GINT_TO_POINTER((int)ev.button.button), (void *)&p); + break; + } + + case SDL_MOUSEBUTTONUP: + { + p.x = ev.button.x; + p.y = ev.button.y; + callback_list_call_attr_3(gr->cbl, attr_button, GINT_TO_POINTER(0), GINT_TO_POINTER((int)ev.button.button), (void *)&p); + break; + } + + case SDL_QUIT: + { + break; + } + default: + { + break; + } + } + } + + if (USERWANTSTOQUIT){ + SDL_Quit(); + exit(0); + } + + return TRUE; +} + + +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; + + hImageData = g_hash_table_new(g_str_hash, g_str_equal); + struct graphics_priv *this = graphics_opengl_new_helper(meth); + graphics_priv_root = this; + + this->nav = nav; + this->parent = NULL; + this->overlay_enabled = 1; + this->framebuffer_name = 0; + this->overlays = NULL; + this->fill_poly = 1; + this->show_overlays = 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; + + this->framebuffer_name = 0; + + graphics_priv_root->cbl = cbl; + graphics_priv_root->width = this->width; + graphics_priv_root->height = this->height; + + struct graphics_opengl_platform *ret=g_new0(struct graphics_opengl_platform,1); + + // SDL Init + int sdl_status = SDL_Init(SDL_INIT_VIDEO|SDL_INIT_EVENTS); + + if (sdl_status != 0){ + fprintf(stderr, "\nUnable to initialize SDL: %i %s\n", sdl_status, SDL_GetError() ); + exit(1); + } + + Uint32 flags = SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL | SDL_WINDOW_FULLSCREEN; + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0); + // I think it's not necessary to sync vblank, update is quite slow + SDL_GL_SetSwapInterval(0); + + ret->eglwindow = SDL_CreateWindow( + "Navit EGL", // window title + SDL_WINDOWPOS_UNDEFINED, // initial x position + SDL_WINDOWPOS_UNDEFINED, // initial y position + this->width, // width, in pixels + this->height, // height, in pixels + flags + ); + + if (ret->eglwindow == NULL) { + fprintf(stderr, "\nUnable to initialize SDL window: %s\n", SDL_GetError() ); + goto error; + } + + ret->eglcontext = SDL_GL_CreateContext(ret->eglwindow); + if (ret->eglcontext == NULL){ + printf("EGL context creation failed\n"); + goto error; + } + + this->platform = ret; + create_framebuffer_texture(this); + g_timeout_add(G_PRIORITY_DEFAULT+10, graphics_sdl_idle, this); + glDisable(GL_DEPTH_TEST); + return this; +error: + SDL_Quit(); + return NULL; +} + +void +plugin_init(void) +{ + plugin_register_category_graphics("egl", graphics_opengl_new); +} + diff --git a/navit/graphics/qt5/CMakeLists.txt b/navit/graphics/qt5/CMakeLists.txt new file mode 100644 index 000000000..9c2574e8b --- /dev/null +++ b/navit/graphics/qt5/CMakeLists.txt @@ -0,0 +1,9 @@ +# Find includes in corresponding build directories +set(CMAKE_INCLUDE_CURRENT_DIR ON) +# Instruct CMake to run moc automatically when needed. +set(CMAKE_AUTOMOC ON) + +# Find the QtWidgets library +find_package(Qt5Widgets) + +module_add_library(graphics_qt5 graphics_qt5.cpp event_qt5.cpp QNavitWidget.cpp) diff --git a/navit/graphics/qt5/QNavitWidget.cpp b/navit/graphics/qt5/QNavitWidget.cpp new file mode 100644 index 000000000..fe3a618e2 --- /dev/null +++ b/navit/graphics/qt5/QNavitWidget.cpp @@ -0,0 +1,158 @@ +/** + * Navit, a modular navigation system. + * Copyright (C) 2005-2008 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 "config.h" +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include "item.h" +#include "point.h" +#include "graphics.h" +#include "color.h" +#include "plugin.h" +#include "event.h" +#include "debug.h" +#include "window.h" +#include "callback.h" +#if defined(WINDOWS) || defined(WIN32) || defined (HAVE_API_WIN32_CE) +#include <windows.h> +#endif +#include "graphics_qt5.h" +#include "QNavitWidget.h" +#include "QNavitWidget.moc" + +QNavitWidget :: QNavitWidget(struct graphics_priv *my_graphics_priv, + QWidget * parent, + Qt::WindowFlags flags): QWidget(parent, flags) +{ + graphics_priv = my_graphics_priv; +} + +bool QNavitWidget::event(QEvent *event) +{ + if (event->type() == QEvent::Gesture) + dbg(lvl_debug, "Gesture event caught"); + //return gestureEvent(static_cast<QGestureEvent*>(event)); + return QWidget::event(event); +} + +void QNavitWidget :: paintEvent(QPaintEvent * event) +{ +// dbg(lvl_debug,"enter\n"); + QPainter painter(this); + /* color background if any */ + if (graphics_priv->background_graphics_gc_priv != NULL) + { + painter.setPen(*graphics_priv->background_graphics_gc_priv->pen); + painter.fillRect(0, 0, graphics_priv->pixmap->width(), + graphics_priv->pixmap->height(), + *graphics_priv->background_graphics_gc_priv->brush); + } + painter.drawPixmap(0,0,*graphics_priv->pixmap); + +} + +void QNavitWidget::resizeEvent(QResizeEvent * event) +{ + QPainter * painter = NULL; + if(graphics_priv->pixmap != NULL) + { + delete graphics_priv->pixmap; + graphics_priv->pixmap = NULL; + } + + graphics_priv->pixmap=new QPixmap(size()); + graphics_priv->pixmap->fill(); + painter = new QPainter(graphics_priv->pixmap); + QBrush brush; + painter->fillRect(0, 0, width(), height(), brush); + if(painter != NULL) + delete painter; + dbg(lvl_debug,"size %dx%d\n", width(), height()); + dbg(lvl_debug,"pixmap %p %dx%d\n", graphics_priv->pixmap, graphics_priv->pixmap->width(), graphics_priv->pixmap->height()); + /* if the root window got resized, tell navit about it */ + if(graphics_priv->root) + resize_callback(width(),height()); +} + +void QNavitWidget::mouseEvent(int pressed, QMouseEvent *event) +{ + struct point p; +// dbg(lvl_debug,"enter\n"); + p.x=event->x(); + p.y=event->y(); + switch (event->button()) { + case Qt::LeftButton: + callback_list_call_attr_3(callbacks, attr_button, GINT_TO_POINTER(pressed), GINT_TO_POINTER(1), GINT_TO_POINTER(&p)); + break; + case Qt::MidButton: + callback_list_call_attr_3(callbacks, attr_button, GINT_TO_POINTER(pressed), GINT_TO_POINTER(2), GINT_TO_POINTER(&p)); + break; + case Qt::RightButton: + callback_list_call_attr_3(callbacks, attr_button, GINT_TO_POINTER(pressed), GINT_TO_POINTER(3), GINT_TO_POINTER(&p)); + break; + default: + break; + } +} + +void QNavitWidget::mousePressEvent(QMouseEvent *event) +{ +// dbg(lvl_debug,"enter\n"); + mouseEvent(1, event); +} + +void QNavitWidget::mouseReleaseEvent(QMouseEvent *event) +{ +// dbg(lvl_debug,"enter\n"); + mouseEvent(0, event); +} + +void QNavitWidget::mouseMoveEvent(QMouseEvent *event) +{ + struct point p; +// dbg(lvl_debug,"enter\n"); + p.x=event->x(); + p.y=event->y(); + callback_list_call_attr_1(callbacks, attr_motion, (void *)&p); +} + +void QNavitWidget::wheelEvent(QWheelEvent *event) +{ + struct point p; + int button; + dbg(lvl_debug,"enter"); + p.x=event->x(); // xy-coordinates of the mouse pointer + p.y=event->y(); + + if (event->delta() > 0) // wheel movement away from the person + button=4; + else if (event->delta() < 0) // wheel movement towards the person + button=5; + else + button=-1; + + if (button != -1) { + callback_list_call_attr_3(callbacks, attr_button, GINT_TO_POINTER(1), GINT_TO_POINTER(button), GINT_TO_POINTER(&p)); + callback_list_call_attr_3(callbacks, attr_button, GINT_TO_POINTER(0), GINT_TO_POINTER(button), GINT_TO_POINTER(&p)); + } + + event->accept(); +} diff --git a/navit/graphics/qt5/QNavitWidget.h b/navit/graphics/qt5/QNavitWidget.h new file mode 100644 index 000000000..3b048ccf7 --- /dev/null +++ b/navit/graphics/qt5/QNavitWidget.h @@ -0,0 +1,33 @@ +#ifndef __QNavitWidget_h +#define __QNavitWidget_h +class QNavitWidget; +#include "graphics_qt5.h" +#include <QPixmap> +#include <QWidget> +#include <QMouseEvent> +#include <QWheelEvent> +#include <QEvent> + +class QNavitWidget : public QWidget +{ + Q_OBJECT +public: + QNavitWidget(struct graphics_priv *my_graphics_priv, + QWidget * parent, + Qt::WindowFlags flags); +protected: + virtual bool event(QEvent *event); + virtual void paintEvent(QPaintEvent * event); + virtual void resizeEvent(QResizeEvent * event); + virtual void mouseEvent(int pressed, QMouseEvent *event); + virtual void mousePressEvent(QMouseEvent *event); + virtual void mouseReleaseEvent(QMouseEvent *event); + virtual void mouseMoveEvent(QMouseEvent *event); + virtual void wheelEvent(QWheelEvent * event); + + +private: + struct graphics_priv *graphics_priv; +}; +#endif + diff --git a/navit/graphics/qt5/event_qt5.cpp b/navit/graphics/qt5/event_qt5.cpp new file mode 100644 index 000000000..9cea3d921 --- /dev/null +++ b/navit/graphics/qt5/event_qt5.cpp @@ -0,0 +1,185 @@ +/** + * Navit, a modular navigation system. + * Copyright (C) 2005-2008 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 <stdio.h> +#include <stdlib.h> +#include "config.h" +#include "navit/point.h" +#include "navit/item.h" +#include "navit/graphics.h" +#include "navit/color.h" +#include "navit/debug.h" +#include "navit/plugin.h" +#include "navit/callback.h" +#include "navit/event.h" +#include "navit/window.h" +#include "navit/keys.h" +#include "navit/navit.h" +#if defined(WINDOWS) || defined(WIN32) || defined (HAVE_API_WIN32_CE) +#include <windows.h> +#endif +#include <QSocketNotifier> +#include "graphics_qt5.h" +#include "event_qt5.h" +#include "event_qt5.moc" + +struct event_watch { + QSocketNotifier *sn; + struct callback *cb; + int fd; +}; + +static void event_qt5_remove_timeout(struct event_timeout *to); + +qt5_navit_timer :: qt5_navit_timer (QObject * parent) : QObject(parent) +{ + timer_type=g_hash_table_new(NULL, NULL); + timer_callback=g_hash_table_new(NULL, NULL); + watches=g_hash_table_new(NULL,NULL); + dbg(lvl_debug, "qt5_navit_timer object created\n"); +} + +void qt5_navit_timer :: timerEvent (QTimerEvent * event) +{ + int id=event->timerId(); + void* multi = NULL; +// dbg(lvl_debug, "TimerEvent (%d)\n", id); + struct callback *cb=(struct callback *)g_hash_table_lookup(timer_callback, (void *)(long)id); + if (cb) + callback_call_0(cb); + /* remove timer if it was oneshot timer */ + if (g_hash_table_lookup_extended(timer_type, (void *)(long)id, NULL, &multi)) + { + /* it's still in the list */ + if(((int)(long)multi) == 0) + event_qt5_remove_timeout((struct event_timeout *)(long)id); + } +// dbg(lvl_debug, "TimerEvent (%d) leave\n", id); +} + +qt5_navit_timer * qt5_timer = NULL; + +static void +event_qt5_main_loop_run(void) +{ + + dbg(lvl_debug,"enter\n"); + if(navit_app != NULL) + navit_app->exec(); + +} + +static void event_qt5_main_loop_quit(void) +{ + dbg(lvl_debug,"enter\n"); + exit(0); +} + +static struct event_watch * +event_qt5_add_watch(int fd, enum event_watch_cond cond, struct callback *cb) +{ + dbg(lvl_debug,"enter fd=%d\n",(int)(long)fd); + struct event_watch *ret=g_new0(struct event_watch, 1); + ret->fd=fd; + ret->cb=cb; + g_hash_table_insert(qt5_timer->watches, GINT_TO_POINTER(fd), ret); + ret->sn=new QSocketNotifier(fd, QSocketNotifier::Read, qt5_timer); + QObject::connect(ret->sn, SIGNAL(activated(int)), qt5_timer, SLOT(watchEvent(int))); + return ret; +} + +static void +event_qt5_remove_watch(struct event_watch *ev) +{ + dbg(lvl_debug,"enter\n"); + g_hash_table_remove(qt5_timer->watches, GINT_TO_POINTER(ev->fd)); + delete(ev->sn); + g_free(ev); +} + + +static struct event_timeout * +event_qt5_add_timeout(int timeout, int multi, struct callback *cb) +{ + int id; + dbg(lvl_debug,"add timeout %d, mul %d, %p ==",timeout, multi, cb); + id=qt5_timer->startTimer(timeout); + dbg(lvl_debug,"%d\n", id); + g_hash_table_insert(qt5_timer->timer_callback, (void *)(long)id, cb); + g_hash_table_insert(qt5_timer->timer_type, (void *)(long)id, (void *)(long)!!multi); + return (struct event_timeout *)(long)id; +} + +static void +event_qt5_remove_timeout(struct event_timeout *to) +{ + dbg(lvl_debug,"remove timeout (%d)\n",(int)(long)to); + qt5_timer->killTimer((int)(long)to); + g_hash_table_remove(qt5_timer->timer_callback, to); + g_hash_table_remove(qt5_timer->timer_type, to); +} + + +static struct event_idle * +event_qt5_add_idle(int priority, struct callback *cb) +{ + dbg(lvl_debug,"add idle event\n"); + return (struct event_idle *)event_qt5_add_timeout(0, 1, cb); +} + +static void +event_qt5_remove_idle(struct event_idle *ev) +{ + dbg(lvl_debug,"Remove idle timeout\n"); + event_qt5_remove_timeout((struct event_timeout *) ev); +} + +static void +event_qt5_call_callback(struct callback_list *cb) +{ + dbg(lvl_debug,"enter\n"); +} + +static struct event_methods event_qt5_methods = { + event_qt5_main_loop_run, + event_qt5_main_loop_quit, + event_qt5_add_watch, + event_qt5_remove_watch, + event_qt5_add_timeout, + event_qt5_remove_timeout, + event_qt5_add_idle, + event_qt5_remove_idle, + event_qt5_call_callback, +}; + +static struct event_priv * +event_qt5_new(struct event_methods *meth) +{ + *meth=event_qt5_methods; + qt5_timer = new qt5_navit_timer(NULL); + return NULL; +} + + +void +qt5_event_init(void) +{ + plugin_register_category_event("qt5", event_qt5_new); +} diff --git a/navit/graphics/qt5/event_qt5.h b/navit/graphics/qt5/event_qt5.h new file mode 100644 index 000000000..f999d3a46 --- /dev/null +++ b/navit/graphics/qt5/event_qt5.h @@ -0,0 +1,18 @@ +#include <glib.h> +#include <QObject> + +class qt5_navit_timer : public QObject +{ + Q_OBJECT +public: + qt5_navit_timer(QObject * parent = 0); + GHashTable *timer_type; + GHashTable *timer_callback; + GHashTable *watches; +protected: + void timerEvent(QTimerEvent * event); +}; + + +void +qt5_event_init(void); diff --git a/navit/graphics/qt5/graphics_qt5.cpp b/navit/graphics/qt5/graphics_qt5.cpp new file mode 100644 index 000000000..81fec32c2 --- /dev/null +++ b/navit/graphics/qt5/graphics_qt5.cpp @@ -0,0 +1,837 @@ +/** + * Navit, a modular navigation system. + * Copyright (C) 2005-2008 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 "config.h" +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include "item.h" +#include "point.h" +#include "graphics.h" +#include "color.h" +#include "plugin.h" +#include "event.h" +#include "debug.h" +#include "window.h" +#include "callback.h" +#if defined(WINDOWS) || defined(WIN32) || defined (HAVE_API_WIN32_CE) +#include <windows.h> +#endif +#include "graphics_qt5.h" +#include "event_qt5.h" +#include "QNavitWidget.h" +#include <QApplication> +#include <QDesktopWidget> +#include <QPixmap> +#include <QPainter> +#include <QFont> +#include <QSvgRenderer> +#include <QPixmapCache> +#include <QDBusConnection> +#include <QDBusInterface> + +struct callback_list* callbacks; +QApplication * navit_app = NULL; + +struct graphics_font_priv { + QFont * font; +}; + +struct graphics_image_priv { + QPixmap * pixmap; +}; + +static void +graphics_destroy(struct graphics_priv *gr) +{ +// dbg(lvl_debug,"enter\n"); +#ifdef QT_QPAINTER_USE_FREETYPE + gr->freetype_methods.destroy(); +#endif + /* destroy painter */ + if(gr->painter != NULL) + delete(gr->painter); + /* destroy pixmap */ + if(gr->pixmap != NULL) + delete(gr->pixmap); + /* destroy widget */ + delete(gr->widget); + /* unregister from parent, if any */ + if(gr->parent != NULL) + { + g_hash_table_remove(gr->parent->overlays, gr); + } +#ifdef SAILFISH_OS + if(gr->display_on_ev != NULL) + { + event_remove_timeout(gr->display_on_ev); + } + if(gr->display_on_cb != NULL) + { + g_free(gr->display_on_cb); + } +#endif + /* destroy overlays hash */ + g_hash_table_destroy(gr->overlays); + /* destroy global application if destroying the last */ + if(gr->root) + { + if(navit_app != NULL) + { + delete (navit_app); + } + navit_app = NULL; + /* destroy argv if any */ + while(gr->argc > 0) + { + gr->argc --; + if(gr->argv[gr->argc] != NULL) + g_free(gr->argv[gr->argc]); + } + } + /* destroy self */ + g_free(gr); +} + +static void font_destroy(struct graphics_font_priv *font) +{ +// dbg(lvl_debug,"enter\n"); + if(font->font != NULL) + delete(font->font); + g_free(font); + +} + +static struct graphics_font_methods font_methods = { + font_destroy +}; + +static struct graphics_font_priv *font_new(struct graphics_priv *gr, struct graphics_font_methods *meth, char *font, int size, int flags) +{ + struct graphics_font_priv *font_priv; +// dbg(lvl_debug,"enter (font %s, %d)\n", font, size); + font_priv = g_new0(struct graphics_font_priv, 1); + if(font != NULL) + font_priv->font=new QFont(font,size/16); + else + font_priv->font=new QFont("Arial",size/16); + font_priv->font->setStyleStrategy(QFont::NoAntialias); + *meth=font_methods; + return font_priv; +} + +static void +gc_destroy(struct graphics_gc_priv *gc) +{ +// dbg(lvl_debug,"enter gc=%p\n", gc); + delete(gc->pen); + delete(gc->brush); + g_free(gc); +} + +static void +gc_set_linewidth(struct graphics_gc_priv *gc, int w) +{ +// dbg(lvl_debug,"enter gc=%p, %d\n", gc, w); + gc->pen->setWidth(w); +} + +static void +gc_set_dashes(struct graphics_gc_priv *gc, int w, int offset, unsigned char *dash_list, int n) +{ + if(n <= 0) + { + dbg(lvl_error, "Refuse to set dashes without dash pattern"); + } + /* use Qt dash feature */ + QVector<qreal> dashes; + gc->pen->setWidth(w); + gc->pen->setDashOffset(offset); + for(int a = 0; a < n; a ++) + { + dashes << dash_list[a]; + } + /* Qt requires the pattern to have even element count. Add the last + * element twice if n doesn't divide by two + */ + if((n % 2) != 0) + { + dashes << dash_list[n-1]; + } + gc->pen->setDashPattern(dashes); +} + +static void +gc_set_foreground(struct graphics_gc_priv *gc, struct color *c) +{ + QColor col(c->r >> 8, c->g >> 8, c->b >> 8, c->a >> 8 ); +// dbg(lvl_debug,"context %p: color %02x%02x%02x\n",gc, c->r >> 8, c->g >> 8, c->b >> 8); + gc->pen->setColor(col); + gc->brush->setColor(col); + //gc->c=*c; +} + +static void +gc_set_background(struct graphics_gc_priv *gc, struct color *c) +{ + QColor col(c->r >> 8, c->g >> 8, c->b >> 8, c->a >> 8 ); +// dbg(lvl_debug,"context %p: color %02x%02x%02x\n",gc, c->r >> 8, c->g >> 8, c->b >> 8); + //gc->pen->setColor(col); + //gc->brush->setColor(col); +} + +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 * graphics_gc_priv = NULL; +// dbg(lvl_debug,"enter gr==%p\n", gr); + graphics_gc_priv = g_new0(struct graphics_gc_priv, 1); + graphics_gc_priv->graphics_priv = gr; + graphics_gc_priv->pen=new QPen(); + graphics_gc_priv->brush=new QBrush(Qt::SolidPattern); + + *meth=gc_methods; + return graphics_gc_priv; +} + +static void image_destroy(struct graphics_image_priv *img) +{ +// dbg(lvl_debug, "enter\n"); + if(img->pixmap != NULL) + delete(img->pixmap); + g_free(img); +} + +struct graphics_image_methods image_methods ={ + image_destroy +}; + + +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) +{ + struct graphics_image_priv * image_priv; +// dbg(lvl_debug,"enter %s, %d %d\n", path, *w, *h); + if(path[0] == 0) + { + dbg(lvl_debug,"Refuse to load image without path\n"); + return NULL; + } + QString key(path); + QString renderer_key(key); + QString extension = key.right(key.lastIndexOf(".")); + QFile imagefile(key); + if(!imagefile.exists()) + { + /* file doesn't exit. Either navit wants us to guess file name by + * ommitting exstension, or the file does really not exist. + */ + if(extension != "") + { + /*file doesn't exist. give up */ + dbg(lvl_debug,"File %s does not exist\n",path); + return NULL; + } + else + { + /* add ".svg" for renderer to try .svg file first in renderer */ + dbg(lvl_debug, "Guess extension on %s\n", path); + renderer_key += ".svg"; + } + } + image_priv = g_new0(struct graphics_image_priv, 1); + *meth = image_methods; + + /* check if this can be rendered */ + if(renderer_key.endsWith("svg")) + { + QSvgRenderer renderer(renderer_key); + if(renderer.isValid()) + { + dbg(lvl_debug,"render %s\n", path); + /* try to render this */ + /* assume "standard" size if size is not given */ + if(*w <= 0) *w = renderer.defaultSize().width(); + if(*h <= 0) *h = renderer.defaultSize().height(); + image_priv->pixmap=new QPixmap(*w, *h); + image_priv->pixmap->fill(Qt::transparent); + QPainter painter(image_priv->pixmap); + renderer.render(&painter); + } + } + + if (image_priv->pixmap == NULL) { + /*cannot be rendered. try to load it */ + dbg(lvl_debug,"cannot render %s\n",path); + image_priv->pixmap=new QPixmap(key); + } + + /* check if we got image */ + if (image_priv->pixmap->isNull()) { + g_free(image_priv); + return NULL; + } + else + { + /* check if we need to scale this */ + if((*w > 0) && (*h > 0)) + { + if((image_priv->pixmap->width() != *w) || + (image_priv->pixmap->height() != *h)) + { + dbg(lvl_debug,"scale pixmap %s, %d->%d,%d->%d\n",path, image_priv->pixmap->width(), *w, image_priv->pixmap->height(), *h); + QPixmap * scaled = new QPixmap(image_priv->pixmap->scaled(*w, *h,Qt::IgnoreAspectRatio,Qt::FastTransformation)); + delete (image_priv->pixmap); + image_priv->pixmap = scaled; + } + } + } + + *w=image_priv->pixmap->width(); + *h=image_priv->pixmap->height(); +// dbg(lvl_debug, "Got (%d,%d)\n", *w,*h); + if (hot) { + hot->x=*w/2; + hot->y=*h/2; + } + + return image_priv; +} + +static void +draw_lines(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int count) +{ + int i; + QPolygon polygon; +// dbg(lvl_debug,"enter gr=%p, gc=%p, (%d, %d)\n", gr, gc, p->x, p->y); + if(gr->painter == NULL) + return; + + for (i = 0 ; i < count ; i++) + polygon.putPoints(i, 1, p[i].x, p[i].y); + gr->painter->setPen(*gc->pen); + gr->painter->drawPolyline(polygon); + +} + +static void +draw_polygon(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int count) +{ + int i; + QPolygon polygon; +// dbg(lvl_debug,"enter gr=%p, gc=%p, (%d, %d)\n", gr, gc, p->x, p->y); + if(gr->painter == NULL) + return; + + for (i = 0 ; i < count ; i++) + polygon.putPoints(i, 1, p[i].x, p[i].y); + gr->painter->setPen(*gc->pen); + gr->painter->setBrush(*gc->brush); + /* if the polygon is transparent, we need to clear it first */ + if(!gc->brush->isOpaque()) + { + QPainter::CompositionMode mode = gr->painter->compositionMode(); + gr->painter->setCompositionMode(QPainter::CompositionMode_Clear); + gr->painter->drawPolygon(polygon); + gr->painter->setCompositionMode(mode); + } + gr->painter->drawPolygon(polygon); +} + +static void +draw_rectangle(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int w, int h) +{ +// dbg(lvl_debug,"gr=%p gc=%p %d,%d,%d,%d\n", gr, gc, p->x, p->y, w, h); + if(gr->painter == NULL) + return; + /* if the rectangle is transparent, we need to clear it first */ + if(!gc->brush->isOpaque()) + { + QPainter::CompositionMode mode = gr->painter->compositionMode(); + gr->painter->setCompositionMode(QPainter::CompositionMode_Clear); + gr->painter->fillRect(p->x,p->y, w, h, *gc->brush); + gr->painter->setCompositionMode(mode); + } + gr->painter->fillRect(p->x,p->y, w, h, *gc->brush); +} + +static void +draw_circle(struct graphics_priv *gr, struct graphics_gc_priv *gc, struct point *p, int r) +{ +// dbg(lvl_debug,"enter gr=%p, gc=%p, (%d,%d) r=%d\n", gr, gc, p->x, p->y, r); + if(gr->painter == NULL) + return; + gr->painter->setPen(*gc->pen); + gr->painter->drawArc(p->x-r/2, p->y-r/2, r, r, 0, 360*16); +} + + +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) +{ +// dbg(lvl_debug,"enter gc=%p, fg=%p, bg=%p pos(%d,%d) %s\n", gr, fg, bg, p->x, p->y, text); + QPainter *painter=gr->painter; + if(painter == NULL) + return; +#ifdef QT_QPAINTER_USE_FREETYPE + struct font_freetype_text *t; + struct font_freetype_glyph *g, **gp; + struct color transparent = {0x0000, 0x0000, 0x0000, 0x0000}; + struct color fgc; + struct color bgc; + QColor temp; + + int i,x,y; + + if (! font) + return; + /* extract colors */ + fgc.r = fg->pen->color().red() << 8; + fgc.g = fg->pen->color().green() << 8; + fgc.b = fg->pen->color().blue() << 8; + fgc.a = fg->pen->color().alpha() << 8; + if(bg != NULL) + { + bgc.r = bg->pen->color().red() << 8; + bgc.g = bg->pen->color().green() << 8; + bgc.b = bg->pen->color().blue() << 8; + bgc.a = bg->pen->color().alpha() << 8; + } + else + { + bgc = transparent; + } + + t=gr->freetype_methods.text_new(text, (struct font_freetype_font *)font, dx, dy); + x=p->x << 6; + y=p->y << 6; + gp=t->glyph; + i=t->glyph_count; + if (bg) { + while (i-- > 0) { + g=*gp++; + if (g->w && g->h) { + unsigned char *data; + QImage img(g->w+2, g->h+2, QImage::Format_ARGB32_Premultiplied); + data=img.bits(); + gr->freetype_methods.get_shadow(g,(unsigned char *)data,img.bytesPerLine(),&bgc,&transparent); + + painter->drawImage(((x+g->x)>>6)-1, ((y+g->y)>>6)-1, img); + } + x+=g->dx; + y+=g->dy; + } + } + x=p->x << 6; + y=p->y << 6; + gp=t->glyph; + i=t->glyph_count; + while (i-- > 0) { + g=*gp++; + if (g->w && g->h) { + unsigned char *data; + QImage img(g->w, g->h, QImage::Format_ARGB32_Premultiplied); + data=img.bits(); + gr->freetype_methods.get_glyph(g,(unsigned char *)data,img.bytesPerLine(),&fgc,&bgc,&transparent); + painter->drawImage((x+g->x)>>6, (y+g->y)>>6, img); + } + x+=g->dx; + y+=g->dy; + } + gr->freetype_methods.text_destroy(t); +#else + QString tmp=QString::fromUtf8(text); + QMatrix sav=gr->painter->worldMatrix(); + QMatrix m(dx/65535.0,dy/65535.0,-dy/65535.0,dx/65535.0,p->x,p->y); + painter->setWorldMatrix(m,TRUE); + painter->setPen(*fg->pen); + painter->setFont(*font->font); + painter->drawText(0, 0, tmp); + painter->setWorldMatrix(sav); +#endif +} + +static void +draw_image(struct graphics_priv *gr, struct graphics_gc_priv *fg, struct point *p, struct graphics_image_priv *img) +{ +// dbg(lvl_debug,"enter\n"); + if(gr->painter != NULL) + gr->painter->drawPixmap(p->x, p->y, *img->pixmap); + else + dbg(lvl_debug, "Try to draw image, but no painter\n"); +} + +static void draw_drag(struct graphics_priv *gr, struct point *p) +{ + if(p != NULL) + { +// dbg(lvl_debug,"enter %p (%d,%d)\n", gr, p->x, p->y); + gr->widget->move(p->x, p->y); + } + else + { +// dbg(lvl_debug,"enter %p (NULL)\n", gr); + } +} + +static void +background_gc(struct graphics_priv *gr, struct graphics_gc_priv *gc) +{ +// dbg(lvl_debug,"register context %p on %p\n", gc, gr); + gr->background_graphics_gc_priv = gc; +} + +static void +draw_mode(struct graphics_priv *gr, enum draw_mode_num mode) +{ + switch (mode) + { + case draw_mode_begin: + dbg(lvl_debug,"Begin drawing on context %p (use == %d)\n", gr, gr->use_count); + gr->use_count ++; + if(gr->painter == NULL) + gr->painter = new QPainter(gr->pixmap); + else + dbg(lvl_debug, "drawing on %p already active\n", gr); + break; + case draw_mode_end: + dbg(lvl_debug,"End drawing on context %p (use == %d)\n", gr, gr->use_count); + gr->use_count --; + if(gr->use_count > 0) + { + dbg(lvl_debug, "drawing on %p still in use\n", gr); + } + else if(gr->painter != NULL) + { + gr->painter->end(); + delete(gr->painter); + gr->painter = NULL; + /* call repaint on widget */ + gr->widget->repaint(); + } + else + dbg(lvl_debug, "Context %p not active!\n", gr) + + break; + default: + dbg(lvl_debug,"Unknown drawing %d on context %p\n", mode, gr); + break; + } +} + +static struct graphics_priv * overlay_new(struct graphics_priv *gr, struct graphics_methods *meth, struct point *p, int w, int h, int wraparound); + +void +resize_callback(int w, int h) +{ +// dbg(lvl_debug,"enter (%d, %d)\n", w, h); + callback_list_call_attr_2(callbacks, attr_resize, + GINT_TO_POINTER(w), GINT_TO_POINTER(h)); +} + +static int +graphics_qt5_fullscreen(struct window *w, int on) +{ + struct graphics_priv * gr; +// dbg(lvl_debug,"enter\n"); + gr = (struct graphics_priv *) w->priv; + if(on) + gr->widget->setWindowState(Qt::WindowFullScreen); + else + gr->widget->setWindowState(Qt::WindowMaximized); + return 1; +} + +#ifdef SAILFISH_OS +static void +keep_display_on(struct graphics_priv * priv) +{ +// dbg(lvl_debug,"enter\n"); + QDBusConnection system = QDBusConnection::connectToBus(QDBusConnection::SystemBus, "system"); + QDBusInterface interface("com.nokia.mce", "/com/nokia/mce/request", "com.nokia.mce.request", system); + + interface.call(QLatin1String("req_display_blanking_pause")); +} +#endif + + +static void +graphics_qt5_disable_suspend(struct window *w) +{ +// dbg(lvl_debug,"enter\n"); +#ifdef SAILFISH_OS + struct graphics_priv * gr; + gr = (struct graphics_priv *) w->priv; + keep_display_on(gr); + /* to keep display on, d-bus trigger must be called at least once per second. + * to cope with fuzz, trigger it once per 30 seconds */ + gr->display_on_cb = callback_new_1(callback_cast(keep_display_on), gr); + gr->display_on_ev = event_add_timeout(30000, 1, gr->display_on_cb); +#endif +} + +static void * +get_data(struct graphics_priv *this_priv, char const *type) +{ +// dbg(lvl_debug,"enter: %s\n", type); + if (strcmp(type, "window") == 0) { + struct window *win; +// dbg(lvl_debug,"window detected\n"); + win = g_new0(struct window, 1); + win->priv = this_priv; + win->fullscreen = graphics_qt5_fullscreen; + win->disable_suspend = graphics_qt5_disable_suspend; + resize_callback(this_priv->widget->width(),this_priv->widget->height()); + return win; + } + return NULL; +} + +static void image_free(struct graphics_priv *gr, struct graphics_image_priv *priv) +{ +// dbg(lvl_debug,"enter\n"); + delete(priv->pixmap); + g_free(priv); +} + +static void get_text_bbox(struct graphics_priv *gr, struct graphics_font_priv *font, char *text, int dx, int dy, struct point *ret, int estimate) +{ +// dbg(lvl_debug,"enter %s %d %d\n", text, dx, dy); + QPainter *painter=gr->painter; + QString tmp=QString::fromUtf8(text); + if(gr->painter != NULL) + { + gr->painter->setFont(*font->font); + QRect r=painter->boundingRect(0,0,gr->pixmap->width(),gr->pixmap->height(),0,tmp); +// dbg (lvl_debug, "Text bbox: %d %d (%d,%d),(%d,%d)\n",dx, dy, r.left(), r.top(), r.right(), r.bottom()); + /* low left */ + ret[0].x = r.left(); + ret[0].y = r.bottom(); + /* top left */ + ret[1].x = r.left(); + ret[1].y = r.top(); + /* top right */ + ret[2].x = r.right(); + ret[2].y = r.top(); + /* low right */ + ret[3].x = r.right(); + ret[3].y = r.bottom(); + } +} + +static void overlay_disable(struct graphics_priv *gr, int disable) +{ + GHashTableIter iter; + struct graphics_priv * key, * value; +// dbg(lvl_debug,"enter gr=%p, %d\n", gr, disable); + + g_hash_table_iter_init (&iter, gr->overlays); + while (g_hash_table_iter_next (&iter, (void **)&key, (void **)&value)) + { + /* disable or enable all overlays of this pane */ + value->widget->setVisible(!disable); + } +} + +static void overlay_resize(struct graphics_priv *gr, struct point *p, int w, int h, int wraparound) +{ +// dbg(lvl_debug,"enter\n"); + gr->widget->move(p->x, p->y); + gr->widget->resize(w, h); + if(gr->painter != NULL) + { + delete(gr->painter); + } + delete(gr->pixmap); + gr->pixmap = new QPixmap(gr->widget->size()); + if(gr->painter != NULL) + gr->painter = new QPainter (gr->pixmap); +} + +static struct graphics_methods graphics_methods = { + graphics_destroy, + draw_mode, + draw_lines, + draw_polygon, + draw_rectangle, + draw_circle, + draw_text, + draw_image, + NULL, + draw_drag, + font_new, + gc_new, + background_gc, + overlay_new, + image_new, + get_data, + image_free, + get_text_bbox, + overlay_disable, + overlay_resize, +}; + +/* create new graphics context on given context */ +static struct graphics_priv * +overlay_new(struct graphics_priv *gr, struct graphics_methods *meth, struct point *p, int w, int h, int wraparound) +{ + struct graphics_priv * graphics_priv = NULL; + graphics_priv = g_new0(struct graphics_priv, 1); + *meth=graphics_methods; +#ifdef QT_QPAINTER_USE_FREETYPE + if (gr->font_freetype_new) { + graphics_priv->font_freetype_new=gr->font_freetype_new; + gr->font_freetype_new(&graphics_priv->freetype_methods); + meth->font_new=(struct graphics_font_priv *(*)(struct graphics_priv *, struct graphics_font_methods *, char *, int, int))graphics_priv->freetype_methods.font_new; + meth->get_text_bbox=(void (*)(struct graphics_priv*, struct graphics_font_priv*, char*, int, int, struct point*, int))graphics_priv->freetype_methods.get_text_bbox; + } +#endif + graphics_priv->widget = new QNavitWidget(graphics_priv, gr->widget, Qt::Widget); + graphics_priv->widget->move(p->x, p->y); + graphics_priv->widget->resize(w, h); + graphics_priv->widget->setVisible(true); + graphics_priv->pixmap = new QPixmap(graphics_priv->widget->size()); + graphics_priv->painter = NULL; + graphics_priv->use_count = 0; + graphics_priv->parent = gr; + graphics_priv->overlays=g_hash_table_new(NULL, NULL); + graphics_priv->root = false; + graphics_priv->argc = 0; + graphics_priv->argv[0] = NULL; + /* register on parent */ + g_hash_table_insert(gr->overlays, graphics_priv, graphics_priv); +// dbg(lvl_debug,"New overlay: %p\n", graphics_priv); + + return graphics_priv; +} + +/* create application and initial graphics context */ +static struct graphics_priv * +graphics_qt5_new(struct navit *nav, struct graphics_methods *meth, struct attr **attrs, struct callback_list *cbl) +{ + struct graphics_priv * graphics_priv = NULL; + struct attr * event_loop_system = NULL; + struct attr * platform = NULL; + struct attr * fullscreen = NULL; + //dbg(lvl_debug,"enter\n"); + + /*register graphic methods by copying in our predefined ones */ + *meth=graphics_methods; + + /* get event loop from config and request event loop*/ + event_loop_system = attr_search(attrs, NULL, attr_event_loop_system); + if (event_loop_system && event_loop_system->u.str) { + //dbg(lvl_debug, "event_system is %s\n", event_loop_system->u.str); + if (!event_request_system(event_loop_system->u.str, "graphics_qt5")) + return NULL; + } else { + /* no event system requested by config. Default to our own */ + if (!event_request_system("qt5", "graphics_qt5")) + return NULL; + } + +#ifdef QT_QPAINTER_USE_FREETYPE + struct font_priv * (*font_freetype_new)(void *meth); + /* get font plugin if present */ + font_freetype_new=(struct font_priv *(*)(void *))plugin_get_category_font("freetype"); + if (!font_freetype_new) { + dbg(lvl_error,"no freetype\n"); + return NULL; + } +#endif + + /* create root graphics layer */ + graphics_priv = g_new0(struct graphics_priv, 1); + /* Prepare argc and argv to call Qt application*/ + graphics_priv->root = true; + graphics_priv->argc = 0; + graphics_priv->argv[graphics_priv->argc] = g_strdup("navit"); + graphics_priv->argc ++; + /* Get qt platform from config */ + if ((platform=attr_search(attrs, NULL, attr_qt5_platform))) + { + graphics_priv->argv[graphics_priv->argc] = g_strdup("-platform"); + graphics_priv->argc ++; + graphics_priv->argv[graphics_priv->argc] = g_strdup(platform->u.str); + graphics_priv->argc ++; + } + /* create surrounding application */ + navit_app = new QApplication(graphics_priv->argc, graphics_priv->argv); + +#ifdef QT_QPAINTER_USE_FREETYPE + graphics_priv->font_freetype_new=font_freetype_new; + font_freetype_new(&graphics_priv->freetype_methods); + meth->font_new=(struct graphics_font_priv *(*)(struct graphics_priv *, struct graphics_font_methods *, char *, int, int))graphics_priv->freetype_methods.font_new; + meth->get_text_bbox=(void (*)(struct graphics_priv*, struct graphics_font_priv*, char*, int, int, struct point*, int))graphics_priv->freetype_methods.get_text_bbox; +#endif + callbacks = cbl; + graphics_priv->pixmap = NULL; + graphics_priv->use_count = 0; + graphics_priv->painter = NULL; + graphics_priv->parent = NULL; + graphics_priv->overlays=g_hash_table_new(NULL, NULL); + + graphics_priv->widget = new QNavitWidget(graphics_priv,NULL,Qt::Window); + if ((fullscreen=attr_search(attrs, NULL, attr_fullscreen)) && (fullscreen->u.num)) { + /* show this maximized */ + graphics_priv->widget->setWindowState(Qt::WindowFullScreen); + } + else + { + /* not maximized. Check what size to use then */ + struct attr * w = NULL; + struct attr * h = NULL; + /* default to desktop size if nothing else is given */ + QRect geomet = navit_app->desktop()->screenGeometry(graphics_priv->widget); + /* check for height */ + if ((h = attr_search(attrs, NULL, attr_h)) && (h->u.num > 100)) + geomet.setHeight(h->u.num); + /* check for width */ + if ((w = attr_search(attrs, NULL, attr_w)) && (w->u.num > 100)) + geomet.setWidth(w->u.num); + graphics_priv->widget->resize(geomet.width(), geomet.height()); + //graphics_priv->widget->setFixedSize(geomet.width(), geomet.height()); + } + /* generate initial pixmap same size as window */ + if(graphics_priv->pixmap == NULL) + graphics_priv->pixmap = new QPixmap(graphics_priv->widget->size()); + + /* tell Navit our geometry */ + resize_callback(graphics_priv->widget->width(),graphics_priv->widget->height()); + + /* show our window */ + graphics_priv->widget->show(); + return graphics_priv; +} + +void +plugin_init(void) +{ +// dbg(lvl_debug,"enter\n"); + plugin_register_category_graphics("qt5", graphics_qt5_new); + qt5_event_init(); +} diff --git a/navit/graphics/qt5/graphics_qt5.h b/navit/graphics/qt5/graphics_qt5.h new file mode 100644 index 000000000..2f19390c4 --- /dev/null +++ b/navit/graphics/qt5/graphics_qt5.h @@ -0,0 +1,63 @@ +#ifndef __graphics_qt_h +#define __graphics_qt_h +#include <glib.h> +#include <QApplication> +#include <QPixmap> +#include <QPainter> +#include <QPen> +#include <QBrush> +#include "QNavitWidget.h" + +#ifndef QT_QPAINTER_USE_FREETYPE +#define QT_QPAINTER_USE_FREETYPE 1 +#endif + +#ifndef SAILFISH_OS +#define SAILFISH_OS 1 +#endif + + +#ifdef QT_QPAINTER_USE_FREETYPE +#include "navit/font/freetype/font_freetype.h" +#endif + +struct graphics_gc_priv; +struct graphics_priv; + +struct graphics_priv { + QNavitWidget * widget; + QPixmap * pixmap; + QPainter * painter; + int use_count; + struct graphics_gc_priv * background_graphics_gc_priv; +#ifdef QT_QPAINTER_USE_FREETYPE + struct font_priv * (*font_freetype_new)(void *meth); + struct font_freetype_methods freetype_methods; +#endif +#ifdef SAILFISH_OS + struct callback *display_on_cb; + struct event_timeout *display_on_ev; +#endif + GHashTable *overlays; + struct graphics_priv * parent; + bool root; + int argc; + char * argv[4]; +}; + +struct graphics_gc_priv { + struct graphics_priv * graphics_priv; + QPen * pen; + QBrush * brush; +}; +/* central exported application info */ +extern QApplication * navit_app; + +/* navit callback list */ +extern struct callback_list* callbacks; + +void +resize_callback(int w, int h); + +#endif + diff --git a/navit/vehicle/qt5/CMakeLists.txt b/navit/vehicle/qt5/CMakeLists.txt new file mode 100644 index 000000000..411d9d6ee --- /dev/null +++ b/navit/vehicle/qt5/CMakeLists.txt @@ -0,0 +1,6 @@ +# Find includes in corresponding build directories +set(CMAKE_INCLUDE_CURRENT_DIR ON) +# Instruct CMake to run moc automatically when needed. +set(CMAKE_AUTOMOC ON) + +module_add_library(vehicle_qt5 vehicle_qt5.cpp) diff --git a/navit/vehicle/qt5/vehicle_qt5.cpp b/navit/vehicle/qt5/vehicle_qt5.cpp new file mode 100644 index 000000000..d6d33d7ab --- /dev/null +++ b/navit/vehicle/qt5/vehicle_qt5.cpp @@ -0,0 +1,300 @@ +/** @file vehicle_null.c + * @brief null uses dbus signals + * + * Navit, a modular navigation system. + * Copyright (C) 2005-2008 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. + * + * @Author Tim Niemeyer <reddog@mastersword.de> + * @date 2008-2009 + */ + +#include "vehicle_qt5.h" +#include "vehicle_qt5.moc" +#include <QDateTime> +QNavitGeoReceiver::QNavitGeoReceiver (QObject * parent, struct vehicle_priv * c):QObject(parent) +{ + priv = c; + if(priv->source != NULL) + { + connect(priv->source, SIGNAL(positionUpdated(QGeoPositionInfo)),this, SLOT(positionUpdated(QGeoPositionInfo))); + } + if(priv->satellites != NULL) + { + connect(priv->satellites, SIGNAL(satellitesInUseUpdated(const QList<QGeoSatelliteInfo> &)), this, SLOT(satellitesInUseUpdated(const QList<QGeoSatelliteInfo> &))); + connect(priv->satellites, SIGNAL(satellitesInViewUpdated(const QList<QGeoSatelliteInfo> &)), this, SLOT(satellitesInViewUpdated(const QList<QGeoSatelliteInfo> &))); + } +} +void QNavitGeoReceiver::satellitesInUseUpdated(const QList<QGeoSatelliteInfo> & sats) +{ + dbg(lvl_debug, "Sats in use: %d\n", sats.count()); + priv->sats_used = sats.count(); + callback_list_call_attr_0(priv->cbl, attr_position_sats_used); +} + +void QNavitGeoReceiver::satellitesInViewUpdated(const QList<QGeoSatelliteInfo> & sats) +{ + dbg(lvl_debug, "Sats in view: %d\n", sats.count()); + priv->sats = sats.count(); + callback_list_call_attr_0(priv->cbl, attr_position_qual); +} + +void QNavitGeoReceiver::positionUpdated(const QGeoPositionInfo &info) +{ + + /* ignore stale view */ + if(info.coordinate().isValid()) + { + if(info.timestamp().toUTC().secsTo(QDateTime::currentDateTimeUtc()) > 20) + { + dbg(lvl_debug,"Ignoring old FIX\n"); + return; + } + } + + if(info.hasAttribute(QGeoPositionInfo::HorizontalAccuracy)) + { + dbg(lvl_debug,"Horizontal acc (%f)\n",info.attribute(QGeoPositionInfo::HorizontalAccuracy)); + priv->radius = info.attribute(QGeoPositionInfo::HorizontalAccuracy); + callback_list_call_attr_0(priv->cbl, attr_position_radius); + } + if(info.hasAttribute(QGeoPositionInfo::GroundSpeed)) + { + dbg(lvl_debug,"Got ground speed (%f)\n",info.attribute(QGeoPositionInfo::GroundSpeed)); + priv->speed = info.attribute(QGeoPositionInfo::GroundSpeed) * 3.6; + callback_list_call_attr_0(priv->cbl, attr_position_speed); + } + if(info.hasAttribute(QGeoPositionInfo::Direction)) + { + dbg(lvl_debug,"Direction (%f)\n",info.attribute(QGeoPositionInfo::Direction)); + priv->direction = info.attribute(QGeoPositionInfo::Direction); + callback_list_call_attr_0(priv->cbl, attr_position_direction); + } + + switch(info.coordinate().type()) + { + case QGeoCoordinate::Coordinate3D: + priv->fix_type = 2; + break; + case QGeoCoordinate::Coordinate2D: + priv->fix_type = 1; + break; + case QGeoCoordinate::InvalidCoordinate: + priv->fix_type = 0; + break; + } + + + if(info.coordinate().isValid()) + { + dbg(lvl_debug, "Got valid coordinate (lat %f, lon %f)\n", info.coordinate().latitude(), info.coordinate().longitude()); + priv->geo.lat = info.coordinate().latitude(); + priv->geo.lng = info.coordinate().longitude(); + priv->have_coords = 1; + if(info.coordinate().type() == QGeoCoordinate::Coordinate3D) + { + dbg(lvl_debug,"Got valid altitude (alt %f)\n", info.coordinate().altitude()); + priv->height = info.coordinate().altitude(); + } + //dbg(lvl_debug, "Time %s\n", info.timestamp().toUTC().toString().toLatin1().data()); + priv->fix_time = info.timestamp().toUTC().toTime_t(); + callback_list_call_attr_0(priv->cbl, attr_position_coord_geo); + } + else + { + dbg(lvl_debug, "Got invalid coordinate\n"); + priv->have_coords = 0; + callback_list_call_attr_0(priv->cbl, attr_position_coord_geo); + } +} + + +/** + * @brief Free the null_vehicle + * + * @param priv + * @returns nothing + */ +static void +vehicle_qt5_destroy(struct vehicle_priv *priv) +{ + dbg(lvl_debug,"enter\n"); + if(priv->receiver != NULL) + delete priv->receiver; + if(priv->source != NULL) + delete priv->source; + g_free(priv); +} + +/** + * @brief Provide the outside with information + * + * @param priv + * @param type TODO: What can this be? + * @param attr + * @returns true/false + */ +static int +vehicle_qt5_position_attr_get(struct vehicle_priv *priv, + enum attr_type type, struct attr *attr) +{ + struct attr * active=NULL; + dbg(lvl_debug,"enter %s\n",attr_to_name(type)); + switch (type) { + case attr_position_valid: + attr->u.num = priv->have_coords; + break; + case attr_position_fix_type: + attr->u.num = priv->fix_type; + break; + case attr_position_height: + attr->u.numd = &priv->height; + break; + case attr_position_speed: + attr->u.numd = &priv->speed; + break; + case attr_position_direction: + attr->u.numd = &priv->direction; + break; + case attr_position_radius: + attr->u.numd = &priv->radius; + break; + case attr_position_qual: + attr->u.num = priv->sats; + break; + case attr_position_sats_used: + attr->u.num = priv->sats_used; + break; + case attr_position_coord_geo: + attr->u.coord_geo = &priv->geo; + if (!priv->have_coords) + return 0; + break; + case attr_position_time_iso8601: + if (priv->fix_time) { + struct tm tm; + if (gmtime_r(&priv->fix_time, &tm)) { + strftime(priv->fixiso8601, sizeof(priv->fixiso8601), + "%Y-%m-%dT%TZ", &tm); + attr->u.str=priv->fixiso8601; + } + else { + priv->fix_time = 0; + return 0; + } + //dbg(lvl_debug,"Fix Time: %s\n", priv->fixiso8601); + } + else { + //dbg(lvl_debug,"Fix Time: 0\n"); + return 0; + } + break; + + case attr_active: + active = attr_search(priv->attrs,NULL,attr_active); + if(active != NULL) { + attr->u.num=active->u.num; + return 1; + } else + return 0; + break; + + default: + return 0; + } + dbg(lvl_debug,"ok\n"); + attr->type = type; + return 1; +} + +static int +vehicle_qt5_set_attr(struct vehicle_priv *priv, struct attr *attr) +{ + switch (attr->type) { + case attr_position_speed: + priv->speed=*attr->u.numd; + break; + case attr_position_direction: + priv->direction=*attr->u.numd; + break; + case attr_position_coord_geo: + priv->geo=*attr->u.coord_geo; + priv->have_coords=1; + break; + default: + break; + } + callback_list_call_attr_0(priv->cbl, attr->type); + return 1; +} + + +struct vehicle_methods vehicle_null_methods = { + vehicle_qt5_destroy, + vehicle_qt5_position_attr_get, + vehicle_qt5_set_attr, +}; + +/** + * @brief Create null_vehicle + * + * @param meth + * @param cbl + * @param attrs + * @returns vehicle_priv + */ +static struct vehicle_priv * +vehicle_qt5_new_qt5(struct vehicle_methods *meth, + struct callback_list *cbl, + struct attr **attrs) +{ + struct vehicle_priv *ret; + + dbg(lvl_debug, "enter\n"); + ret = g_new0(struct vehicle_priv, 1); + ret->cbl = cbl; + *meth = vehicle_null_methods; + ret->attrs = attrs; + ret->source = QGeoPositionInfoSource::createDefaultSource(NULL); + ret->satellites = QGeoSatelliteInfoSource::createDefaultSource(NULL); + if(ret->source == NULL) + { + dbg(lvl_error, "Got NO QGeoPositionInfoSource\n"); + } + else + { + dbg(lvl_debug, "Using %s\n", ret->source->sourceName().toLatin1().data()); + ret->receiver = new QNavitGeoReceiver(NULL,ret); + ret->satellites->setUpdateInterval(1000); + ret->satellites->startUpdates(); + ret->source->setUpdateInterval(500); + ret->source->startUpdates(); + } + dbg(lvl_debug, "return\n"); + return ret; +} + +/** + * @brief register vehicle_null + * + * @returns nothing + */ +void +plugin_init(void) +{ + dbg(lvl_debug, "enter\n"); + plugin_register_category_vehicle("qt5", vehicle_qt5_new_qt5); +} diff --git a/navit/vehicle/qt5/vehicle_qt5.h b/navit/vehicle/qt5/vehicle_qt5.h new file mode 100644 index 000000000..c38c45284 --- /dev/null +++ b/navit/vehicle/qt5/vehicle_qt5.h @@ -0,0 +1,58 @@ +#ifndef __vehicle_qt5_h +#define __vehicle_qt5_h + +#include <config.h> +#include <string.h> +#include <glib.h> +#include <math.h> +#include <time.h> +#include "debug.h" +#include "plugin.h" +#include "coord.h" +#include "item.h" +#include "vehicle.h" + +#include <QObject> +#include <QStringList> +#include <QGeoPositionInfoSource> +#include <QGeoSatelliteInfoSource> + +#include "callback.h" + +#include <QObject> + +class QNavitGeoReceiver; +struct vehicle_priv { + struct callback_list *cbl; + struct coord_geo geo; + double speed; + double direction; + double height; + double radius; + int fix_type; + time_t fix_time; + char fixiso8601[128]; + int sats; + int sats_used; + int have_coords; + struct attr ** attrs; + + QGeoPositionInfoSource *source; + QGeoSatelliteInfoSource *satellites; + QNavitGeoReceiver * receiver; +}; + +class QNavitGeoReceiver : public QObject +{ + Q_OBJECT +public: + QNavitGeoReceiver (QObject * parent, struct vehicle_priv * c); +public slots: + void positionUpdated(const QGeoPositionInfo &info); + void satellitesInUseUpdated(const QList<QGeoSatelliteInfo> & satellites); + void satellitesInViewUpdated(const QList<QGeoSatelliteInfo> & satellites); + +private: + struct vehicle_priv * priv; +}; +#endif
\ No newline at end of file |