From fa8167b94d13e94a6cb953e7f549a89f155f77c6 Mon Sep 17 00:00:00 2001 From: inmarket <andrewh@inmarket.com.au> Date: Wed, 21 Jan 2015 17:26:24 +1000 Subject: Big file rename to reduce problems with brain-dead IDE's that don't handle project file hierarchies well. Naming is more consistent with the new scheme. May affect some third party drivers (header file renames). --- demos/tools/touch_driver_test/main.c | 4 +- demos/tools/touch_raw_readings/main.c | 4 +- docs/src/containers.dox | 2 +- docs/src/widgets.dox | 2 +- docs/src/windows.dox | 2 +- drivers/gadc/AT91SAM7/gadc_lld.c | 2 +- drivers/gaudio/Win32/gaudio_play_lld.c | 2 +- drivers/gaudio/Win32/gaudio_record_lld.c | 2 +- drivers/gaudio/gadc/gaudio_record_lld.c | 2 +- drivers/gaudio/pwm/gaudio_play_lld.c | 2 +- drivers/gaudio/vs1053/gaudio_play_lld.c | 2 +- drivers/gdisp/ED060SC4/gdisp_lld_ED060SC4.c | 2 +- drivers/gdisp/HX8347D/gdisp_lld_HX8347D.c | 2 +- drivers/gdisp/ILI9320/gdisp_lld_ILI9320.c | 2 +- drivers/gdisp/ILI9325/gdisp_lld_ILI9325.c | 2 +- drivers/gdisp/ILI9341/gdisp_lld_ILI9341.c | 2 +- drivers/gdisp/ILI93xx/gdisp_lld_ILI93xx.c | 2 +- drivers/gdisp/ILI9481/gdisp_lld_ILI9481.c | 2 +- drivers/gdisp/LGDP4532/gdisp_lld_LGDP4532.c | 2 +- .../gdisp/Nokia6610GE12/gdisp_lld_Nokia6610GE12.c | 2 +- .../gdisp/Nokia6610GE8/gdisp_lld_Nokia6610GE8.c | 2 +- drivers/gdisp/PCD8544/gdisp_lld_PCD8544.c | 2 +- drivers/gdisp/PCF8812/gdisp_lld_PCF8812.c | 2 +- drivers/gdisp/R61505U/gdisp_lld_R61505U.c | 2 +- drivers/gdisp/RA8875/gdisp_lld_RA8875.c | 2 +- drivers/gdisp/S6D1121/gdisp_lld_S6D1121.c | 2 +- drivers/gdisp/SPFD54124B/gdisp_lld_SPFD54124B.c | 2 +- drivers/gdisp/SSD1289/gdisp_lld_SSD1289.c | 2 +- drivers/gdisp/SSD1306/gdisp_lld_SSD1306.c | 2 +- drivers/gdisp/SSD1963/gdisp_lld_SSD1963.c | 2 +- drivers/gdisp/SSD2119/gdisp_lld_SSD2119.c | 2 +- drivers/gdisp/ST7565/gdisp_lld_ST7565.c | 2 +- .../gdisp_lld_STM32F429iDiscovery.c | 2 +- drivers/gdisp/TestStub/gdisp_lld_TestStub.c | 2 +- drivers/gdisp/framebuffer/gdisp_lld_framebuffer.c | 2 +- drivers/ginput/dial/GADC/ginput_lld_dial.c | 2 +- drivers/ginput/toggle/Pal/ginput_lld_toggle.c | 2 +- drivers/ginput/touch/ADS7843/gmouse_lld_ADS7843.c | 2 +- drivers/ginput/touch/FT5x06/gmouse_lld_FT5x06.c | 2 +- .../ginput/touch/MAX11802/gmouse_lld_MAX11802.c | 2 +- drivers/ginput/touch/MCU/gmouse_lld_MCU.c | 2 +- .../ginput/touch/STMPE811/gmouse_lld_STMPE811.c | 2 +- drivers/multiple/Win32/gdisp_lld_Win32.c | 10 +- drivers/multiple/X/gdisp_lld_X.c | 8 +- drivers/multiple/uGFXnet/gdisp_lld_uGFXnet.c | 4 +- gfx.h | 72 +- gfx.mk | 24 +- src/gadc/driver.h | 153 - src/gadc/gadc.c | 353 ++ src/gadc/gadc.h | 252 ++ src/gadc/gadc.mk | 1 + src/gadc/gadc_driver.h | 153 + src/gadc/gadc_gadc.c | 362 --- src/gadc/gadc_options.h | 42 + src/gadc/gadc_rules.h | 41 + src/gadc/sys_defs.h | 252 -- src/gadc/sys_make.mk | 1 - src/gadc/sys_options.h | 42 - src/gadc/sys_rules.h | 41 - src/gaudio/driver_play.h | 126 - src/gaudio/driver_record.h | 108 - src/gaudio/gaudio.c | 275 ++ src/gaudio/gaudio.h | 297 ++ src/gaudio/gaudio.mk | 1 + src/gaudio/gaudio_driver_play.h | 126 + src/gaudio/gaudio_driver_record.h | 108 + src/gaudio/gaudio_gaudio.c | 283 -- src/gaudio/gaudio_options.h | 44 + src/gaudio/gaudio_rules.h | 56 + src/gaudio/sys_defs.h | 297 -- src/gaudio/sys_make.mk | 1 - src/gaudio/sys_options.h | 44 - src/gaudio/sys_rules.h | 56 - src/gdisp/driver.h | 1070 ------- src/gdisp/gdisp.c | 3370 +++++++++++++++++++ src/gdisp/gdisp.h | 1124 +++++++ src/gdisp/gdisp.mk | 14 + src/gdisp/gdisp_driver.h | 1070 +++++++ src/gdisp/gdisp_fonts.c | 9 - src/gdisp/gdisp_gdisp.c | 3378 -------------------- src/gdisp/gdisp_image.c | 8 - src/gdisp/gdisp_image_bmp.c | 33 - src/gdisp/gdisp_image_gif.c | 8 - src/gdisp/gdisp_image_jpg.c | 5 - src/gdisp/gdisp_image_native.c | 5 - src/gdisp/gdisp_image_png.c | 5 - src/gdisp/gdisp_options.h | 445 +++ src/gdisp/gdisp_pixmap.c | 4 +- src/gdisp/gdisp_rules.h | 89 + src/gdisp/sys_defs.h | 1124 ------- src/gdisp/sys_make.mk | 14 - src/gdisp/sys_options.h | 382 --- src/gdisp/sys_rules.h | 89 - src/gdriver/gdriver.c | 148 + src/gdriver/gdriver.h | 159 + src/gdriver/gdriver.mk | 1 + src/gdriver/gdriver_gdriver.c | 148 - src/gdriver/gdriver_options.h | 32 + src/gdriver/gdriver_rules.h | 23 + src/gdriver/sys_defs.h | 159 - src/gdriver/sys_make.mk | 1 - src/gdriver/sys_options.h | 32 - src/gdriver/sys_rules.h | 23 - src/gevent/gevent.c | 238 ++ src/gevent/gevent.h | 246 ++ src/gevent/gevent.mk | 1 + src/gevent/gevent_gevent.c | 246 -- src/gevent/gevent_options.h | 55 + src/gevent/gevent_rules.h | 23 + src/gevent/sys_defs.h | 246 -- src/gevent/sys_make.mk | 1 - src/gevent/sys_options.h | 55 - src/gevent/sys_rules.h | 23 - src/gfile/gfile.c | 401 +++ src/gfile/gfile.h | 470 +++ src/gfile/gfile.mk | 18 + src/gfile/gfile_fatfs_diskio_chibios.c | 6 - src/gfile/gfile_fatfs_wrapper.c | 6 - src/gfile/gfile_gfile.c | 407 --- src/gfile/gfile_options.h | 207 ++ src/gfile/gfile_petitfs_diskio_chibios.c | 6 - src/gfile/gfile_petitfs_wrapper.c | 6 - src/gfile/gfile_rules.h | 26 + src/gfile/sys_defs.h | 470 --- src/gfile/sys_make.mk | 18 - src/gfile/sys_options.h | 207 -- src/gfile/sys_rules.h | 26 - src/ginput/driver_dial.h | 45 - src/ginput/driver_keyboard.h | 129 - src/ginput/driver_mouse.h | 174 - src/ginput/driver_toggle.h | 61 - src/ginput/ginput.c | 54 + src/ginput/ginput.h | 50 + src/ginput/ginput.mk | 6 + src/ginput/ginput_dial.c | 2 +- src/ginput/ginput_driver_dial.h | 45 + src/ginput/ginput_driver_keyboard.h | 129 + src/ginput/ginput_driver_mouse.h | 174 + src/ginput/ginput_driver_toggle.h | 61 + src/ginput/ginput_ginput.c | 54 - src/ginput/ginput_keyboard.c | 4 +- src/ginput/ginput_keyboard_microcode.c | 73 + src/ginput/ginput_keyboard_microcode.h | 107 + src/ginput/ginput_mouse.c | 2 +- src/ginput/ginput_options.h | 221 ++ src/ginput/ginput_rules.h | 52 + src/ginput/ginput_toggle.c | 2 +- src/ginput/keyboard_microcode.c | 73 - src/ginput/keyboard_microcode.h | 107 - src/ginput/sys_defs.h | 50 - src/ginput/sys_make.mk | 6 - src/ginput/sys_options.h | 221 -- src/ginput/sys_rules.h | 52 - src/gmisc/gmisc.c | 22 + src/gmisc/gmisc.h | 471 +++ src/gmisc/gmisc.mk | 4 + src/gmisc/gmisc_arrayops.c | 8 - src/gmisc/gmisc_gmisc.c | 27 - src/gmisc/gmisc_matrix2d.c | 4 - src/gmisc/gmisc_options.h | 85 + src/gmisc/gmisc_rules.h | 23 + src/gmisc/gmisc_trig.c | 8 - src/gmisc/sys_defs.h | 471 --- src/gmisc/sys_make.mk | 4 - src/gmisc/sys_options.h | 85 - src/gmisc/sys_rules.h | 23 - src/gos/gos.h | 470 +++ src/gos/gos.mk | 9 + src/gos/gos_options.h | 115 + src/gos/gos_rawrtos.c | 7 + src/gos/gos_rules.h | 36 + src/gos/gos_win32.c | 4 - src/gos/sys_defs.h | 470 --- src/gos/sys_make.mk | 9 - src/gos/sys_options.h | 115 - src/gos/sys_rules.h | 36 - src/gqueue/gqueue.c | 464 +++ src/gqueue/gqueue.h | 412 +++ src/gqueue/gqueue.mk | 1 + src/gqueue/gqueue_gqueue.c | 469 --- src/gqueue/gqueue_options.h | 59 + src/gqueue/gqueue_rules.h | 30 + src/gqueue/sys_defs.h | 412 --- src/gqueue/sys_make.mk | 1 - src/gqueue/sys_options.h | 59 - src/gqueue/sys_rules.h | 30 - src/gtimer/gtimer.c | 229 ++ src/gtimer/gtimer.h | 180 ++ src/gtimer/gtimer.mk | 1 + src/gtimer/gtimer_gtimer.c | 238 -- src/gtimer/gtimer_options.h | 46 + src/gtimer/gtimer_rules.h | 29 + src/gtimer/sys_defs.h | 180 -- src/gtimer/sys_make.mk | 1 - src/gtimer/sys_options.h | 46 - src/gtimer/sys_rules.h | 29 - src/gwin/gwin.c | 400 +++ src/gwin/gwin.h | 1026 ++++++ src/gwin/gwin.mk | 19 + src/gwin/gwin_console.h | 2 +- src/gwin/gwin_container.h | 2 +- src/gwin/gwin_gl3d.h | 2 +- src/gwin/gwin_graph.h | 2 +- src/gwin/gwin_gwin.c | 400 --- src/gwin/gwin_image.h | 2 +- src/gwin/gwin_options.h | 334 ++ src/gwin/gwin_rules.h | 129 + src/gwin/gwin_widget.h | 2 +- src/gwin/sys_defs.h | 1026 ------ src/gwin/sys_make.mk | 19 - src/gwin/sys_options.h | 334 -- src/gwin/sys_rules.h | 129 - 212 files changed, 15598 insertions(+), 15707 deletions(-) delete mode 100644 src/gadc/driver.h create mode 100644 src/gadc/gadc.c create mode 100644 src/gadc/gadc.h create mode 100644 src/gadc/gadc.mk create mode 100644 src/gadc/gadc_driver.h delete mode 100644 src/gadc/gadc_gadc.c create mode 100644 src/gadc/gadc_options.h create mode 100644 src/gadc/gadc_rules.h delete mode 100644 src/gadc/sys_defs.h delete mode 100644 src/gadc/sys_make.mk delete mode 100644 src/gadc/sys_options.h delete mode 100644 src/gadc/sys_rules.h delete mode 100644 src/gaudio/driver_play.h delete mode 100644 src/gaudio/driver_record.h create mode 100644 src/gaudio/gaudio.c create mode 100644 src/gaudio/gaudio.h create mode 100644 src/gaudio/gaudio.mk create mode 100644 src/gaudio/gaudio_driver_play.h create mode 100644 src/gaudio/gaudio_driver_record.h delete mode 100644 src/gaudio/gaudio_gaudio.c create mode 100644 src/gaudio/gaudio_options.h create mode 100644 src/gaudio/gaudio_rules.h delete mode 100644 src/gaudio/sys_defs.h delete mode 100644 src/gaudio/sys_make.mk delete mode 100644 src/gaudio/sys_options.h delete mode 100644 src/gaudio/sys_rules.h delete mode 100644 src/gdisp/driver.h create mode 100644 src/gdisp/gdisp.c create mode 100644 src/gdisp/gdisp.h create mode 100644 src/gdisp/gdisp.mk create mode 100644 src/gdisp/gdisp_driver.h delete mode 100644 src/gdisp/gdisp_gdisp.c create mode 100644 src/gdisp/gdisp_options.h create mode 100644 src/gdisp/gdisp_rules.h delete mode 100644 src/gdisp/sys_defs.h delete mode 100644 src/gdisp/sys_make.mk delete mode 100644 src/gdisp/sys_options.h delete mode 100644 src/gdisp/sys_rules.h create mode 100644 src/gdriver/gdriver.c create mode 100644 src/gdriver/gdriver.h create mode 100644 src/gdriver/gdriver.mk delete mode 100644 src/gdriver/gdriver_gdriver.c create mode 100644 src/gdriver/gdriver_options.h create mode 100644 src/gdriver/gdriver_rules.h delete mode 100644 src/gdriver/sys_defs.h delete mode 100644 src/gdriver/sys_make.mk delete mode 100644 src/gdriver/sys_options.h delete mode 100644 src/gdriver/sys_rules.h create mode 100644 src/gevent/gevent.c create mode 100644 src/gevent/gevent.h create mode 100644 src/gevent/gevent.mk delete mode 100644 src/gevent/gevent_gevent.c create mode 100644 src/gevent/gevent_options.h create mode 100644 src/gevent/gevent_rules.h delete mode 100644 src/gevent/sys_defs.h delete mode 100644 src/gevent/sys_make.mk delete mode 100644 src/gevent/sys_options.h delete mode 100644 src/gevent/sys_rules.h create mode 100644 src/gfile/gfile.c create mode 100644 src/gfile/gfile.h create mode 100644 src/gfile/gfile.mk delete mode 100644 src/gfile/gfile_gfile.c create mode 100644 src/gfile/gfile_options.h create mode 100644 src/gfile/gfile_rules.h delete mode 100644 src/gfile/sys_defs.h delete mode 100644 src/gfile/sys_make.mk delete mode 100644 src/gfile/sys_options.h delete mode 100644 src/gfile/sys_rules.h delete mode 100644 src/ginput/driver_dial.h delete mode 100644 src/ginput/driver_keyboard.h delete mode 100644 src/ginput/driver_mouse.h delete mode 100644 src/ginput/driver_toggle.h create mode 100644 src/ginput/ginput.c create mode 100644 src/ginput/ginput.h create mode 100644 src/ginput/ginput.mk create mode 100644 src/ginput/ginput_driver_dial.h create mode 100644 src/ginput/ginput_driver_keyboard.h create mode 100644 src/ginput/ginput_driver_mouse.h create mode 100644 src/ginput/ginput_driver_toggle.h delete mode 100644 src/ginput/ginput_ginput.c create mode 100644 src/ginput/ginput_keyboard_microcode.c create mode 100644 src/ginput/ginput_keyboard_microcode.h create mode 100644 src/ginput/ginput_options.h create mode 100644 src/ginput/ginput_rules.h delete mode 100644 src/ginput/keyboard_microcode.c delete mode 100644 src/ginput/keyboard_microcode.h delete mode 100644 src/ginput/sys_defs.h delete mode 100644 src/ginput/sys_make.mk delete mode 100644 src/ginput/sys_options.h delete mode 100644 src/ginput/sys_rules.h create mode 100644 src/gmisc/gmisc.c create mode 100644 src/gmisc/gmisc.h create mode 100644 src/gmisc/gmisc.mk delete mode 100644 src/gmisc/gmisc_gmisc.c create mode 100644 src/gmisc/gmisc_options.h create mode 100644 src/gmisc/gmisc_rules.h delete mode 100644 src/gmisc/sys_defs.h delete mode 100644 src/gmisc/sys_make.mk delete mode 100644 src/gmisc/sys_options.h delete mode 100644 src/gmisc/sys_rules.h create mode 100644 src/gos/gos.h create mode 100644 src/gos/gos.mk create mode 100644 src/gos/gos_options.h create mode 100644 src/gos/gos_rules.h delete mode 100644 src/gos/sys_defs.h delete mode 100644 src/gos/sys_make.mk delete mode 100644 src/gos/sys_options.h delete mode 100644 src/gos/sys_rules.h create mode 100644 src/gqueue/gqueue.c create mode 100644 src/gqueue/gqueue.h create mode 100644 src/gqueue/gqueue.mk delete mode 100644 src/gqueue/gqueue_gqueue.c create mode 100644 src/gqueue/gqueue_options.h create mode 100644 src/gqueue/gqueue_rules.h delete mode 100644 src/gqueue/sys_defs.h delete mode 100644 src/gqueue/sys_make.mk delete mode 100644 src/gqueue/sys_options.h delete mode 100644 src/gqueue/sys_rules.h create mode 100644 src/gtimer/gtimer.c create mode 100644 src/gtimer/gtimer.h create mode 100644 src/gtimer/gtimer.mk delete mode 100644 src/gtimer/gtimer_gtimer.c create mode 100644 src/gtimer/gtimer_options.h create mode 100644 src/gtimer/gtimer_rules.h delete mode 100644 src/gtimer/sys_defs.h delete mode 100644 src/gtimer/sys_make.mk delete mode 100644 src/gtimer/sys_options.h delete mode 100644 src/gtimer/sys_rules.h create mode 100644 src/gwin/gwin.c create mode 100644 src/gwin/gwin.h create mode 100644 src/gwin/gwin.mk delete mode 100644 src/gwin/gwin_gwin.c create mode 100644 src/gwin/gwin_options.h create mode 100644 src/gwin/gwin_rules.h delete mode 100644 src/gwin/sys_defs.h delete mode 100644 src/gwin/sys_make.mk delete mode 100644 src/gwin/sys_options.h delete mode 100644 src/gwin/sys_rules.h diff --git a/demos/tools/touch_driver_test/main.c b/demos/tools/touch_driver_test/main.c index 13136a57..39924032 100644 --- a/demos/tools/touch_driver_test/main.c +++ b/demos/tools/touch_driver_test/main.c @@ -30,8 +30,8 @@ #include "gfx.h" // We get nasty and look at some internal structures - get the relevant information -#include "src/gdriver/sys_defs.h" -#include "src/ginput/driver_mouse.h" +#include "src/gdriver/gdriver.h" +#include "src/ginput/ginput_driver_mouse.h" #include <string.h> diff --git a/demos/tools/touch_raw_readings/main.c b/demos/tools/touch_raw_readings/main.c index d6c97920..fb1b0772 100644 --- a/demos/tools/touch_raw_readings/main.c +++ b/demos/tools/touch_raw_readings/main.c @@ -30,8 +30,8 @@ #include "gfx.h" // We get nasty and look at some internal structures - get the relevant information -#include "src/gdriver/sys_defs.h" -#include "src/ginput/driver_mouse.h" +#include "src/gdriver/gdriver.h" +#include "src/ginput/ginput_driver_mouse.h" #include <string.h> diff --git a/docs/src/containers.dox b/docs/src/containers.dox index 3d0a6368..e8d6452c 100644 --- a/docs/src/containers.dox +++ b/docs/src/containers.dox @@ -6,7 +6,7 @@ */ /** - * @file src/gwin/sys_defs.h + * @file src/gwin/gwin.h * * @defgroup Containers Containers * @ingroup GWIN diff --git a/docs/src/widgets.dox b/docs/src/widgets.dox index 24be391d..4626508c 100644 --- a/docs/src/widgets.dox +++ b/docs/src/widgets.dox @@ -6,7 +6,7 @@ */ /** - * @file src/gwin/sys_defs.h + * @file src/gwin/gwin.h * * @defgroup Widgets Widgets * @ingroup GWIN diff --git a/docs/src/windows.dox b/docs/src/windows.dox index 699281a2..c5acc31f 100644 --- a/docs/src/windows.dox +++ b/docs/src/windows.dox @@ -6,7 +6,7 @@ */ /** - * @file src/gwin/sys_defs.h + * @file src/gwin/gwin.h * * @defgroup Windows Windows * @ingroup GWIN diff --git a/drivers/gadc/AT91SAM7/gadc_lld.c b/drivers/gadc/AT91SAM7/gadc_lld.c index b01fdd6c..fa1870b0 100644 --- a/drivers/gadc/AT91SAM7/gadc_lld.c +++ b/drivers/gadc/AT91SAM7/gadc_lld.c @@ -14,7 +14,7 @@ #if GFX_USE_GADC -#include "src/gadc/driver.h" +#include "src/gadc/gadc_driver.h" static uint32_t nextfreq; diff --git a/drivers/gaudio/Win32/gaudio_play_lld.c b/drivers/gaudio/Win32/gaudio_play_lld.c index c0adf03e..cdc9a62a 100644 --- a/drivers/gaudio/Win32/gaudio_play_lld.c +++ b/drivers/gaudio/Win32/gaudio_play_lld.c @@ -10,7 +10,7 @@ #if GFX_USE_GAUDIO && GAUDIO_NEED_PLAY /* Include the driver defines */ -#include "src/gaudio/driver_play.h" +#include "src/gaudio/gaudio_driver_play.h" #undef Red #undef Green diff --git a/drivers/gaudio/Win32/gaudio_record_lld.c b/drivers/gaudio/Win32/gaudio_record_lld.c index c9ac8187..cfe8edf5 100644 --- a/drivers/gaudio/Win32/gaudio_record_lld.c +++ b/drivers/gaudio/Win32/gaudio_record_lld.c @@ -10,7 +10,7 @@ #if GFX_USE_GAUDIO && GAUDIO_NEED_RECORD /* Include the driver defines */ -#include "src/gaudio/driver_record.h" +#include "src/gaudio/gaudio_driver_record.h" #undef Red #undef Green diff --git a/drivers/gaudio/gadc/gaudio_record_lld.c b/drivers/gaudio/gadc/gaudio_record_lld.c index 1d70a259..6495309a 100644 --- a/drivers/gaudio/gadc/gaudio_record_lld.c +++ b/drivers/gaudio/gadc/gaudio_record_lld.c @@ -16,7 +16,7 @@ #endif /* Include the driver defines */ -#include "src/gaudio/driver_record.h" +#include "src/gaudio/gaudio_driver_record.h" static void gadcCallbackI(void) { GDataBuffer *pd; diff --git a/drivers/gaudio/pwm/gaudio_play_lld.c b/drivers/gaudio/pwm/gaudio_play_lld.c index 07fd14b2..d4d85c33 100644 --- a/drivers/gaudio/pwm/gaudio_play_lld.c +++ b/drivers/gaudio/pwm/gaudio_play_lld.c @@ -10,7 +10,7 @@ #if GFX_USE_GAUDIO && GAUDIO_NEED_PLAY /* Include the driver defines */ -#include "src/gaudio/driver_play.h" +#include "src/gaudio/gaudio_driver_play.h" /* Forward definition */ static void gaudio_play_pwm_timer_callbackI(void); diff --git a/drivers/gaudio/vs1053/gaudio_play_lld.c b/drivers/gaudio/vs1053/gaudio_play_lld.c index 8e7fb0a5..eaf677cb 100644 --- a/drivers/gaudio/vs1053/gaudio_play_lld.c +++ b/drivers/gaudio/vs1053/gaudio_play_lld.c @@ -10,7 +10,7 @@ #if GFX_USE_GAUDIO && GAUDIO_NEED_PLAY /* Include the driver defines */ -#include "src/gaudio/driver_play.h" +#include "src/gaudio/gaudio_driver_play.h" /* Include the vs1053 registers */ #include "drivers/gaudio/vs1053/vs1053.h" diff --git a/drivers/gdisp/ED060SC4/gdisp_lld_ED060SC4.c b/drivers/gdisp/ED060SC4/gdisp_lld_ED060SC4.c index 789053c8..470c2768 100644 --- a/drivers/gdisp/ED060SC4/gdisp_lld_ED060SC4.c +++ b/drivers/gdisp/ED060SC4/gdisp_lld_ED060SC4.c @@ -11,7 +11,7 @@ #define GDISP_DRIVER_VMT GDISPVMT_ED060SC4 #include "drivers/gdisp/ED060SC4/gdisp_lld_config.h" -#include "src/gdisp/driver.h" +#include "src/gdisp/gdisp_driver.h" #include "board_ED060SC4.h" diff --git a/drivers/gdisp/HX8347D/gdisp_lld_HX8347D.c b/drivers/gdisp/HX8347D/gdisp_lld_HX8347D.c index 34051c22..9c1e7f66 100644 --- a/drivers/gdisp/HX8347D/gdisp_lld_HX8347D.c +++ b/drivers/gdisp/HX8347D/gdisp_lld_HX8347D.c @@ -11,7 +11,7 @@ #define GDISP_DRIVER_VMT GDISPVMT_HX8347D #include "drivers/gdisp/HX8347D/gdisp_lld_config.h" -#include "src/gdisp/driver.h" +#include "src/gdisp/gdisp_driver.h" #include "board_HX8347D.h" diff --git a/drivers/gdisp/ILI9320/gdisp_lld_ILI9320.c b/drivers/gdisp/ILI9320/gdisp_lld_ILI9320.c index 87f29390..d3d1ca0e 100644 --- a/drivers/gdisp/ILI9320/gdisp_lld_ILI9320.c +++ b/drivers/gdisp/ILI9320/gdisp_lld_ILI9320.c @@ -21,7 +21,7 @@ #define GDISP_DRIVER_VMT GDISPVMT_ILI9320 #include "drivers/gdisp/ILI9320/gdisp_lld_config.h" -#include "src/gdisp/driver.h" +#include "src/gdisp/gdisp_driver.h" #include "board_ILI9320.h" diff --git a/drivers/gdisp/ILI9325/gdisp_lld_ILI9325.c b/drivers/gdisp/ILI9325/gdisp_lld_ILI9325.c index 118e5933..5637b0b2 100644 --- a/drivers/gdisp/ILI9325/gdisp_lld_ILI9325.c +++ b/drivers/gdisp/ILI9325/gdisp_lld_ILI9325.c @@ -21,7 +21,7 @@ #define GDISP_DRIVER_VMT GDISPVMT_ILI9325 #include "drivers/gdisp/ILI9325/gdisp_lld_config.h" -#include "src/gdisp/driver.h" +#include "src/gdisp/gdisp_driver.h" #include "board_ILI9325.h" diff --git a/drivers/gdisp/ILI9341/gdisp_lld_ILI9341.c b/drivers/gdisp/ILI9341/gdisp_lld_ILI9341.c index 20b1e270..6a9a929b 100644 --- a/drivers/gdisp/ILI9341/gdisp_lld_ILI9341.c +++ b/drivers/gdisp/ILI9341/gdisp_lld_ILI9341.c @@ -20,7 +20,7 @@ #define GDISP_DRIVER_VMT GDISPVMT_ILI9341 #include "drivers/gdisp/ILI9341/gdisp_lld_config.h" -#include "src/gdisp/driver.h" +#include "src/gdisp/gdisp_driver.h" #include "board_ILI9341.h" diff --git a/drivers/gdisp/ILI93xx/gdisp_lld_ILI93xx.c b/drivers/gdisp/ILI93xx/gdisp_lld_ILI93xx.c index 277d0aff..b883cee6 100644 --- a/drivers/gdisp/ILI93xx/gdisp_lld_ILI93xx.c +++ b/drivers/gdisp/ILI93xx/gdisp_lld_ILI93xx.c @@ -21,7 +21,7 @@ #define GDISP_DRIVER_VMT GDISPVMT_ILI93xx #include "drivers/gdisp/ILI93xx/gdisp_lld_config.h" -#include "src/gdisp/driver.h" +#include "src/gdisp/gdisp_driver.h" #include "board_ILI93xx.h" diff --git a/drivers/gdisp/ILI9481/gdisp_lld_ILI9481.c b/drivers/gdisp/ILI9481/gdisp_lld_ILI9481.c index f0bc7355..dec1cc31 100644 --- a/drivers/gdisp/ILI9481/gdisp_lld_ILI9481.c +++ b/drivers/gdisp/ILI9481/gdisp_lld_ILI9481.c @@ -20,7 +20,7 @@ #define GDISP_DRIVER_VMT GDISPVMT_ILI9481 #include "drivers/gdisp/ILI9481/gdisp_lld_config.h" -#include "src/gdisp/driver.h" +#include "src/gdisp/gdisp_driver.h" #include "board_ILI9481.h" diff --git a/drivers/gdisp/LGDP4532/gdisp_lld_LGDP4532.c b/drivers/gdisp/LGDP4532/gdisp_lld_LGDP4532.c index ceeca05f..067851e6 100644 --- a/drivers/gdisp/LGDP4532/gdisp_lld_LGDP4532.c +++ b/drivers/gdisp/LGDP4532/gdisp_lld_LGDP4532.c @@ -21,7 +21,7 @@ #define GDISP_DRIVER_VMT GDISPVMT_LGDP4532 #include "gdisp_lld_config.h" -#include "src/gdisp/driver.h" +#include "src/gdisp/gdisp_driver.h" #include "board_LGDP4532.h" diff --git a/drivers/gdisp/Nokia6610GE12/gdisp_lld_Nokia6610GE12.c b/drivers/gdisp/Nokia6610GE12/gdisp_lld_Nokia6610GE12.c index c6aa2a7c..6439b91e 100644 --- a/drivers/gdisp/Nokia6610GE12/gdisp_lld_Nokia6610GE12.c +++ b/drivers/gdisp/Nokia6610GE12/gdisp_lld_Nokia6610GE12.c @@ -20,7 +20,7 @@ #define GDISP_DRIVER_VMT GDISPVMT_Nokia6610GE12 #include "drivers/gdisp/Nokia6610GE12/gdisp_lld_config.h" -#include "src/gdisp/driver.h" +#include "src/gdisp/gdisp_driver.h" #include "board_Nokia6610GE12.h" diff --git a/drivers/gdisp/Nokia6610GE8/gdisp_lld_Nokia6610GE8.c b/drivers/gdisp/Nokia6610GE8/gdisp_lld_Nokia6610GE8.c index cd265e00..f419abf5 100644 --- a/drivers/gdisp/Nokia6610GE8/gdisp_lld_Nokia6610GE8.c +++ b/drivers/gdisp/Nokia6610GE8/gdisp_lld_Nokia6610GE8.c @@ -52,7 +52,7 @@ #define GDISP_DRIVER_VMT GDISPVMT_Nokia6610GE8 #include "drivers/gdisp/Nokia6610GE8/gdisp_lld_config.h" -#include "src/gdisp/driver.h" +#include "src/gdisp/gdisp_driver.h" #include "board_Nokia6610GE8.h" diff --git a/drivers/gdisp/PCD8544/gdisp_lld_PCD8544.c b/drivers/gdisp/PCD8544/gdisp_lld_PCD8544.c index ee447202..531a5a42 100644 --- a/drivers/gdisp/PCD8544/gdisp_lld_PCD8544.c +++ b/drivers/gdisp/PCD8544/gdisp_lld_PCD8544.c @@ -11,7 +11,7 @@ #define GDISP_DRIVER_VMT GDISPVMT_PCD8544 #include "drivers/gdisp/PCD8544/gdisp_lld_config.h" -#include "src/gdisp/driver.h" +#include "src/gdisp/gdisp_driver.h" #include "board_PCD8544.h" /*===========================================================================*/ diff --git a/drivers/gdisp/PCF8812/gdisp_lld_PCF8812.c b/drivers/gdisp/PCF8812/gdisp_lld_PCF8812.c index 56d14f3e..cfb4ad83 100644 --- a/drivers/gdisp/PCF8812/gdisp_lld_PCF8812.c +++ b/drivers/gdisp/PCF8812/gdisp_lld_PCF8812.c @@ -11,7 +11,7 @@ #define GDISP_DRIVER_VMT GDISPVMT_PCF8812 #include "drivers/gdisp/PCF8812/gdisp_lld_config.h" -#include "src/gdisp/driver.h" +#include "src/gdisp/gdisp_driver.h" #include "board_PCF8812.h" /*===========================================================================*/ diff --git a/drivers/gdisp/R61505U/gdisp_lld_R61505U.c b/drivers/gdisp/R61505U/gdisp_lld_R61505U.c index dd966c03..94832248 100644 --- a/drivers/gdisp/R61505U/gdisp_lld_R61505U.c +++ b/drivers/gdisp/R61505U/gdisp_lld_R61505U.c @@ -21,7 +21,7 @@ #define GDISP_DRIVER_VMT GDISPVMT_R61505U #include "drivers/gdisp/R61505U/gdisp_lld_config.h" -#include "src/gdisp/driver.h" +#include "src/gdisp/gdisp_driver.h" #include "board_R61505U.h" diff --git a/drivers/gdisp/RA8875/gdisp_lld_RA8875.c b/drivers/gdisp/RA8875/gdisp_lld_RA8875.c index 6b58868b..0da233db 100644 --- a/drivers/gdisp/RA8875/gdisp_lld_RA8875.c +++ b/drivers/gdisp/RA8875/gdisp_lld_RA8875.c @@ -11,7 +11,7 @@ #define GDISP_DRIVER_VMT GDISPVMT_RA8875 #include "drivers/gdisp/RA8875/gdisp_lld_config.h" -#include "src/gdisp/driver.h" +#include "src/gdisp/gdisp_driver.h" /* include the users board interface */ #include "board_RA8875.h" diff --git a/drivers/gdisp/S6D1121/gdisp_lld_S6D1121.c b/drivers/gdisp/S6D1121/gdisp_lld_S6D1121.c index e3f1dd0c..0611ae1c 100644 --- a/drivers/gdisp/S6D1121/gdisp_lld_S6D1121.c +++ b/drivers/gdisp/S6D1121/gdisp_lld_S6D1121.c @@ -20,7 +20,7 @@ #define GDISP_DRIVER_VMT GDISPVMT_S6D1121 #include "drivers/gdisp/S6D1121/gdisp_lld_config.h" -#include "src/gdisp/driver.h" +#include "src/gdisp/gdisp_driver.h" #include "board_S6D1121.h" diff --git a/drivers/gdisp/SPFD54124B/gdisp_lld_SPFD54124B.c b/drivers/gdisp/SPFD54124B/gdisp_lld_SPFD54124B.c index 91298aff..f66d11d6 100644 --- a/drivers/gdisp/SPFD54124B/gdisp_lld_SPFD54124B.c +++ b/drivers/gdisp/SPFD54124B/gdisp_lld_SPFD54124B.c @@ -25,7 +25,7 @@ #endif #include "drivers/gdisp/SPFD54124B/gdisp_lld_config.h" -#include "src/gdisp/driver.h" +#include "src/gdisp/gdisp_driver.h" #include "board_SPFD54124B.h" diff --git a/drivers/gdisp/SSD1289/gdisp_lld_SSD1289.c b/drivers/gdisp/SSD1289/gdisp_lld_SSD1289.c index 0af1339e..82ad57f0 100644 --- a/drivers/gdisp/SSD1289/gdisp_lld_SSD1289.c +++ b/drivers/gdisp/SSD1289/gdisp_lld_SSD1289.c @@ -11,7 +11,7 @@ #define GDISP_DRIVER_VMT GDISPVMT_SSD1289 #include "drivers/gdisp/SSD1289/gdisp_lld_config.h" -#include "src/gdisp/driver.h" +#include "src/gdisp/gdisp_driver.h" #include "board_SSD1289.h" diff --git a/drivers/gdisp/SSD1306/gdisp_lld_SSD1306.c b/drivers/gdisp/SSD1306/gdisp_lld_SSD1306.c index d2f2a872..898c6ece 100644 --- a/drivers/gdisp/SSD1306/gdisp_lld_SSD1306.c +++ b/drivers/gdisp/SSD1306/gdisp_lld_SSD1306.c @@ -11,7 +11,7 @@ #define GDISP_DRIVER_VMT GDISPVMT_SSD1306 #include "drivers/gdisp/SSD1306/gdisp_lld_config.h" -#include "src/gdisp/driver.h" +#include "src/gdisp/gdisp_driver.h" #include "board_SSD1306.h" #include <string.h> // for memset diff --git a/drivers/gdisp/SSD1963/gdisp_lld_SSD1963.c b/drivers/gdisp/SSD1963/gdisp_lld_SSD1963.c index 5c20d3d6..4ffc9f3c 100644 --- a/drivers/gdisp/SSD1963/gdisp_lld_SSD1963.c +++ b/drivers/gdisp/SSD1963/gdisp_lld_SSD1963.c @@ -11,7 +11,7 @@ #define GDISP_DRIVER_VMT GDISPVMT_SSD1963 #include "drivers/gdisp/SSD1963/gdisp_lld_config.h" -#include "src/gdisp/driver.h" +#include "src/gdisp/gdisp_driver.h" #define CALC_PERIOD(w,b,f,p) (p+b+w+f) #define CALC_FPR(w,h,hb,hf,hp,vb,vf,vp,fps) ((fps * CALC_PERIOD(w,hb,hf,hp) * CALC_PERIOD(h,vb,vf,vp) * 1048576)/100000000) diff --git a/drivers/gdisp/SSD2119/gdisp_lld_SSD2119.c b/drivers/gdisp/SSD2119/gdisp_lld_SSD2119.c index 5a789548..e32e2b12 100644 --- a/drivers/gdisp/SSD2119/gdisp_lld_SSD2119.c +++ b/drivers/gdisp/SSD2119/gdisp_lld_SSD2119.c @@ -11,7 +11,7 @@ #define GDISP_DRIVER_VMT GDISPVMT_SSD2119 #include "drivers/gdisp/SSD2119/gdisp_lld_config.h" -#include "src/gdisp/driver.h" +#include "src/gdisp/gdisp_driver.h" #include "board_SSD2119.h" diff --git a/drivers/gdisp/ST7565/gdisp_lld_ST7565.c b/drivers/gdisp/ST7565/gdisp_lld_ST7565.c index 8d331fbc..686d1aa2 100644 --- a/drivers/gdisp/ST7565/gdisp_lld_ST7565.c +++ b/drivers/gdisp/ST7565/gdisp_lld_ST7565.c @@ -11,7 +11,7 @@ #define GDISP_DRIVER_VMT GDISPVMT_ST7565 #include "drivers/gdisp/ST7565/gdisp_lld_config.h" -#include "src/gdisp/driver.h" +#include "src/gdisp/gdisp_driver.h" #include "board_ST7565.h" diff --git a/drivers/gdisp/STM32F429iDiscovery/gdisp_lld_STM32F429iDiscovery.c b/drivers/gdisp/STM32F429iDiscovery/gdisp_lld_STM32F429iDiscovery.c index 65e582b2..6330c32a 100644 --- a/drivers/gdisp/STM32F429iDiscovery/gdisp_lld_STM32F429iDiscovery.c +++ b/drivers/gdisp/STM32F429iDiscovery/gdisp_lld_STM32F429iDiscovery.c @@ -20,7 +20,7 @@ #define GDISP_DRIVER_VMT GDISPVMT_STM32F429iDiscovery #include "drivers/gdisp/STM32F429iDiscovery/gdisp_lld_config.h" -#include "src/gdisp/driver.h" +#include "src/gdisp/gdisp_driver.h" #include "stm32_ltdc.h" diff --git a/drivers/gdisp/TestStub/gdisp_lld_TestStub.c b/drivers/gdisp/TestStub/gdisp_lld_TestStub.c index 4051c818..1d21ab6f 100644 --- a/drivers/gdisp/TestStub/gdisp_lld_TestStub.c +++ b/drivers/gdisp/TestStub/gdisp_lld_TestStub.c @@ -11,7 +11,7 @@ #define GDISP_DRIVER_VMT GDISPVMT_TestStub #include "drivers/gdisp/TestStub/gdisp_lld_config.h" -#include "src/gdisp/driver.h" +#include "src/gdisp/gdisp_driver.h" #ifndef GDISP_SCREEN_HEIGHT #define GDISP_SCREEN_HEIGHT 128 diff --git a/drivers/gdisp/framebuffer/gdisp_lld_framebuffer.c b/drivers/gdisp/framebuffer/gdisp_lld_framebuffer.c index 6e38b5f4..79432bcd 100644 --- a/drivers/gdisp/framebuffer/gdisp_lld_framebuffer.c +++ b/drivers/gdisp/framebuffer/gdisp_lld_framebuffer.c @@ -16,7 +16,7 @@ typedef struct fbInfo { #define GDISP_DRIVER_VMT GDISPVMT_framebuffer #include "drivers/gdisp/framebuffer/gdisp_lld_config.h" -#include "src/gdisp/driver.h" +#include "src/gdisp/gdisp_driver.h" #include "board_framebuffer.h" typedef struct fbPriv { diff --git a/drivers/ginput/dial/GADC/ginput_lld_dial.c b/drivers/ginput/dial/GADC/ginput_lld_dial.c index c07419bc..9ad43748 100644 --- a/drivers/ginput/dial/GADC/ginput_lld_dial.c +++ b/drivers/ginput/dial/GADC/ginput_lld_dial.c @@ -9,7 +9,7 @@ #if GFX_USE_GINPUT && GINPUT_NEED_DIAL -#include "src/ginput/driver_dial.h" +#include "src/ginput/ginput_driver_dial.h" #if GINPUT_DIAL_NUM_PORTS >= 5 #error "GINPUT: Dial - GADC driver currently only supports 4 devices" diff --git a/drivers/ginput/toggle/Pal/ginput_lld_toggle.c b/drivers/ginput/toggle/Pal/ginput_lld_toggle.c index f42d222f..96ef071e 100644 --- a/drivers/ginput/toggle/Pal/ginput_lld_toggle.c +++ b/drivers/ginput/toggle/Pal/ginput_lld_toggle.c @@ -9,7 +9,7 @@ #if (GFX_USE_GINPUT && GINPUT_NEED_TOGGLE) /*|| defined(__DOXYGEN__)*/ -#include "src/ginput/driver_toggle.h" +#include "src/ginput/ginput_driver_toggle.h" GINPUT_TOGGLE_DECLARE_STRUCTURE(); diff --git a/drivers/ginput/touch/ADS7843/gmouse_lld_ADS7843.c b/drivers/ginput/touch/ADS7843/gmouse_lld_ADS7843.c index 31443c45..e9492f55 100644 --- a/drivers/ginput/touch/ADS7843/gmouse_lld_ADS7843.c +++ b/drivers/ginput/touch/ADS7843/gmouse_lld_ADS7843.c @@ -10,7 +10,7 @@ #if (GFX_USE_GINPUT && GINPUT_NEED_MOUSE) #define GMOUSE_DRIVER_VMT GMOUSEVMT_ADS7843 -#include "src/ginput/driver_mouse.h" +#include "src/ginput/ginput_driver_mouse.h" // Get the hardware interface #include "gmouse_lld_ADS7843_board.h" diff --git a/drivers/ginput/touch/FT5x06/gmouse_lld_FT5x06.c b/drivers/ginput/touch/FT5x06/gmouse_lld_FT5x06.c index 6b0bcdb2..ba3db50b 100644 --- a/drivers/ginput/touch/FT5x06/gmouse_lld_FT5x06.c +++ b/drivers/ginput/touch/FT5x06/gmouse_lld_FT5x06.c @@ -10,7 +10,7 @@ #if GFX_USE_GINPUT && GINPUT_NEED_MOUSE #define GMOUSE_DRIVER_VMT GMOUSEVMT_FT5x06 -#include "src/ginput/driver_mouse.h" +#include "src/ginput/ginput_driver_mouse.h" // Get the hardware interface #include "gmouse_lld_FT5x06_board.h" diff --git a/drivers/ginput/touch/MAX11802/gmouse_lld_MAX11802.c b/drivers/ginput/touch/MAX11802/gmouse_lld_MAX11802.c index 12f45645..713b9aad 100644 --- a/drivers/ginput/touch/MAX11802/gmouse_lld_MAX11802.c +++ b/drivers/ginput/touch/MAX11802/gmouse_lld_MAX11802.c @@ -10,7 +10,7 @@ #if GFX_USE_GINPUT && GINPUT_NEED_MOUSE #define GMOUSE_DRIVER_VMT GMOUSEVMT_MAX11802 -#include "src/ginput/driver_mouse.h" +#include "src/ginput/ginput_driver_mouse.h" // Hardware definitions #include "drivers/ginput/touch/MAX11802/max11802.h" diff --git a/drivers/ginput/touch/MCU/gmouse_lld_MCU.c b/drivers/ginput/touch/MCU/gmouse_lld_MCU.c index babf8bc3..a69ae72e 100644 --- a/drivers/ginput/touch/MCU/gmouse_lld_MCU.c +++ b/drivers/ginput/touch/MCU/gmouse_lld_MCU.c @@ -10,7 +10,7 @@ #if GFX_USE_GINPUT && GINPUT_NEED_MOUSE #define GMOUSE_DRIVER_VMT GMOUSEVMT_MCU -#include "src/ginput/driver_mouse.h" +#include "src/ginput/ginput_driver_mouse.h" // Get the hardware interface #include "gmouse_lld_MCU_board.h" diff --git a/drivers/ginput/touch/STMPE811/gmouse_lld_STMPE811.c b/drivers/ginput/touch/STMPE811/gmouse_lld_STMPE811.c index 27bc280e..353539f9 100644 --- a/drivers/ginput/touch/STMPE811/gmouse_lld_STMPE811.c +++ b/drivers/ginput/touch/STMPE811/gmouse_lld_STMPE811.c @@ -10,7 +10,7 @@ #if GFX_USE_GINPUT && GINPUT_NEED_MOUSE #define GMOUSE_DRIVER_VMT GMOUSEVMT_STMPE811 -#include "src/ginput/driver_mouse.h" +#include "src/ginput/ginput_driver_mouse.h" // Hardware definitions #include "drivers/ginput/touch/STMPE811/stmpe811.h" diff --git a/drivers/multiple/Win32/gdisp_lld_Win32.c b/drivers/multiple/Win32/gdisp_lld_Win32.c index a85fae91..0335feee 100644 --- a/drivers/multiple/Win32/gdisp_lld_Win32.c +++ b/drivers/multiple/Win32/gdisp_lld_Win32.c @@ -11,7 +11,7 @@ #define GDISP_DRIVER_VMT GDISPVMT_Win32 #include "gdisp_lld_config.h" -#include "src/gdisp/driver.h" +#include "src/gdisp/gdisp_driver.h" // Configuration parameters for this driver #ifndef GDISP_SCREEN_WIDTH @@ -79,13 +79,13 @@ #if GINPUT_NEED_TOGGLE /* Include toggle support code */ - #include "src/ginput/driver_toggle.h" + #include "src/ginput/ginput_driver_toggle.h" #endif #if GINPUT_NEED_MOUSE // Include mouse support code #define GMOUSE_DRIVER_VMT GMOUSEVMT_Win32 - #include "src/ginput/driver_mouse.h" + #include "src/ginput/ginput_driver_mouse.h" // Forward definitions static bool_t Win32MouseInit(GMouse *m, unsigned driverinstance); @@ -126,14 +126,14 @@ #if GINPUT_NEED_KEYBOARD #define GKEYBOARD_DRIVER_VMT GKEYBOARDVMT_Win32 - #include "src/ginput/driver_keyboard.h" + #include "src/ginput/ginput_driver_keyboard.h" #if !GKEYBOARD_WIN32_NO_LAYOUT #if GKEYBOARD_LAYOUT_OFF #error "The Win32 keyboard driver is using the layout engine. Please set GKEYBOARD_LAYOUT_OFF=FALSE or GKEYBOARD_WIN32_NO_LAYOUT=TRUE." #endif - #include "src/ginput/keyboard_microcode.h" + #include "src/ginput/ginput_keyboard_microcode.h" // Forward definitions extern uint8_t GKEYBOARD_WIN32_DEFAULT_LAYOUT[]; diff --git a/drivers/multiple/X/gdisp_lld_X.c b/drivers/multiple/X/gdisp_lld_X.c index 8a29f033..265d03ee 100644 --- a/drivers/multiple/X/gdisp_lld_X.c +++ b/drivers/multiple/X/gdisp_lld_X.c @@ -17,7 +17,7 @@ #define GDISP_DRIVER_VMT GDISPVMT_X11 #include "gdisp_lld_config.h" -#include "src/gdisp/driver.h" +#include "src/gdisp/gdisp_driver.h" // Configuration parameters for this driver #ifndef GDISP_FORCE_24BIT @@ -53,7 +53,7 @@ #if GINPUT_NEED_MOUSE // Include mouse support code #define GMOUSE_DRIVER_VMT GMOUSEVMT_X11 - #include "src/ginput/driver_mouse.h" + #include "src/ginput/ginput_driver_mouse.h" // Forward definitions static bool_t XMouseInit(GMouse *m, unsigned driverinstance); @@ -95,7 +95,7 @@ #if GINPUT_NEED_KEYBOARD // Include mouse support code #define GKEYBOARD_DRIVER_VMT GKEYBOARDVMT_X - #include "src/ginput/driver_keyboard.h" + #include "src/ginput/ginput_driver_keyboard.h" #if !GKEYBOARD_X_NO_LAYOUT #if GKEYBOARD_LAYOUT_OFF @@ -105,7 +105,7 @@ // Forward definitions extern uint8_t GKEYBOARD_X_DEFAULT_LAYOUT[]; - #include "src/ginput/keyboard_microcode.h" + #include "src/ginput/ginput_keyboard_microcode.h" #include <X11/keysym.h> // This is the layout code for the English US keyboard. diff --git a/drivers/multiple/uGFXnet/gdisp_lld_uGFXnet.c b/drivers/multiple/uGFXnet/gdisp_lld_uGFXnet.c index 3b42bcda..fbc40916 100644 --- a/drivers/multiple/uGFXnet/gdisp_lld_uGFXnet.c +++ b/drivers/multiple/uGFXnet/gdisp_lld_uGFXnet.c @@ -11,7 +11,7 @@ #define GDISP_DRIVER_VMT GDISPVMT_uGFXnet #include "gdisp_lld_config.h" -#include "src/gdisp/driver.h" +#include "src/gdisp/gdisp_driver.h" #include "uGFXnetProtocol.h" #ifndef GDISP_SCREEN_WIDTH @@ -36,7 +36,7 @@ #if GINPUT_NEED_MOUSE // Include mouse support code #define GMOUSE_DRIVER_VMT GMOUSEVMT_uGFXnet - #include "src/ginput/driver_mouse.h" + #include "src/ginput/ginput_driver_mouse.h" // Forward definitions static bool_t NMouseInit(GMouse *m, unsigned driverinstance); diff --git a/gfx.h b/gfx.h index dc7605e8..8be39815 100644 --- a/gfx.h +++ b/gfx.h @@ -161,18 +161,18 @@ * Get all the options for each sub-system. * */ -#include "src/gos/sys_options.h" -#include "src/gdriver/sys_options.h" -#include "src/gfile/sys_options.h" -#include "src/gmisc/sys_options.h" -#include "src/gqueue/sys_options.h" -#include "src/gevent/sys_options.h" -#include "src/gtimer/sys_options.h" -#include "src/gdisp/sys_options.h" -#include "src/gwin/sys_options.h" -#include "src/ginput/sys_options.h" -#include "src/gadc/sys_options.h" -#include "src/gaudio/sys_options.h" +#include "src/gos/gos_options.h" +#include "src/gdriver/gdriver_options.h" +#include "src/gfile/gfile_options.h" +#include "src/gmisc/gmisc_options.h" +#include "src/gqueue/gqueue_options.h" +#include "src/gevent/gevent_options.h" +#include "src/gtimer/gtimer_options.h" +#include "src/gdisp/gdisp_options.h" +#include "src/gwin/gwin_options.h" +#include "src/ginput/ginput_options.h" +#include "src/gadc/gadc_options.h" +#include "src/gaudio/gaudio_options.h" /** * Interdependency safety checks on the sub-systems. @@ -182,34 +182,34 @@ #ifndef GFX_DISPLAY_RULE_WARNINGS #define GFX_DISPLAY_RULE_WARNINGS FALSE #endif -#include "src/gwin/sys_rules.h" -#include "src/ginput/sys_rules.h" -#include "src/gdisp/sys_rules.h" -#include "src/gaudio/sys_rules.h" -#include "src/gadc/sys_rules.h" -#include "src/gevent/sys_rules.h" -#include "src/gtimer/sys_rules.h" -#include "src/gqueue/sys_rules.h" -#include "src/gmisc/sys_rules.h" -#include "src/gfile/sys_rules.h" -#include "src/gdriver/sys_rules.h" -#include "src/gos/sys_rules.h" +#include "src/gwin/gwin_rules.h" +#include "src/ginput/ginput_rules.h" +#include "src/gdisp/gdisp_rules.h" +#include "src/gaudio/gaudio_rules.h" +#include "src/gadc/gadc_rules.h" +#include "src/gevent/gevent_rules.h" +#include "src/gtimer/gtimer_rules.h" +#include "src/gqueue/gqueue_rules.h" +#include "src/gmisc/gmisc_rules.h" +#include "src/gfile/gfile_rules.h" +#include "src/gdriver/gdriver_rules.h" +#include "src/gos/gos_rules.h" /** * Include the sub-system header files */ -#include "src/gos/sys_defs.h" -//#include "src/gdriver/sys_defs.h" // This module is only included by source that needs it. -#include "src/gfile/sys_defs.h" -#include "src/gmisc/sys_defs.h" -#include "src/gqueue/sys_defs.h" -#include "src/gevent/sys_defs.h" -#include "src/gtimer/sys_defs.h" -#include "src/gdisp/sys_defs.h" -#include "src/gwin/sys_defs.h" -#include "src/ginput/sys_defs.h" -#include "src/gadc/sys_defs.h" -#include "src/gaudio/sys_defs.h" +#include "src/gos/gos.h" +//#include "src/gdriver/gdriver.h" // This module is only included by source that needs it. +#include "src/gfile/gfile.h" +#include "src/gmisc/gmisc.h" +#include "src/gqueue/gqueue.h" +#include "src/gevent/gevent.h" +#include "src/gtimer/gtimer.h" +#include "src/gdisp/gdisp.h" +#include "src/gwin/gwin.h" +#include "src/ginput/ginput.h" +#include "src/gadc/gadc.h" +#include "src/gaudio/gaudio.h" #ifdef __cplusplus extern "C" { diff --git a/gfx.mk b/gfx.mk index 89c3282a..765c02b1 100644 --- a/gfx.mk +++ b/gfx.mk @@ -1,18 +1,18 @@ GFXINC += $(GFXLIB) GFXSRC += $(GFXLIB)/src/gfx.c -include $(GFXLIB)/src/gos/sys_make.mk -include $(GFXLIB)/src/gdriver/sys_make.mk -include $(GFXLIB)/src/gqueue/sys_make.mk -include $(GFXLIB)/src/gdisp/sys_make.mk -include $(GFXLIB)/src/gevent/sys_make.mk -include $(GFXLIB)/src/gtimer/sys_make.mk -include $(GFXLIB)/src/gwin/sys_make.mk -include $(GFXLIB)/src/ginput/sys_make.mk -include $(GFXLIB)/src/gadc/sys_make.mk -include $(GFXLIB)/src/gaudio/sys_make.mk -include $(GFXLIB)/src/gmisc/sys_make.mk -include $(GFXLIB)/src/gfile/sys_make.mk +include $(GFXLIB)/src/gos/gos.mk +include $(GFXLIB)/src/gdriver/gdriver.mk +include $(GFXLIB)/src/gqueue/gqueue.mk +include $(GFXLIB)/src/gdisp/gdisp.mk +include $(GFXLIB)/src/gevent/gevent.mk +include $(GFXLIB)/src/gtimer/gtimer.mk +include $(GFXLIB)/src/gwin/gwin.mk +include $(GFXLIB)/src/ginput/ginput.mk +include $(GFXLIB)/src/gadc/gadc.mk +include $(GFXLIB)/src/gaudio/gaudio.mk +include $(GFXLIB)/src/gmisc/gmisc.mk +include $(GFXLIB)/src/gfile/gfile.mk # Include the boards and drivers ifneq ($(GFXBOARD),) diff --git a/src/gadc/driver.h b/src/gadc/driver.h deleted file mode 100644 index 77604b9f..00000000 --- a/src/gadc/driver.h +++ /dev/null @@ -1,153 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gadc/driver.h - * @brief GADC - Periodic ADC driver header file. - * - * @defgroup Driver Driver - * @ingroup GADC - * @{ - */ - -#ifndef _GADC_LLD_H -#define _GADC_LLD_H - -#include "gfx.h" - -#if GFX_USE_GADC || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Type definitions */ -/*===========================================================================*/ - -/** - * @brief The structure passed to start a timer conversion - * @{ - */ -typedef struct GadcTimerJob_t { - uint32_t physdev; // @< The physical device/s. The exact meaning of physdev is hardware dependent. - uint32_t frequency; // @< The frequency to sample - adcsample_t *buffer; // @< Where to put the samples - size_t todo; // @< How many conversions to do - size_t done; // @< How many conversions have already been done -} GadcTimerJob; -/** @} */ - -/** - * @brief The structure passed to do a single conversion - * @{ - */ -typedef struct GadcNonTimerJob_t { - uint32_t physdev; // @< The physical device/s. The exact meaning of physdev is hardware dependent. - adcsample_t *buffer; // @< Where to put the samples. - } GadcNonTimerJob; -/** @} */ - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief These routines are the callbacks that the driver uses. - * @details Defined in the high level GADC code. - * - * @notapi - * @{ - */ - /** - * @brief Indicate that some data has been placed into the buffer for the current job - * - * @param[in] n The number of samples placed in the buffer - * - * @note Calling this with n = 0 causes the current job to be terminated early or aborted. - * It can be called in this mode on an ADC conversion error. Any job will then be - * restarted by the high level code as appropriate. - */ - void gadcGotDataI(size_t n); -/** - * @} - */ - -/** - * @brief Initialise the driver - * - * @api - */ -void gadc_lld_init(void); - -/** - * @brief Using the hardware dependant "physdev", return the number of samples for each conversion - * - * @param[in] physdev The hardware dependent physical device descriptor - * - * @return The number of samples per conversion - * - * @api - */ -size_t gadc_lld_samplesperconversion(uint32_t physdev); - -/** - * @brief Start a periodic timer for high frequency conversions. - * - * @param[in] freq The frequency for the timer - * - * @note This will only be called if the timer is currently stopped. - * - * @api - * @iclass - */ -void gadc_lld_start_timerI(uint32_t freq); - -/** - * @brief Stop the periodic timer for high frequency conversions. - * - * @note This will only be called if the timer is currently running and all timer jobs - * have been completed/aborted. - * - * @api - * @iclass - */ -void gadc_lld_stop_timerI(void); - -/** - * @brief Start a set of high frequency conversions. - * - * @note This will only be called if the timer is currently running and the ADC should be ready for - * a new job. - * - * @param[in] pjob The job to be started. - * - * @api - * @iclass - */ -void gadc_lld_timerjobI(GadcTimerJob *pjob); - -/** - * @brief Start a non-timer conversion. - * - * @note This will only be called if the ADC should be ready for a new job. - * - * @param[in] pjob The job to be started - * - * @api - * @iclass - */ -void gadc_lld_nontimerjobI(GadcNonTimerJob *pjob); - -#ifdef __cplusplus -} -#endif - -#endif /* GFX_USE_GADC */ - -#endif /* _GADC_LLD_H */ -/** @} */ diff --git a/src/gadc/gadc.c b/src/gadc/gadc.c new file mode 100644 index 00000000..ec0bc880 --- /dev/null +++ b/src/gadc/gadc.c @@ -0,0 +1,353 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +#include "gfx.h" + +#if GFX_USE_GADC + +/* Include the driver defines */ +#include "gadc_driver.h" + +#if GADC_MAX_HIGH_SPEED_SAMPLERATE > GADC_MAX_SAMPLE_FREQUENCY/2 + #error "GADC: GADC_MAX_HIGH_SPEED_SAMPLERATE has been set too high. It must be less than half the maximum CPU rate" +#endif + +#define GADC_HSADC_GTIMER 0x8000 +#define GADC_ADC_RUNNING 0x4000 +#define GADC_HSADC_CONVERTION 0x2000 + +typedef struct NonTimerData_t { + gfxQueueGSyncItem next; + GADCCallbackFunction callback; + union { + void *param; + gfxSem sigdone; + }; + GadcNonTimerJob job; + } NonTimerData; + +static volatile uint16_t hsFlags; +static size_t hsBytesPerConv; +static GadcTimerJob hsJob; +static GDataBuffer *hsData; +static gfxQueueGSync hsListDone; +static GADCISRCallbackFunction hsISRcallback; +#if GFX_USE_GEVENT + static GTimer hsGTimer; +#endif + +static GTimer lsGTimer; +static gfxQueueGSync lsListToDo; +static gfxQueueGSync lsListDone; +static NonTimerData *lsData; + +void gadcGotDataI(size_t n) { + if ((hsFlags & GADC_HSADC_CONVERTION)) { + + // A set of timer conversions is done - add them + hsJob.done += n; + + // Are we finished yet? (or driver signalled complete now) + if (n && hsJob.done < hsJob.todo) + return; + + // Clear event flags we might set + hsFlags &= ~(GADC_HSADC_GOTBUFFER|GADC_HSADC_STALL); + + // Is there any data in it + if (!hsJob.done) { + + // Oops - no data in this buffer. Just return it to the free-list + gfxBufferReleaseI(hsData); + goto starttimerjob; // Restart the timer job + } + + // Save the buffer on the hsListDone list + hsData->len = hsJob.done * hsBytesPerConv; + gfxQueueGSyncPutI(&hsListDone, (gfxQueueGSyncItem *)hsData); + hsFlags |= GADC_HSADC_GOTBUFFER; + + /* Signal a buffer completion */ + if (hsISRcallback) + hsISRcallback(); + #if GFX_USE_GEVENT + if (hsFlags & GADC_HSADC_GTIMER) + gtimerJabI(&hsGTimer); + #endif + + // Stop if we have been told to + if (!(hsFlags & GADC_HSADC_RUNNING)) { + gadc_lld_stop_timerI(); + + // Get the next free buffer + } else if (!(hsData = gfxBufferGetI())) { + + // Oops - no free buffers. Stall + hsFlags &= ~GADC_HSADC_RUNNING; + hsFlags |= GADC_HSADC_STALL; + gadc_lld_stop_timerI(); + + // Prepare the next job + } else { + + // Return this new job + #if GFX_USE_OS_CHIBIOS + // ChibiOS api bug - samples must be even + hsJob.todo = (hsData->size / hsBytesPerConv) & ~1; + #else + hsJob.todo = hsData->size / hsBytesPerConv; + #endif + hsJob.done = 0; + hsJob.buffer = (adcsample_t *)(hsData+1); + } + + // Start a job preferring a non-timer job + if ((lsData = (NonTimerData *)gfxQueueGSyncGetI(&lsListToDo))) { + hsFlags &= ~GADC_HSADC_CONVERTION; + gadc_lld_nontimerjobI(&lsData->job); + } else if ((hsFlags & GADC_HSADC_RUNNING)) { + hsFlags |= GADC_HSADC_CONVERTION; + gadc_lld_timerjobI(&hsJob); + } else + hsFlags &= ~GADC_ADC_RUNNING; + + } else { + + // Did it fail + if (!n) { + // Push it back on the head of the queue - it didn't actually get done + gfxQueueGSyncPushI(&lsListToDo, (gfxQueueGSyncItem *)lsData); + lsData = 0; + goto starttimerjob; + } + + // A non-timer job completed - signal + if (lsData->callback) { + // Put it on the completed list and signal the timer to do the call-backs + gfxQueueGSyncPutI(&lsListDone, (gfxQueueGSyncItem *)lsData); + gtimerJabI(&lsGTimer); + } else { + // Signal the thread directly + gfxSemSignalI(&lsData->sigdone); + } + lsData = 0; + + // Start a job preferring a timer job +starttimerjob: + if ((hsFlags & GADC_HSADC_RUNNING)) { + hsFlags |= GADC_HSADC_CONVERTION; + gadc_lld_timerjobI(&hsJob); + } else if ((lsData = (NonTimerData *)gfxQueueGSyncGetI(&lsListToDo))) { + hsFlags &= ~GADC_HSADC_CONVERTION; + gadc_lld_nontimerjobI(&lsData->job); + } else + hsFlags &= ~GADC_ADC_RUNNING; + } +} + +/* Our module initialiser */ +void _gadcInit(void) +{ + gadc_lld_init(); + + gfxQueueGSyncInit(&hsListDone); + #if GFX_USE_GEVENT + gtimerInit(&hsGTimer); + #endif + gtimerInit(&lsGTimer); + gfxQueueGSyncInit(&lsListToDo); + gfxQueueGSyncInit(&lsListDone); +} + +void _gadcDeinit(void) +{ + /* commented stuff is ToDo */ + + // gadc_lld_deinit(); + gfxQueueGSyncDeinit(&hsListDone); + #if GFX_USE_GEVENT + gtimerDeinit(&hsGTimer); + #endif + gtimerDeinit(&lsGTimer); + gfxQueueGSyncDeinit(&lsListToDo); + gfxQueueGSyncDeinit(&lsListDone); +} + +#if GFX_USE_GEVENT + static void HighSpeedGTimerCallback(void *param) { + (void) param; + GSourceListener *psl; + GEventADC *pe; + + psl = 0; + while ((psl = geventGetSourceListener((GSourceHandle)(&hsGTimer), psl))) { + if (!(pe = (GEventADC *)geventGetEventBuffer(psl))) { + // This listener is missing - save this. + psl->srcflags |= GADC_HSADC_LOSTEVENT; + continue; + } + + pe->type = GEVENT_ADC; + pe->flags = (hsFlags & (GADC_HSADC_RUNNING|GADC_HSADC_GOTBUFFER|GADC_HSADC_STALL)) | psl->srcflags; + psl->srcflags = 0; + geventSendEvent(psl); + } + } +#endif + +void gadcHighSpeedInit(uint32_t physdev, uint32_t frequency) +{ + if ((hsFlags & GADC_HSADC_RUNNING)) + gadcHighSpeedStop(); + + /* Just save the details and reset everything for now */ + hsJob.physdev = physdev; + hsJob.frequency = frequency; + hsISRcallback = 0; + hsBytesPerConv = gadc_lld_samplesperconversion(physdev) * sizeof(adcsample_t); +} + +#if GFX_USE_GEVENT + GSourceHandle gadcHighSpeedGetSource(void) { + if (!gtimerIsActive(&hsGTimer)) + gtimerStart(&hsGTimer, HighSpeedGTimerCallback, 0, TRUE, TIME_INFINITE); + hsFlags |= GADC_HSADC_GTIMER; + return (GSourceHandle)&hsGTimer; + } +#endif + +void gadcHighSpeedSetISRCallback(GADCISRCallbackFunction isrfn) { + hsISRcallback = isrfn; +} + +GDataBuffer *gadcHighSpeedGetData(delaytime_t ms) { + return (GDataBuffer *)gfxQueueGSyncGet(&hsListDone, ms); +} + +GDataBuffer *gadcHighSpeedGetDataI(void) { + return (GDataBuffer *)gfxQueueGSyncGetI(&hsListDone); +} + +void gadcHighSpeedStart(void) { + // Safety first + if (!hsJob.frequency || !hsBytesPerConv) + return; + + gfxSystemLock(); + if (!(hsFlags & GADC_HSADC_RUNNING)) { + if (!(hsData = gfxBufferGetI())) { + // Oops - no free buffers. Stall + hsFlags |= GADC_HSADC_STALL; + #if GFX_USE_GEVENT + if (hsFlags & GADC_HSADC_GTIMER) + gtimerJabI(&hsGTimer); + #endif + + // Prepare the next job + } else { + + #if GFX_USE_OS_CHIBIOS + // ChibiOS api bug - samples must be even + hsJob.todo = (hsData->size / hsBytesPerConv) & ~1; + #else + hsJob.todo = hsData->size / hsBytesPerConv; + #endif + hsJob.done = 0; + hsJob.buffer = (adcsample_t *)(hsData+1); + hsFlags |= GADC_HSADC_RUNNING; + + // Start the timer + gadc_lld_start_timerI(hsJob.frequency); + + // If nothing is running start the job + if (!(hsFlags & GADC_ADC_RUNNING)) { + hsFlags |= (GADC_HSADC_CONVERTION|GADC_ADC_RUNNING); + gadc_lld_timerjobI(&hsJob); + } + } + } + gfxSystemUnlock(); +} + +void gadcHighSpeedStop(void) { + // Stop it and wait for completion + hsFlags &= ~GADC_HSADC_RUNNING; + while ((hsFlags & GADC_HSADC_CONVERTION)) + gfxYield(); +} + +static void LowSpeedGTimerCallback(void *param) { + (void) param; + NonTimerData *pdata; + + // Look for completed non-timer jobs and call the call-backs for each + while ((pdata = (NonTimerData *)gfxQueueGSyncGet(&lsListDone, TIME_IMMEDIATE))) { + pdata->callback(pdata->job.buffer, pdata->param); + gfxFree(pdata); + } +} + +void gadcLowSpeedGet(uint32_t physdev, adcsample_t *buffer) { + NonTimerData ndata; + + // Prepare the job + gfxSemInit(&ndata.sigdone, 0, 1); + ndata.job.physdev = physdev; + ndata.job.buffer = buffer; + ndata.callback = 0; + + // Activate it + gfxSystemLock(); + if (!(hsFlags & GADC_ADC_RUNNING)) { + // Nothing is running - start the job + lsData = &ndata; + hsFlags |= GADC_ADC_RUNNING; + hsFlags &= ~GADC_HSADC_CONVERTION; + gadc_lld_nontimerjobI(&ndata.job); + } else { + // Just put it on the queue + gfxQueueGSyncPutI(&lsListToDo, (gfxQueueGSyncItem *)&ndata); + } + gfxSystemUnlock(); + + // Wait for it to complete + gfxSemWait(&ndata.sigdone, TIME_INFINITE); + gfxSemDestroy(&ndata.sigdone); +} + +bool_t gadcLowSpeedStart(uint32_t physdev, adcsample_t *buffer, GADCCallbackFunction fn, void *param) { + NonTimerData *pdata; + + /* Start the Low Speed Timer */ + if (!gtimerIsActive(&lsGTimer)) + gtimerStart(&lsGTimer, LowSpeedGTimerCallback, 0, TRUE, TIME_INFINITE); + + // Prepare the job + if (!(pdata = gfxAlloc(sizeof(NonTimerData)))) + return FALSE; + pdata->job.physdev = physdev; + pdata->job.buffer = buffer; + pdata->callback = fn; + pdata->param = param; + + // Activate it + gfxSystemLock(); + if (!(hsFlags & GADC_ADC_RUNNING)) { + // Nothing is running - start the job + lsData = pdata; + hsFlags |= GADC_ADC_RUNNING; + hsFlags &= ~GADC_HSADC_CONVERTION; + gadc_lld_nontimerjobI(&pdata->job); + } else { + // Just put it on the queue + gfxQueueGSyncPutI(&lsListToDo, (gfxQueueGSyncItem *)pdata); + } + gfxSystemUnlock(); + return TRUE; +} + +#endif /* GFX_USE_GADC */ diff --git a/src/gadc/gadc.h b/src/gadc/gadc.h new file mode 100644 index 00000000..461e490e --- /dev/null +++ b/src/gadc/gadc.h @@ -0,0 +1,252 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gadc/gadc.h + * + * @addtogroup GADC + * + * @brief Module to abstract the very variable ADC interfaces of the underlying systems + * + * @details The reason why ChibiOS/GFX has it's own ADC abstraction is because + * the Chibi-OS drivers are very CPU specific and do not + * provide a way across all hardware platforms to create periodic + * ADC conversions. There are also issues with devices with different + * characteristics or periodic requirements on the same ADC + * device (but different channels). This layer attempts to solve these + * problems to provide a architecture neutral API. It also provides extra + * features such as multi-buffer chaining for high speed ADC sources. + * It provides one high speed virtual ADC device (eg a microphone) and + * numerous low speed (less than 100Hz) virtual ADC devices (eg dials, + * temperature sensors etc). The high speed device has timer based polling + * to ensure exact conversion periods and a buffer management system. + * The low speed devices are assumed to be non-critical timing devices + * and do not have any buffer management. + * Note that while only one high speed device has been provided it can + * be used to read multiple physical ADC channels on the one physical + * ADC device. + * All callback routines are thread based unlike the Chibi-OS interrupt based + * routines. + * + * @{ + */ + +#ifndef _GADC_H +#define _GADC_H + +#include "gfx.h" + +#if GFX_USE_GADC || defined(__DOXYGEN__) + +/* Include the driver defines */ +#include "gadc_lld_config.h" + +/*===========================================================================*/ +/* Type definitions */ +/*===========================================================================*/ + +// Event types for GADC +#define GEVENT_ADC (GEVENT_GADC_FIRST+0) + +/** + * @brief The High Speed ADC event structure. + * @{ + */ +typedef struct GEventADC_t { + #if GFX_USE_GEVENT || defined(__DOXYGEN__) + /** + * @brief The type of this event (GEVENT_ADC) + */ + GEventType type; + #endif + + /** + * @brief The event flags + */ + uint16_t flags; + /** + * @brief The event flag values. + * @{ + */ + #define GADC_HSADC_LOSTEVENT 0x0001 /**< @brief The last GEVENT_HSDADC event was lost */ + #define GADC_HSADC_RUNNING 0x0002 /**< @brief The High Speed ADC is currently running */ + #define GADC_HSADC_GOTBUFFER 0x0004 /**< @brief A buffer is ready for processing */ + #define GADC_HSADC_STALL 0x0008 /**< @brief The High Speed ADC has stalled due to no free buffers */ + /** @} */ +} GEventADC; +/** @} */ + +/** + * @brief A callback function (executed in a thread context) for a low speed conversion + */ +typedef void (*GADCCallbackFunction)(adcsample_t *buffer, void *param); + +/** + * @brief A callback function (executed in an ISR context) for a high speed conversion + */ +typedef void (*GADCISRCallbackFunction)(void); + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initialise the high speed ADC. + * @details Initialises but does not start the conversions. + * + * @param[in] physdev A value passed to describe which physical ADC devices/channels to use. + * @param[in] frequency The frequency to create ADC conversions + * + * @note If the high speed ADC is running it will be stopped. The Event subsystem is + * disconnected from the high speed ADC and any binary semaphore event is forgotten. + * @note ChibiOS ONLY: Due to a bug in ChibiOS each buffer on the free-list must contain an even number of + * samples and for multi-channel devices it must hold a number of samples that is evenly divisible + * by 2 times the number of active channels. + * @note The physdev parameter may be used to turn on more than one ADC channel. + * Each channel is then interleaved into the provided buffer. Make sure your buffers all hold + * a number of samples evenly divisible by the number of active channels. + * As an example, if physdev turns on 2 devices then the buffer contains + * alternate device samples and the buffer must contain multiples of 2 samples. + * The exact meaning of physdev is hardware dependent. + * @note While the high speed ADC is running, low speed conversions can only occur at + * the frequency of the high speed events. Thus if high speed events are + * being created at 50Hz (eg 100 samples/buffer, frequency = 5kHz) then the maximum + * frequency for low speed conversions will be 50Hz. + * @note Only a single sample format is supported - that provided by the GADC driver. That sample + * format applies to both high speed and low speed sampling. + * + * @api + */ +void gadcHighSpeedInit(uint32_t physdev, uint32_t frequency); + +#if GFX_USE_GEVENT || defined(__DOXYGEN__) + /** + * @brief Turn on sending results to the GEVENT sub-system. + * @details Returns a GSourceHandle to listen for GEVENT_ADC events. + * + * @note The high speed ADC will not use the GEVENT system unless this is + * called first. This saves processing time if the application does + * not want to use the GEVENT sub-system for the high speed ADC. + * Once turned on it can only be turned off by calling @p gadcHighSpeedInit() again. + * @note The high speed ADC is capable of signalling via this method, an ISR callback and a + * binary semaphore at the same time. + * + * @return The GSourceHandle + * + * @api + */ + GSourceHandle gadcHighSpeedGetSource(void); +#endif + +/** + * @brief Allow retrieving of results from the high speed ADC using an ISR callback. + * + * @param[in] isrfn The callback function (called in an ISR context). + * + * @note Passing a NULL for isrfn will turn off signalling via this method as will calling + * @p gadcHighSpeedInit(). + * @note The high speed ADC is capable of signalling via this method, a blocked thread and the GEVENT + * sub-system at the same time. + * + * @api + */ +void gadcHighSpeedSetISRCallback(GADCISRCallbackFunction isrfn); + +/** + * @brief Get a filled buffer from the ADC + * @return A GDataBuffer pointer or NULL if the timeout is exceeded + * + * @param[in] ms The maximum amount of time in milliseconds to wait for data if some is not currently available. + * + * @note After processing the data, your application must return the buffer to the free-list so that + * it can be used again. This can be done using @p gfxBufferRelease(). + * @note A buffer may be returned to the free-list before you have finished processing it provided you finish + * processing it before GADC re-uses it. This is useful when RAM usage is critical to reduce the number + * of buffers required. It works before the free list is a FIFO queue and therefore buffers are kept + * in the queue as long as possible before they are re-used. + * @note The function ending with "I" is the interrupt class function. + * @api + * @{ + */ +GDataBuffer *gadcHighSpeedGetData(delaytime_t ms); +GDataBuffer *gadcHighSpeedGetDataI(void); +/** @} */ + +/** + * @brief Start the high speed ADC conversions. + * @pre It must have been initialised first with @p gadcHighSpeedInit() + * + * @api + */ +void gadcHighSpeedStart(void); + +/** + * @brief Stop the high speed ADC conversions. + * + * @api + */ +void gadcHighSpeedStop(void); + +/** + * @brief Perform a single low speed ADC conversion + * @details Blocks until the conversion is complete + * @pre This should not be called from within a GTimer callback as this routine + * blocks until the conversion is ready. + * + * @param[in] physdev A value passed to describe which physical ADC devices/channels to use. + * @param[in] buffer The static buffer to put the ADC samples into. + * + * @note This may take a while to complete if the high speed ADC is running as the + * conversion is interleaved with the high speed ADC conversions on a buffer + * completion. + * @note The result buffer must be large enough to store one sample per device + * described by the 'physdev' parameter. + * @note Specifying more than one device in physdev is possible but discouraged as the + * calculations to ensure the high speed ADC correctness will be incorrect. Symptoms + * from over-running the high speed ADC include high speed device stalling or samples being lost. + * + * @api + */ +void gadcLowSpeedGet(uint32_t physdev, adcsample_t *buffer); + +/** + * @brief Perform a low speed ADC conversion with callback (in a thread context) + * @details Returns FALSE if internal memory allocation fails + * + * @param[in] physdev A value passed to describe which physical ADC devices/channels to use. + * @param[in] buffer The static buffer to put the ADC samples into. + * @param[in] fn The callback function to call when the conversion is complete. + * @param[in] param A parameter to pass to the callback function. + * + * @return FALSE if no free low speed ADC slots. + * + * @note This may be safely called from within a GTimer callback. + * @note The callback may take a while to occur if the high speed ADC is running as the + * conversion is interleaved with the high speed ADC conversions on a buffer + * completion. + * @note The result buffer must be large enough to store one sample per device + * described by the 'physdev' parameter. + * @note Specifying more than one device in physdev is possible but discouraged as the + * calculations to ensure the high speed ADC correctness will be incorrect. Symptoms + * from over-running the high speed ADC include high speed samples being lost. + * + * @api + */ +bool_t gadcLowSpeedStart(uint32_t physdev, adcsample_t *buffer, GADCCallbackFunction fn, void *param); + +#ifdef __cplusplus +} +#endif + +#endif /* GFX_USE_GADC */ + +#endif /* _GADC_H */ +/** @} */ diff --git a/src/gadc/gadc.mk b/src/gadc/gadc.mk new file mode 100644 index 00000000..05b1e9cc --- /dev/null +++ b/src/gadc/gadc.mk @@ -0,0 +1 @@ +GFXSRC += $(GFXLIB)/src/gadc/gadc.c diff --git a/src/gadc/gadc_driver.h b/src/gadc/gadc_driver.h new file mode 100644 index 00000000..7c72dfd4 --- /dev/null +++ b/src/gadc/gadc_driver.h @@ -0,0 +1,153 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gadc/gadc_driver.h + * @brief GADC - Periodic ADC driver header file. + * + * @defgroup Driver Driver + * @ingroup GADC + * @{ + */ + +#ifndef _GADC_LLD_H +#define _GADC_LLD_H + +#include "gfx.h" + +#if GFX_USE_GADC || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Type definitions */ +/*===========================================================================*/ + +/** + * @brief The structure passed to start a timer conversion + * @{ + */ +typedef struct GadcTimerJob_t { + uint32_t physdev; // @< The physical device/s. The exact meaning of physdev is hardware dependent. + uint32_t frequency; // @< The frequency to sample + adcsample_t *buffer; // @< Where to put the samples + size_t todo; // @< How many conversions to do + size_t done; // @< How many conversions have already been done +} GadcTimerJob; +/** @} */ + +/** + * @brief The structure passed to do a single conversion + * @{ + */ +typedef struct GadcNonTimerJob_t { + uint32_t physdev; // @< The physical device/s. The exact meaning of physdev is hardware dependent. + adcsample_t *buffer; // @< Where to put the samples. + } GadcNonTimerJob; +/** @} */ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief These routines are the callbacks that the driver uses. + * @details Defined in the high level GADC code. + * + * @notapi + * @{ + */ + /** + * @brief Indicate that some data has been placed into the buffer for the current job + * + * @param[in] n The number of samples placed in the buffer + * + * @note Calling this with n = 0 causes the current job to be terminated early or aborted. + * It can be called in this mode on an ADC conversion error. Any job will then be + * restarted by the high level code as appropriate. + */ + void gadcGotDataI(size_t n); +/** + * @} + */ + +/** + * @brief Initialise the driver + * + * @api + */ +void gadc_lld_init(void); + +/** + * @brief Using the hardware dependant "physdev", return the number of samples for each conversion + * + * @param[in] physdev The hardware dependent physical device descriptor + * + * @return The number of samples per conversion + * + * @api + */ +size_t gadc_lld_samplesperconversion(uint32_t physdev); + +/** + * @brief Start a periodic timer for high frequency conversions. + * + * @param[in] freq The frequency for the timer + * + * @note This will only be called if the timer is currently stopped. + * + * @api + * @iclass + */ +void gadc_lld_start_timerI(uint32_t freq); + +/** + * @brief Stop the periodic timer for high frequency conversions. + * + * @note This will only be called if the timer is currently running and all timer jobs + * have been completed/aborted. + * + * @api + * @iclass + */ +void gadc_lld_stop_timerI(void); + +/** + * @brief Start a set of high frequency conversions. + * + * @note This will only be called if the timer is currently running and the ADC should be ready for + * a new job. + * + * @param[in] pjob The job to be started. + * + * @api + * @iclass + */ +void gadc_lld_timerjobI(GadcTimerJob *pjob); + +/** + * @brief Start a non-timer conversion. + * + * @note This will only be called if the ADC should be ready for a new job. + * + * @param[in] pjob The job to be started + * + * @api + * @iclass + */ +void gadc_lld_nontimerjobI(GadcNonTimerJob *pjob); + +#ifdef __cplusplus +} +#endif + +#endif /* GFX_USE_GADC */ + +#endif /* _GADC_LLD_H */ +/** @} */ diff --git a/src/gadc/gadc_gadc.c b/src/gadc/gadc_gadc.c deleted file mode 100644 index 5ac8e53c..00000000 --- a/src/gadc/gadc_gadc.c +++ /dev/null @@ -1,362 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gadc/gadc_gadc.c - * @brief GADC sub-system code. - * - * @addtogroup GADC - * @{ - */ -#include "gfx.h" - -#if GFX_USE_GADC - -/* Include the driver defines */ -#include "driver.h" - -#if GADC_MAX_HIGH_SPEED_SAMPLERATE > GADC_MAX_SAMPLE_FREQUENCY/2 - #error "GADC: GADC_MAX_HIGH_SPEED_SAMPLERATE has been set too high. It must be less than half the maximum CPU rate" -#endif - -#define GADC_HSADC_GTIMER 0x8000 -#define GADC_ADC_RUNNING 0x4000 -#define GADC_HSADC_CONVERTION 0x2000 - -typedef struct NonTimerData_t { - gfxQueueGSyncItem next; - GADCCallbackFunction callback; - union { - void *param; - gfxSem sigdone; - }; - GadcNonTimerJob job; - } NonTimerData; - -static volatile uint16_t hsFlags; -static size_t hsBytesPerConv; -static GadcTimerJob hsJob; -static GDataBuffer *hsData; -static gfxQueueGSync hsListDone; -static GADCISRCallbackFunction hsISRcallback; -#if GFX_USE_GEVENT - static GTimer hsGTimer; -#endif - -static GTimer lsGTimer; -static gfxQueueGSync lsListToDo; -static gfxQueueGSync lsListDone; -static NonTimerData *lsData; - -void gadcGotDataI(size_t n) { - if ((hsFlags & GADC_HSADC_CONVERTION)) { - - // A set of timer conversions is done - add them - hsJob.done += n; - - // Are we finished yet? (or driver signalled complete now) - if (n && hsJob.done < hsJob.todo) - return; - - // Clear event flags we might set - hsFlags &= ~(GADC_HSADC_GOTBUFFER|GADC_HSADC_STALL); - - // Is there any data in it - if (!hsJob.done) { - - // Oops - no data in this buffer. Just return it to the free-list - gfxBufferReleaseI(hsData); - goto starttimerjob; // Restart the timer job - } - - // Save the buffer on the hsListDone list - hsData->len = hsJob.done * hsBytesPerConv; - gfxQueueGSyncPutI(&hsListDone, (gfxQueueGSyncItem *)hsData); - hsFlags |= GADC_HSADC_GOTBUFFER; - - /* Signal a buffer completion */ - if (hsISRcallback) - hsISRcallback(); - #if GFX_USE_GEVENT - if (hsFlags & GADC_HSADC_GTIMER) - gtimerJabI(&hsGTimer); - #endif - - // Stop if we have been told to - if (!(hsFlags & GADC_HSADC_RUNNING)) { - gadc_lld_stop_timerI(); - - // Get the next free buffer - } else if (!(hsData = gfxBufferGetI())) { - - // Oops - no free buffers. Stall - hsFlags &= ~GADC_HSADC_RUNNING; - hsFlags |= GADC_HSADC_STALL; - gadc_lld_stop_timerI(); - - // Prepare the next job - } else { - - // Return this new job - #if GFX_USE_OS_CHIBIOS - // ChibiOS api bug - samples must be even - hsJob.todo = (hsData->size / hsBytesPerConv) & ~1; - #else - hsJob.todo = hsData->size / hsBytesPerConv; - #endif - hsJob.done = 0; - hsJob.buffer = (adcsample_t *)(hsData+1); - } - - // Start a job preferring a non-timer job - if ((lsData = (NonTimerData *)gfxQueueGSyncGetI(&lsListToDo))) { - hsFlags &= ~GADC_HSADC_CONVERTION; - gadc_lld_nontimerjobI(&lsData->job); - } else if ((hsFlags & GADC_HSADC_RUNNING)) { - hsFlags |= GADC_HSADC_CONVERTION; - gadc_lld_timerjobI(&hsJob); - } else - hsFlags &= ~GADC_ADC_RUNNING; - - } else { - - // Did it fail - if (!n) { - // Push it back on the head of the queue - it didn't actually get done - gfxQueueGSyncPushI(&lsListToDo, (gfxQueueGSyncItem *)lsData); - lsData = 0; - goto starttimerjob; - } - - // A non-timer job completed - signal - if (lsData->callback) { - // Put it on the completed list and signal the timer to do the call-backs - gfxQueueGSyncPutI(&lsListDone, (gfxQueueGSyncItem *)lsData); - gtimerJabI(&lsGTimer); - } else { - // Signal the thread directly - gfxSemSignalI(&lsData->sigdone); - } - lsData = 0; - - // Start a job preferring a timer job -starttimerjob: - if ((hsFlags & GADC_HSADC_RUNNING)) { - hsFlags |= GADC_HSADC_CONVERTION; - gadc_lld_timerjobI(&hsJob); - } else if ((lsData = (NonTimerData *)gfxQueueGSyncGetI(&lsListToDo))) { - hsFlags &= ~GADC_HSADC_CONVERTION; - gadc_lld_nontimerjobI(&lsData->job); - } else - hsFlags &= ~GADC_ADC_RUNNING; - } -} - -/* Our module initialiser */ -void _gadcInit(void) -{ - gadc_lld_init(); - - gfxQueueGSyncInit(&hsListDone); - #if GFX_USE_GEVENT - gtimerInit(&hsGTimer); - #endif - gtimerInit(&lsGTimer); - gfxQueueGSyncInit(&lsListToDo); - gfxQueueGSyncInit(&lsListDone); -} - -void _gadcDeinit(void) -{ - /* commented stuff is ToDo */ - - // gadc_lld_deinit(); - gfxQueueGSyncDeinit(&hsListDone); - #if GFX_USE_GEVENT - gtimerDeinit(&hsGTimer); - #endif - gtimerDeinit(&lsGTimer); - gfxQueueGSyncDeinit(&lsListToDo); - gfxQueueGSyncDeinit(&lsListDone); -} - -#if GFX_USE_GEVENT - static void HighSpeedGTimerCallback(void *param) { - (void) param; - GSourceListener *psl; - GEventADC *pe; - - psl = 0; - while ((psl = geventGetSourceListener((GSourceHandle)(&hsGTimer), psl))) { - if (!(pe = (GEventADC *)geventGetEventBuffer(psl))) { - // This listener is missing - save this. - psl->srcflags |= GADC_HSADC_LOSTEVENT; - continue; - } - - pe->type = GEVENT_ADC; - pe->flags = (hsFlags & (GADC_HSADC_RUNNING|GADC_HSADC_GOTBUFFER|GADC_HSADC_STALL)) | psl->srcflags; - psl->srcflags = 0; - geventSendEvent(psl); - } - } -#endif - -void gadcHighSpeedInit(uint32_t physdev, uint32_t frequency) -{ - if ((hsFlags & GADC_HSADC_RUNNING)) - gadcHighSpeedStop(); - - /* Just save the details and reset everything for now */ - hsJob.physdev = physdev; - hsJob.frequency = frequency; - hsISRcallback = 0; - hsBytesPerConv = gadc_lld_samplesperconversion(physdev) * sizeof(adcsample_t); -} - -#if GFX_USE_GEVENT - GSourceHandle gadcHighSpeedGetSource(void) { - if (!gtimerIsActive(&hsGTimer)) - gtimerStart(&hsGTimer, HighSpeedGTimerCallback, 0, TRUE, TIME_INFINITE); - hsFlags |= GADC_HSADC_GTIMER; - return (GSourceHandle)&hsGTimer; - } -#endif - -void gadcHighSpeedSetISRCallback(GADCISRCallbackFunction isrfn) { - hsISRcallback = isrfn; -} - -GDataBuffer *gadcHighSpeedGetData(delaytime_t ms) { - return (GDataBuffer *)gfxQueueGSyncGet(&hsListDone, ms); -} - -GDataBuffer *gadcHighSpeedGetDataI(void) { - return (GDataBuffer *)gfxQueueGSyncGetI(&hsListDone); -} - -void gadcHighSpeedStart(void) { - // Safety first - if (!hsJob.frequency || !hsBytesPerConv) - return; - - gfxSystemLock(); - if (!(hsFlags & GADC_HSADC_RUNNING)) { - if (!(hsData = gfxBufferGetI())) { - // Oops - no free buffers. Stall - hsFlags |= GADC_HSADC_STALL; - #if GFX_USE_GEVENT - if (hsFlags & GADC_HSADC_GTIMER) - gtimerJabI(&hsGTimer); - #endif - - // Prepare the next job - } else { - - #if GFX_USE_OS_CHIBIOS - // ChibiOS api bug - samples must be even - hsJob.todo = (hsData->size / hsBytesPerConv) & ~1; - #else - hsJob.todo = hsData->size / hsBytesPerConv; - #endif - hsJob.done = 0; - hsJob.buffer = (adcsample_t *)(hsData+1); - hsFlags |= GADC_HSADC_RUNNING; - - // Start the timer - gadc_lld_start_timerI(hsJob.frequency); - - // If nothing is running start the job - if (!(hsFlags & GADC_ADC_RUNNING)) { - hsFlags |= (GADC_HSADC_CONVERTION|GADC_ADC_RUNNING); - gadc_lld_timerjobI(&hsJob); - } - } - } - gfxSystemUnlock(); -} - -void gadcHighSpeedStop(void) { - // Stop it and wait for completion - hsFlags &= ~GADC_HSADC_RUNNING; - while ((hsFlags & GADC_HSADC_CONVERTION)) - gfxYield(); -} - -static void LowSpeedGTimerCallback(void *param) { - (void) param; - NonTimerData *pdata; - - // Look for completed non-timer jobs and call the call-backs for each - while ((pdata = (NonTimerData *)gfxQueueGSyncGet(&lsListDone, TIME_IMMEDIATE))) { - pdata->callback(pdata->job.buffer, pdata->param); - gfxFree(pdata); - } -} - -void gadcLowSpeedGet(uint32_t physdev, adcsample_t *buffer) { - NonTimerData ndata; - - // Prepare the job - gfxSemInit(&ndata.sigdone, 0, 1); - ndata.job.physdev = physdev; - ndata.job.buffer = buffer; - ndata.callback = 0; - - // Activate it - gfxSystemLock(); - if (!(hsFlags & GADC_ADC_RUNNING)) { - // Nothing is running - start the job - lsData = &ndata; - hsFlags |= GADC_ADC_RUNNING; - hsFlags &= ~GADC_HSADC_CONVERTION; - gadc_lld_nontimerjobI(&ndata.job); - } else { - // Just put it on the queue - gfxQueueGSyncPutI(&lsListToDo, (gfxQueueGSyncItem *)&ndata); - } - gfxSystemUnlock(); - - // Wait for it to complete - gfxSemWait(&ndata.sigdone, TIME_INFINITE); - gfxSemDestroy(&ndata.sigdone); -} - -bool_t gadcLowSpeedStart(uint32_t physdev, adcsample_t *buffer, GADCCallbackFunction fn, void *param) { - NonTimerData *pdata; - - /* Start the Low Speed Timer */ - if (!gtimerIsActive(&lsGTimer)) - gtimerStart(&lsGTimer, LowSpeedGTimerCallback, 0, TRUE, TIME_INFINITE); - - // Prepare the job - if (!(pdata = gfxAlloc(sizeof(NonTimerData)))) - return FALSE; - pdata->job.physdev = physdev; - pdata->job.buffer = buffer; - pdata->callback = fn; - pdata->param = param; - - // Activate it - gfxSystemLock(); - if (!(hsFlags & GADC_ADC_RUNNING)) { - // Nothing is running - start the job - lsData = pdata; - hsFlags |= GADC_ADC_RUNNING; - hsFlags &= ~GADC_HSADC_CONVERTION; - gadc_lld_nontimerjobI(&pdata->job); - } else { - // Just put it on the queue - gfxQueueGSyncPutI(&lsListToDo, (gfxQueueGSyncItem *)pdata); - } - gfxSystemUnlock(); - return TRUE; -} - -#endif /* GFX_USE_GADC */ -/** @} */ - diff --git a/src/gadc/gadc_options.h b/src/gadc/gadc_options.h new file mode 100644 index 00000000..18c5038c --- /dev/null +++ b/src/gadc/gadc_options.h @@ -0,0 +1,42 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gadc/gadc_options.h + * @brief GADC - Periodic ADC subsystem options header file. + * + * @addtogroup GADC + * @{ + */ + +#ifndef _GADC_OPTIONS_H +#define _GADC_OPTIONS_H + +/** + * @name GADC Functionality to be included + * @{ + */ +/** + * @} + * + * @name GADC Optional Sizing Parameters + * @{ + */ + /** + * @brief The maximum GADC sample rate + * @details Defaults to 44000 + * @note This value must be less than half the maximum sample rate allowed by the CPU. + * This is to ensure there is time between high speed samples to perform low + * speed device sampling. + */ + #ifndef GADC_MAX_HIGH_SPEED_SAMPLERATE + #define GADC_MAX_HIGH_SPEED_SAMPLERATE 44000 + #endif +/** @} */ + +#endif /* _GADC_OPTIONS_H */ +/** @} */ diff --git a/src/gadc/gadc_rules.h b/src/gadc/gadc_rules.h new file mode 100644 index 00000000..d6c6e5ee --- /dev/null +++ b/src/gadc/gadc_rules.h @@ -0,0 +1,41 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gadc/gadc_rules.h + * @brief GADC safety rules header file. + * + * @addtogroup GADC + * @{ + */ + +#ifndef _GADC_RULES_H +#define _GADC_RULES_H + +#if GFX_USE_GADC + #if !GFX_USE_GTIMER + #if GFX_DISPLAY_RULE_WARNINGS + #warning "GADC: GFX_USE_GTIMER is required if GFX_USE_GADC is TRUE. It has been turned on for you." + #endif + #undef GFX_USE_GTIMER + #define GFX_USE_GTIMER TRUE + #endif + #if !GFX_USE_GQUEUE || !GQUEUE_NEED_GSYNC || !GQUEUE_NEED_BUFFERS + #if GFX_DISPLAY_RULE_WARNINGS + #warning "GADC: GFX_USE_GQUEUE, GQUEUE_NEED_BUFFERS and GQUEUE_NEED_GSYNC are required if GFX_USE_GADC is TRUE. They have been turned on for you." + #endif + #undef GFX_USE_GQUEUE + #define GFX_USE_GQUEUE TRUE + #undef GQUEUE_NEED_BUFFERS + #define GQUEUE_NEED_BUFFERS TRUE + #undef GQUEUE_NEED_GSYNC + #define GQUEUE_NEED_GSYNC TRUE + #endif +#endif + +#endif /* _GADC_RULES_H */ +/** @} */ diff --git a/src/gadc/sys_defs.h b/src/gadc/sys_defs.h deleted file mode 100644 index 1f40f6df..00000000 --- a/src/gadc/sys_defs.h +++ /dev/null @@ -1,252 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gadc/sys_defs.h - * - * @addtogroup GADC - * - * @brief Module to abstract the very variable ADC interfaces of the underlying systems - * - * @details The reason why ChibiOS/GFX has it's own ADC abstraction is because - * the Chibi-OS drivers are very CPU specific and do not - * provide a way across all hardware platforms to create periodic - * ADC conversions. There are also issues with devices with different - * characteristics or periodic requirements on the same ADC - * device (but different channels). This layer attempts to solve these - * problems to provide a architecture neutral API. It also provides extra - * features such as multi-buffer chaining for high speed ADC sources. - * It provides one high speed virtual ADC device (eg a microphone) and - * numerous low speed (less than 100Hz) virtual ADC devices (eg dials, - * temperature sensors etc). The high speed device has timer based polling - * to ensure exact conversion periods and a buffer management system. - * The low speed devices are assumed to be non-critical timing devices - * and do not have any buffer management. - * Note that while only one high speed device has been provided it can - * be used to read multiple physical ADC channels on the one physical - * ADC device. - * All callback routines are thread based unlike the Chibi-OS interrupt based - * routines. - * - * @{ - */ - -#ifndef _GADC_H -#define _GADC_H - -#include "gfx.h" - -#if GFX_USE_GADC || defined(__DOXYGEN__) - -/* Include the driver defines */ -#include "gadc_lld_config.h" - -/*===========================================================================*/ -/* Type definitions */ -/*===========================================================================*/ - -// Event types for GADC -#define GEVENT_ADC (GEVENT_GADC_FIRST+0) - -/** - * @brief The High Speed ADC event structure. - * @{ - */ -typedef struct GEventADC_t { - #if GFX_USE_GEVENT || defined(__DOXYGEN__) - /** - * @brief The type of this event (GEVENT_ADC) - */ - GEventType type; - #endif - - /** - * @brief The event flags - */ - uint16_t flags; - /** - * @brief The event flag values. - * @{ - */ - #define GADC_HSADC_LOSTEVENT 0x0001 /**< @brief The last GEVENT_HSDADC event was lost */ - #define GADC_HSADC_RUNNING 0x0002 /**< @brief The High Speed ADC is currently running */ - #define GADC_HSADC_GOTBUFFER 0x0004 /**< @brief A buffer is ready for processing */ - #define GADC_HSADC_STALL 0x0008 /**< @brief The High Speed ADC has stalled due to no free buffers */ - /** @} */ -} GEventADC; -/** @} */ - -/** - * @brief A callback function (executed in a thread context) for a low speed conversion - */ -typedef void (*GADCCallbackFunction)(adcsample_t *buffer, void *param); - -/** - * @brief A callback function (executed in an ISR context) for a high speed conversion - */ -typedef void (*GADCISRCallbackFunction)(void); - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Initialise the high speed ADC. - * @details Initialises but does not start the conversions. - * - * @param[in] physdev A value passed to describe which physical ADC devices/channels to use. - * @param[in] frequency The frequency to create ADC conversions - * - * @note If the high speed ADC is running it will be stopped. The Event subsystem is - * disconnected from the high speed ADC and any binary semaphore event is forgotten. - * @note ChibiOS ONLY: Due to a bug in ChibiOS each buffer on the free-list must contain an even number of - * samples and for multi-channel devices it must hold a number of samples that is evenly divisible - * by 2 times the number of active channels. - * @note The physdev parameter may be used to turn on more than one ADC channel. - * Each channel is then interleaved into the provided buffer. Make sure your buffers all hold - * a number of samples evenly divisible by the number of active channels. - * As an example, if physdev turns on 2 devices then the buffer contains - * alternate device samples and the buffer must contain multiples of 2 samples. - * The exact meaning of physdev is hardware dependent. - * @note While the high speed ADC is running, low speed conversions can only occur at - * the frequency of the high speed events. Thus if high speed events are - * being created at 50Hz (eg 100 samples/buffer, frequency = 5kHz) then the maximum - * frequency for low speed conversions will be 50Hz. - * @note Only a single sample format is supported - that provided by the GADC driver. That sample - * format applies to both high speed and low speed sampling. - * - * @api - */ -void gadcHighSpeedInit(uint32_t physdev, uint32_t frequency); - -#if GFX_USE_GEVENT || defined(__DOXYGEN__) - /** - * @brief Turn on sending results to the GEVENT sub-system. - * @details Returns a GSourceHandle to listen for GEVENT_ADC events. - * - * @note The high speed ADC will not use the GEVENT system unless this is - * called first. This saves processing time if the application does - * not want to use the GEVENT sub-system for the high speed ADC. - * Once turned on it can only be turned off by calling @p gadcHighSpeedInit() again. - * @note The high speed ADC is capable of signalling via this method, an ISR callback and a - * binary semaphore at the same time. - * - * @return The GSourceHandle - * - * @api - */ - GSourceHandle gadcHighSpeedGetSource(void); -#endif - -/** - * @brief Allow retrieving of results from the high speed ADC using an ISR callback. - * - * @param[in] isrfn The callback function (called in an ISR context). - * - * @note Passing a NULL for isrfn will turn off signalling via this method as will calling - * @p gadcHighSpeedInit(). - * @note The high speed ADC is capable of signalling via this method, a blocked thread and the GEVENT - * sub-system at the same time. - * - * @api - */ -void gadcHighSpeedSetISRCallback(GADCISRCallbackFunction isrfn); - -/** - * @brief Get a filled buffer from the ADC - * @return A GDataBuffer pointer or NULL if the timeout is exceeded - * - * @param[in] ms The maximum amount of time in milliseconds to wait for data if some is not currently available. - * - * @note After processing the data, your application must return the buffer to the free-list so that - * it can be used again. This can be done using @p gfxBufferRelease(). - * @note A buffer may be returned to the free-list before you have finished processing it provided you finish - * processing it before GADC re-uses it. This is useful when RAM usage is critical to reduce the number - * of buffers required. It works before the free list is a FIFO queue and therefore buffers are kept - * in the queue as long as possible before they are re-used. - * @note The function ending with "I" is the interrupt class function. - * @api - * @{ - */ -GDataBuffer *gadcHighSpeedGetData(delaytime_t ms); -GDataBuffer *gadcHighSpeedGetDataI(void); -/** @} */ - -/** - * @brief Start the high speed ADC conversions. - * @pre It must have been initialised first with @p gadcHighSpeedInit() - * - * @api - */ -void gadcHighSpeedStart(void); - -/** - * @brief Stop the high speed ADC conversions. - * - * @api - */ -void gadcHighSpeedStop(void); - -/** - * @brief Perform a single low speed ADC conversion - * @details Blocks until the conversion is complete - * @pre This should not be called from within a GTimer callback as this routine - * blocks until the conversion is ready. - * - * @param[in] physdev A value passed to describe which physical ADC devices/channels to use. - * @param[in] buffer The static buffer to put the ADC samples into. - * - * @note This may take a while to complete if the high speed ADC is running as the - * conversion is interleaved with the high speed ADC conversions on a buffer - * completion. - * @note The result buffer must be large enough to store one sample per device - * described by the 'physdev' parameter. - * @note Specifying more than one device in physdev is possible but discouraged as the - * calculations to ensure the high speed ADC correctness will be incorrect. Symptoms - * from over-running the high speed ADC include high speed device stalling or samples being lost. - * - * @api - */ -void gadcLowSpeedGet(uint32_t physdev, adcsample_t *buffer); - -/** - * @brief Perform a low speed ADC conversion with callback (in a thread context) - * @details Returns FALSE if internal memory allocation fails - * - * @param[in] physdev A value passed to describe which physical ADC devices/channels to use. - * @param[in] buffer The static buffer to put the ADC samples into. - * @param[in] fn The callback function to call when the conversion is complete. - * @param[in] param A parameter to pass to the callback function. - * - * @return FALSE if no free low speed ADC slots. - * - * @note This may be safely called from within a GTimer callback. - * @note The callback may take a while to occur if the high speed ADC is running as the - * conversion is interleaved with the high speed ADC conversions on a buffer - * completion. - * @note The result buffer must be large enough to store one sample per device - * described by the 'physdev' parameter. - * @note Specifying more than one device in physdev is possible but discouraged as the - * calculations to ensure the high speed ADC correctness will be incorrect. Symptoms - * from over-running the high speed ADC include high speed samples being lost. - * - * @api - */ -bool_t gadcLowSpeedStart(uint32_t physdev, adcsample_t *buffer, GADCCallbackFunction fn, void *param); - -#ifdef __cplusplus -} -#endif - -#endif /* GFX_USE_GADC */ - -#endif /* _GADC_H */ -/** @} */ diff --git a/src/gadc/sys_make.mk b/src/gadc/sys_make.mk deleted file mode 100644 index 21a07dbe..00000000 --- a/src/gadc/sys_make.mk +++ /dev/null @@ -1 +0,0 @@ -GFXSRC += $(GFXLIB)/src/gadc/gadc_gadc.c diff --git a/src/gadc/sys_options.h b/src/gadc/sys_options.h deleted file mode 100644 index d9519c39..00000000 --- a/src/gadc/sys_options.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gadc/sys_options.h - * @brief GADC - Periodic ADC subsystem options header file. - * - * @addtogroup GADC - * @{ - */ - -#ifndef _GADC_OPTIONS_H -#define _GADC_OPTIONS_H - -/** - * @name GADC Functionality to be included - * @{ - */ -/** - * @} - * - * @name GADC Optional Sizing Parameters - * @{ - */ - /** - * @brief The maximum GADC sample rate - * @details Defaults to 44000 - * @note This value must be less than half the maximum sample rate allowed by the CPU. - * This is to ensure there is time between high speed samples to perform low - * speed device sampling. - */ - #ifndef GADC_MAX_HIGH_SPEED_SAMPLERATE - #define GADC_MAX_HIGH_SPEED_SAMPLERATE 44000 - #endif -/** @} */ - -#endif /* _GADC_OPTIONS_H */ -/** @} */ diff --git a/src/gadc/sys_rules.h b/src/gadc/sys_rules.h deleted file mode 100644 index 363b2434..00000000 --- a/src/gadc/sys_rules.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gadc/sys_rules.h - * @brief GADC safety rules header file. - * - * @addtogroup GADC - * @{ - */ - -#ifndef _GADC_RULES_H -#define _GADC_RULES_H - -#if GFX_USE_GADC - #if !GFX_USE_GTIMER - #if GFX_DISPLAY_RULE_WARNINGS - #warning "GADC: GFX_USE_GTIMER is required if GFX_USE_GADC is TRUE. It has been turned on for you." - #endif - #undef GFX_USE_GTIMER - #define GFX_USE_GTIMER TRUE - #endif - #if !GFX_USE_GQUEUE || !GQUEUE_NEED_GSYNC || !GQUEUE_NEED_BUFFERS - #if GFX_DISPLAY_RULE_WARNINGS - #warning "GADC: GFX_USE_GQUEUE, GQUEUE_NEED_BUFFERS and GQUEUE_NEED_GSYNC are required if GFX_USE_GADC is TRUE. They have been turned on for you." - #endif - #undef GFX_USE_GQUEUE - #define GFX_USE_GQUEUE TRUE - #undef GQUEUE_NEED_BUFFERS - #define GQUEUE_NEED_BUFFERS TRUE - #undef GQUEUE_NEED_GSYNC - #define GQUEUE_NEED_GSYNC TRUE - #endif -#endif - -#endif /* _GADC_RULES_H */ -/** @} */ diff --git a/src/gaudio/driver_play.h b/src/gaudio/driver_play.h deleted file mode 100644 index 343a0fed..00000000 --- a/src/gaudio/driver_play.h +++ /dev/null @@ -1,126 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gaudio/driver_play.h - * @brief GAUDIO - Audio play driver header file. - * - * @defgroup Driver Driver - * @ingroup GAUDIO - * @{ - */ - -#ifndef _GAUDIO_PLAY_LLD_H -#define _GAUDIO_PLAY_LLD_H - -#include "gfx.h" - -#if (GFX_USE_GAUDIO && GAUDIO_NEED_PLAY) || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Type definitions */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Get a block of audio data to play - * @return A pointer to the GAaudioData structure or NULL if none is currently available - * - * @note Defined in the high level GAUDIO code for use by the GAUDIO play drivers. - * - * @iclass - * @notapi - */ -GDataBuffer *gaudioPlayGetDataBlockI(void); - -/** - * @brief Release a block of audio data to the free list - * - * @param[in] paud The GDataBuffer block to be released. - * - * @note Defined in the high level GAUDIO code for use by the GAUDIO play drivers. - * - * @iclass - * @notapi - */ -void gaudioPlayReleaseDataBlockI(GDataBuffer *paud); - -/** - * @brief Signal that all playing has now stopped - * - * @note Defined in the high level GAUDIO code for use by the GAUDIO play drivers. - * - * @iclass - * @notapi - */ -void gaudioPlayDoneI(void); - -/** - * @brief Initialise the play driver - * @return TRUE if the channel, frequency and format are valid. - * - * @param[in] channel The channel to use (see the driver for the available channels provided) - * @param[in] frequency The sample frequency to use - * @param[in] format The sample format - * - * @note The driver will always have been stopped and de-init before this is called. - * - * @api - */ -bool_t gaudio_play_lld_init(uint16_t channel, uint32_t frequency, ArrayDataFormat format); - -/** - * @brief Start the audio output playing - * - * @note This may be called at any stage including while the driver - * is already playing. The driver should check for data blocks - * to play using @p gaudioPlayGetDataBlockI(). - * - * @api - */ -void gaudio_play_lld_start(void); - -/** - * @brief Stop the audio output playing. - * - * @note Some drivers may only stop playing at a data block boundary. - * @note It is possible but unlikely for it to be called when playing has already stopped. - * @note It should not return until all active buffers (currently in use by the driver) - * have been returned to the free-list and @p gaudioPlayDoneI() has been called. - * - * @api - */ -void gaudio_play_lld_stop(void); - -/** - * @brief Set the output volume. - * @return TRUE if successful. - * - * @param[in] vol 0->255 (0 = muted) - * - * @note Some drivers may not support this. They will return FALSE. - * @note For stereo devices, both channels are set to the same volume. - * - * @api - */ -bool_t gaudio_play_lld_set_volume(uint8_t vol); - -#ifdef __cplusplus -} -#endif - -#endif /* GFX_USE_GAUDIO && GAUDIO_NEED_PLAY */ - -#endif /* _GAUDIO_PLAY_LLD_H */ -/** @} */ diff --git a/src/gaudio/driver_record.h b/src/gaudio/driver_record.h deleted file mode 100644 index 20136dd7..00000000 --- a/src/gaudio/driver_record.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gaudio/driver_record.h - * @brief GAUDIO - Audio Recording driver header file. - * - * @defgroup Driver Driver - * @ingroup GAUDIO - * @{ - */ - -#ifndef _GAUDIO_RECORD_LLD_H -#define _GAUDIO_RECORD_LLD_H - -#include "gfx.h" - -#if (GFX_USE_GAUDIO && GAUDIO_NEED_RECORD) || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Type definitions */ -/*===========================================================================*/ - -/** - * @brief Get a free block of audio data that we can record into - * @return A pointer to the GAaudioData structure or NULL if none is currently available - * - * @note Defined in the high level GAUDIO code for use by the GAUDIO record drivers. - * - * @iclass - * @notapi - */ -#define gaudioRecordGetFreeBlockI() gfxBufferGetI() - -/** - * @brief Save a block of recorded audio data ready for the application - * - * @param[in] paud The GDataBuffer block with data. - * - * @note Defined in the high level GAUDIO code for use by the GAUDIO record drivers. - * - * @iclass - * @notapi - */ -void gaudioRecordSaveDataBlockI(GDataBuffer *paud); - -/** - * @brief Signal that all recording has now stopped - * - * @note Defined in the high level GAUDIO code for use by the GAUDIO record drivers. - * - * @iclass - * @notapi - */ -void gaudioRecordDoneI(void); - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Initialise the record driver - * @return TRUE if the channel, frequency and format are valid. - * - * @param[in] channel The channel to use (see the driver for the available channels provided) - * @param[in] frequency The sample frequency to use - * @param[in] format The sample format - * - * @note The driver will always have been stopped and de-init before this is called. - * - * @api - */ -bool_t gaudio_record_lld_init(uint16_t channel, uint32_t frequency, ArrayDataFormat format); - -/** - * @brief Start the audio recording - * - * @api - */ -void gaudio_record_lld_start(void); - -/** - * @brief Stop the audio recording. - * - * @note Some drivers may only stop recording at a data block boundary. - * @note This routine should not return until any currently active buffers have been - * saved (even if with zero length) and @p gaudioRecordDoneI() has been called. - * - * @api - */ -void gaudio_record_lld_stop(void); - -#ifdef __cplusplus -} -#endif - -#endif /* GFX_USE_GAUDIO && GAUDIO_NEED_RECORD */ - -#endif /* _GAUDIO_RECORD_LLD_H */ -/** @} */ diff --git a/src/gaudio/gaudio.c b/src/gaudio/gaudio.c new file mode 100644 index 00000000..0abc9382 --- /dev/null +++ b/src/gaudio/gaudio.c @@ -0,0 +1,275 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +#include "gfx.h" + +#if GFX_USE_GAUDIO + +#if GAUDIO_NEED_PLAY + #include "gaudio_driver_play.h" + + static gfxQueueASync playList; + static gfxSem playComplete; + static uint16_t playFlags; + #define PLAYFLG_USEEVENTS 0x0001 + #define PLAYFLG_PLAYING 0x0002 + #define PLAYFLG_ISINIT 0x0004 + #if GFX_USE_GEVENT + static GTimer playTimer; + static void PlayTimerCallback(void *param); + #endif +#endif + +#if GAUDIO_NEED_RECORD + #include "gaudio_driver_record.h" + + static gfxQueueGSync recordList; + static uint16_t recordFlags; + #define RECORDFLG_USEEVENTS 0x0001 + #define RECORDFLG_RECORDING 0x0002 + #define RECORDFLG_STALLED 0x0004 + #define RECORDFLG_ISINIT 0x0008 + #if GFX_USE_GEVENT + static GTimer recordTimer; + static void RecordTimerCallback(void *param); + #endif +#endif + + +void _gaudioInit(void) +{ + #if GAUDIO_NEED_PLAY + gfxQueueASyncInit(&playList); + #if GFX_USE_GEVENT + gtimerInit(&playTimer); + #endif + gfxSemInit(&playComplete, 0, 0); + #endif + #if GAUDIO_NEED_RECORD + gfxQueueGSyncInit(&recordList); + #if GFX_USE_GEVENT + gtimerInit(&recordTimer); + #endif + #endif +} + +void _gaudioDeinit(void) +{ + #if GAUDIO_NEED_PLAY + gfxQueueASyncDeinit(&playList); + #if GFX_USE_GEVENT + gtimerDeinit(&playTimer); + #endif + gfxSemDestroy(&playComplete); + #endif + #if GAUDIO_NEED_RECORD + gfxQueueGSyncDeinit(&recordList); + #if GFX_USE_GEVENT + gtimerDeinit(&recordTimer); + #endif + #endif +} + +#if GAUDIO_NEED_PLAY + + bool_t gaudioPlayInit(uint16_t channel, uint32_t frequency, ArrayDataFormat format) { + gaudioPlayStop(); + playFlags &= ~PLAYFLG_ISINIT; + if (!gaudio_play_lld_init(channel, frequency, format)) + return FALSE; + playFlags |= PLAYFLG_ISINIT; + return TRUE; + } + + void gaudioPlay(GDataBuffer *pd) { + if (!(playFlags & PLAYFLG_ISINIT)) { + // Oops - init failed - return it directly to the free-list + if (pd) { + gfxBufferRelease(pd); + gfxYield(); // Make sure we get no endless cpu hogging loops + } + return; + } + + if (pd) + gfxQueueASyncPut(&playList, (gfxQueueASyncItem *)pd); + playFlags |= PLAYFLG_PLAYING; + gaudio_play_lld_start(); + } + + void gaudioPlayPause(void) { + if ((playFlags & (PLAYFLG_ISINIT|PLAYFLG_PLAYING)) == (PLAYFLG_ISINIT|PLAYFLG_PLAYING)) + gaudio_play_lld_stop(); + } + + void gaudioPlayStop(void) { + GDataBuffer *pd; + + if (playFlags & PLAYFLG_PLAYING) + gaudio_play_lld_stop(); + while((pd = (GDataBuffer *)gfxQueueASyncGet(&playList))) + gfxBufferRelease(pd); + } + + bool_t gaudioPlaySetVolume(uint8_t vol) { + return gaudio_play_lld_set_volume(vol); + } + + bool_t gaudioPlayWait(delaytime_t ms) { + if (!(playFlags & PLAYFLG_PLAYING)) + return TRUE; + return gfxSemWait(&playComplete, ms); + } + + #if GFX_USE_GEVENT + static void PlayTimerCallback(void *param) { + (void) param; + GSourceListener *psl; + GEventAudioPlay *pe; + + psl = 0; + while ((psl = geventGetSourceListener((GSourceHandle)&playTimer, psl))) { + if (!(pe = (GEventAudioPlay *)geventGetEventBuffer(psl))) { + // This listener is missing - save this. + psl->srcflags |= GAUDIO_PLAY_LOSTEVENT; + continue; + } + + pe->type = GEVENT_AUDIO_PLAY; + pe->flags = psl->srcflags; + psl->srcflags = 0; + if ((playFlags & PLAYFLG_PLAYING)) + pe->flags |= GAUDIO_PLAY_PLAYING; + if (gfxBufferIsAvailable()) + pe->flags |= GAUDIO_PLAY_FREEBLOCK; + geventSendEvent(psl); + } + } + + GSourceHandle gaudioPlayGetSource(void) { + if (!gtimerIsActive(&playTimer)) + gtimerStart(&playTimer, PlayTimerCallback, 0, TRUE, TIME_INFINITE); + playFlags |= PLAYFLG_USEEVENTS; + return (GSourceHandle)&playTimer; + } + #endif + + /** + * Routines provided for use by drivers. + */ + + GDataBuffer *gaudioPlayGetDataBlockI(void) { + return (GDataBuffer *)gfxQueueASyncGetI(&playList); + } + + void gaudioPlayReleaseDataBlockI(GDataBuffer *pd) { + gfxBufferReleaseI(pd); + #if GFX_USE_GEVENT + if (playFlags & PLAYFLG_USEEVENTS) + gtimerJabI(&playTimer); + #endif + } + + void gaudioPlayDoneI(void) { + playFlags &= ~PLAYFLG_PLAYING; + #if GFX_USE_GEVENT + if (playFlags & PLAYFLG_USEEVENTS) + gtimerJabI(&playTimer); + #endif + gfxSemSignalI(&playComplete); // This should really be gfxSemSignalAllI(&playComplete); + } +#endif + +#if GAUDIO_NEED_RECORD + bool_t gaudioRecordInit(uint16_t channel, uint32_t frequency, ArrayDataFormat format) { + gaudioRecordStop(); + recordFlags &= ~RECORDFLG_ISINIT; + if (!gaudio_record_lld_init(channel, frequency, format)) + return FALSE; + recordFlags |= RECORDFLG_ISINIT; + return TRUE; + } + + void gaudioRecordStart(void) { + if (!(recordFlags & RECORDFLG_ISINIT)) + return; // Oops - init failed + + recordFlags |= RECORDFLG_RECORDING; + recordFlags &= ~RECORDFLG_STALLED; + gaudio_record_lld_start(); + } + + void gaudioRecordStop(void) { + GDataBuffer *pd; + + if ((recordFlags & (RECORDFLG_RECORDING|RECORDFLG_STALLED)) == RECORDFLG_RECORDING) + gaudio_record_lld_stop(); + recordFlags &= ~(RECORDFLG_RECORDING|RECORDFLG_STALLED); + while((pd = (GDataBuffer *)gfxQueueGSyncGet(&recordList, TIME_IMMEDIATE))) + gfxBufferRelease(pd); + } + + GDataBuffer *gaudioRecordGetData(delaytime_t ms) { + return (GDataBuffer *)gfxQueueGSyncGet(&recordList, ms); + } + + #if GFX_USE_GEVENT + static void RecordTimerCallback(void *param) { + (void) param; + GSourceListener *psl; + GEventAudioRecord *pe; + + psl = 0; + while ((psl = geventGetSourceListener((GSourceHandle)&recordTimer, psl))) { + if (!(pe = (GEventAudioRecord *)geventGetEventBuffer(psl))) { + // This listener is missing - save this. + psl->srcflags |= GAUDIO_RECORD_LOSTEVENT; + continue; + } + pe->type = GEVENT_AUDIO_RECORD; + pe->flags = psl->srcflags; + psl->srcflags = 0; + if ((recordFlags & RECORDFLG_RECORDING)) + pe->flags |= GAUDIO_RECORD_RECORDING; + if ((recordFlags & RECORDFLG_STALLED)) + pe->flags |= GAUDIO_RECORD_STALL; + if (!gfxQueueGSyncIsEmpty(&recordList)) + pe->flags |= GAUDIO_RECORD_GOTBUFFER; + geventSendEvent(psl); + } + } + + GSourceHandle gaudioRecordGetSource(void) { + if (!gtimerIsActive(&recordTimer)) + gtimerStart(&recordTimer, RecordTimerCallback, 0, TRUE, TIME_INFINITE); + recordFlags |= RECORDFLG_USEEVENTS; + return (GSourceHandle)&recordTimer; + } + #endif + + /** + * Routines provided for use by drivers. + */ + + void gaudioRecordSaveDataBlockI(GDataBuffer *paud) { + gfxQueueGSyncPutI(&recordList, (gfxQueueGSyncItem *)paud); + #if GFX_USE_GEVENT + if (recordFlags & RECORDFLG_USEEVENTS) + gtimerJabI(&recordTimer); + #endif + } + + void gaudioRecordDoneI(void) { + recordFlags |= RECORDFLG_STALLED; + #if GFX_USE_GEVENT + if (recordFlags & RECORDFLG_USEEVENTS) + gtimerJabI(&recordTimer); + #endif + } +#endif + +#endif /* GFX_USE_GAUDIO */ diff --git a/src/gaudio/gaudio.h b/src/gaudio/gaudio.h new file mode 100644 index 00000000..23403b15 --- /dev/null +++ b/src/gaudio/gaudio.h @@ -0,0 +1,297 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gaudio/gaudio.h + * + * @addtogroup GAUDIO + * + * @brief Module to handle audio recording and play-back + * + * @{ + */ + +#ifndef _GAUDIO_H +#define _GAUDIO_H + +#include "gfx.h" + +#if GFX_USE_GAUDIO || defined(__DOXYGEN__) + +/* Include the driver defines */ +#if GAUDIO_NEED_PLAY + #include "gaudio_play_config.h" +#endif +#if GAUDIO_NEED_RECORD + #include "gaudio_record_config.h" +#endif + +/*===========================================================================*/ +/* Type definitions */ +/*===========================================================================*/ + +// Event types for GAUDIO +#define GEVENT_AUDIO_PLAY (GEVENT_GAUDIO_FIRST+0) +#define GEVENT_AUDIO_RECORD (GEVENT_GAUDIO_FIRST+1) + +#if GFX_USE_GEVENT || defined(__DOXYGEN__) + /** + * @brief The Audio play event structure. + * @{ + */ + typedef struct GEventAudioPlay_t { + /** + * @brief The type of this event (GEVENT_AUDIO_PLAY) + */ + GEventType type; + /** + * @brief The event flags + */ + uint16_t flags; + /** + * @brief The event flag values. + * @{ + */ + #define GAUDIO_PLAY_LOSTEVENT 0x0001 /**< @brief The last GEVENT_AUDIO_PLAY event was lost */ + #define GAUDIO_PLAY_PLAYING 0x0002 /**< @brief The audio out system is currently playing */ + #define GAUDIO_PLAY_FREEBLOCK 0x0004 /**< @brief An audio buffer has been freed */ + /** @} */ + } GEventAudioPlay; + /** @} */ + + /** + * @brief The Audio record event structure. + * @{ + */ + typedef struct GEventAudioRecord_t { + /** + * @brief The type of this event (GEVENT_AUDIO_RECORD) + */ + GEventType type; + /** + * @brief The event flags + */ + uint16_t flags; + /** + * @brief The event flag values. + * @{ + */ + #define GAUDIO_RECORD_LOSTEVENT 0x0001 /**< @brief The last GEVENT_AUDIO_IN event was lost */ + #define GAUDIO_RECORD_RECORDING 0x0002 /**< @brief The audio recording system is currently recording */ + #define GAUDIO_RECORD_GOTBUFFER 0x0004 /**< @brief An audio buffer is ready for processing */ + #define GAUDIO_RECORD_STALL 0x0008 /**< @brief The recording process has stalled due to no free buffers */ + /** @} */ + } GEventAudioRecord; + /** @} */ +#endif + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#if GAUDIO_NEED_PLAY || defined(__DOXYGEN__) + /** + * @brief Set the audio device to play on the specified channel and with the specified + * sample frequency. + * @return TRUE is successful, FALSE if the driver doesn't accept those parameters. + * + * @param[in] channel The audio output channel to use. Can be set from 0 to GAUDIO_PLAY_NUM_CHANNELS - 1 + * @param[in] frequency The audio sample rate in samples per second + * @param[in] format The audio sample format + * + * @note Some channels are mono, and some are stereo. See your driver config file + * to determine which channels to use and whether they are stereo or not. + * @note Only one channel can be playing at a time. Calling this will stop any + * currently playing channel. + * + * @api + */ + bool_t gaudioPlayInit(uint16_t channel, uint32_t frequency, ArrayDataFormat format); + + /** + * @brief Play the specified sample data. + * @details The sample data is output to the audio channel. On completion the buffer is returned to the free-list. + * @pre @p gaudioPlayInit must have been called first to set the channel and sample frequency. + * + * @param[in] paud The audio sample buffer to play. It can be NULL (used to restart paused audio) + * + * @note Calling this will cancel any pause. + * @note Before calling this function the len field of the GDataBuffer structure must be + * specified (in bytes). + * @note For stereo channels the sample data is interleaved in the buffer. + * @note This call returns before the data has completed playing. Subject to available buffers (which + * can be obtained from the free-list), any number of buffers may be played. They will be queued + * for playing in the order they are supplied to this routine and played when previous buffers are + * complete. In this way continuous playing can be obtained without audio gaps. + * + * @api + */ + void gaudioPlay(GDataBuffer *paud); + + /** + * @brief Pause any currently playing sounds. + * + * @note If nothing is currently playing this routine does nothing. To restart playing call @p gaudioPlay() + * with or without a new sample buffer. + * @note Some drivers will not respond until a buffer boundary. + * + * @api + */ + void gaudioPlayPause(void); + + /** + * @brief Stop any currently playing sounds. + * + * @note This stops any playing sounds and returns any currently queued buffers back to the free-list. + * @note Some drivers will not respond until a buffer boundary. + * + * @api + */ + void gaudioPlayStop(void); + + /** + * @brief Set the output volume. + * @return TRUE if successful. + * + * @param[in] vol 0->255 (0 = muted) + * + * @note Some drivers may not support this. They will return FALSE. + * @note For stereo devices, both channels are set to the same volume. + * + * @api + */ + bool_t gaudioPlaySetVolume(uint8_t vol); + + #if GFX_USE_GEVENT || defined(__DOXYGEN__) + /** + * @brief Turn on sending results to the GEVENT sub-system. + * @details Returns a GSourceHandle to listen for GEVENT_AUDIO_OUT events. + * + * @note The audio output will not use the GEVENT system unless this is + * called first. This saves processing time if the application does + * not want to use the GEVENT sub-system for audio output. + * Once turned on it can only be turned off by calling @p gaudioPlayInit() again. + * @note The audio output is capable of signaling via this method and other methods + * at the same time. + * + * @return The GSourceHandle + * + * @api + */ + GSourceHandle gaudioPlayGetSource(void); + #endif + + /** + * @brief Wait for any currently playing sounds to complete + * @return TRUE if there is now nothing playing or FALSE if the timeout is exceeded + * + * @param[in] ms The maximum amount of time in milliseconds to wait for playing to complete. + * + * @api + */ + bool_t gaudioPlayWait(delaytime_t ms); +#endif + +#if GAUDIO_NEED_RECORD || defined(__DOXYGEN__) + /** + * @brief Initialise (but not start) the Audio Recording sub-system. + * @details Returns FALSE for an invalid channel or other invalid parameter. + * + * @param[in] channel The channel to convert. Can be set from 0 to GAUDIO_RECORD_NUM_CHANNELS - 1 + * @param[in] frequency The sample frequency + * @param[in] format The audio sample format requested + * + * @note Only one channel is active at a time. If an audio input is running it will be stopped. + * The Event subsystem is disconnected from the audio subsystem and any binary semaphore + * event is forgotten. + * @note Some channels may be stereo channels which return twice as much sample data with + * the left and right channel data interleaved. Other channels may be mono channels. + * Where stereo channels exist the low level driver may also + * offer the left and right channels separately. + * @note Due to a bug in Chibi-OS each buffer on the free-list must contain an even number of + * samples and for stereo devices it must hold a number of samples that is evenly divisible by 4. + * This requirement applies only to ChibiOS where the audio driver uses + * a ChibiOS hal driver like the cpu ADC driver. This applies even it is used indirectly via + * the uGFX GADC driver. + * @note The number of samples for stereo devices will be double the number of conversions. + * Make sure you allocate your buffers large enough. Each channel is then interleaved + * into the provided buffer. + * + * @return FALSE if invalid channel or parameter + * + * @api + */ + bool_t gaudioRecordInit(uint16_t channel, uint32_t frequency, ArrayDataFormat format); + + /** + * @brief Start the audio recording. + * @pre It must have been initialised first with @p gaudioRecordInit() + * + * @api + */ + void gaudioRecordStart(void); + + /** + * @brief Stop the audio recording. + * + * @note All audio recording data that has not yet been retrieved is automatically + * returned to the free-list. + * @api + */ + void gaudioRecordStop(void); + + /** + * @brief Get a filled audio buffer from the recording list + * @return A GDataBuffer pointer or NULL if the timeout is exceeded + * + * @param[in] ms The maximum amount of time in milliseconds to wait for data if some is not currently available. + * + * @note After processing the audio data, your application must return the buffer to the free-list so that + * it can be used to record more audio into. This can be done via the play list using @p gaudioPlay() or + * directly using @p gfxBufferRelease(). + * @note A buffer may be returned to the free-list before you have finished processing it provided you finish + * processing it before GADC re-uses it. This is useful when RAM usage is critical to reduce the number + * of buffers required. It works before the free list is a FIFO queue and therefore buffers are kept + * in the queue as long as possible before they are re-used. + * + * @api + */ + GDataBuffer *gaudioRecordGetData(delaytime_t ms); + + #if GFX_USE_GEVENT || defined(__DOXYGEN__) + /** + * @brief Turn on sending results to the GEVENT sub-system. + * @details Returns a GSourceHandle to listen for GEVENT_AUDIO_RECORD events. + * + * @note Audio recording will not use the GEVENT system unless this is + * called first. This saves processing time if the application does + * not want to use the GEVENT sub-system for audio recording. + * Once turned on it can only be turned off by calling @p gaudioRecordInit() again. + * @note The audio input is capable of signaling via this and other methods + * at the same time. + * + * @return The GSourceHandle + * + * @api + */ + GSourceHandle gaudioRecordGetSource(void); + #endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* GFX_USE_GAUDIO */ + +#endif /* _GAUDIO_H */ +/** @} */ + diff --git a/src/gaudio/gaudio.mk b/src/gaudio/gaudio.mk new file mode 100644 index 00000000..438892c0 --- /dev/null +++ b/src/gaudio/gaudio.mk @@ -0,0 +1 @@ +GFXSRC += $(GFXLIB)/src/gaudio/gaudio.c diff --git a/src/gaudio/gaudio_driver_play.h b/src/gaudio/gaudio_driver_play.h new file mode 100644 index 00000000..fced1210 --- /dev/null +++ b/src/gaudio/gaudio_driver_play.h @@ -0,0 +1,126 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gaudio/gaudio_driver_play.h + * @brief GAUDIO - Audio play driver header file. + * + * @defgroup Driver Driver + * @ingroup GAUDIO + * @{ + */ + +#ifndef _GAUDIO_PLAY_LLD_H +#define _GAUDIO_PLAY_LLD_H + +#include "gfx.h" + +#if (GFX_USE_GAUDIO && GAUDIO_NEED_PLAY) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Type definitions */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Get a block of audio data to play + * @return A pointer to the GAaudioData structure or NULL if none is currently available + * + * @note Defined in the high level GAUDIO code for use by the GAUDIO play drivers. + * + * @iclass + * @notapi + */ +GDataBuffer *gaudioPlayGetDataBlockI(void); + +/** + * @brief Release a block of audio data to the free list + * + * @param[in] paud The GDataBuffer block to be released. + * + * @note Defined in the high level GAUDIO code for use by the GAUDIO play drivers. + * + * @iclass + * @notapi + */ +void gaudioPlayReleaseDataBlockI(GDataBuffer *paud); + +/** + * @brief Signal that all playing has now stopped + * + * @note Defined in the high level GAUDIO code for use by the GAUDIO play drivers. + * + * @iclass + * @notapi + */ +void gaudioPlayDoneI(void); + +/** + * @brief Initialise the play driver + * @return TRUE if the channel, frequency and format are valid. + * + * @param[in] channel The channel to use (see the driver for the available channels provided) + * @param[in] frequency The sample frequency to use + * @param[in] format The sample format + * + * @note The driver will always have been stopped and de-init before this is called. + * + * @api + */ +bool_t gaudio_play_lld_init(uint16_t channel, uint32_t frequency, ArrayDataFormat format); + +/** + * @brief Start the audio output playing + * + * @note This may be called at any stage including while the driver + * is already playing. The driver should check for data blocks + * to play using @p gaudioPlayGetDataBlockI(). + * + * @api + */ +void gaudio_play_lld_start(void); + +/** + * @brief Stop the audio output playing. + * + * @note Some drivers may only stop playing at a data block boundary. + * @note It is possible but unlikely for it to be called when playing has already stopped. + * @note It should not return until all active buffers (currently in use by the driver) + * have been returned to the free-list and @p gaudioPlayDoneI() has been called. + * + * @api + */ +void gaudio_play_lld_stop(void); + +/** + * @brief Set the output volume. + * @return TRUE if successful. + * + * @param[in] vol 0->255 (0 = muted) + * + * @note Some drivers may not support this. They will return FALSE. + * @note For stereo devices, both channels are set to the same volume. + * + * @api + */ +bool_t gaudio_play_lld_set_volume(uint8_t vol); + +#ifdef __cplusplus +} +#endif + +#endif /* GFX_USE_GAUDIO && GAUDIO_NEED_PLAY */ + +#endif /* _GAUDIO_PLAY_LLD_H */ +/** @} */ diff --git a/src/gaudio/gaudio_driver_record.h b/src/gaudio/gaudio_driver_record.h new file mode 100644 index 00000000..280be5d1 --- /dev/null +++ b/src/gaudio/gaudio_driver_record.h @@ -0,0 +1,108 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gaudio/gaudio_driver_record.h + * @brief GAUDIO - Audio Recording driver header file. + * + * @defgroup Driver Driver + * @ingroup GAUDIO + * @{ + */ + +#ifndef _GAUDIO_RECORD_LLD_H +#define _GAUDIO_RECORD_LLD_H + +#include "gfx.h" + +#if (GFX_USE_GAUDIO && GAUDIO_NEED_RECORD) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Type definitions */ +/*===========================================================================*/ + +/** + * @brief Get a free block of audio data that we can record into + * @return A pointer to the GAaudioData structure or NULL if none is currently available + * + * @note Defined in the high level GAUDIO code for use by the GAUDIO record drivers. + * + * @iclass + * @notapi + */ +#define gaudioRecordGetFreeBlockI() gfxBufferGetI() + +/** + * @brief Save a block of recorded audio data ready for the application + * + * @param[in] paud The GDataBuffer block with data. + * + * @note Defined in the high level GAUDIO code for use by the GAUDIO record drivers. + * + * @iclass + * @notapi + */ +void gaudioRecordSaveDataBlockI(GDataBuffer *paud); + +/** + * @brief Signal that all recording has now stopped + * + * @note Defined in the high level GAUDIO code for use by the GAUDIO record drivers. + * + * @iclass + * @notapi + */ +void gaudioRecordDoneI(void); + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initialise the record driver + * @return TRUE if the channel, frequency and format are valid. + * + * @param[in] channel The channel to use (see the driver for the available channels provided) + * @param[in] frequency The sample frequency to use + * @param[in] format The sample format + * + * @note The driver will always have been stopped and de-init before this is called. + * + * @api + */ +bool_t gaudio_record_lld_init(uint16_t channel, uint32_t frequency, ArrayDataFormat format); + +/** + * @brief Start the audio recording + * + * @api + */ +void gaudio_record_lld_start(void); + +/** + * @brief Stop the audio recording. + * + * @note Some drivers may only stop recording at a data block boundary. + * @note This routine should not return until any currently active buffers have been + * saved (even if with zero length) and @p gaudioRecordDoneI() has been called. + * + * @api + */ +void gaudio_record_lld_stop(void); + +#ifdef __cplusplus +} +#endif + +#endif /* GFX_USE_GAUDIO && GAUDIO_NEED_RECORD */ + +#endif /* _GAUDIO_RECORD_LLD_H */ +/** @} */ diff --git a/src/gaudio/gaudio_gaudio.c b/src/gaudio/gaudio_gaudio.c deleted file mode 100644 index bb12e6d2..00000000 --- a/src/gaudio/gaudio_gaudio.c +++ /dev/null @@ -1,283 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gaudio/gaudio_gaudio.c - * @brief GAUDIO sub-system code. - * - * @addtogroup GAUDIO - * @{ - */ -#include "gfx.h" - -#if GFX_USE_GAUDIO - -#if GAUDIO_NEED_PLAY - #include "driver_play.h" - - static gfxQueueASync playList; - static gfxSem playComplete; - static uint16_t playFlags; - #define PLAYFLG_USEEVENTS 0x0001 - #define PLAYFLG_PLAYING 0x0002 - #define PLAYFLG_ISINIT 0x0004 - #if GFX_USE_GEVENT - static GTimer playTimer; - static void PlayTimerCallback(void *param); - #endif -#endif - -#if GAUDIO_NEED_RECORD - #include "driver_record.h" - - static gfxQueueGSync recordList; - static uint16_t recordFlags; - #define RECORDFLG_USEEVENTS 0x0001 - #define RECORDFLG_RECORDING 0x0002 - #define RECORDFLG_STALLED 0x0004 - #define RECORDFLG_ISINIT 0x0008 - #if GFX_USE_GEVENT - static GTimer recordTimer; - static void RecordTimerCallback(void *param); - #endif -#endif - - -void _gaudioInit(void) -{ - #if GAUDIO_NEED_PLAY - gfxQueueASyncInit(&playList); - #if GFX_USE_GEVENT - gtimerInit(&playTimer); - #endif - gfxSemInit(&playComplete, 0, 0); - #endif - #if GAUDIO_NEED_RECORD - gfxQueueGSyncInit(&recordList); - #if GFX_USE_GEVENT - gtimerInit(&recordTimer); - #endif - #endif -} - -void _gaudioDeinit(void) -{ - #if GAUDIO_NEED_PLAY - gfxQueueASyncDeinit(&playList); - #if GFX_USE_GEVENT - gtimerDeinit(&playTimer); - #endif - gfxSemDestroy(&playComplete); - #endif - #if GAUDIO_NEED_RECORD - gfxQueueGSyncDeinit(&recordList); - #if GFX_USE_GEVENT - gtimerDeinit(&recordTimer); - #endif - #endif -} - -#if GAUDIO_NEED_PLAY - - bool_t gaudioPlayInit(uint16_t channel, uint32_t frequency, ArrayDataFormat format) { - gaudioPlayStop(); - playFlags &= ~PLAYFLG_ISINIT; - if (!gaudio_play_lld_init(channel, frequency, format)) - return FALSE; - playFlags |= PLAYFLG_ISINIT; - return TRUE; - } - - void gaudioPlay(GDataBuffer *pd) { - if (!(playFlags & PLAYFLG_ISINIT)) { - // Oops - init failed - return it directly to the free-list - if (pd) { - gfxBufferRelease(pd); - gfxYield(); // Make sure we get no endless cpu hogging loops - } - return; - } - - if (pd) - gfxQueueASyncPut(&playList, (gfxQueueASyncItem *)pd); - playFlags |= PLAYFLG_PLAYING; - gaudio_play_lld_start(); - } - - void gaudioPlayPause(void) { - if ((playFlags & (PLAYFLG_ISINIT|PLAYFLG_PLAYING)) == (PLAYFLG_ISINIT|PLAYFLG_PLAYING)) - gaudio_play_lld_stop(); - } - - void gaudioPlayStop(void) { - GDataBuffer *pd; - - if (playFlags & PLAYFLG_PLAYING) - gaudio_play_lld_stop(); - while((pd = (GDataBuffer *)gfxQueueASyncGet(&playList))) - gfxBufferRelease(pd); - } - - bool_t gaudioPlaySetVolume(uint8_t vol) { - return gaudio_play_lld_set_volume(vol); - } - - bool_t gaudioPlayWait(delaytime_t ms) { - if (!(playFlags & PLAYFLG_PLAYING)) - return TRUE; - return gfxSemWait(&playComplete, ms); - } - - #if GFX_USE_GEVENT - static void PlayTimerCallback(void *param) { - (void) param; - GSourceListener *psl; - GEventAudioPlay *pe; - - psl = 0; - while ((psl = geventGetSourceListener((GSourceHandle)&playTimer, psl))) { - if (!(pe = (GEventAudioPlay *)geventGetEventBuffer(psl))) { - // This listener is missing - save this. - psl->srcflags |= GAUDIO_PLAY_LOSTEVENT; - continue; - } - - pe->type = GEVENT_AUDIO_PLAY; - pe->flags = psl->srcflags; - psl->srcflags = 0; - if ((playFlags & PLAYFLG_PLAYING)) - pe->flags |= GAUDIO_PLAY_PLAYING; - if (gfxBufferIsAvailable()) - pe->flags |= GAUDIO_PLAY_FREEBLOCK; - geventSendEvent(psl); - } - } - - GSourceHandle gaudioPlayGetSource(void) { - if (!gtimerIsActive(&playTimer)) - gtimerStart(&playTimer, PlayTimerCallback, 0, TRUE, TIME_INFINITE); - playFlags |= PLAYFLG_USEEVENTS; - return (GSourceHandle)&playTimer; - } - #endif - - /** - * Routines provided for use by drivers. - */ - - GDataBuffer *gaudioPlayGetDataBlockI(void) { - return (GDataBuffer *)gfxQueueASyncGetI(&playList); - } - - void gaudioPlayReleaseDataBlockI(GDataBuffer *pd) { - gfxBufferReleaseI(pd); - #if GFX_USE_GEVENT - if (playFlags & PLAYFLG_USEEVENTS) - gtimerJabI(&playTimer); - #endif - } - - void gaudioPlayDoneI(void) { - playFlags &= ~PLAYFLG_PLAYING; - #if GFX_USE_GEVENT - if (playFlags & PLAYFLG_USEEVENTS) - gtimerJabI(&playTimer); - #endif - gfxSemSignalI(&playComplete); // This should really be gfxSemSignalAllI(&playComplete); - } -#endif - -#if GAUDIO_NEED_RECORD - bool_t gaudioRecordInit(uint16_t channel, uint32_t frequency, ArrayDataFormat format) { - gaudioRecordStop(); - recordFlags &= ~RECORDFLG_ISINIT; - if (!gaudio_record_lld_init(channel, frequency, format)) - return FALSE; - recordFlags |= RECORDFLG_ISINIT; - return TRUE; - } - - void gaudioRecordStart(void) { - if (!(recordFlags & RECORDFLG_ISINIT)) - return; // Oops - init failed - - recordFlags |= RECORDFLG_RECORDING; - recordFlags &= ~RECORDFLG_STALLED; - gaudio_record_lld_start(); - } - - void gaudioRecordStop(void) { - GDataBuffer *pd; - - if ((recordFlags & (RECORDFLG_RECORDING|RECORDFLG_STALLED)) == RECORDFLG_RECORDING) - gaudio_record_lld_stop(); - recordFlags &= ~(RECORDFLG_RECORDING|RECORDFLG_STALLED); - while((pd = (GDataBuffer *)gfxQueueGSyncGet(&recordList, TIME_IMMEDIATE))) - gfxBufferRelease(pd); - } - - GDataBuffer *gaudioRecordGetData(delaytime_t ms) { - return (GDataBuffer *)gfxQueueGSyncGet(&recordList, ms); - } - - #if GFX_USE_GEVENT - static void RecordTimerCallback(void *param) { - (void) param; - GSourceListener *psl; - GEventAudioRecord *pe; - - psl = 0; - while ((psl = geventGetSourceListener((GSourceHandle)&recordTimer, psl))) { - if (!(pe = (GEventAudioRecord *)geventGetEventBuffer(psl))) { - // This listener is missing - save this. - psl->srcflags |= GAUDIO_RECORD_LOSTEVENT; - continue; - } - pe->type = GEVENT_AUDIO_RECORD; - pe->flags = psl->srcflags; - psl->srcflags = 0; - if ((recordFlags & RECORDFLG_RECORDING)) - pe->flags |= GAUDIO_RECORD_RECORDING; - if ((recordFlags & RECORDFLG_STALLED)) - pe->flags |= GAUDIO_RECORD_STALL; - if (!gfxQueueGSyncIsEmpty(&recordList)) - pe->flags |= GAUDIO_RECORD_GOTBUFFER; - geventSendEvent(psl); - } - } - - GSourceHandle gaudioRecordGetSource(void) { - if (!gtimerIsActive(&recordTimer)) - gtimerStart(&recordTimer, RecordTimerCallback, 0, TRUE, TIME_INFINITE); - recordFlags |= RECORDFLG_USEEVENTS; - return (GSourceHandle)&recordTimer; - } - #endif - - /** - * Routines provided for use by drivers. - */ - - void gaudioRecordSaveDataBlockI(GDataBuffer *paud) { - gfxQueueGSyncPutI(&recordList, (gfxQueueGSyncItem *)paud); - #if GFX_USE_GEVENT - if (recordFlags & RECORDFLG_USEEVENTS) - gtimerJabI(&recordTimer); - #endif - } - - void gaudioRecordDoneI(void) { - recordFlags |= RECORDFLG_STALLED; - #if GFX_USE_GEVENT - if (recordFlags & RECORDFLG_USEEVENTS) - gtimerJabI(&recordTimer); - #endif - } -#endif - -#endif /* GFX_USE_GAUDIO */ -/** @} */ diff --git a/src/gaudio/gaudio_options.h b/src/gaudio/gaudio_options.h new file mode 100644 index 00000000..d6dfb105 --- /dev/null +++ b/src/gaudio/gaudio_options.h @@ -0,0 +1,44 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gaudio/gaudio_options.h + * @brief GAUDIO - Audio subsystem options header file. + * + * @addtogroup GAUDIO + * @{ + */ + +#ifndef _GAUDIO_OPTIONS_H +#define _GAUDIO_OPTIONS_H + +/** + * @name GAUDIO Functionality to be included + * @{ + */ + /** + * @brief Audio Play capability is needed + */ + #ifndef GAUDIO_NEED_PLAY + #define GAUDIO_NEED_PLAY FALSE + #endif + /** + * @brief Audio Recording capability is needed + */ + #ifndef GAUDIO_NEED_RECORD + #define GAUDIO_NEED_RECORD FALSE + #endif +/** + * @} + * + * @name GAUDIO Optional Sizing Parameters + * @{ + */ +/** @} */ + +#endif /* _GAUDIO_OPTIONS_H */ +/** @} */ diff --git a/src/gaudio/gaudio_rules.h b/src/gaudio/gaudio_rules.h new file mode 100644 index 00000000..2dbad17d --- /dev/null +++ b/src/gaudio/gaudio_rules.h @@ -0,0 +1,56 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gaudio/gaudio_rules.h + * @brief GAUDIO safety rules header file. + * + * @addtogroup GAUDIO + * @{ + */ + +#ifndef _GAUDIO_RULES_H +#define _GAUDIO_RULES_H + +#if GFX_USE_GAUDIO + #if !GAUDIO_NEED_PLAY && !GAUDIO_NEED_RECORD + #error "GAUDIO: GAUDIO_NEED_PLAY and/or GAUDIO_NEED_RECORD is required if GFX_USE_GAUDIO is TRUE" + #endif + #if !GFX_USE_GQUEUE + #if GFX_DISPLAY_RULE_WARNINGS + #warning "GAUDIO: GFX_USE_GQUEUE is required if GFX_USE_GAUDIO is TRUE. It has been turned on for you." + #endif + #undef GFX_USE_GQUEUE + #define GFX_USE_GQUEUE TRUE + #endif + #if GAUDIO_NEED_PLAY && !GQUEUE_NEED_ASYNC + #if GFX_DISPLAY_RULE_WARNINGS + #warning "GAUDIO: GQUEUE_NEED_ASYNC is required if GAUDIO_NEED_PLAY is TRUE. It has been turned on for you." + #endif + #undef GQUEUE_NEED_ASYNC + #define GQUEUE_NEED_ASYNC TRUE + #endif + #if !GQUEUE_NEED_GSYNC || !GQUEUE_NEED_BUFFERS + #if GFX_DISPLAY_RULE_WARNINGS + #warning "GAUDIO: GQUEUE_NEED_BUFFERS and GQUEUE_NEED_GSYNC are required if GFX_USE_GAUDIO is TRUE. They have been turned on for you." + #endif + #undef GQUEUE_NEED_BUFFERS + #define GQUEUE_NEED_BUFFERS TRUE + #undef GQUEUE_NEED_GSYNC + #define GQUEUE_NEED_GSYNC TRUE + #endif + #if GFX_USE_GEVENT && !GFX_USE_GTIMER + #if GFX_DISPLAY_RULE_WARNINGS + #warning "GAUDIO: GFX_USE_GTIMER is required if GFX_USE_GAUDIO and GFX_USE_GEVENT are TRUE. It has been turned on for you." + #endif + #undef GFX_USE_GTIMER + #define GFX_USE_GTIMER TRUE + #endif +#endif + +#endif /* _GAUDIO_RULES_H */ +/** @} */ diff --git a/src/gaudio/sys_defs.h b/src/gaudio/sys_defs.h deleted file mode 100644 index 5a43af18..00000000 --- a/src/gaudio/sys_defs.h +++ /dev/null @@ -1,297 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gaudio/sys_defs.h - * - * @addtogroup GAUDIO - * - * @brief Module to handle audio recording and play-back - * - * @{ - */ - -#ifndef _GAUDIO_H -#define _GAUDIO_H - -#include "gfx.h" - -#if GFX_USE_GAUDIO || defined(__DOXYGEN__) - -/* Include the driver defines */ -#if GAUDIO_NEED_PLAY - #include "gaudio_play_config.h" -#endif -#if GAUDIO_NEED_RECORD - #include "gaudio_record_config.h" -#endif - -/*===========================================================================*/ -/* Type definitions */ -/*===========================================================================*/ - -// Event types for GAUDIO -#define GEVENT_AUDIO_PLAY (GEVENT_GAUDIO_FIRST+0) -#define GEVENT_AUDIO_RECORD (GEVENT_GAUDIO_FIRST+1) - -#if GFX_USE_GEVENT || defined(__DOXYGEN__) - /** - * @brief The Audio play event structure. - * @{ - */ - typedef struct GEventAudioPlay_t { - /** - * @brief The type of this event (GEVENT_AUDIO_PLAY) - */ - GEventType type; - /** - * @brief The event flags - */ - uint16_t flags; - /** - * @brief The event flag values. - * @{ - */ - #define GAUDIO_PLAY_LOSTEVENT 0x0001 /**< @brief The last GEVENT_AUDIO_PLAY event was lost */ - #define GAUDIO_PLAY_PLAYING 0x0002 /**< @brief The audio out system is currently playing */ - #define GAUDIO_PLAY_FREEBLOCK 0x0004 /**< @brief An audio buffer has been freed */ - /** @} */ - } GEventAudioPlay; - /** @} */ - - /** - * @brief The Audio record event structure. - * @{ - */ - typedef struct GEventAudioRecord_t { - /** - * @brief The type of this event (GEVENT_AUDIO_RECORD) - */ - GEventType type; - /** - * @brief The event flags - */ - uint16_t flags; - /** - * @brief The event flag values. - * @{ - */ - #define GAUDIO_RECORD_LOSTEVENT 0x0001 /**< @brief The last GEVENT_AUDIO_IN event was lost */ - #define GAUDIO_RECORD_RECORDING 0x0002 /**< @brief The audio recording system is currently recording */ - #define GAUDIO_RECORD_GOTBUFFER 0x0004 /**< @brief An audio buffer is ready for processing */ - #define GAUDIO_RECORD_STALL 0x0008 /**< @brief The recording process has stalled due to no free buffers */ - /** @} */ - } GEventAudioRecord; - /** @} */ -#endif - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#ifdef __cplusplus -extern "C" { -#endif - -#if GAUDIO_NEED_PLAY || defined(__DOXYGEN__) - /** - * @brief Set the audio device to play on the specified channel and with the specified - * sample frequency. - * @return TRUE is successful, FALSE if the driver doesn't accept those parameters. - * - * @param[in] channel The audio output channel to use. Can be set from 0 to GAUDIO_PLAY_NUM_CHANNELS - 1 - * @param[in] frequency The audio sample rate in samples per second - * @param[in] format The audio sample format - * - * @note Some channels are mono, and some are stereo. See your driver config file - * to determine which channels to use and whether they are stereo or not. - * @note Only one channel can be playing at a time. Calling this will stop any - * currently playing channel. - * - * @api - */ - bool_t gaudioPlayInit(uint16_t channel, uint32_t frequency, ArrayDataFormat format); - - /** - * @brief Play the specified sample data. - * @details The sample data is output to the audio channel. On completion the buffer is returned to the free-list. - * @pre @p gaudioPlayInit must have been called first to set the channel and sample frequency. - * - * @param[in] paud The audio sample buffer to play. It can be NULL (used to restart paused audio) - * - * @note Calling this will cancel any pause. - * @note Before calling this function the len field of the GDataBuffer structure must be - * specified (in bytes). - * @note For stereo channels the sample data is interleaved in the buffer. - * @note This call returns before the data has completed playing. Subject to available buffers (which - * can be obtained from the free-list), any number of buffers may be played. They will be queued - * for playing in the order they are supplied to this routine and played when previous buffers are - * complete. In this way continuous playing can be obtained without audio gaps. - * - * @api - */ - void gaudioPlay(GDataBuffer *paud); - - /** - * @brief Pause any currently playing sounds. - * - * @note If nothing is currently playing this routine does nothing. To restart playing call @p gaudioPlay() - * with or without a new sample buffer. - * @note Some drivers will not respond until a buffer boundary. - * - * @api - */ - void gaudioPlayPause(void); - - /** - * @brief Stop any currently playing sounds. - * - * @note This stops any playing sounds and returns any currently queued buffers back to the free-list. - * @note Some drivers will not respond until a buffer boundary. - * - * @api - */ - void gaudioPlayStop(void); - - /** - * @brief Set the output volume. - * @return TRUE if successful. - * - * @param[in] vol 0->255 (0 = muted) - * - * @note Some drivers may not support this. They will return FALSE. - * @note For stereo devices, both channels are set to the same volume. - * - * @api - */ - bool_t gaudioPlaySetVolume(uint8_t vol); - - #if GFX_USE_GEVENT || defined(__DOXYGEN__) - /** - * @brief Turn on sending results to the GEVENT sub-system. - * @details Returns a GSourceHandle to listen for GEVENT_AUDIO_OUT events. - * - * @note The audio output will not use the GEVENT system unless this is - * called first. This saves processing time if the application does - * not want to use the GEVENT sub-system for audio output. - * Once turned on it can only be turned off by calling @p gaudioPlayInit() again. - * @note The audio output is capable of signaling via this method and other methods - * at the same time. - * - * @return The GSourceHandle - * - * @api - */ - GSourceHandle gaudioPlayGetSource(void); - #endif - - /** - * @brief Wait for any currently playing sounds to complete - * @return TRUE if there is now nothing playing or FALSE if the timeout is exceeded - * - * @param[in] ms The maximum amount of time in milliseconds to wait for playing to complete. - * - * @api - */ - bool_t gaudioPlayWait(delaytime_t ms); -#endif - -#if GAUDIO_NEED_RECORD || defined(__DOXYGEN__) - /** - * @brief Initialise (but not start) the Audio Recording sub-system. - * @details Returns FALSE for an invalid channel or other invalid parameter. - * - * @param[in] channel The channel to convert. Can be set from 0 to GAUDIO_RECORD_NUM_CHANNELS - 1 - * @param[in] frequency The sample frequency - * @param[in] format The audio sample format requested - * - * @note Only one channel is active at a time. If an audio input is running it will be stopped. - * The Event subsystem is disconnected from the audio subsystem and any binary semaphore - * event is forgotten. - * @note Some channels may be stereo channels which return twice as much sample data with - * the left and right channel data interleaved. Other channels may be mono channels. - * Where stereo channels exist the low level driver may also - * offer the left and right channels separately. - * @note Due to a bug in Chibi-OS each buffer on the free-list must contain an even number of - * samples and for stereo devices it must hold a number of samples that is evenly divisible by 4. - * This requirement applies only to ChibiOS where the audio driver uses - * a ChibiOS hal driver like the cpu ADC driver. This applies even it is used indirectly via - * the uGFX GADC driver. - * @note The number of samples for stereo devices will be double the number of conversions. - * Make sure you allocate your buffers large enough. Each channel is then interleaved - * into the provided buffer. - * - * @return FALSE if invalid channel or parameter - * - * @api - */ - bool_t gaudioRecordInit(uint16_t channel, uint32_t frequency, ArrayDataFormat format); - - /** - * @brief Start the audio recording. - * @pre It must have been initialised first with @p gaudioRecordInit() - * - * @api - */ - void gaudioRecordStart(void); - - /** - * @brief Stop the audio recording. - * - * @note All audio recording data that has not yet been retrieved is automatically - * returned to the free-list. - * @api - */ - void gaudioRecordStop(void); - - /** - * @brief Get a filled audio buffer from the recording list - * @return A GDataBuffer pointer or NULL if the timeout is exceeded - * - * @param[in] ms The maximum amount of time in milliseconds to wait for data if some is not currently available. - * - * @note After processing the audio data, your application must return the buffer to the free-list so that - * it can be used to record more audio into. This can be done via the play list using @p gaudioPlay() or - * directly using @p gfxBufferRelease(). - * @note A buffer may be returned to the free-list before you have finished processing it provided you finish - * processing it before GADC re-uses it. This is useful when RAM usage is critical to reduce the number - * of buffers required. It works before the free list is a FIFO queue and therefore buffers are kept - * in the queue as long as possible before they are re-used. - * - * @api - */ - GDataBuffer *gaudioRecordGetData(delaytime_t ms); - - #if GFX_USE_GEVENT || defined(__DOXYGEN__) - /** - * @brief Turn on sending results to the GEVENT sub-system. - * @details Returns a GSourceHandle to listen for GEVENT_AUDIO_RECORD events. - * - * @note Audio recording will not use the GEVENT system unless this is - * called first. This saves processing time if the application does - * not want to use the GEVENT sub-system for audio recording. - * Once turned on it can only be turned off by calling @p gaudioRecordInit() again. - * @note The audio input is capable of signaling via this and other methods - * at the same time. - * - * @return The GSourceHandle - * - * @api - */ - GSourceHandle gaudioRecordGetSource(void); - #endif -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* GFX_USE_GAUDIO */ - -#endif /* _GAUDIO_H */ -/** @} */ - diff --git a/src/gaudio/sys_make.mk b/src/gaudio/sys_make.mk deleted file mode 100644 index ea02e010..00000000 --- a/src/gaudio/sys_make.mk +++ /dev/null @@ -1 +0,0 @@ -GFXSRC += $(GFXLIB)/src/gaudio/gaudio_gaudio.c diff --git a/src/gaudio/sys_options.h b/src/gaudio/sys_options.h deleted file mode 100644 index eb903424..00000000 --- a/src/gaudio/sys_options.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gaudio/sys_options.h - * @brief GAUDIO - Audio subsystem options header file. - * - * @addtogroup GAUDIO - * @{ - */ - -#ifndef _GAUDIO_OPTIONS_H -#define _GAUDIO_OPTIONS_H - -/** - * @name GAUDIO Functionality to be included - * @{ - */ - /** - * @brief Audio Play capability is needed - */ - #ifndef GAUDIO_NEED_PLAY - #define GAUDIO_NEED_PLAY FALSE - #endif - /** - * @brief Audio Recording capability is needed - */ - #ifndef GAUDIO_NEED_RECORD - #define GAUDIO_NEED_RECORD FALSE - #endif -/** - * @} - * - * @name GAUDIO Optional Sizing Parameters - * @{ - */ -/** @} */ - -#endif /* _GAUDIO_OPTIONS_H */ -/** @} */ diff --git a/src/gaudio/sys_rules.h b/src/gaudio/sys_rules.h deleted file mode 100644 index 4786fa5f..00000000 --- a/src/gaudio/sys_rules.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gaudio/sys_rules.h - * @brief GAUDIO safety rules header file. - * - * @addtogroup GAUDIO - * @{ - */ - -#ifndef _GAUDIO_RULES_H -#define _GAUDIO_RULES_H - -#if GFX_USE_GAUDIO - #if !GAUDIO_NEED_PLAY && !GAUDIO_NEED_RECORD - #error "GAUDIO: GAUDIO_NEED_PLAY and/or GAUDIO_NEED_RECORD is required if GFX_USE_GAUDIO is TRUE" - #endif - #if !GFX_USE_GQUEUE - #if GFX_DISPLAY_RULE_WARNINGS - #warning "GAUDIO: GFX_USE_GQUEUE is required if GFX_USE_GAUDIO is TRUE. It has been turned on for you." - #endif - #undef GFX_USE_GQUEUE - #define GFX_USE_GQUEUE TRUE - #endif - #if GAUDIO_NEED_PLAY && !GQUEUE_NEED_ASYNC - #if GFX_DISPLAY_RULE_WARNINGS - #warning "GAUDIO: GQUEUE_NEED_ASYNC is required if GAUDIO_NEED_PLAY is TRUE. It has been turned on for you." - #endif - #undef GQUEUE_NEED_ASYNC - #define GQUEUE_NEED_ASYNC TRUE - #endif - #if !GQUEUE_NEED_GSYNC || !GQUEUE_NEED_BUFFERS - #if GFX_DISPLAY_RULE_WARNINGS - #warning "GAUDIO: GQUEUE_NEED_BUFFERS and GQUEUE_NEED_GSYNC are required if GFX_USE_GAUDIO is TRUE. They have been turned on for you." - #endif - #undef GQUEUE_NEED_BUFFERS - #define GQUEUE_NEED_BUFFERS TRUE - #undef GQUEUE_NEED_GSYNC - #define GQUEUE_NEED_GSYNC TRUE - #endif - #if GFX_USE_GEVENT && !GFX_USE_GTIMER - #if GFX_DISPLAY_RULE_WARNINGS - #warning "GAUDIO: GFX_USE_GTIMER is required if GFX_USE_GAUDIO and GFX_USE_GEVENT are TRUE. It has been turned on for you." - #endif - #undef GFX_USE_GTIMER - #define GFX_USE_GTIMER TRUE - #endif -#endif - -#endif /* _GAUDIO_RULES_H */ -/** @} */ diff --git a/src/gdisp/driver.h b/src/gdisp/driver.h deleted file mode 100644 index efcb2872..00000000 --- a/src/gdisp/driver.h +++ /dev/null @@ -1,1070 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gdisp/driver.h - * @brief GDISP Graphic Driver subsystem low level driver header. - * - * @addtogroup GDISP - * @{ - */ - -#ifndef _GDISP_LLD_H -#define _GDISP_LLD_H - -#if GFX_USE_GDISP - -// Include the GDRIVER infrastructure -#include "src/gdriver/sys_defs.h" - -// Are we currently compiling the driver itself? -#if defined(GDISP_DRIVER_VMT) - #define IN_DRIVER TRUE -#else - #define IN_DRIVER FALSE -#endif - -// Is this a multiple driver situation? -#if defined(GDISP_DRIVER_LIST) - #define IS_MULTIPLE TRUE -#else - #define IS_MULTIPLE FALSE -#endif - -// Do we need to use VMT calling rather than direct calls to the driver? -#if IS_MULTIPLE || GDISP_NEED_PIXMAP - #define USE_VMT TRUE -#else - #define USE_VMT FALSE -#endif - -// Are we in the pixmap virtual driver -#ifndef IN_PIXMAP_DRIVER - #define IN_PIXMAP_DRIVER FALSE -#endif - -//------------------------------------------------------------------------------------------------------------ - -// Our special auto-detect hardware code which uses the VMT. -#define HARDWARE_AUTODETECT 2 - -#if USE_VMT && !IN_DRIVER - // Multiple controllers the default is to hardware detect - #define HARDWARE_DEFAULT HARDWARE_AUTODETECT -#else - // The default is not to include code functions that aren't needed - #define HARDWARE_DEFAULT FALSE -#endif - -//------------------------------------------------------------------------------------------------------------ - -/** - * @name GDISP hardware accelerated support - * @{ - */ - /** - * @brief The display hardware can benefit from being de-initialized when usage is complete. - * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT - * - * @note HARDWARE_AUTODETECT is only meaningful when GDISP_DRIVER_LIST is defined - * @note This is most useful for displays such as remote network displays. - */ - #ifndef GDISP_HARDWARE_DEINIT - #define GDISP_HARDWARE_DEINIT HARDWARE_DEFAULT - #endif - - /** - * @brief The display hardware can benefit from being flushed. - * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT - * - * @note HARDWARE_AUTODETECT is only meaningful when GDISP_DRIVER_LIST is defined - * @note Some controllers ** require ** the application to flush - */ - #ifndef GDISP_HARDWARE_FLUSH - #define GDISP_HARDWARE_FLUSH HARDWARE_DEFAULT - #endif - - /** - * @brief Hardware streaming writing is supported. - * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT - * - * @note HARDWARE_AUTODETECT is only meaningful when GDISP_DRIVER_LIST is defined - * @note Either GDISP_HARDWARE_STREAM_WRITE or GDISP_HARDWARE_DRAWPIXEL must be provided by each driver - */ - #ifndef GDISP_HARDWARE_STREAM_WRITE - #define GDISP_HARDWARE_STREAM_WRITE HARDWARE_DEFAULT - #endif - - /** - * @brief Hardware streaming reading of the display surface is supported. - * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT - * - * @note HARDWARE_AUTODETECT is only meaningful when GDISP_DRIVER_LIST is defined - * - */ - #ifndef GDISP_HARDWARE_STREAM_READ - #define GDISP_HARDWARE_STREAM_READ HARDWARE_DEFAULT - #endif - - /** - * @brief Hardware supports setting the cursor position within the stream window. - * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT - * - * @note HARDWARE_AUTODETECT is only meaningful when GDISP_DRIVER_LIST is defined - * @note This is used to optimise setting of individual pixels within a stream window. - * It should therefore not be implemented unless it is cheaper than just setting - * a new window. - */ - #ifndef GDISP_HARDWARE_STREAM_POS - #define GDISP_HARDWARE_STREAM_POS HARDWARE_DEFAULT - #endif - - /** - * @brief Hardware accelerated draw pixel. - * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT - * - * @note HARDWARE_AUTODETECT is only meaningful when GDISP_DRIVER_LIST is defined - * @note Either GDISP_HARDWARE_STREAM_WRITE or GDISP_HARDWARE_DRAWPIXEL must be provided by the driver - */ - #ifndef GDISP_HARDWARE_DRAWPIXEL - #define GDISP_HARDWARE_DRAWPIXEL HARDWARE_DEFAULT - #endif - - /** - * @brief Hardware accelerated screen clears. - * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT - * - * @note HARDWARE_AUTODETECT is only meaningful when GDISP_DRIVER_LIST is defined - * @note This clears the entire display surface regardless of the clipping area currently set - */ - #ifndef GDISP_HARDWARE_CLEARS - #define GDISP_HARDWARE_CLEARS HARDWARE_DEFAULT - #endif - - /** - * @brief Hardware accelerated rectangular fills. - * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT - * - * @note HARDWARE_AUTODETECT is only meaningful when GDISP_DRIVER_LIST is defined - */ - #ifndef GDISP_HARDWARE_FILLS - #define GDISP_HARDWARE_FILLS HARDWARE_DEFAULT - #endif - - /** - * @brief Hardware accelerated fills from an image. - * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT - * - * @note HARDWARE_AUTODETECT is only meaningful when GDISP_DRIVER_LIST is defined - */ - #ifndef GDISP_HARDWARE_BITFILLS - #define GDISP_HARDWARE_BITFILLS HARDWARE_DEFAULT - #endif - - /** - * @brief Hardware accelerated scrolling. - * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT - * - * @note HARDWARE_AUTODETECT is only meaningful when GDISP_DRIVER_LIST is defined - */ - #ifndef GDISP_HARDWARE_SCROLL - #define GDISP_HARDWARE_SCROLL HARDWARE_DEFAULT - #endif - - /** - * @brief Reading back of pixel values. - * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT - * - * @note HARDWARE_AUTODETECT is only meaningful when GDISP_DRIVER_LIST is defined - */ - #ifndef GDISP_HARDWARE_PIXELREAD - #define GDISP_HARDWARE_PIXELREAD HARDWARE_DEFAULT - #endif - - /** - * @brief The driver supports one or more control commands. - * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT - * - * @note HARDWARE_AUTODETECT is only meaningful when GDISP_DRIVER_LIST is defined - */ - #ifndef GDISP_HARDWARE_CONTROL - #define GDISP_HARDWARE_CONTROL HARDWARE_DEFAULT - #endif - - /** - * @brief The driver supports a non-standard query. - * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT - * - * @note HARDWARE_AUTODETECT is only meaningful when GDISP_DRIVER_LIST is defined - */ - #ifndef GDISP_HARDWARE_QUERY - #define GDISP_HARDWARE_QUERY HARDWARE_DEFAULT - #endif - - /** - * @brief The driver supports a clipping in hardware. - * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT - * - * @note HARDWARE_AUTODETECT is only meaningful when GDISP_DRIVER_LIST is defined - * @note If this is defined the driver must perform its own clipping on all calls to - * the driver and respond appropriately if a parameter is outside the display area. - * @note If this is not defined then the software ensures that all calls to the - * driver do not exceed the display area (provided GDISP_NEED_CLIP or GDISP_NEED_VALIDATION - * has been set). - */ - #ifndef GDISP_HARDWARE_CLIP - #define GDISP_HARDWARE_CLIP HARDWARE_DEFAULT - #endif -/** @} */ - -//------------------------------------------------------------------------------------------------------------ - -// For pixmaps certain routines MUST not be FALSE as they are needed for pixmap drawing -// Similarly some routines MUST not be TRUE as pixmap's don't provide them. -#if GDISP_NEED_PIXMAP && !IN_DRIVER - #if !GDISP_HARDWARE_DEINIT - #undef GDISP_HARDWARE_DEINIT - #define GDISP_HARDWARE_DEINIT HARDWARE_AUTODETECT - #endif - #if !GDISP_HARDWARE_DRAWPIXEL - #undef GDISP_HARDWARE_DRAWPIXEL - #define GDISP_HARDWARE_DRAWPIXEL HARDWARE_AUTODETECT - #endif - #if !GDISP_HARDWARE_PIXELREAD - #undef GDISP_HARDWARE_PIXELREAD - #define GDISP_HARDWARE_PIXELREAD HARDWARE_AUTODETECT - #endif - #if !GDISP_HARDWARE_CONTROL - #undef GDISP_HARDWARE_CONTROL - #define GDISP_HARDWARE_CONTROL HARDWARE_AUTODETECT - #endif - #if GDISP_HARDWARE_FLUSH == TRUE - #undef GDISP_HARDWARE_FLUSH - #define GDISP_HARDWARE_FLUSH HARDWARE_AUTODETECT - #endif - #if GDISP_HARDWARE_STREAM_WRITE == TRUE - #undef GDISP_HARDWARE_STREAM_WRITE - #define GDISP_HARDWARE_STREAM_WRITE HARDWARE_AUTODETECT - #endif - #if GDISP_HARDWARE_STREAM_READ == TRUE - #undef GDISP_HARDWARE_STREAM_READ - #define GDISP_HARDWARE_STREAM_READ HARDWARE_AUTODETECT - #endif - #if GDISP_HARDWARE_CLEARS == TRUE - #undef GDISP_HARDWARE_CLEARS - #define GDISP_HARDWARE_CLEARS HARDWARE_AUTODETECT - #endif - #if GDISP_HARDWARE_FILLS == TRUE - #undef GDISP_HARDWARE_FILLS - #define GDISP_HARDWARE_FILLS HARDWARE_AUTODETECT - #endif - #if GDISP_HARDWARE_BITFILLS == TRUE - #undef GDISP_HARDWARE_BITFILLS - #define GDISP_HARDWARE_BITFILLS HARDWARE_AUTODETECT - #endif - #if GDISP_HARDWARE_SCROLL == TRUE - #undef GDISP_HARDWARE_SCROLL - #define GDISP_HARDWARE_SCROLL HARDWARE_AUTODETECT - #endif - #if GDISP_HARDWARE_QUERY == TRUE - #undef GDISP_HARDWARE_QUERY - #define GDISP_HARDWARE_QUERY HARDWARE_AUTODETECT - #endif - #if GDISP_HARDWARE_CLIP == TRUE - #undef GDISP_HARDWARE_CLIP - #define GDISP_HARDWARE_CLIP HARDWARE_AUTODETECT - #endif -#endif - -//------------------------------------------------------------------------------------------------------------ - -/* Verify information for packed pixels and define a non-packed pixel macro */ -#if !GDISP_PACKED_PIXELS - #define gdispPackPixels(buf,cx,x,y,c) { ((color_t *)(buf))[(y)*(cx)+(x)] = (c); } -#elif !GDISP_HARDWARE_BITFILLS - #error "GDISP: packed pixel formats are only supported for hardware accelerated drivers." -#elif GDISP_PIXELFORMAT != GDISP_PIXELFORMAT_RGB888 \ - && GDISP_PIXELFORMAT != GDISP_PIXELFORMAT_RGB444 \ - && GDISP_PIXELFORMAT != GDISP_PIXELFORMAT_RGB666 \ - && GDISP_PIXELFORMAT != GDISP_PIXELFORMAT_CUSTOM - #error "GDISP: A packed pixel format has been specified for an unsupported pixel format." -#endif - -/* Support routine for packed pixel formats */ -#if !defined(gdispPackPixels) || defined(__DOXYGEN__) - /** - * @brief Pack a pixel into a pixel buffer. - * @note This function performs no buffer boundary checking - * regardless of whether GDISP_NEED_CLIP has been specified. - * - * @param[in] buf The buffer to put the pixel in - * @param[in] cx The width of a pixel line - * @param[in] x, y The location of the pixel to place - * @param[in] color The color to put into the buffer - * - * @api - */ - void gdispPackPixels(const pixel_t *buf, coord_t cx, coord_t x, coord_t y, color_t color); -#endif - -//------------------------------------------------------------------------------------------------------------ - -struct GDisplay { - struct GDriver d; // This must be the first element - #define gvmt(g) ((const GDISPVMT const *)((g)->d.vmt)) // For ease of access to the vmt member - - struct GDISPControl { - coord_t Width; - coord_t Height; - orientation_t Orientation; - powermode_t Powermode; - uint8_t Backlight; - uint8_t Contrast; - } g; - - void * priv; // A private area just for the drivers use. - void * board; // A private area just for the board interfaces use. - - uint8_t systemdisplay; - uint8_t controllerdisplay; - uint16_t flags; - #define GDISP_FLG_INSTREAM 0x0001 // We are in a user based stream operation - #define GDISP_FLG_SCRSTREAM 0x0002 // The stream area currently covers the whole screen - #define GDISP_FLG_DRIVER 0x0004 // This flags and above are for use by the driver - - // Multithread Mutex - #if GDISP_NEED_MULTITHREAD - gfxMutex mutex; - #endif - - // Software clipping - #if GDISP_HARDWARE_CLIP != TRUE && (GDISP_NEED_CLIP || GDISP_NEED_VALIDATION) - coord_t clipx0, clipy0; - coord_t clipx1, clipy1; /* not inclusive */ - #endif - - // Driver call parameters - struct { - coord_t x, y; - coord_t cx, cy; - coord_t x1, y1; - coord_t x2, y2; - color_t color; - void *ptr; - } p; - - // In call working buffers - - #if GDISP_NEED_TEXT - // Text rendering parameters - struct { - font_t font; - color_t color; - color_t bgcolor; - coord_t clipx0, clipy0; - coord_t clipx1, clipy1; - } t; - #endif - #if GDISP_LINEBUF_SIZE != 0 && ((GDISP_NEED_SCROLL && !GDISP_HARDWARE_SCROLL) || (!GDISP_HARDWARE_STREAM_WRITE && GDISP_HARDWARE_BITFILLS)) - // A pixel line buffer - color_t linebuf[GDISP_LINEBUF_SIZE]; - #endif -}; - -typedef struct GDISPVMT { - GDriverVMT d; - #define GDISP_VFLG_DYNAMICONLY 0x0001 // This display should never be statically initialised - #define GDISP_VFLG_PIXMAP 0x0002 // This is a pixmap display - bool_t (*init)(GDisplay *g); - void (*deinit)(GDisplay *g); - void (*writestart)(GDisplay *g); // Uses p.x,p.y p.cx,p.cy - void (*writepos)(GDisplay *g); // Uses p.x,p.y - void (*writecolor)(GDisplay *g); // Uses p.color - void (*writestop)(GDisplay *g); // Uses no parameters - void (*readstart)(GDisplay *g); // Uses p.x,p.y p.cx,p.cy - color_t (*readcolor)(GDisplay *g); // Uses no parameters - void (*readstop)(GDisplay *g); // Uses no parameters - void (*pixel)(GDisplay *g); // Uses p.x,p.y p.color - void (*clear)(GDisplay *g); // Uses p.color - void (*fill)(GDisplay *g); // Uses p.x,p.y p.cx,p.cy p.color - void (*blit)(GDisplay *g); // Uses p.x,p.y p.cx,p.cy p.x1,p.y1 (=srcx,srcy) p.x2 (=srccx), p.ptr (=buffer) - color_t (*get)(GDisplay *g); // Uses p.x,p.y - void (*vscroll)(GDisplay *g); // Uses p.x,p.y p.cx,p.cy, p.y1 (=lines) p.color - void (*control)(GDisplay *g); // Uses p.x (=what) p.ptr (=value) - void *(*query)(GDisplay *g); // Uses p.x (=what); - void (*setclip)(GDisplay *g); // Uses p.x,p.y p.cx,p.cy - void (*flush)(GDisplay *g); // Uses no parameters -} GDISPVMT; - -//------------------------------------------------------------------------------------------------------------ - -// Do we need function definitions or macro's (via the VMT) -#if IN_DRIVER || !USE_VMT || defined(__DOXYGEN__) - #ifdef __cplusplus - extern "C" { - #endif - - // Should the driver routines should be static or not - #if USE_VMT - #define LLDSPEC static - #else - #define LLDSPEC - #endif - - /** - * @brief Initialize the driver. - * @return TRUE if successful. - * @param[in] g The driver structure - * @param[out] g->g The driver must fill in the GDISPControl structure - */ - LLDSPEC bool_t gdisp_lld_init(GDisplay *g); - - #if GDISP_HARDWARE_DEINIT || defined(__DOXYGEN__) - /** - * @brief The driver is being de-initialized - * @pre GDISP_HARDWARE_FLUSH is TRUE - * - * @param[in] g The driver structure - * - */ - LLDSPEC void gdisp_lld_deinit(GDisplay *g); - #endif - - #if GDISP_HARDWARE_FLUSH || defined(__DOXYGEN__) - /** - * @brief Flush the current drawing operations to the display - * @pre GDISP_HARDWARE_FLUSH is TRUE - * - * @param[in] g The driver structure - * - * @note The parameter variables must not be altered by the driver. - */ - LLDSPEC void gdisp_lld_flush(GDisplay *g); - #endif - - #if GDISP_HARDWARE_STREAM_WRITE || defined(__DOXYGEN__) - /** - * @brief Start a streamed write operation - * @pre GDISP_HARDWARE_STREAM_WRITE is TRUE - * - * @param[in] g The driver structure - * - * @note g->p.x,g->p.y The window position - * @note g->p.cx,g->p.cy The window size - * - * @note The parameter variables must not be altered by the driver. - * @note Streaming operations that wrap the defined window have - * undefined results. - * @note This must be followed by a call to @p gdisp_lld_write_pos() if GDISP_HARDWARE_STREAM_POS is TRUE. - */ - LLDSPEC void gdisp_lld_write_start(GDisplay *g); - - /** - * @brief Send a pixel to the current streaming position and then increment that position - * @pre GDISP_HARDWARE_STREAM_WRITE is TRUE - * - * @param[in] g The driver structure - * - * @note g->p.color The color to display at the curent position - * @note The parameter variables must not be altered by the driver. - */ - LLDSPEC void gdisp_lld_write_color(GDisplay *g); - - /** - * @brief End the current streaming write operation - * @pre GDISP_HARDWARE_STREAM_WRITE is TRUE - * - * @param[in] g The driver structure - * - * @note The parameter variables must not be altered by the driver. - */ - LLDSPEC void gdisp_lld_write_stop(GDisplay *g); - - #if GDISP_HARDWARE_STREAM_POS || defined(__DOXYGEN__) - /** - * @brief Change the current position within the current streaming window - * @pre GDISP_HARDWARE_STREAM_POS is TRUE and GDISP_HARDWARE_STREAM_WRITE is TRUE - * - * @param[in] g The driver structure - * @param[in] g->p.x,g->p.y The new position (which will always be within the existing stream window) - * - * @note The parameter variables must not be altered by the driver. - */ - LLDSPEC void gdisp_lld_write_pos(GDisplay *g); - #endif - #endif - - #if GDISP_HARDWARE_STREAM_READ || defined(__DOXYGEN__) - /** - * @brief Start a streamed read operation - * @pre GDISP_HARDWARE_STREAM_READ is TRUE - * - * @param[in] g The driver structure - * @param[in] g->p.x,g->p.y The window position - * @param[in] g->p.cx,g->p.cy The window size - * - * @note The parameter variables must not be altered by the driver. - * @note Streaming operations that wrap the defined window have - * undefined results. - */ - LLDSPEC void gdisp_lld_read_start(GDisplay *g); - - /** - * @brief Read a pixel from the current streaming position and then increment that position - * @return The color at the current position - * @pre GDISP_HARDWARE_STREAM_READ is TRUE - * - * @param[in] g The driver structure - * - * @note The parameter variables must not be altered by the driver. - */ - LLDSPEC color_t gdisp_lld_read_color(GDisplay *g); - - /** - * @brief End the current streaming operation - * @pre GDISP_HARDWARE_STREAM_READ is TRUE - * - * @param[in] g The driver structure - * - * @note The parameter variables must not be altered by the driver. - */ - LLDSPEC void gdisp_lld_read_stop(GDisplay *g); - #endif - - #if GDISP_HARDWARE_DRAWPIXEL || defined(__DOXYGEN__) - /** - * @brief Draw a pixel - * @pre GDISP_HARDWARE_DRAWPIXEL is TRUE - * - * @param[in] g The driver structure - * @param[in] g->p.x,g->p.y The pixel position - * @param[in] g->p.color The color to set - * - * @note The parameter variables must not be altered by the driver. - */ - LLDSPEC void gdisp_lld_draw_pixel(GDisplay *g); - #endif - - #if GDISP_HARDWARE_CLEARS || defined(__DOXYGEN__) - /** - * @brief Clear the screen using the defined color - * @pre GDISP_HARDWARE_CLEARS is TRUE - * - * @param[in] g The driver structure - * @param[in] g->p.color The color to set - * - * @note The parameter variables must not be altered by the driver. - */ - LLDSPEC void gdisp_lld_clear(GDisplay *g); - #endif - - #if GDISP_HARDWARE_FILLS || defined(__DOXYGEN__) - /** - * @brief Fill an area with a single color - * @pre GDISP_HARDWARE_FILLS is TRUE - * - * @param[in] g The driver structure - * @param[in] g->p.x,g->p.y The area position - * @param[in] g->p.cx,g->p.cy The area size - * @param[in] g->p.color The color to set - * - * @note The parameter variables must not be altered by the driver. - */ - LLDSPEC void gdisp_lld_fill_area(GDisplay *g); - #endif - - #if GDISP_HARDWARE_BITFILLS || defined(__DOXYGEN__) - /** - * @brief Fill an area using a bitmap - * @pre GDISP_HARDWARE_BITFILLS is TRUE - * - * @param[in] g The driver structure - * @param[in] g->p.x,g->p.y The area position - * @param[in] g->p.cx,g->p.cy The area size - * @param[in] g->p.x1,g->p.y1 The starting position in the bitmap - * @param[in] g->p.x2 The width of a bitmap line - * @param[in] g->p.ptr The pointer to the bitmap - * - * @note The parameter variables must not be altered by the driver. - */ - LLDSPEC void gdisp_lld_blit_area(GDisplay *g); - #endif - - #if GDISP_HARDWARE_PIXELREAD || defined(__DOXYGEN__) - /** - * @brief Read a pixel from the display - * @return The color at the defined position - * @pre GDISP_HARDWARE_PIXELREAD is TRUE (and the application needs it) - * - * @param[in] g The driver structure - * @param[in] g->p.x,g->p.y The pixel position - * - * @note The parameter variables must not be altered by the driver. - */ - LLDSPEC color_t gdisp_lld_get_pixel_color(GDisplay *g); - #endif - - #if (GDISP_HARDWARE_SCROLL && GDISP_NEED_SCROLL) || defined(__DOXYGEN__) - /** - * @brief Scroll an area of the screen - * @pre GDISP_HARDWARE_SCROLL is TRUE (and the application needs it) - * - * @param[in] g The driver structure - * @param[in] g->p.x,g->p.y The area position - * @param[in] g->p.cx,g->p.cy The area size - * @param[in] g->p.y1 The number of lines to scroll (positive or negative) - * - * @note The parameter variables must not be altered by the driver. - * @note This can be easily implemented if the hardware supports - * display area to display area copying. - * @note Clearing the exposed area on the scroll operation is not - * needed as the high level code handles this. - */ - LLDSPEC void gdisp_lld_vertical_scroll(GDisplay *g); - #endif - - #if (GDISP_HARDWARE_CONTROL && GDISP_NEED_CONTROL) || defined(__DOXYGEN__) - /** - * @brief Control some feature of the hardware - * @pre GDISP_HARDWARE_CONTROL is TRUE (and the application needs it) - * - * @param[in] g The driver structure - * @param[in] g->p.x The operation to perform - * @param[in] g->p.ptr The operation parameter - * - * @note The parameter variables must not be altered by the driver. - */ - LLDSPEC void gdisp_lld_control(GDisplay *g); - #endif - - #if (GDISP_HARDWARE_QUERY && GDISP_NEED_QUERY) || defined(__DOXYGEN__) - /** - * @brief Query some feature of the hardware - * @return The information requested (typecast as void *) - * @pre GDISP_HARDWARE_QUERY is TRUE (and the application needs it) - * - * @param[in] g The driver structure - * @param[in] g->p.x What to query - * - * @note The parameter variables must not be altered by the driver. - */ - LLDSPEC void *gdisp_lld_query(GDisplay *g); // Uses p.x (=what); - #endif - - #if (GDISP_HARDWARE_CLIP && (GDISP_NEED_CLIP || GDISP_NEED_VALIDATION)) || defined(__DOXYGEN__) - /** - * @brief Set the hardware clipping area - * @pre GDISP_HARDWARE_CLIP is TRUE (and the application needs it) - * - * @param[in] g The driver structure - * @param[in] g->p.x,g->p.y The area position - * @param[in] g->p.cx,g->p.cy The area size - * - * @note The parameter variables must not be altered by the driver. - */ - LLDSPEC void gdisp_lld_set_clip(GDisplay *g); - #endif - - #ifdef __cplusplus - } - #endif - -#else - #define gdisp_lld_init(g) gvmt(g)->init(g) - #define gdisp_lld_deinit(g) gvmt(g)->deinit(g) - #define gdisp_lld_flush(g) gvmt(g)->flush(g) - #define gdisp_lld_write_start(g) gvmt(g)->writestart(g) - #define gdisp_lld_write_pos(g) gvmt(g)->writepos(g) - #define gdisp_lld_write_color(g) gvmt(g)->writecolor(g) - #define gdisp_lld_write_stop(g) gvmt(g)->writestop(g) - #define gdisp_lld_read_start(g) gvmt(g)->readstart(g) - #define gdisp_lld_read_color(g) gvmt(g)->readcolor(g) - #define gdisp_lld_read_stop(g) gvmt(g)->readstop(g) - #define gdisp_lld_draw_pixel(g) gvmt(g)->pixel(g) - #define gdisp_lld_clear(g) gvmt(g)->clear(g) - #define gdisp_lld_fill_area(g) gvmt(g)->fill(g) - #define gdisp_lld_blit_area(g) gvmt(g)->blit(g) - #define gdisp_lld_get_pixel_color(g) gvmt(g)->get(g) - #define gdisp_lld_vertical_scroll(g) gvmt(g)->vscroll(g) - #define gdisp_lld_control(g) gvmt(g)->control(g) - #define gdisp_lld_query(g) gvmt(g)->query(g) - #define gdisp_lld_set_clip(g) gvmt(g)->setclip(g) -#endif - -//------------------------------------------------------------------------------------------------------------ - -// If compiling the driver then build the VMT and set the low level driver color macros. -#if IN_DRIVER - - // Make sure the driver has a valid model - #if !GDISP_HARDWARE_STREAM_WRITE && !GDISP_HARDWARE_DRAWPIXEL - #error "GDISP Driver: Either GDISP_HARDWARE_STREAM_WRITE or GDISP_HARDWARE_DRAWPIXEL must be TRUE" - #endif - - // If we are not using multiple displays then hard-code the VMT name (except for the pixmap driver) - #if !IS_MULTIPLE && !IN_PIXMAP_DRIVER - #undef GDISP_DRIVER_VMT - #define GDISP_DRIVER_VMT GDISPVMT_OnlyOne - #endif - - // Default the flags if the driver doesn't specify any - #ifndef GDISP_DRIVER_VMT_FLAGS - #define GDISP_DRIVER_VMT_FLAGS 0 - #endif - - // Routines needed by the general driver VMT - #ifdef __cplusplus - extern "C" { - #endif - bool_t _gdispInitDriver(GDriver *g, void *param, unsigned driverinstance, unsigned systeminstance); - void _gdispPostInitDriver(GDriver *g); - void _gdispDeInitDriver(GDriver *g); - #ifdef __cplusplus - } - #endif - - // Build the VMT - const GDISPVMT const GDISP_DRIVER_VMT[1] = {{ - { GDRIVER_TYPE_DISPLAY, 0, sizeof(GDisplay), _gdispInitDriver, _gdispPostInitDriver, _gdispDeInitDriver }, - gdisp_lld_init, - #if GDISP_HARDWARE_DEINIT - gdisp_lld_deinit, - #else - 0, - #endif - #if GDISP_HARDWARE_STREAM_WRITE - gdisp_lld_write_start, - #if GDISP_HARDWARE_STREAM_POS - gdisp_lld_write_pos, - #else - 0, - #endif - gdisp_lld_write_color, - gdisp_lld_write_stop, - #else - 0, 0, 0, 0, - #endif - #if GDISP_HARDWARE_STREAM_READ - gdisp_lld_read_start, - gdisp_lld_read_color, - gdisp_lld_read_stop, - #else - 0, 0, 0, - #endif - #if GDISP_HARDWARE_DRAWPIXEL - gdisp_lld_draw_pixel, - #else - 0, - #endif - #if GDISP_HARDWARE_CLEARS - gdisp_lld_clear, - #else - 0, - #endif - #if GDISP_HARDWARE_FILLS - gdisp_lld_fill_area, - #else - 0, - #endif - #if GDISP_HARDWARE_BITFILLS - gdisp_lld_blit_area, - #else - 0, - #endif - #if GDISP_HARDWARE_PIXELREAD - gdisp_lld_get_pixel_color, - #else - 0, - #endif - #if GDISP_HARDWARE_SCROLL && GDISP_NEED_SCROLL - gdisp_lld_vertical_scroll, - #else - 0, - #endif - #if GDISP_HARDWARE_CONTROL && GDISP_NEED_CONTROL - gdisp_lld_control, - #else - 0, - #endif - #if GDISP_HARDWARE_QUERY && GDISP_NEED_QUERY - gdisp_lld_query, - #else - 0, - #endif - #if GDISP_HARDWARE_CLIP && (GDISP_NEED_CLIP || GDISP_NEED_VALIDATION) - gdisp_lld_set_clip, - #else - 0, - #endif - #if GDISP_HARDWARE_FLUSH - gdisp_lld_flush, - #else - 0, - #endif - }}; - - //-------------------------------------------------------------------------------------------------------- - - /* Low level driver pixel format information */ - //------------------------- - // True-Color color system - //------------------------- - #if GDISP_LLD_PIXELFORMAT & GDISP_COLORSYSTEM_TRUECOLOR - #define LLDCOLOR_SYSTEM GDISP_COLORSYSTEM_TRUECOLOR - - // Calculate the number of bits - #define LLDCOLOR_BITS_R ((GDISP_LLD_PIXELFORMAT>>8) & 0x0F) - #define LLDCOLOR_BITS_G ((GDISP_LLD_PIXELFORMAT>>4) & 0x0F) - #define LLDCOLOR_BITS_B ((GDISP_LLD_PIXELFORMAT>>0) & 0x0F) - #define LLDCOLOR_BITS (LLDCOLOR_BITS_R + LLDCOLOR_BITS_G + LLDCOLOR_BITS_B) - - // From the number of bits determine COLOR_TYPE, COLOR_TYPE_BITS and masking - #if LLDCOLOR_BITS <= 8 - #define LLDCOLOR_TYPE uint8_t - #define LLDCOLOR_TYPE_BITS 8 - #elif LLDCOLOR_BITS <= 16 - #define LLDCOLOR_TYPE uint16_t - #define LLDCOLOR_TYPE_BITS 16 - #elif LLDCOLOR_BITS <= 32 - #define LLDCOLOR_TYPE uint32_t - #define LLDCOLOR_TYPE_BITS 32 - #else - #error "GDISP: Cannot define low level driver color types with more than 32 bits" - #endif - #if LLDCOLOR_TYPE_BITS == LLDCOLOR_BITS - #define LLDCOLOR_NEEDS_MASK FALSE - #else - #define LLDCOLOR_NEEDS_MASK TRUE - #endif - #define LLDCOLOR_MASK() ((1 << LLDCOLOR_BITS)-1) - - // Calculate the component bit shifts - #if (GDISP_LLD_PIXELFORMAT & GDISP_COLORSYSTEM_MASK) == GDISP_COLORSYSTEM_RGB - #define LLDCOLOR_SHIFT_R (LLDCOLOR_BITS_B+LLDCOLOR_BITS_G) - #define LLDCOLOR_SHIFT_G LLDCOLOR_BITS_B - #define LLDCOLOR_SHIFT_B 0 - #else - #define LLDCOLOR_SHIFT_B (LLDCOLOR_BITS_R+LLDCOLOR_BITS_G) - #define LLDCOLOR_SHIFT_G LLDCOLOR_BITS_R - #define LLDCOLOR_SHIFT_R 0 - #endif - - // Calculate LLDRED_OF, LLDGREEN_OF, LLDBLUE_OF and LLDRGB2COLOR - #if LLDCOLOR_BITS_R + LLDCOLOR_SHIFT_R == 8 - #define LLDRED_OF(c) ((c) & (((1<<LLDCOLOR_BITS_R)-1) << LLDCOLOR_SHIFT_R)) - #define LLDRGB2COLOR_R(r) ((LLDCOLOR_TYPE)((r) & (0xFF & ~((1<<(8-LLDCOLOR_BITS_R))-1)))) - #elif LLDCOLOR_BITS_R + LLDCOLOR_SHIFT_R > 8 - #define LLDRED_OF(c) (((c) & (((1<<LLDCOLOR_BITS_R)-1) << LLDCOLOR_SHIFT_R)) >> (LLDCOLOR_BITS_R+LLDCOLOR_SHIFT_R-8)) - #define LLDRGB2COLOR_R(r) (((LLDCOLOR_TYPE)((r) & (0xFF & ~((1<<(8-LLDCOLOR_BITS_R))-1)))) << (LLDCOLOR_BITS_R+LLDCOLOR_SHIFT_R-8)) - #else // LLDCOLOR_BITS_R + LLDCOLOR_SHIFT_R < 8 - #define LLDRED_OF(c) (((c) & (((1<<LLDCOLOR_BITS_R)-1) << LLDCOLOR_SHIFT_R)) << (8-(LLDCOLOR_BITS_R+LLDCOLOR_SHIFT_R))) - #define LLDRGB2COLOR_R(r) (((LLDCOLOR_TYPE)((r) & (0xFF & ~((1<<(8-LLDCOLOR_BITS_R))-1)))) >> (8-(LLDCOLOR_BITS_R+LLDCOLOR_SHIFT_R))) - #endif - #if LLDCOLOR_BITS_G + LLDCOLOR_SHIFT_G == 8 - #define LLDGREEN_OF(c) ((c) & (((1<<LLDCOLOR_BITS_G)-1) << LLDCOLOR_SHIFT_G)) - #define LLDRGB2COLOR_G(g) ((LLDCOLOR_TYPE)((g) & (0xFF & ~((1<<(8-LLDCOLOR_BITS_G))-1)))) - #elif LLDCOLOR_BITS_G + LLDCOLOR_SHIFT_G > 8 - #define LLDGREEN_OF(c) (((c) & (((1<<LLDCOLOR_BITS_G)-1) << LLDCOLOR_SHIFT_G)) >> (LLDCOLOR_BITS_G+LLDCOLOR_SHIFT_G-8)) - #define LLDRGB2COLOR_G(g) (((LLDCOLOR_TYPE)((g) & (0xFF & ~((1<<(8-LLDCOLOR_BITS_G))-1)))) << (LLDCOLOR_BITS_G+LLDCOLOR_SHIFT_G-8)) - #else // LLDCOLOR_BITS_G + LLDCOLOR_SHIFT_G < 8 - #define LLDGREEN_OF(c) (((c) & (((1<<LLDCOLOR_BITS_G)-1) << LLDCOLOR_SHIFT_G)) << (8-(LLDCOLOR_BITS_G+LLDCOLOR_SHIFT_G))) - #define LLDRGB2COLOR_G(g) (((LLDCOLOR_TYPE)((g) & (0xFF & ~((1<<(8-LLDCOLOR_BITS_G))-1)))) >> (8-(LLDCOLOR_BITS_LLDG+COLOR_SHIFT_G))) - #endif - #if LLDCOLOR_BITS_B + LLDCOLOR_SHIFT_B == 8 - #define LLDBLUE_OF(c) ((c) & (((1<<LLDCOLOR_BITS_B)-1) << LLDCOLOR_SHIFT_B)) - #define LLDRGB2COLOR_B(b) ((LLDCOLOR_TYPE)((b) & (0xFF & ~((1<<(8-LLDCOLOR_BITS_B))-1)))) - #elif LLDCOLOR_BITS_B + LLDCOLOR_SHIFT_B > 8 - #define LLDBLUE_OF(c) (((c) & (((1<<LLDCOLOR_BITS_B)-1) << LLDCOLOR_SHIFT_B)) >> (LLDCOLOR_BITS_B+LLDCOLOR_SHIFT_B-8)) - #define LLDRGB2COLOR_B(b) (((LLDCOLOR_TYPE)((b) & (0xFF & ~((1<<(8-LLDCOLOR_BITS_B))-1)))) << (LLDCOLOR_BITS_B+LLDCOLOR_SHIFT_B-8)) - #else // LLDCOLOR_BITS_B + LLDCOLOR_SHIFT_B < 8 - #define LLDBLUE_OF(c) (((c) & (((1<<LLDCOLOR_BITS_B)-1) << LLDCOLOR_SHIFT_B)) << (8-(LLDCOLOR_BITS_B+LLDCOLOR_SHIFT_B))) - #define LLDRGB2COLOR_B(b) (((COLOR_TYPE)((b) & (0xFF & ~((1<<(8-LLDCOLOR_BITS_B))-1)))) >> (8-(LLDCOLOR_BITS_B+LLDCOLOR_SHIFT_B))) - #endif - #define LLDLUMA_OF(c) ((LLDRED_OF(c)+((uint16_t)LLDGREEN_OF(c)<<1)+LLDBLUE_OF(c))>>2) - #define LLDEXACT_RED_OF(c) (((uint16_t)(((c)>>LLDCOLOR_SHIFT_R)&((1<<LLDCOLOR_BITS_R)-1))*255)/((1<<LLDCOLOR_BITS_R)-1)) - #define LLDEXACT_GREEN_OF(c) (((uint16_t)(((c)>>LLDCOLOR_SHIFT_G)&((1<<LLDCOLOR_BITS_G)-1))*255)/((1<<LLDCOLOR_BITS_G)-1)) - #define LLDEXACT_BLUE_OF(c) (((uint16_t)(((c)>>LLDCOLOR_SHIFT_B)&((1<<LLDCOLOR_BITS_B)-1))*255)/((1<<LLDCOLOR_BITS_B)-1)) - #define LLDEXACT_LUMA_OF(c) ((LLDEXACT_RED_OF(c)+((uint16_t)LLDEXACT_GREEN_OF(c)<<1)+LLDEXACT_BLUE_OF(c))>>2) - #define LLDLUMA2COLOR(l) (LLDRGB2COLOR_R(l) | LLDRGB2COLOR_G(l) | LLDRGB2COLOR_B(l)) - #define LLDRGB2COLOR(r,g,b) (LLDRGB2COLOR_R(r) | LLDRGB2COLOR_G(g) | LLDRGB2COLOR_B(b)) - - // Calculate LLDHTML2COLOR - #if LLDCOLOR_BITS_R + LLDCOLOR_SHIFT_R == 24 - #define LLDHTML2COLOR_R(h) ((h) & ((0xFF & ~((1<<(8-LLDCOLOR_BITS_R))-1))<<16)) - #elif COLOR_BITS_R + COLOR_SHIFT_R > 24 - #define LLDHTML2COLOR_R(h) (((h) & ((0xFF & ~((1<<(8-LLDCOLOR_BITS_R))-1))<<16)) << (LLDCOLOR_BITS_R+LLDCOLOR_SHIFT_R-24)) - #else // COLOR_BITS_R + COLOR_SHIFT_R < 24 - #define LLDHTML2COLOR_R(h) (((h) & ((0xFF & ~((1<<(8-LLDCOLOR_BITS_R))-1))<<16)) >> (24-(LLDCOLOR_BITS_R+LLDCOLOR_SHIFT_R))) - #endif - #if LLDCOLOR_BITS_G + LLDCOLOR_SHIFT_G == 16 - #define LLDHTML2COLOR_G(h) ((h) & ((0xFF & ~((1<<(8-LLDCOLOR_BITS_G))-1))<<8)) - #elif LLDCOLOR_BITS_G + LLDCOLOR_SHIFT_G > 16 - #define LLDHTML2COLOR_G(h) (((h) & ((0xFF & ~((1<<(8-LLDCOLOR_BITS_G))-1))<<8)) << (LLDCOLOR_BITS_G+LLDCOLOR_SHIFT_G-16)) - #else // LLDCOLOR_BITS_G + LLDCOLOR_SHIFT_G < 16 - #define LLDHTML2COLOR_G(h) (((h) & ((0xFF & ~((1<<(8-LLDCOLOR_BITS_G))-1))<<8)) >> (16-(LLDCOLOR_BITS_G+LLDCOLOR_SHIFT_G))) - #endif - #if LLDCOLOR_BITS_B + LLDCOLOR_SHIFT_B == 8 - #define LLDHTML2COLOR_B(h) ((h) & (0xFF & ~((1<<(8-LLDCOLOR_BITS_B))-1))) - #elif LLDCOLOR_BITS_B + LLDCOLOR_SHIFT_B > 8 - #define LLDHTML2COLOR_B(h) (((h) & (0xFF & ~((1<<(8-LLDCOLOR_BITS_B))-1))) << (LLDCOLOR_BITS_B+LLDCOLOR_SHIFT_B-8)) - #else // LLDCOLOR_BITS_B + LLDCOLOR_SHIFT_B < 8 - #define LLDHTML2COLOR_B(h) (((h) & (0xFF & ~((1<<(8-LLDCOLOR_BITS_B))-1))) >> (8-(LLDCOLOR_BITS_B+LLDCOLOR_SHIFT_B))) - #endif - #define LLDHTML2COLOR(h) ((LLDCOLOR_TYPE)(LLDHTML2COLOR_R(h) | LLDHTML2COLOR_G(h) | LLDHTML2COLOR_B(h))) - - //------------------------- - // Gray-scale color system - //------------------------- - #elif (GDISP_LLD_PIXELFORMAT & GDISP_COLORSYSTEM_MASK) == GDISP_COLORSYSTEM_GRAYSCALE - #define LLDCOLOR_SYSTEM GDISP_COLORSYSTEM_GRAYSCALE - - // Calculate the number of bits and shifts - #define LLDCOLOR_BITS (GDISP_LLD_PIXELFORMAT & 0xFF) - #define LLDCOLOR_BITS_R LLDCOLOR_BITS - #define LLDCOLOR_BITS_G LLDCOLOR_BITS - #define LLDCOLOR_BITS_B LLDCOLOR_BITS - #define LLDCOLOR_SHIFT_R 0 - #define LLDCOLOR_SHIFT_G 0 - #define LLDCOLOR_SHIFT_B 0 - - // From the number of bits determine COLOR_TYPE, COLOR_TYPE_BITS and masking - #if LLDCOLOR_BITS <= 8 - #define LLDCOLOR_TYPE uint8_t - #define LLDCOLOR_TYPE_BITS 8 - #else - #error "GDISP: Cannot define gray-scale low level driver color types with more than 8 bits" - #endif - #if LLDCOLOR_TYPE_BITS == LLDCOLOR_BITS - #define LLDCOLOR_NEEDS_MASK FALSE - #else - #define LLDCOLOR_NEEDS_MASK TRUE - #endif - #define LLDCOLOR_MASK() ((1 << LLDCOLOR_BITS)-1) - - #if COLOR_BITS == 1 - #define LLDRGB2COLOR(r,g,b) (((r)|(g)|(b)) ? 1 : 0) - #define LLDLUMA2COLOR(l) ((l) ? 1 : 0) - #define LLDHTML2COLOR(h) ((h) ? 1 : 0) - #define LLDLUMA_OF(c) ((c) ? 255 : 0) - #define LLDEXACT_LUMA_OF(c) LLDLUMA_OF(c) - #else - // They eye is more sensitive to green - #define LLDRGB2COLOR(r,g,b) ((LLDCOLOR_TYPE)(((uint16_t)(r)+(g)+(g)+(b)) >> (10-LLDCOLOR_BITS))) - #define LLDLUMA2COLOR(l) ((LLDCOLOR_TYPE)((l)>>(8-LLDCOLOR_BITS))) - #define LLDHTML2COLOR(h) ((LLDCOLOR_TYPE)(((((h)&0xFF0000)>>16)+(((h)&0x00FF00)>>7)+((h)&0x0000FF)) >> (10-LLDCOLOR_BITS))) - #define LLDLUMA_OF(c) (((c) & ((1<<LLDCOLOR_BITS)-1)) << (8-LLDCOLOR_BITS)) - #define LLDEXACT_LUMA_OF(c) ((((uint16_t)(c) & ((1<<LLDCOLOR_BITS)-1))*255)/((1<<LLDCOLOR_BITS)-1)) - #endif - - #define LLDRED_OF(c) LLDLUMA_OF(c) - #define LLDGREEN_OF(c) LLDLUMA_OF(c) - #define LLDBLUE_OF(c) LLDLUMA_OF(c) - #define LLDEXACT_RED_OF(c) LLDEXACT_LUMA_OF(c) - #define LLDEXACT_GREEN_OF(c) LLDEXACT_LUMA_OF(c) - #define LLDEXACT_BLUE_OF(c) LLDEXACT_LUMA_OF(c) - - //------------------------- - // Palette color system - //------------------------- - #elif (GDISP_LLD_PIXELFORMAT & GDISP_COLORSYSTEM_MASK) == GDISP_COLORSYSTEM_PALETTE - #define LLDCOLOR_SYSTEM GDISP_COLORSYSTEM_PALETTE - - #error "GDISP: A palette color system for low level drivers is not currently supported" - - //------------------------- - // Some other color system - //------------------------- - #else - #error "GDISP: Unsupported color system for low level drivers" - #endif - - /* Which is the larger color type */ - #if COLOR_BITS > LLDCOLOR_BITS - #define LARGER_COLOR_BITS COLOR_BITS - #define LARGER_COLOR_TYPE COLOR_TYPE - #else - #define LARGER_COLOR_BITS LLDCOLOR_BITS - #define LARGER_COLOR_TYPE LLDCOLOR_TYPE - #endif - - /** - * @brief Controls color conversion accuracy for a low level driver - * @details Should higher precision be used when converting colors. - * @note Color conversion is only necessary if GDISP_PIXELFORMAT != GDISP_LLD_PIXELFORMAT - * @note It only makes sense to turn this on if you have a high bit depth display but - * are running the application in low bit depths. - * @note To achieve higher color accuracy bit shifting is replaced with multiplies and divides. - */ - #ifndef GDISP_HARDWARE_USE_EXACT_COLOR - #if LLDCOLOR_BITS_R - COLOR_BITS_R >= LLDCOLOR_BITS_R/2 || LLDCOLOR_BITS_G - COLOR_BITS_G >= LLDCOLOR_BITS_G/2 || LLDCOLOR_BITS_B - COLOR_BITS_B >= LLDCOLOR_BITS_B/2 - #define GDISP_HARDWARE_USE_EXACT_COLOR TRUE - #else - #define GDISP_HARDWARE_USE_EXACT_COLOR FALSE - #endif - #endif - - /* Low level driver pixel format conversion functions */ - #if GDISP_PIXELFORMAT == GDISP_LLD_PIXELFORMAT || defined(__DOXYGEN__) - /** - * @brief Convert from a standard color format to the low level driver pixel format - * @note For use only by low level drivers - */ - #define gdispColor2Native(c) (c) - /** - * @brief Convert from a low level driver pixel format to the standard color format - * @note For use only by low level drivers - */ - #define gdispNative2Color(c) (c) - #else - static LLDCOLOR_TYPE gdispColor2Native(color_t c) { - #if COLOR_SYSTEM == GDISP_COLORSYSTEM_GRAYSCALE || LLDCOLOR_SYSTEM == GDISP_COLORSYSTEM_GRAYSCALE - #if GDISP_HARDWARE_USE_EXACT_COLOR - return LLDLUMA2COLOR(EXACT_LUMA_OF(c)); - #else - return LLDLUMA2COLOR(LUMA_OF(c)); - #endif - #elif COLOR_SYSTEM == GDISP_COLORSYSTEM_TRUECOLOR && LLDCOLOR_SYSTEM == GDISP_COLORSYSTEM_TRUECOLOR - #if GDISP_HARDWARE_USE_EXACT_COLOR - return LLDRGB2COLOR(EXACT_RED_OF(c), EXACT_GREEN_OF(c), EXACT_BLUE_OF(c)); - #else - return LLDRGB2COLOR(RED_OF(c), GREEN_OF(c), BLUE_OF(c)); - #endif - #else - #error "GDISP: This pixel format conversion is not supported yet" - #endif - } - static color_t gdispNative2Color(LLDCOLOR_TYPE c) { - #if COLOR_SYSTEM == GDISP_COLORSYSTEM_GRAYSCALE || LLDCOLOR_SYSTEM == GDISP_COLORSYSTEM_GRAYSCALE - #if GDISP_HARDWARE_USE_EXACT_COLOR - return LUMA2COLOR(LLDEXACT_LUMA_OF(c)); - #else - return LUMA2COLOR(LLDLUMA_OF(c)); - #endif - #elif COLOR_SYSTEM == GDISP_COLORSYSTEM_TRUECOLOR && LLDCOLOR_SYSTEM == GDISP_COLORSYSTEM_TRUECOLOR - #if GDISP_HARDWARE_USE_EXACT_COLOR - return RGB2COLOR(LLDEXACT_RED_OF(c), LLDEXACT_GREEN_OF(c), LLDEXACT_BLUE_OF(c)); - #else - return RGB2COLOR(LLDRED_OF(c), LLDGREEN_OF(c), LLDBLUE_OF(c)); - #endif - #else - #error "GDISP: This pixel format conversion is not supported yet" - #endif - } - #endif - -#endif - -//------------------------------------------------------------------------------------------------------------ - -#undef IN_PIXMAP_DRIVER -#undef IS_MULTIPLE -#undef IN_DRIVER -#undef USE_VMT -#endif /* GFX_USE_GDISP */ - -#endif /* _GDISP_LLD_H */ -/** @} */ diff --git a/src/gdisp/gdisp.c b/src/gdisp/gdisp.c new file mode 100644 index 00000000..cebed1ef --- /dev/null +++ b/src/gdisp/gdisp.c @@ -0,0 +1,3370 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +#include "gfx.h" + +#if GFX_USE_GDISP + +/* Include the low level driver information */ +#include "gdisp_driver.h" + +#if 1 + #undef INLINE + #if defined(__KEIL__) || defined(__C51__) + #define INLINE __inline + #else + #define INLINE inline + #endif +#else + #undef INLINE + #define INLINE +#endif + +// Number of milliseconds for the startup logo - 0 means disabled. +#if GDISP_NEED_STARTUP_LOGO + #define GDISP_STARTUP_LOGO_TIMEOUT 1000 +#else + #define GDISP_STARTUP_LOGO_TIMEOUT 0 +#endif + +/*===========================================================================*/ +/* Driver local variables. */ +/*===========================================================================*/ + +#if GDISP_NEED_TIMERFLUSH + static GTimer FlushTimer; +#endif + +GDisplay *GDISP; + +#if GDISP_NEED_MULTITHREAD + #define MUTEX_INIT(g) gfxMutexInit(&(g)->mutex) + #define MUTEX_ENTER(g) gfxMutexEnter(&(g)->mutex) + #define MUTEX_EXIT(g) gfxMutexExit(&(g)->mutex) + #define MUTEX_DEINIT(g) gfxMutexDestroy(&(g)->mutex) +#else + #define MUTEX_INIT(g) + #define MUTEX_ENTER(g) + #define MUTEX_EXIT(g) + #define MUTEX_DEINIT(g) +#endif + +#define NEED_CLIPPING (GDISP_HARDWARE_CLIP != TRUE && (GDISP_NEED_VALIDATION || GDISP_NEED_CLIP)) + +#if !NEED_CLIPPING + #define TEST_CLIP_AREA(g) +#elif GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT + #define TEST_CLIP_AREA(g) \ + if (!gvmt(g)->setclip) { \ + if ((g)->p.x < (g)->clipx0) { (g)->p.cx -= (g)->clipx0 - (g)->p.x; (g)->p.x = (g)->clipx0; } \ + if ((g)->p.y < (g)->clipy0) { (g)->p.cy -= (g)->clipy0 - (g)->p.y; (g)->p.y = (g)->clipy0; } \ + if ((g)->p.x + (g)->p.cx > (g)->clipx1) (g)->p.cx = (g)->clipx1 - (g)->p.x; \ + if ((g)->p.y + (g)->p.cy > (g)->clipy1) (g)->p.cy = (g)->clipy1 - (g)->p.y; \ + } \ + if ((g)->p.cx > 0 && (g)->p.cy > 0) +#else + #define TEST_CLIP_AREA(g) \ + if ((g)->p.x < (g)->clipx0) { (g)->p.cx -= (g)->clipx0 - (g)->p.x; (g)->p.x = (g)->clipx0; } \ + if ((g)->p.y < (g)->clipy0) { (g)->p.cy -= (g)->clipy0 - (g)->p.y; (g)->p.y = (g)->clipy0; } \ + if ((g)->p.x + (g)->p.cx > (g)->clipx1) (g)->p.cx = (g)->clipx1 - (g)->p.x; \ + if ((g)->p.y + (g)->p.cy > (g)->clipy1) (g)->p.cy = (g)->clipy1 - (g)->p.y; \ + if ((g)->p.cx > 0 && (g)->p.cy > 0) +#endif + +/*==========================================================================*/ +/* Internal functions. */ +/*==========================================================================*/ + +#if GDISP_HARDWARE_STREAM_POS && GDISP_HARDWARE_STREAM_WRITE + static INLINE void setglobalwindow(GDisplay *g) { + coord_t x, y; + x = g->p.x; y = g->p.y; + g->p.x = g->p.y = 0; + g->p.cx = g->g.Width; g->p.cy = g->g.Height; + gdisp_lld_write_start(g); + g->p.x = x; g->p.y = y; + g->flags |= GDISP_FLG_SCRSTREAM; + } +#endif + +#if GDISP_NEED_AUTOFLUSH && GDISP_HARDWARE_FLUSH == HARDWARE_AUTODETECT + #define autoflush_stopdone(g) if (gvmt(g)->flush) gdisp_lld_flush(g) +#elif GDISP_NEED_AUTOFLUSH && GDISP_HARDWARE_FLUSH + #define autoflush_stopdone(g) gdisp_lld_flush(g) +#else + #define autoflush_stopdone(g) +#endif + +#if GDISP_HARDWARE_STREAM_POS && GDISP_HARDWARE_STREAM_WRITE + #define autoflush(g) \ + { \ + if ((g->flags & GDISP_FLG_SCRSTREAM)) { \ + gdisp_lld_write_stop(g); \ + g->flags &= ~GDISP_FLG_SCRSTREAM; \ + } \ + autoflush_stopdone(g); \ + } +#else + #define autoflush(g) autoflush_stopdone(g) +#endif + +// drawpixel(g) +// Parameters: x,y +// Alters: cx, cy (if using streaming) +// Does not clip +static INLINE void drawpixel(GDisplay *g) { + + // Best is hardware accelerated pixel draw + #if GDISP_HARDWARE_DRAWPIXEL + #if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT + if (gvmt(g)->pixel) + #endif + { + gdisp_lld_draw_pixel(g); + return; + } + #endif + + // Next best is cursor based streaming + #if GDISP_HARDWARE_DRAWPIXEL != TRUE && GDISP_HARDWARE_STREAM_POS && GDISP_HARDWARE_STREAM_WRITE + #if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT + if (gvmt(g)->writepos) + #endif + { + if (!(g->flags & GDISP_FLG_SCRSTREAM)) + setglobalwindow(g); + gdisp_lld_write_pos(g); + gdisp_lld_write_color(g); + return; + } + #endif + + // Worst is general streaming + #if GDISP_HARDWARE_DRAWPIXEL != TRUE && GDISP_HARDWARE_STREAM_POS != TRUE && GDISP_HARDWARE_STREAM_WRITE + // The following test is unneeded because we are guaranteed to have streaming if we don't have drawpixel + //#if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT + // if (gvmt(g)->writestart) + //#endif + { + g->p.cx = g->p.cy = 1; + gdisp_lld_write_start(g); + gdisp_lld_write_color(g); + gdisp_lld_write_stop(g); + return; + } + #endif +} + +// drawpixel_clip(g) +// Parameters: x,y +// Alters: cx, cy (if using streaming) +#if NEED_CLIPPING + static INLINE void drawpixel_clip(GDisplay *g) { + #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT + if (!gvmt(g)->setclip) + #endif + { + if (g->p.x < g->clipx0 || g->p.x >= g->clipx1 || g->p.y < g->clipy0 || g->p.y >= g->clipy1) + return; + } + drawpixel(g); + } +#else + #define drawpixel_clip(g) drawpixel(g) +#endif + +// fillarea(g) +// Parameters: x,y cx,cy and color +// Alters: nothing +// Note: This is not clipped +// Resets the streaming area if GDISP_HARDWARE_STREAM_WRITE and GDISP_HARDWARE_STREAM_POS is set. +static INLINE void fillarea(GDisplay *g) { + + // Best is hardware accelerated area fill + #if GDISP_HARDWARE_FILLS + #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT + if (gvmt(g)->fill) + #endif + { + gdisp_lld_fill_area(g); + return; + } + #endif + + // Next best is hardware streaming + #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE + #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT + if (gvmt(g)->writestart) + #endif + { + uint32_t area; + + #if GDISP_HARDWARE_STREAM_POS + if ((g->flags & GDISP_FLG_SCRSTREAM)) { + gdisp_lld_write_stop(g); + g->flags &= ~GDISP_FLG_SCRSTREAM; + } + #endif + + area = (uint32_t)g->p.cx * g->p.cy; + gdisp_lld_write_start(g); + #if GDISP_HARDWARE_STREAM_POS + #if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT + if (gvmt(g)->writepos) + #endif + gdisp_lld_write_pos(g); + #endif + for(; area; area--) + gdisp_lld_write_color(g); + gdisp_lld_write_stop(g); + return; + } + #endif + + // Worst is pixel drawing + #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_DRAWPIXEL + // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming + //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT + // if (gvmt(g)->pixel) + //#endif + { + coord_t x0, y0, x1, y1; + + x0 = g->p.x; + y0 = g->p.y; + x1 = g->p.x + g->p.cx; + y1 = g->p.y + g->p.cy; + for(; g->p.y < y1; g->p.y++, g->p.x = x0) + for(; g->p.x < x1; g->p.x++) + gdisp_lld_draw_pixel(g); + g->p.y = y0; + return; + } + #endif +} + +// Parameters: x,y and x1 +// Alters: x,y x1,y1 cx,cy +// Assumes the window covers the screen and a write_stop() will occur later +// if GDISP_HARDWARE_STREAM_WRITE and GDISP_HARDWARE_STREAM_POS is set. +static void hline_clip(GDisplay *g) { + // Swap the points if necessary so it always goes from x to x1 + if (g->p.x1 < g->p.x) { + g->p.cx = g->p.x; g->p.x = g->p.x1; g->p.x1 = g->p.cx; + } + + // Clipping + #if NEED_CLIPPING + #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT + if (!gvmt(g)->setclip) + #endif + { + if (g->p.y < g->clipy0 || g->p.y >= g->clipy1) return; + if (g->p.x < g->clipx0) g->p.x = g->clipx0; + if (g->p.x1 >= g->clipx1) g->p.x1 = g->clipx1 - 1; + if (g->p.x1 < g->p.x) return; + } + #endif + + // This is an optimization for the point case. It is only worthwhile however if we + // have hardware fills or if we support both hardware pixel drawing and hardware streaming + #if GDISP_HARDWARE_FILLS || (GDISP_HARDWARE_DRAWPIXEL && GDISP_HARDWARE_STREAM_WRITE) + // Is this a point + if (g->p.x == g->p.x1) { + drawpixel(g); + return; + } + #endif + + // Best is hardware accelerated area fill + #if GDISP_HARDWARE_FILLS + #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT + if (gvmt(g)->fill) + #endif + { + g->p.cx = g->p.x1 - g->p.x + 1; + g->p.cy = 1; + gdisp_lld_fill_area(g); + return; + } + #endif + + // Next best is cursor based streaming + #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_POS && GDISP_HARDWARE_STREAM_WRITE + #if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT + if (gvmt(g)->writepos) + #endif + { + if (!(g->flags & GDISP_FLG_SCRSTREAM)) + setglobalwindow(g); + g->p.cx = g->p.x1 - g->p.x + 1; + gdisp_lld_write_pos(g); + do { gdisp_lld_write_color(g); } while(--g->p.cx); + return; + } + #endif + + // Next best is streaming + #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_POS != TRUE && GDISP_HARDWARE_STREAM_WRITE + #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT + if (gvmt(g)->writestart) + #endif + { + g->p.cx = g->p.x1 - g->p.x + 1; + g->p.cy = 1; + gdisp_lld_write_start(g); + do { gdisp_lld_write_color(g); } while(--g->p.cx); + gdisp_lld_write_stop(g); + return; + } + #endif + + // Worst is drawing pixels + #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_DRAWPIXEL + // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming + //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT + // if (gvmt(g)->pixel) + //#endif + { + for(; g->p.x <= g->p.x1; g->p.x++) + gdisp_lld_draw_pixel(g); + return; + } + #endif +} + +// Parameters: x,y and y1 +// Alters: x,y x1,y1 cx,cy +static void vline_clip(GDisplay *g) { + // Swap the points if necessary so it always goes from y to y1 + if (g->p.y1 < g->p.y) { + g->p.cy = g->p.y; g->p.y = g->p.y1; g->p.y1 = g->p.cy; + } + + // Clipping + #if NEED_CLIPPING + #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT + if (!gvmt(g)->setclip) + #endif + { + if (g->p.x < g->clipx0 || g->p.x >= g->clipx1) return; + if (g->p.y < g->clipy0) g->p.y = g->clipy0; + if (g->p.y1 >= g->clipy1) g->p.y1 = g->clipy1 - 1; + if (g->p.y1 < g->p.y) return; + } + #endif + + // This is an optimization for the point case. It is only worthwhile however if we + // have hardware fills or if we support both hardware pixel drawing and hardware streaming + #if GDISP_HARDWARE_FILLS || (GDISP_HARDWARE_DRAWPIXEL && GDISP_HARDWARE_STREAM_WRITE) || (GDISP_HARDWARE_STREAM_POS && GDISP_HARDWARE_STREAM_WRITE) + // Is this a point + if (g->p.y == g->p.y1) { + drawpixel(g); + return; + } + #endif + + // Best is hardware accelerated area fill + #if GDISP_HARDWARE_FILLS + #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT + if (gvmt(g)->fill) + #endif + { + g->p.cy = g->p.y1 - g->p.y + 1; + g->p.cx = 1; + gdisp_lld_fill_area(g); + return; + } + #endif + + // Next best is streaming + #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE + #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT + if (gvmt(g)->writestart) + #endif + { + #if GDISP_HARDWARE_STREAM_POS + if ((g->flags & GDISP_FLG_SCRSTREAM)) { + gdisp_lld_write_stop(g); + g->flags &= ~GDISP_FLG_SCRSTREAM; + } + #endif + g->p.cy = g->p.y1 - g->p.y + 1; + g->p.cx = 1; + gdisp_lld_write_start(g); + #if GDISP_HARDWARE_STREAM_POS + #if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT + if (gvmt(g)->writepos) + #endif + gdisp_lld_write_pos(g); + #endif + do { gdisp_lld_write_color(g); } while(--g->p.cy); + gdisp_lld_write_stop(g); + return; + } + #endif + + // Worst is drawing pixels + #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_DRAWPIXEL + // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming + //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT + // if (gvmt(g)->pixel) + //#endif + { + for(; g->p.y <= g->p.y1; g->p.y++) + gdisp_lld_draw_pixel(g); + return; + } + #endif +} + +// Parameters: x,y and x1,y1 +// Alters: x,y x1,y1 cx,cy +static void line_clip(GDisplay *g) { + int16_t dy, dx; + int16_t addx, addy; + int16_t P, diff, i; + + // Is this a horizontal line (or a point) + if (g->p.y == g->p.y1) { + hline_clip(g); + return; + } + + // Is this a vertical line (or a point) + if (g->p.x == g->p.x1) { + vline_clip(g); + return; + } + + // Not horizontal or vertical + + // Use Bresenham's line drawing algorithm. + // This should be replaced with fixed point slope based line drawing + // which is more efficient on modern processors as it branches less. + // When clipping is needed, all the clipping could also be done up front + // instead of on each pixel. + + if (g->p.x1 >= g->p.x) { + dx = g->p.x1 - g->p.x; + addx = 1; + } else { + dx = g->p.x - g->p.x1; + addx = -1; + } + if (g->p.y1 >= g->p.y) { + dy = g->p.y1 - g->p.y; + addy = 1; + } else { + dy = g->p.y - g->p.y1; + addy = -1; + } + + if (dx >= dy) { + dy <<= 1; + P = dy - dx; + diff = P - dx; + + for(i=0; i<=dx; ++i) { + drawpixel_clip(g); + if (P < 0) { + P += dy; + g->p.x += addx; + } else { + P += diff; + g->p.x += addx; + g->p.y += addy; + } + } + } else { + dx <<= 1; + P = dx - dy; + diff = P - dy; + + for(i=0; i<=dy; ++i) { + drawpixel_clip(g); + if (P < 0) { + P += dx; + g->p.y += addy; + } else { + P += diff; + g->p.x += addx; + g->p.y += addy; + } + } + } +} + +#if GDISP_STARTUP_LOGO_TIMEOUT > 0 + static bool_t initDone; + static void StartupLogoDisplay(GDisplay *g) { + coord_t x, y, w; + const coord_t * p; + static const coord_t blks[] = { + // u + 2, 6, 1, 10, + 3, 11, 4, 1, + 6, 6, 1, 6, + // G + 8, 0, 1, 12, + 9, 0, 6, 1, + 9, 11, 6, 1, + 14, 6, 1, 5, + 12, 6, 2, 1, + // F + 16, 0, 1, 12, + 17, 0, 6, 1, + 17, 6, 3, 1, + // X + 22, 6, 7, 1, + 24, 0, 1, 6, + 22, 7, 1, 5, + 28, 0, 1, 6, + 26, 7, 1, 5, + }; + + // Get a starting position and a scale + // Work on a 8x16 grid for each char, 4 chars (uGFX) in 1 line, using half the screen + w = g->g.Width/(8*4*2); + if (!w) w = 1; + x = (g->g.Width - (8*4)*w)/2; + y = (g->g.Height - (16*1)*w)/2; + + // Simple but crude! + for(p = blks; p < blks+sizeof(blks)/sizeof(blks[0]); p+=4) + gdispGFillArea(g, x+p[0]*w, y+p[1]*w, p[2]*w, p[3]*w, Blue); + } +#endif + +#if GDISP_NEED_TIMERFLUSH + static void FlushTimerFn(void *param) { + GDisplay * g; + (void) param; + + for(g = (GDisplay *)gdriverGetNext(GDRIVER_TYPE_DISPLAY, 0); g; g = (GDisplay *)gdriverGetNext(GDRIVER_TYPE_DISPLAY, (GDriver *)g)) + gdispGFlush(g); + } +#endif + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +typedef const GDISPVMT const GDISPVMTLIST[]; + +void _gdispInit(void) +{ + // GDISP_DRIVER_LIST is defined - create each driver instance + #if defined(GDISP_DRIVER_LIST) + { + unsigned i; + + extern GDISPVMTLIST GDISP_DRIVER_LIST; + static GDISPVMTLIST dclist[] = {GDISP_DRIVER_LIST}; + + for(i = 0; i < sizeof(dclist)/sizeof(dclist[0]); i++) + if (!(dclist[i]->d.flags & GDISP_VFLG_DYNAMICONLY)) + gdriverRegister(&dclist[i]->d, 0); + } + #elif GDISP_TOTAL_DISPLAYS > 1 + { + unsigned i; + extern GDISPVMTLIST GDISPVMT_OnlyOne; + + if (!(GDISPVMT_OnlyOne->d.flags & GDISP_VFLG_DYNAMICONLY)) { + for(i = 0; i < GDISP_TOTAL_DISPLAYS; i++) + gdriverRegister(&GDISPVMT_OnlyOne->d, 0); + } + } + #else + { + extern GDISPVMTLIST GDISPVMT_OnlyOne; + + if (!(GDISPVMT_OnlyOne->d.flags & GDISP_VFLG_DYNAMICONLY)) + gdriverRegister(&GDISPVMT_OnlyOne->d, 0); + } + #endif + + // Re-clear the display after the timeout if we added the logo + #if GDISP_STARTUP_LOGO_TIMEOUT > 0 + { + GDisplay *g; + + gfxSleepMilliseconds(GDISP_STARTUP_LOGO_TIMEOUT); + + for(g = (GDisplay *)gdriverGetNext(GDRIVER_TYPE_DISPLAY, 0); g; g = (GDisplay *)gdriverGetNext(GDRIVER_TYPE_DISPLAY, (GDriver *)g)) { + gdispGClear(g, GDISP_STARTUP_COLOR); + #if GDISP_HARDWARE_FLUSH + gdispGFlush(g); + #endif + } + + initDone = TRUE; + } + #endif + + // Start the automatic timer flush (if required) + #if GDISP_NEED_TIMERFLUSH + gtimerInit(&FlushTimer); + gtimerStart(&FlushTimer, FlushTimerFn, 0, TRUE, GDISP_NEED_TIMERFLUSH); + #endif +} + +void _gdispDeinit(void) +{ + /* ToDo */ +} + +bool_t _gdispInitDriver(GDriver *g, void *param, unsigned driverinstance, unsigned systeminstance) { + #define gd ((GDisplay *)g) + bool_t ret; + + // Intialise fields + gd->systemdisplay = systeminstance; + gd->controllerdisplay = driverinstance; + gd->flags = 0; + gd->priv = param; + MUTEX_INIT(gd); + + // Call the driver init + MUTEX_ENTER(gd); + ret = gdisp_lld_init(gd); + MUTEX_EXIT(gd); + return ret; + + #undef gd +} + +void _gdispPostInitDriver(GDriver *g) { + #define gd ((GDisplay *)g) + + // Set orientation, clip + #if defined(GDISP_DEFAULT_ORIENTATION) && GDISP_NEED_CONTROL && GDISP_HARDWARE_CONTROL + #if GDISP_NEED_PIXMAP + // Pixmaps should stay in their created orientation (at least initially) + if (!(gvmt(gd)->d.flags & GDISP_VFLG_PIXMAP)) + #endif + gdispGControl(gd, GDISP_CONTROL_ORIENTATION, (void *)GDISP_DEFAULT_ORIENTATION); + #endif + #if GDISP_NEED_VALIDATION || GDISP_NEED_CLIP + gdispGSetClip(gd, 0, 0, gd->g.Width, gd->g.Height); + #endif + + // Clear the Screen + gdispGClear(gd, GDISP_STARTUP_COLOR); + + // Display the startup logo if this is a static initialised display + #if GDISP_STARTUP_LOGO_TIMEOUT > 0 + if (!initDone) + StartupLogoDisplay(gd); + #endif + + // Flush + #if GDISP_HARDWARE_FLUSH + gdispGFlush(gd); + #endif + + // If this is the first driver set GDISP + if (!GDISP) + GDISP = gd; + + #undef gd +} + +void _gdispDeInitDriver(GDriver *g) { + #define gd ((GDisplay *)g) + + if (GDISP == gd) + GDISP = (GDisplay *)gdriverGetInstance(GDRIVER_TYPE_DISPLAY, 0); + + #if GDISP_HARDWARE_DEINIT + #if GDISP_HARDWARE_DEINIT == HARDWARE_AUTODETECT + if (gvmt(gd)->deinit) + #endif + { + MUTEX_ENTER(gd); + gdisp_lld_deinit(gd); + MUTEX_EXIT(gd); + } + #endif + MUTEX_DEINIT(gd); + + #undef gd +} + +GDisplay *gdispGetDisplay(unsigned display) { + return (GDisplay *)gdriverGetInstance(GDRIVER_TYPE_DISPLAY, display); +} + +void gdispSetDisplay(GDisplay *g) { + if (g) GDISP = g; +} + +unsigned gdispGetDisplayCount(void) { + return gdriverInstanceCount(GDRIVER_TYPE_DISPLAY); +} + +coord_t gdispGGetWidth(GDisplay *g) { return g->g.Width; } +coord_t gdispGGetHeight(GDisplay *g) { return g->g.Height; } +powermode_t gdispGGetPowerMode(GDisplay *g) { return g->g.Powermode; } +orientation_t gdispGGetOrientation(GDisplay *g) { return g->g.Orientation; } +uint8_t gdispGGetBacklight(GDisplay *g) { return g->g.Backlight; } +uint8_t gdispGGetContrast(GDisplay *g) { return g->g.Contrast; } + +void gdispGFlush(GDisplay *g) { + #if GDISP_HARDWARE_FLUSH + #if GDISP_HARDWARE_FLUSH == HARDWARE_AUTODETECT + if (gvmt(g)->flush) + #endif + { + MUTEX_ENTER(g); + gdisp_lld_flush(g); + MUTEX_EXIT(g); + } + #else + (void) g; + #endif +} + +#if GDISP_NEED_STREAMING + void gdispGStreamStart(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy) { + MUTEX_ENTER(g); + + #if NEED_CLIPPING + #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT + if (!gvmt(g)->setclip) + #endif + // Test if the area is valid - if not then exit + if (x < g->clipx0 || x+cx > g->clipx1 || y < g->clipy0 || y+cy > g->clipy1) { + MUTEX_EXIT(g); + return; + } + #endif + + g->flags |= GDISP_FLG_INSTREAM; + + // Best is hardware streaming + #if GDISP_HARDWARE_STREAM_WRITE + #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT + if (gvmt(g)->writestart) + #endif + { + g->p.x = x; + g->p.y = y; + g->p.cx = cx; + g->p.cy = cy; + gdisp_lld_write_start(g); + #if GDISP_HARDWARE_STREAM_POS + #if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT + if (gvmt(g)->writepos) + #endif + gdisp_lld_write_pos(g); + #endif + return; + } + #endif + + // Worst - save the parameters and use pixel drawing and/or area fills + #if GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_DRAWPIXEL + // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming + //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT + // if (gvmt(g)->pixel) + //#endif + { + // Use x,y as the current position, x1,y1 as the save position and x2,y2 as the end position, cx = bufpos + g->p.x1 = g->p.x = x; + g->p.y1 = g->p.y = y; + g->p.x2 = x + cx; + g->p.y2 = y + cy; + #if (GDISP_LINEBUF_SIZE != 0 && GDISP_HARDWARE_BITFILLS) || GDISP_HARDWARE_FILLS + g->p.cx = 0; + g->p.cy = 1; + #endif + return; + } + #endif + + // Don't release the mutex as gdispStreamEnd() will do that. + } + + void gdispGStreamColor(GDisplay *g, color_t color) { + #if !GDISP_HARDWARE_STREAM_WRITE && GDISP_LINEBUF_SIZE != 0 && GDISP_HARDWARE_BITFILLS + coord_t sx1, sy1; + #endif + + // Don't touch the mutex as we should already own it + + // Ignore this call if we are not streaming + if (!(g->flags & GDISP_FLG_INSTREAM)) + return; + + // Best is hardware streaming + #if GDISP_HARDWARE_STREAM_WRITE + #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT + if (gvmt(g)->writestart) + #endif + { + g->p.color = color; + gdisp_lld_write_color(g); + return; + } + #endif + + // Next best is to use bitfills with our line buffer + #if GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_LINEBUF_SIZE != 0 && GDISP_HARDWARE_BITFILLS + #if GDISP_HARDWARE_BITFILLS == HARDWARE_AUTODETECT + if (gvmt(g)->blit) + #endif + { + g->linebuf[g->p.cx++] = color; + if (g->p.cx >= GDISP_LINEBUF_SIZE) { + sx1 = g->p.x1; + sy1 = g->p.y1; + g->p.x1 = 0; + g->p.y1 = 0; + g->p.ptr = (void *)g->linebuf; + gdisp_lld_blit_area(g); + g->p.x1 = sx1; + g->p.y1 = sy1; + g->p.x += g->p.cx; + g->p.cx = 0; + } + + // Just wrap at end-of-line and end-of-buffer + if (g->p.x+g->p.cx >= g->p.x2) { + if (g->p.cx) { + sx1 = g->p.x1; + sy1 = g->p.y1; + g->p.x1 = 0; + g->p.y1 = 0; + g->p.ptr = (void *)g->linebuf; + gdisp_lld_blit_area(g); + g->p.x1 = sx1; + g->p.y1 = sy1; + g->p.cx = 0; + } + g->p.x = g->p.x1; + if (++g->p.y >= g->p.y2) + g->p.y = g->p.y1; + } + } + #endif + + // Only slightly better than drawing pixels is to look for runs and use fillarea + #if GDISP_HARDWARE_STREAM_WRITE != TRUE && (GDISP_LINEBUF_SIZE == 0 || GDISP_HARDWARE_BITFILLS != TRUE) && GDISP_HARDWARE_FILLS + // We don't need to test for auto-detect on drawpixel as we know we have it because we don't have streaming. + #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT + if (gvmt(g)->fill) + #endif + { + if (!g->p.cx || g->p.color == color) { + g->p.cx++; + g->p.color = color; + } else { + if (g->p.cx == 1) + gdisp_lld_draw_pixel(g); + else + gdisp_lld_fill_area(g); + g->p.x += g->p.cx; + g->p.color = color; + g->p.cx = 1; + } + // Just wrap at end-of-line and end-of-buffer + if (g->p.x+g->p.cx >= g->p.x2) { + if (g->p.cx) { + if (g->p.cx == 1) + gdisp_lld_draw_pixel(g); + else + gdisp_lld_fill_area(g); + g->p.cx = 0; + } + g->p.x = g->p.x1; + if (++g->p.y >= g->p.y2) + g->p.y = g->p.y1; + } + return; + } + #endif + + // Worst is using pixel drawing + #if GDISP_HARDWARE_STREAM_WRITE != TRUE && (GDISP_LINEBUF_SIZE == 0 || GDISP_HARDWARE_BITFILLS != TRUE) && GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_DRAWPIXEL + // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming + //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT + // if (gvmt(g)->pixel) + //#endif + { + g->p.color = color; + gdisp_lld_draw_pixel(g); + + // Just wrap at end-of-line and end-of-buffer + if (++g->p.x >= g->p.x2) { + g->p.x = g->p.x1; + if (++g->p.y >= g->p.y2) + g->p.y = g->p.y1; + } + return; + } + #endif + } + + void gdispGStreamStop(GDisplay *g) { + // Only release the mutex and end the stream if we are actually streaming. + if (!(g->flags & GDISP_FLG_INSTREAM)) + return; + + // Clear the flag + g->flags &= ~GDISP_FLG_INSTREAM; + + // The cleanup below must match the streaming code above. + + #if GDISP_HARDWARE_STREAM_WRITE + #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT + if (gvmt(g)->writestart) + #endif + { + gdisp_lld_write_stop(g); + autoflush_stopdone(g); + MUTEX_EXIT(g); + return; + } + #endif + + #if GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_LINEBUF_SIZE != 0 && GDISP_HARDWARE_BITFILLS + #if GDISP_HARDWARE_BITFILLS == HARDWARE_AUTODETECT + if (gvmt(g)->blit) + #endif + { + if (g->p.cx) { + g->p.x1 = 0; + g->p.y1 = 0; + g->p.ptr = (void *)g->linebuf; + gdisp_lld_blit_area(g); + } + autoflush_stopdone(g); + MUTEX_EXIT(g); + return; + } + #endif + + #if GDISP_HARDWARE_STREAM_WRITE != TRUE && (GDISP_LINEBUF_SIZE == 0 || GDISP_HARDWARE_BITFILLS != TRUE) && GDISP_HARDWARE_FILLS + // We don't need to test for auto-detect on drawpixel as we know we have it because we don't have streaming. + #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT + if (gvmt(g)->fill) + #endif + { + if (g->p.cx) { + if (g->p.cx == 1) + gdisp_lld_draw_pixel(g); + else + gdisp_lld_fill_area(g); + } + autoflush_stopdone(g); + MUTEX_EXIT(g); + return; + } + #endif + + #if GDISP_HARDWARE_STREAM_WRITE != TRUE && (GDISP_LINEBUF_SIZE == 0 || GDISP_HARDWARE_BITFILLS != TRUE) && GDISP_HARDWARE_FILLS != TRUE + { + autoflush_stopdone(g); + MUTEX_EXIT(g); + } + #endif + } +#endif + +void gdispGDrawPixel(GDisplay *g, coord_t x, coord_t y, color_t color) { + MUTEX_ENTER(g); + g->p.x = x; + g->p.y = y; + g->p.color = color; + drawpixel_clip(g); + autoflush(g); + MUTEX_EXIT(g); +} + +void gdispGDrawLine(GDisplay *g, coord_t x0, coord_t y0, coord_t x1, coord_t y1, color_t color) { + MUTEX_ENTER(g); + g->p.x = x0; + g->p.y = y0; + g->p.x1 = x1; + g->p.y1 = y1; + g->p.color = color; + line_clip(g); + autoflush(g); + MUTEX_EXIT(g); +} + +void gdispGClear(GDisplay *g, color_t color) { + // Note - clear() ignores the clipping area. It clears the screen. + MUTEX_ENTER(g); + + // Best is hardware accelerated clear + #if GDISP_HARDWARE_CLEARS + #if GDISP_HARDWARE_CLEARS == HARDWARE_AUTODETECT + if (gvmt(g)->clear) + #endif + { + g->p.color = color; + gdisp_lld_clear(g); + autoflush_stopdone(g); + MUTEX_EXIT(g); + return; + } + #endif + + // Next best is hardware accelerated area fill + #if GDISP_HARDWARE_CLEARS != TRUE && GDISP_HARDWARE_FILLS + #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT + if (gvmt(g)->fill) + #endif + { + g->p.x = g->p.y = 0; + g->p.cx = g->g.Width; + g->p.cy = g->g.Height; + g->p.color = color; + gdisp_lld_fill_area(g); + autoflush_stopdone(g); + MUTEX_EXIT(g); + return; + } + #endif + + // Next best is streaming + #if GDISP_HARDWARE_CLEARS != TRUE && GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE + #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT + if (gvmt(g)->writestart) + #endif + { + uint32_t area; + + g->p.x = g->p.y = 0; + g->p.cx = g->g.Width; + g->p.cy = g->g.Height; + g->p.color = color; + area = (uint32_t)g->p.cx * g->p.cy; + + gdisp_lld_write_start(g); + #if GDISP_HARDWARE_STREAM_POS + #if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT + if (gvmt(g)->writepos) + #endif + gdisp_lld_write_pos(g); + #endif + for(; area; area--) + gdisp_lld_write_color(g); + gdisp_lld_write_stop(g); + autoflush_stopdone(g); + MUTEX_EXIT(g); + return; + } + #endif + + // Worst is drawing pixels + #if GDISP_HARDWARE_CLEARS != TRUE && GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_DRAWPIXEL + // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming + //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT + // if (gvmt(g)->pixel) + //#endif + { + g->p.color = color; + for(g->p.y = 0; g->p.y < g->g.Height; g->p.y++) + for(g->p.x = 0; g->p.x < g->g.Width; g->p.x++) + gdisp_lld_draw_pixel(g); + autoflush_stopdone(g); + MUTEX_EXIT(g); + return; + } + #endif +} + +void gdispGFillArea(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, color_t color) { + MUTEX_ENTER(g); + g->p.x = x; + g->p.y = y; + g->p.cx = cx; + g->p.cy = cy; + g->p.color = color; + TEST_CLIP_AREA(g) { + fillarea(g); + } + autoflush_stopdone(g); + MUTEX_EXIT(g); +} + +void gdispGBlitArea(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t srcx, coord_t srcy, coord_t srccx, const pixel_t *buffer) { + MUTEX_ENTER(g); + + #if NEED_CLIPPING + #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT + if (!gvmt(g)->setclip) + #endif + { + // This is a different clipping to fillarea(g) as it needs to take into account srcx,srcy + if (x < g->clipx0) { cx -= g->clipx0 - x; srcx += g->clipx0 - x; x = g->clipx0; } + if (y < g->clipy0) { cy -= g->clipy0 - y; srcy += g->clipy0 - x; y = g->clipy0; } + if (x+cx > g->clipx1) cx = g->clipx1 - x; + if (y+cy > g->clipy1) cy = g->clipy1 - y; + if (srcx+cx > srccx) cx = srccx - srcx; + if (cx <= 0 || cy <= 0) { MUTEX_EXIT(g); return; } + } + #endif + + // Best is hardware bitfills + #if GDISP_HARDWARE_BITFILLS + #if GDISP_HARDWARE_BITFILLS == HARDWARE_AUTODETECT + if (gvmt(g)->blit) + #endif + { + g->p.x = x; + g->p.y = y; + g->p.cx = cx; + g->p.cy = cy; + g->p.x1 = srcx; + g->p.y1 = srcy; + g->p.x2 = srccx; + g->p.ptr = (void *)buffer; + gdisp_lld_blit_area(g); + autoflush_stopdone(g); + MUTEX_EXIT(g); + return; + } + #endif + + // Next best is hardware streaming + #if GDISP_HARDWARE_BITFILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE + #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT + if (gvmt(g)->writestart) + #endif + { + // Translate buffer to the real image data, use srcx,srcy as the end point, srccx as the buffer line gap + buffer += srcy*srccx+srcx; + srcx = x + cx; + srcy = y + cy; + srccx -= cx; + + g->p.x = x; + g->p.y = y; + g->p.cx = cx; + g->p.cy = cy; + gdisp_lld_write_start(g); + #if GDISP_HARDWARE_STREAM_POS + #if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT + if (gvmt(g)->writepos) + #endif + gdisp_lld_write_pos(g); + #endif + for(g->p.y = y; g->p.y < srcy; g->p.y++, buffer += srccx) { + for(g->p.x = x; g->p.x < srcx; g->p.x++) { + g->p.color = *buffer++; + gdisp_lld_write_color(g); + } + } + gdisp_lld_write_stop(g); + autoflush_stopdone(g); + MUTEX_EXIT(g); + return; + } + #endif + + // Only slightly better than drawing pixels is to look for runs and use fill area + #if GDISP_HARDWARE_BITFILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_FILLS + // We don't need to test for auto-detect on drawpixel as we know we have it because we don't have streaming. + #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT + if (gvmt(g)->fill) + #endif + { + // Translate buffer to the real image data, use srcx,srcy as the end point, srccx as the buffer line gap + buffer += srcy*srccx+srcx; + srcx = x + cx; + srcy = y + cy; + srccx -= cx; + + g->p.cy = 1; + for(g->p.y = y; g->p.y < srcy; g->p.y++, buffer += srccx) { + for(g->p.x=x; g->p.x < srcx; g->p.x += g->p.cx) { + g->p.cx=1; + g->p.color = *buffer++; + while(g->p.x+g->p.cx < srcx && *buffer == g->p.color) { + g->p.cx++; + buffer++; + } + if (g->p.cx == 1) { + gdisp_lld_draw_pixel(g); + } else { + gdisp_lld_fill_area(g); + } + } + } + autoflush_stopdone(g); + MUTEX_EXIT(g); + return; + } + #endif + + // Worst is drawing pixels + #if GDISP_HARDWARE_BITFILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_DRAWPIXEL + // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming + //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT + // if (gvmt(g)->pixel) + //#endif + { + // Translate buffer to the real image data, use srcx,srcy as the end point, srccx as the buffer line gap + buffer += srcy*srccx+srcx; + srcx = x + cx; + srcy = y + cy; + srccx -= cx; + + for(g->p.y = y; g->p.y < srcy; g->p.y++, buffer += srccx) { + for(g->p.x=x; g->p.x < srcx; g->p.x++) { + g->p.color = *buffer++; + gdisp_lld_draw_pixel(g); + } + } + autoflush_stopdone(g); + MUTEX_EXIT(g); + return; + } + #endif +} + +#if GDISP_NEED_CLIP || GDISP_NEED_VALIDATION + void gdispGSetClip(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy) { + MUTEX_ENTER(g); + + // Best is using hardware clipping + #if GDISP_HARDWARE_CLIP + #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT + if (gvmt(g)->setclip) + #endif + { + g->p.x = x; + g->p.y = y; + g->p.cx = cx; + g->p.cy = cy; + gdisp_lld_set_clip(g); + } + #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT + else + #endif + #endif + + // Worst is using software clipping + #if GDISP_HARDWARE_CLIP != TRUE + { + if (x < 0) { cx += x; x = 0; } + if (y < 0) { cy += y; y = 0; } + if (cx <= 0 || cy <= 0 || x >= g->g.Width || y >= g->g.Height) { MUTEX_EXIT(g); return; } + g->clipx0 = x; + g->clipy0 = y; + g->clipx1 = x+cx; if (g->clipx1 > g->g.Width) g->clipx1 = g->g.Width; + g->clipy1 = y+cy; if (g->clipy1 > g->g.Height) g->clipy1 = g->g.Height; + } + #endif + MUTEX_EXIT(g); + } +#endif + +#if GDISP_NEED_CIRCLE + void gdispGDrawCircle(GDisplay *g, coord_t x, coord_t y, coord_t radius, color_t color) { + coord_t a, b, P; + + MUTEX_ENTER(g); + + // Calculate intermediates + a = 1; + b = radius; + P = 4 - radius; + g->p.color = color; + + // Away we go using Bresenham's circle algorithm + // Optimized to prevent double drawing + g->p.x = x; g->p.y = y + b; drawpixel_clip(g); + g->p.x = x; g->p.y = y - b; drawpixel_clip(g); + g->p.x = x + b; g->p.y = y; drawpixel_clip(g); + g->p.x = x - b; g->p.y = y; drawpixel_clip(g); + do { + g->p.x = x + a; g->p.y = y + b; drawpixel_clip(g); + g->p.x = x + a; g->p.y = y - b; drawpixel_clip(g); + g->p.x = x + b; g->p.y = y + a; drawpixel_clip(g); + g->p.x = x - b; g->p.y = y + a; drawpixel_clip(g); + g->p.x = x - a; g->p.y = y + b; drawpixel_clip(g); + g->p.x = x - a; g->p.y = y - b; drawpixel_clip(g); + g->p.x = x + b; g->p.y = y - a; drawpixel_clip(g); + g->p.x = x - b; g->p.y = y - a; drawpixel_clip(g); + if (P < 0) + P += 3 + 2*a++; + else + P += 5 + 2*(a++ - b--); + } while(a < b); + g->p.x = x + a; g->p.y = y + b; drawpixel_clip(g); + g->p.x = x + a; g->p.y = y - b; drawpixel_clip(g); + g->p.x = x - a; g->p.y = y + b; drawpixel_clip(g); + g->p.x = x - a; g->p.y = y - b; drawpixel_clip(g); + + autoflush(g); + MUTEX_EXIT(g); + } +#endif + +#if GDISP_NEED_CIRCLE + void gdispGFillCircle(GDisplay *g, coord_t x, coord_t y, coord_t radius, color_t color) { + coord_t a, b, P; + + MUTEX_ENTER(g); + + // Calculate intermediates + a = 1; + b = radius; + P = 4 - radius; + g->p.color = color; + + // Away we go using Bresenham's circle algorithm + // This is optimized to prevent overdrawing by drawing a line only when a variable is about to change value + g->p.y = y; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); + g->p.y = y+b; g->p.x = x; drawpixel_clip(g); + g->p.y = y-b; g->p.x = x; drawpixel_clip(g); + do { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); + if (P < 0) { + P += 3 + 2*a++; + } else { + g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); + g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); + P += 5 + 2*(a++ - b--); + } + } while(a < b); + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); + + autoflush(g); + MUTEX_EXIT(g); + } +#endif + +#if GDISP_NEED_ELLIPSE + void gdispGDrawEllipse(GDisplay *g, coord_t x, coord_t y, coord_t a, coord_t b, color_t color) { + coord_t dx, dy; + int32_t a2, b2; + int32_t err, e2; + + MUTEX_ENTER(g); + + // Calculate intermediates + dx = 0; + dy = b; + a2 = a*a; + b2 = b*b; + err = b2-(2*b-1)*a2; + g->p.color = color; + + // Away we go using Bresenham's ellipse algorithm + do { + g->p.x = x + dx; g->p.y = y + dy; drawpixel_clip(g); + g->p.x = x - dx; g->p.y = y + dy; drawpixel_clip(g); + g->p.x = x - dx; g->p.y = y - dy; drawpixel_clip(g); + g->p.x = x + dx; g->p.y = y - dy; drawpixel_clip(g); + + e2 = 2*err; + if(e2 < (2*dx+1)*b2) { + dx++; + err += (2*dx+1)*b2; + } + if(e2 > -(2*dy-1)*a2) { + dy--; + err -= (2*dy-1)*a2; + } + } while(dy >= 0); + + autoflush(g); + MUTEX_EXIT(g); + } +#endif + +#if GDISP_NEED_ELLIPSE + void gdispGFillEllipse(GDisplay *g, coord_t x, coord_t y, coord_t a, coord_t b, color_t color) { + coord_t dx, dy; + int32_t a2, b2; + int32_t err, e2; + + MUTEX_ENTER(g); + + // Calculate intermediates + dx = 0; + dy = b; + a2 = a*a; + b2 = b*b; + err = b2-(2*b-1)*a2; + g->p.color = color; + + // Away we go using Bresenham's ellipse algorithm + // This is optimized to prevent overdrawing by drawing a line only when a y is about to change value + do { + e2 = 2*err; + if(e2 < (2*dx+1)*b2) { + dx++; + err += (2*dx+1)*b2; + } + if(e2 > -(2*dy-1)*a2) { + g->p.y = y + dy; g->p.x = x - dx; g->p.x1 = x + dx; hline_clip(g); + if (y) { g->p.y = y - dy; g->p.x = x - dx; g->p.x1 = x + dx; hline_clip(g); } + dy--; + err -= (2*dy-1)*a2; + } + } while(dy >= 0); + + autoflush(g); + MUTEX_EXIT(g); + } +#endif + +#if GDISP_NEED_ARCSECTORS + void gdispGDrawArcSectors(GDisplay *g, coord_t x, coord_t y, coord_t radius, uint8_t sectors, color_t color) { + coord_t a, b, P; + + MUTEX_ENTER(g); + + // Calculate intermediates + a = 1; // x in many explanations + b = radius; // y in many explanations + P = 4 - radius; + g->p.color = color; + + // Away we go using Bresenham's circle algorithm + // Optimized to prevent double drawing + if (sectors & 0x06) { g->p.x = x; g->p.y = y - b; drawpixel_clip(g); } // Upper upper + if (sectors & 0x60) { g->p.x = x; g->p.y = y + b; drawpixel_clip(g); } // Lower lower + if (sectors & 0x81) { g->p.x = x + b; g->p.y = y; drawpixel_clip(g); } // Right right + if (sectors & 0x18) { g->p.x = x - b; g->p.y = y; drawpixel_clip(g); } // Left left + + do { + if (sectors & 0x01) { g->p.x = x + b; g->p.y = y - a; drawpixel_clip(g); } // Upper right right + if (sectors & 0x02) { g->p.x = x + a; g->p.y = y - b; drawpixel_clip(g); } // Upper upper right + if (sectors & 0x04) { g->p.x = x - a; g->p.y = y - b; drawpixel_clip(g); } // Upper upper left + if (sectors & 0x08) { g->p.x = x - b; g->p.y = y - a; drawpixel_clip(g); } // Upper left left + if (sectors & 0x10) { g->p.x = x - b; g->p.y = y + a; drawpixel_clip(g); } // Lower left left + if (sectors & 0x20) { g->p.x = x - a; g->p.y = y + b; drawpixel_clip(g); } // Lower lower left + if (sectors & 0x40) { g->p.x = x + a; g->p.y = y + b; drawpixel_clip(g); } // Lower lower right + if (sectors & 0x80) { g->p.x = x + b; g->p.y = y + a; drawpixel_clip(g); } // Lower right right + if (P < 0) + P += 3 + 2*a++; + else + P += 5 + 2*(a++ - b--); + } while(a < b); + + if (sectors & 0xC0) { g->p.x = x + a; g->p.y = y + b; drawpixel_clip(g); } // Lower right + if (sectors & 0x03) { g->p.x = x + a; g->p.y = y - b; drawpixel_clip(g); } // Upper right + if (sectors & 0x30) { g->p.x = x - a; g->p.y = y + b; drawpixel_clip(g); } // Lower left + if (sectors & 0x0C) { g->p.x = x - a; g->p.y = y - b; drawpixel_clip(g); } // Upper left + + autoflush(g); + MUTEX_EXIT(g); + } +#endif + +#if GDISP_NEED_ARCSECTORS + void gdispGFillArcSectors(GDisplay *g, coord_t x, coord_t y, coord_t radius, uint8_t sectors, color_t color) { + coord_t a, b, P; + + MUTEX_ENTER(g); + + // Calculate intermediates + a = 1; // x in many explanations + b = radius; // y in many explanations + P = 4 - radius; + g->p.color = color; + + // Away we go using Bresenham's circle algorithm + // Optimized to prevent double drawing + if (sectors & 0x06) { g->p.x = x; g->p.y = y - b; drawpixel_clip(g); } // Upper upper + if (sectors & 0x60) { g->p.x = x; g->p.y = y + b; drawpixel_clip(g); } // Lower lower + if (sectors & 0x81) { // Center right + g->p.y = y; g->p.x = x; g->p.x1 = x + b; + if (sectors & 0x18) g->p.x -= b; // Left right + hline_clip(g); + } else if (sectors & 0x18) { // Left center + g->p.x = x - b; g->p.x1 = x; g->p.y = y; + hline_clip(g); + } + + do { + // Top half + switch(sectors & 0x0F) { + case 0x01: + g->p.y = y - a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g); + break; + case 0x02: + g->p.y = y - b; g->p.x = x; g->p.x1 = x + a; hline_clip(g); + g->p.y = y - a; g->p.x = x; g->p.x1 = x + a; hline_clip(g); + break; + case 0x03: + g->p.y = y - b; g->p.x = x; g->p.x1 = x + a; hline_clip(g); + g->p.y = y - a; g->p.x = x; g->p.x1 = x + b; hline_clip(g); + break; + case 0x04: + g->p.y = y - b; g->p.x = x - a; g->p.x1 = x; hline_clip(g); + g->p.y = y - a; g->p.x = x - a; g->p.x1 = x; hline_clip(g); + break; + case 0x05: + g->p.y = y - b; g->p.x = x - a; g->p.x1 = x; hline_clip(g); + g->p.y = y - a; g->p.x = x - a; g->p.x1 = x; hline_clip(g); + g->p.y = y - a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g); + break; + case 0x06: + g->p.y = y - b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g); + g->p.y = y - a; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g); + break; + case 0x07: + g->p.y = y - b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g); + g->p.y = y - a; g->p.x = x - a; g->p.x1 = x + b; hline_clip(g); + break; + case 0x08: + g->p.y = y - a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g); + break; + case 0x09: + g->p.y = y - a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g); + g->p.y = y - a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g); + break; + case 0x0A: + g->p.y = y - b; g->p.x = x; g->p.x1 = x + a; hline_clip(g); + g->p.y = y - a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g); + g->p.y = y - a; g->p.x = x; g->p.x1 = x + a; hline_clip(g); + break; + case 0x0B: + g->p.y = y - b; g->p.x = x; g->p.x1 = x + a; hline_clip(g); + g->p.y = y - a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g); + g->p.y = y - a; g->p.x = x; g->p.x1 = x + b; hline_clip(g); + break; + case 0x0C: + g->p.y = y - b; g->p.x = x - a; g->p.x1 = x; hline_clip(g); + g->p.y = y - a; g->p.x = x - b; g->p.x1 = x; hline_clip(g); + break; + case 0x0D: + g->p.y = y - b; g->p.x = x - a; g->p.x1 = x; hline_clip(g); + g->p.y = y - a; g->p.x = x - b; g->p.x1 = x; hline_clip(g); + g->p.y = y - a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g); + break; + case 0x0E: + g->p.y = y - b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g); + g->p.y = y - a; g->p.x = x - b; g->p.x1 = x + a; hline_clip(g); + break; + case 0x0F: + g->p.y = y - b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g); + g->p.y = y - a; g->p.x = x - b; g->p.x1 = x + b; hline_clip(g); + break; + } + + // Bottom half + switch((sectors & 0xF0)>>4) { + case 0x01: + g->p.y = y + a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g); + break; + case 0x02: + g->p.y = y + b; g->p.x = x - a; g->p.x1 = x; hline_clip(g); + g->p.y = y + a; g->p.x = x - a; g->p.x1 = x; hline_clip(g); + break; + case 0x03: + g->p.y = y + b; g->p.x = x - a; g->p.x1 = x; hline_clip(g); + g->p.y = y + a; g->p.x = x - b; g->p.x1 = x; hline_clip(g); + break; + case 0x04: + g->p.y = y + b; g->p.x = x; g->p.x1 = x + a; hline_clip(g); + g->p.y = y + a; g->p.x = x; g->p.x1 = x + a; hline_clip(g); + break; + case 0x05: + g->p.y = y + b; g->p.x = x; g->p.x1 = x + a; hline_clip(g); + g->p.y = y + a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g); + g->p.y = y + a; g->p.x = x; g->p.x1 = x + a; hline_clip(g); + break; + case 0x06: + g->p.y = y + b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g); + g->p.y = y + a; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g); + break; + case 0x07: + g->p.y = y + b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g); + g->p.y = y + a; g->p.x = x - b; g->p.x1 = x + a; hline_clip(g); + break; + case 0x08: + g->p.y = y + a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g); + break; + case 0x09: + g->p.y = y + a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g); + g->p.y = y + a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g); + break; + case 0x0A: + g->p.y = y + b; g->p.x = x - a; g->p.x1 = x; hline_clip(g); + g->p.y = y + a; g->p.x = x - a; g->p.x1 = x; hline_clip(g); + g->p.y = y + a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g); + break; + case 0x0B: + g->p.y = y + b; g->p.x = x - a; g->p.x1 = x; hline_clip(g); + g->p.y = y + a; g->p.x = x - b; g->p.x1 = x; hline_clip(g); + g->p.y = y + a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g); + break; + case 0x0C: + g->p.y = y + b; g->p.x = x; g->p.x1 = x + a; hline_clip(g); + g->p.y = y + a; g->p.x = x; g->p.x1 = x + b; hline_clip(g); + break; + case 0x0D: + g->p.y = y + b; g->p.x = x; g->p.x1 = x + a; hline_clip(g); + g->p.y = y + a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g); + g->p.y = y + a; g->p.x = x; g->p.x1 = x + b; hline_clip(g); + break; + case 0x0E: + g->p.y = y + b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g); + g->p.y = y + a; g->p.x = x - a; g->p.x1 = x + b; hline_clip(g); + break; + case 0x0F: + g->p.y = y + b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g); + g->p.y = y + a; g->p.x = x - b; g->p.x1 = x + b; hline_clip(g); + break; + } + + if (P < 0) + P += 3 + 2*a++; + else + P += 5 + 2*(a++ - b--); + } while(a < b); + + // Top half + if (sectors & 0x02) { g->p.y = y - a; g->p.x = x; g->p.x1 = x + a; hline_clip(g); } + else if (sectors & 0x01) { g->p.y = y - a; g->p.x = x + a; drawpixel_clip(g); } + if (sectors & 0x04) { g->p.y = y - a; g->p.x = x - a; g->p.x1 = x; hline_clip(g); } + else if (sectors & 0x08) { g->p.y = y - a; g->p.x = x - a; drawpixel_clip(g); } + + // Bottom half + if (sectors & 0x40) { g->p.y = y + a; g->p.x = x; g->p.x1 = x + a; hline_clip(g); } + else if (sectors & 0x80) { g->p.y = y + a; g->p.x = x + a; drawpixel_clip(g); } + if (sectors & 0x20) { g->p.y = y + a; g->p.x = x - a; g->p.x1 = x; hline_clip(g); } + else if (sectors & 0x10) { g->p.y = y + a; g->p.x = x - a; drawpixel_clip(g); } + + autoflush(g); + MUTEX_EXIT(g); + } +#endif + +#if GDISP_NEED_ARC + #if (!GMISC_NEED_FIXEDTRIG && !GMISC_NEED_FASTTRIG) || !GFX_USE_GMISC + #include <math.h> + #endif + + void gdispGDrawArc(GDisplay *g, coord_t x, coord_t y, coord_t radius, coord_t start, coord_t end, color_t color) { + coord_t a, b, P, sedge, eedge; + uint8_t full, sbit, ebit, tbit; + + // Normalize the angles + if (start < 0) + start -= (start/360-1)*360; + else if (start >= 360) + start %= 360; + if (end < 0) + end -= (end/360-1)*360; + else if (end >= 360) + end %= 360; + + sbit = 1<<(start/45); + ebit = 1<<(end/45); + full = 0; + if (start == end) { + full = 0xFF; + } else if (end < start) { + for(tbit=sbit<<1; tbit; tbit<<=1) full |= tbit; + for(tbit=ebit>>1; tbit; tbit>>=1) full |= tbit; + } else if (sbit < 0x80) { + for(tbit=sbit<<1; tbit < ebit; tbit<<=1) full |= tbit; + } + tbit = start%45 == 0 ? sbit : 0; + + MUTEX_ENTER(g); + g->p.color = color; + + if (full) { + // Draw full sectors + // Optimized to prevent double drawing + a = 1; + b = radius; + P = 4 - radius; + if (full & 0x60) { g->p.y = y+b; g->p.x = x; drawpixel_clip(g); } + if (full & 0x06) { g->p.y = y-b; g->p.x = x; drawpixel_clip(g); } + if (full & 0x81) { g->p.y = y; g->p.x = x+b; drawpixel_clip(g); } + if (full & 0x18) { g->p.y = y; g->p.x = x-b; drawpixel_clip(g); } + do { + if (full & 0x01) { g->p.x = x+b; g->p.y = y-a; drawpixel_clip(g); } + if (full & 0x02) { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } + if (full & 0x04) { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } + if (full & 0x08) { g->p.x = x-b; g->p.y = y-a; drawpixel_clip(g); } + if (full & 0x10) { g->p.x = x-b; g->p.y = y+a; drawpixel_clip(g); } + if (full & 0x20) { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } + if (full & 0x40) { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } + if (full & 0x80) { g->p.x = x+b; g->p.y = y+a; drawpixel_clip(g); } + if (P < 0) + P += 3 + 2*a++; + else + P += 5 + 2*(a++ - b--); + } while(a < b); + if (full & 0xC0) { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } + if (full & 0x0C) { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } + if (full & 0x03) { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } + if (full & 0x30) { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } + if (full == 0xFF) { + autoflush(g); + MUTEX_EXIT(g); + return; + } + } + + #if GFX_USE_GMISC && GMISC_NEED_FIXEDTRIG + sedge = NONFIXED(radius * ((sbit & 0x99) ? ffsin(start) : ffcos(start)) + FIXED0_5); + eedge = NONFIXED(radius * ((ebit & 0x99) ? ffsin(end) : ffcos(end)) + FIXED0_5); + #elif GFX_USE_GMISC && GMISC_NEED_FASTTRIG + sedge = round(radius * ((sbit & 0x99) ? fsin(start) : fcos(start))); + eedge = round(radius * ((ebit & 0x99) ? fsin(end) : fcos(end))); + #else + sedge = round(radius * ((sbit & 0x99) ? sin(start*M_PI/180) : cos(start*M_PI/180))); + eedge = round(radius * ((ebit & 0x99) ? sin(end*M_PI/180) : cos(end*M_PI/180))); + #endif + if (sbit & 0xB4) sedge = -sedge; + if (ebit & 0xB4) eedge = -eedge; + + if (sbit != ebit) { + // Draw start and end sectors + // Optimized to prevent double drawing + a = 1; + b = radius; + P = 4 - radius; + if ((sbit & 0x20) || (tbit & 0x40) || (ebit & 0x40)) { g->p.x = x; g->p.y = y+b; drawpixel_clip(g); } + if ((sbit & 0x02) || (tbit & 0x04) || (ebit & 0x04)) { g->p.x = x; g->p.y = y-b; drawpixel_clip(g); } + if ((sbit & 0x80) || (tbit & 0x01) || (ebit & 0x01)) { g->p.x = x+b; g->p.y = y; drawpixel_clip(g); } + if ((sbit & 0x08) || (tbit & 0x10) || (ebit & 0x10)) { g->p.x = x-b; g->p.y = y; drawpixel_clip(g); } + do { + if (((sbit & 0x01) && a >= sedge) || ((ebit & 0x01) && a <= eedge)) { g->p.x = x+b; g->p.y = y-a; drawpixel_clip(g); } + if (((sbit & 0x02) && a <= sedge) || ((ebit & 0x02) && a >= eedge)) { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } + if (((sbit & 0x04) && a >= sedge) || ((ebit & 0x04) && a <= eedge)) { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } + if (((sbit & 0x08) && a <= sedge) || ((ebit & 0x08) && a >= eedge)) { g->p.x = x-b; g->p.y = y-a; drawpixel_clip(g); } + if (((sbit & 0x10) && a >= sedge) || ((ebit & 0x10) && a <= eedge)) { g->p.x = x-b; g->p.y = y+a; drawpixel_clip(g); } + if (((sbit & 0x20) && a <= sedge) || ((ebit & 0x20) && a >= eedge)) { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } + if (((sbit & 0x40) && a >= sedge) || ((ebit & 0x40) && a <= eedge)) { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } + if (((sbit & 0x80) && a <= sedge) || ((ebit & 0x80) && a >= eedge)) { g->p.x = x+b; g->p.y = y+a; drawpixel_clip(g); } + if (P < 0) + P += 3 + 2*a++; + else + P += 5 + 2*(a++ - b--); + } while(a < b); + if (((sbit & 0x40) && a >= sedge) || ((ebit & 0x40) && a <= eedge) || ((sbit & 0x80) && a <= sedge) || ((ebit & 0x80) && a >= eedge)) + { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } + if (((sbit & 0x04) && a >= sedge) || ((ebit & 0x04) && a <= eedge) || ((sbit & 0x08) && a <= sedge) || ((ebit & 0x08) && a >= eedge)) + { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } + if (((sbit & 0x01) && a >= sedge) || ((ebit & 0x01) && a <= eedge) || ((sbit & 0x02) && a <= sedge) || ((ebit & 0x02) && a >= eedge)) + { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } + if (((sbit & 0x10) && a >= sedge) || ((ebit & 0x10) && a <= eedge) || ((sbit & 0x20) && a <= sedge) || ((ebit & 0x20) && a >= eedge)) + { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } + } else if (end < start) { + // Draw start/end sector where it is a non-internal angle + // Optimized to prevent double drawing + a = 1; + b = radius; + P = 4 - radius; + if ((sbit & 0x60) || (tbit & 0xC0)) { g->p.x = x; g->p.y = y+b; drawpixel_clip(g); } + if ((sbit & 0x06) || (tbit & 0x0C)) { g->p.x = x; g->p.y = y-b; drawpixel_clip(g); } + if ((sbit & 0x81) || (tbit & 0x03)) { g->p.x = x+b; g->p.y = y; drawpixel_clip(g); } + if ((sbit & 0x18) || (tbit & 0x30)) { g->p.x = x-b; g->p.y = y; drawpixel_clip(g); } + do { + if ((sbit & 0x01) && (a >= sedge || a <= eedge)) { g->p.x = x+b; g->p.y = y-a; drawpixel_clip(g); } + if ((sbit & 0x02) && (a <= sedge || a >= eedge)) { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } + if ((sbit & 0x04) && (a >= sedge || a <= eedge)) { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } + if ((sbit & 0x08) && (a <= sedge || a >= eedge)) { g->p.x = x-b; g->p.y = y-a; drawpixel_clip(g); } + if ((sbit & 0x10) && (a >= sedge || a <= eedge)) { g->p.x = x-b; g->p.y = y+a; drawpixel_clip(g); } + if ((sbit & 0x20) && (a <= sedge || a >= eedge)) { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } + if ((sbit & 0x40) && (a >= sedge || a <= eedge)) { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } + if ((sbit & 0x80) && (a <= sedge || a >= eedge)) { g->p.x = x+b; g->p.y = y+a; drawpixel_clip(g); } + if (P < 0) + P += 3 + 2*a++; + else + P += 5 + 2*(a++ - b--); + } while(a < b); + if (((sbit & 0x04) && (a >= sedge || a <= eedge)) || ((sbit & 0x08) && (a <= sedge || a >= eedge))) + { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } + if (((sbit & 0x40) && (a >= sedge || a <= eedge)) || ((sbit & 0x80) && (a <= sedge || a >= eedge))) + { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } + if (((sbit & 0x01) && (a >= sedge || a <= eedge)) || ((sbit & 0x02) && (a <= sedge || a >= eedge))) + { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } + if (((sbit & 0x10) && (a >= sedge || a <= eedge)) || ((sbit & 0x20) && (a <= sedge || a >= eedge))) + { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } + } else { + // Draw start/end sector where it is a internal angle + // Optimized to prevent double drawing + a = 1; + b = radius; + P = 4 - radius; + if (((sbit & 0x20) && !eedge) || ((sbit & 0x40) && !sedge)) { g->p.x = x; g->p.y = y+b; drawpixel_clip(g); } + if (((sbit & 0x02) && !eedge) || ((sbit & 0x04) && !sedge)) { g->p.x = x; g->p.y = y-b; drawpixel_clip(g); } + if (((sbit & 0x80) && !eedge) || ((sbit & 0x01) && !sedge)) { g->p.x = x+b; g->p.y = y; drawpixel_clip(g); } + if (((sbit & 0x08) && !eedge) || ((sbit & 0x10) && !sedge)) { g->p.x = x-b; g->p.y = y; drawpixel_clip(g); } + do { + if (((sbit & 0x01) && a >= sedge && a <= eedge)) { g->p.x = x+b; g->p.y = y-a; drawpixel_clip(g); } + if (((sbit & 0x02) && a <= sedge && a >= eedge)) { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } + if (((sbit & 0x04) && a >= sedge && a <= eedge)) { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } + if (((sbit & 0x08) && a <= sedge && a >= eedge)) { g->p.x = x-b; g->p.y = y-a; drawpixel_clip(g); } + if (((sbit & 0x10) && a >= sedge && a <= eedge)) { g->p.x = x-b; g->p.y = y+a; drawpixel_clip(g); } + if (((sbit & 0x20) && a <= sedge && a >= eedge)) { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } + if (((sbit & 0x40) && a >= sedge && a <= eedge)) { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } + if (((sbit & 0x80) && a <= sedge && a >= eedge)) { g->p.x = x+b; g->p.y = y+a; drawpixel_clip(g); } + if (P < 0) + P += 3 + 2*a++; + else + P += 5 + 2*(a++ - b--); + } while(a < b); + if (((sbit & 0x04) && a >= sedge && a <= eedge) || ((sbit & 0x08) && a <= sedge && a >= eedge)) + { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } + if (((sbit & 0x40) && a >= sedge && a <= eedge) || ((sbit & 0x80) && a <= sedge && a >= eedge)) + { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } + if (((sbit & 0x01) && a >= sedge && a <= eedge) || ((sbit & 0x02) && a <= sedge && a >= eedge)) + { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } + if (((sbit & 0x10) && a >= sedge && a <= eedge) || ((sbit & 0x20) && a <= sedge && a >= eedge)) + { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } + } + + autoflush(g); + MUTEX_EXIT(g); + } +#endif + +#if GDISP_NEED_ARC + void gdispGFillArc(GDisplay *g, coord_t x, coord_t y, coord_t radius, coord_t start, coord_t end, color_t color) { + coord_t a, b, P; + coord_t sy, ey; + fixed sxa, sxb, sxd, exa, exb, exd; + uint8_t qtr; + + MUTEX_ENTER(g); + + // Do the trig to get the formulas for the start and end lines. + sxa = exa = FIXED(x)+FIXED0_5; + #if GFX_USE_GMISC && GMISC_NEED_FIXEDTRIG + sxb = radius*ffcos(start); sy = -NONFIXED(radius*ffsin(start) + FIXED0_5); + exb = radius*ffcos(end); ey = -NONFIXED(radius*ffsin(end) + FIXED0_5); + #elif GFX_USE_GMISC && GMISC_NEED_FASTTRIG + sxb = FP2FIXED(radius*fcos(start)); sy = -round(radius*fsin(start)); + exb = FP2FIXED(radius*fcos(end)); ey = -round(radius*fsin(end)); + #else + sxb = FP2FIXED(radius*cos(start*M_PI/180)); sy = -round(radius*sin(start*M_PI/180)); + exb = FP2FIXED(radius*cos(end*M_PI/180)); ey = -round(radius*sin(end*M_PI/180)); + #endif + sxd = sy ? sxb/sy : sxb; + exd = ey ? exb/ey : exb; + + // Calculate which quarters and which direction we are traveling + qtr = 0; + if (sxb > 0) qtr |= 0x01; // S1=0001(1), S2=0000(0), S3=0010(2), S4=0011(3) + if (sy > 0) qtr |= 0x02; + if (exb > 0) qtr |= 0x04; // E1=0100(4), E2=0000(0), E3=1000(8), E4=1100(12) + if (ey > 0) qtr |= 0x08; + if (sy > ey) qtr |= 0x10; // order of start and end lines + + // Calculate intermediates + a = 1; + b = radius; + P = 4 - radius; + g->p.color = color; + sxb += sxa; + exb += exa; + + // Away we go using Bresenham's circle algorithm + // This is optimized to prevent overdrawing by drawing a line only when a variable is about to change value + + switch(qtr) { + case 0: // S2E2 sy <= ey + case 1: // S1E2 sy <= ey + if (ey && sy) { + g->p.x = x; g->p.x1 = x; // E2S + sxa -= sxd; exa -= exd; + } else if (sy) { + g->p.x = x-b; g->p.x1 = x; // C2S + sxa -= sxd; + } else if (ey) { + g->p.x = x; g->p.x1 = x+b; // E2C + exa -= exd; + } else { + g->p.x = x-b; g->p.x1 = x+b; // C2C + } + g->p.y = y; + hline_clip(g); + do { + if (-a >= ey) { + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = NONFIXED(sxa); hline_clip(g); // E2S + sxa -= sxd; exa -= exd; + } else if (-a >= sy) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S + sxa -= sxd; + } else if (qtr & 1) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + if (P < 0) { + P += 3 + 2*a++; + } else { + if (-b >= ey) { + g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = NONFIXED(sxb); hline_clip(g); // E2S + sxb += sxd; exb += exd; + } else if (-b >= sy) { + g->p.y = y-b; g->p.x = x-a; g->p.x1 = NONFIXED(sxb); hline_clip(g); // C2S + sxb += sxd; + } else if (qtr & 1) { + g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + } + P += 5 + 2*(a++ - b--); + } + } while(a < b); + if (-a >= ey) { + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = NONFIXED(sxa); hline_clip(g); // E2S + } else if (-a >= sy) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S + } else if (qtr & 1) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + break; + + case 2: // S3E2 sy <= ey + case 3: // S4E2 sy <= ey + case 6: // S3E1 sy <= ey + case 7: // S4E1 sy <= ey + case 18: // S3E2 sy > ey + case 19: // S4E2 sy > ey + case 22: // S3E1 sy > ey + case 23: // S4E1 sy > ey + g->p.y = y; g->p.x = x; g->p.x1 = x+b; hline_clip(g); // SE2C + sxa += sxd; exa -= exd; + do { + if (-a >= ey) { + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C + exa -= exd; + } else if (!(qtr & 4)) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + if (a <= sy) { + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C + sxa += sxd; + } else if (!(qtr & 1)) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + if (P < 0) { + P += 3 + 2*a++; + } else { + if (-b >= ey) { + g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = x+a; hline_clip(g); // E2C + exb += exd; + } else if (!(qtr & 4)) { + g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + } + if (b <= sy) { + g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = x+a; hline_clip(g); // S2C + sxb -= sxd; + } else if (!(qtr & 1)) { + g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + } + P += 5 + 2*(a++ - b--); + } + } while(a < b); + if (-a >= ey) { + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C + } else if (!(qtr & 4)) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + if (a <= sy) { + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+a; hline_clip(g); // S2C + } else if (!(qtr & 1)) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+a; hline_clip(g); // C2C + } + break; + + case 4: // S2E1 sy <= ey + case 5: // S1E1 sy <= ey + g->p.y = y; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + do { + if (-a >= ey) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C + sxa -= sxd; exa -= exd; + } else if (-a >= sy) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S + sxa -= sxd; + } else if (qtr & 1) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + if (P < 0) { + P += 3 + 2*a++; + } else { + if (-b >= ey) { + g->p.y = y-b; g->p.x = x-a; g->p.x1 = NONFIXED(sxb); hline_clip(g); // C2S + g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = x+a; hline_clip(g); // E2C + sxb += sxd; exb += exd; + } else if (-b >= sy) { + g->p.y = y-b; g->p.x = x-a; g->p.x1 = NONFIXED(sxb); hline_clip(g); // C2S + sxb += sxd; + } else if (qtr & 1) { + g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + } + g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + P += 5 + 2*(a++ - b--); + } + } while(a < b); + if (-a >= ey) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C + } else if (-a >= sy) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S + } else if (qtr & 1) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + break; + + case 8: // S2E3 sy <= ey + case 9: // S1E3 sy <= ey + case 12: // S2E4 sy <= ey + case 13: // S1E4 sy <= ey + case 24: // S2E3 sy > ey + case 25: // S1E3 sy > ey + case 28: // S2E3 sy > ey + case 29: // S1E3 sy > ey + g->p.y = y; g->p.x = x-b; g->p.x1 = x; hline_clip(g); // C2SE + sxa -= sxd; exa += exd; + do { + if (-a >= sy) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S + sxa -= sxd; + } else if (qtr & 1) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + if (a <= ey) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E + exa += exd; + } else if (qtr & 4) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + if (P < 0) { + P += 3 + 2*a++; + } else { + if (-b >= sy) { + g->p.y = y-b; g->p.x = x-a; g->p.x1 = NONFIXED(sxb); hline_clip(g); // C2S + sxb += sxd; + } else if (qtr & 1) { + g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + } + if (b <= ey) { + g->p.y = y+b; g->p.x = x-a; g->p.x1 = NONFIXED(exb); hline_clip(g); // C2E + exb -= exd; + } else if (qtr & 4) { + g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + } + P += 5 + 2*(a++ - b--); + } + } while(a < b); + if (-a >= sy) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S + } else if (qtr & 1) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + if (a <= ey) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E + } else if (qtr & 4) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+a; hline_clip(g); // C2C + } + break; + + case 10: // S3E3 sy <= ey + case 14: // S3E4 sy <= ey + g->p.y = y; g->p.x = x; drawpixel_clip(g); // S2E + sxa += sxd; exa += exd; + do { + if (a <= sy) { + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = NONFIXED(exa); hline_clip(g); // S2E + sxa += sxd; exa += exd; + } else if (a <= ey) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E + exa += exd; + } else if (qtr & 4) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + if (P < 0) { + P += 3 + 2*a++; + } else { + if (b <= sy) { + g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = NONFIXED(exb); hline_clip(g); // S2E + sxb -= sxd; exb -= exd; + } else if (b <= ey) { + g->p.y = y+b; g->p.x = x-a; g->p.x1 = NONFIXED(exb); hline_clip(g); // C2E + exb -= exd; + } else if (qtr & 4) { + g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + } + P += 5 + 2*(a++ - b--); + } + } while(a < b); + if (a <= sy) { + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = NONFIXED(exa); hline_clip(g); // S2E + } else if (a <= ey) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E + } else if (qtr & 4) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + break; + + case 11: // S4E3 sy <= ey + case 15: // S4E4 sy <= ey + g->p.y = y; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + do { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + if (a <= sy) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C + sxa += sxd; exa += exd; + } else if (a <= ey) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E + exa += exd; + } else if (qtr & 4) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + if (P < 0) { + P += 3 + 2*a++; + } else { + g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + if (b <= sy) { + g->p.y = y+b; g->p.x = x-a; g->p.x1 = NONFIXED(exb); hline_clip(g); // C2E + g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = x+a; hline_clip(g); // S2C + sxb -= sxd; exb -= exd; + } else if (b <= ey) { + g->p.y = y+b; g->p.x = x-a; g->p.x1 = NONFIXED(exb); hline_clip(g); // C2E + exb -= exd; + } else if (qtr & 4) { + g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + } + P += 5 + 2*(a++ - b--); + } + } while(a < b); + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + if (a <= sy) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C + } else if (a <= ey) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E + } else if (qtr & 4) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + break; + + case 16: // S2E2 sy > ey + case 20: // S2E1 sy > ey + g->p.y = y; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + sxa -= sxd; exa -= exd; + do { + if (-a >= sy) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C + sxa -= sxd; exa -= exd; + } else if (-a >= ey) { + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C + exa -= exd; + } else if (!(qtr & 4)){ + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + if (P < 0) { + P += 3 + 2*a++; + } else { + if (-b >= sy) { + g->p.y = y-b; g->p.x = x-a; g->p.x1 = NONFIXED(sxb); hline_clip(g); // C2S + g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = x+a; hline_clip(g); // E2C + sxb += sxd; exb += exd; + } else if (-b >= ey) { + g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = x+a; hline_clip(g); // E2C + exb += exd; + } else if (!(qtr & 4)){ + g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + } + g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + P += 5 + 2*(a++ - b--); + } + } while(a < b); + if (-a >= sy) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C + } else if (-a >= ey) { + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C + } else if (!(qtr & 4)){ + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + break; + + case 17: // S1E2 sy > ey + case 21: // S1E1 sy > ey + if (sy) { + g->p.x = x; g->p.x1 = x; // E2S + sxa -= sxd; exa -= exd; + } else { + g->p.x = x; g->p.x1 = x+b; // E2C + exa -= exd; + } + g->p.y = y; + hline_clip(g); + do { + if (-a >= sy) { + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = NONFIXED(sxa); hline_clip(g); // E2S + sxa -= sxd; exa -= exd; + } else if (-a >= ey) { + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C + exa -= exd; + } else if (!(qtr & 4)) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + if (P < 0) { + P += 3 + 2*a++; + } else { + if (-b >= sy) { + g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = NONFIXED(sxb); hline_clip(g); // E2S + sxb += sxd; exb += exd; + } else if (-b >= ey) { + g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = x+a; hline_clip(g); // E2C + exb += exd; + } else if (!(qtr & 4)) { + g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + } + P += 5 + 2*(a++ - b--); + } + } while(a < b); + if (-a >= sy) { + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = NONFIXED(sxa); hline_clip(g); // E2S + } else if (-a >= ey) { + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C + } else if (!(qtr & 4)) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + break; + + case 26: // S3E3 sy > ey + case 27: // S4E3 sy > ey + g->p.y = y; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + do { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + if (a <= ey) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C + sxa += sxd; exa += exd; + } else if (a <= sy) { + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C + sxa += sxd; + } else if (!(qtr & 1)) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + if (P < 0) { + P += 3 + 2*a++; + } else { + g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + if (b <= ey) { + g->p.y = y+b; g->p.x = x-a; g->p.x1 = NONFIXED(exb); hline_clip(g); // C2E + g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = x+a; hline_clip(g); // S2C + sxb -= sxd; exb -= exd; + } else if (b <= sy) { + g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = x+a; hline_clip(g); // S2C + sxb -= sxd; + } else if (!(qtr & 1)) { + g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + } + P += 5 + 2*(a++ - b--); + } + } while(a < b); + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + if (a <= ey) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C + } else if (a <= sy) { + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C + } else if (!(qtr & 4)) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + break; + + case 30: // S3E4 sy > ey + case 31: // S4E4 sy > ey + do { + if (a <= ey) { + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = NONFIXED(exa); hline_clip(g); // S2E + sxa += sxd; exa += exd; + } else if (a <= sy) { + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C + sxa += sxd; + } else if (!(qtr & 1)) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + if (P < 0) { + P += 3 + 2*a++; + } else { + if (b <= ey) { + g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = NONFIXED(exb); hline_clip(g); // S2E + sxb -= sxd; exb -= exd; + } else if (b <= sy) { + g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = x+a; hline_clip(g); // S2C + sxb -= sxd; + } else if (!(qtr & 1)) { + g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + } + P += 5 + 2*(a++ - b--); + } + } while(a < b); + if (a <= ey) { + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C + } else if (a <= sy) { + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C + } else if (!(qtr & 4)) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + break; + } + + autoflush(g); + MUTEX_EXIT(g); + } +#endif + +#if GDISP_NEED_ARC || GDISP_NEED_ARCSECTORS + void gdispGDrawRoundedBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t radius, color_t color) { + if (2*radius > cx || 2*radius > cy) { + gdispGDrawBox(g, x, y, cx, cy, color); + return; + } + + #if GDISP_NEED_ARCSECTORS + gdispGDrawArcSectors(g, x+radius, y+radius, radius, 0x0C, color); + gdispGDrawArcSectors(g, x+cx-1-radius, y+radius, radius, 0x03, color); + gdispGDrawArcSectors(g, x+cx-1-radius, y+cy-1-radius, radius, 0xC0, color); + gdispGDrawArcSectors(g, x+radius, y+cy-1-radius, radius, 0x30, color); + #else + gdispGDrawArc(g, x+radius, y+radius, radius, 90, 180, color); + gdispGDrawArc(g, x+cx-1-radius, y+radius, radius, 0, 90, color); + gdispGDrawArc(g, x+cx-1-radius, y+cy-1-radius, radius, 270, 360, color); + gdispGDrawArc(g, x+radius, y+cy-1-radius, radius, 180, 270, color); + #endif + gdispGDrawLine(g, x+radius+1, y, x+cx-2-radius, y, color); + gdispGDrawLine(g, x+cx-1, y+radius+1, x+cx-1, y+cy-2-radius, color); + gdispGDrawLine(g, x+radius+1, y+cy-1, x+cx-2-radius, y+cy-1, color); + gdispGDrawLine(g, x, y+radius+1, x, y+cy-2-radius, color); + } +#endif + +#if GDISP_NEED_ARC || GDISP_NEED_ARCSECTORS + void gdispGFillRoundedBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t radius, color_t color) { + coord_t radius2; + + radius2 = radius*2; + if (radius2 > cx || radius2 > cy) { + gdispGFillArea(g, x, y, cx, cy, color); + return; + } + #if GDISP_NEED_ARCSECTORS + gdispGFillArcSectors(g, x+radius, y+radius, radius, 0x0C, color); + gdispGFillArcSectors(g, x+cx-1-radius, y+radius, radius, 0x03, color); + gdispGFillArcSectors(g, x+cx-1-radius, y+cy-1-radius, radius, 0xC0, color); + gdispGFillArcSectors(g, x+radius, y+cy-1-radius, radius, 0x30, color); + #else + gdispGFillArc(g, x+radius, y+radius, radius, 90, 180, color); + gdispGFillArc(g, x+cx-1-radius, y+radius, radius, 0, 90, color); + gdispGFillArc(g, x+cx-1-radius, y+cy-1-radius, radius, 270, 360, color); + gdispGFillArc(g, x+radius, y+cy-1-radius, radius, 180, 270, color); + #endif + gdispGFillArea(g, x+radius+1, y, cx-radius2, radius, color); + gdispGFillArea(g, x+radius+1, y+cy-radius, cx-radius2, radius, color); + gdispGFillArea(g, x, y+radius, cx, cy-radius2, color); + } +#endif + +#if GDISP_NEED_PIXELREAD + color_t gdispGGetPixelColor(GDisplay *g, coord_t x, coord_t y) { + color_t c; + + /* Always synchronous as it must return a value */ + MUTEX_ENTER(g); + #if GDISP_HARDWARE_PIXELREAD + #if GDISP_HARDWARE_PIXELREAD == HARDWARE_AUTODETECT + if (gvmt(g)->get) + #endif + { + // Best is direct pixel read + g->p.x = x; + g->p.y = y; + c = gdisp_lld_get_pixel_color(g); + MUTEX_EXIT(g); + return c; + } + #endif + #if GDISP_HARDWARE_PIXELREAD != TRUE && GDISP_HARDWARE_STREAM_READ + #if GDISP_HARDWARE_STREAM_READ == HARDWARE_AUTODETECT + if (gvmt(g)->readcolor) + #endif + { + // Next best is hardware streaming + g->p.x = x; + g->p.y = y; + g->p.cx = 1; + g->p.cy = 1; + gdisp_lld_read_start(g); + c = gdisp_lld_read_color(g); + gdisp_lld_read_stop(g); + MUTEX_EXIT(g); + return c; + } + #endif + #if GDISP_HARDWARE_PIXELREAD != TRUE && GDISP_HARDWARE_STREAM_READ != TRUE + #if !GDISP_HARDWARE_PIXELREAD && !GDISP_HARDWARE_STREAM_READ + // Worst is "not possible" + #error "GDISP: GDISP_NEED_PIXELREAD has been set but there is no hardware support for reading the display" + #endif + MUTEX_EXIT(g); + return 0; + #endif + } +#endif + +#if GDISP_NEED_SCROLL + void gdispGVerticalScroll(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, int lines, color_t bgcolor) { + coord_t abslines; + #if GDISP_HARDWARE_SCROLL != TRUE + coord_t fy, dy, ix, fx, i, j; + #endif + + MUTEX_ENTER(g); + #if NEED_CLIPPING + #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT + if (!gvmt(g)->setclip) + #endif + { + if (x < g->clipx0) { cx -= g->clipx0 - x; x = g->clipx0; } + if (y < g->clipy0) { cy -= g->clipy0 - y; y = g->clipy0; } + if (!lines || cx <= 0 || cy <= 0 || x >= g->clipx1 || y >= g->clipy1) { MUTEX_EXIT(g); return; } + if (x+cx > g->clipx1) cx = g->clipx1 - x; + if (y+cy > g->clipy1) cy = g->clipy1 - y; + } + #endif + + abslines = lines < 0 ? -lines : lines; + if (abslines >= cy) { + abslines = cy; + cy = 0; + } else { + // Best is hardware scroll + #if GDISP_HARDWARE_SCROLL + #if GDISP_HARDWARE_SCROLL == HARDWARE_AUTODETECT + if (gvmt(g)->vscroll) + #endif + { + g->p.x = x; + g->p.y = y; + g->p.cx = cx; + g->p.cy = cy; + g->p.y1 = lines; + g->p.color = bgcolor; + gdisp_lld_vertical_scroll(g); + cy -= abslines; + } + #if GDISP_HARDWARE_SCROLL == HARDWARE_AUTODETECT + else + #endif + #elif GDISP_LINEBUF_SIZE == 0 + #error "GDISP: GDISP_NEED_SCROLL is set but there is no hardware support and GDISP_LINEBUF_SIZE is zero." + #endif + + // Scroll Emulation + #if GDISP_HARDWARE_SCROLL != TRUE + { + cy -= abslines; + if (lines < 0) { + fy = y+cy-1; + dy = -1; + } else { + fy = y; + dy = 1; + } + // Move the screen - one line at a time + for(i = 0; i < cy; i++, fy += dy) { + + // Handle where the buffer is smaller than a line + for(ix=0; ix < cx; ix += GDISP_LINEBUF_SIZE) { + + // Calculate the data we can move in one operation + fx = cx - ix; + if (fx > GDISP_LINEBUF_SIZE) + fx = GDISP_LINEBUF_SIZE; + + // Read one line of data from the screen + + // Best line read is hardware streaming + #if GDISP_HARDWARE_STREAM_READ + #if GDISP_HARDWARE_STREAM_READ == HARDWARE_AUTODETECT + if (gvmt(g)->readstart) + #endif + { + g->p.x = x+ix; + g->p.y = fy+lines; + g->p.cx = fx; + g->p.cy = 1; + gdisp_lld_read_start(g); + for(j=0; j < fx; j++) + g->linebuf[j] = gdisp_lld_read_color(g); + gdisp_lld_read_stop(g); + } + #if GDISP_HARDWARE_STREAM_READ == HARDWARE_AUTODETECT + else + #endif + #endif + + // Next best line read is single pixel reads + #if GDISP_HARDWARE_STREAM_READ != TRUE && GDISP_HARDWARE_PIXELREAD + #if GDISP_HARDWARE_PIXELREAD == HARDWARE_AUTODETECT + if (gvmt(g)->get) + #endif + { + for(j=0; j < fx; j++) { + g->p.x = x+ix+j; + g->p.y = fy+lines; + g->linebuf[j] = gdisp_lld_get_pixel_color(g); + } + } + #if GDISP_HARDWARE_PIXELREAD == HARDWARE_AUTODETECT + else { + // Worst is "not possible" + MUTEX_EXIT(g); + return; + } + #endif + #endif + + // Worst is "not possible" + #if !GDISP_HARDWARE_STREAM_READ && !GDISP_HARDWARE_PIXELREAD + #error "GDISP: GDISP_NEED_SCROLL is set but there is no hardware support for scrolling or reading pixels." + #endif + + // Write that line to the new location + + // Best line write is hardware bitfills + #if GDISP_HARDWARE_BITFILLS + #if GDISP_HARDWARE_BITFILLS == HARDWARE_AUTODETECT + if (gvmt(g)->blit) + #endif + { + g->p.x = x+ix; + g->p.y = fy; + g->p.cx = fx; + g->p.cy = 1; + g->p.x1 = 0; + g->p.y1 = 0; + g->p.x2 = fx; + g->p.ptr = (void *)g->linebuf; + gdisp_lld_blit_area(g); + } + #if GDISP_HARDWARE_BITFILLS == HARDWARE_AUTODETECT + else + #endif + #endif + + // Next best line write is hardware streaming + #if GDISP_HARDWARE_BITFILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE + #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT + if (gvmt(g)->writestart) + #endif + { + g->p.x = x+ix; + g->p.y = fy; + g->p.cx = fx; + g->p.cy = 1; + gdisp_lld_write_start(g); + #if GDISP_HARDWARE_STREAM_POS + gdisp_lld_write_pos(g); + #endif + for(j = 0; j < fx; j++) { + g->p.color = g->linebuf[j]; + gdisp_lld_write_color(g); + } + gdisp_lld_write_stop(g); + } + #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT + else + #endif + #endif + + // Next best line write is drawing pixels in combination with filling + #if GDISP_HARDWARE_BITFILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_FILLS && GDISP_HARDWARE_DRAWPIXEL + // We don't need to test for auto-detect on drawpixel as we know we have it because we don't have streaming. + #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT + if (gvmt(g)->fill) + #endif + { + g->p.y = fy; + g->p.cy = 1; + g->p.x = x+ix; + g->p.cx = 1; + for(j = 0; j < fx; ) { + g->p.color = g->linebuf[j]; + if (j + g->p.cx < fx && g->linebuf[j] == g->linebuf[j + g->p.cx]) + g->p.cx++; + else if (g->p.cx == 1) { + gdisp_lld_draw_pixel(g); + j++; + g->p.x++; + } else { + gdisp_lld_fill_area(g); + j += g->p.cx; + g->p.x += g->p.cx; + g->p.cx = 1; + } + } + } + #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT + else + #endif + #endif + + // Worst line write is drawing pixels + #if GDISP_HARDWARE_BITFILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_DRAWPIXEL + // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming + //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT + // if (gvmt(g)->pixel) + //#endif + { + g->p.y = fy; + for(g->p.x = x+ix, j = 0; j < fx; g->p.x++, j++) { + g->p.color = g->linebuf[j]; + gdisp_lld_draw_pixel(g); + } + } + #endif + } + } + } + #endif + } + + /* fill the remaining gap */ + g->p.x = x; + g->p.y = lines > 0 ? (y+cy) : y; + g->p.cx = cx; + g->p.cy = abslines; + g->p.color = bgcolor; + fillarea(g); + autoflush_stopdone(g); + MUTEX_EXIT(g); + } +#endif + +#if GDISP_NEED_CONTROL + #if GDISP_HARDWARE_CONTROL + void gdispGControl(GDisplay *g, unsigned what, void *value) { + #if GDISP_HARDWARE_CONTROL == HARDWARE_AUTODETECT + if (!gvmt(g)->control) + return; + #endif + MUTEX_ENTER(g); + g->p.x = what; + g->p.ptr = value; + if (what == GDISP_CONTROL_ORIENTATION) { + switch ((orientation_t) value) { + case GDISP_ROTATE_LANDSCAPE: + g->p.ptr = g->g.Width >= g->g.Height ? (void *)GDISP_ROTATE_0 : (void *)GDISP_ROTATE_90; + break; + case GDISP_ROTATE_PORTRAIT: + g->p.ptr = g->g.Width >= g->g.Height ? (void *)GDISP_ROTATE_90 : (void *)GDISP_ROTATE_0; + break; + default: + break; + } + } + gdisp_lld_control(g); + #if GDISP_NEED_CLIP || GDISP_NEED_VALIDATION + if (what == GDISP_CONTROL_ORIENTATION) { + // Best is hardware clipping + #if GDISP_HARDWARE_CLIP + #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT + if (gvmt(g)->setclip) + #endif + { + g->p.x = 0; + g->p.y = 0; + g->p.cx = g->g.Width; + g->p.cy = g->g.Height; + gdisp_lld_set_clip(g); + } + #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT + else + #endif + #endif + + // Worst is software clipping + #if GDISP_HARDWARE_CLIP != TRUE + { + g->clipx0 = 0; + g->clipy0 = 0; + g->clipx1 = g->g.Width; + g->clipy1 = g->g.Height; + } + #endif + } + #endif + MUTEX_EXIT(g); + } + #else + void gdispGControl(GDisplay *g, unsigned what, void *value) { + (void)g; + (void)what; + (void)value; + /* Ignore everything */ + } + #endif +#endif + +#if GDISP_NEED_QUERY + #if GDISP_HARDWARE_QUERY + void *gdispGQuery(GDisplay *g, unsigned what) { + void *res; + + #if GDISP_HARDWARE_QUERY == HARDWARE_AUTODETECT + if (!gvmt(g)->query) + return -1; + #endif + MUTEX_ENTER(g); + g->p.x = (coord_t)what; + res = gdisp_lld_query(g); + MUTEX_EXIT(g); + return res; + } + #else + void *gdispGQuery(GDisplay *g, unsigned what) { + (void) what; + return (void *)-1; + } + #endif +#endif + +/*===========================================================================*/ +/* High Level Driver Routines. */ +/*===========================================================================*/ + +void gdispGDrawBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, color_t color) { + if (cx <= 0 || cy <= 0) return; + cx = x+cx-1; cy = y+cy-1; // cx, cy are now the end point. + + MUTEX_ENTER(g); + + g->p.color = color; + + if (cx - x > 2) { + g->p.x = x; g->p.y = y; g->p.x1 = cx; hline_clip(g); + if (y != cy) { + g->p.x = x; g->p.y = cy; g->p.x1 = cx; hline_clip(g); + if (cy - y > 2) { + y++; cy--; + g->p.x = x; g->p.y = y; g->p.y1 = cy; vline_clip(g); + g->p.x = cx; g->p.y = y; g->p.y1 = cy; vline_clip(g); + } + } + } else { + g->p.x = x; g->p.y = y; g->p.y1 = cy; vline_clip(g); + if (x != cx) { + g->p.x = cx; g->p.y = y; g->p.y1 = cy; vline_clip(g); + } + } + + autoflush(g); + MUTEX_EXIT(g); +} + +#if GDISP_NEED_CONVEX_POLYGON + void gdispGDrawPoly(GDisplay *g, coord_t tx, coord_t ty, const point *pntarray, unsigned cnt, color_t color) { + const point *epnt, *p; + + epnt = &pntarray[cnt-1]; + + MUTEX_ENTER(g); + g->p.color = color; + for(p = pntarray; p < epnt; p++) { + g->p.x=tx+p->x; g->p.y=ty+p->y; g->p.x1=tx+p[1].x; g->p.y1=ty+p[1].y; line_clip(g); + } + g->p.x=tx+p->x; g->p.y=ty+p->y; g->p.x1=tx+pntarray->x; g->p.y1=ty+pntarray->y; line_clip(g); + + autoflush(g); + MUTEX_EXIT(g); + } + + void gdispGFillConvexPoly(GDisplay *g, coord_t tx, coord_t ty, const point *pntarray, unsigned cnt, color_t color) { + const point *lpnt, *rpnt, *epnts; + fixed lx, rx, lk, rk; + coord_t y, ymax, lxc, rxc; + + epnts = &pntarray[cnt-1]; + + /* Find a top point */ + rpnt = pntarray; + for(lpnt=pntarray+1; lpnt <= epnts; lpnt++) { + if (lpnt->y < rpnt->y) + rpnt = lpnt; + } + lx = rx = FIXED(rpnt->x); + y = rpnt->y; + + /* Work out the slopes of the two attached line segs */ + for (lpnt = rpnt <= pntarray ? epnts : rpnt-1; lpnt->y == y; cnt--) { + if (!cnt) return; + lx = FIXED(lpnt->x); + lpnt = lpnt <= pntarray ? epnts : lpnt-1; + } + for (rpnt = rpnt >= epnts ? pntarray : rpnt+1; rpnt->y == y; cnt--) { + if (!cnt) return; + rx = FIXED(rpnt->x); + rpnt = rpnt >= epnts ? pntarray : rpnt+1; + } + lk = (FIXED(lpnt->x) - lx) / (lpnt->y - y); + rk = (FIXED(rpnt->x) - rx) / (rpnt->y - y); + + MUTEX_ENTER(g); + g->p.color = color; + while(1) { + /* Determine our boundary */ + ymax = rpnt->y < lpnt->y ? rpnt->y : lpnt->y; + + /* Scan down the line segments until we hit a boundary */ + for(; y < ymax; y++) { + lxc = NONFIXED(lx); + rxc = NONFIXED(rx); + /* + * Doesn't print the right hand point in order to allow polygon joining. + * Also ensures that we draw from left to right with the minimum number + * of pixels. + */ + if (lxc < rxc) { + g->p.x=tx+lxc; g->p.y=ty+y; g->p.x1=tx+rxc-1; hline_clip(g); + } else if (lxc > rxc) { + g->p.x=tx+rxc; g->p.y=ty+y; g->p.x1=tx+lxc-1; hline_clip(g); + } + + lx += lk; + rx += rk; + } + + if (!cnt) { + autoflush(g); + MUTEX_EXIT(g); + return; + } + cnt--; + + /* Replace the appropriate point */ + if (ymax == lpnt->y) { + for (lpnt = lpnt <= pntarray ? epnts : lpnt-1; lpnt->y == y; cnt--) { + if (!cnt) { + autoflush(g); + MUTEX_EXIT(g); + return; + } + lx = FIXED(lpnt->x); + lpnt = lpnt <= pntarray ? epnts : lpnt-1; + } + lk = (FIXED(lpnt->x) - lx) / (lpnt->y - y); + } else { + for (rpnt = rpnt >= epnts ? pntarray : rpnt+1; rpnt->y == y; cnt--) { + if (!cnt) { + autoflush(g); + MUTEX_EXIT(g); + return; + } + rx = FIXED(rpnt->x); + rpnt = rpnt >= epnts ? pntarray : rpnt+1; + } + rk = (FIXED(rpnt->x) - rx) / (rpnt->y - y); + } + } + } + + static int32_t rounding_div(const int32_t n, const int32_t d) + { + if ((n < 0) != (d < 0)) + return (n - d/2) / d; + else + return (n + d/2) / d; + } + + /* Find a vector (nx, ny) that is perpendicular to (dx, dy) and has length + * equal to 'norm'. */ + static void get_normal_vector(coord_t dx, coord_t dy, coord_t norm, coord_t *nx, coord_t *ny) + { + int32_t dx2, dy2, len_sq, norm_sq, norm_sq2; + int div, step, best, delta, abs_delta; + + dx2 = dx; dy2 = dy; + norm_sq = (int32_t)norm * norm; + norm_sq2 = norm_sq * 512; + + /* Scale dx2 and dy2 so that + * len_sq / 2 <= norm_sq * 512 <= len_sq * 2. + * The scaling by 512 is to yield higher accuracy in division later. */ + len_sq = dx2 * dx2 + dy2 * dy2; + + if (len_sq < norm_sq2) + { + while (len_sq && len_sq < norm_sq2) + { + len_sq <<= 2; dx2 <<= 1; dy2 <<= 1; + } + } + else if (len_sq > norm_sq2) + { + while (len_sq && len_sq > norm_sq2) + { + len_sq >>= 2; dx2 >>= 1; dy2 >>= 1; + } + } + + /* Now find the divider div so that + * len_sq / div^2 == norm_sq i.e. div = sqrt(len_sq / norm_sq) + * + * This is done using bisection search to avoid the need for floating + * point sqrt. + * + * Based on previous scaling, we know that + * len_sq / 2 <= norm_sq * 512 <=> div <= sqrt(1024) = 32 + * len_sq * 2 >= norm_sq * 512 <=> div >= sqrt(256) = 16 + */ + div = 24; step = 8; + best = 256; + + for (;;) + { + dx = dx2 / div; + dy = dy2 / div; + len_sq = dx*dx + dy*dy; + + delta = len_sq - norm_sq; + + abs_delta = (delta >= 0) ? delta : -delta; + + if (abs_delta < best) + { + *nx = dy; + *ny = -dx; + best = abs_delta; + } + + if (delta > 0) + div += step; + else if (delta < 0) + div -= step; + else if (delta == 0) + break; + + if (step == 0) + break; + else + step >>= 1; /* Do one round with step = 0 to calculate final result. */ + } + } + + void gdispGDrawThickLine(GDisplay *g, coord_t x0, coord_t y0, coord_t x1, coord_t y1, color_t color, coord_t width, bool_t round) { + coord_t dx, dy, nx = 0, ny = 0; + + /* Compute the direction vector for the line */ + dx = x1 - x0; + dy = y1 - y0; + + /* Draw a small dot if the line length is zero. */ + if (dx == 0 && dy == 0) + dx += 1; + + /* Compute a normal vector with length 'width'. */ + get_normal_vector(dx, dy, width, &nx, &ny); + + /* Handle 1px wide lines gracefully */ + if (nx == 0 && ny == 0) + nx = 1; + + /* Offset the x0,y0 by half the width of the line. This way we + * can keep the width of the line accurate even if it is not evenly + * divisible by 2. + */ + { + x0 -= rounding_div(nx, 2); + y0 -= rounding_div(ny, 2); + } + + /* Fill in the point array */ + if (!round) { + /* We use 4 points for the basic line shape: + * + * pt1 pt2 + * (+n) ------------------------------------ (d+n) + * | | + * (0,0) ----------------------------------- (d) + * pt0 pt3 + */ + point pntarray[4]; + + pntarray[0].x = 0; + pntarray[0].y = 0; + pntarray[1].x = nx; + pntarray[1].y = ny; + pntarray[2].x = dx + nx; + pntarray[2].y = dy + ny; + pntarray[3].x = dx; + pntarray[3].y = dy; + + gdispGFillConvexPoly(g, x0, y0, pntarray, 4, color); + } else { + /* We use 4 points for basic shape, plus 4 extra points for ends: + * + * pt3 ------------------ pt4 + * / \ + * pt2 pt5 + * | | + * pt1 pt6 + * \ / + * pt0 -------------------pt7 + */ + point pntarray[8]; + coord_t nx2, ny2; + + /* Magic numbers: + * 75/256 = sin(45) / (1 + sqrt(2)) diagonal octagon segments + * 106/256 = 1 / (1 + sqrt(2)) octagon side + * 53/256 = 0.5 / (1 + sqrt(2)) half of octagon side + * 150/256 = 1 - 1 / (1 + sqrt(2)) octagon height minus one side + */ + + /* Rotate the normal vector 45 deg counter-clockwise and reduce + * to 1 / (1 + sqrt(2)) length, for forming octagonal ends. */ + nx2 = rounding_div((nx * 75 + ny * 75), 256); + ny2 = rounding_div((-nx * 75 + ny * 75), 256); + + /* Offset and extend the line so that the center of the octagon + * is at the specified points. */ + x0 += ny * 53 / 256; + y0 -= nx * 53 / 256; + dx -= ny * 106 / 256; + dy += nx * 106 / 256; + + /* Now fill in the points by summing the calculated vectors. */ + pntarray[0].x = 0; + pntarray[0].y = 0; + pntarray[1].x = nx2; + pntarray[1].y = ny2; + pntarray[2].x = nx2 + nx * 106/256; + pntarray[2].y = ny2 + ny * 106/256; + pntarray[3].x = nx; + pntarray[3].y = ny; + pntarray[4].x = dx + nx; + pntarray[4].y = dy + ny; + pntarray[5].x = dx + nx - nx2; + pntarray[5].y = dy + ny - ny2; + pntarray[6].x = dx + nx * 150/256 - nx2; + pntarray[6].y = dy + ny * 150/256 - ny2; + pntarray[7].x = dx; + pntarray[7].y = dy; + + gdispGFillConvexPoly(g, x0, y0, pntarray, 8, color); + } + } +#endif + +#if GDISP_NEED_TEXT + #include "mcufont/mcufont.h" + + #if GDISP_NEED_ANTIALIAS && GDISP_HARDWARE_PIXELREAD + static void drawcharline(int16_t x, int16_t y, uint8_t count, uint8_t alpha, void *state) { + #define GD ((GDisplay *)state) + if (y < GD->t.clipy0 || y >= GD->t.clipy1 || x+count <= GD->t.clipx0 || x >= GD->t.clipx1) + return; + if (x < GD->t.clipx0) { + count -= GD->t.clipx0 - x; + x = GD->t.clipx0; + } + if (x+count > GD->t.clipx1) + count = GD->t.clipx1 - x; + if (alpha == 255) { + GD->p.x = x; GD->p.y = y; GD->p.x1 = x+count-1; GD->p.color = GD->t.color; + hline_clip(GD); + } else { + for (; count; count--, x++) { + GD->p.x = x; GD->p.y = y; + GD->p.color = gdispBlendColor(GD->t.color, gdisp_lld_get_pixel_color(GD), alpha); + drawpixel_clip(GD); + } + } + #undef GD + } + #else + static void drawcharline(int16_t x, int16_t y, uint8_t count, uint8_t alpha, void *state) { + #define GD ((GDisplay *)state) + if (y < GD->t.clipy0 || y >= GD->t.clipy1 || x+count <= GD->t.clipx0 || x >= GD->t.clipx1) + return; + if (x < GD->t.clipx0) { + count -= GD->t.clipx0 - x; + x = GD->t.clipx0; + } + if (x+count > GD->t.clipx1) + count = GD->t.clipx1 - x; + if (alpha > 0x80) { // A best approximation when using anti-aliased fonts but we can't actually draw them anti-aliased + GD->p.x = x; GD->p.y = y; GD->p.x1 = x+count-1; GD->p.color = GD->t.color; + hline_clip(GD); + } + #undef GD + } + #endif + + #if GDISP_NEED_ANTIALIAS + static void fillcharline(int16_t x, int16_t y, uint8_t count, uint8_t alpha, void *state) { + #define GD ((GDisplay *)state) + if (y < GD->t.clipy0 || y >= GD->t.clipy1 || x+count <= GD->t.clipx0 || x >= GD->t.clipx1) + return; + if (x < GD->t.clipx0) { + count -= GD->t.clipx0 - x; + x = GD->t.clipx0; + } + if (x+count > GD->t.clipx1) + count = GD->t.clipx1 - x; + if (alpha == 255) { + GD->p.color = GD->t.color; + } else { + GD->p.color = gdispBlendColor(GD->t.color, GD->t.bgcolor, alpha); + } + GD->p.x = x; GD->p.y = y; GD->p.x1 = x+count-1; + hline_clip(GD); + #undef GD + } + #else + #define fillcharline drawcharline + #endif + + /* Callback to render characters. */ + static uint8_t drawcharglyph(int16_t x, int16_t y, mf_char ch, void *state) { + #define GD ((GDisplay *)state) + return mf_render_character(GD->t.font, x, y, ch, drawcharline, state); + #undef GD + } + + /* Callback to render characters. */ + static uint8_t fillcharglyph(int16_t x, int16_t y, mf_char ch, void *state) { + #define GD ((GDisplay *)state) + return mf_render_character(GD->t.font, x, y, ch, fillcharline, state); + #undef GD + } + + void gdispGDrawChar(GDisplay *g, coord_t x, coord_t y, uint16_t c, font_t font, color_t color) { + MUTEX_ENTER(g); + g->t.font = font; + g->t.clipx0 = x; + g->t.clipy0 = y; + g->t.clipx1 = x + mf_character_width(font, c) + font->baseline_x; + g->t.clipy1 = y + font->height; + g->t.color = color; + mf_render_character(font, x, y, c, drawcharline, g); + autoflush(g); + MUTEX_EXIT(g); + } + + void gdispGFillChar(GDisplay *g, coord_t x, coord_t y, uint16_t c, font_t font, color_t color, color_t bgcolor) { + MUTEX_ENTER(g); + g->p.cx = mf_character_width(font, c) + font->baseline_x; + g->p.cy = font->height; + g->t.font = font; + g->t.clipx0 = g->p.x = x; + g->t.clipy0 = g->p.y = y; + g->t.clipx1 = g->p.x+g->p.cx; + g->t.clipy1 = g->p.y+g->p.cy; + g->t.color = color; + g->t.bgcolor = g->p.color = bgcolor; + + TEST_CLIP_AREA(g) { + fillarea(g); + mf_render_character(font, x, y, c, fillcharline, g); + } + autoflush(g); + MUTEX_EXIT(g); + } + + void gdispGDrawString(GDisplay *g, coord_t x, coord_t y, const char *str, font_t font, color_t color) { + MUTEX_ENTER(g); + g->t.font = font; + g->t.clipx0 = x; + g->t.clipy0 = y; + g->t.clipx1 = x + mf_get_string_width(font, str, 0, 0); + g->t.clipy1 = y + font->height; + g->t.color = color; + + mf_render_aligned(font, x+font->baseline_x, y, MF_ALIGN_LEFT, str, 0, drawcharglyph, g); + autoflush(g); + MUTEX_EXIT(g); + } + + void gdispGFillString(GDisplay *g, coord_t x, coord_t y, const char *str, font_t font, color_t color, color_t bgcolor) { + MUTEX_ENTER(g); + g->p.cx = mf_get_string_width(font, str, 0, 0); + g->p.cy = font->height; + g->t.font = font; + g->t.clipx0 = g->p.x = x; + g->t.clipy0 = g->p.y = y; + g->t.clipx1 = g->p.x+g->p.cx; + g->t.clipy1 = g->p.y+g->p.cy; + g->t.color = color; + g->t.bgcolor = g->p.color = bgcolor; + + TEST_CLIP_AREA(g) { + fillarea(g); + mf_render_aligned(font, x+font->baseline_x, y, MF_ALIGN_LEFT, str, 0, fillcharglyph, g); + } + + autoflush(g); + MUTEX_EXIT(g); + } + + void gdispGDrawStringBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, const char* str, font_t font, color_t color, justify_t justify) { + MUTEX_ENTER(g); + g->t.font = font; + g->t.clipx0 = x; + g->t.clipy0 = y; + g->t.clipx1 = x+cx; + g->t.clipy1 = y+cy; + g->t.color = color; + + /* Select the anchor position */ + switch(justify) { + case justifyCenter: + x += (cx + 1) / 2; + break; + case justifyRight: + x += cx; + break; + default: // justifyLeft + x += font->baseline_x; + break; + } + y += (cy+1 - font->height)/2; + + mf_render_aligned(font, x, y, justify, str, 0, drawcharglyph, g); + + autoflush(g); + MUTEX_EXIT(g); + } + + void gdispGFillStringBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, const char* str, font_t font, color_t color, color_t bgcolor, justify_t justify) { + MUTEX_ENTER(g); + g->p.cx = cx; + g->p.cy = cy; + g->t.font = font; + g->t.clipx0 = g->p.x = x; + g->t.clipy0 = g->p.y = y; + g->t.clipx1 = x+cx; + g->t.clipy1 = y+cy; + g->t.color = color; + g->t.bgcolor = g->p.color = bgcolor; + + TEST_CLIP_AREA(g) { + + // background fill + fillarea(g); + + /* Select the anchor position */ + switch(justify) { + case justifyCenter: + x += (cx + 1) / 2; + break; + case justifyRight: + x += cx; + break; + default: // justifyLeft + x += font->baseline_x; + break; + } + y += (cy+1 - font->height)/2; + + /* Render */ + mf_render_aligned(font, x, y, justify, str, 0, fillcharglyph, g); + } + + autoflush(g); + MUTEX_EXIT(g); + } + + coord_t gdispGetFontMetric(font_t font, fontmetric_t metric) { + /* No mutex required as we only read static data */ + switch(metric) { + case fontHeight: return font->height; + case fontDescendersHeight: return font->height - font->baseline_y; + case fontLineSpacing: return font->line_height; + case fontCharPadding: return 0; + case fontMinWidth: return font->min_x_advance; + case fontMaxWidth: return font->max_x_advance; + } + return 0; + } + + coord_t gdispGetCharWidth(char c, font_t font) { + /* No mutex required as we only read static data */ + return mf_character_width(font, c); + } + + coord_t gdispGetStringWidth(const char* str, font_t font) { + if (!str) + return 0; + + /* No mutex required as we only read static data */ + return mf_get_string_width(font, str, 0, 0); + } +#endif + +color_t gdispBlendColor(color_t fg, color_t bg, uint8_t alpha) +{ + uint16_t fg_ratio = alpha + 1; + uint16_t bg_ratio = 256 - alpha; + uint16_t r, g, b; + + r = RED_OF(fg) * fg_ratio; + g = GREEN_OF(fg) * fg_ratio; + b = BLUE_OF(fg) * fg_ratio; + + r += RED_OF(bg) * bg_ratio; + g += GREEN_OF(bg) * bg_ratio; + b += BLUE_OF(bg) * bg_ratio; + + r >>= 8; + g >>= 8; + b >>= 8; + + return RGB2COLOR(r, g, b); +} + +color_t gdispContrastColor(color_t color) { + uint16_t r, g, b; + + r = RED_OF(color) > 128 ? 0 : 255; + g = GREEN_OF(color) > 128 ? 0 : 255; + b = BLUE_OF(color) > 128 ? 0 : 255; + + return RGB2COLOR(r, g, b); +} + +#if (!defined(gdispPackPixels) && !defined(GDISP_PIXELFORMAT_CUSTOM)) + void gdispPackPixels(pixel_t *buf, coord_t cx, coord_t x, coord_t y, color_t color) { + /* No mutex required as we only read static data */ + #if defined(GDISP_PIXELFORMAT_RGB888) + #error "GDISP: Packed pixels not supported yet" + #elif defined(GDISP_PIXELFORMAT_RGB444) + #error "GDISP: Packed pixels not supported yet" + #elif defined(GDISP_PIXELFORMAT_RGB666) + #error "GDISP: Packed pixels not supported yet" + #elif + #error "GDISP: Unsupported packed pixel format" + #endif + } +#endif + +#endif /* GFX_USE_GDISP */ diff --git a/src/gdisp/gdisp.h b/src/gdisp/gdisp.h new file mode 100644 index 00000000..aaf53478 --- /dev/null +++ b/src/gdisp/gdisp.h @@ -0,0 +1,1124 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gdisp/gdisp.h + * @brief GDISP Graphic Driver subsystem header file. + * + * @addtogroup GDISP + * + * @brief Module to interface graphic / pixel oriented displays + * + * @details The GDISP module provides high level abstraction to interface pixel oriented graphic displays. + * + * @pre GFX_USE_GDISP must be set to TRUE in gfxconf.h + * + * @note Each drawing routine supports a gdispXXXX and a gdispGXXXX function. The difference is that the + * gdispXXXX function does not require a display to be specified. Note there is a slight anomaly + * in the naming with gdispGBlitArea() vs gdispBlitAreaEx() and gdispBlitArea(), the latter of + * which is now deprecated. + * @{ + */ + +#ifndef _GDISP_H +#define _GDISP_H + +#include "gfx.h" + +/* This type definition is defined here as it gets used in other gfx sub-systems even + * if GFX_USE_GDISP is FALSE. + */ + +/** + * @brief The type for a coordinate or length on the screen. + */ +typedef int16_t coord_t; + +#if GFX_USE_GDISP || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Type definitions */ +/*===========================================================================*/ + +/** + * @brief Type for a 2D point on the screen. + */ +typedef struct point { coord_t x, y; } point, point_t; +/** + * @brief Type for the text justification. + */ +typedef enum justify { justifyLeft=0, justifyCenter=1, justifyRight=2 } justify_t; +/** + * @brief Type for the font metric. + */ +typedef enum fontmetric { fontHeight, fontDescendersHeight, fontLineSpacing, fontCharPadding, fontMinWidth, fontMaxWidth } fontmetric_t; +/** + * @brief The type of a font. + */ +typedef const struct mf_font_s* font_t; +/** + * @brief Type for the screen orientation. + * @note GDISP_ROTATE_LANDSCAPE and GDISP_ROTATE_PORTRAIT are internally converted to the + * most appropriate other orientation. + */ +typedef enum orientation { GDISP_ROTATE_0=0, GDISP_ROTATE_90=90, GDISP_ROTATE_180=180, GDISP_ROTATE_270=270, GDISP_ROTATE_PORTRAIT=1000, GDISP_ROTATE_LANDSCAPE=1001 } orientation_t; +/** + * @brief Type for the available power modes for the screen. + */ +typedef enum powermode { powerOff, powerSleep, powerDeepSleep, powerOn } powermode_t; + +/* + * Our black box display structure. + */ +typedef struct GDisplay GDisplay; + +/** + * @brief The default screen to use for the gdispXXXX calls. + * @note This is set by default to the first display in the system. You can change + * it by calling @p gdispGSetDisplay(). + */ +extern GDisplay *GDISP; + +/*===========================================================================*/ +/* Constants. */ +/*===========================================================================*/ + +/** + * @brief Driver Control Constants + * @details Unsupported control codes are ignored. + * @note The value parameter should always be typecast to (void *). + * @note There are some predefined and some specific to the low level driver. + * @note GDISP_CONTROL_POWER - Takes a gdisp_powermode_t + * GDISP_CONTROL_ORIENTATION - Takes a gdisp_orientation_t + * GDISP_CONTROL_BACKLIGHT - Takes an int from 0 to 100. For a driver + * that only supports off/on anything other + * than zero is on. + * GDISP_CONTROL_CONTRAST - Takes an int from 0 to 100. + * GDISP_CONTROL_LLD - Low level driver control constants start at + * this value. + */ +#define GDISP_CONTROL_POWER 0 +#define GDISP_CONTROL_ORIENTATION 1 +#define GDISP_CONTROL_BACKLIGHT 2 +#define GDISP_CONTROL_CONTRAST 3 +#define GDISP_CONTROL_LLD 1000 + +/*===========================================================================*/ +/* Defines relating to the display hardware */ +/*===========================================================================*/ + +#if !defined(GDISP_DRIVER_LIST) + // Pull in the default hardware configuration for a single controller. + // If we have multiple controllers the settings must be set in the + // users gfxconf.h file. + #include "gdisp_lld_config.h" + + // Unless the user has specified a specific pixel format, use + // the native format for the controller. + #if !defined(GDISP_PIXELFORMAT) && defined(GDISP_LLD_PIXELFORMAT) + #define GDISP_PIXELFORMAT GDISP_LLD_PIXELFORMAT + #endif +#endif + +/** + * @name GDISP pixel format choices + * @{ + */ + /** + * @brief The pixel format. + * @details It generally defaults to the hardware pixel format. + * @note This doesn't need to match the hardware pixel format. + * It is definitely more efficient when it does. + * @note When GDISP_DRIVER_LIST is defined, this must + * be explicitly defined and you should ensure the best match + * with your hardware across all devices. + */ + #ifndef GDISP_PIXELFORMAT + #define GDISP_PIXELFORMAT GDISP_PIXELFORMAT_ERROR + #endif + /** + * @brief Do pixels require packing for a blit + * @note Is only valid for a pixel format that doesn't fill it's datatype. eg formats: + * GDISP_PIXELFORMAT_RGB888 + * GDISP_PIXELFORMAT_RGB444 + * GDISP_PIXELFORMAT_RGB666 + * GDISP_PIXELFORMAT_CUSTOM + * @note Very few cases should actually require packed pixels as the low + * level driver can also pack on the fly as it is sending it + * to the graphics device. + * @note Packed pixels are not really supported at this point. + */ + #ifndef GDISP_PACKED_PIXELS + #define GDISP_PACKED_PIXELS FALSE + #endif + + /** + * @brief Do lines of pixels require packing for a blit + * @note Ignored if GDISP_PACKED_PIXELS is FALSE + */ + #ifndef GDISP_PACKED_LINES + #define GDISP_PACKED_LINES FALSE + #endif +/** @} */ + +/*===========================================================================*/ +/* Defines related to the pixel format */ +/*===========================================================================*/ + +/* Load our color definitions and pixel formats */ +#include "gdisp_colors.h" + +/** + * @brief The type of a pixel. + */ +typedef color_t pixel_t; + +#ifdef __cplusplus +extern "C" { +#endif + +/* Color Utility Functions */ + +/** + * @brief Blend 2 colors according to the alpha + * @return The combined color + * + * @param[in] fg The foreground color + * @param[in] bg The background color + * @param[in] alpha The alpha value (0-255). 0 is all background, 255 is all foreground. + * + * @api + */ +color_t gdispBlendColor(color_t fg, color_t bg, uint8_t alpha); + +/** + * @brief Find a contrasting color + * @return The contrasting color + * + * @param[in] color The color to contrast + * + * @api + */ +color_t gdispContrastColor(color_t color); + +/* Base Functions */ + +/** + * @brief Get the specified display + * @return The pointer to the display or NULL if the display doesn't exist + * @note The GDISP variable contains the display used by the gdispXxxx routines + * as opposed to the gdispGXxxx routines which take an explicit display + * parameter. + * @note Displays are numbered from 0 to @p gdispGetDisplayCount() - 1 + * + * @param[in] display The display number (0..n) + * + * @api + */ +GDisplay *gdispGetDisplay(unsigned display); + +/** + * @brief Set the current default display to the specified display + * @note The default display is used for the gdispXxxx functions. + * @note The default display is contained in the variable GDISP. Using + * this function to set it protects against it being set to a NULL + * value. + * @note If a NULL is passed for the dispay this call is ignored. + * + * @param[in] g The display to use + * + * @api + */ +void gdispSetDisplay(GDisplay *g); + +/** + * @brief Get the count of currently active displays + * @return The count of displays currently in the system + * + * @note Displays are numbered from 0 to @p gdispGetDisplayCount() - 1 + */ +unsigned gdispGetDisplayCount(void); + +/* Property Functions */ + +/** + * @brief Get the display width in pixels. + * + * @param[in] g The display to use + * + * @return The width of the display + * + * @api + */ +coord_t gdispGGetWidth(GDisplay *g); +#define gdispGetWidth() gdispGGetWidth(GDISP) + +/** + * @brief Get the display height in pixels. + * + * @param[in] g The display to use + * + * @return The height of the display + * + * @api + */ +coord_t gdispGGetHeight(GDisplay *g); +#define gdispGetHeight() gdispGGetHeight(GDISP) + +/** + * @brief Get the current display power mode. + * + * @param[in] g The display to use + * + * @return The current power mode + * + * @api + */ +powermode_t gdispGGetPowerMode(GDisplay *g); +#define gdispGetPowerMode() gdispGGetPowerMode(GDISP) + +/** + * @brief Get the current display orientation. + * + * @param[in] g The display to use + * + * @return The current orientation + * + * @api + */ +orientation_t gdispGGetOrientation(GDisplay *g); +#define gdispGetOrientation() gdispGGetOrientation(GDISP) + +/** + * @brief Get the current display backlight brightness. + * + * @param[in] g The display to use + * + * @return The current backlight value + * + * @api + */ +uint8_t gdispGGetBacklight(GDisplay *g); +#define gdispGetBacklight() gdispGGetBacklight(GDISP) + +/** + * @brief Get the current display contrast. + * + * @param[in] g The display to use + * + * @return The current contrast value + * + * @api + */ +uint8_t gdispGGetContrast(GDisplay *g); +#define gdispGetContrast() gdispGGetContrast(GDISP) + +/* Drawing Functions */ + +/** + * @brief Flush current drawing operations to the display + * @note Some low level drivers do not update the display until + * the display is flushed. For others it is optional but can + * help prevent tearing effects. For some it is ignored. + * Calling it at the end of a logic set of drawing operations + * in your application will ensure controller portability. If you + * know your controller does not need to be flushed there is no + * need to call it (which is in reality most controllers). + * @note Even for displays that require flushing, there is no need to + * call this function if GDISP_NEED_AUTOFLUSH is TRUE. + * Calling it again won't hurt though. + * + * + * @param[in] g The display to use + * + * @api + */ +void gdispGFlush(GDisplay *g); +#define gdispFlush() gdispGFlush(GDISP) + +/** + * @brief Clear the display to the specified color. + * + * @param[in] g The display to use + * @param[in] color The color to use when clearing the screen + * + * @api + */ +void gdispGClear(GDisplay *g, color_t color); +#define gdispClear(c) gdispGClear(GDISP, c) + +/** + * @brief Set a pixel in the specified color. + * + * @param[in] g The display to use + * @param[in] x,y The position to set the pixel. + * @param[in] color The color to use + * + * @api + */ +void gdispGDrawPixel(GDisplay *g, coord_t x, coord_t y, color_t color); +#define gdispDrawPixel(x,y,c) gdispGDrawPixel(GDISP,x,y,c) + +/** + * @brief Draw a line. + * + * @param[in] g The display to use + * @param[in] x0,y0 The start position + * @param[in] x1,y1 The end position + * @param[in] color The color to use + * + * @api + */ +void gdispGDrawLine(GDisplay *g, coord_t x0, coord_t y0, coord_t x1, coord_t y1, color_t color); +#define gdispDrawLine(x0,y0,x1,y1,c) gdispGDrawLine(GDISP,x0,y0,x1,y1,c) + +/** + * @brief Fill an area with a color. + * + * @param[in] g The display to use + * @param[in] x,y The start position + * @param[in] cx,cy The size of the box (outside dimensions) + * @param[in] color The color to use + * + * @api + */ +void gdispGFillArea(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, color_t color); +#define gdispFillArea(x,y,cx,cy,c) gdispGFillArea(GDISP,x,y,cx,cy,c) + +/** + * @brief Fill an area using the supplied bitmap. + * @details The bitmap is in the pixel format specified by the low level driver + * @note If a packed pixel format is used and the width doesn't + * match a whole number of bytes, the next line will start on a + * non-byte boundary (no end-of-line padding). + * @note If GDISP_NEED_ASYNC is defined then the buffer must be static + * or at least retained until this call has finished the blit. You can + * tell when all graphics drawing is finished by @p gdispIsBusy() going FALSE. + * + * @param[in] g The display to use + * @param[in] x,y The start position + * @param[in] cx,cy The size of the filled area + * @param[in] srcx,srcy The bitmap position to start the fill form + * @param[in] srccx The width of a line in the bitmap + * @param[in] buffer The bitmap in the driver's pixel format + * + * @api + */ +void gdispGBlitArea(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t srcx, coord_t srcy, coord_t srccx, const pixel_t *buffer); +#define gdispBlitAreaEx(x,y,cx,cy,sx,sy,rx,b) gdispGBlitArea(GDISP,x,y,cx,cy,sx,sy,rx,b) + +/** + * @brief Draw a rectangular box. + * + * @param[in] g The display to use + * @param[in] x,y The start position + * @param[in] cx,cy The size of the box (outside dimensions) + * @param[in] color The color to use + * + * @api + */ +void gdispGDrawBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, color_t color); +#define gdispDrawBox(x,y,cx,cy,c) gdispGDrawBox(GDISP,x,y,cx,cy,c) + +/* Streaming Functions */ + +#if GDISP_NEED_STREAMING || defined(__DOXYGEN__) + /** + * @brief Start a streaming operation. + * @details Stream data to a window on the display sequentially and very fast. + * @pre GDISP_NEED_STREAMING must be TRUE in your gfxconf.h + * @note While streaming is in operation - no other calls to GDISP functions + * can be made (with the exception of @p gdispBlendColor() and streaming + * functions). If a call is made (eg in a multi-threaded application) the other + * call is blocked waiting for the streaming operation to finish. + * @note @p gdispStreamStop() must be called to finish the streaming operation. + * @note If more data is written than the defined area then the results are unspecified. + * Some drivers may wrap back to the beginning of the area, others may just + * ignore subsequent data. + * @note Unlike most operations that clip the defined area to the display to generate + * a smaller active area, this call will just silently fail if any of the stream + * region lies outside the current clipping area. + * @note A streaming operation may be terminated early (without writing to every location + * in the stream area) by calling @p gdispStreamStop(). + * + * @param[in] g The display to use + * @param[in] x,y The start position + * @param[in] cx,cy The size of the streamable area + * + * @api + */ + void gdispGStreamStart(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy); + #define gdispStreamStart(x,y,cx,cy) gdispGStreamStart(GDISP,x,y,cx,cy) + + /** + * @brief Send pixel data to the stream. + * @details Write a pixel to the next position in the streamed area and increment the position + * @pre GDISP_NEED_STREAMING must be TRUE in your gfxconf.h + * @pre @p gdispStreamStart() has been called. + * @note If the gdispStreamStart() has not been called (or failed due to clipping), the + * data provided here is simply thrown away. + * + * @param[in] g The display to use + * @param[in] color The color of the pixel to write + * + * @api + */ + void gdispGStreamColor(GDisplay *g, color_t color); + #define gdispStreamColor(c) gdispGStreamColor(GDISP,c) + + /** + * @brief Finish the current streaming operation. + * @details Completes the current streaming operation and allows other GDISP calls to operate again. + * @pre GDISP_NEED_STREAMING must be TRUE in your gfxconf.h + * @pre @p gdispStreamStart() has been called. + * @note If the gdispStreamStart() has not been called (or failed due to clipping), this + * call is simply ignored. + * + * @param[in] g The display to use + * + * @api + */ + void gdispGStreamStop(GDisplay *g); + #define gdispStreamStop() gdispGStreamStop(GDISP) +#endif + +/* Clipping Functions */ + +#if GDISP_NEED_CLIP || defined(__DOXYGEN__) + /** + * @brief Clip all drawing to the defined area. + * @pre GDISP_NEED_CLIP must be TRUE in your gfxconf.h + * + * @param[in] g The display to use + * @param[in] x,y The start position + * @param[in] cx,cy The size of the clip area + * + * @api + */ + void gdispGSetClip(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy); + #define gdispSetClip(x,y,cx,cy) gdispGSetClip(GDISP,x,y,cx,cy) +#endif + +/* Circle Functions */ + +#if GDISP_NEED_CIRCLE || defined(__DOXYGEN__) + /** + * @brief Draw a circle. + * @pre GDISP_NEED_CIRCLE must be TRUE in your gfxconf.h + * + * @param[in] g The display to use + * @param[in] x,y The center of the circle + * @param[in] radius The radius of the circle + * @param[in] color The color to use + * + * @api + */ + void gdispGDrawCircle(GDisplay *g, coord_t x, coord_t y, coord_t radius, color_t color); + #define gdispDrawCircle(x,y,r,c) gdispGDrawCircle(GDISP,x,y,r,c) + + /** + * @brief Draw a filled circle. + * @pre GDISP_NEED_CIRCLE must be TRUE in your gfxconf.h + * + * @param[in] g The display to use + * @param[in] x,y The center of the circle + * @param[in] radius The radius of the circle + * @param[in] color The color to use + * + * @api + */ + void gdispGFillCircle(GDisplay *g, coord_t x, coord_t y, coord_t radius, color_t color); + #define gdispFillCircle(x,y,r,c) gdispGFillCircle(GDISP,x,y,r,c) +#endif + +/* Ellipse Functions */ + +#if GDISP_NEED_ELLIPSE || defined(__DOXYGEN__) + /** + * @brief Draw an ellipse. + * @pre GDISP_NEED_ELLIPSE must be TRUE in your gfxconf.h + * + * @param[in] g The display to use + * @param[in] x,y The center of the ellipse + * @param[in] a,b The dimensions of the ellipse + * @param[in] color The color to use + * + * @api + */ + void gdispGDrawEllipse(GDisplay *g, coord_t x, coord_t y, coord_t a, coord_t b, color_t color); + #define gdispDrawEllipse(x,y,a,b,c) gdispGDrawEllipse(GDISP,x,y,a,b,c) + + /** + * @brief Draw a filled ellipse. + * @pre GDISP_NEED_ELLIPSE must be TRUE in your gfxconf.h + * + * @param[in] g The display to use + * @param[in] x,y The center of the ellipse + * @param[in] a,b The dimensions of the ellipse + * @param[in] color The color to use + * + * @api + */ + void gdispGFillEllipse(GDisplay *g, coord_t x, coord_t y, coord_t a, coord_t b, color_t color); + #define gdispFillEllipse(x,y,a,b,c) gdispGFillEllipse(GDISP,x,y,a,b,c) +#endif + +/* Arc Functions */ +#if GDISP_NEED_ARCSECTORS || defined(__DOXYGEN__) + /** + * @brief Draw a selection of 45 degree arcs of a circle + * @pre GDISP_NEED_ARCSECTORS must be TRUE in your gfxconf.h + * + * @param[in] g The display to use + * @param[in] x,y The center of the circle + * @param[in] radius The radius of the circle + * @param[in] sectors Bits determine which sectors are drawn. + * Bits go anti-clockwise from the 0 degree mark (y = 0, x is positive), as follows: + * bit 0 - upper right right ----- + * bit 1 - upper upper right /2 1\ + * bit 2 - upper upper left /3 0\ + * bit 3 - upper left left \4 7/ + * bit 4 - lower left left \5 6/ + * bit 5 - lower lower left ----- + * bit 6 - lower lower right + * bit 7 - lower left left + * @param[in] color The color to use + * + * @note This is a more limited versions of the general arc drawing routine. It + * doesn't require trig libraries or tables or floating point and is smaller in code size. + * There is probably little point in including both this and the general + * arc routine as the general arc routine can do everything this can do. + * + * @api + */ + void gdispGDrawArcSectors(GDisplay *g, coord_t x, coord_t y, coord_t radius, uint8_t sectors, color_t color); + #define gdispDrawArcSectors(x,y,r,s,c) gdispGDrawArcSectors(GDISP,x,y,r,s,c) + + /** + * @brief Fill a selection of 45 degree arcs of a circle + * @pre GDISP_NEED_ARCSECTORS must be TRUE in your gfxconf.h + * + * @param[in] g The display to use + * @param[in] x,y The center of the circle + * @param[in] radius The radius of the circle + * @param[in] sectors Bits determine which sectors are drawn. + * Bits go anti-clockwise from the 0 degree mark (y = 0, x is positive), as follows: + * bit 0 - upper right right ----- + * bit 1 - upper upper right /2 1\ + * bit 2 - upper upper left /3 0\ + * bit 3 - upper left left \4 7/ + * bit 4 - lower left left \5 6/ + * bit 5 - lower lower left ----- + * bit 6 - lower lower right + * bit 7 - lower left left + * @param[in] color The color to use + * + * @note This is a more limited versions of the general arc filling routine. It + * doesn't require trig libraries or tables or floating point and is smaller in code size. + * There is probably little point in including both this and the general + * arc routine as the general arc routine can do everything this can do. + * + * @api + */ + void gdispGFillArcSectors(GDisplay *g, coord_t x, coord_t y, coord_t radius, uint8_t sectors, color_t color); + #define gdispFillArcSectors(x,y,r,s,c) gdispGFillArcSectors(GDISP,x,y,r,s,c) +#endif + +#if GDISP_NEED_ARC || defined(__DOXYGEN__) + /* + * @brief Draw an arc. + * @pre GDISP_NEED_ARC must be TRUE in your gfxconf.h + * + * @param[in] g The display to use + * @param[in] x0,y0 The center point + * @param[in] radius The radius of the arc + * @param[in] start The start angle (0 to 360) + * @param[in] end The end angle (0 to 360) + * @param[in] color The color of the arc + * + * @note If you are just doing 45 degree angles consider using @p gdispDrawArcSectors() instead. + * @note This routine requires trig support. It can either come from your C runtime library + * cos() and sin() which requires floating point support (and is slow), or you can define GFX_USE_GMISC + * and either GMISC_NEED_FIXEDTRIG or GMISC_NEED_FASTTRIG. + * GMISC_NEED_FASTTRIG uses table based floating point trig operations. + * GMISC_NEED_FIXEDTRIG uses fixed point integer trig operations. + * Note accuracy on both the table based options are more than adequate for the one degree + * resolution provided by these arc routines. Both are much faster than your C runtime library. + * + * @api + */ + void gdispGDrawArc(GDisplay *g, coord_t x, coord_t y, coord_t radius, coord_t startangle, coord_t endangle, color_t color); + #define gdispDrawArc(x,y,r,s,e,c) gdispGDrawArc(GDISP,x,y,r,s,e,c) + + /* + * @brief Draw a filled arc. + * @pre GDISP_NEED_ARC must be TRUE in your gfxconf.h + * + * @param[in] g The display to use + * @param[in] x0,y0 The center point + * @param[in] radius The radius of the arc + * @param[in] start The start angle (0 to 360) + * @param[in] end The end angle (0 to 360) + * @param[in] color The color of the arc + * + * @note If you are just doing 45 degree angles consider using @p gdispFillArcSectors() instead. + * @note This routine requires trig support. It can either come from your C runtime library + * cos() and sin() which requires floating point support (and is slow), or you can define GFX_USE_GMISC + * and either GMISC_NEED_FIXEDTRIG or GMISC_NEED_FASTTRIG. + * GMISC_NEED_FASTTRIG uses table based floating point trig operations. + * GMISC_NEED_FIXEDTRIG uses fixed point integer trig operations. + * Note accuracy on both the table based options are more than adequate for the one degree + * resolution provided by these arc routines. Both are much faster than your C runtime library. + * + * @api + */ + void gdispGFillArc(GDisplay *g, coord_t x, coord_t y, coord_t radius, coord_t startangle, coord_t endangle, color_t color); + #define gdispFillArc(x,y,r,s,e,c) gdispGFillArc(GDISP,x,y,r,s,e,c) +#endif + +/* Read a pixel Function */ + +#if GDISP_NEED_PIXELREAD || defined(__DOXYGEN__) + /** + * @brief Get the color of a pixel. + * @return The color of the pixel. + * @pre GDISP_NEED_PIXELREAD must be TRUE in your gfxconf.h + * + * @param[in] g The display to use + * @param[in] x,y The position of the pixel + * + * @api + */ + color_t gdispGGetPixelColor(GDisplay *g, coord_t x, coord_t y); + #define gdispGetPixelColor(x,y) gdispGGetPixelColor(GDISP,x,y) +#endif + +/* Scrolling Function - clears the area scrolled out */ + +#if GDISP_NEED_SCROLL || defined(__DOXYGEN__) + /** + * @brief Scroll vertically a section of the screen. + * @pre GDISP_NEED_SCROLL must be set to TRUE in gfxconf.h + * @note Optional. + * @note If lines is >= cy, it is equivelent to a area fill with bgcolor. + * + * @param[in] g The display to use + * @param[in] x, y The start of the area to be scrolled + * @param[in] cx, cy The size of the area to be scrolled + * @param[in] lines The number of lines to scroll (Can be positive or negative) + * @param[in] bgcolor The color to fill the newly exposed area. + * + * @api + */ + void gdispGVerticalScroll(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, int lines, color_t bgcolor); + #define gdispVerticalScroll(x,y,cx,cy,l,b) gdispGVerticalScroll(GDISP,x,y,cx,cy,l,b) +#endif + +/* Set driver specific control */ + +#if GDISP_NEED_CONTROL || defined(__DOXYGEN__) + /** + * @brief Control hardware specific parts of the display. eg powermodes, backlight etc + * @pre GDISP_NEED_CONTROL must be TRUE in your gfxconf.h + * @note Depending on the hardware implementation this function may not + * support some codes. They will be ignored. + * + * @param[in] g The display to use + * @param[in] what what you want to control + * @param[in] value The value to be assigned + * + * @api + */ + void gdispGControl(GDisplay *g, unsigned what, void *value); + #define gdispControl(w,v) gdispGControl(GDISP,w,v) +#endif + +/* Query driver specific data */ + +#if GDISP_NEED_QUERY || defined(__DOXYGEN__) + /** + * @brief Query a property of the display. + * @pre GDISP_NEED_QUERY must be TRUE in your gfxconf.h + * @note The result must be typecast to the correct type. + * @note An unsupported query will return (void *)-1. + * + * @param[in] g The display to use + * @param[in] what What to query + * + * @api + */ + void *gdispGQuery(GDisplay *g, unsigned what); + #define gdispQuery(w) gdispGQuery(GDISP,w) +#endif + +#if GDISP_NEED_CONVEX_POLYGON || defined(__DOXYGEN__) + /** + * @brief Draw an enclosed polygon (convex, non-convex or complex). + * @pre GDISP_NEED_CONVEX_POLYGON must be TRUE in your gfxconf.h + * + * @param[in] g The display to use + * @param[in] tx, ty Transform all points in pntarray by tx, ty + * @param[in] pntarray An array of points + * @param[in] cnt The number of points in the array + * @param[in] color The color to use + * + * @api + */ + void gdispGDrawPoly(GDisplay *g, coord_t tx, coord_t ty, const point *pntarray, unsigned cnt, color_t color); + #define gdispDrawPoly(x,y,p,i,c) gdispGDrawPoly(GDISP,x,y,p,i,c) + + /** + * @brief Fill a convex polygon + * @details Doesn't handle non-convex or complex polygons. + * @pre GDISP_NEED_CONVEX_POLYGON must be TRUE in your gfxconf.h + * + * @param[in] g The display to use + * @param[in] tx, ty Transform all points in pntarray by tx, ty + * @param[in] pntarray An array of points + * @param[in] cnt The number of points in the array + * @param[in] color The color to use + * + * @note Convex polygons are those that have no internal angles. That is; + * you can draw a line from any point on the polygon to any other point + * on the polygon without it going outside the polygon. In our case we generalise + * this a little by saying that an infinite horizontal line (at any y value) will cross + * no more than two edges on the polygon. Some non-convex polygons do fit this criteria + * and can therefore be drawn. + * @note This routine is designed to be very efficient with even simple display hardware. + * + * @api + */ + void gdispGFillConvexPoly(GDisplay *g, coord_t tx, coord_t ty, const point *pntarray, unsigned cnt, color_t color); + #define gdispFillConvexPoly(x,y,p,i,c) gdispGFillConvexPoly(GDISP,x,y,p,i,c) + + /** + * @brief Draw a line with a specified thickness + * @details The line thickness is specified in pixels. The line ends can + * be selected to be either flat or round. + * @pre GDISP_NEED_CONVEX_POLYGON must be TRUE in your gfxconf.h + * @note Uses gdispGFillConvexPoly() internally to perform the drawing. + * + * @param[in] g The display to use + * @param[in] x0,y0 The start position + * @param[in] x1,y1 The end position + * @param[in] color The color to use + * @param[in] width The width of the line + * @param[in] round Use round ends for the line + * + * @api + */ + void gdispGDrawThickLine(GDisplay *g, coord_t x0, coord_t y0, coord_t x1, coord_t y1, color_t color, coord_t width, bool_t round); + #define gdispDrawThickLine(x0,y0,x1,y1,c,w,r) gdispGDrawThickLine(GDISP,x0,y0,x1,y1,c,w,r) +#endif + +/* Text Functions */ + +#if GDISP_NEED_TEXT || defined(__DOXYGEN__) + /** + * @brief Draw a text character. + * @pre GDISP_NEED_TEXT must be TRUE in your gfxconf.h + * + * @param[in] g The display to use + * @param[in] x,y The position for the text + * @param[in] c The character to draw + * @param[in] font The font to use + * @param[in] color The color to use + * + * @api + */ + void gdispGDrawChar(GDisplay *g, coord_t x, coord_t y, uint16_t c, font_t font, color_t color); + #define gdispDrawChar(x,y,s,f,c) gdispGDrawChar(GDISP,x,y,s,f,c) + + /** + * @brief Draw a text character with a filled background. + * @pre GDISP_NEED_TEXT must be TRUE in your gfxconf.h + * + * @param[in] g The display to use + * @param[in] x,y The position for the text + * @param[in] c The character to draw + * @param[in] font The font to use + * @param[in] color The color to use + * @param[in] bgcolor The background color to use + * + * @api + */ + void gdispGFillChar(GDisplay *g, coord_t x, coord_t y, uint16_t c, font_t font, color_t color, color_t bgcolor); + #define gdispFillChar(x,y,s,f,c,b) gdispGFillChar(GDISP,x,y,s,f,c,b) + + /** + * @brief Draw a text string. + * @pre GDISP_NEED_TEXT must be TRUE in your gfxconf.h + * + * @param[in] g The display to use + * @param[in] x,y The position for the text + * @param[in] str The string to draw + * @param[in] font The font to use + * @param[in] color The color to use + * + * @api + */ + void gdispGDrawString(GDisplay *g, coord_t x, coord_t y, const char *str, font_t font, color_t color); + #define gdispDrawString(x,y,s,f,c) gdispGDrawString(GDISP,x,y,s,f,c) + + /** + * @brief Draw a text string. + * @pre GDISP_NEED_TEXT must be TRUE in your gfxconf.h + * + * @param[in] g The display to use + * @param[in] x,y The position for the text + * @param[in] str The string to draw + * @param[in] font The font to use + * @param[in] color The color to use + * @param[in] bgcolor The background color to use + * + * @api + */ + void gdispGFillString(GDisplay *g, coord_t x, coord_t y, const char *str, font_t font, color_t color, color_t bgcolor); + #define gdispFillString(x,y,s,f,c,b) gdispGFillString(GDISP,x,y,s,f,c,b) + + /** + * @brief Draw a text string vertically centered within the specified box. + * @pre GDISP_NEED_TEXT must be TRUE in your gfxconf.h + * + * @param[in] g The display to use + * @param[in] x,y The position for the text (need to define top-right or base-line - check code) + * @param[in] cx,cy The width and height of the box + * @param[in] str The string to draw + * @param[in] font The font to use + * @param[in] color The color to use + * @param[in] justify Justify the text left, center or right within the box + * + * @api + */ + void gdispGDrawStringBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, const char* str, font_t font, color_t color, justify_t justify); + #define gdispDrawStringBox(x,y,cx,cy,s,f,c,j) gdispGDrawStringBox(GDISP,x,y,cx,cy,s,f,c,j) + + /** + * @brief Draw a text string vertically centered within the specified box. The box background is filled with the specified background color. + * @pre GDISP_NEED_TEXT must be TRUE in your gfxconf.h + * @note The entire box is filled + * + * @param[in] g The display to use + * @param[in] x,y The position for the text (need to define top-right or base-line - check code) + * @param[in] cx,cy The width and height of the box + * @param[in] str The string to draw + * @param[in] font The font to use + * @param[in] color The color to use + * @param[in] bgColor The background color to use + * @param[in] justify Justify the text left, center or right within the box + * + * @api + */ + void gdispGFillStringBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, const char* str, font_t font, color_t color, color_t bgColor, justify_t justify); + #define gdispFillStringBox(x,y,cx,cy,s,f,c,b,j) gdispGFillStringBox(GDISP,x,y,cx,cy,s,f,c,b,j) + + /** + * @brief Get a metric of a font. + * @return The metric requested in pixels. + * @pre GDISP_NEED_TEXT must be TRUE in your gfxconf.h + * + * @param[in] font The font to test + * @param[in] metric The metric to measure + * + * @api + */ + coord_t gdispGetFontMetric(font_t font, fontmetric_t metric); + + /** + * @brief Get the pixel width of a character. + * @return The width of the character in pixels. Does not include any between character padding. + * @pre GDISP_NEED_TEXT must be TRUE in your gfxconf.h + * + * @param[in] c The character to draw + * @param[in] font The font to use + * + * @api + */ + coord_t gdispGetCharWidth(char c, font_t font); + + /** + * @brief Get the pixel width of a string. + * @return The width of the string in pixels. + * @pre GDISP_NEED_TEXT must be TRUE in your gfxconf.h + * + * @param[in] str The string to measure + * @param[in] font The font to use + * + * @api + */ + coord_t gdispGetStringWidth(const char* str, font_t font); + + /** + * @brief Find a font and return it. + * @details The supplied name is matched against the font name. A '*' will replace 0 or more characters. + * @return Returns a font or NULL if no matching font could be found. + * @pre GDISP_NEED_TEXT must be TRUE in your gfxconf.h + * + * @param[in] name The font name to find. + * + * @note Wildcard matching will match the shortest possible match. + * + * @api + */ + font_t gdispOpenFont(const char *name); + + /** + * @brief Release a font after use. + * @pre GDISP_NEED_TEXT must be TRUE in your gfxconf.h + * + * @param[in] font The font to release. + * + * @api + */ + void gdispCloseFont(font_t font); + + /** + * @brief Make a scaled copy of an existing font. + * @details Allocates memory for new font metadata using gfxAlloc, remember to close font after use! + * @return A new font or NULL if out of memory. + * @pre GDISP_NEED_TEXT must be TRUE in your gfxconf.h + * + * @param[in] font The base font to use. + * @param[in] scale_x The scale factor in horizontal direction. + * @param[in] scale_y The scale factor in vertical direction. + */ + font_t gdispScaleFont(font_t font, uint8_t scale_x, uint8_t scale_y); + + /** + * @brief Get the name of the specified font. + * @returns The name of the font. + * @pre GDISP_NEED_TEXT must be TRUE in your gfxconf.h + * + * @param[in] font The font to get the name for. + * + * @api + */ + const char *gdispGetFontName(font_t font); +#endif + +/* Extra Arc Functions */ + +#if GDISP_NEED_ARC || GDISP_NEED_ARCSECTORS || defined(__DOXYGEN__) + /** + * @brief Draw a rectangular box with rounded corners + * @pre GDISP_NEED_ARC or GDISP_NEED_ARCSECTORS must be TRUE in your gfxconf.h + * + * @param[in] g The display to use + * @param[in] x,y The start position + * @param[in] cx,cy The size of the box (outside dimensions) + * @param[in] radius The radius of the rounded corners + * @param[in] color The color to use + * + * @api + */ + void gdispGDrawRoundedBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t radius, color_t color); + #define gdispDrawRoundedBox(x,y,cx,cy,r,c) gdispGDrawRoundedBox(GDISP,x,y,cx,cy,r,c) + + /** + * @brief Draw a filled rectangular box with rounded corners + * @pre GDISP_NEED_ARC or GDISP_NEED_ARCSECTORS must be TRUE in your gfxconf.h + * + * @param[in] g The display to use + * @param[in] x,y The start position + * @param[in] cx,cy The size of the box (outside dimensions) + * @param[in] radius The radius of the rounded corners + * @param[in] color The color to use + * + * @api + */ + void gdispGFillRoundedBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t radius, color_t color); + #define gdispFillRoundedBox(x,y,cx,cy,r,c) gdispGFillRoundedBox(GDISP,x,y,cx,cy,r,c) +#endif + +/* + * Macro definitions + */ + +/* Now obsolete functions */ +#define gdispBlitArea(x, y, cx, cy, buffer) gdispGBlitArea(GDISP, x, y, cx, cy, 0, 0, cx, buffer) + +/* Macro definitions for common gets and sets */ + +/** + * @brief Set the display power mode. + * @note Ignored if not supported by the display. + * + * @param[in] g The display to use + * @param[in] powerMode The new power mode + * + * @api + */ +#define gdispGSetPowerMode(g, powerMode) gdispGControl((g), GDISP_CONTROL_POWER, (void *)(unsigned)(powerMode)) +#define gdispSetPowerMode(powerMode) gdispGControl(GDISP, GDISP_CONTROL_POWER, (void *)(unsigned)(powerMode)) + +/** + * @brief Set the display orientation. + * @note Ignored if not supported by the display. + * + * @param[in] g The display to use + * @param[in] newOrientation The new orientation + * + * @api + */ +#define gdispGSetOrientation(g, newOrientation) gdispGControl((g), GDISP_CONTROL_ORIENTATION, (void *)(unsigned)(newOrientation)) +#define gdispSetOrientation(newOrientation) gdispGControl(GDISP, GDISP_CONTROL_ORIENTATION, (void *)(unsigned)(newOrientation)) + +/** + * @brief Set the display backlight. + * @note Ignored if not supported by the display. + * + * @param[in] g The display to use + * @param[in] percent The new brightness (0 - 100%) + * + * @note For displays that only support backlight off and on, + * 0 = off, anything else = on + * + * @api + */ +#define gdispGSetBacklight(g, percent) gdispGControl((g), GDISP_CONTROL_BACKLIGHT, (void *)(unsigned)(percent)) +#define gdispSetBacklight(percent) gdispGControl(GDISP, GDISP_CONTROL_BACKLIGHT, (void *)(unsigned)(percent)) + +/** + * @brief Set the display contrast. + * @note Ignored if not supported by the display. + * + * @param[in] g The display to use + * @param[in] percent The new contrast (0 - 100%) + * + * @api + */ +#define gdispGSetContrast(g, percent) gdispGControl((g), GDISP_CONTROL_CONTRAST, (void *)(unsigned)(percent)) +#define gdispSetContrast(percent) gdispGControl(GDISP, GDISP_CONTROL_CONTRAST, (void *)(unsigned)(percent)) + +/* More interesting macros */ + +/** + * @brief Reset the clip area to the full screen + * + * @param[in] g The display to use + * + * @api + */ +#define gdispGUnsetClip(g) gdispGSetClip((g),0,0,gdispGGetWidth(g),gdispGGetHeight(g)) +#define gdispUnsetClip() gdispGUnsetClip(GDISP) + +#ifdef __cplusplus +} +#endif + +#if GDISP_NEED_IMAGE || defined(__DOXYGEN__) + #include "gdisp_image.h" +#endif +#if GDISP_NEED_PIXMAP || defined(__DOXYGEN__) + #include "gdisp_pixmap.h" +#endif + + +#endif /* GFX_USE_GDISP */ + +#endif /* _GDISP_H */ +/** @} */ diff --git a/src/gdisp/gdisp.mk b/src/gdisp/gdisp.mk new file mode 100644 index 00000000..3c8af80a --- /dev/null +++ b/src/gdisp/gdisp.mk @@ -0,0 +1,14 @@ +GFXSRC += $(GFXLIB)/src/gdisp/gdisp.c \ + $(GFXLIB)/src/gdisp/gdisp_fonts.c \ + $(GFXLIB)/src/gdisp/gdisp_pixmap.c \ + $(GFXLIB)/src/gdisp/gdisp_image.c \ + $(GFXLIB)/src/gdisp/gdisp_image_native.c \ + $(GFXLIB)/src/gdisp/gdisp_image_gif.c \ + $(GFXLIB)/src/gdisp/gdisp_image_bmp.c \ + $(GFXLIB)/src/gdisp/gdisp_image_jpg.c \ + $(GFXLIB)/src/gdisp/gdisp_image_png.c + +MFDIR = $(GFXLIB)/src/gdisp/mcufont +include $(GFXLIB)/src/gdisp/mcufont/mcufont.mk +GFXINC += $(MFDIR) +GFXSRC += $(MFSRC) diff --git a/src/gdisp/gdisp_driver.h b/src/gdisp/gdisp_driver.h new file mode 100644 index 00000000..085e248f --- /dev/null +++ b/src/gdisp/gdisp_driver.h @@ -0,0 +1,1070 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gdisp/gdisp_driver.h + * @brief GDISP Graphic Driver subsystem low level driver header. + * + * @addtogroup GDISP + * @{ + */ + +#ifndef _GDISP_LLD_H +#define _GDISP_LLD_H + +#if GFX_USE_GDISP + +// Include the GDRIVER infrastructure +#include "src/gdriver/gdriver.h" + +// Are we currently compiling the driver itself? +#if defined(GDISP_DRIVER_VMT) + #define IN_DRIVER TRUE +#else + #define IN_DRIVER FALSE +#endif + +// Is this a multiple driver situation? +#if defined(GDISP_DRIVER_LIST) + #define IS_MULTIPLE TRUE +#else + #define IS_MULTIPLE FALSE +#endif + +// Do we need to use VMT calling rather than direct calls to the driver? +#if IS_MULTIPLE || GDISP_NEED_PIXMAP + #define USE_VMT TRUE +#else + #define USE_VMT FALSE +#endif + +// Are we in the pixmap virtual driver +#ifndef IN_PIXMAP_DRIVER + #define IN_PIXMAP_DRIVER FALSE +#endif + +//------------------------------------------------------------------------------------------------------------ + +// Our special auto-detect hardware code which uses the VMT. +#define HARDWARE_AUTODETECT 2 + +#if USE_VMT && !IN_DRIVER + // Multiple controllers the default is to hardware detect + #define HARDWARE_DEFAULT HARDWARE_AUTODETECT +#else + // The default is not to include code functions that aren't needed + #define HARDWARE_DEFAULT FALSE +#endif + +//------------------------------------------------------------------------------------------------------------ + +/** + * @name GDISP hardware accelerated support + * @{ + */ + /** + * @brief The display hardware can benefit from being de-initialized when usage is complete. + * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT + * + * @note HARDWARE_AUTODETECT is only meaningful when GDISP_DRIVER_LIST is defined + * @note This is most useful for displays such as remote network displays. + */ + #ifndef GDISP_HARDWARE_DEINIT + #define GDISP_HARDWARE_DEINIT HARDWARE_DEFAULT + #endif + + /** + * @brief The display hardware can benefit from being flushed. + * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT + * + * @note HARDWARE_AUTODETECT is only meaningful when GDISP_DRIVER_LIST is defined + * @note Some controllers ** require ** the application to flush + */ + #ifndef GDISP_HARDWARE_FLUSH + #define GDISP_HARDWARE_FLUSH HARDWARE_DEFAULT + #endif + + /** + * @brief Hardware streaming writing is supported. + * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT + * + * @note HARDWARE_AUTODETECT is only meaningful when GDISP_DRIVER_LIST is defined + * @note Either GDISP_HARDWARE_STREAM_WRITE or GDISP_HARDWARE_DRAWPIXEL must be provided by each driver + */ + #ifndef GDISP_HARDWARE_STREAM_WRITE + #define GDISP_HARDWARE_STREAM_WRITE HARDWARE_DEFAULT + #endif + + /** + * @brief Hardware streaming reading of the display surface is supported. + * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT + * + * @note HARDWARE_AUTODETECT is only meaningful when GDISP_DRIVER_LIST is defined + * + */ + #ifndef GDISP_HARDWARE_STREAM_READ + #define GDISP_HARDWARE_STREAM_READ HARDWARE_DEFAULT + #endif + + /** + * @brief Hardware supports setting the cursor position within the stream window. + * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT + * + * @note HARDWARE_AUTODETECT is only meaningful when GDISP_DRIVER_LIST is defined + * @note This is used to optimise setting of individual pixels within a stream window. + * It should therefore not be implemented unless it is cheaper than just setting + * a new window. + */ + #ifndef GDISP_HARDWARE_STREAM_POS + #define GDISP_HARDWARE_STREAM_POS HARDWARE_DEFAULT + #endif + + /** + * @brief Hardware accelerated draw pixel. + * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT + * + * @note HARDWARE_AUTODETECT is only meaningful when GDISP_DRIVER_LIST is defined + * @note Either GDISP_HARDWARE_STREAM_WRITE or GDISP_HARDWARE_DRAWPIXEL must be provided by the driver + */ + #ifndef GDISP_HARDWARE_DRAWPIXEL + #define GDISP_HARDWARE_DRAWPIXEL HARDWARE_DEFAULT + #endif + + /** + * @brief Hardware accelerated screen clears. + * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT + * + * @note HARDWARE_AUTODETECT is only meaningful when GDISP_DRIVER_LIST is defined + * @note This clears the entire display surface regardless of the clipping area currently set + */ + #ifndef GDISP_HARDWARE_CLEARS + #define GDISP_HARDWARE_CLEARS HARDWARE_DEFAULT + #endif + + /** + * @brief Hardware accelerated rectangular fills. + * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT + * + * @note HARDWARE_AUTODETECT is only meaningful when GDISP_DRIVER_LIST is defined + */ + #ifndef GDISP_HARDWARE_FILLS + #define GDISP_HARDWARE_FILLS HARDWARE_DEFAULT + #endif + + /** + * @brief Hardware accelerated fills from an image. + * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT + * + * @note HARDWARE_AUTODETECT is only meaningful when GDISP_DRIVER_LIST is defined + */ + #ifndef GDISP_HARDWARE_BITFILLS + #define GDISP_HARDWARE_BITFILLS HARDWARE_DEFAULT + #endif + + /** + * @brief Hardware accelerated scrolling. + * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT + * + * @note HARDWARE_AUTODETECT is only meaningful when GDISP_DRIVER_LIST is defined + */ + #ifndef GDISP_HARDWARE_SCROLL + #define GDISP_HARDWARE_SCROLL HARDWARE_DEFAULT + #endif + + /** + * @brief Reading back of pixel values. + * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT + * + * @note HARDWARE_AUTODETECT is only meaningful when GDISP_DRIVER_LIST is defined + */ + #ifndef GDISP_HARDWARE_PIXELREAD + #define GDISP_HARDWARE_PIXELREAD HARDWARE_DEFAULT + #endif + + /** + * @brief The driver supports one or more control commands. + * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT + * + * @note HARDWARE_AUTODETECT is only meaningful when GDISP_DRIVER_LIST is defined + */ + #ifndef GDISP_HARDWARE_CONTROL + #define GDISP_HARDWARE_CONTROL HARDWARE_DEFAULT + #endif + + /** + * @brief The driver supports a non-standard query. + * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT + * + * @note HARDWARE_AUTODETECT is only meaningful when GDISP_DRIVER_LIST is defined + */ + #ifndef GDISP_HARDWARE_QUERY + #define GDISP_HARDWARE_QUERY HARDWARE_DEFAULT + #endif + + /** + * @brief The driver supports a clipping in hardware. + * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT + * + * @note HARDWARE_AUTODETECT is only meaningful when GDISP_DRIVER_LIST is defined + * @note If this is defined the driver must perform its own clipping on all calls to + * the driver and respond appropriately if a parameter is outside the display area. + * @note If this is not defined then the software ensures that all calls to the + * driver do not exceed the display area (provided GDISP_NEED_CLIP or GDISP_NEED_VALIDATION + * has been set). + */ + #ifndef GDISP_HARDWARE_CLIP + #define GDISP_HARDWARE_CLIP HARDWARE_DEFAULT + #endif +/** @} */ + +//------------------------------------------------------------------------------------------------------------ + +// For pixmaps certain routines MUST not be FALSE as they are needed for pixmap drawing +// Similarly some routines MUST not be TRUE as pixmap's don't provide them. +#if GDISP_NEED_PIXMAP && !IN_DRIVER + #if !GDISP_HARDWARE_DEINIT + #undef GDISP_HARDWARE_DEINIT + #define GDISP_HARDWARE_DEINIT HARDWARE_AUTODETECT + #endif + #if !GDISP_HARDWARE_DRAWPIXEL + #undef GDISP_HARDWARE_DRAWPIXEL + #define GDISP_HARDWARE_DRAWPIXEL HARDWARE_AUTODETECT + #endif + #if !GDISP_HARDWARE_PIXELREAD + #undef GDISP_HARDWARE_PIXELREAD + #define GDISP_HARDWARE_PIXELREAD HARDWARE_AUTODETECT + #endif + #if !GDISP_HARDWARE_CONTROL + #undef GDISP_HARDWARE_CONTROL + #define GDISP_HARDWARE_CONTROL HARDWARE_AUTODETECT + #endif + #if GDISP_HARDWARE_FLUSH == TRUE + #undef GDISP_HARDWARE_FLUSH + #define GDISP_HARDWARE_FLUSH HARDWARE_AUTODETECT + #endif + #if GDISP_HARDWARE_STREAM_WRITE == TRUE + #undef GDISP_HARDWARE_STREAM_WRITE + #define GDISP_HARDWARE_STREAM_WRITE HARDWARE_AUTODETECT + #endif + #if GDISP_HARDWARE_STREAM_READ == TRUE + #undef GDISP_HARDWARE_STREAM_READ + #define GDISP_HARDWARE_STREAM_READ HARDWARE_AUTODETECT + #endif + #if GDISP_HARDWARE_CLEARS == TRUE + #undef GDISP_HARDWARE_CLEARS + #define GDISP_HARDWARE_CLEARS HARDWARE_AUTODETECT + #endif + #if GDISP_HARDWARE_FILLS == TRUE + #undef GDISP_HARDWARE_FILLS + #define GDISP_HARDWARE_FILLS HARDWARE_AUTODETECT + #endif + #if GDISP_HARDWARE_BITFILLS == TRUE + #undef GDISP_HARDWARE_BITFILLS + #define GDISP_HARDWARE_BITFILLS HARDWARE_AUTODETECT + #endif + #if GDISP_HARDWARE_SCROLL == TRUE + #undef GDISP_HARDWARE_SCROLL + #define GDISP_HARDWARE_SCROLL HARDWARE_AUTODETECT + #endif + #if GDISP_HARDWARE_QUERY == TRUE + #undef GDISP_HARDWARE_QUERY + #define GDISP_HARDWARE_QUERY HARDWARE_AUTODETECT + #endif + #if GDISP_HARDWARE_CLIP == TRUE + #undef GDISP_HARDWARE_CLIP + #define GDISP_HARDWARE_CLIP HARDWARE_AUTODETECT + #endif +#endif + +//------------------------------------------------------------------------------------------------------------ + +/* Verify information for packed pixels and define a non-packed pixel macro */ +#if !GDISP_PACKED_PIXELS + #define gdispPackPixels(buf,cx,x,y,c) { ((color_t *)(buf))[(y)*(cx)+(x)] = (c); } +#elif !GDISP_HARDWARE_BITFILLS + #error "GDISP: packed pixel formats are only supported for hardware accelerated drivers." +#elif GDISP_PIXELFORMAT != GDISP_PIXELFORMAT_RGB888 \ + && GDISP_PIXELFORMAT != GDISP_PIXELFORMAT_RGB444 \ + && GDISP_PIXELFORMAT != GDISP_PIXELFORMAT_RGB666 \ + && GDISP_PIXELFORMAT != GDISP_PIXELFORMAT_CUSTOM + #error "GDISP: A packed pixel format has been specified for an unsupported pixel format." +#endif + +/* Support routine for packed pixel formats */ +#if !defined(gdispPackPixels) || defined(__DOXYGEN__) + /** + * @brief Pack a pixel into a pixel buffer. + * @note This function performs no buffer boundary checking + * regardless of whether GDISP_NEED_CLIP has been specified. + * + * @param[in] buf The buffer to put the pixel in + * @param[in] cx The width of a pixel line + * @param[in] x, y The location of the pixel to place + * @param[in] color The color to put into the buffer + * + * @api + */ + void gdispPackPixels(const pixel_t *buf, coord_t cx, coord_t x, coord_t y, color_t color); +#endif + +//------------------------------------------------------------------------------------------------------------ + +struct GDisplay { + struct GDriver d; // This must be the first element + #define gvmt(g) ((const GDISPVMT const *)((g)->d.vmt)) // For ease of access to the vmt member + + struct GDISPControl { + coord_t Width; + coord_t Height; + orientation_t Orientation; + powermode_t Powermode; + uint8_t Backlight; + uint8_t Contrast; + } g; + + void * priv; // A private area just for the drivers use. + void * board; // A private area just for the board interfaces use. + + uint8_t systemdisplay; + uint8_t controllerdisplay; + uint16_t flags; + #define GDISP_FLG_INSTREAM 0x0001 // We are in a user based stream operation + #define GDISP_FLG_SCRSTREAM 0x0002 // The stream area currently covers the whole screen + #define GDISP_FLG_DRIVER 0x0004 // This flags and above are for use by the driver + + // Multithread Mutex + #if GDISP_NEED_MULTITHREAD + gfxMutex mutex; + #endif + + // Software clipping + #if GDISP_HARDWARE_CLIP != TRUE && (GDISP_NEED_CLIP || GDISP_NEED_VALIDATION) + coord_t clipx0, clipy0; + coord_t clipx1, clipy1; /* not inclusive */ + #endif + + // Driver call parameters + struct { + coord_t x, y; + coord_t cx, cy; + coord_t x1, y1; + coord_t x2, y2; + color_t color; + void *ptr; + } p; + + // In call working buffers + + #if GDISP_NEED_TEXT + // Text rendering parameters + struct { + font_t font; + color_t color; + color_t bgcolor; + coord_t clipx0, clipy0; + coord_t clipx1, clipy1; + } t; + #endif + #if GDISP_LINEBUF_SIZE != 0 && ((GDISP_NEED_SCROLL && !GDISP_HARDWARE_SCROLL) || (!GDISP_HARDWARE_STREAM_WRITE && GDISP_HARDWARE_BITFILLS)) + // A pixel line buffer + color_t linebuf[GDISP_LINEBUF_SIZE]; + #endif +}; + +typedef struct GDISPVMT { + GDriverVMT d; + #define GDISP_VFLG_DYNAMICONLY 0x0001 // This display should never be statically initialised + #define GDISP_VFLG_PIXMAP 0x0002 // This is a pixmap display + bool_t (*init)(GDisplay *g); + void (*deinit)(GDisplay *g); + void (*writestart)(GDisplay *g); // Uses p.x,p.y p.cx,p.cy + void (*writepos)(GDisplay *g); // Uses p.x,p.y + void (*writecolor)(GDisplay *g); // Uses p.color + void (*writestop)(GDisplay *g); // Uses no parameters + void (*readstart)(GDisplay *g); // Uses p.x,p.y p.cx,p.cy + color_t (*readcolor)(GDisplay *g); // Uses no parameters + void (*readstop)(GDisplay *g); // Uses no parameters + void (*pixel)(GDisplay *g); // Uses p.x,p.y p.color + void (*clear)(GDisplay *g); // Uses p.color + void (*fill)(GDisplay *g); // Uses p.x,p.y p.cx,p.cy p.color + void (*blit)(GDisplay *g); // Uses p.x,p.y p.cx,p.cy p.x1,p.y1 (=srcx,srcy) p.x2 (=srccx), p.ptr (=buffer) + color_t (*get)(GDisplay *g); // Uses p.x,p.y + void (*vscroll)(GDisplay *g); // Uses p.x,p.y p.cx,p.cy, p.y1 (=lines) p.color + void (*control)(GDisplay *g); // Uses p.x (=what) p.ptr (=value) + void *(*query)(GDisplay *g); // Uses p.x (=what); + void (*setclip)(GDisplay *g); // Uses p.x,p.y p.cx,p.cy + void (*flush)(GDisplay *g); // Uses no parameters +} GDISPVMT; + +//------------------------------------------------------------------------------------------------------------ + +// Do we need function definitions or macro's (via the VMT) +#if IN_DRIVER || !USE_VMT || defined(__DOXYGEN__) + #ifdef __cplusplus + extern "C" { + #endif + + // Should the driver routines should be static or not + #if USE_VMT + #define LLDSPEC static + #else + #define LLDSPEC + #endif + + /** + * @brief Initialize the driver. + * @return TRUE if successful. + * @param[in] g The driver structure + * @param[out] g->g The driver must fill in the GDISPControl structure + */ + LLDSPEC bool_t gdisp_lld_init(GDisplay *g); + + #if GDISP_HARDWARE_DEINIT || defined(__DOXYGEN__) + /** + * @brief The driver is being de-initialized + * @pre GDISP_HARDWARE_FLUSH is TRUE + * + * @param[in] g The driver structure + * + */ + LLDSPEC void gdisp_lld_deinit(GDisplay *g); + #endif + + #if GDISP_HARDWARE_FLUSH || defined(__DOXYGEN__) + /** + * @brief Flush the current drawing operations to the display + * @pre GDISP_HARDWARE_FLUSH is TRUE + * + * @param[in] g The driver structure + * + * @note The parameter variables must not be altered by the driver. + */ + LLDSPEC void gdisp_lld_flush(GDisplay *g); + #endif + + #if GDISP_HARDWARE_STREAM_WRITE || defined(__DOXYGEN__) + /** + * @brief Start a streamed write operation + * @pre GDISP_HARDWARE_STREAM_WRITE is TRUE + * + * @param[in] g The driver structure + * + * @note g->p.x,g->p.y The window position + * @note g->p.cx,g->p.cy The window size + * + * @note The parameter variables must not be altered by the driver. + * @note Streaming operations that wrap the defined window have + * undefined results. + * @note This must be followed by a call to @p gdisp_lld_write_pos() if GDISP_HARDWARE_STREAM_POS is TRUE. + */ + LLDSPEC void gdisp_lld_write_start(GDisplay *g); + + /** + * @brief Send a pixel to the current streaming position and then increment that position + * @pre GDISP_HARDWARE_STREAM_WRITE is TRUE + * + * @param[in] g The driver structure + * + * @note g->p.color The color to display at the curent position + * @note The parameter variables must not be altered by the driver. + */ + LLDSPEC void gdisp_lld_write_color(GDisplay *g); + + /** + * @brief End the current streaming write operation + * @pre GDISP_HARDWARE_STREAM_WRITE is TRUE + * + * @param[in] g The driver structure + * + * @note The parameter variables must not be altered by the driver. + */ + LLDSPEC void gdisp_lld_write_stop(GDisplay *g); + + #if GDISP_HARDWARE_STREAM_POS || defined(__DOXYGEN__) + /** + * @brief Change the current position within the current streaming window + * @pre GDISP_HARDWARE_STREAM_POS is TRUE and GDISP_HARDWARE_STREAM_WRITE is TRUE + * + * @param[in] g The driver structure + * @param[in] g->p.x,g->p.y The new position (which will always be within the existing stream window) + * + * @note The parameter variables must not be altered by the driver. + */ + LLDSPEC void gdisp_lld_write_pos(GDisplay *g); + #endif + #endif + + #if GDISP_HARDWARE_STREAM_READ || defined(__DOXYGEN__) + /** + * @brief Start a streamed read operation + * @pre GDISP_HARDWARE_STREAM_READ is TRUE + * + * @param[in] g The driver structure + * @param[in] g->p.x,g->p.y The window position + * @param[in] g->p.cx,g->p.cy The window size + * + * @note The parameter variables must not be altered by the driver. + * @note Streaming operations that wrap the defined window have + * undefined results. + */ + LLDSPEC void gdisp_lld_read_start(GDisplay *g); + + /** + * @brief Read a pixel from the current streaming position and then increment that position + * @return The color at the current position + * @pre GDISP_HARDWARE_STREAM_READ is TRUE + * + * @param[in] g The driver structure + * + * @note The parameter variables must not be altered by the driver. + */ + LLDSPEC color_t gdisp_lld_read_color(GDisplay *g); + + /** + * @brief End the current streaming operation + * @pre GDISP_HARDWARE_STREAM_READ is TRUE + * + * @param[in] g The driver structure + * + * @note The parameter variables must not be altered by the driver. + */ + LLDSPEC void gdisp_lld_read_stop(GDisplay *g); + #endif + + #if GDISP_HARDWARE_DRAWPIXEL || defined(__DOXYGEN__) + /** + * @brief Draw a pixel + * @pre GDISP_HARDWARE_DRAWPIXEL is TRUE + * + * @param[in] g The driver structure + * @param[in] g->p.x,g->p.y The pixel position + * @param[in] g->p.color The color to set + * + * @note The parameter variables must not be altered by the driver. + */ + LLDSPEC void gdisp_lld_draw_pixel(GDisplay *g); + #endif + + #if GDISP_HARDWARE_CLEARS || defined(__DOXYGEN__) + /** + * @brief Clear the screen using the defined color + * @pre GDISP_HARDWARE_CLEARS is TRUE + * + * @param[in] g The driver structure + * @param[in] g->p.color The color to set + * + * @note The parameter variables must not be altered by the driver. + */ + LLDSPEC void gdisp_lld_clear(GDisplay *g); + #endif + + #if GDISP_HARDWARE_FILLS || defined(__DOXYGEN__) + /** + * @brief Fill an area with a single color + * @pre GDISP_HARDWARE_FILLS is TRUE + * + * @param[in] g The driver structure + * @param[in] g->p.x,g->p.y The area position + * @param[in] g->p.cx,g->p.cy The area size + * @param[in] g->p.color The color to set + * + * @note The parameter variables must not be altered by the driver. + */ + LLDSPEC void gdisp_lld_fill_area(GDisplay *g); + #endif + + #if GDISP_HARDWARE_BITFILLS || defined(__DOXYGEN__) + /** + * @brief Fill an area using a bitmap + * @pre GDISP_HARDWARE_BITFILLS is TRUE + * + * @param[in] g The driver structure + * @param[in] g->p.x,g->p.y The area position + * @param[in] g->p.cx,g->p.cy The area size + * @param[in] g->p.x1,g->p.y1 The starting position in the bitmap + * @param[in] g->p.x2 The width of a bitmap line + * @param[in] g->p.ptr The pointer to the bitmap + * + * @note The parameter variables must not be altered by the driver. + */ + LLDSPEC void gdisp_lld_blit_area(GDisplay *g); + #endif + + #if GDISP_HARDWARE_PIXELREAD || defined(__DOXYGEN__) + /** + * @brief Read a pixel from the display + * @return The color at the defined position + * @pre GDISP_HARDWARE_PIXELREAD is TRUE (and the application needs it) + * + * @param[in] g The driver structure + * @param[in] g->p.x,g->p.y The pixel position + * + * @note The parameter variables must not be altered by the driver. + */ + LLDSPEC color_t gdisp_lld_get_pixel_color(GDisplay *g); + #endif + + #if (GDISP_HARDWARE_SCROLL && GDISP_NEED_SCROLL) || defined(__DOXYGEN__) + /** + * @brief Scroll an area of the screen + * @pre GDISP_HARDWARE_SCROLL is TRUE (and the application needs it) + * + * @param[in] g The driver structure + * @param[in] g->p.x,g->p.y The area position + * @param[in] g->p.cx,g->p.cy The area size + * @param[in] g->p.y1 The number of lines to scroll (positive or negative) + * + * @note The parameter variables must not be altered by the driver. + * @note This can be easily implemented if the hardware supports + * display area to display area copying. + * @note Clearing the exposed area on the scroll operation is not + * needed as the high level code handles this. + */ + LLDSPEC void gdisp_lld_vertical_scroll(GDisplay *g); + #endif + + #if (GDISP_HARDWARE_CONTROL && GDISP_NEED_CONTROL) || defined(__DOXYGEN__) + /** + * @brief Control some feature of the hardware + * @pre GDISP_HARDWARE_CONTROL is TRUE (and the application needs it) + * + * @param[in] g The driver structure + * @param[in] g->p.x The operation to perform + * @param[in] g->p.ptr The operation parameter + * + * @note The parameter variables must not be altered by the driver. + */ + LLDSPEC void gdisp_lld_control(GDisplay *g); + #endif + + #if (GDISP_HARDWARE_QUERY && GDISP_NEED_QUERY) || defined(__DOXYGEN__) + /** + * @brief Query some feature of the hardware + * @return The information requested (typecast as void *) + * @pre GDISP_HARDWARE_QUERY is TRUE (and the application needs it) + * + * @param[in] g The driver structure + * @param[in] g->p.x What to query + * + * @note The parameter variables must not be altered by the driver. + */ + LLDSPEC void *gdisp_lld_query(GDisplay *g); // Uses p.x (=what); + #endif + + #if (GDISP_HARDWARE_CLIP && (GDISP_NEED_CLIP || GDISP_NEED_VALIDATION)) || defined(__DOXYGEN__) + /** + * @brief Set the hardware clipping area + * @pre GDISP_HARDWARE_CLIP is TRUE (and the application needs it) + * + * @param[in] g The driver structure + * @param[in] g->p.x,g->p.y The area position + * @param[in] g->p.cx,g->p.cy The area size + * + * @note The parameter variables must not be altered by the driver. + */ + LLDSPEC void gdisp_lld_set_clip(GDisplay *g); + #endif + + #ifdef __cplusplus + } + #endif + +#else + #define gdisp_lld_init(g) gvmt(g)->init(g) + #define gdisp_lld_deinit(g) gvmt(g)->deinit(g) + #define gdisp_lld_flush(g) gvmt(g)->flush(g) + #define gdisp_lld_write_start(g) gvmt(g)->writestart(g) + #define gdisp_lld_write_pos(g) gvmt(g)->writepos(g) + #define gdisp_lld_write_color(g) gvmt(g)->writecolor(g) + #define gdisp_lld_write_stop(g) gvmt(g)->writestop(g) + #define gdisp_lld_read_start(g) gvmt(g)->readstart(g) + #define gdisp_lld_read_color(g) gvmt(g)->readcolor(g) + #define gdisp_lld_read_stop(g) gvmt(g)->readstop(g) + #define gdisp_lld_draw_pixel(g) gvmt(g)->pixel(g) + #define gdisp_lld_clear(g) gvmt(g)->clear(g) + #define gdisp_lld_fill_area(g) gvmt(g)->fill(g) + #define gdisp_lld_blit_area(g) gvmt(g)->blit(g) + #define gdisp_lld_get_pixel_color(g) gvmt(g)->get(g) + #define gdisp_lld_vertical_scroll(g) gvmt(g)->vscroll(g) + #define gdisp_lld_control(g) gvmt(g)->control(g) + #define gdisp_lld_query(g) gvmt(g)->query(g) + #define gdisp_lld_set_clip(g) gvmt(g)->setclip(g) +#endif + +//------------------------------------------------------------------------------------------------------------ + +// If compiling the driver then build the VMT and set the low level driver color macros. +#if IN_DRIVER + + // Make sure the driver has a valid model + #if !GDISP_HARDWARE_STREAM_WRITE && !GDISP_HARDWARE_DRAWPIXEL + #error "GDISP Driver: Either GDISP_HARDWARE_STREAM_WRITE or GDISP_HARDWARE_DRAWPIXEL must be TRUE" + #endif + + // If we are not using multiple displays then hard-code the VMT name (except for the pixmap driver) + #if !IS_MULTIPLE && !IN_PIXMAP_DRIVER + #undef GDISP_DRIVER_VMT + #define GDISP_DRIVER_VMT GDISPVMT_OnlyOne + #endif + + // Default the flags if the driver doesn't specify any + #ifndef GDISP_DRIVER_VMT_FLAGS + #define GDISP_DRIVER_VMT_FLAGS 0 + #endif + + // Routines needed by the general driver VMT + #ifdef __cplusplus + extern "C" { + #endif + bool_t _gdispInitDriver(GDriver *g, void *param, unsigned driverinstance, unsigned systeminstance); + void _gdispPostInitDriver(GDriver *g); + void _gdispDeInitDriver(GDriver *g); + #ifdef __cplusplus + } + #endif + + // Build the VMT + const GDISPVMT const GDISP_DRIVER_VMT[1] = {{ + { GDRIVER_TYPE_DISPLAY, 0, sizeof(GDisplay), _gdispInitDriver, _gdispPostInitDriver, _gdispDeInitDriver }, + gdisp_lld_init, + #if GDISP_HARDWARE_DEINIT + gdisp_lld_deinit, + #else + 0, + #endif + #if GDISP_HARDWARE_STREAM_WRITE + gdisp_lld_write_start, + #if GDISP_HARDWARE_STREAM_POS + gdisp_lld_write_pos, + #else + 0, + #endif + gdisp_lld_write_color, + gdisp_lld_write_stop, + #else + 0, 0, 0, 0, + #endif + #if GDISP_HARDWARE_STREAM_READ + gdisp_lld_read_start, + gdisp_lld_read_color, + gdisp_lld_read_stop, + #else + 0, 0, 0, + #endif + #if GDISP_HARDWARE_DRAWPIXEL + gdisp_lld_draw_pixel, + #else + 0, + #endif + #if GDISP_HARDWARE_CLEARS + gdisp_lld_clear, + #else + 0, + #endif + #if GDISP_HARDWARE_FILLS + gdisp_lld_fill_area, + #else + 0, + #endif + #if GDISP_HARDWARE_BITFILLS + gdisp_lld_blit_area, + #else + 0, + #endif + #if GDISP_HARDWARE_PIXELREAD + gdisp_lld_get_pixel_color, + #else + 0, + #endif + #if GDISP_HARDWARE_SCROLL && GDISP_NEED_SCROLL + gdisp_lld_vertical_scroll, + #else + 0, + #endif + #if GDISP_HARDWARE_CONTROL && GDISP_NEED_CONTROL + gdisp_lld_control, + #else + 0, + #endif + #if GDISP_HARDWARE_QUERY && GDISP_NEED_QUERY + gdisp_lld_query, + #else + 0, + #endif + #if GDISP_HARDWARE_CLIP && (GDISP_NEED_CLIP || GDISP_NEED_VALIDATION) + gdisp_lld_set_clip, + #else + 0, + #endif + #if GDISP_HARDWARE_FLUSH + gdisp_lld_flush, + #else + 0, + #endif + }}; + + //-------------------------------------------------------------------------------------------------------- + + /* Low level driver pixel format information */ + //------------------------- + // True-Color color system + //------------------------- + #if GDISP_LLD_PIXELFORMAT & GDISP_COLORSYSTEM_TRUECOLOR + #define LLDCOLOR_SYSTEM GDISP_COLORSYSTEM_TRUECOLOR + + // Calculate the number of bits + #define LLDCOLOR_BITS_R ((GDISP_LLD_PIXELFORMAT>>8) & 0x0F) + #define LLDCOLOR_BITS_G ((GDISP_LLD_PIXELFORMAT>>4) & 0x0F) + #define LLDCOLOR_BITS_B ((GDISP_LLD_PIXELFORMAT>>0) & 0x0F) + #define LLDCOLOR_BITS (LLDCOLOR_BITS_R + LLDCOLOR_BITS_G + LLDCOLOR_BITS_B) + + // From the number of bits determine COLOR_TYPE, COLOR_TYPE_BITS and masking + #if LLDCOLOR_BITS <= 8 + #define LLDCOLOR_TYPE uint8_t + #define LLDCOLOR_TYPE_BITS 8 + #elif LLDCOLOR_BITS <= 16 + #define LLDCOLOR_TYPE uint16_t + #define LLDCOLOR_TYPE_BITS 16 + #elif LLDCOLOR_BITS <= 32 + #define LLDCOLOR_TYPE uint32_t + #define LLDCOLOR_TYPE_BITS 32 + #else + #error "GDISP: Cannot define low level driver color types with more than 32 bits" + #endif + #if LLDCOLOR_TYPE_BITS == LLDCOLOR_BITS + #define LLDCOLOR_NEEDS_MASK FALSE + #else + #define LLDCOLOR_NEEDS_MASK TRUE + #endif + #define LLDCOLOR_MASK() ((1 << LLDCOLOR_BITS)-1) + + // Calculate the component bit shifts + #if (GDISP_LLD_PIXELFORMAT & GDISP_COLORSYSTEM_MASK) == GDISP_COLORSYSTEM_RGB + #define LLDCOLOR_SHIFT_R (LLDCOLOR_BITS_B+LLDCOLOR_BITS_G) + #define LLDCOLOR_SHIFT_G LLDCOLOR_BITS_B + #define LLDCOLOR_SHIFT_B 0 + #else + #define LLDCOLOR_SHIFT_B (LLDCOLOR_BITS_R+LLDCOLOR_BITS_G) + #define LLDCOLOR_SHIFT_G LLDCOLOR_BITS_R + #define LLDCOLOR_SHIFT_R 0 + #endif + + // Calculate LLDRED_OF, LLDGREEN_OF, LLDBLUE_OF and LLDRGB2COLOR + #if LLDCOLOR_BITS_R + LLDCOLOR_SHIFT_R == 8 + #define LLDRED_OF(c) ((c) & (((1<<LLDCOLOR_BITS_R)-1) << LLDCOLOR_SHIFT_R)) + #define LLDRGB2COLOR_R(r) ((LLDCOLOR_TYPE)((r) & (0xFF & ~((1<<(8-LLDCOLOR_BITS_R))-1)))) + #elif LLDCOLOR_BITS_R + LLDCOLOR_SHIFT_R > 8 + #define LLDRED_OF(c) (((c) & (((1<<LLDCOLOR_BITS_R)-1) << LLDCOLOR_SHIFT_R)) >> (LLDCOLOR_BITS_R+LLDCOLOR_SHIFT_R-8)) + #define LLDRGB2COLOR_R(r) (((LLDCOLOR_TYPE)((r) & (0xFF & ~((1<<(8-LLDCOLOR_BITS_R))-1)))) << (LLDCOLOR_BITS_R+LLDCOLOR_SHIFT_R-8)) + #else // LLDCOLOR_BITS_R + LLDCOLOR_SHIFT_R < 8 + #define LLDRED_OF(c) (((c) & (((1<<LLDCOLOR_BITS_R)-1) << LLDCOLOR_SHIFT_R)) << (8-(LLDCOLOR_BITS_R+LLDCOLOR_SHIFT_R))) + #define LLDRGB2COLOR_R(r) (((LLDCOLOR_TYPE)((r) & (0xFF & ~((1<<(8-LLDCOLOR_BITS_R))-1)))) >> (8-(LLDCOLOR_BITS_R+LLDCOLOR_SHIFT_R))) + #endif + #if LLDCOLOR_BITS_G + LLDCOLOR_SHIFT_G == 8 + #define LLDGREEN_OF(c) ((c) & (((1<<LLDCOLOR_BITS_G)-1) << LLDCOLOR_SHIFT_G)) + #define LLDRGB2COLOR_G(g) ((LLDCOLOR_TYPE)((g) & (0xFF & ~((1<<(8-LLDCOLOR_BITS_G))-1)))) + #elif LLDCOLOR_BITS_G + LLDCOLOR_SHIFT_G > 8 + #define LLDGREEN_OF(c) (((c) & (((1<<LLDCOLOR_BITS_G)-1) << LLDCOLOR_SHIFT_G)) >> (LLDCOLOR_BITS_G+LLDCOLOR_SHIFT_G-8)) + #define LLDRGB2COLOR_G(g) (((LLDCOLOR_TYPE)((g) & (0xFF & ~((1<<(8-LLDCOLOR_BITS_G))-1)))) << (LLDCOLOR_BITS_G+LLDCOLOR_SHIFT_G-8)) + #else // LLDCOLOR_BITS_G + LLDCOLOR_SHIFT_G < 8 + #define LLDGREEN_OF(c) (((c) & (((1<<LLDCOLOR_BITS_G)-1) << LLDCOLOR_SHIFT_G)) << (8-(LLDCOLOR_BITS_G+LLDCOLOR_SHIFT_G))) + #define LLDRGB2COLOR_G(g) (((LLDCOLOR_TYPE)((g) & (0xFF & ~((1<<(8-LLDCOLOR_BITS_G))-1)))) >> (8-(LLDCOLOR_BITS_LLDG+COLOR_SHIFT_G))) + #endif + #if LLDCOLOR_BITS_B + LLDCOLOR_SHIFT_B == 8 + #define LLDBLUE_OF(c) ((c) & (((1<<LLDCOLOR_BITS_B)-1) << LLDCOLOR_SHIFT_B)) + #define LLDRGB2COLOR_B(b) ((LLDCOLOR_TYPE)((b) & (0xFF & ~((1<<(8-LLDCOLOR_BITS_B))-1)))) + #elif LLDCOLOR_BITS_B + LLDCOLOR_SHIFT_B > 8 + #define LLDBLUE_OF(c) (((c) & (((1<<LLDCOLOR_BITS_B)-1) << LLDCOLOR_SHIFT_B)) >> (LLDCOLOR_BITS_B+LLDCOLOR_SHIFT_B-8)) + #define LLDRGB2COLOR_B(b) (((LLDCOLOR_TYPE)((b) & (0xFF & ~((1<<(8-LLDCOLOR_BITS_B))-1)))) << (LLDCOLOR_BITS_B+LLDCOLOR_SHIFT_B-8)) + #else // LLDCOLOR_BITS_B + LLDCOLOR_SHIFT_B < 8 + #define LLDBLUE_OF(c) (((c) & (((1<<LLDCOLOR_BITS_B)-1) << LLDCOLOR_SHIFT_B)) << (8-(LLDCOLOR_BITS_B+LLDCOLOR_SHIFT_B))) + #define LLDRGB2COLOR_B(b) (((COLOR_TYPE)((b) & (0xFF & ~((1<<(8-LLDCOLOR_BITS_B))-1)))) >> (8-(LLDCOLOR_BITS_B+LLDCOLOR_SHIFT_B))) + #endif + #define LLDLUMA_OF(c) ((LLDRED_OF(c)+((uint16_t)LLDGREEN_OF(c)<<1)+LLDBLUE_OF(c))>>2) + #define LLDEXACT_RED_OF(c) (((uint16_t)(((c)>>LLDCOLOR_SHIFT_R)&((1<<LLDCOLOR_BITS_R)-1))*255)/((1<<LLDCOLOR_BITS_R)-1)) + #define LLDEXACT_GREEN_OF(c) (((uint16_t)(((c)>>LLDCOLOR_SHIFT_G)&((1<<LLDCOLOR_BITS_G)-1))*255)/((1<<LLDCOLOR_BITS_G)-1)) + #define LLDEXACT_BLUE_OF(c) (((uint16_t)(((c)>>LLDCOLOR_SHIFT_B)&((1<<LLDCOLOR_BITS_B)-1))*255)/((1<<LLDCOLOR_BITS_B)-1)) + #define LLDEXACT_LUMA_OF(c) ((LLDEXACT_RED_OF(c)+((uint16_t)LLDEXACT_GREEN_OF(c)<<1)+LLDEXACT_BLUE_OF(c))>>2) + #define LLDLUMA2COLOR(l) (LLDRGB2COLOR_R(l) | LLDRGB2COLOR_G(l) | LLDRGB2COLOR_B(l)) + #define LLDRGB2COLOR(r,g,b) (LLDRGB2COLOR_R(r) | LLDRGB2COLOR_G(g) | LLDRGB2COLOR_B(b)) + + // Calculate LLDHTML2COLOR + #if LLDCOLOR_BITS_R + LLDCOLOR_SHIFT_R == 24 + #define LLDHTML2COLOR_R(h) ((h) & ((0xFF & ~((1<<(8-LLDCOLOR_BITS_R))-1))<<16)) + #elif COLOR_BITS_R + COLOR_SHIFT_R > 24 + #define LLDHTML2COLOR_R(h) (((h) & ((0xFF & ~((1<<(8-LLDCOLOR_BITS_R))-1))<<16)) << (LLDCOLOR_BITS_R+LLDCOLOR_SHIFT_R-24)) + #else // COLOR_BITS_R + COLOR_SHIFT_R < 24 + #define LLDHTML2COLOR_R(h) (((h) & ((0xFF & ~((1<<(8-LLDCOLOR_BITS_R))-1))<<16)) >> (24-(LLDCOLOR_BITS_R+LLDCOLOR_SHIFT_R))) + #endif + #if LLDCOLOR_BITS_G + LLDCOLOR_SHIFT_G == 16 + #define LLDHTML2COLOR_G(h) ((h) & ((0xFF & ~((1<<(8-LLDCOLOR_BITS_G))-1))<<8)) + #elif LLDCOLOR_BITS_G + LLDCOLOR_SHIFT_G > 16 + #define LLDHTML2COLOR_G(h) (((h) & ((0xFF & ~((1<<(8-LLDCOLOR_BITS_G))-1))<<8)) << (LLDCOLOR_BITS_G+LLDCOLOR_SHIFT_G-16)) + #else // LLDCOLOR_BITS_G + LLDCOLOR_SHIFT_G < 16 + #define LLDHTML2COLOR_G(h) (((h) & ((0xFF & ~((1<<(8-LLDCOLOR_BITS_G))-1))<<8)) >> (16-(LLDCOLOR_BITS_G+LLDCOLOR_SHIFT_G))) + #endif + #if LLDCOLOR_BITS_B + LLDCOLOR_SHIFT_B == 8 + #define LLDHTML2COLOR_B(h) ((h) & (0xFF & ~((1<<(8-LLDCOLOR_BITS_B))-1))) + #elif LLDCOLOR_BITS_B + LLDCOLOR_SHIFT_B > 8 + #define LLDHTML2COLOR_B(h) (((h) & (0xFF & ~((1<<(8-LLDCOLOR_BITS_B))-1))) << (LLDCOLOR_BITS_B+LLDCOLOR_SHIFT_B-8)) + #else // LLDCOLOR_BITS_B + LLDCOLOR_SHIFT_B < 8 + #define LLDHTML2COLOR_B(h) (((h) & (0xFF & ~((1<<(8-LLDCOLOR_BITS_B))-1))) >> (8-(LLDCOLOR_BITS_B+LLDCOLOR_SHIFT_B))) + #endif + #define LLDHTML2COLOR(h) ((LLDCOLOR_TYPE)(LLDHTML2COLOR_R(h) | LLDHTML2COLOR_G(h) | LLDHTML2COLOR_B(h))) + + //------------------------- + // Gray-scale color system + //------------------------- + #elif (GDISP_LLD_PIXELFORMAT & GDISP_COLORSYSTEM_MASK) == GDISP_COLORSYSTEM_GRAYSCALE + #define LLDCOLOR_SYSTEM GDISP_COLORSYSTEM_GRAYSCALE + + // Calculate the number of bits and shifts + #define LLDCOLOR_BITS (GDISP_LLD_PIXELFORMAT & 0xFF) + #define LLDCOLOR_BITS_R LLDCOLOR_BITS + #define LLDCOLOR_BITS_G LLDCOLOR_BITS + #define LLDCOLOR_BITS_B LLDCOLOR_BITS + #define LLDCOLOR_SHIFT_R 0 + #define LLDCOLOR_SHIFT_G 0 + #define LLDCOLOR_SHIFT_B 0 + + // From the number of bits determine COLOR_TYPE, COLOR_TYPE_BITS and masking + #if LLDCOLOR_BITS <= 8 + #define LLDCOLOR_TYPE uint8_t + #define LLDCOLOR_TYPE_BITS 8 + #else + #error "GDISP: Cannot define gray-scale low level driver color types with more than 8 bits" + #endif + #if LLDCOLOR_TYPE_BITS == LLDCOLOR_BITS + #define LLDCOLOR_NEEDS_MASK FALSE + #else + #define LLDCOLOR_NEEDS_MASK TRUE + #endif + #define LLDCOLOR_MASK() ((1 << LLDCOLOR_BITS)-1) + + #if COLOR_BITS == 1 + #define LLDRGB2COLOR(r,g,b) (((r)|(g)|(b)) ? 1 : 0) + #define LLDLUMA2COLOR(l) ((l) ? 1 : 0) + #define LLDHTML2COLOR(h) ((h) ? 1 : 0) + #define LLDLUMA_OF(c) ((c) ? 255 : 0) + #define LLDEXACT_LUMA_OF(c) LLDLUMA_OF(c) + #else + // They eye is more sensitive to green + #define LLDRGB2COLOR(r,g,b) ((LLDCOLOR_TYPE)(((uint16_t)(r)+(g)+(g)+(b)) >> (10-LLDCOLOR_BITS))) + #define LLDLUMA2COLOR(l) ((LLDCOLOR_TYPE)((l)>>(8-LLDCOLOR_BITS))) + #define LLDHTML2COLOR(h) ((LLDCOLOR_TYPE)(((((h)&0xFF0000)>>16)+(((h)&0x00FF00)>>7)+((h)&0x0000FF)) >> (10-LLDCOLOR_BITS))) + #define LLDLUMA_OF(c) (((c) & ((1<<LLDCOLOR_BITS)-1)) << (8-LLDCOLOR_BITS)) + #define LLDEXACT_LUMA_OF(c) ((((uint16_t)(c) & ((1<<LLDCOLOR_BITS)-1))*255)/((1<<LLDCOLOR_BITS)-1)) + #endif + + #define LLDRED_OF(c) LLDLUMA_OF(c) + #define LLDGREEN_OF(c) LLDLUMA_OF(c) + #define LLDBLUE_OF(c) LLDLUMA_OF(c) + #define LLDEXACT_RED_OF(c) LLDEXACT_LUMA_OF(c) + #define LLDEXACT_GREEN_OF(c) LLDEXACT_LUMA_OF(c) + #define LLDEXACT_BLUE_OF(c) LLDEXACT_LUMA_OF(c) + + //------------------------- + // Palette color system + //------------------------- + #elif (GDISP_LLD_PIXELFORMAT & GDISP_COLORSYSTEM_MASK) == GDISP_COLORSYSTEM_PALETTE + #define LLDCOLOR_SYSTEM GDISP_COLORSYSTEM_PALETTE + + #error "GDISP: A palette color system for low level drivers is not currently supported" + + //------------------------- + // Some other color system + //------------------------- + #else + #error "GDISP: Unsupported color system for low level drivers" + #endif + + /* Which is the larger color type */ + #if COLOR_BITS > LLDCOLOR_BITS + #define LARGER_COLOR_BITS COLOR_BITS + #define LARGER_COLOR_TYPE COLOR_TYPE + #else + #define LARGER_COLOR_BITS LLDCOLOR_BITS + #define LARGER_COLOR_TYPE LLDCOLOR_TYPE + #endif + + /** + * @brief Controls color conversion accuracy for a low level driver + * @details Should higher precision be used when converting colors. + * @note Color conversion is only necessary if GDISP_PIXELFORMAT != GDISP_LLD_PIXELFORMAT + * @note It only makes sense to turn this on if you have a high bit depth display but + * are running the application in low bit depths. + * @note To achieve higher color accuracy bit shifting is replaced with multiplies and divides. + */ + #ifndef GDISP_HARDWARE_USE_EXACT_COLOR + #if LLDCOLOR_BITS_R - COLOR_BITS_R >= LLDCOLOR_BITS_R/2 || LLDCOLOR_BITS_G - COLOR_BITS_G >= LLDCOLOR_BITS_G/2 || LLDCOLOR_BITS_B - COLOR_BITS_B >= LLDCOLOR_BITS_B/2 + #define GDISP_HARDWARE_USE_EXACT_COLOR TRUE + #else + #define GDISP_HARDWARE_USE_EXACT_COLOR FALSE + #endif + #endif + + /* Low level driver pixel format conversion functions */ + #if GDISP_PIXELFORMAT == GDISP_LLD_PIXELFORMAT || defined(__DOXYGEN__) + /** + * @brief Convert from a standard color format to the low level driver pixel format + * @note For use only by low level drivers + */ + #define gdispColor2Native(c) (c) + /** + * @brief Convert from a low level driver pixel format to the standard color format + * @note For use only by low level drivers + */ + #define gdispNative2Color(c) (c) + #else + static LLDCOLOR_TYPE gdispColor2Native(color_t c) { + #if COLOR_SYSTEM == GDISP_COLORSYSTEM_GRAYSCALE || LLDCOLOR_SYSTEM == GDISP_COLORSYSTEM_GRAYSCALE + #if GDISP_HARDWARE_USE_EXACT_COLOR + return LLDLUMA2COLOR(EXACT_LUMA_OF(c)); + #else + return LLDLUMA2COLOR(LUMA_OF(c)); + #endif + #elif COLOR_SYSTEM == GDISP_COLORSYSTEM_TRUECOLOR && LLDCOLOR_SYSTEM == GDISP_COLORSYSTEM_TRUECOLOR + #if GDISP_HARDWARE_USE_EXACT_COLOR + return LLDRGB2COLOR(EXACT_RED_OF(c), EXACT_GREEN_OF(c), EXACT_BLUE_OF(c)); + #else + return LLDRGB2COLOR(RED_OF(c), GREEN_OF(c), BLUE_OF(c)); + #endif + #else + #error "GDISP: This pixel format conversion is not supported yet" + #endif + } + static color_t gdispNative2Color(LLDCOLOR_TYPE c) { + #if COLOR_SYSTEM == GDISP_COLORSYSTEM_GRAYSCALE || LLDCOLOR_SYSTEM == GDISP_COLORSYSTEM_GRAYSCALE + #if GDISP_HARDWARE_USE_EXACT_COLOR + return LUMA2COLOR(LLDEXACT_LUMA_OF(c)); + #else + return LUMA2COLOR(LLDLUMA_OF(c)); + #endif + #elif COLOR_SYSTEM == GDISP_COLORSYSTEM_TRUECOLOR && LLDCOLOR_SYSTEM == GDISP_COLORSYSTEM_TRUECOLOR + #if GDISP_HARDWARE_USE_EXACT_COLOR + return RGB2COLOR(LLDEXACT_RED_OF(c), LLDEXACT_GREEN_OF(c), LLDEXACT_BLUE_OF(c)); + #else + return RGB2COLOR(LLDRED_OF(c), LLDGREEN_OF(c), LLDBLUE_OF(c)); + #endif + #else + #error "GDISP: This pixel format conversion is not supported yet" + #endif + } + #endif + +#endif + +//------------------------------------------------------------------------------------------------------------ + +#undef IN_PIXMAP_DRIVER +#undef IS_MULTIPLE +#undef IN_DRIVER +#undef USE_VMT +#endif /* GFX_USE_GDISP */ + +#endif /* _GDISP_LLD_H */ +/** @} */ diff --git a/src/gdisp/gdisp_fonts.c b/src/gdisp/gdisp_fonts.c index cdb8e075..81df08ec 100644 --- a/src/gdisp/gdisp_fonts.c +++ b/src/gdisp/gdisp_fonts.c @@ -5,14 +5,6 @@ * http://ugfx.org/license.html */ -/** - * @file src/gdisp/gdisp_fonts.c - * @brief GDISP Font Handling. - * - * @addtogroup GDISP - * @{ - */ - #include "gfx.h" #if GFX_USE_GDISP && GDISP_NEED_TEXT @@ -93,4 +85,3 @@ const char *gdispGetFontName(font_t font) { } #endif /* GFX_USE_GDISP && GDISP_NEED_TEXT */ -/** @} */ diff --git a/src/gdisp/gdisp_gdisp.c b/src/gdisp/gdisp_gdisp.c deleted file mode 100644 index 8910bcf1..00000000 --- a/src/gdisp/gdisp_gdisp.c +++ /dev/null @@ -1,3378 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gdisp/gdisp_gdisp.c - * @brief GDISP Driver code. - * - * @addtogroup GDISP - * @{ - */ -#include "gfx.h" - -#if GFX_USE_GDISP - -/* Include the low level driver information */ -#include "driver.h" - -#if 1 - #undef INLINE - #if defined(__KEIL__) || defined(__C51__) - #define INLINE __inline - #else - #define INLINE inline - #endif -#else - #undef INLINE - #define INLINE -#endif - -// Number of milliseconds for the startup logo - 0 means disabled. -#if GDISP_NEED_STARTUP_LOGO - #define GDISP_STARTUP_LOGO_TIMEOUT 1000 -#else - #define GDISP_STARTUP_LOGO_TIMEOUT 0 -#endif - -/*===========================================================================*/ -/* Driver local variables. */ -/*===========================================================================*/ - -#if GDISP_NEED_TIMERFLUSH - static GTimer FlushTimer; -#endif - -GDisplay *GDISP; - -#if GDISP_NEED_MULTITHREAD - #define MUTEX_INIT(g) gfxMutexInit(&(g)->mutex) - #define MUTEX_ENTER(g) gfxMutexEnter(&(g)->mutex) - #define MUTEX_EXIT(g) gfxMutexExit(&(g)->mutex) - #define MUTEX_DEINIT(g) gfxMutexDestroy(&(g)->mutex) -#else - #define MUTEX_INIT(g) - #define MUTEX_ENTER(g) - #define MUTEX_EXIT(g) - #define MUTEX_DEINIT(g) -#endif - -#define NEED_CLIPPING (GDISP_HARDWARE_CLIP != TRUE && (GDISP_NEED_VALIDATION || GDISP_NEED_CLIP)) - -#if !NEED_CLIPPING - #define TEST_CLIP_AREA(g) -#elif GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT - #define TEST_CLIP_AREA(g) \ - if (!gvmt(g)->setclip) { \ - if ((g)->p.x < (g)->clipx0) { (g)->p.cx -= (g)->clipx0 - (g)->p.x; (g)->p.x = (g)->clipx0; } \ - if ((g)->p.y < (g)->clipy0) { (g)->p.cy -= (g)->clipy0 - (g)->p.y; (g)->p.y = (g)->clipy0; } \ - if ((g)->p.x + (g)->p.cx > (g)->clipx1) (g)->p.cx = (g)->clipx1 - (g)->p.x; \ - if ((g)->p.y + (g)->p.cy > (g)->clipy1) (g)->p.cy = (g)->clipy1 - (g)->p.y; \ - } \ - if ((g)->p.cx > 0 && (g)->p.cy > 0) -#else - #define TEST_CLIP_AREA(g) \ - if ((g)->p.x < (g)->clipx0) { (g)->p.cx -= (g)->clipx0 - (g)->p.x; (g)->p.x = (g)->clipx0; } \ - if ((g)->p.y < (g)->clipy0) { (g)->p.cy -= (g)->clipy0 - (g)->p.y; (g)->p.y = (g)->clipy0; } \ - if ((g)->p.x + (g)->p.cx > (g)->clipx1) (g)->p.cx = (g)->clipx1 - (g)->p.x; \ - if ((g)->p.y + (g)->p.cy > (g)->clipy1) (g)->p.cy = (g)->clipy1 - (g)->p.y; \ - if ((g)->p.cx > 0 && (g)->p.cy > 0) -#endif - -/*==========================================================================*/ -/* Internal functions. */ -/*==========================================================================*/ - -#if GDISP_HARDWARE_STREAM_POS && GDISP_HARDWARE_STREAM_WRITE - static INLINE void setglobalwindow(GDisplay *g) { - coord_t x, y; - x = g->p.x; y = g->p.y; - g->p.x = g->p.y = 0; - g->p.cx = g->g.Width; g->p.cy = g->g.Height; - gdisp_lld_write_start(g); - g->p.x = x; g->p.y = y; - g->flags |= GDISP_FLG_SCRSTREAM; - } -#endif - -#if GDISP_NEED_AUTOFLUSH && GDISP_HARDWARE_FLUSH == HARDWARE_AUTODETECT - #define autoflush_stopdone(g) if (gvmt(g)->flush) gdisp_lld_flush(g) -#elif GDISP_NEED_AUTOFLUSH && GDISP_HARDWARE_FLUSH - #define autoflush_stopdone(g) gdisp_lld_flush(g) -#else - #define autoflush_stopdone(g) -#endif - -#if GDISP_HARDWARE_STREAM_POS && GDISP_HARDWARE_STREAM_WRITE - #define autoflush(g) \ - { \ - if ((g->flags & GDISP_FLG_SCRSTREAM)) { \ - gdisp_lld_write_stop(g); \ - g->flags &= ~GDISP_FLG_SCRSTREAM; \ - } \ - autoflush_stopdone(g); \ - } -#else - #define autoflush(g) autoflush_stopdone(g) -#endif - -// drawpixel(g) -// Parameters: x,y -// Alters: cx, cy (if using streaming) -// Does not clip -static INLINE void drawpixel(GDisplay *g) { - - // Best is hardware accelerated pixel draw - #if GDISP_HARDWARE_DRAWPIXEL - #if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT - if (gvmt(g)->pixel) - #endif - { - gdisp_lld_draw_pixel(g); - return; - } - #endif - - // Next best is cursor based streaming - #if GDISP_HARDWARE_DRAWPIXEL != TRUE && GDISP_HARDWARE_STREAM_POS && GDISP_HARDWARE_STREAM_WRITE - #if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT - if (gvmt(g)->writepos) - #endif - { - if (!(g->flags & GDISP_FLG_SCRSTREAM)) - setglobalwindow(g); - gdisp_lld_write_pos(g); - gdisp_lld_write_color(g); - return; - } - #endif - - // Worst is general streaming - #if GDISP_HARDWARE_DRAWPIXEL != TRUE && GDISP_HARDWARE_STREAM_POS != TRUE && GDISP_HARDWARE_STREAM_WRITE - // The following test is unneeded because we are guaranteed to have streaming if we don't have drawpixel - //#if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT - // if (gvmt(g)->writestart) - //#endif - { - g->p.cx = g->p.cy = 1; - gdisp_lld_write_start(g); - gdisp_lld_write_color(g); - gdisp_lld_write_stop(g); - return; - } - #endif -} - -// drawpixel_clip(g) -// Parameters: x,y -// Alters: cx, cy (if using streaming) -#if NEED_CLIPPING - static INLINE void drawpixel_clip(GDisplay *g) { - #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT - if (!gvmt(g)->setclip) - #endif - { - if (g->p.x < g->clipx0 || g->p.x >= g->clipx1 || g->p.y < g->clipy0 || g->p.y >= g->clipy1) - return; - } - drawpixel(g); - } -#else - #define drawpixel_clip(g) drawpixel(g) -#endif - -// fillarea(g) -// Parameters: x,y cx,cy and color -// Alters: nothing -// Note: This is not clipped -// Resets the streaming area if GDISP_HARDWARE_STREAM_WRITE and GDISP_HARDWARE_STREAM_POS is set. -static INLINE void fillarea(GDisplay *g) { - - // Best is hardware accelerated area fill - #if GDISP_HARDWARE_FILLS - #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT - if (gvmt(g)->fill) - #endif - { - gdisp_lld_fill_area(g); - return; - } - #endif - - // Next best is hardware streaming - #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE - #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT - if (gvmt(g)->writestart) - #endif - { - uint32_t area; - - #if GDISP_HARDWARE_STREAM_POS - if ((g->flags & GDISP_FLG_SCRSTREAM)) { - gdisp_lld_write_stop(g); - g->flags &= ~GDISP_FLG_SCRSTREAM; - } - #endif - - area = (uint32_t)g->p.cx * g->p.cy; - gdisp_lld_write_start(g); - #if GDISP_HARDWARE_STREAM_POS - #if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT - if (gvmt(g)->writepos) - #endif - gdisp_lld_write_pos(g); - #endif - for(; area; area--) - gdisp_lld_write_color(g); - gdisp_lld_write_stop(g); - return; - } - #endif - - // Worst is pixel drawing - #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_DRAWPIXEL - // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming - //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT - // if (gvmt(g)->pixel) - //#endif - { - coord_t x0, y0, x1, y1; - - x0 = g->p.x; - y0 = g->p.y; - x1 = g->p.x + g->p.cx; - y1 = g->p.y + g->p.cy; - for(; g->p.y < y1; g->p.y++, g->p.x = x0) - for(; g->p.x < x1; g->p.x++) - gdisp_lld_draw_pixel(g); - g->p.y = y0; - return; - } - #endif -} - -// Parameters: x,y and x1 -// Alters: x,y x1,y1 cx,cy -// Assumes the window covers the screen and a write_stop() will occur later -// if GDISP_HARDWARE_STREAM_WRITE and GDISP_HARDWARE_STREAM_POS is set. -static void hline_clip(GDisplay *g) { - // Swap the points if necessary so it always goes from x to x1 - if (g->p.x1 < g->p.x) { - g->p.cx = g->p.x; g->p.x = g->p.x1; g->p.x1 = g->p.cx; - } - - // Clipping - #if NEED_CLIPPING - #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT - if (!gvmt(g)->setclip) - #endif - { - if (g->p.y < g->clipy0 || g->p.y >= g->clipy1) return; - if (g->p.x < g->clipx0) g->p.x = g->clipx0; - if (g->p.x1 >= g->clipx1) g->p.x1 = g->clipx1 - 1; - if (g->p.x1 < g->p.x) return; - } - #endif - - // This is an optimization for the point case. It is only worthwhile however if we - // have hardware fills or if we support both hardware pixel drawing and hardware streaming - #if GDISP_HARDWARE_FILLS || (GDISP_HARDWARE_DRAWPIXEL && GDISP_HARDWARE_STREAM_WRITE) - // Is this a point - if (g->p.x == g->p.x1) { - drawpixel(g); - return; - } - #endif - - // Best is hardware accelerated area fill - #if GDISP_HARDWARE_FILLS - #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT - if (gvmt(g)->fill) - #endif - { - g->p.cx = g->p.x1 - g->p.x + 1; - g->p.cy = 1; - gdisp_lld_fill_area(g); - return; - } - #endif - - // Next best is cursor based streaming - #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_POS && GDISP_HARDWARE_STREAM_WRITE - #if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT - if (gvmt(g)->writepos) - #endif - { - if (!(g->flags & GDISP_FLG_SCRSTREAM)) - setglobalwindow(g); - g->p.cx = g->p.x1 - g->p.x + 1; - gdisp_lld_write_pos(g); - do { gdisp_lld_write_color(g); } while(--g->p.cx); - return; - } - #endif - - // Next best is streaming - #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_POS != TRUE && GDISP_HARDWARE_STREAM_WRITE - #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT - if (gvmt(g)->writestart) - #endif - { - g->p.cx = g->p.x1 - g->p.x + 1; - g->p.cy = 1; - gdisp_lld_write_start(g); - do { gdisp_lld_write_color(g); } while(--g->p.cx); - gdisp_lld_write_stop(g); - return; - } - #endif - - // Worst is drawing pixels - #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_DRAWPIXEL - // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming - //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT - // if (gvmt(g)->pixel) - //#endif - { - for(; g->p.x <= g->p.x1; g->p.x++) - gdisp_lld_draw_pixel(g); - return; - } - #endif -} - -// Parameters: x,y and y1 -// Alters: x,y x1,y1 cx,cy -static void vline_clip(GDisplay *g) { - // Swap the points if necessary so it always goes from y to y1 - if (g->p.y1 < g->p.y) { - g->p.cy = g->p.y; g->p.y = g->p.y1; g->p.y1 = g->p.cy; - } - - // Clipping - #if NEED_CLIPPING - #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT - if (!gvmt(g)->setclip) - #endif - { - if (g->p.x < g->clipx0 || g->p.x >= g->clipx1) return; - if (g->p.y < g->clipy0) g->p.y = g->clipy0; - if (g->p.y1 >= g->clipy1) g->p.y1 = g->clipy1 - 1; - if (g->p.y1 < g->p.y) return; - } - #endif - - // This is an optimization for the point case. It is only worthwhile however if we - // have hardware fills or if we support both hardware pixel drawing and hardware streaming - #if GDISP_HARDWARE_FILLS || (GDISP_HARDWARE_DRAWPIXEL && GDISP_HARDWARE_STREAM_WRITE) || (GDISP_HARDWARE_STREAM_POS && GDISP_HARDWARE_STREAM_WRITE) - // Is this a point - if (g->p.y == g->p.y1) { - drawpixel(g); - return; - } - #endif - - // Best is hardware accelerated area fill - #if GDISP_HARDWARE_FILLS - #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT - if (gvmt(g)->fill) - #endif - { - g->p.cy = g->p.y1 - g->p.y + 1; - g->p.cx = 1; - gdisp_lld_fill_area(g); - return; - } - #endif - - // Next best is streaming - #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE - #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT - if (gvmt(g)->writestart) - #endif - { - #if GDISP_HARDWARE_STREAM_POS - if ((g->flags & GDISP_FLG_SCRSTREAM)) { - gdisp_lld_write_stop(g); - g->flags &= ~GDISP_FLG_SCRSTREAM; - } - #endif - g->p.cy = g->p.y1 - g->p.y + 1; - g->p.cx = 1; - gdisp_lld_write_start(g); - #if GDISP_HARDWARE_STREAM_POS - #if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT - if (gvmt(g)->writepos) - #endif - gdisp_lld_write_pos(g); - #endif - do { gdisp_lld_write_color(g); } while(--g->p.cy); - gdisp_lld_write_stop(g); - return; - } - #endif - - // Worst is drawing pixels - #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_DRAWPIXEL - // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming - //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT - // if (gvmt(g)->pixel) - //#endif - { - for(; g->p.y <= g->p.y1; g->p.y++) - gdisp_lld_draw_pixel(g); - return; - } - #endif -} - -// Parameters: x,y and x1,y1 -// Alters: x,y x1,y1 cx,cy -static void line_clip(GDisplay *g) { - int16_t dy, dx; - int16_t addx, addy; - int16_t P, diff, i; - - // Is this a horizontal line (or a point) - if (g->p.y == g->p.y1) { - hline_clip(g); - return; - } - - // Is this a vertical line (or a point) - if (g->p.x == g->p.x1) { - vline_clip(g); - return; - } - - // Not horizontal or vertical - - // Use Bresenham's line drawing algorithm. - // This should be replaced with fixed point slope based line drawing - // which is more efficient on modern processors as it branches less. - // When clipping is needed, all the clipping could also be done up front - // instead of on each pixel. - - if (g->p.x1 >= g->p.x) { - dx = g->p.x1 - g->p.x; - addx = 1; - } else { - dx = g->p.x - g->p.x1; - addx = -1; - } - if (g->p.y1 >= g->p.y) { - dy = g->p.y1 - g->p.y; - addy = 1; - } else { - dy = g->p.y - g->p.y1; - addy = -1; - } - - if (dx >= dy) { - dy <<= 1; - P = dy - dx; - diff = P - dx; - - for(i=0; i<=dx; ++i) { - drawpixel_clip(g); - if (P < 0) { - P += dy; - g->p.x += addx; - } else { - P += diff; - g->p.x += addx; - g->p.y += addy; - } - } - } else { - dx <<= 1; - P = dx - dy; - diff = P - dy; - - for(i=0; i<=dy; ++i) { - drawpixel_clip(g); - if (P < 0) { - P += dx; - g->p.y += addy; - } else { - P += diff; - g->p.x += addx; - g->p.y += addy; - } - } - } -} - -#if GDISP_STARTUP_LOGO_TIMEOUT > 0 - static bool_t initDone; - static void StartupLogoDisplay(GDisplay *g) { - coord_t x, y, w; - const coord_t * p; - static const coord_t blks[] = { - // u - 2, 6, 1, 10, - 3, 11, 4, 1, - 6, 6, 1, 6, - // G - 8, 0, 1, 12, - 9, 0, 6, 1, - 9, 11, 6, 1, - 14, 6, 1, 5, - 12, 6, 2, 1, - // F - 16, 0, 1, 12, - 17, 0, 6, 1, - 17, 6, 3, 1, - // X - 22, 6, 7, 1, - 24, 0, 1, 6, - 22, 7, 1, 5, - 28, 0, 1, 6, - 26, 7, 1, 5, - }; - - // Get a starting position and a scale - // Work on a 8x16 grid for each char, 4 chars (uGFX) in 1 line, using half the screen - w = g->g.Width/(8*4*2); - if (!w) w = 1; - x = (g->g.Width - (8*4)*w)/2; - y = (g->g.Height - (16*1)*w)/2; - - // Simple but crude! - for(p = blks; p < blks+sizeof(blks)/sizeof(blks[0]); p+=4) - gdispGFillArea(g, x+p[0]*w, y+p[1]*w, p[2]*w, p[3]*w, Blue); - } -#endif - -#if GDISP_NEED_TIMERFLUSH - static void FlushTimerFn(void *param) { - GDisplay * g; - (void) param; - - for(g = (GDisplay *)gdriverGetNext(GDRIVER_TYPE_DISPLAY, 0); g; g = (GDisplay *)gdriverGetNext(GDRIVER_TYPE_DISPLAY, (GDriver *)g)) - gdispGFlush(g); - } -#endif - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -typedef const GDISPVMT const GDISPVMTLIST[]; - -void _gdispInit(void) -{ - // GDISP_DRIVER_LIST is defined - create each driver instance - #if defined(GDISP_DRIVER_LIST) - { - unsigned i; - - extern GDISPVMTLIST GDISP_DRIVER_LIST; - static GDISPVMTLIST dclist[] = {GDISP_DRIVER_LIST}; - - for(i = 0; i < sizeof(dclist)/sizeof(dclist[0]); i++) - if (!(dclist[i]->d.flags & GDISP_VFLG_DYNAMICONLY)) - gdriverRegister(&dclist[i]->d, 0); - } - #elif GDISP_TOTAL_DISPLAYS > 1 - { - unsigned i; - extern GDISPVMTLIST GDISPVMT_OnlyOne; - - if (!(GDISPVMT_OnlyOne->d.flags & GDISP_VFLG_DYNAMICONLY)) { - for(i = 0; i < GDISP_TOTAL_DISPLAYS; i++) - gdriverRegister(&GDISPVMT_OnlyOne->d, 0); - } - } - #else - { - extern GDISPVMTLIST GDISPVMT_OnlyOne; - - if (!(GDISPVMT_OnlyOne->d.flags & GDISP_VFLG_DYNAMICONLY)) - gdriverRegister(&GDISPVMT_OnlyOne->d, 0); - } - #endif - - // Re-clear the display after the timeout if we added the logo - #if GDISP_STARTUP_LOGO_TIMEOUT > 0 - { - GDisplay *g; - - gfxSleepMilliseconds(GDISP_STARTUP_LOGO_TIMEOUT); - - for(g = (GDisplay *)gdriverGetNext(GDRIVER_TYPE_DISPLAY, 0); g; g = (GDisplay *)gdriverGetNext(GDRIVER_TYPE_DISPLAY, (GDriver *)g)) { - gdispGClear(g, GDISP_STARTUP_COLOR); - #if GDISP_HARDWARE_FLUSH - gdispGFlush(g); - #endif - } - - initDone = TRUE; - } - #endif - - // Start the automatic timer flush (if required) - #if GDISP_NEED_TIMERFLUSH - gtimerInit(&FlushTimer); - gtimerStart(&FlushTimer, FlushTimerFn, 0, TRUE, GDISP_NEED_TIMERFLUSH); - #endif -} - -void _gdispDeinit(void) -{ - /* ToDo */ -} - -bool_t _gdispInitDriver(GDriver *g, void *param, unsigned driverinstance, unsigned systeminstance) { - #define gd ((GDisplay *)g) - bool_t ret; - - // Intialise fields - gd->systemdisplay = systeminstance; - gd->controllerdisplay = driverinstance; - gd->flags = 0; - gd->priv = param; - MUTEX_INIT(gd); - - // Call the driver init - MUTEX_ENTER(gd); - ret = gdisp_lld_init(gd); - MUTEX_EXIT(gd); - return ret; - - #undef gd -} - -void _gdispPostInitDriver(GDriver *g) { - #define gd ((GDisplay *)g) - - // Set orientation, clip - #if defined(GDISP_DEFAULT_ORIENTATION) && GDISP_NEED_CONTROL && GDISP_HARDWARE_CONTROL - #if GDISP_NEED_PIXMAP - // Pixmaps should stay in their created orientation (at least initially) - if (!(gvmt(gd)->d.flags & GDISP_VFLG_PIXMAP)) - #endif - gdispGControl(gd, GDISP_CONTROL_ORIENTATION, (void *)GDISP_DEFAULT_ORIENTATION); - #endif - #if GDISP_NEED_VALIDATION || GDISP_NEED_CLIP - gdispGSetClip(gd, 0, 0, gd->g.Width, gd->g.Height); - #endif - - // Clear the Screen - gdispGClear(gd, GDISP_STARTUP_COLOR); - - // Display the startup logo if this is a static initialised display - #if GDISP_STARTUP_LOGO_TIMEOUT > 0 - if (!initDone) - StartupLogoDisplay(gd); - #endif - - // Flush - #if GDISP_HARDWARE_FLUSH - gdispGFlush(gd); - #endif - - // If this is the first driver set GDISP - if (!GDISP) - GDISP = gd; - - #undef gd -} - -void _gdispDeInitDriver(GDriver *g) { - #define gd ((GDisplay *)g) - - if (GDISP == gd) - GDISP = (GDisplay *)gdriverGetInstance(GDRIVER_TYPE_DISPLAY, 0); - - #if GDISP_HARDWARE_DEINIT - #if GDISP_HARDWARE_DEINIT == HARDWARE_AUTODETECT - if (gvmt(gd)->deinit) - #endif - { - MUTEX_ENTER(gd); - gdisp_lld_deinit(gd); - MUTEX_EXIT(gd); - } - #endif - MUTEX_DEINIT(gd); - - #undef gd -} - -GDisplay *gdispGetDisplay(unsigned display) { - return (GDisplay *)gdriverGetInstance(GDRIVER_TYPE_DISPLAY, display); -} - -void gdispSetDisplay(GDisplay *g) { - if (g) GDISP = g; -} - -unsigned gdispGetDisplayCount(void) { - return gdriverInstanceCount(GDRIVER_TYPE_DISPLAY); -} - -coord_t gdispGGetWidth(GDisplay *g) { return g->g.Width; } -coord_t gdispGGetHeight(GDisplay *g) { return g->g.Height; } -powermode_t gdispGGetPowerMode(GDisplay *g) { return g->g.Powermode; } -orientation_t gdispGGetOrientation(GDisplay *g) { return g->g.Orientation; } -uint8_t gdispGGetBacklight(GDisplay *g) { return g->g.Backlight; } -uint8_t gdispGGetContrast(GDisplay *g) { return g->g.Contrast; } - -void gdispGFlush(GDisplay *g) { - #if GDISP_HARDWARE_FLUSH - #if GDISP_HARDWARE_FLUSH == HARDWARE_AUTODETECT - if (gvmt(g)->flush) - #endif - { - MUTEX_ENTER(g); - gdisp_lld_flush(g); - MUTEX_EXIT(g); - } - #else - (void) g; - #endif -} - -#if GDISP_NEED_STREAMING - void gdispGStreamStart(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy) { - MUTEX_ENTER(g); - - #if NEED_CLIPPING - #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT - if (!gvmt(g)->setclip) - #endif - // Test if the area is valid - if not then exit - if (x < g->clipx0 || x+cx > g->clipx1 || y < g->clipy0 || y+cy > g->clipy1) { - MUTEX_EXIT(g); - return; - } - #endif - - g->flags |= GDISP_FLG_INSTREAM; - - // Best is hardware streaming - #if GDISP_HARDWARE_STREAM_WRITE - #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT - if (gvmt(g)->writestart) - #endif - { - g->p.x = x; - g->p.y = y; - g->p.cx = cx; - g->p.cy = cy; - gdisp_lld_write_start(g); - #if GDISP_HARDWARE_STREAM_POS - #if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT - if (gvmt(g)->writepos) - #endif - gdisp_lld_write_pos(g); - #endif - return; - } - #endif - - // Worst - save the parameters and use pixel drawing and/or area fills - #if GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_DRAWPIXEL - // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming - //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT - // if (gvmt(g)->pixel) - //#endif - { - // Use x,y as the current position, x1,y1 as the save position and x2,y2 as the end position, cx = bufpos - g->p.x1 = g->p.x = x; - g->p.y1 = g->p.y = y; - g->p.x2 = x + cx; - g->p.y2 = y + cy; - #if (GDISP_LINEBUF_SIZE != 0 && GDISP_HARDWARE_BITFILLS) || GDISP_HARDWARE_FILLS - g->p.cx = 0; - g->p.cy = 1; - #endif - return; - } - #endif - - // Don't release the mutex as gdispStreamEnd() will do that. - } - - void gdispGStreamColor(GDisplay *g, color_t color) { - #if !GDISP_HARDWARE_STREAM_WRITE && GDISP_LINEBUF_SIZE != 0 && GDISP_HARDWARE_BITFILLS - coord_t sx1, sy1; - #endif - - // Don't touch the mutex as we should already own it - - // Ignore this call if we are not streaming - if (!(g->flags & GDISP_FLG_INSTREAM)) - return; - - // Best is hardware streaming - #if GDISP_HARDWARE_STREAM_WRITE - #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT - if (gvmt(g)->writestart) - #endif - { - g->p.color = color; - gdisp_lld_write_color(g); - return; - } - #endif - - // Next best is to use bitfills with our line buffer - #if GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_LINEBUF_SIZE != 0 && GDISP_HARDWARE_BITFILLS - #if GDISP_HARDWARE_BITFILLS == HARDWARE_AUTODETECT - if (gvmt(g)->blit) - #endif - { - g->linebuf[g->p.cx++] = color; - if (g->p.cx >= GDISP_LINEBUF_SIZE) { - sx1 = g->p.x1; - sy1 = g->p.y1; - g->p.x1 = 0; - g->p.y1 = 0; - g->p.ptr = (void *)g->linebuf; - gdisp_lld_blit_area(g); - g->p.x1 = sx1; - g->p.y1 = sy1; - g->p.x += g->p.cx; - g->p.cx = 0; - } - - // Just wrap at end-of-line and end-of-buffer - if (g->p.x+g->p.cx >= g->p.x2) { - if (g->p.cx) { - sx1 = g->p.x1; - sy1 = g->p.y1; - g->p.x1 = 0; - g->p.y1 = 0; - g->p.ptr = (void *)g->linebuf; - gdisp_lld_blit_area(g); - g->p.x1 = sx1; - g->p.y1 = sy1; - g->p.cx = 0; - } - g->p.x = g->p.x1; - if (++g->p.y >= g->p.y2) - g->p.y = g->p.y1; - } - } - #endif - - // Only slightly better than drawing pixels is to look for runs and use fillarea - #if GDISP_HARDWARE_STREAM_WRITE != TRUE && (GDISP_LINEBUF_SIZE == 0 || GDISP_HARDWARE_BITFILLS != TRUE) && GDISP_HARDWARE_FILLS - // We don't need to test for auto-detect on drawpixel as we know we have it because we don't have streaming. - #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT - if (gvmt(g)->fill) - #endif - { - if (!g->p.cx || g->p.color == color) { - g->p.cx++; - g->p.color = color; - } else { - if (g->p.cx == 1) - gdisp_lld_draw_pixel(g); - else - gdisp_lld_fill_area(g); - g->p.x += g->p.cx; - g->p.color = color; - g->p.cx = 1; - } - // Just wrap at end-of-line and end-of-buffer - if (g->p.x+g->p.cx >= g->p.x2) { - if (g->p.cx) { - if (g->p.cx == 1) - gdisp_lld_draw_pixel(g); - else - gdisp_lld_fill_area(g); - g->p.cx = 0; - } - g->p.x = g->p.x1; - if (++g->p.y >= g->p.y2) - g->p.y = g->p.y1; - } - return; - } - #endif - - // Worst is using pixel drawing - #if GDISP_HARDWARE_STREAM_WRITE != TRUE && (GDISP_LINEBUF_SIZE == 0 || GDISP_HARDWARE_BITFILLS != TRUE) && GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_DRAWPIXEL - // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming - //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT - // if (gvmt(g)->pixel) - //#endif - { - g->p.color = color; - gdisp_lld_draw_pixel(g); - - // Just wrap at end-of-line and end-of-buffer - if (++g->p.x >= g->p.x2) { - g->p.x = g->p.x1; - if (++g->p.y >= g->p.y2) - g->p.y = g->p.y1; - } - return; - } - #endif - } - - void gdispGStreamStop(GDisplay *g) { - // Only release the mutex and end the stream if we are actually streaming. - if (!(g->flags & GDISP_FLG_INSTREAM)) - return; - - // Clear the flag - g->flags &= ~GDISP_FLG_INSTREAM; - - // The cleanup below must match the streaming code above. - - #if GDISP_HARDWARE_STREAM_WRITE - #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT - if (gvmt(g)->writestart) - #endif - { - gdisp_lld_write_stop(g); - autoflush_stopdone(g); - MUTEX_EXIT(g); - return; - } - #endif - - #if GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_LINEBUF_SIZE != 0 && GDISP_HARDWARE_BITFILLS - #if GDISP_HARDWARE_BITFILLS == HARDWARE_AUTODETECT - if (gvmt(g)->blit) - #endif - { - if (g->p.cx) { - g->p.x1 = 0; - g->p.y1 = 0; - g->p.ptr = (void *)g->linebuf; - gdisp_lld_blit_area(g); - } - autoflush_stopdone(g); - MUTEX_EXIT(g); - return; - } - #endif - - #if GDISP_HARDWARE_STREAM_WRITE != TRUE && (GDISP_LINEBUF_SIZE == 0 || GDISP_HARDWARE_BITFILLS != TRUE) && GDISP_HARDWARE_FILLS - // We don't need to test for auto-detect on drawpixel as we know we have it because we don't have streaming. - #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT - if (gvmt(g)->fill) - #endif - { - if (g->p.cx) { - if (g->p.cx == 1) - gdisp_lld_draw_pixel(g); - else - gdisp_lld_fill_area(g); - } - autoflush_stopdone(g); - MUTEX_EXIT(g); - return; - } - #endif - - #if GDISP_HARDWARE_STREAM_WRITE != TRUE && (GDISP_LINEBUF_SIZE == 0 || GDISP_HARDWARE_BITFILLS != TRUE) && GDISP_HARDWARE_FILLS != TRUE - { - autoflush_stopdone(g); - MUTEX_EXIT(g); - } - #endif - } -#endif - -void gdispGDrawPixel(GDisplay *g, coord_t x, coord_t y, color_t color) { - MUTEX_ENTER(g); - g->p.x = x; - g->p.y = y; - g->p.color = color; - drawpixel_clip(g); - autoflush(g); - MUTEX_EXIT(g); -} - -void gdispGDrawLine(GDisplay *g, coord_t x0, coord_t y0, coord_t x1, coord_t y1, color_t color) { - MUTEX_ENTER(g); - g->p.x = x0; - g->p.y = y0; - g->p.x1 = x1; - g->p.y1 = y1; - g->p.color = color; - line_clip(g); - autoflush(g); - MUTEX_EXIT(g); -} - -void gdispGClear(GDisplay *g, color_t color) { - // Note - clear() ignores the clipping area. It clears the screen. - MUTEX_ENTER(g); - - // Best is hardware accelerated clear - #if GDISP_HARDWARE_CLEARS - #if GDISP_HARDWARE_CLEARS == HARDWARE_AUTODETECT - if (gvmt(g)->clear) - #endif - { - g->p.color = color; - gdisp_lld_clear(g); - autoflush_stopdone(g); - MUTEX_EXIT(g); - return; - } - #endif - - // Next best is hardware accelerated area fill - #if GDISP_HARDWARE_CLEARS != TRUE && GDISP_HARDWARE_FILLS - #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT - if (gvmt(g)->fill) - #endif - { - g->p.x = g->p.y = 0; - g->p.cx = g->g.Width; - g->p.cy = g->g.Height; - g->p.color = color; - gdisp_lld_fill_area(g); - autoflush_stopdone(g); - MUTEX_EXIT(g); - return; - } - #endif - - // Next best is streaming - #if GDISP_HARDWARE_CLEARS != TRUE && GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE - #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT - if (gvmt(g)->writestart) - #endif - { - uint32_t area; - - g->p.x = g->p.y = 0; - g->p.cx = g->g.Width; - g->p.cy = g->g.Height; - g->p.color = color; - area = (uint32_t)g->p.cx * g->p.cy; - - gdisp_lld_write_start(g); - #if GDISP_HARDWARE_STREAM_POS - #if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT - if (gvmt(g)->writepos) - #endif - gdisp_lld_write_pos(g); - #endif - for(; area; area--) - gdisp_lld_write_color(g); - gdisp_lld_write_stop(g); - autoflush_stopdone(g); - MUTEX_EXIT(g); - return; - } - #endif - - // Worst is drawing pixels - #if GDISP_HARDWARE_CLEARS != TRUE && GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_DRAWPIXEL - // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming - //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT - // if (gvmt(g)->pixel) - //#endif - { - g->p.color = color; - for(g->p.y = 0; g->p.y < g->g.Height; g->p.y++) - for(g->p.x = 0; g->p.x < g->g.Width; g->p.x++) - gdisp_lld_draw_pixel(g); - autoflush_stopdone(g); - MUTEX_EXIT(g); - return; - } - #endif -} - -void gdispGFillArea(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, color_t color) { - MUTEX_ENTER(g); - g->p.x = x; - g->p.y = y; - g->p.cx = cx; - g->p.cy = cy; - g->p.color = color; - TEST_CLIP_AREA(g) { - fillarea(g); - } - autoflush_stopdone(g); - MUTEX_EXIT(g); -} - -void gdispGBlitArea(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t srcx, coord_t srcy, coord_t srccx, const pixel_t *buffer) { - MUTEX_ENTER(g); - - #if NEED_CLIPPING - #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT - if (!gvmt(g)->setclip) - #endif - { - // This is a different clipping to fillarea(g) as it needs to take into account srcx,srcy - if (x < g->clipx0) { cx -= g->clipx0 - x; srcx += g->clipx0 - x; x = g->clipx0; } - if (y < g->clipy0) { cy -= g->clipy0 - y; srcy += g->clipy0 - x; y = g->clipy0; } - if (x+cx > g->clipx1) cx = g->clipx1 - x; - if (y+cy > g->clipy1) cy = g->clipy1 - y; - if (srcx+cx > srccx) cx = srccx - srcx; - if (cx <= 0 || cy <= 0) { MUTEX_EXIT(g); return; } - } - #endif - - // Best is hardware bitfills - #if GDISP_HARDWARE_BITFILLS - #if GDISP_HARDWARE_BITFILLS == HARDWARE_AUTODETECT - if (gvmt(g)->blit) - #endif - { - g->p.x = x; - g->p.y = y; - g->p.cx = cx; - g->p.cy = cy; - g->p.x1 = srcx; - g->p.y1 = srcy; - g->p.x2 = srccx; - g->p.ptr = (void *)buffer; - gdisp_lld_blit_area(g); - autoflush_stopdone(g); - MUTEX_EXIT(g); - return; - } - #endif - - // Next best is hardware streaming - #if GDISP_HARDWARE_BITFILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE - #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT - if (gvmt(g)->writestart) - #endif - { - // Translate buffer to the real image data, use srcx,srcy as the end point, srccx as the buffer line gap - buffer += srcy*srccx+srcx; - srcx = x + cx; - srcy = y + cy; - srccx -= cx; - - g->p.x = x; - g->p.y = y; - g->p.cx = cx; - g->p.cy = cy; - gdisp_lld_write_start(g); - #if GDISP_HARDWARE_STREAM_POS - #if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT - if (gvmt(g)->writepos) - #endif - gdisp_lld_write_pos(g); - #endif - for(g->p.y = y; g->p.y < srcy; g->p.y++, buffer += srccx) { - for(g->p.x = x; g->p.x < srcx; g->p.x++) { - g->p.color = *buffer++; - gdisp_lld_write_color(g); - } - } - gdisp_lld_write_stop(g); - autoflush_stopdone(g); - MUTEX_EXIT(g); - return; - } - #endif - - // Only slightly better than drawing pixels is to look for runs and use fill area - #if GDISP_HARDWARE_BITFILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_FILLS - // We don't need to test for auto-detect on drawpixel as we know we have it because we don't have streaming. - #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT - if (gvmt(g)->fill) - #endif - { - // Translate buffer to the real image data, use srcx,srcy as the end point, srccx as the buffer line gap - buffer += srcy*srccx+srcx; - srcx = x + cx; - srcy = y + cy; - srccx -= cx; - - g->p.cy = 1; - for(g->p.y = y; g->p.y < srcy; g->p.y++, buffer += srccx) { - for(g->p.x=x; g->p.x < srcx; g->p.x += g->p.cx) { - g->p.cx=1; - g->p.color = *buffer++; - while(g->p.x+g->p.cx < srcx && *buffer == g->p.color) { - g->p.cx++; - buffer++; - } - if (g->p.cx == 1) { - gdisp_lld_draw_pixel(g); - } else { - gdisp_lld_fill_area(g); - } - } - } - autoflush_stopdone(g); - MUTEX_EXIT(g); - return; - } - #endif - - // Worst is drawing pixels - #if GDISP_HARDWARE_BITFILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_DRAWPIXEL - // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming - //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT - // if (gvmt(g)->pixel) - //#endif - { - // Translate buffer to the real image data, use srcx,srcy as the end point, srccx as the buffer line gap - buffer += srcy*srccx+srcx; - srcx = x + cx; - srcy = y + cy; - srccx -= cx; - - for(g->p.y = y; g->p.y < srcy; g->p.y++, buffer += srccx) { - for(g->p.x=x; g->p.x < srcx; g->p.x++) { - g->p.color = *buffer++; - gdisp_lld_draw_pixel(g); - } - } - autoflush_stopdone(g); - MUTEX_EXIT(g); - return; - } - #endif -} - -#if GDISP_NEED_CLIP || GDISP_NEED_VALIDATION - void gdispGSetClip(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy) { - MUTEX_ENTER(g); - - // Best is using hardware clipping - #if GDISP_HARDWARE_CLIP - #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT - if (gvmt(g)->setclip) - #endif - { - g->p.x = x; - g->p.y = y; - g->p.cx = cx; - g->p.cy = cy; - gdisp_lld_set_clip(g); - } - #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT - else - #endif - #endif - - // Worst is using software clipping - #if GDISP_HARDWARE_CLIP != TRUE - { - if (x < 0) { cx += x; x = 0; } - if (y < 0) { cy += y; y = 0; } - if (cx <= 0 || cy <= 0 || x >= g->g.Width || y >= g->g.Height) { MUTEX_EXIT(g); return; } - g->clipx0 = x; - g->clipy0 = y; - g->clipx1 = x+cx; if (g->clipx1 > g->g.Width) g->clipx1 = g->g.Width; - g->clipy1 = y+cy; if (g->clipy1 > g->g.Height) g->clipy1 = g->g.Height; - } - #endif - MUTEX_EXIT(g); - } -#endif - -#if GDISP_NEED_CIRCLE - void gdispGDrawCircle(GDisplay *g, coord_t x, coord_t y, coord_t radius, color_t color) { - coord_t a, b, P; - - MUTEX_ENTER(g); - - // Calculate intermediates - a = 1; - b = radius; - P = 4 - radius; - g->p.color = color; - - // Away we go using Bresenham's circle algorithm - // Optimized to prevent double drawing - g->p.x = x; g->p.y = y + b; drawpixel_clip(g); - g->p.x = x; g->p.y = y - b; drawpixel_clip(g); - g->p.x = x + b; g->p.y = y; drawpixel_clip(g); - g->p.x = x - b; g->p.y = y; drawpixel_clip(g); - do { - g->p.x = x + a; g->p.y = y + b; drawpixel_clip(g); - g->p.x = x + a; g->p.y = y - b; drawpixel_clip(g); - g->p.x = x + b; g->p.y = y + a; drawpixel_clip(g); - g->p.x = x - b; g->p.y = y + a; drawpixel_clip(g); - g->p.x = x - a; g->p.y = y + b; drawpixel_clip(g); - g->p.x = x - a; g->p.y = y - b; drawpixel_clip(g); - g->p.x = x + b; g->p.y = y - a; drawpixel_clip(g); - g->p.x = x - b; g->p.y = y - a; drawpixel_clip(g); - if (P < 0) - P += 3 + 2*a++; - else - P += 5 + 2*(a++ - b--); - } while(a < b); - g->p.x = x + a; g->p.y = y + b; drawpixel_clip(g); - g->p.x = x + a; g->p.y = y - b; drawpixel_clip(g); - g->p.x = x - a; g->p.y = y + b; drawpixel_clip(g); - g->p.x = x - a; g->p.y = y - b; drawpixel_clip(g); - - autoflush(g); - MUTEX_EXIT(g); - } -#endif - -#if GDISP_NEED_CIRCLE - void gdispGFillCircle(GDisplay *g, coord_t x, coord_t y, coord_t radius, color_t color) { - coord_t a, b, P; - - MUTEX_ENTER(g); - - // Calculate intermediates - a = 1; - b = radius; - P = 4 - radius; - g->p.color = color; - - // Away we go using Bresenham's circle algorithm - // This is optimized to prevent overdrawing by drawing a line only when a variable is about to change value - g->p.y = y; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); - g->p.y = y+b; g->p.x = x; drawpixel_clip(g); - g->p.y = y-b; g->p.x = x; drawpixel_clip(g); - do { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); - g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); - if (P < 0) { - P += 3 + 2*a++; - } else { - g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); - g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); - P += 5 + 2*(a++ - b--); - } - } while(a < b); - g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); - g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); - - autoflush(g); - MUTEX_EXIT(g); - } -#endif - -#if GDISP_NEED_ELLIPSE - void gdispGDrawEllipse(GDisplay *g, coord_t x, coord_t y, coord_t a, coord_t b, color_t color) { - coord_t dx, dy; - int32_t a2, b2; - int32_t err, e2; - - MUTEX_ENTER(g); - - // Calculate intermediates - dx = 0; - dy = b; - a2 = a*a; - b2 = b*b; - err = b2-(2*b-1)*a2; - g->p.color = color; - - // Away we go using Bresenham's ellipse algorithm - do { - g->p.x = x + dx; g->p.y = y + dy; drawpixel_clip(g); - g->p.x = x - dx; g->p.y = y + dy; drawpixel_clip(g); - g->p.x = x - dx; g->p.y = y - dy; drawpixel_clip(g); - g->p.x = x + dx; g->p.y = y - dy; drawpixel_clip(g); - - e2 = 2*err; - if(e2 < (2*dx+1)*b2) { - dx++; - err += (2*dx+1)*b2; - } - if(e2 > -(2*dy-1)*a2) { - dy--; - err -= (2*dy-1)*a2; - } - } while(dy >= 0); - - autoflush(g); - MUTEX_EXIT(g); - } -#endif - -#if GDISP_NEED_ELLIPSE - void gdispGFillEllipse(GDisplay *g, coord_t x, coord_t y, coord_t a, coord_t b, color_t color) { - coord_t dx, dy; - int32_t a2, b2; - int32_t err, e2; - - MUTEX_ENTER(g); - - // Calculate intermediates - dx = 0; - dy = b; - a2 = a*a; - b2 = b*b; - err = b2-(2*b-1)*a2; - g->p.color = color; - - // Away we go using Bresenham's ellipse algorithm - // This is optimized to prevent overdrawing by drawing a line only when a y is about to change value - do { - e2 = 2*err; - if(e2 < (2*dx+1)*b2) { - dx++; - err += (2*dx+1)*b2; - } - if(e2 > -(2*dy-1)*a2) { - g->p.y = y + dy; g->p.x = x - dx; g->p.x1 = x + dx; hline_clip(g); - if (y) { g->p.y = y - dy; g->p.x = x - dx; g->p.x1 = x + dx; hline_clip(g); } - dy--; - err -= (2*dy-1)*a2; - } - } while(dy >= 0); - - autoflush(g); - MUTEX_EXIT(g); - } -#endif - -#if GDISP_NEED_ARCSECTORS - void gdispGDrawArcSectors(GDisplay *g, coord_t x, coord_t y, coord_t radius, uint8_t sectors, color_t color) { - coord_t a, b, P; - - MUTEX_ENTER(g); - - // Calculate intermediates - a = 1; // x in many explanations - b = radius; // y in many explanations - P = 4 - radius; - g->p.color = color; - - // Away we go using Bresenham's circle algorithm - // Optimized to prevent double drawing - if (sectors & 0x06) { g->p.x = x; g->p.y = y - b; drawpixel_clip(g); } // Upper upper - if (sectors & 0x60) { g->p.x = x; g->p.y = y + b; drawpixel_clip(g); } // Lower lower - if (sectors & 0x81) { g->p.x = x + b; g->p.y = y; drawpixel_clip(g); } // Right right - if (sectors & 0x18) { g->p.x = x - b; g->p.y = y; drawpixel_clip(g); } // Left left - - do { - if (sectors & 0x01) { g->p.x = x + b; g->p.y = y - a; drawpixel_clip(g); } // Upper right right - if (sectors & 0x02) { g->p.x = x + a; g->p.y = y - b; drawpixel_clip(g); } // Upper upper right - if (sectors & 0x04) { g->p.x = x - a; g->p.y = y - b; drawpixel_clip(g); } // Upper upper left - if (sectors & 0x08) { g->p.x = x - b; g->p.y = y - a; drawpixel_clip(g); } // Upper left left - if (sectors & 0x10) { g->p.x = x - b; g->p.y = y + a; drawpixel_clip(g); } // Lower left left - if (sectors & 0x20) { g->p.x = x - a; g->p.y = y + b; drawpixel_clip(g); } // Lower lower left - if (sectors & 0x40) { g->p.x = x + a; g->p.y = y + b; drawpixel_clip(g); } // Lower lower right - if (sectors & 0x80) { g->p.x = x + b; g->p.y = y + a; drawpixel_clip(g); } // Lower right right - if (P < 0) - P += 3 + 2*a++; - else - P += 5 + 2*(a++ - b--); - } while(a < b); - - if (sectors & 0xC0) { g->p.x = x + a; g->p.y = y + b; drawpixel_clip(g); } // Lower right - if (sectors & 0x03) { g->p.x = x + a; g->p.y = y - b; drawpixel_clip(g); } // Upper right - if (sectors & 0x30) { g->p.x = x - a; g->p.y = y + b; drawpixel_clip(g); } // Lower left - if (sectors & 0x0C) { g->p.x = x - a; g->p.y = y - b; drawpixel_clip(g); } // Upper left - - autoflush(g); - MUTEX_EXIT(g); - } -#endif - -#if GDISP_NEED_ARCSECTORS - void gdispGFillArcSectors(GDisplay *g, coord_t x, coord_t y, coord_t radius, uint8_t sectors, color_t color) { - coord_t a, b, P; - - MUTEX_ENTER(g); - - // Calculate intermediates - a = 1; // x in many explanations - b = radius; // y in many explanations - P = 4 - radius; - g->p.color = color; - - // Away we go using Bresenham's circle algorithm - // Optimized to prevent double drawing - if (sectors & 0x06) { g->p.x = x; g->p.y = y - b; drawpixel_clip(g); } // Upper upper - if (sectors & 0x60) { g->p.x = x; g->p.y = y + b; drawpixel_clip(g); } // Lower lower - if (sectors & 0x81) { // Center right - g->p.y = y; g->p.x = x; g->p.x1 = x + b; - if (sectors & 0x18) g->p.x -= b; // Left right - hline_clip(g); - } else if (sectors & 0x18) { // Left center - g->p.x = x - b; g->p.x1 = x; g->p.y = y; - hline_clip(g); - } - - do { - // Top half - switch(sectors & 0x0F) { - case 0x01: - g->p.y = y - a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g); - break; - case 0x02: - g->p.y = y - b; g->p.x = x; g->p.x1 = x + a; hline_clip(g); - g->p.y = y - a; g->p.x = x; g->p.x1 = x + a; hline_clip(g); - break; - case 0x03: - g->p.y = y - b; g->p.x = x; g->p.x1 = x + a; hline_clip(g); - g->p.y = y - a; g->p.x = x; g->p.x1 = x + b; hline_clip(g); - break; - case 0x04: - g->p.y = y - b; g->p.x = x - a; g->p.x1 = x; hline_clip(g); - g->p.y = y - a; g->p.x = x - a; g->p.x1 = x; hline_clip(g); - break; - case 0x05: - g->p.y = y - b; g->p.x = x - a; g->p.x1 = x; hline_clip(g); - g->p.y = y - a; g->p.x = x - a; g->p.x1 = x; hline_clip(g); - g->p.y = y - a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g); - break; - case 0x06: - g->p.y = y - b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g); - g->p.y = y - a; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g); - break; - case 0x07: - g->p.y = y - b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g); - g->p.y = y - a; g->p.x = x - a; g->p.x1 = x + b; hline_clip(g); - break; - case 0x08: - g->p.y = y - a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g); - break; - case 0x09: - g->p.y = y - a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g); - g->p.y = y - a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g); - break; - case 0x0A: - g->p.y = y - b; g->p.x = x; g->p.x1 = x + a; hline_clip(g); - g->p.y = y - a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g); - g->p.y = y - a; g->p.x = x; g->p.x1 = x + a; hline_clip(g); - break; - case 0x0B: - g->p.y = y - b; g->p.x = x; g->p.x1 = x + a; hline_clip(g); - g->p.y = y - a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g); - g->p.y = y - a; g->p.x = x; g->p.x1 = x + b; hline_clip(g); - break; - case 0x0C: - g->p.y = y - b; g->p.x = x - a; g->p.x1 = x; hline_clip(g); - g->p.y = y - a; g->p.x = x - b; g->p.x1 = x; hline_clip(g); - break; - case 0x0D: - g->p.y = y - b; g->p.x = x - a; g->p.x1 = x; hline_clip(g); - g->p.y = y - a; g->p.x = x - b; g->p.x1 = x; hline_clip(g); - g->p.y = y - a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g); - break; - case 0x0E: - g->p.y = y - b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g); - g->p.y = y - a; g->p.x = x - b; g->p.x1 = x + a; hline_clip(g); - break; - case 0x0F: - g->p.y = y - b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g); - g->p.y = y - a; g->p.x = x - b; g->p.x1 = x + b; hline_clip(g); - break; - } - - // Bottom half - switch((sectors & 0xF0)>>4) { - case 0x01: - g->p.y = y + a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g); - break; - case 0x02: - g->p.y = y + b; g->p.x = x - a; g->p.x1 = x; hline_clip(g); - g->p.y = y + a; g->p.x = x - a; g->p.x1 = x; hline_clip(g); - break; - case 0x03: - g->p.y = y + b; g->p.x = x - a; g->p.x1 = x; hline_clip(g); - g->p.y = y + a; g->p.x = x - b; g->p.x1 = x; hline_clip(g); - break; - case 0x04: - g->p.y = y + b; g->p.x = x; g->p.x1 = x + a; hline_clip(g); - g->p.y = y + a; g->p.x = x; g->p.x1 = x + a; hline_clip(g); - break; - case 0x05: - g->p.y = y + b; g->p.x = x; g->p.x1 = x + a; hline_clip(g); - g->p.y = y + a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g); - g->p.y = y + a; g->p.x = x; g->p.x1 = x + a; hline_clip(g); - break; - case 0x06: - g->p.y = y + b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g); - g->p.y = y + a; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g); - break; - case 0x07: - g->p.y = y + b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g); - g->p.y = y + a; g->p.x = x - b; g->p.x1 = x + a; hline_clip(g); - break; - case 0x08: - g->p.y = y + a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g); - break; - case 0x09: - g->p.y = y + a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g); - g->p.y = y + a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g); - break; - case 0x0A: - g->p.y = y + b; g->p.x = x - a; g->p.x1 = x; hline_clip(g); - g->p.y = y + a; g->p.x = x - a; g->p.x1 = x; hline_clip(g); - g->p.y = y + a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g); - break; - case 0x0B: - g->p.y = y + b; g->p.x = x - a; g->p.x1 = x; hline_clip(g); - g->p.y = y + a; g->p.x = x - b; g->p.x1 = x; hline_clip(g); - g->p.y = y + a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g); - break; - case 0x0C: - g->p.y = y + b; g->p.x = x; g->p.x1 = x + a; hline_clip(g); - g->p.y = y + a; g->p.x = x; g->p.x1 = x + b; hline_clip(g); - break; - case 0x0D: - g->p.y = y + b; g->p.x = x; g->p.x1 = x + a; hline_clip(g); - g->p.y = y + a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g); - g->p.y = y + a; g->p.x = x; g->p.x1 = x + b; hline_clip(g); - break; - case 0x0E: - g->p.y = y + b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g); - g->p.y = y + a; g->p.x = x - a; g->p.x1 = x + b; hline_clip(g); - break; - case 0x0F: - g->p.y = y + b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g); - g->p.y = y + a; g->p.x = x - b; g->p.x1 = x + b; hline_clip(g); - break; - } - - if (P < 0) - P += 3 + 2*a++; - else - P += 5 + 2*(a++ - b--); - } while(a < b); - - // Top half - if (sectors & 0x02) { g->p.y = y - a; g->p.x = x; g->p.x1 = x + a; hline_clip(g); } - else if (sectors & 0x01) { g->p.y = y - a; g->p.x = x + a; drawpixel_clip(g); } - if (sectors & 0x04) { g->p.y = y - a; g->p.x = x - a; g->p.x1 = x; hline_clip(g); } - else if (sectors & 0x08) { g->p.y = y - a; g->p.x = x - a; drawpixel_clip(g); } - - // Bottom half - if (sectors & 0x40) { g->p.y = y + a; g->p.x = x; g->p.x1 = x + a; hline_clip(g); } - else if (sectors & 0x80) { g->p.y = y + a; g->p.x = x + a; drawpixel_clip(g); } - if (sectors & 0x20) { g->p.y = y + a; g->p.x = x - a; g->p.x1 = x; hline_clip(g); } - else if (sectors & 0x10) { g->p.y = y + a; g->p.x = x - a; drawpixel_clip(g); } - - autoflush(g); - MUTEX_EXIT(g); - } -#endif - -#if GDISP_NEED_ARC - #if (!GMISC_NEED_FIXEDTRIG && !GMISC_NEED_FASTTRIG) || !GFX_USE_GMISC - #include <math.h> - #endif - - void gdispGDrawArc(GDisplay *g, coord_t x, coord_t y, coord_t radius, coord_t start, coord_t end, color_t color) { - coord_t a, b, P, sedge, eedge; - uint8_t full, sbit, ebit, tbit; - - // Normalize the angles - if (start < 0) - start -= (start/360-1)*360; - else if (start >= 360) - start %= 360; - if (end < 0) - end -= (end/360-1)*360; - else if (end >= 360) - end %= 360; - - sbit = 1<<(start/45); - ebit = 1<<(end/45); - full = 0; - if (start == end) { - full = 0xFF; - } else if (end < start) { - for(tbit=sbit<<1; tbit; tbit<<=1) full |= tbit; - for(tbit=ebit>>1; tbit; tbit>>=1) full |= tbit; - } else if (sbit < 0x80) { - for(tbit=sbit<<1; tbit < ebit; tbit<<=1) full |= tbit; - } - tbit = start%45 == 0 ? sbit : 0; - - MUTEX_ENTER(g); - g->p.color = color; - - if (full) { - // Draw full sectors - // Optimized to prevent double drawing - a = 1; - b = radius; - P = 4 - radius; - if (full & 0x60) { g->p.y = y+b; g->p.x = x; drawpixel_clip(g); } - if (full & 0x06) { g->p.y = y-b; g->p.x = x; drawpixel_clip(g); } - if (full & 0x81) { g->p.y = y; g->p.x = x+b; drawpixel_clip(g); } - if (full & 0x18) { g->p.y = y; g->p.x = x-b; drawpixel_clip(g); } - do { - if (full & 0x01) { g->p.x = x+b; g->p.y = y-a; drawpixel_clip(g); } - if (full & 0x02) { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } - if (full & 0x04) { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } - if (full & 0x08) { g->p.x = x-b; g->p.y = y-a; drawpixel_clip(g); } - if (full & 0x10) { g->p.x = x-b; g->p.y = y+a; drawpixel_clip(g); } - if (full & 0x20) { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } - if (full & 0x40) { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } - if (full & 0x80) { g->p.x = x+b; g->p.y = y+a; drawpixel_clip(g); } - if (P < 0) - P += 3 + 2*a++; - else - P += 5 + 2*(a++ - b--); - } while(a < b); - if (full & 0xC0) { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } - if (full & 0x0C) { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } - if (full & 0x03) { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } - if (full & 0x30) { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } - if (full == 0xFF) { - autoflush(g); - MUTEX_EXIT(g); - return; - } - } - - #if GFX_USE_GMISC && GMISC_NEED_FIXEDTRIG - sedge = NONFIXED(radius * ((sbit & 0x99) ? ffsin(start) : ffcos(start)) + FIXED0_5); - eedge = NONFIXED(radius * ((ebit & 0x99) ? ffsin(end) : ffcos(end)) + FIXED0_5); - #elif GFX_USE_GMISC && GMISC_NEED_FASTTRIG - sedge = round(radius * ((sbit & 0x99) ? fsin(start) : fcos(start))); - eedge = round(radius * ((ebit & 0x99) ? fsin(end) : fcos(end))); - #else - sedge = round(radius * ((sbit & 0x99) ? sin(start*M_PI/180) : cos(start*M_PI/180))); - eedge = round(radius * ((ebit & 0x99) ? sin(end*M_PI/180) : cos(end*M_PI/180))); - #endif - if (sbit & 0xB4) sedge = -sedge; - if (ebit & 0xB4) eedge = -eedge; - - if (sbit != ebit) { - // Draw start and end sectors - // Optimized to prevent double drawing - a = 1; - b = radius; - P = 4 - radius; - if ((sbit & 0x20) || (tbit & 0x40) || (ebit & 0x40)) { g->p.x = x; g->p.y = y+b; drawpixel_clip(g); } - if ((sbit & 0x02) || (tbit & 0x04) || (ebit & 0x04)) { g->p.x = x; g->p.y = y-b; drawpixel_clip(g); } - if ((sbit & 0x80) || (tbit & 0x01) || (ebit & 0x01)) { g->p.x = x+b; g->p.y = y; drawpixel_clip(g); } - if ((sbit & 0x08) || (tbit & 0x10) || (ebit & 0x10)) { g->p.x = x-b; g->p.y = y; drawpixel_clip(g); } - do { - if (((sbit & 0x01) && a >= sedge) || ((ebit & 0x01) && a <= eedge)) { g->p.x = x+b; g->p.y = y-a; drawpixel_clip(g); } - if (((sbit & 0x02) && a <= sedge) || ((ebit & 0x02) && a >= eedge)) { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } - if (((sbit & 0x04) && a >= sedge) || ((ebit & 0x04) && a <= eedge)) { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } - if (((sbit & 0x08) && a <= sedge) || ((ebit & 0x08) && a >= eedge)) { g->p.x = x-b; g->p.y = y-a; drawpixel_clip(g); } - if (((sbit & 0x10) && a >= sedge) || ((ebit & 0x10) && a <= eedge)) { g->p.x = x-b; g->p.y = y+a; drawpixel_clip(g); } - if (((sbit & 0x20) && a <= sedge) || ((ebit & 0x20) && a >= eedge)) { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } - if (((sbit & 0x40) && a >= sedge) || ((ebit & 0x40) && a <= eedge)) { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } - if (((sbit & 0x80) && a <= sedge) || ((ebit & 0x80) && a >= eedge)) { g->p.x = x+b; g->p.y = y+a; drawpixel_clip(g); } - if (P < 0) - P += 3 + 2*a++; - else - P += 5 + 2*(a++ - b--); - } while(a < b); - if (((sbit & 0x40) && a >= sedge) || ((ebit & 0x40) && a <= eedge) || ((sbit & 0x80) && a <= sedge) || ((ebit & 0x80) && a >= eedge)) - { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } - if (((sbit & 0x04) && a >= sedge) || ((ebit & 0x04) && a <= eedge) || ((sbit & 0x08) && a <= sedge) || ((ebit & 0x08) && a >= eedge)) - { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } - if (((sbit & 0x01) && a >= sedge) || ((ebit & 0x01) && a <= eedge) || ((sbit & 0x02) && a <= sedge) || ((ebit & 0x02) && a >= eedge)) - { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } - if (((sbit & 0x10) && a >= sedge) || ((ebit & 0x10) && a <= eedge) || ((sbit & 0x20) && a <= sedge) || ((ebit & 0x20) && a >= eedge)) - { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } - } else if (end < start) { - // Draw start/end sector where it is a non-internal angle - // Optimized to prevent double drawing - a = 1; - b = radius; - P = 4 - radius; - if ((sbit & 0x60) || (tbit & 0xC0)) { g->p.x = x; g->p.y = y+b; drawpixel_clip(g); } - if ((sbit & 0x06) || (tbit & 0x0C)) { g->p.x = x; g->p.y = y-b; drawpixel_clip(g); } - if ((sbit & 0x81) || (tbit & 0x03)) { g->p.x = x+b; g->p.y = y; drawpixel_clip(g); } - if ((sbit & 0x18) || (tbit & 0x30)) { g->p.x = x-b; g->p.y = y; drawpixel_clip(g); } - do { - if ((sbit & 0x01) && (a >= sedge || a <= eedge)) { g->p.x = x+b; g->p.y = y-a; drawpixel_clip(g); } - if ((sbit & 0x02) && (a <= sedge || a >= eedge)) { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } - if ((sbit & 0x04) && (a >= sedge || a <= eedge)) { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } - if ((sbit & 0x08) && (a <= sedge || a >= eedge)) { g->p.x = x-b; g->p.y = y-a; drawpixel_clip(g); } - if ((sbit & 0x10) && (a >= sedge || a <= eedge)) { g->p.x = x-b; g->p.y = y+a; drawpixel_clip(g); } - if ((sbit & 0x20) && (a <= sedge || a >= eedge)) { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } - if ((sbit & 0x40) && (a >= sedge || a <= eedge)) { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } - if ((sbit & 0x80) && (a <= sedge || a >= eedge)) { g->p.x = x+b; g->p.y = y+a; drawpixel_clip(g); } - if (P < 0) - P += 3 + 2*a++; - else - P += 5 + 2*(a++ - b--); - } while(a < b); - if (((sbit & 0x04) && (a >= sedge || a <= eedge)) || ((sbit & 0x08) && (a <= sedge || a >= eedge))) - { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } - if (((sbit & 0x40) && (a >= sedge || a <= eedge)) || ((sbit & 0x80) && (a <= sedge || a >= eedge))) - { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } - if (((sbit & 0x01) && (a >= sedge || a <= eedge)) || ((sbit & 0x02) && (a <= sedge || a >= eedge))) - { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } - if (((sbit & 0x10) && (a >= sedge || a <= eedge)) || ((sbit & 0x20) && (a <= sedge || a >= eedge))) - { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } - } else { - // Draw start/end sector where it is a internal angle - // Optimized to prevent double drawing - a = 1; - b = radius; - P = 4 - radius; - if (((sbit & 0x20) && !eedge) || ((sbit & 0x40) && !sedge)) { g->p.x = x; g->p.y = y+b; drawpixel_clip(g); } - if (((sbit & 0x02) && !eedge) || ((sbit & 0x04) && !sedge)) { g->p.x = x; g->p.y = y-b; drawpixel_clip(g); } - if (((sbit & 0x80) && !eedge) || ((sbit & 0x01) && !sedge)) { g->p.x = x+b; g->p.y = y; drawpixel_clip(g); } - if (((sbit & 0x08) && !eedge) || ((sbit & 0x10) && !sedge)) { g->p.x = x-b; g->p.y = y; drawpixel_clip(g); } - do { - if (((sbit & 0x01) && a >= sedge && a <= eedge)) { g->p.x = x+b; g->p.y = y-a; drawpixel_clip(g); } - if (((sbit & 0x02) && a <= sedge && a >= eedge)) { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } - if (((sbit & 0x04) && a >= sedge && a <= eedge)) { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } - if (((sbit & 0x08) && a <= sedge && a >= eedge)) { g->p.x = x-b; g->p.y = y-a; drawpixel_clip(g); } - if (((sbit & 0x10) && a >= sedge && a <= eedge)) { g->p.x = x-b; g->p.y = y+a; drawpixel_clip(g); } - if (((sbit & 0x20) && a <= sedge && a >= eedge)) { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } - if (((sbit & 0x40) && a >= sedge && a <= eedge)) { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } - if (((sbit & 0x80) && a <= sedge && a >= eedge)) { g->p.x = x+b; g->p.y = y+a; drawpixel_clip(g); } - if (P < 0) - P += 3 + 2*a++; - else - P += 5 + 2*(a++ - b--); - } while(a < b); - if (((sbit & 0x04) && a >= sedge && a <= eedge) || ((sbit & 0x08) && a <= sedge && a >= eedge)) - { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } - if (((sbit & 0x40) && a >= sedge && a <= eedge) || ((sbit & 0x80) && a <= sedge && a >= eedge)) - { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } - if (((sbit & 0x01) && a >= sedge && a <= eedge) || ((sbit & 0x02) && a <= sedge && a >= eedge)) - { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } - if (((sbit & 0x10) && a >= sedge && a <= eedge) || ((sbit & 0x20) && a <= sedge && a >= eedge)) - { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } - } - - autoflush(g); - MUTEX_EXIT(g); - } -#endif - -#if GDISP_NEED_ARC - void gdispGFillArc(GDisplay *g, coord_t x, coord_t y, coord_t radius, coord_t start, coord_t end, color_t color) { - coord_t a, b, P; - coord_t sy, ey; - fixed sxa, sxb, sxd, exa, exb, exd; - uint8_t qtr; - - MUTEX_ENTER(g); - - // Do the trig to get the formulas for the start and end lines. - sxa = exa = FIXED(x)+FIXED0_5; - #if GFX_USE_GMISC && GMISC_NEED_FIXEDTRIG - sxb = radius*ffcos(start); sy = -NONFIXED(radius*ffsin(start) + FIXED0_5); - exb = radius*ffcos(end); ey = -NONFIXED(radius*ffsin(end) + FIXED0_5); - #elif GFX_USE_GMISC && GMISC_NEED_FASTTRIG - sxb = FP2FIXED(radius*fcos(start)); sy = -round(radius*fsin(start)); - exb = FP2FIXED(radius*fcos(end)); ey = -round(radius*fsin(end)); - #else - sxb = FP2FIXED(radius*cos(start*M_PI/180)); sy = -round(radius*sin(start*M_PI/180)); - exb = FP2FIXED(radius*cos(end*M_PI/180)); ey = -round(radius*sin(end*M_PI/180)); - #endif - sxd = sy ? sxb/sy : sxb; - exd = ey ? exb/ey : exb; - - // Calculate which quarters and which direction we are traveling - qtr = 0; - if (sxb > 0) qtr |= 0x01; // S1=0001(1), S2=0000(0), S3=0010(2), S4=0011(3) - if (sy > 0) qtr |= 0x02; - if (exb > 0) qtr |= 0x04; // E1=0100(4), E2=0000(0), E3=1000(8), E4=1100(12) - if (ey > 0) qtr |= 0x08; - if (sy > ey) qtr |= 0x10; // order of start and end lines - - // Calculate intermediates - a = 1; - b = radius; - P = 4 - radius; - g->p.color = color; - sxb += sxa; - exb += exa; - - // Away we go using Bresenham's circle algorithm - // This is optimized to prevent overdrawing by drawing a line only when a variable is about to change value - - switch(qtr) { - case 0: // S2E2 sy <= ey - case 1: // S1E2 sy <= ey - if (ey && sy) { - g->p.x = x; g->p.x1 = x; // E2S - sxa -= sxd; exa -= exd; - } else if (sy) { - g->p.x = x-b; g->p.x1 = x; // C2S - sxa -= sxd; - } else if (ey) { - g->p.x = x; g->p.x1 = x+b; // E2C - exa -= exd; - } else { - g->p.x = x-b; g->p.x1 = x+b; // C2C - } - g->p.y = y; - hline_clip(g); - do { - if (-a >= ey) { - g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = NONFIXED(sxa); hline_clip(g); // E2S - sxa -= sxd; exa -= exd; - } else if (-a >= sy) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S - sxa -= sxd; - } else if (qtr & 1) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - if (P < 0) { - P += 3 + 2*a++; - } else { - if (-b >= ey) { - g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = NONFIXED(sxb); hline_clip(g); // E2S - sxb += sxd; exb += exd; - } else if (-b >= sy) { - g->p.y = y-b; g->p.x = x-a; g->p.x1 = NONFIXED(sxb); hline_clip(g); // C2S - sxb += sxd; - } else if (qtr & 1) { - g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C - } - P += 5 + 2*(a++ - b--); - } - } while(a < b); - if (-a >= ey) { - g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = NONFIXED(sxa); hline_clip(g); // E2S - } else if (-a >= sy) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S - } else if (qtr & 1) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - break; - - case 2: // S3E2 sy <= ey - case 3: // S4E2 sy <= ey - case 6: // S3E1 sy <= ey - case 7: // S4E1 sy <= ey - case 18: // S3E2 sy > ey - case 19: // S4E2 sy > ey - case 22: // S3E1 sy > ey - case 23: // S4E1 sy > ey - g->p.y = y; g->p.x = x; g->p.x1 = x+b; hline_clip(g); // SE2C - sxa += sxd; exa -= exd; - do { - if (-a >= ey) { - g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C - exa -= exd; - } else if (!(qtr & 4)) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - if (a <= sy) { - g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C - sxa += sxd; - } else if (!(qtr & 1)) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - if (P < 0) { - P += 3 + 2*a++; - } else { - if (-b >= ey) { - g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = x+a; hline_clip(g); // E2C - exb += exd; - } else if (!(qtr & 4)) { - g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C - } - if (b <= sy) { - g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = x+a; hline_clip(g); // S2C - sxb -= sxd; - } else if (!(qtr & 1)) { - g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C - } - P += 5 + 2*(a++ - b--); - } - } while(a < b); - if (-a >= ey) { - g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C - } else if (!(qtr & 4)) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - if (a <= sy) { - g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+a; hline_clip(g); // S2C - } else if (!(qtr & 1)) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+a; hline_clip(g); // C2C - } - break; - - case 4: // S2E1 sy <= ey - case 5: // S1E1 sy <= ey - g->p.y = y; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - do { - if (-a >= ey) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S - g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C - sxa -= sxd; exa -= exd; - } else if (-a >= sy) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S - sxa -= sxd; - } else if (qtr & 1) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - if (P < 0) { - P += 3 + 2*a++; - } else { - if (-b >= ey) { - g->p.y = y-b; g->p.x = x-a; g->p.x1 = NONFIXED(sxb); hline_clip(g); // C2S - g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = x+a; hline_clip(g); // E2C - sxb += sxd; exb += exd; - } else if (-b >= sy) { - g->p.y = y-b; g->p.x = x-a; g->p.x1 = NONFIXED(sxb); hline_clip(g); // C2S - sxb += sxd; - } else if (qtr & 1) { - g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C - } - g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C - P += 5 + 2*(a++ - b--); - } - } while(a < b); - if (-a >= ey) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S - g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C - } else if (-a >= sy) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S - } else if (qtr & 1) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C - break; - - case 8: // S2E3 sy <= ey - case 9: // S1E3 sy <= ey - case 12: // S2E4 sy <= ey - case 13: // S1E4 sy <= ey - case 24: // S2E3 sy > ey - case 25: // S1E3 sy > ey - case 28: // S2E3 sy > ey - case 29: // S1E3 sy > ey - g->p.y = y; g->p.x = x-b; g->p.x1 = x; hline_clip(g); // C2SE - sxa -= sxd; exa += exd; - do { - if (-a >= sy) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S - sxa -= sxd; - } else if (qtr & 1) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - if (a <= ey) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E - exa += exd; - } else if (qtr & 4) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - if (P < 0) { - P += 3 + 2*a++; - } else { - if (-b >= sy) { - g->p.y = y-b; g->p.x = x-a; g->p.x1 = NONFIXED(sxb); hline_clip(g); // C2S - sxb += sxd; - } else if (qtr & 1) { - g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C - } - if (b <= ey) { - g->p.y = y+b; g->p.x = x-a; g->p.x1 = NONFIXED(exb); hline_clip(g); // C2E - exb -= exd; - } else if (qtr & 4) { - g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C - } - P += 5 + 2*(a++ - b--); - } - } while(a < b); - if (-a >= sy) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S - } else if (qtr & 1) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - if (a <= ey) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E - } else if (qtr & 4) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+a; hline_clip(g); // C2C - } - break; - - case 10: // S3E3 sy <= ey - case 14: // S3E4 sy <= ey - g->p.y = y; g->p.x = x; drawpixel_clip(g); // S2E - sxa += sxd; exa += exd; - do { - if (a <= sy) { - g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = NONFIXED(exa); hline_clip(g); // S2E - sxa += sxd; exa += exd; - } else if (a <= ey) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E - exa += exd; - } else if (qtr & 4) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - if (P < 0) { - P += 3 + 2*a++; - } else { - if (b <= sy) { - g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = NONFIXED(exb); hline_clip(g); // S2E - sxb -= sxd; exb -= exd; - } else if (b <= ey) { - g->p.y = y+b; g->p.x = x-a; g->p.x1 = NONFIXED(exb); hline_clip(g); // C2E - exb -= exd; - } else if (qtr & 4) { - g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C - } - P += 5 + 2*(a++ - b--); - } - } while(a < b); - if (a <= sy) { - g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = NONFIXED(exa); hline_clip(g); // S2E - } else if (a <= ey) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E - } else if (qtr & 4) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - break; - - case 11: // S4E3 sy <= ey - case 15: // S4E4 sy <= ey - g->p.y = y; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - do { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - if (a <= sy) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E - g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C - sxa += sxd; exa += exd; - } else if (a <= ey) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E - exa += exd; - } else if (qtr & 4) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - if (P < 0) { - P += 3 + 2*a++; - } else { - g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C - if (b <= sy) { - g->p.y = y+b; g->p.x = x-a; g->p.x1 = NONFIXED(exb); hline_clip(g); // C2E - g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = x+a; hline_clip(g); // S2C - sxb -= sxd; exb -= exd; - } else if (b <= ey) { - g->p.y = y+b; g->p.x = x-a; g->p.x1 = NONFIXED(exb); hline_clip(g); // C2E - exb -= exd; - } else if (qtr & 4) { - g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C - } - P += 5 + 2*(a++ - b--); - } - } while(a < b); - g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - if (a <= sy) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E - g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C - } else if (a <= ey) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E - } else if (qtr & 4) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - break; - - case 16: // S2E2 sy > ey - case 20: // S2E1 sy > ey - g->p.y = y; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - sxa -= sxd; exa -= exd; - do { - if (-a >= sy) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S - g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C - sxa -= sxd; exa -= exd; - } else if (-a >= ey) { - g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C - exa -= exd; - } else if (!(qtr & 4)){ - g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - if (P < 0) { - P += 3 + 2*a++; - } else { - if (-b >= sy) { - g->p.y = y-b; g->p.x = x-a; g->p.x1 = NONFIXED(sxb); hline_clip(g); // C2S - g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = x+a; hline_clip(g); // E2C - sxb += sxd; exb += exd; - } else if (-b >= ey) { - g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = x+a; hline_clip(g); // E2C - exb += exd; - } else if (!(qtr & 4)){ - g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C - } - g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C - P += 5 + 2*(a++ - b--); - } - } while(a < b); - if (-a >= sy) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S - g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C - } else if (-a >= ey) { - g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C - } else if (!(qtr & 4)){ - g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - break; - - case 17: // S1E2 sy > ey - case 21: // S1E1 sy > ey - if (sy) { - g->p.x = x; g->p.x1 = x; // E2S - sxa -= sxd; exa -= exd; - } else { - g->p.x = x; g->p.x1 = x+b; // E2C - exa -= exd; - } - g->p.y = y; - hline_clip(g); - do { - if (-a >= sy) { - g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = NONFIXED(sxa); hline_clip(g); // E2S - sxa -= sxd; exa -= exd; - } else if (-a >= ey) { - g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C - exa -= exd; - } else if (!(qtr & 4)) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - if (P < 0) { - P += 3 + 2*a++; - } else { - if (-b >= sy) { - g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = NONFIXED(sxb); hline_clip(g); // E2S - sxb += sxd; exb += exd; - } else if (-b >= ey) { - g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = x+a; hline_clip(g); // E2C - exb += exd; - } else if (!(qtr & 4)) { - g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C - } - P += 5 + 2*(a++ - b--); - } - } while(a < b); - if (-a >= sy) { - g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = NONFIXED(sxa); hline_clip(g); // E2S - } else if (-a >= ey) { - g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C - } else if (!(qtr & 4)) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - break; - - case 26: // S3E3 sy > ey - case 27: // S4E3 sy > ey - g->p.y = y; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - do { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - if (a <= ey) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E - g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C - sxa += sxd; exa += exd; - } else if (a <= sy) { - g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C - sxa += sxd; - } else if (!(qtr & 1)) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - if (P < 0) { - P += 3 + 2*a++; - } else { - g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C - if (b <= ey) { - g->p.y = y+b; g->p.x = x-a; g->p.x1 = NONFIXED(exb); hline_clip(g); // C2E - g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = x+a; hline_clip(g); // S2C - sxb -= sxd; exb -= exd; - } else if (b <= sy) { - g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = x+a; hline_clip(g); // S2C - sxb -= sxd; - } else if (!(qtr & 1)) { - g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C - } - P += 5 + 2*(a++ - b--); - } - } while(a < b); - g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - if (a <= ey) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E - g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C - } else if (a <= sy) { - g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C - } else if (!(qtr & 4)) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - break; - - case 30: // S3E4 sy > ey - case 31: // S4E4 sy > ey - do { - if (a <= ey) { - g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = NONFIXED(exa); hline_clip(g); // S2E - sxa += sxd; exa += exd; - } else if (a <= sy) { - g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C - sxa += sxd; - } else if (!(qtr & 1)) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - if (P < 0) { - P += 3 + 2*a++; - } else { - if (b <= ey) { - g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = NONFIXED(exb); hline_clip(g); // S2E - sxb -= sxd; exb -= exd; - } else if (b <= sy) { - g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = x+a; hline_clip(g); // S2C - sxb -= sxd; - } else if (!(qtr & 1)) { - g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C - } - P += 5 + 2*(a++ - b--); - } - } while(a < b); - if (a <= ey) { - g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C - } else if (a <= sy) { - g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C - } else if (!(qtr & 4)) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - break; - } - - autoflush(g); - MUTEX_EXIT(g); - } -#endif - -#if GDISP_NEED_ARC || GDISP_NEED_ARCSECTORS - void gdispGDrawRoundedBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t radius, color_t color) { - if (2*radius > cx || 2*radius > cy) { - gdispGDrawBox(g, x, y, cx, cy, color); - return; - } - - #if GDISP_NEED_ARCSECTORS - gdispGDrawArcSectors(g, x+radius, y+radius, radius, 0x0C, color); - gdispGDrawArcSectors(g, x+cx-1-radius, y+radius, radius, 0x03, color); - gdispGDrawArcSectors(g, x+cx-1-radius, y+cy-1-radius, radius, 0xC0, color); - gdispGDrawArcSectors(g, x+radius, y+cy-1-radius, radius, 0x30, color); - #else - gdispGDrawArc(g, x+radius, y+radius, radius, 90, 180, color); - gdispGDrawArc(g, x+cx-1-radius, y+radius, radius, 0, 90, color); - gdispGDrawArc(g, x+cx-1-radius, y+cy-1-radius, radius, 270, 360, color); - gdispGDrawArc(g, x+radius, y+cy-1-radius, radius, 180, 270, color); - #endif - gdispGDrawLine(g, x+radius+1, y, x+cx-2-radius, y, color); - gdispGDrawLine(g, x+cx-1, y+radius+1, x+cx-1, y+cy-2-radius, color); - gdispGDrawLine(g, x+radius+1, y+cy-1, x+cx-2-radius, y+cy-1, color); - gdispGDrawLine(g, x, y+radius+1, x, y+cy-2-radius, color); - } -#endif - -#if GDISP_NEED_ARC || GDISP_NEED_ARCSECTORS - void gdispGFillRoundedBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t radius, color_t color) { - coord_t radius2; - - radius2 = radius*2; - if (radius2 > cx || radius2 > cy) { - gdispGFillArea(g, x, y, cx, cy, color); - return; - } - #if GDISP_NEED_ARCSECTORS - gdispGFillArcSectors(g, x+radius, y+radius, radius, 0x0C, color); - gdispGFillArcSectors(g, x+cx-1-radius, y+radius, radius, 0x03, color); - gdispGFillArcSectors(g, x+cx-1-radius, y+cy-1-radius, radius, 0xC0, color); - gdispGFillArcSectors(g, x+radius, y+cy-1-radius, radius, 0x30, color); - #else - gdispGFillArc(g, x+radius, y+radius, radius, 90, 180, color); - gdispGFillArc(g, x+cx-1-radius, y+radius, radius, 0, 90, color); - gdispGFillArc(g, x+cx-1-radius, y+cy-1-radius, radius, 270, 360, color); - gdispGFillArc(g, x+radius, y+cy-1-radius, radius, 180, 270, color); - #endif - gdispGFillArea(g, x+radius+1, y, cx-radius2, radius, color); - gdispGFillArea(g, x+radius+1, y+cy-radius, cx-radius2, radius, color); - gdispGFillArea(g, x, y+radius, cx, cy-radius2, color); - } -#endif - -#if GDISP_NEED_PIXELREAD - color_t gdispGGetPixelColor(GDisplay *g, coord_t x, coord_t y) { - color_t c; - - /* Always synchronous as it must return a value */ - MUTEX_ENTER(g); - #if GDISP_HARDWARE_PIXELREAD - #if GDISP_HARDWARE_PIXELREAD == HARDWARE_AUTODETECT - if (gvmt(g)->get) - #endif - { - // Best is direct pixel read - g->p.x = x; - g->p.y = y; - c = gdisp_lld_get_pixel_color(g); - MUTEX_EXIT(g); - return c; - } - #endif - #if GDISP_HARDWARE_PIXELREAD != TRUE && GDISP_HARDWARE_STREAM_READ - #if GDISP_HARDWARE_STREAM_READ == HARDWARE_AUTODETECT - if (gvmt(g)->readcolor) - #endif - { - // Next best is hardware streaming - g->p.x = x; - g->p.y = y; - g->p.cx = 1; - g->p.cy = 1; - gdisp_lld_read_start(g); - c = gdisp_lld_read_color(g); - gdisp_lld_read_stop(g); - MUTEX_EXIT(g); - return c; - } - #endif - #if GDISP_HARDWARE_PIXELREAD != TRUE && GDISP_HARDWARE_STREAM_READ != TRUE - #if !GDISP_HARDWARE_PIXELREAD && !GDISP_HARDWARE_STREAM_READ - // Worst is "not possible" - #error "GDISP: GDISP_NEED_PIXELREAD has been set but there is no hardware support for reading the display" - #endif - MUTEX_EXIT(g); - return 0; - #endif - } -#endif - -#if GDISP_NEED_SCROLL - void gdispGVerticalScroll(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, int lines, color_t bgcolor) { - coord_t abslines; - #if GDISP_HARDWARE_SCROLL != TRUE - coord_t fy, dy, ix, fx, i, j; - #endif - - MUTEX_ENTER(g); - #if NEED_CLIPPING - #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT - if (!gvmt(g)->setclip) - #endif - { - if (x < g->clipx0) { cx -= g->clipx0 - x; x = g->clipx0; } - if (y < g->clipy0) { cy -= g->clipy0 - y; y = g->clipy0; } - if (!lines || cx <= 0 || cy <= 0 || x >= g->clipx1 || y >= g->clipy1) { MUTEX_EXIT(g); return; } - if (x+cx > g->clipx1) cx = g->clipx1 - x; - if (y+cy > g->clipy1) cy = g->clipy1 - y; - } - #endif - - abslines = lines < 0 ? -lines : lines; - if (abslines >= cy) { - abslines = cy; - cy = 0; - } else { - // Best is hardware scroll - #if GDISP_HARDWARE_SCROLL - #if GDISP_HARDWARE_SCROLL == HARDWARE_AUTODETECT - if (gvmt(g)->vscroll) - #endif - { - g->p.x = x; - g->p.y = y; - g->p.cx = cx; - g->p.cy = cy; - g->p.y1 = lines; - g->p.color = bgcolor; - gdisp_lld_vertical_scroll(g); - cy -= abslines; - } - #if GDISP_HARDWARE_SCROLL == HARDWARE_AUTODETECT - else - #endif - #elif GDISP_LINEBUF_SIZE == 0 - #error "GDISP: GDISP_NEED_SCROLL is set but there is no hardware support and GDISP_LINEBUF_SIZE is zero." - #endif - - // Scroll Emulation - #if GDISP_HARDWARE_SCROLL != TRUE - { - cy -= abslines; - if (lines < 0) { - fy = y+cy-1; - dy = -1; - } else { - fy = y; - dy = 1; - } - // Move the screen - one line at a time - for(i = 0; i < cy; i++, fy += dy) { - - // Handle where the buffer is smaller than a line - for(ix=0; ix < cx; ix += GDISP_LINEBUF_SIZE) { - - // Calculate the data we can move in one operation - fx = cx - ix; - if (fx > GDISP_LINEBUF_SIZE) - fx = GDISP_LINEBUF_SIZE; - - // Read one line of data from the screen - - // Best line read is hardware streaming - #if GDISP_HARDWARE_STREAM_READ - #if GDISP_HARDWARE_STREAM_READ == HARDWARE_AUTODETECT - if (gvmt(g)->readstart) - #endif - { - g->p.x = x+ix; - g->p.y = fy+lines; - g->p.cx = fx; - g->p.cy = 1; - gdisp_lld_read_start(g); - for(j=0; j < fx; j++) - g->linebuf[j] = gdisp_lld_read_color(g); - gdisp_lld_read_stop(g); - } - #if GDISP_HARDWARE_STREAM_READ == HARDWARE_AUTODETECT - else - #endif - #endif - - // Next best line read is single pixel reads - #if GDISP_HARDWARE_STREAM_READ != TRUE && GDISP_HARDWARE_PIXELREAD - #if GDISP_HARDWARE_PIXELREAD == HARDWARE_AUTODETECT - if (gvmt(g)->get) - #endif - { - for(j=0; j < fx; j++) { - g->p.x = x+ix+j; - g->p.y = fy+lines; - g->linebuf[j] = gdisp_lld_get_pixel_color(g); - } - } - #if GDISP_HARDWARE_PIXELREAD == HARDWARE_AUTODETECT - else { - // Worst is "not possible" - MUTEX_EXIT(g); - return; - } - #endif - #endif - - // Worst is "not possible" - #if !GDISP_HARDWARE_STREAM_READ && !GDISP_HARDWARE_PIXELREAD - #error "GDISP: GDISP_NEED_SCROLL is set but there is no hardware support for scrolling or reading pixels." - #endif - - // Write that line to the new location - - // Best line write is hardware bitfills - #if GDISP_HARDWARE_BITFILLS - #if GDISP_HARDWARE_BITFILLS == HARDWARE_AUTODETECT - if (gvmt(g)->blit) - #endif - { - g->p.x = x+ix; - g->p.y = fy; - g->p.cx = fx; - g->p.cy = 1; - g->p.x1 = 0; - g->p.y1 = 0; - g->p.x2 = fx; - g->p.ptr = (void *)g->linebuf; - gdisp_lld_blit_area(g); - } - #if GDISP_HARDWARE_BITFILLS == HARDWARE_AUTODETECT - else - #endif - #endif - - // Next best line write is hardware streaming - #if GDISP_HARDWARE_BITFILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE - #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT - if (gvmt(g)->writestart) - #endif - { - g->p.x = x+ix; - g->p.y = fy; - g->p.cx = fx; - g->p.cy = 1; - gdisp_lld_write_start(g); - #if GDISP_HARDWARE_STREAM_POS - gdisp_lld_write_pos(g); - #endif - for(j = 0; j < fx; j++) { - g->p.color = g->linebuf[j]; - gdisp_lld_write_color(g); - } - gdisp_lld_write_stop(g); - } - #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT - else - #endif - #endif - - // Next best line write is drawing pixels in combination with filling - #if GDISP_HARDWARE_BITFILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_FILLS && GDISP_HARDWARE_DRAWPIXEL - // We don't need to test for auto-detect on drawpixel as we know we have it because we don't have streaming. - #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT - if (gvmt(g)->fill) - #endif - { - g->p.y = fy; - g->p.cy = 1; - g->p.x = x+ix; - g->p.cx = 1; - for(j = 0; j < fx; ) { - g->p.color = g->linebuf[j]; - if (j + g->p.cx < fx && g->linebuf[j] == g->linebuf[j + g->p.cx]) - g->p.cx++; - else if (g->p.cx == 1) { - gdisp_lld_draw_pixel(g); - j++; - g->p.x++; - } else { - gdisp_lld_fill_area(g); - j += g->p.cx; - g->p.x += g->p.cx; - g->p.cx = 1; - } - } - } - #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT - else - #endif - #endif - - // Worst line write is drawing pixels - #if GDISP_HARDWARE_BITFILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_DRAWPIXEL - // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming - //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT - // if (gvmt(g)->pixel) - //#endif - { - g->p.y = fy; - for(g->p.x = x+ix, j = 0; j < fx; g->p.x++, j++) { - g->p.color = g->linebuf[j]; - gdisp_lld_draw_pixel(g); - } - } - #endif - } - } - } - #endif - } - - /* fill the remaining gap */ - g->p.x = x; - g->p.y = lines > 0 ? (y+cy) : y; - g->p.cx = cx; - g->p.cy = abslines; - g->p.color = bgcolor; - fillarea(g); - autoflush_stopdone(g); - MUTEX_EXIT(g); - } -#endif - -#if GDISP_NEED_CONTROL - #if GDISP_HARDWARE_CONTROL - void gdispGControl(GDisplay *g, unsigned what, void *value) { - #if GDISP_HARDWARE_CONTROL == HARDWARE_AUTODETECT - if (!gvmt(g)->control) - return; - #endif - MUTEX_ENTER(g); - g->p.x = what; - g->p.ptr = value; - if (what == GDISP_CONTROL_ORIENTATION) { - switch ((orientation_t) value) { - case GDISP_ROTATE_LANDSCAPE: - g->p.ptr = g->g.Width >= g->g.Height ? (void *)GDISP_ROTATE_0 : (void *)GDISP_ROTATE_90; - break; - case GDISP_ROTATE_PORTRAIT: - g->p.ptr = g->g.Width >= g->g.Height ? (void *)GDISP_ROTATE_90 : (void *)GDISP_ROTATE_0; - break; - default: - break; - } - } - gdisp_lld_control(g); - #if GDISP_NEED_CLIP || GDISP_NEED_VALIDATION - if (what == GDISP_CONTROL_ORIENTATION) { - // Best is hardware clipping - #if GDISP_HARDWARE_CLIP - #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT - if (gvmt(g)->setclip) - #endif - { - g->p.x = 0; - g->p.y = 0; - g->p.cx = g->g.Width; - g->p.cy = g->g.Height; - gdisp_lld_set_clip(g); - } - #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT - else - #endif - #endif - - // Worst is software clipping - #if GDISP_HARDWARE_CLIP != TRUE - { - g->clipx0 = 0; - g->clipy0 = 0; - g->clipx1 = g->g.Width; - g->clipy1 = g->g.Height; - } - #endif - } - #endif - MUTEX_EXIT(g); - } - #else - void gdispGControl(GDisplay *g, unsigned what, void *value) { - (void)g; - (void)what; - (void)value; - /* Ignore everything */ - } - #endif -#endif - -#if GDISP_NEED_QUERY - #if GDISP_HARDWARE_QUERY - void *gdispGQuery(GDisplay *g, unsigned what) { - void *res; - - #if GDISP_HARDWARE_QUERY == HARDWARE_AUTODETECT - if (!gvmt(g)->query) - return -1; - #endif - MUTEX_ENTER(g); - g->p.x = (coord_t)what; - res = gdisp_lld_query(g); - MUTEX_EXIT(g); - return res; - } - #else - void *gdispGQuery(GDisplay *g, unsigned what) { - (void) what; - return (void *)-1; - } - #endif -#endif - -/*===========================================================================*/ -/* High Level Driver Routines. */ -/*===========================================================================*/ - -void gdispGDrawBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, color_t color) { - if (cx <= 0 || cy <= 0) return; - cx = x+cx-1; cy = y+cy-1; // cx, cy are now the end point. - - MUTEX_ENTER(g); - - g->p.color = color; - - if (cx - x > 2) { - g->p.x = x; g->p.y = y; g->p.x1 = cx; hline_clip(g); - if (y != cy) { - g->p.x = x; g->p.y = cy; g->p.x1 = cx; hline_clip(g); - if (cy - y > 2) { - y++; cy--; - g->p.x = x; g->p.y = y; g->p.y1 = cy; vline_clip(g); - g->p.x = cx; g->p.y = y; g->p.y1 = cy; vline_clip(g); - } - } - } else { - g->p.x = x; g->p.y = y; g->p.y1 = cy; vline_clip(g); - if (x != cx) { - g->p.x = cx; g->p.y = y; g->p.y1 = cy; vline_clip(g); - } - } - - autoflush(g); - MUTEX_EXIT(g); -} - -#if GDISP_NEED_CONVEX_POLYGON - void gdispGDrawPoly(GDisplay *g, coord_t tx, coord_t ty, const point *pntarray, unsigned cnt, color_t color) { - const point *epnt, *p; - - epnt = &pntarray[cnt-1]; - - MUTEX_ENTER(g); - g->p.color = color; - for(p = pntarray; p < epnt; p++) { - g->p.x=tx+p->x; g->p.y=ty+p->y; g->p.x1=tx+p[1].x; g->p.y1=ty+p[1].y; line_clip(g); - } - g->p.x=tx+p->x; g->p.y=ty+p->y; g->p.x1=tx+pntarray->x; g->p.y1=ty+pntarray->y; line_clip(g); - - autoflush(g); - MUTEX_EXIT(g); - } - - void gdispGFillConvexPoly(GDisplay *g, coord_t tx, coord_t ty, const point *pntarray, unsigned cnt, color_t color) { - const point *lpnt, *rpnt, *epnts; - fixed lx, rx, lk, rk; - coord_t y, ymax, lxc, rxc; - - epnts = &pntarray[cnt-1]; - - /* Find a top point */ - rpnt = pntarray; - for(lpnt=pntarray+1; lpnt <= epnts; lpnt++) { - if (lpnt->y < rpnt->y) - rpnt = lpnt; - } - lx = rx = FIXED(rpnt->x); - y = rpnt->y; - - /* Work out the slopes of the two attached line segs */ - for (lpnt = rpnt <= pntarray ? epnts : rpnt-1; lpnt->y == y; cnt--) { - if (!cnt) return; - lx = FIXED(lpnt->x); - lpnt = lpnt <= pntarray ? epnts : lpnt-1; - } - for (rpnt = rpnt >= epnts ? pntarray : rpnt+1; rpnt->y == y; cnt--) { - if (!cnt) return; - rx = FIXED(rpnt->x); - rpnt = rpnt >= epnts ? pntarray : rpnt+1; - } - lk = (FIXED(lpnt->x) - lx) / (lpnt->y - y); - rk = (FIXED(rpnt->x) - rx) / (rpnt->y - y); - - MUTEX_ENTER(g); - g->p.color = color; - while(1) { - /* Determine our boundary */ - ymax = rpnt->y < lpnt->y ? rpnt->y : lpnt->y; - - /* Scan down the line segments until we hit a boundary */ - for(; y < ymax; y++) { - lxc = NONFIXED(lx); - rxc = NONFIXED(rx); - /* - * Doesn't print the right hand point in order to allow polygon joining. - * Also ensures that we draw from left to right with the minimum number - * of pixels. - */ - if (lxc < rxc) { - g->p.x=tx+lxc; g->p.y=ty+y; g->p.x1=tx+rxc-1; hline_clip(g); - } else if (lxc > rxc) { - g->p.x=tx+rxc; g->p.y=ty+y; g->p.x1=tx+lxc-1; hline_clip(g); - } - - lx += lk; - rx += rk; - } - - if (!cnt) { - autoflush(g); - MUTEX_EXIT(g); - return; - } - cnt--; - - /* Replace the appropriate point */ - if (ymax == lpnt->y) { - for (lpnt = lpnt <= pntarray ? epnts : lpnt-1; lpnt->y == y; cnt--) { - if (!cnt) { - autoflush(g); - MUTEX_EXIT(g); - return; - } - lx = FIXED(lpnt->x); - lpnt = lpnt <= pntarray ? epnts : lpnt-1; - } - lk = (FIXED(lpnt->x) - lx) / (lpnt->y - y); - } else { - for (rpnt = rpnt >= epnts ? pntarray : rpnt+1; rpnt->y == y; cnt--) { - if (!cnt) { - autoflush(g); - MUTEX_EXIT(g); - return; - } - rx = FIXED(rpnt->x); - rpnt = rpnt >= epnts ? pntarray : rpnt+1; - } - rk = (FIXED(rpnt->x) - rx) / (rpnt->y - y); - } - } - } - - static int32_t rounding_div(const int32_t n, const int32_t d) - { - if ((n < 0) != (d < 0)) - return (n - d/2) / d; - else - return (n + d/2) / d; - } - - /* Find a vector (nx, ny) that is perpendicular to (dx, dy) and has length - * equal to 'norm'. */ - static void get_normal_vector(coord_t dx, coord_t dy, coord_t norm, coord_t *nx, coord_t *ny) - { - int32_t dx2, dy2, len_sq, norm_sq, norm_sq2; - int div, step, best, delta, abs_delta; - - dx2 = dx; dy2 = dy; - norm_sq = (int32_t)norm * norm; - norm_sq2 = norm_sq * 512; - - /* Scale dx2 and dy2 so that - * len_sq / 2 <= norm_sq * 512 <= len_sq * 2. - * The scaling by 512 is to yield higher accuracy in division later. */ - len_sq = dx2 * dx2 + dy2 * dy2; - - if (len_sq < norm_sq2) - { - while (len_sq && len_sq < norm_sq2) - { - len_sq <<= 2; dx2 <<= 1; dy2 <<= 1; - } - } - else if (len_sq > norm_sq2) - { - while (len_sq && len_sq > norm_sq2) - { - len_sq >>= 2; dx2 >>= 1; dy2 >>= 1; - } - } - - /* Now find the divider div so that - * len_sq / div^2 == norm_sq i.e. div = sqrt(len_sq / norm_sq) - * - * This is done using bisection search to avoid the need for floating - * point sqrt. - * - * Based on previous scaling, we know that - * len_sq / 2 <= norm_sq * 512 <=> div <= sqrt(1024) = 32 - * len_sq * 2 >= norm_sq * 512 <=> div >= sqrt(256) = 16 - */ - div = 24; step = 8; - best = 256; - - for (;;) - { - dx = dx2 / div; - dy = dy2 / div; - len_sq = dx*dx + dy*dy; - - delta = len_sq - norm_sq; - - abs_delta = (delta >= 0) ? delta : -delta; - - if (abs_delta < best) - { - *nx = dy; - *ny = -dx; - best = abs_delta; - } - - if (delta > 0) - div += step; - else if (delta < 0) - div -= step; - else if (delta == 0) - break; - - if (step == 0) - break; - else - step >>= 1; /* Do one round with step = 0 to calculate final result. */ - } - } - - void gdispGDrawThickLine(GDisplay *g, coord_t x0, coord_t y0, coord_t x1, coord_t y1, color_t color, coord_t width, bool_t round) { - coord_t dx, dy, nx = 0, ny = 0; - - /* Compute the direction vector for the line */ - dx = x1 - x0; - dy = y1 - y0; - - /* Draw a small dot if the line length is zero. */ - if (dx == 0 && dy == 0) - dx += 1; - - /* Compute a normal vector with length 'width'. */ - get_normal_vector(dx, dy, width, &nx, &ny); - - /* Handle 1px wide lines gracefully */ - if (nx == 0 && ny == 0) - nx = 1; - - /* Offset the x0,y0 by half the width of the line. This way we - * can keep the width of the line accurate even if it is not evenly - * divisible by 2. - */ - { - x0 -= rounding_div(nx, 2); - y0 -= rounding_div(ny, 2); - } - - /* Fill in the point array */ - if (!round) { - /* We use 4 points for the basic line shape: - * - * pt1 pt2 - * (+n) ------------------------------------ (d+n) - * | | - * (0,0) ----------------------------------- (d) - * pt0 pt3 - */ - point pntarray[4]; - - pntarray[0].x = 0; - pntarray[0].y = 0; - pntarray[1].x = nx; - pntarray[1].y = ny; - pntarray[2].x = dx + nx; - pntarray[2].y = dy + ny; - pntarray[3].x = dx; - pntarray[3].y = dy; - - gdispGFillConvexPoly(g, x0, y0, pntarray, 4, color); - } else { - /* We use 4 points for basic shape, plus 4 extra points for ends: - * - * pt3 ------------------ pt4 - * / \ - * pt2 pt5 - * | | - * pt1 pt6 - * \ / - * pt0 -------------------pt7 - */ - point pntarray[8]; - coord_t nx2, ny2; - - /* Magic numbers: - * 75/256 = sin(45) / (1 + sqrt(2)) diagonal octagon segments - * 106/256 = 1 / (1 + sqrt(2)) octagon side - * 53/256 = 0.5 / (1 + sqrt(2)) half of octagon side - * 150/256 = 1 - 1 / (1 + sqrt(2)) octagon height minus one side - */ - - /* Rotate the normal vector 45 deg counter-clockwise and reduce - * to 1 / (1 + sqrt(2)) length, for forming octagonal ends. */ - nx2 = rounding_div((nx * 75 + ny * 75), 256); - ny2 = rounding_div((-nx * 75 + ny * 75), 256); - - /* Offset and extend the line so that the center of the octagon - * is at the specified points. */ - x0 += ny * 53 / 256; - y0 -= nx * 53 / 256; - dx -= ny * 106 / 256; - dy += nx * 106 / 256; - - /* Now fill in the points by summing the calculated vectors. */ - pntarray[0].x = 0; - pntarray[0].y = 0; - pntarray[1].x = nx2; - pntarray[1].y = ny2; - pntarray[2].x = nx2 + nx * 106/256; - pntarray[2].y = ny2 + ny * 106/256; - pntarray[3].x = nx; - pntarray[3].y = ny; - pntarray[4].x = dx + nx; - pntarray[4].y = dy + ny; - pntarray[5].x = dx + nx - nx2; - pntarray[5].y = dy + ny - ny2; - pntarray[6].x = dx + nx * 150/256 - nx2; - pntarray[6].y = dy + ny * 150/256 - ny2; - pntarray[7].x = dx; - pntarray[7].y = dy; - - gdispGFillConvexPoly(g, x0, y0, pntarray, 8, color); - } - } -#endif - -#if GDISP_NEED_TEXT - #include "mcufont/mcufont.h" - - #if GDISP_NEED_ANTIALIAS && GDISP_HARDWARE_PIXELREAD - static void drawcharline(int16_t x, int16_t y, uint8_t count, uint8_t alpha, void *state) { - #define GD ((GDisplay *)state) - if (y < GD->t.clipy0 || y >= GD->t.clipy1 || x+count <= GD->t.clipx0 || x >= GD->t.clipx1) - return; - if (x < GD->t.clipx0) { - count -= GD->t.clipx0 - x; - x = GD->t.clipx0; - } - if (x+count > GD->t.clipx1) - count = GD->t.clipx1 - x; - if (alpha == 255) { - GD->p.x = x; GD->p.y = y; GD->p.x1 = x+count-1; GD->p.color = GD->t.color; - hline_clip(GD); - } else { - for (; count; count--, x++) { - GD->p.x = x; GD->p.y = y; - GD->p.color = gdispBlendColor(GD->t.color, gdisp_lld_get_pixel_color(GD), alpha); - drawpixel_clip(GD); - } - } - #undef GD - } - #else - static void drawcharline(int16_t x, int16_t y, uint8_t count, uint8_t alpha, void *state) { - #define GD ((GDisplay *)state) - if (y < GD->t.clipy0 || y >= GD->t.clipy1 || x+count <= GD->t.clipx0 || x >= GD->t.clipx1) - return; - if (x < GD->t.clipx0) { - count -= GD->t.clipx0 - x; - x = GD->t.clipx0; - } - if (x+count > GD->t.clipx1) - count = GD->t.clipx1 - x; - if (alpha > 0x80) { // A best approximation when using anti-aliased fonts but we can't actually draw them anti-aliased - GD->p.x = x; GD->p.y = y; GD->p.x1 = x+count-1; GD->p.color = GD->t.color; - hline_clip(GD); - } - #undef GD - } - #endif - - #if GDISP_NEED_ANTIALIAS - static void fillcharline(int16_t x, int16_t y, uint8_t count, uint8_t alpha, void *state) { - #define GD ((GDisplay *)state) - if (y < GD->t.clipy0 || y >= GD->t.clipy1 || x+count <= GD->t.clipx0 || x >= GD->t.clipx1) - return; - if (x < GD->t.clipx0) { - count -= GD->t.clipx0 - x; - x = GD->t.clipx0; - } - if (x+count > GD->t.clipx1) - count = GD->t.clipx1 - x; - if (alpha == 255) { - GD->p.color = GD->t.color; - } else { - GD->p.color = gdispBlendColor(GD->t.color, GD->t.bgcolor, alpha); - } - GD->p.x = x; GD->p.y = y; GD->p.x1 = x+count-1; - hline_clip(GD); - #undef GD - } - #else - #define fillcharline drawcharline - #endif - - /* Callback to render characters. */ - static uint8_t drawcharglyph(int16_t x, int16_t y, mf_char ch, void *state) { - #define GD ((GDisplay *)state) - return mf_render_character(GD->t.font, x, y, ch, drawcharline, state); - #undef GD - } - - /* Callback to render characters. */ - static uint8_t fillcharglyph(int16_t x, int16_t y, mf_char ch, void *state) { - #define GD ((GDisplay *)state) - return mf_render_character(GD->t.font, x, y, ch, fillcharline, state); - #undef GD - } - - void gdispGDrawChar(GDisplay *g, coord_t x, coord_t y, uint16_t c, font_t font, color_t color) { - MUTEX_ENTER(g); - g->t.font = font; - g->t.clipx0 = x; - g->t.clipy0 = y; - g->t.clipx1 = x + mf_character_width(font, c) + font->baseline_x; - g->t.clipy1 = y + font->height; - g->t.color = color; - mf_render_character(font, x, y, c, drawcharline, g); - autoflush(g); - MUTEX_EXIT(g); - } - - void gdispGFillChar(GDisplay *g, coord_t x, coord_t y, uint16_t c, font_t font, color_t color, color_t bgcolor) { - MUTEX_ENTER(g); - g->p.cx = mf_character_width(font, c) + font->baseline_x; - g->p.cy = font->height; - g->t.font = font; - g->t.clipx0 = g->p.x = x; - g->t.clipy0 = g->p.y = y; - g->t.clipx1 = g->p.x+g->p.cx; - g->t.clipy1 = g->p.y+g->p.cy; - g->t.color = color; - g->t.bgcolor = g->p.color = bgcolor; - - TEST_CLIP_AREA(g) { - fillarea(g); - mf_render_character(font, x, y, c, fillcharline, g); - } - autoflush(g); - MUTEX_EXIT(g); - } - - void gdispGDrawString(GDisplay *g, coord_t x, coord_t y, const char *str, font_t font, color_t color) { - MUTEX_ENTER(g); - g->t.font = font; - g->t.clipx0 = x; - g->t.clipy0 = y; - g->t.clipx1 = x + mf_get_string_width(font, str, 0, 0); - g->t.clipy1 = y + font->height; - g->t.color = color; - - mf_render_aligned(font, x+font->baseline_x, y, MF_ALIGN_LEFT, str, 0, drawcharglyph, g); - autoflush(g); - MUTEX_EXIT(g); - } - - void gdispGFillString(GDisplay *g, coord_t x, coord_t y, const char *str, font_t font, color_t color, color_t bgcolor) { - MUTEX_ENTER(g); - g->p.cx = mf_get_string_width(font, str, 0, 0); - g->p.cy = font->height; - g->t.font = font; - g->t.clipx0 = g->p.x = x; - g->t.clipy0 = g->p.y = y; - g->t.clipx1 = g->p.x+g->p.cx; - g->t.clipy1 = g->p.y+g->p.cy; - g->t.color = color; - g->t.bgcolor = g->p.color = bgcolor; - - TEST_CLIP_AREA(g) { - fillarea(g); - mf_render_aligned(font, x+font->baseline_x, y, MF_ALIGN_LEFT, str, 0, fillcharglyph, g); - } - - autoflush(g); - MUTEX_EXIT(g); - } - - void gdispGDrawStringBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, const char* str, font_t font, color_t color, justify_t justify) { - MUTEX_ENTER(g); - g->t.font = font; - g->t.clipx0 = x; - g->t.clipy0 = y; - g->t.clipx1 = x+cx; - g->t.clipy1 = y+cy; - g->t.color = color; - - /* Select the anchor position */ - switch(justify) { - case justifyCenter: - x += (cx + 1) / 2; - break; - case justifyRight: - x += cx; - break; - default: // justifyLeft - x += font->baseline_x; - break; - } - y += (cy+1 - font->height)/2; - - mf_render_aligned(font, x, y, justify, str, 0, drawcharglyph, g); - - autoflush(g); - MUTEX_EXIT(g); - } - - void gdispGFillStringBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, const char* str, font_t font, color_t color, color_t bgcolor, justify_t justify) { - MUTEX_ENTER(g); - g->p.cx = cx; - g->p.cy = cy; - g->t.font = font; - g->t.clipx0 = g->p.x = x; - g->t.clipy0 = g->p.y = y; - g->t.clipx1 = x+cx; - g->t.clipy1 = y+cy; - g->t.color = color; - g->t.bgcolor = g->p.color = bgcolor; - - TEST_CLIP_AREA(g) { - - // background fill - fillarea(g); - - /* Select the anchor position */ - switch(justify) { - case justifyCenter: - x += (cx + 1) / 2; - break; - case justifyRight: - x += cx; - break; - default: // justifyLeft - x += font->baseline_x; - break; - } - y += (cy+1 - font->height)/2; - - /* Render */ - mf_render_aligned(font, x, y, justify, str, 0, fillcharglyph, g); - } - - autoflush(g); - MUTEX_EXIT(g); - } - - coord_t gdispGetFontMetric(font_t font, fontmetric_t metric) { - /* No mutex required as we only read static data */ - switch(metric) { - case fontHeight: return font->height; - case fontDescendersHeight: return font->height - font->baseline_y; - case fontLineSpacing: return font->line_height; - case fontCharPadding: return 0; - case fontMinWidth: return font->min_x_advance; - case fontMaxWidth: return font->max_x_advance; - } - return 0; - } - - coord_t gdispGetCharWidth(char c, font_t font) { - /* No mutex required as we only read static data */ - return mf_character_width(font, c); - } - - coord_t gdispGetStringWidth(const char* str, font_t font) { - if (!str) - return 0; - - /* No mutex required as we only read static data */ - return mf_get_string_width(font, str, 0, 0); - } -#endif - -color_t gdispBlendColor(color_t fg, color_t bg, uint8_t alpha) -{ - uint16_t fg_ratio = alpha + 1; - uint16_t bg_ratio = 256 - alpha; - uint16_t r, g, b; - - r = RED_OF(fg) * fg_ratio; - g = GREEN_OF(fg) * fg_ratio; - b = BLUE_OF(fg) * fg_ratio; - - r += RED_OF(bg) * bg_ratio; - g += GREEN_OF(bg) * bg_ratio; - b += BLUE_OF(bg) * bg_ratio; - - r >>= 8; - g >>= 8; - b >>= 8; - - return RGB2COLOR(r, g, b); -} - -color_t gdispContrastColor(color_t color) { - uint16_t r, g, b; - - r = RED_OF(color) > 128 ? 0 : 255; - g = GREEN_OF(color) > 128 ? 0 : 255; - b = BLUE_OF(color) > 128 ? 0 : 255; - - return RGB2COLOR(r, g, b); -} - -#if (!defined(gdispPackPixels) && !defined(GDISP_PIXELFORMAT_CUSTOM)) - void gdispPackPixels(pixel_t *buf, coord_t cx, coord_t x, coord_t y, color_t color) { - /* No mutex required as we only read static data */ - #if defined(GDISP_PIXELFORMAT_RGB888) - #error "GDISP: Packed pixels not supported yet" - #elif defined(GDISP_PIXELFORMAT_RGB444) - #error "GDISP: Packed pixels not supported yet" - #elif defined(GDISP_PIXELFORMAT_RGB666) - #error "GDISP: Packed pixels not supported yet" - #elif - #error "GDISP: Unsupported packed pixel format" - #endif - } -#endif - -#endif /* GFX_USE_GDISP */ -/** @} */ diff --git a/src/gdisp/gdisp_image.c b/src/gdisp/gdisp_image.c index 2851a7c8..35020634 100644 --- a/src/gdisp/gdisp_image.c +++ b/src/gdisp/gdisp_image.c @@ -5,13 +5,6 @@ * http://ugfx.org/license.html */ -/** - * @file src/gdisp/gdisp_image.c - * @brief GDISP generic image code. - * - * @defgroup Image Image - * @ingroup GDISP - */ #include "gfx.h" #if GFX_USE_GDISP && GDISP_NEED_IMAGE @@ -230,4 +223,3 @@ void gdispImageFree(gdispImage *img, void *ptr, size_t sz) { } #endif /* GFX_USE_GDISP && GDISP_NEED_IMAGE */ -/** @} */ diff --git a/src/gdisp/gdisp_image_bmp.c b/src/gdisp/gdisp_image_bmp.c index d4507ee2..9fc0e13a 100644 --- a/src/gdisp/gdisp_image_bmp.c +++ b/src/gdisp/gdisp_image_bmp.c @@ -5,42 +5,10 @@ * http://ugfx.org/license.html */ -/** - * @file src/gdisp/gdisp_image_bmp.c - * @brief GDISP native image code. - * - * @defgroup Image Image - * @ingroup GDISP - */ #include "gfx.h" #if GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_BMP -#ifndef GDISP_NEED_IMAGE_BMP_1 - #define GDISP_NEED_IMAGE_BMP_1 TRUE -#endif -#ifndef GDISP_NEED_IMAGE_BMP_4 - #define GDISP_NEED_IMAGE_BMP_4 TRUE -#endif -#ifndef GDISP_NEED_IMAGE_BMP_4_RLE - #define GDISP_NEED_IMAGE_BMP_4_RLE TRUE -#endif -#ifndef GDISP_NEED_IMAGE_BMP_8 - #define GDISP_NEED_IMAGE_BMP_8 TRUE -#endif -#ifndef GDISP_NEED_IMAGE_BMP_8_RLE - #define GDISP_NEED_IMAGE_BMP_8_RLE TRUE -#endif -#ifndef GDISP_NEED_IMAGE_BMP_16 - #define GDISP_NEED_IMAGE_BMP_16 TRUE -#endif -#ifndef GDISP_NEED_IMAGE_BMP_24 - #define GDISP_NEED_IMAGE_BMP_24 TRUE -#endif -#ifndef GDISP_NEED_IMAGE_BMP_32 - #define GDISP_NEED_IMAGE_BMP_32 TRUE -#endif - /** * Helper Routines Needed */ @@ -901,4 +869,3 @@ delaytime_t gdispImageNext_BMP(gdispImage *img) { } #endif /* GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_BMP */ -/** @} */ diff --git a/src/gdisp/gdisp_image_gif.c b/src/gdisp/gdisp_image_gif.c index 8ea9beb2..d5c6b3ca 100644 --- a/src/gdisp/gdisp_image_gif.c +++ b/src/gdisp/gdisp_image_gif.c @@ -5,13 +5,6 @@ * http://ugfx.org/license.html */ -/** - * @file src/gdisp/gdisp_image_gif.c - * @brief GDISP native image code. - * - * @defgroup Image Image - * @ingroup GDISP -*/ #include "gfx.h" #if GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_GIF @@ -1205,4 +1198,3 @@ delaytime_t gdispImageNext_GIF(gdispImage *img) { } #endif /* GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_GIF */ -/** @} */ diff --git a/src/gdisp/gdisp_image_jpg.c b/src/gdisp/gdisp_image_jpg.c index f20884bc..02ec9a8f 100644 --- a/src/gdisp/gdisp_image_jpg.c +++ b/src/gdisp/gdisp_image_jpg.c @@ -5,10 +5,6 @@ * http://ugfx.org/license.html */ -/** - * @file src/gdisp/gdisp_image_jpg.c - * @brief GDISP native image code. - */ #include "gfx.h" #if GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_JPG @@ -16,4 +12,3 @@ #error "JPG support not implemented yet" #endif /* GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_JPG */ -/** @} */ diff --git a/src/gdisp/gdisp_image_native.c b/src/gdisp/gdisp_image_native.c index 21dcb0fc..ec22f386 100644 --- a/src/gdisp/gdisp_image_native.c +++ b/src/gdisp/gdisp_image_native.c @@ -5,10 +5,6 @@ * http://ugfx.org/license.html */ -/** - * @file src/gdisp/gdisp_image_native.c - * @brief GDISP native image code. - */ #include "gfx.h" #if GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_NATIVE @@ -142,4 +138,3 @@ delaytime_t gdispImageNext_NATIVE(gdispImage *img) { } #endif /* GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_NATIVE */ -/** @} */ diff --git a/src/gdisp/gdisp_image_png.c b/src/gdisp/gdisp_image_png.c index 5d2c1450..6538b2ff 100644 --- a/src/gdisp/gdisp_image_png.c +++ b/src/gdisp/gdisp_image_png.c @@ -5,10 +5,6 @@ * http://ugfx.org/license.html */ -/** - * @file src/gdisp/gdisp_image_png.c - * @brief GDISP native image code. - */ #include "gfx.h" #if GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_PNG @@ -16,4 +12,3 @@ #error "PNG support not implemented yet" #endif /* GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_PNG */ -/** @} */ diff --git a/src/gdisp/gdisp_options.h b/src/gdisp/gdisp_options.h new file mode 100644 index 00000000..51d759d8 --- /dev/null +++ b/src/gdisp/gdisp_options.h @@ -0,0 +1,445 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gdisp/gdisp_options.h + * @brief GDISP sub-system options header file. + * + * @addtogroup GDISP + * @{ + */ + +#ifndef _GDISP_OPTIONS_H +#define _GDISP_OPTIONS_H + +/** + * @name GDISP Functionality to be included + * @{ + */ + /** + * @brief Should drawing operations be automatically flushed. + * @details Defaults to FALSE + * @note If set to FALSE and the controller requires flushing + * then the application must manually call @p gdispGFlush(). + * Setting this to TRUE causes GDISP to automatically flush + * after each drawing operation. Note this may be slow but enables + * an application to avoid having to manually call the flush routine. + * @note If TRUE and GDISP_NEED_TIMERFLUSH is also TRUE, this takes precedence. + * @note Most controllers don't need flushing which is why this is set to + * FALSE by default. + */ + #ifndef GDISP_NEED_AUTOFLUSH + #define GDISP_NEED_AUTOFLUSH FALSE + #endif + /** + * @brief Should drawing operations be automatically flushed on a timer. + * @details Defaults to FALSE, Can be set to FALSE or a timer period in milliseconds. + * @note The period should not be set too short or it will consume all your CPU. A + * value between 250 and 500 milliseconds would probably be suitable. + * @note If TRUE and GDISP_NEED_AUTOFLUSH is also TRUE, this is ineffective. + * @note Most controllers don't need flushing which is why this is set to + * FALSE by default. + */ + #ifndef GDISP_NEED_TIMERFLUSH + #define GDISP_NEED_TIMERFLUSH FALSE + #endif + /** + * @brief Should all operations be clipped to the screen and colors validated. + * @details Defaults to TRUE. + * @note If this is FALSE, any operations that extend beyond the + * edge of the screen will have undefined results. Any + * out-of-range colors will produce undefined results. + * @note This should always be left as the default (TRUE) unless you + * are a maniac for speed and you have thoroughly tested your code + * and it never overwrites the edges of the screen. + * @note Setting GDISP_NEED_CLIP to TRUE internally uses the same mechanism + * as this validation. There is no advantage in setting this FALSE if + * GDISP_NEED_CLIP is TRUE. + */ + #ifndef GDISP_NEED_VALIDATION + #define GDISP_NEED_VALIDATION TRUE + #endif + /** + * @brief Are clipping functions needed. + * @details Defaults to TRUE + */ + #ifndef GDISP_NEED_CLIP + #define GDISP_NEED_CLIP TRUE + #endif + /** + * @brief Streaming functions are needed + * @details Defaults to FALSE. + */ + #ifndef GDISP_NEED_STREAMING + #define GDISP_NEED_STREAMING FALSE + #endif + /** + * @brief Are text functions needed. + * @details Defaults to FALSE + * @note You must also define at least one font. + */ + #ifndef GDISP_NEED_TEXT + #define GDISP_NEED_TEXT FALSE + #endif + /** + * @brief Are circle functions needed. + * @details Defaults to FALSE + * @note Uses integer algorithms only. It does not use any trig or floating point. + */ + #ifndef GDISP_NEED_CIRCLE + #define GDISP_NEED_CIRCLE FALSE + #endif + /** + * @brief Are ellipse functions needed. + * @details Defaults to FALSE + * @note Uses integer algorithms only. It does not use any trig or floating point. + */ + #ifndef GDISP_NEED_ELLIPSE + #define GDISP_NEED_ELLIPSE FALSE + #endif + /** + * @brief Are arc sector functions needed. + * @details Defaults to FALSE + * @note Uses integer algorithms only. It does not use any trig or floating point. + */ + #ifndef GDISP_NEED_ARCSECTORS + #define GDISP_NEED_ARCSECTORS FALSE + #endif + /** + * @brief Are arc functions needed. + * @details Defaults to FALSE + * @note This can be compiled using fully integer mathematics by + * defining GFX_USE_GMISC and GMISC_NEED_FIXEDTRIG as TRUE. + * @note This can be compiled to use floating point but no trig functions + * by defining GFX_USE_GMISC and GMISC_NEED_FASTTRIG as TRUE. + * @note If neither of the above are defined it requires the maths library + * to be included in the link to provide floating point and trig support. + * ie include -lm in your compiler flags. + */ + #ifndef GDISP_NEED_ARC + #define GDISP_NEED_ARC FALSE + #endif + /** + * @brief Are convex polygon functions needed. + * @details Defaults to FALSE + * @note Convex polygons are those that have no internal angles. That is; + * you can draw a line from any point on the polygon to any other point + * on the polygon without it going outside the polygon. + */ + #ifndef GDISP_NEED_CONVEX_POLYGON + #define GDISP_NEED_CONVEX_POLYGON FALSE + #endif + /** + * @brief Are scrolling functions needed. + * @details Defaults to FALSE + * @note This function must be supported by the low level GDISP driver + * you have included in your project. If it isn't, defining this + * option will cause a compile error. + */ + #ifndef GDISP_NEED_SCROLL + #define GDISP_NEED_SCROLL FALSE + #endif + /** + * @brief Is the capability to read pixels back needed. + * @details Defaults to FALSE + * @note This function must be supported by the low level GDISP driver + * you have included in your project. If it isn't, defining this + * option will cause a compile error. + */ + #ifndef GDISP_NEED_PIXELREAD + #define GDISP_NEED_PIXELREAD FALSE + #endif + /** + * @brief Control some aspect of the hardware operation. + * @details Defaults to FALSE + * @note This allows control of hardware specific features such as + * screen rotation, backlight levels, contrast etc + */ + #ifndef GDISP_NEED_CONTROL + #define GDISP_NEED_CONTROL FALSE + #endif + /** + * @brief Query some aspect of the hardware operation. + * @details Defaults to FALSE + * @note This allows query of hardware specific features + */ + #ifndef GDISP_NEED_QUERY + #define GDISP_NEED_QUERY FALSE + #endif + /** + * @brief Is the image interface required. + * @details Defaults to FALSE + */ + #ifndef GDISP_NEED_IMAGE + #define GDISP_NEED_IMAGE FALSE + #endif + /** + * @brief Is the image interface required. + * @details Defaults to FALSE + */ + #ifndef GDISP_NEED_PIXMAP + #define GDISP_NEED_PIXMAP FALSE + #endif +/** + * @} + * + * @name GDISP Multi-Threading Options + * @{ + */ + /** + * @brief Do the drawing functions need to be thread-safe. + * @details Defaults to FALSE + */ + #ifndef GDISP_NEED_MULTITHREAD + #define GDISP_NEED_MULTITHREAD FALSE + #endif +/** + * @} + * + * @name GDISP Optional Parameters + * @{ + */ + /** + * @brief Should the startup logo be displayed + * + * @details Defaults to TRUE + */ + #ifndef GDISP_NEED_STARTUP_LOGO + #define GDISP_NEED_STARTUP_LOGO TRUE + #endif + /** + * @brief Define the initial background color for all displays in the system. + */ + #ifndef GDISP_STARTUP_COLOR + #define GDISP_STARTUP_COLOR Black + #endif + /** + * @brief Define the default orientation for all displays in the system. + * @note GDISP_NEED_CONTROL must also be set (and the hardware must support it) + * @note If not specified then displays default to the native hardware orientation + */ + // #define GDISP_DEFAULT_ORIENTATION GDISP_ROTATE_LANDSCAPE + /** + * @brief The size of pixel buffer (in pixels) used for optimization. + * @details Set to zero to guarantee disabling of the buffer. + * @note Depending on the driver and what operations the application + * needs, this buffer may never be allocated. + * @note Setting the size to zero may cause some operations to not + * compile eg. Scrolling if there is no hardware scroll support. + * @note Increasing the size will speedup certain operations + * at the expense of RAM. + * @note Currently only used to support scrolling on hardware without + * scrolling support, and to increase the speed of streaming + * operations on non-streaming hardware where there is a + * hardware supported bit-blit. + */ + #ifndef GDISP_LINEBUF_SIZE + #define GDISP_LINEBUF_SIZE 128 + #endif +/** + * @} + * + * @name GDISP Multiple Display Support + * @{ + */ + /** + * @brief The total number of displays using the default driver. + * @note If you want to use multiple displays either set GDISP_TOTAL_DISPLAYS or GDISP_DRIVER_LIST + * but not both. + */ + #ifndef GDISP_TOTAL_DISPLAYS + #define GDISP_TOTAL_DISPLAYS 1 + #endif + #if defined(__DOXYGEN__) + /** + * @brief The list of display drivers. + * @note Replace this example with your own definition in your gfxconf.h file. See the gdisp_lld.c + * in each driver (near the top) to get the name of the VMT for a driver. + * @note The same driver can occur more than once in the list to create an extra instance of that driver. + * @note If defining this you must also define GDISP_PIXELFORMAT for your application to use. + * Choose a value that is most common accross all your drivers for efficiency. + * @note If using this you may optionally define the GDISP_HARDWARE_xxx values as either TRUE or FALSE. + * Doing this causes GDISP to assume that all (TRUE) or none (FALSE) of the listed drivers have that + * capability. This can help improve drawing speed and efficiency. + */ + #define GDISP_DRIVER_LIST GDISPVMT_Win32, GDISPVMT_SSD1963 + #endif +/** + * @} + * + * @name GDISP Image Options + * @pre GDISP_NEED_IMAGE must be TRUE + * @{ + */ + /** + * @brief Is native image decoding required. + * @details Defaults to FALSE + */ + #ifndef GDISP_NEED_IMAGE_NATIVE + #define GDISP_NEED_IMAGE_NATIVE FALSE + #endif + /** + * @brief Is GIF image decoding required. + * @details Defaults to FALSE + */ + #ifndef GDISP_NEED_IMAGE_GIF + #define GDISP_NEED_IMAGE_GIF FALSE + #endif + /** + * @brief Is BMP image decoding required. + * @details Defaults to FALSE + */ + #ifndef GDISP_NEED_IMAGE_BMP + #define GDISP_NEED_IMAGE_BMP FALSE + #endif + /** + * @brief Is JPG image decoding required. + * @details Defaults to FALSE + */ + #ifndef GDISP_NEED_IMAGE_JPG + #define GDISP_NEED_IMAGE_JPG FALSE + #endif + /** + * @brief Is PNG image decoding required. + * @details Defaults to FALSE + */ + #ifndef GDISP_NEED_IMAGE_PNG + #define GDISP_NEED_IMAGE_PNG FALSE + #endif + /** + * @brief Is memory accounting required during image decoding. + * @details Defaults to FALSE + */ + #ifndef GDISP_NEED_IMAGE_ACCOUNTING + #define GDISP_NEED_IMAGE_ACCOUNTING FALSE + #endif +/** + * @} + * + * @name GDISP BMP Image Options + * @pre GDISP_NEED_IMAGE and GDISP_NEED_IMAGE_BMP must be TRUE + * @{ + */ + /** + * @brief Is BMP 1 bit per pixel (monochrome/2 color) image decoding required. + * @details Defaults to TRUE + */ + #ifndef GDISP_NEED_IMAGE_BMP_1 + #define GDISP_NEED_IMAGE_BMP_1 TRUE + #endif + /** + * @brief Is BMP 4 bits per pixel (16 color) image decoding required. + * @details Defaults to TRUE + */ + #ifndef GDISP_NEED_IMAGE_BMP_4 + #define GDISP_NEED_IMAGE_BMP_4 TRUE + #endif + /** + * @brief Is BMP 4 bits per pixel (16 color) with RLE compression image decoding required. + * @details Defaults to TRUE + */ + #ifndef GDISP_NEED_IMAGE_BMP_4_RLE + #define GDISP_NEED_IMAGE_BMP_4_RLE TRUE + #endif + /** + * @brief Is BMP 8 bits per pixel (256 color) image decoding required. + * @details Defaults to TRUE + */ + #ifndef GDISP_NEED_IMAGE_BMP_8 + #define GDISP_NEED_IMAGE_BMP_8 TRUE + #endif + /** + * @brief Is BMP 8 bits per pixel (256 color) with RLE compression image decoding required. + * @details Defaults to TRUE + */ + #ifndef GDISP_NEED_IMAGE_BMP_8_RLE + #define GDISP_NEED_IMAGE_BMP_8_RLE TRUE + #endif + /** + * @brief Is BMP 16 bits per pixel (65536 color) image decoding required. + * @details Defaults to TRUE + */ + #ifndef GDISP_NEED_IMAGE_BMP_16 + #define GDISP_NEED_IMAGE_BMP_16 TRUE + #endif + /** + * @brief Is BMP 24 bits per pixel (true-color) image decoding required. + * @details Defaults to TRUE + */ + #ifndef GDISP_NEED_IMAGE_BMP_24 + #define GDISP_NEED_IMAGE_BMP_24 TRUE + #endif + /** + * @brief Is BMP 32 bits per pixel (true-color) image decoding required. + * @details Defaults to TRUE + */ + #ifndef GDISP_NEED_IMAGE_BMP_32 + #define GDISP_NEED_IMAGE_BMP_32 TRUE + #endif +/** + * @} + * + * @name GDISP Text Rendering Options + * @{ + */ + /** + * @brief Enable UTF-8 support for text rendering. + * @details Defaults to FALSE + */ + #ifndef GDISP_NEED_UTF8 + #define GDISP_NEED_UTF8 FALSE + #endif + /** + * @brief Enable kerning for font rendering (improves character placement). + * @details Defaults to FALSE + */ + #ifndef GDISP_NEED_TEXT_KERNING + #define GDISP_NEED_TEXT_KERNING FALSE + #endif + /** + * @brief Enable antialiased font support + * @details Defaults to FALSE + */ + #ifndef GDISP_NEED_ANTIALIAS + #define GDISP_NEED_ANTIALIAS FALSE + #endif +/** + * @} + * + * @name GDISP Pixmap Options + * @{ + */ + #ifndef GDISP_NEED_PIXMAP_IMAGE + #define GDISP_NEED_PIXMAP_IMAGE FALSE + #endif +/** + * @} + * + * @name GDISP Optional Low Level Driver Defines + * @{ + */ + /** + * @brief Set the screen height and width. + * @note Ignored by some low level GDISP drivers, optional for others. + * @note Where these values are allowed, a default is always provided be the low level driver. + * @note The list of GDISP low level drivers that allow these to be set are... + * WIN32, SSD1289, SSD1963, TestStub + */ + /* #define GDISP_SCREEN_WIDTH nnnn */ + /* #define GDISP_SCREEN_HEIGHT nnnn */ + /** + * @brief Define which bus interface to use. + * @details Only required by the SSD1963 driver. + * @note This will be replaced eventually by board definition files + */ + // #define GDISP_USE_FSMC + // #define GDISP_USE_GPIO +/** @} */ + +#endif /* _GDISP_OPTIONS_H */ +/** @} */ + diff --git a/src/gdisp/gdisp_pixmap.c b/src/gdisp/gdisp_pixmap.c index d088a57b..b6226289 100644 --- a/src/gdisp/gdisp_pixmap.c +++ b/src/gdisp/gdisp_pixmap.c @@ -39,8 +39,8 @@ #error "GDISP Pixmap: Pixmap's do not currently support the specified GDISP_LLD_PIXELFORMAT" #endif -#include "src/gdisp/driver.h" -#include "src/gdriver/sys_defs.h" +#include "src/gdisp/gdisp_driver.h" +#include "src/gdriver/gdriver.h" typedef struct pixmap { #if GDISP_NEED_PIXMAP_IMAGE diff --git a/src/gdisp/gdisp_rules.h b/src/gdisp/gdisp_rules.h new file mode 100644 index 00000000..3e5051e5 --- /dev/null +++ b/src/gdisp/gdisp_rules.h @@ -0,0 +1,89 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gdisp/gdisp_rules.h + * @brief GDISP safety rules header file. + * + * @addtogroup GDISP + * @{ + */ + +#ifndef _GDISP_RULES_H +#define _GDISP_RULES_H + +#if GFX_USE_GDISP + #if !GFX_USE_GDRIVER + #if GFX_DISPLAY_RULE_WARNINGS + #warning "GDISP: GFX_USE_GDRIVER is required. GFX_USE_GDRIVER has turned on for you." + #endif + #undef GFX_USE_GDRIVER + #define GFX_USE_GDRIVER TRUE + #endif + #if defined(GDISP_DRIVER_LIST) + #if GDISP_TOTAL_DISPLAYS != 1 + #error "GDISP Multiple Drivers: You can't specify both GDISP_TOTAL_DISPLAYS and GDISP_DRIVER_LIST" + #endif + #ifndef GDISP_PIXELFORMAT + #error "GDISP Multiple Drivers: You must specify a value for GDISP_PIXELFORMAT when using GDISP_DRIVER_LIST" + #endif + #endif + #if GDISP_NEED_AUTOFLUSH && GDISP_NEED_TIMERFLUSH + #if GFX_DISPLAY_RULE_WARNINGS + #warning "GDISP: Both GDISP_NEED_AUTOFLUSH and GDISP_NEED_TIMERFLUSH has been set. GDISP_NEED_TIMERFLUSH has disabled for you." + #endif + #undef GDISP_NEED_TIMERFLUSH + #define GDISP_NEED_TIMERFLUSH FALSE + #endif + #if GDISP_NEED_TIMERFLUSH + #if GDISP_NEED_TIMERFLUSH < 50 || GDISP_NEED_TIMERFLUSH > 1200 + #error "GDISP: GDISP_NEED_TIMERFLUSH has been set to an invalid value (FALSE, 50-1200)." + #endif + #if !GFX_USE_GTIMER + #if GFX_DISPLAY_RULE_WARNINGS + #warning "GDISP: GDISP_NEED_TIMERFLUSH has been set but GFX_USE_GTIMER has not been set. It has been turned on for you." + #endif + #undef GFX_USE_GTIMER + #define GFX_USE_GTIMER TRUE + #undef GDISP_NEED_MULTITHREAD + #define GDISP_NEED_MULTITHREAD TRUE + #endif + #endif + #if GDISP_NEED_ANTIALIAS && !GDISP_NEED_PIXELREAD + #if GDISP_HARDWARE_PIXELREAD + #if GFX_DISPLAY_RULE_WARNINGS + #warning "GDISP: GDISP_NEED_ANTIALIAS has been set but GDISP_NEED_PIXELREAD has not. It has been turned on for you." + #endif + #undef GDISP_NEED_PIXELREAD + #define GDISP_NEED_PIXELREAD TRUE + #else + #if GFX_DISPLAY_RULE_WARNINGS + #warning "GDISP: GDISP_NEED_ANTIALIAS has been set but your hardware does not support reading back pixels. Anti-aliasing will only occur for filled characters." + #endif + #endif + #endif + #if (defined(GDISP_INCLUDE_FONT_SMALL) && GDISP_INCLUDE_FONT_SMALL) || (defined(GDISP_INCLUDE_FONT_LARGER) && GDISP_INCLUDE_FONT_LARGER) + #if GFX_DISPLAY_RULE_WARNINGS + #warning "GDISP: An old font (Small or Larger) has been defined. A single default font of UI2 has been added instead." + #warning "GDISP: Please see <$(GFXLIB)/include/gdisp/fonts/fonts.h> for a list of available font names." + #endif + #undef GDISP_INCLUDE_FONT_UI2 + #define GDISP_INCLUDE_FONT_UI2 TRUE + #endif + #if GDISP_NEED_IMAGE + #if !GFX_USE_GFILE + #if GFX_DISPLAY_RULE_WARNINGS + #warning "GDISP: GFX_USE_GFILE is required when GDISP_NEED_IMAGE is TRUE. It has been turned on for you." + #endif + #undef GFX_USE_GFILE + #define GFX_USE_GFILE TRUE + #endif + #endif +#endif + +#endif /* _GDISP_RULES_H */ +/** @} */ diff --git a/src/gdisp/sys_defs.h b/src/gdisp/sys_defs.h deleted file mode 100644 index 6b77bab8..00000000 --- a/src/gdisp/sys_defs.h +++ /dev/null @@ -1,1124 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gdisp/sys_defs.h - * @brief GDISP Graphic Driver subsystem header file. - * - * @addtogroup GDISP - * - * @brief Module to interface graphic / pixel oriented displays - * - * @details The GDISP module provides high level abstraction to interface pixel oriented graphic displays. - * - * @pre GFX_USE_GDISP must be set to TRUE in gfxconf.h - * - * @note Each drawing routine supports a gdispXXXX and a gdispGXXXX function. The difference is that the - * gdispXXXX function does not require a display to be specified. Note there is a slight anomaly - * in the naming with gdispGBlitArea() vs gdispBlitAreaEx() and gdispBlitArea(), the latter of - * which is now deprecated. - * @{ - */ - -#ifndef _GDISP_H -#define _GDISP_H - -#include "gfx.h" - -/* This type definition is defined here as it gets used in other gfx sub-systems even - * if GFX_USE_GDISP is FALSE. - */ - -/** - * @brief The type for a coordinate or length on the screen. - */ -typedef int16_t coord_t; - -#if GFX_USE_GDISP || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Type definitions */ -/*===========================================================================*/ - -/** - * @brief Type for a 2D point on the screen. - */ -typedef struct point { coord_t x, y; } point, point_t; -/** - * @brief Type for the text justification. - */ -typedef enum justify { justifyLeft=0, justifyCenter=1, justifyRight=2 } justify_t; -/** - * @brief Type for the font metric. - */ -typedef enum fontmetric { fontHeight, fontDescendersHeight, fontLineSpacing, fontCharPadding, fontMinWidth, fontMaxWidth } fontmetric_t; -/** - * @brief The type of a font. - */ -typedef const struct mf_font_s* font_t; -/** - * @brief Type for the screen orientation. - * @note GDISP_ROTATE_LANDSCAPE and GDISP_ROTATE_PORTRAIT are internally converted to the - * most appropriate other orientation. - */ -typedef enum orientation { GDISP_ROTATE_0=0, GDISP_ROTATE_90=90, GDISP_ROTATE_180=180, GDISP_ROTATE_270=270, GDISP_ROTATE_PORTRAIT=1000, GDISP_ROTATE_LANDSCAPE=1001 } orientation_t; -/** - * @brief Type for the available power modes for the screen. - */ -typedef enum powermode { powerOff, powerSleep, powerDeepSleep, powerOn } powermode_t; - -/* - * Our black box display structure. - */ -typedef struct GDisplay GDisplay; - -/** - * @brief The default screen to use for the gdispXXXX calls. - * @note This is set by default to the first display in the system. You can change - * it by calling @p gdispGSetDisplay(). - */ -extern GDisplay *GDISP; - -/*===========================================================================*/ -/* Constants. */ -/*===========================================================================*/ - -/** - * @brief Driver Control Constants - * @details Unsupported control codes are ignored. - * @note The value parameter should always be typecast to (void *). - * @note There are some predefined and some specific to the low level driver. - * @note GDISP_CONTROL_POWER - Takes a gdisp_powermode_t - * GDISP_CONTROL_ORIENTATION - Takes a gdisp_orientation_t - * GDISP_CONTROL_BACKLIGHT - Takes an int from 0 to 100. For a driver - * that only supports off/on anything other - * than zero is on. - * GDISP_CONTROL_CONTRAST - Takes an int from 0 to 100. - * GDISP_CONTROL_LLD - Low level driver control constants start at - * this value. - */ -#define GDISP_CONTROL_POWER 0 -#define GDISP_CONTROL_ORIENTATION 1 -#define GDISP_CONTROL_BACKLIGHT 2 -#define GDISP_CONTROL_CONTRAST 3 -#define GDISP_CONTROL_LLD 1000 - -/*===========================================================================*/ -/* Defines relating to the display hardware */ -/*===========================================================================*/ - -#if !defined(GDISP_DRIVER_LIST) - // Pull in the default hardware configuration for a single controller. - // If we have multiple controllers the settings must be set in the - // users gfxconf.h file. - #include "gdisp_lld_config.h" - - // Unless the user has specified a specific pixel format, use - // the native format for the controller. - #if !defined(GDISP_PIXELFORMAT) && defined(GDISP_LLD_PIXELFORMAT) - #define GDISP_PIXELFORMAT GDISP_LLD_PIXELFORMAT - #endif -#endif - -/** - * @name GDISP pixel format choices - * @{ - */ - /** - * @brief The pixel format. - * @details It generally defaults to the hardware pixel format. - * @note This doesn't need to match the hardware pixel format. - * It is definitely more efficient when it does. - * @note When GDISP_DRIVER_LIST is defined, this must - * be explicitly defined and you should ensure the best match - * with your hardware across all devices. - */ - #ifndef GDISP_PIXELFORMAT - #define GDISP_PIXELFORMAT GDISP_PIXELFORMAT_ERROR - #endif - /** - * @brief Do pixels require packing for a blit - * @note Is only valid for a pixel format that doesn't fill it's datatype. eg formats: - * GDISP_PIXELFORMAT_RGB888 - * GDISP_PIXELFORMAT_RGB444 - * GDISP_PIXELFORMAT_RGB666 - * GDISP_PIXELFORMAT_CUSTOM - * @note Very few cases should actually require packed pixels as the low - * level driver can also pack on the fly as it is sending it - * to the graphics device. - * @note Packed pixels are not really supported at this point. - */ - #ifndef GDISP_PACKED_PIXELS - #define GDISP_PACKED_PIXELS FALSE - #endif - - /** - * @brief Do lines of pixels require packing for a blit - * @note Ignored if GDISP_PACKED_PIXELS is FALSE - */ - #ifndef GDISP_PACKED_LINES - #define GDISP_PACKED_LINES FALSE - #endif -/** @} */ - -/*===========================================================================*/ -/* Defines related to the pixel format */ -/*===========================================================================*/ - -/* Load our color definitions and pixel formats */ -#include "gdisp_colors.h" - -/** - * @brief The type of a pixel. - */ -typedef color_t pixel_t; - -#ifdef __cplusplus -extern "C" { -#endif - -/* Color Utility Functions */ - -/** - * @brief Blend 2 colors according to the alpha - * @return The combined color - * - * @param[in] fg The foreground color - * @param[in] bg The background color - * @param[in] alpha The alpha value (0-255). 0 is all background, 255 is all foreground. - * - * @api - */ -color_t gdispBlendColor(color_t fg, color_t bg, uint8_t alpha); - -/** - * @brief Find a contrasting color - * @return The contrasting color - * - * @param[in] color The color to contrast - * - * @api - */ -color_t gdispContrastColor(color_t color); - -/* Base Functions */ - -/** - * @brief Get the specified display - * @return The pointer to the display or NULL if the display doesn't exist - * @note The GDISP variable contains the display used by the gdispXxxx routines - * as opposed to the gdispGXxxx routines which take an explicit display - * parameter. - * @note Displays are numbered from 0 to @p gdispGetDisplayCount() - 1 - * - * @param[in] display The display number (0..n) - * - * @api - */ -GDisplay *gdispGetDisplay(unsigned display); - -/** - * @brief Set the current default display to the specified display - * @note The default display is used for the gdispXxxx functions. - * @note The default display is contained in the variable GDISP. Using - * this function to set it protects against it being set to a NULL - * value. - * @note If a NULL is passed for the dispay this call is ignored. - * - * @param[in] g The display to use - * - * @api - */ -void gdispSetDisplay(GDisplay *g); - -/** - * @brief Get the count of currently active displays - * @return The count of displays currently in the system - * - * @note Displays are numbered from 0 to @p gdispGetDisplayCount() - 1 - */ -unsigned gdispGetDisplayCount(void); - -/* Property Functions */ - -/** - * @brief Get the display width in pixels. - * - * @param[in] g The display to use - * - * @return The width of the display - * - * @api - */ -coord_t gdispGGetWidth(GDisplay *g); -#define gdispGetWidth() gdispGGetWidth(GDISP) - -/** - * @brief Get the display height in pixels. - * - * @param[in] g The display to use - * - * @return The height of the display - * - * @api - */ -coord_t gdispGGetHeight(GDisplay *g); -#define gdispGetHeight() gdispGGetHeight(GDISP) - -/** - * @brief Get the current display power mode. - * - * @param[in] g The display to use - * - * @return The current power mode - * - * @api - */ -powermode_t gdispGGetPowerMode(GDisplay *g); -#define gdispGetPowerMode() gdispGGetPowerMode(GDISP) - -/** - * @brief Get the current display orientation. - * - * @param[in] g The display to use - * - * @return The current orientation - * - * @api - */ -orientation_t gdispGGetOrientation(GDisplay *g); -#define gdispGetOrientation() gdispGGetOrientation(GDISP) - -/** - * @brief Get the current display backlight brightness. - * - * @param[in] g The display to use - * - * @return The current backlight value - * - * @api - */ -uint8_t gdispGGetBacklight(GDisplay *g); -#define gdispGetBacklight() gdispGGetBacklight(GDISP) - -/** - * @brief Get the current display contrast. - * - * @param[in] g The display to use - * - * @return The current contrast value - * - * @api - */ -uint8_t gdispGGetContrast(GDisplay *g); -#define gdispGetContrast() gdispGGetContrast(GDISP) - -/* Drawing Functions */ - -/** - * @brief Flush current drawing operations to the display - * @note Some low level drivers do not update the display until - * the display is flushed. For others it is optional but can - * help prevent tearing effects. For some it is ignored. - * Calling it at the end of a logic set of drawing operations - * in your application will ensure controller portability. If you - * know your controller does not need to be flushed there is no - * need to call it (which is in reality most controllers). - * @note Even for displays that require flushing, there is no need to - * call this function if GDISP_NEED_AUTOFLUSH is TRUE. - * Calling it again won't hurt though. - * - * - * @param[in] g The display to use - * - * @api - */ -void gdispGFlush(GDisplay *g); -#define gdispFlush() gdispGFlush(GDISP) - -/** - * @brief Clear the display to the specified color. - * - * @param[in] g The display to use - * @param[in] color The color to use when clearing the screen - * - * @api - */ -void gdispGClear(GDisplay *g, color_t color); -#define gdispClear(c) gdispGClear(GDISP, c) - -/** - * @brief Set a pixel in the specified color. - * - * @param[in] g The display to use - * @param[in] x,y The position to set the pixel. - * @param[in] color The color to use - * - * @api - */ -void gdispGDrawPixel(GDisplay *g, coord_t x, coord_t y, color_t color); -#define gdispDrawPixel(x,y,c) gdispGDrawPixel(GDISP,x,y,c) - -/** - * @brief Draw a line. - * - * @param[in] g The display to use - * @param[in] x0,y0 The start position - * @param[in] x1,y1 The end position - * @param[in] color The color to use - * - * @api - */ -void gdispGDrawLine(GDisplay *g, coord_t x0, coord_t y0, coord_t x1, coord_t y1, color_t color); -#define gdispDrawLine(x0,y0,x1,y1,c) gdispGDrawLine(GDISP,x0,y0,x1,y1,c) - -/** - * @brief Fill an area with a color. - * - * @param[in] g The display to use - * @param[in] x,y The start position - * @param[in] cx,cy The size of the box (outside dimensions) - * @param[in] color The color to use - * - * @api - */ -void gdispGFillArea(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, color_t color); -#define gdispFillArea(x,y,cx,cy,c) gdispGFillArea(GDISP,x,y,cx,cy,c) - -/** - * @brief Fill an area using the supplied bitmap. - * @details The bitmap is in the pixel format specified by the low level driver - * @note If a packed pixel format is used and the width doesn't - * match a whole number of bytes, the next line will start on a - * non-byte boundary (no end-of-line padding). - * @note If GDISP_NEED_ASYNC is defined then the buffer must be static - * or at least retained until this call has finished the blit. You can - * tell when all graphics drawing is finished by @p gdispIsBusy() going FALSE. - * - * @param[in] g The display to use - * @param[in] x,y The start position - * @param[in] cx,cy The size of the filled area - * @param[in] srcx,srcy The bitmap position to start the fill form - * @param[in] srccx The width of a line in the bitmap - * @param[in] buffer The bitmap in the driver's pixel format - * - * @api - */ -void gdispGBlitArea(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t srcx, coord_t srcy, coord_t srccx, const pixel_t *buffer); -#define gdispBlitAreaEx(x,y,cx,cy,sx,sy,rx,b) gdispGBlitArea(GDISP,x,y,cx,cy,sx,sy,rx,b) - -/** - * @brief Draw a rectangular box. - * - * @param[in] g The display to use - * @param[in] x,y The start position - * @param[in] cx,cy The size of the box (outside dimensions) - * @param[in] color The color to use - * - * @api - */ -void gdispGDrawBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, color_t color); -#define gdispDrawBox(x,y,cx,cy,c) gdispGDrawBox(GDISP,x,y,cx,cy,c) - -/* Streaming Functions */ - -#if GDISP_NEED_STREAMING || defined(__DOXYGEN__) - /** - * @brief Start a streaming operation. - * @details Stream data to a window on the display sequentially and very fast. - * @pre GDISP_NEED_STREAMING must be TRUE in your gfxconf.h - * @note While streaming is in operation - no other calls to GDISP functions - * can be made (with the exception of @p gdispBlendColor() and streaming - * functions). If a call is made (eg in a multi-threaded application) the other - * call is blocked waiting for the streaming operation to finish. - * @note @p gdispStreamStop() must be called to finish the streaming operation. - * @note If more data is written than the defined area then the results are unspecified. - * Some drivers may wrap back to the beginning of the area, others may just - * ignore subsequent data. - * @note Unlike most operations that clip the defined area to the display to generate - * a smaller active area, this call will just silently fail if any of the stream - * region lies outside the current clipping area. - * @note A streaming operation may be terminated early (without writing to every location - * in the stream area) by calling @p gdispStreamStop(). - * - * @param[in] g The display to use - * @param[in] x,y The start position - * @param[in] cx,cy The size of the streamable area - * - * @api - */ - void gdispGStreamStart(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy); - #define gdispStreamStart(x,y,cx,cy) gdispGStreamStart(GDISP,x,y,cx,cy) - - /** - * @brief Send pixel data to the stream. - * @details Write a pixel to the next position in the streamed area and increment the position - * @pre GDISP_NEED_STREAMING must be TRUE in your gfxconf.h - * @pre @p gdispStreamStart() has been called. - * @note If the gdispStreamStart() has not been called (or failed due to clipping), the - * data provided here is simply thrown away. - * - * @param[in] g The display to use - * @param[in] color The color of the pixel to write - * - * @api - */ - void gdispGStreamColor(GDisplay *g, color_t color); - #define gdispStreamColor(c) gdispGStreamColor(GDISP,c) - - /** - * @brief Finish the current streaming operation. - * @details Completes the current streaming operation and allows other GDISP calls to operate again. - * @pre GDISP_NEED_STREAMING must be TRUE in your gfxconf.h - * @pre @p gdispStreamStart() has been called. - * @note If the gdispStreamStart() has not been called (or failed due to clipping), this - * call is simply ignored. - * - * @param[in] g The display to use - * - * @api - */ - void gdispGStreamStop(GDisplay *g); - #define gdispStreamStop() gdispGStreamStop(GDISP) -#endif - -/* Clipping Functions */ - -#if GDISP_NEED_CLIP || defined(__DOXYGEN__) - /** - * @brief Clip all drawing to the defined area. - * @pre GDISP_NEED_CLIP must be TRUE in your gfxconf.h - * - * @param[in] g The display to use - * @param[in] x,y The start position - * @param[in] cx,cy The size of the clip area - * - * @api - */ - void gdispGSetClip(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy); - #define gdispSetClip(x,y,cx,cy) gdispGSetClip(GDISP,x,y,cx,cy) -#endif - -/* Circle Functions */ - -#if GDISP_NEED_CIRCLE || defined(__DOXYGEN__) - /** - * @brief Draw a circle. - * @pre GDISP_NEED_CIRCLE must be TRUE in your gfxconf.h - * - * @param[in] g The display to use - * @param[in] x,y The center of the circle - * @param[in] radius The radius of the circle - * @param[in] color The color to use - * - * @api - */ - void gdispGDrawCircle(GDisplay *g, coord_t x, coord_t y, coord_t radius, color_t color); - #define gdispDrawCircle(x,y,r,c) gdispGDrawCircle(GDISP,x,y,r,c) - - /** - * @brief Draw a filled circle. - * @pre GDISP_NEED_CIRCLE must be TRUE in your gfxconf.h - * - * @param[in] g The display to use - * @param[in] x,y The center of the circle - * @param[in] radius The radius of the circle - * @param[in] color The color to use - * - * @api - */ - void gdispGFillCircle(GDisplay *g, coord_t x, coord_t y, coord_t radius, color_t color); - #define gdispFillCircle(x,y,r,c) gdispGFillCircle(GDISP,x,y,r,c) -#endif - -/* Ellipse Functions */ - -#if GDISP_NEED_ELLIPSE || defined(__DOXYGEN__) - /** - * @brief Draw an ellipse. - * @pre GDISP_NEED_ELLIPSE must be TRUE in your gfxconf.h - * - * @param[in] g The display to use - * @param[in] x,y The center of the ellipse - * @param[in] a,b The dimensions of the ellipse - * @param[in] color The color to use - * - * @api - */ - void gdispGDrawEllipse(GDisplay *g, coord_t x, coord_t y, coord_t a, coord_t b, color_t color); - #define gdispDrawEllipse(x,y,a,b,c) gdispGDrawEllipse(GDISP,x,y,a,b,c) - - /** - * @brief Draw a filled ellipse. - * @pre GDISP_NEED_ELLIPSE must be TRUE in your gfxconf.h - * - * @param[in] g The display to use - * @param[in] x,y The center of the ellipse - * @param[in] a,b The dimensions of the ellipse - * @param[in] color The color to use - * - * @api - */ - void gdispGFillEllipse(GDisplay *g, coord_t x, coord_t y, coord_t a, coord_t b, color_t color); - #define gdispFillEllipse(x,y,a,b,c) gdispGFillEllipse(GDISP,x,y,a,b,c) -#endif - -/* Arc Functions */ -#if GDISP_NEED_ARCSECTORS || defined(__DOXYGEN__) - /** - * @brief Draw a selection of 45 degree arcs of a circle - * @pre GDISP_NEED_ARCSECTORS must be TRUE in your gfxconf.h - * - * @param[in] g The display to use - * @param[in] x,y The center of the circle - * @param[in] radius The radius of the circle - * @param[in] sectors Bits determine which sectors are drawn. - * Bits go anti-clockwise from the 0 degree mark (y = 0, x is positive), as follows: - * bit 0 - upper right right ----- - * bit 1 - upper upper right /2 1\ - * bit 2 - upper upper left /3 0\ - * bit 3 - upper left left \4 7/ - * bit 4 - lower left left \5 6/ - * bit 5 - lower lower left ----- - * bit 6 - lower lower right - * bit 7 - lower left left - * @param[in] color The color to use - * - * @note This is a more limited versions of the general arc drawing routine. It - * doesn't require trig libraries or tables or floating point and is smaller in code size. - * There is probably little point in including both this and the general - * arc routine as the general arc routine can do everything this can do. - * - * @api - */ - void gdispGDrawArcSectors(GDisplay *g, coord_t x, coord_t y, coord_t radius, uint8_t sectors, color_t color); - #define gdispDrawArcSectors(x,y,r,s,c) gdispGDrawArcSectors(GDISP,x,y,r,s,c) - - /** - * @brief Fill a selection of 45 degree arcs of a circle - * @pre GDISP_NEED_ARCSECTORS must be TRUE in your gfxconf.h - * - * @param[in] g The display to use - * @param[in] x,y The center of the circle - * @param[in] radius The radius of the circle - * @param[in] sectors Bits determine which sectors are drawn. - * Bits go anti-clockwise from the 0 degree mark (y = 0, x is positive), as follows: - * bit 0 - upper right right ----- - * bit 1 - upper upper right /2 1\ - * bit 2 - upper upper left /3 0\ - * bit 3 - upper left left \4 7/ - * bit 4 - lower left left \5 6/ - * bit 5 - lower lower left ----- - * bit 6 - lower lower right - * bit 7 - lower left left - * @param[in] color The color to use - * - * @note This is a more limited versions of the general arc filling routine. It - * doesn't require trig libraries or tables or floating point and is smaller in code size. - * There is probably little point in including both this and the general - * arc routine as the general arc routine can do everything this can do. - * - * @api - */ - void gdispGFillArcSectors(GDisplay *g, coord_t x, coord_t y, coord_t radius, uint8_t sectors, color_t color); - #define gdispFillArcSectors(x,y,r,s,c) gdispGFillArcSectors(GDISP,x,y,r,s,c) -#endif - -#if GDISP_NEED_ARC || defined(__DOXYGEN__) - /* - * @brief Draw an arc. - * @pre GDISP_NEED_ARC must be TRUE in your gfxconf.h - * - * @param[in] g The display to use - * @param[in] x0,y0 The center point - * @param[in] radius The radius of the arc - * @param[in] start The start angle (0 to 360) - * @param[in] end The end angle (0 to 360) - * @param[in] color The color of the arc - * - * @note If you are just doing 45 degree angles consider using @p gdispDrawArcSectors() instead. - * @note This routine requires trig support. It can either come from your C runtime library - * cos() and sin() which requires floating point support (and is slow), or you can define GFX_USE_GMISC - * and either GMISC_NEED_FIXEDTRIG or GMISC_NEED_FASTTRIG. - * GMISC_NEED_FASTTRIG uses table based floating point trig operations. - * GMISC_NEED_FIXEDTRIG uses fixed point integer trig operations. - * Note accuracy on both the table based options are more than adequate for the one degree - * resolution provided by these arc routines. Both are much faster than your C runtime library. - * - * @api - */ - void gdispGDrawArc(GDisplay *g, coord_t x, coord_t y, coord_t radius, coord_t startangle, coord_t endangle, color_t color); - #define gdispDrawArc(x,y,r,s,e,c) gdispGDrawArc(GDISP,x,y,r,s,e,c) - - /* - * @brief Draw a filled arc. - * @pre GDISP_NEED_ARC must be TRUE in your gfxconf.h - * - * @param[in] g The display to use - * @param[in] x0,y0 The center point - * @param[in] radius The radius of the arc - * @param[in] start The start angle (0 to 360) - * @param[in] end The end angle (0 to 360) - * @param[in] color The color of the arc - * - * @note If you are just doing 45 degree angles consider using @p gdispFillArcSectors() instead. - * @note This routine requires trig support. It can either come from your C runtime library - * cos() and sin() which requires floating point support (and is slow), or you can define GFX_USE_GMISC - * and either GMISC_NEED_FIXEDTRIG or GMISC_NEED_FASTTRIG. - * GMISC_NEED_FASTTRIG uses table based floating point trig operations. - * GMISC_NEED_FIXEDTRIG uses fixed point integer trig operations. - * Note accuracy on both the table based options are more than adequate for the one degree - * resolution provided by these arc routines. Both are much faster than your C runtime library. - * - * @api - */ - void gdispGFillArc(GDisplay *g, coord_t x, coord_t y, coord_t radius, coord_t startangle, coord_t endangle, color_t color); - #define gdispFillArc(x,y,r,s,e,c) gdispGFillArc(GDISP,x,y,r,s,e,c) -#endif - -/* Read a pixel Function */ - -#if GDISP_NEED_PIXELREAD || defined(__DOXYGEN__) - /** - * @brief Get the color of a pixel. - * @return The color of the pixel. - * @pre GDISP_NEED_PIXELREAD must be TRUE in your gfxconf.h - * - * @param[in] g The display to use - * @param[in] x,y The position of the pixel - * - * @api - */ - color_t gdispGGetPixelColor(GDisplay *g, coord_t x, coord_t y); - #define gdispGetPixelColor(x,y) gdispGGetPixelColor(GDISP,x,y) -#endif - -/* Scrolling Function - clears the area scrolled out */ - -#if GDISP_NEED_SCROLL || defined(__DOXYGEN__) - /** - * @brief Scroll vertically a section of the screen. - * @pre GDISP_NEED_SCROLL must be set to TRUE in gfxconf.h - * @note Optional. - * @note If lines is >= cy, it is equivelent to a area fill with bgcolor. - * - * @param[in] g The display to use - * @param[in] x, y The start of the area to be scrolled - * @param[in] cx, cy The size of the area to be scrolled - * @param[in] lines The number of lines to scroll (Can be positive or negative) - * @param[in] bgcolor The color to fill the newly exposed area. - * - * @api - */ - void gdispGVerticalScroll(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, int lines, color_t bgcolor); - #define gdispVerticalScroll(x,y,cx,cy,l,b) gdispGVerticalScroll(GDISP,x,y,cx,cy,l,b) -#endif - -/* Set driver specific control */ - -#if GDISP_NEED_CONTROL || defined(__DOXYGEN__) - /** - * @brief Control hardware specific parts of the display. eg powermodes, backlight etc - * @pre GDISP_NEED_CONTROL must be TRUE in your gfxconf.h - * @note Depending on the hardware implementation this function may not - * support some codes. They will be ignored. - * - * @param[in] g The display to use - * @param[in] what what you want to control - * @param[in] value The value to be assigned - * - * @api - */ - void gdispGControl(GDisplay *g, unsigned what, void *value); - #define gdispControl(w,v) gdispGControl(GDISP,w,v) -#endif - -/* Query driver specific data */ - -#if GDISP_NEED_QUERY || defined(__DOXYGEN__) - /** - * @brief Query a property of the display. - * @pre GDISP_NEED_QUERY must be TRUE in your gfxconf.h - * @note The result must be typecast to the correct type. - * @note An unsupported query will return (void *)-1. - * - * @param[in] g The display to use - * @param[in] what What to query - * - * @api - */ - void *gdispGQuery(GDisplay *g, unsigned what); - #define gdispQuery(w) gdispGQuery(GDISP,w) -#endif - -#if GDISP_NEED_CONVEX_POLYGON || defined(__DOXYGEN__) - /** - * @brief Draw an enclosed polygon (convex, non-convex or complex). - * @pre GDISP_NEED_CONVEX_POLYGON must be TRUE in your gfxconf.h - * - * @param[in] g The display to use - * @param[in] tx, ty Transform all points in pntarray by tx, ty - * @param[in] pntarray An array of points - * @param[in] cnt The number of points in the array - * @param[in] color The color to use - * - * @api - */ - void gdispGDrawPoly(GDisplay *g, coord_t tx, coord_t ty, const point *pntarray, unsigned cnt, color_t color); - #define gdispDrawPoly(x,y,p,i,c) gdispGDrawPoly(GDISP,x,y,p,i,c) - - /** - * @brief Fill a convex polygon - * @details Doesn't handle non-convex or complex polygons. - * @pre GDISP_NEED_CONVEX_POLYGON must be TRUE in your gfxconf.h - * - * @param[in] g The display to use - * @param[in] tx, ty Transform all points in pntarray by tx, ty - * @param[in] pntarray An array of points - * @param[in] cnt The number of points in the array - * @param[in] color The color to use - * - * @note Convex polygons are those that have no internal angles. That is; - * you can draw a line from any point on the polygon to any other point - * on the polygon without it going outside the polygon. In our case we generalise - * this a little by saying that an infinite horizontal line (at any y value) will cross - * no more than two edges on the polygon. Some non-convex polygons do fit this criteria - * and can therefore be drawn. - * @note This routine is designed to be very efficient with even simple display hardware. - * - * @api - */ - void gdispGFillConvexPoly(GDisplay *g, coord_t tx, coord_t ty, const point *pntarray, unsigned cnt, color_t color); - #define gdispFillConvexPoly(x,y,p,i,c) gdispGFillConvexPoly(GDISP,x,y,p,i,c) - - /** - * @brief Draw a line with a specified thickness - * @details The line thickness is specified in pixels. The line ends can - * be selected to be either flat or round. - * @pre GDISP_NEED_CONVEX_POLYGON must be TRUE in your gfxconf.h - * @note Uses gdispGFillConvexPoly() internally to perform the drawing. - * - * @param[in] g The display to use - * @param[in] x0,y0 The start position - * @param[in] x1,y1 The end position - * @param[in] color The color to use - * @param[in] width The width of the line - * @param[in] round Use round ends for the line - * - * @api - */ - void gdispGDrawThickLine(GDisplay *g, coord_t x0, coord_t y0, coord_t x1, coord_t y1, color_t color, coord_t width, bool_t round); - #define gdispDrawThickLine(x0,y0,x1,y1,c,w,r) gdispGDrawThickLine(GDISP,x0,y0,x1,y1,c,w,r) -#endif - -/* Text Functions */ - -#if GDISP_NEED_TEXT || defined(__DOXYGEN__) - /** - * @brief Draw a text character. - * @pre GDISP_NEED_TEXT must be TRUE in your gfxconf.h - * - * @param[in] g The display to use - * @param[in] x,y The position for the text - * @param[in] c The character to draw - * @param[in] font The font to use - * @param[in] color The color to use - * - * @api - */ - void gdispGDrawChar(GDisplay *g, coord_t x, coord_t y, uint16_t c, font_t font, color_t color); - #define gdispDrawChar(x,y,s,f,c) gdispGDrawChar(GDISP,x,y,s,f,c) - - /** - * @brief Draw a text character with a filled background. - * @pre GDISP_NEED_TEXT must be TRUE in your gfxconf.h - * - * @param[in] g The display to use - * @param[in] x,y The position for the text - * @param[in] c The character to draw - * @param[in] font The font to use - * @param[in] color The color to use - * @param[in] bgcolor The background color to use - * - * @api - */ - void gdispGFillChar(GDisplay *g, coord_t x, coord_t y, uint16_t c, font_t font, color_t color, color_t bgcolor); - #define gdispFillChar(x,y,s,f,c,b) gdispGFillChar(GDISP,x,y,s,f,c,b) - - /** - * @brief Draw a text string. - * @pre GDISP_NEED_TEXT must be TRUE in your gfxconf.h - * - * @param[in] g The display to use - * @param[in] x,y The position for the text - * @param[in] str The string to draw - * @param[in] font The font to use - * @param[in] color The color to use - * - * @api - */ - void gdispGDrawString(GDisplay *g, coord_t x, coord_t y, const char *str, font_t font, color_t color); - #define gdispDrawString(x,y,s,f,c) gdispGDrawString(GDISP,x,y,s,f,c) - - /** - * @brief Draw a text string. - * @pre GDISP_NEED_TEXT must be TRUE in your gfxconf.h - * - * @param[in] g The display to use - * @param[in] x,y The position for the text - * @param[in] str The string to draw - * @param[in] font The font to use - * @param[in] color The color to use - * @param[in] bgcolor The background color to use - * - * @api - */ - void gdispGFillString(GDisplay *g, coord_t x, coord_t y, const char *str, font_t font, color_t color, color_t bgcolor); - #define gdispFillString(x,y,s,f,c,b) gdispGFillString(GDISP,x,y,s,f,c,b) - - /** - * @brief Draw a text string vertically centered within the specified box. - * @pre GDISP_NEED_TEXT must be TRUE in your gfxconf.h - * - * @param[in] g The display to use - * @param[in] x,y The position for the text (need to define top-right or base-line - check code) - * @param[in] cx,cy The width and height of the box - * @param[in] str The string to draw - * @param[in] font The font to use - * @param[in] color The color to use - * @param[in] justify Justify the text left, center or right within the box - * - * @api - */ - void gdispGDrawStringBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, const char* str, font_t font, color_t color, justify_t justify); - #define gdispDrawStringBox(x,y,cx,cy,s,f,c,j) gdispGDrawStringBox(GDISP,x,y,cx,cy,s,f,c,j) - - /** - * @brief Draw a text string vertically centered within the specified box. The box background is filled with the specified background color. - * @pre GDISP_NEED_TEXT must be TRUE in your gfxconf.h - * @note The entire box is filled - * - * @param[in] g The display to use - * @param[in] x,y The position for the text (need to define top-right or base-line - check code) - * @param[in] cx,cy The width and height of the box - * @param[in] str The string to draw - * @param[in] font The font to use - * @param[in] color The color to use - * @param[in] bgColor The background color to use - * @param[in] justify Justify the text left, center or right within the box - * - * @api - */ - void gdispGFillStringBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, const char* str, font_t font, color_t color, color_t bgColor, justify_t justify); - #define gdispFillStringBox(x,y,cx,cy,s,f,c,b,j) gdispGFillStringBox(GDISP,x,y,cx,cy,s,f,c,b,j) - - /** - * @brief Get a metric of a font. - * @return The metric requested in pixels. - * @pre GDISP_NEED_TEXT must be TRUE in your gfxconf.h - * - * @param[in] font The font to test - * @param[in] metric The metric to measure - * - * @api - */ - coord_t gdispGetFontMetric(font_t font, fontmetric_t metric); - - /** - * @brief Get the pixel width of a character. - * @return The width of the character in pixels. Does not include any between character padding. - * @pre GDISP_NEED_TEXT must be TRUE in your gfxconf.h - * - * @param[in] c The character to draw - * @param[in] font The font to use - * - * @api - */ - coord_t gdispGetCharWidth(char c, font_t font); - - /** - * @brief Get the pixel width of a string. - * @return The width of the string in pixels. - * @pre GDISP_NEED_TEXT must be TRUE in your gfxconf.h - * - * @param[in] str The string to measure - * @param[in] font The font to use - * - * @api - */ - coord_t gdispGetStringWidth(const char* str, font_t font); - - /** - * @brief Find a font and return it. - * @details The supplied name is matched against the font name. A '*' will replace 0 or more characters. - * @return Returns a font or NULL if no matching font could be found. - * @pre GDISP_NEED_TEXT must be TRUE in your gfxconf.h - * - * @param[in] name The font name to find. - * - * @note Wildcard matching will match the shortest possible match. - * - * @api - */ - font_t gdispOpenFont(const char *name); - - /** - * @brief Release a font after use. - * @pre GDISP_NEED_TEXT must be TRUE in your gfxconf.h - * - * @param[in] font The font to release. - * - * @api - */ - void gdispCloseFont(font_t font); - - /** - * @brief Make a scaled copy of an existing font. - * @details Allocates memory for new font metadata using gfxAlloc, remember to close font after use! - * @return A new font or NULL if out of memory. - * @pre GDISP_NEED_TEXT must be TRUE in your gfxconf.h - * - * @param[in] font The base font to use. - * @param[in] scale_x The scale factor in horizontal direction. - * @param[in] scale_y The scale factor in vertical direction. - */ - font_t gdispScaleFont(font_t font, uint8_t scale_x, uint8_t scale_y); - - /** - * @brief Get the name of the specified font. - * @returns The name of the font. - * @pre GDISP_NEED_TEXT must be TRUE in your gfxconf.h - * - * @param[in] font The font to get the name for. - * - * @api - */ - const char *gdispGetFontName(font_t font); -#endif - -/* Extra Arc Functions */ - -#if GDISP_NEED_ARC || GDISP_NEED_ARCSECTORS || defined(__DOXYGEN__) - /** - * @brief Draw a rectangular box with rounded corners - * @pre GDISP_NEED_ARC or GDISP_NEED_ARCSECTORS must be TRUE in your gfxconf.h - * - * @param[in] g The display to use - * @param[in] x,y The start position - * @param[in] cx,cy The size of the box (outside dimensions) - * @param[in] radius The radius of the rounded corners - * @param[in] color The color to use - * - * @api - */ - void gdispGDrawRoundedBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t radius, color_t color); - #define gdispDrawRoundedBox(x,y,cx,cy,r,c) gdispGDrawRoundedBox(GDISP,x,y,cx,cy,r,c) - - /** - * @brief Draw a filled rectangular box with rounded corners - * @pre GDISP_NEED_ARC or GDISP_NEED_ARCSECTORS must be TRUE in your gfxconf.h - * - * @param[in] g The display to use - * @param[in] x,y The start position - * @param[in] cx,cy The size of the box (outside dimensions) - * @param[in] radius The radius of the rounded corners - * @param[in] color The color to use - * - * @api - */ - void gdispGFillRoundedBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t radius, color_t color); - #define gdispFillRoundedBox(x,y,cx,cy,r,c) gdispGFillRoundedBox(GDISP,x,y,cx,cy,r,c) -#endif - -/* - * Macro definitions - */ - -/* Now obsolete functions */ -#define gdispBlitArea(x, y, cx, cy, buffer) gdispGBlitArea(GDISP, x, y, cx, cy, 0, 0, cx, buffer) - -/* Macro definitions for common gets and sets */ - -/** - * @brief Set the display power mode. - * @note Ignored if not supported by the display. - * - * @param[in] g The display to use - * @param[in] powerMode The new power mode - * - * @api - */ -#define gdispGSetPowerMode(g, powerMode) gdispGControl((g), GDISP_CONTROL_POWER, (void *)(unsigned)(powerMode)) -#define gdispSetPowerMode(powerMode) gdispGControl(GDISP, GDISP_CONTROL_POWER, (void *)(unsigned)(powerMode)) - -/** - * @brief Set the display orientation. - * @note Ignored if not supported by the display. - * - * @param[in] g The display to use - * @param[in] newOrientation The new orientation - * - * @api - */ -#define gdispGSetOrientation(g, newOrientation) gdispGControl((g), GDISP_CONTROL_ORIENTATION, (void *)(unsigned)(newOrientation)) -#define gdispSetOrientation(newOrientation) gdispGControl(GDISP, GDISP_CONTROL_ORIENTATION, (void *)(unsigned)(newOrientation)) - -/** - * @brief Set the display backlight. - * @note Ignored if not supported by the display. - * - * @param[in] g The display to use - * @param[in] percent The new brightness (0 - 100%) - * - * @note For displays that only support backlight off and on, - * 0 = off, anything else = on - * - * @api - */ -#define gdispGSetBacklight(g, percent) gdispGControl((g), GDISP_CONTROL_BACKLIGHT, (void *)(unsigned)(percent)) -#define gdispSetBacklight(percent) gdispGControl(GDISP, GDISP_CONTROL_BACKLIGHT, (void *)(unsigned)(percent)) - -/** - * @brief Set the display contrast. - * @note Ignored if not supported by the display. - * - * @param[in] g The display to use - * @param[in] percent The new contrast (0 - 100%) - * - * @api - */ -#define gdispGSetContrast(g, percent) gdispGControl((g), GDISP_CONTROL_CONTRAST, (void *)(unsigned)(percent)) -#define gdispSetContrast(percent) gdispGControl(GDISP, GDISP_CONTROL_CONTRAST, (void *)(unsigned)(percent)) - -/* More interesting macros */ - -/** - * @brief Reset the clip area to the full screen - * - * @param[in] g The display to use - * - * @api - */ -#define gdispGUnsetClip(g) gdispGSetClip((g),0,0,gdispGGetWidth(g),gdispGGetHeight(g)) -#define gdispUnsetClip() gdispGUnsetClip(GDISP) - -#ifdef __cplusplus -} -#endif - -#if GDISP_NEED_IMAGE || defined(__DOXYGEN__) - #include "gdisp_image.h" -#endif -#if GDISP_NEED_PIXMAP || defined(__DOXYGEN__) - #include "gdisp_pixmap.h" -#endif - - -#endif /* GFX_USE_GDISP */ - -#endif /* _GDISP_H */ -/** @} */ diff --git a/src/gdisp/sys_make.mk b/src/gdisp/sys_make.mk deleted file mode 100644 index 34710597..00000000 --- a/src/gdisp/sys_make.mk +++ /dev/null @@ -1,14 +0,0 @@ -GFXSRC += $(GFXLIB)/src/gdisp/gdisp_gdisp.c \ - $(GFXLIB)/src/gdisp/gdisp_fonts.c \ - $(GFXLIB)/src/gdisp/gdisp_pixmap.c \ - $(GFXLIB)/src/gdisp/gdisp_image.c \ - $(GFXLIB)/src/gdisp/gdisp_image_native.c \ - $(GFXLIB)/src/gdisp/gdisp_image_gif.c \ - $(GFXLIB)/src/gdisp/gdisp_image_bmp.c \ - $(GFXLIB)/src/gdisp/gdisp_image_jpg.c \ - $(GFXLIB)/src/gdisp/gdisp_image_png.c - -MFDIR = $(GFXLIB)/src/gdisp/mcufont -include $(GFXLIB)/src/gdisp/mcufont/mcufont.mk -GFXINC += $(MFDIR) -GFXSRC += $(MFSRC) diff --git a/src/gdisp/sys_options.h b/src/gdisp/sys_options.h deleted file mode 100644 index ebb054e1..00000000 --- a/src/gdisp/sys_options.h +++ /dev/null @@ -1,382 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gdisp/sys_options.h - * @brief GDISP sub-system options header file. - * - * @addtogroup GDISP - * @{ - */ - -#ifndef _GDISP_OPTIONS_H -#define _GDISP_OPTIONS_H - -/** - * @name GDISP Functionality to be included - * @{ - */ - /** - * @brief Should drawing operations be automatically flushed. - * @details Defaults to FALSE - * @note If set to FALSE and the controller requires flushing - * then the application must manually call @p gdispGFlush(). - * Setting this to TRUE causes GDISP to automatically flush - * after each drawing operation. Note this may be slow but enables - * an application to avoid having to manually call the flush routine. - * @note If TRUE and GDISP_NEED_TIMERFLUSH is also TRUE, this takes precedence. - * @note Most controllers don't need flushing which is why this is set to - * FALSE by default. - */ - #ifndef GDISP_NEED_AUTOFLUSH - #define GDISP_NEED_AUTOFLUSH FALSE - #endif - /** - * @brief Should drawing operations be automatically flushed on a timer. - * @details Defaults to FALSE, Can be set to FALSE or a timer period in milliseconds. - * @note The period should not be set too short or it will consume all your CPU. A - * value between 250 and 500 milliseconds would probably be suitable. - * @note If TRUE and GDISP_NEED_AUTOFLUSH is also TRUE, this is ineffective. - * @note Most controllers don't need flushing which is why this is set to - * FALSE by default. - */ - #ifndef GDISP_NEED_TIMERFLUSH - #define GDISP_NEED_TIMERFLUSH FALSE - #endif - /** - * @brief Should all operations be clipped to the screen and colors validated. - * @details Defaults to TRUE. - * @note If this is FALSE, any operations that extend beyond the - * edge of the screen will have undefined results. Any - * out-of-range colors will produce undefined results. - * @note This should always be left as the default (TRUE) unless you - * are a maniac for speed and you have thoroughly tested your code - * and it never overwrites the edges of the screen. - * @note Setting GDISP_NEED_CLIP to TRUE internally uses the same mechanism - * as this validation. There is no advantage in setting this FALSE if - * GDISP_NEED_CLIP is TRUE. - */ - #ifndef GDISP_NEED_VALIDATION - #define GDISP_NEED_VALIDATION TRUE - #endif - /** - * @brief Are clipping functions needed. - * @details Defaults to TRUE - */ - #ifndef GDISP_NEED_CLIP - #define GDISP_NEED_CLIP TRUE - #endif - /** - * @brief Streaming functions are needed - * @details Defaults to FALSE. - */ - #ifndef GDISP_NEED_STREAMING - #define GDISP_NEED_STREAMING FALSE - #endif - /** - * @brief Are text functions needed. - * @details Defaults to FALSE - * @note You must also define at least one font. - */ - #ifndef GDISP_NEED_TEXT - #define GDISP_NEED_TEXT FALSE - #endif - /** - * @brief Are circle functions needed. - * @details Defaults to FALSE - * @note Uses integer algorithms only. It does not use any trig or floating point. - */ - #ifndef GDISP_NEED_CIRCLE - #define GDISP_NEED_CIRCLE FALSE - #endif - /** - * @brief Are ellipse functions needed. - * @details Defaults to FALSE - * @note Uses integer algorithms only. It does not use any trig or floating point. - */ - #ifndef GDISP_NEED_ELLIPSE - #define GDISP_NEED_ELLIPSE FALSE - #endif - /** - * @brief Are arc sector functions needed. - * @details Defaults to FALSE - * @note Uses integer algorithms only. It does not use any trig or floating point. - */ - #ifndef GDISP_NEED_ARCSECTORS - #define GDISP_NEED_ARCSECTORS FALSE - #endif - /** - * @brief Are arc functions needed. - * @details Defaults to FALSE - * @note This can be compiled using fully integer mathematics by - * defining GFX_USE_GMISC and GMISC_NEED_FIXEDTRIG as TRUE. - * @note This can be compiled to use floating point but no trig functions - * by defining GFX_USE_GMISC and GMISC_NEED_FASTTRIG as TRUE. - * @note If neither of the above are defined it requires the maths library - * to be included in the link to provide floating point and trig support. - * ie include -lm in your compiler flags. - */ - #ifndef GDISP_NEED_ARC - #define GDISP_NEED_ARC FALSE - #endif - /** - * @brief Are convex polygon functions needed. - * @details Defaults to FALSE - * @note Convex polygons are those that have no internal angles. That is; - * you can draw a line from any point on the polygon to any other point - * on the polygon without it going outside the polygon. - */ - #ifndef GDISP_NEED_CONVEX_POLYGON - #define GDISP_NEED_CONVEX_POLYGON FALSE - #endif - /** - * @brief Are scrolling functions needed. - * @details Defaults to FALSE - * @note This function must be supported by the low level GDISP driver - * you have included in your project. If it isn't, defining this - * option will cause a compile error. - */ - #ifndef GDISP_NEED_SCROLL - #define GDISP_NEED_SCROLL FALSE - #endif - /** - * @brief Is the capability to read pixels back needed. - * @details Defaults to FALSE - * @note This function must be supported by the low level GDISP driver - * you have included in your project. If it isn't, defining this - * option will cause a compile error. - */ - #ifndef GDISP_NEED_PIXELREAD - #define GDISP_NEED_PIXELREAD FALSE - #endif - /** - * @brief Control some aspect of the hardware operation. - * @details Defaults to FALSE - * @note This allows control of hardware specific features such as - * screen rotation, backlight levels, contrast etc - */ - #ifndef GDISP_NEED_CONTROL - #define GDISP_NEED_CONTROL FALSE - #endif - /** - * @brief Query some aspect of the hardware operation. - * @details Defaults to FALSE - * @note This allows query of hardware specific features - */ - #ifndef GDISP_NEED_QUERY - #define GDISP_NEED_QUERY FALSE - #endif - /** - * @brief Is the image interface required. - * @details Defaults to FALSE - */ - #ifndef GDISP_NEED_IMAGE - #define GDISP_NEED_IMAGE FALSE - #endif - /** - * @brief Is the image interface required. - * @details Defaults to FALSE - */ - #ifndef GDISP_NEED_PIXMAP - #define GDISP_NEED_PIXMAP FALSE - #endif -/** - * @} - * - * @name GDISP Multi-Threading Options - * @{ - */ - /** - * @brief Do the drawing functions need to be thread-safe. - * @details Defaults to FALSE - */ - #ifndef GDISP_NEED_MULTITHREAD - #define GDISP_NEED_MULTITHREAD FALSE - #endif -/** - * @} - * - * @name GDISP Optional Parameters - * @{ - */ - /** - * @brief Should the startup logo be displayed - * - * @details Defaults to TRUE - */ - #ifndef GDISP_NEED_STARTUP_LOGO - #define GDISP_NEED_STARTUP_LOGO TRUE - #endif - /** - * @brief Define the initial background color for all displays in the system. - */ - #ifndef GDISP_STARTUP_COLOR - #define GDISP_STARTUP_COLOR Black - #endif - /** - * @brief Define the default orientation for all displays in the system. - * @note GDISP_NEED_CONTROL must also be set (and the hardware must support it) - * @note If not specified then displays default to the native hardware orientation - */ - // #define GDISP_DEFAULT_ORIENTATION GDISP_ROTATE_LANDSCAPE - /** - * @brief The size of pixel buffer (in pixels) used for optimization. - * @details Set to zero to guarantee disabling of the buffer. - * @note Depending on the driver and what operations the application - * needs, this buffer may never be allocated. - * @note Setting the size to zero may cause some operations to not - * compile eg. Scrolling if there is no hardware scroll support. - * @note Increasing the size will speedup certain operations - * at the expense of RAM. - * @note Currently only used to support scrolling on hardware without - * scrolling support, and to increase the speed of streaming - * operations on non-streaming hardware where there is a - * hardware supported bit-blit. - */ - #ifndef GDISP_LINEBUF_SIZE - #define GDISP_LINEBUF_SIZE 128 - #endif -/** - * @} - * - * @name GDISP Multiple Display Support - * @{ - */ - /** - * @brief The total number of displays using the default driver. - * @note If you want to use multiple displays either set GDISP_TOTAL_DISPLAYS or GDISP_DRIVER_LIST - * but not both. - */ - #ifndef GDISP_TOTAL_DISPLAYS - #define GDISP_TOTAL_DISPLAYS 1 - #endif - #if defined(__DOXYGEN__) - /** - * @brief The list of display drivers. - * @note Replace this example with your own definition in your gfxconf.h file. See the gdisp_lld.c - * in each driver (near the top) to get the name of the VMT for a driver. - * @note The same driver can occur more than once in the list to create an extra instance of that driver. - * @note If defining this you must also define GDISP_PIXELFORMAT for your application to use. - * Choose a value that is most common accross all your drivers for efficiency. - * @note If using this you may optionally define the GDISP_HARDWARE_xxx values as either TRUE or FALSE. - * Doing this causes GDISP to assume that all (TRUE) or none (FALSE) of the listed drivers have that - * capability. This can help improve drawing speed and efficiency. - */ - #define GDISP_DRIVER_LIST GDISPVMT_Win32, GDISPVMT_SSD1963 - #endif -/** - * @} - * - * @name GDISP Image Options - * @pre GDISP_NEED_IMAGE must be TRUE - * @{ - */ - /** - * @brief Is native image decoding required. - * @details Defaults to FALSE - */ - #ifndef GDISP_NEED_IMAGE_NATIVE - #define GDISP_NEED_IMAGE_NATIVE FALSE - #endif - /** - * @brief Is GIF image decoding required. - * @details Defaults to FALSE - */ - #ifndef GDISP_NEED_IMAGE_GIF - #define GDISP_NEED_IMAGE_GIF FALSE - #endif - /** - * @brief Is BMP image decoding required. - * @details Defaults to FALSE - */ - #ifndef GDISP_NEED_IMAGE_BMP - #define GDISP_NEED_IMAGE_BMP FALSE - #endif - /** - * @brief Is JPG image decoding required. - * @details Defaults to FALSE - */ - #ifndef GDISP_NEED_IMAGE_JPG - #define GDISP_NEED_IMAGE_JPG FALSE - #endif - /** - * @brief Is PNG image decoding required. - * @details Defaults to FALSE - */ - #ifndef GDISP_NEED_IMAGE_PNG - #define GDISP_NEED_IMAGE_PNG FALSE - #endif - /** - * @brief Is memory accounting required during image decoding. - * @details Defaults to FALSE - */ - #ifndef GDISP_NEED_IMAGE_ACCOUNTING - #define GDISP_NEED_IMAGE_ACCOUNTING FALSE - #endif -/** - * @} - * - * @name GDISP Text Rendering Options - * @{ - */ - /** - * @brief Enable UTF-8 support for text rendering. - * @details Defaults to FALSE - */ - #ifndef GDISP_NEED_UTF8 - #define GDISP_NEED_UTF8 FALSE - #endif - /** - * @brief Enable kerning for font rendering (improves character placement). - * @details Defaults to FALSE - */ - #ifndef GDISP_NEED_TEXT_KERNING - #define GDISP_NEED_TEXT_KERNING FALSE - #endif - /** - * @brief Enable antialiased font support - * @details Defaults to FALSE - */ - #ifndef GDISP_NEED_ANTIALIAS - #define GDISP_NEED_ANTIALIAS FALSE - #endif -/** - * @} - * - * @name GDISP Pixmap Options - * @{ - */ - #ifndef GDISP_NEED_PIXMAP_IMAGE - #define GDISP_NEED_PIXMAP_IMAGE FALSE - #endif -/** - * @} - * - * @name GDISP Optional Low Level Driver Defines - * @{ - */ - /** - * @brief Set the screen height and width. - * @note Ignored by some low level GDISP drivers, optional for others. - * @note Where these values are allowed, a default is always provided be the low level driver. - * @note The list of GDISP low level drivers that allow these to be set are... - * WIN32, SSD1289, SSD1963, TestStub - */ - /* #define GDISP_SCREEN_WIDTH nnnn */ - /* #define GDISP_SCREEN_HEIGHT nnnn */ - /** - * @brief Define which bus interface to use. - * @details Only required by the SSD1963 driver. - * @note This will be replaced eventually by board definition files - */ - // #define GDISP_USE_FSMC - // #define GDISP_USE_GPIO -/** @} */ - -#endif /* _GDISP_OPTIONS_H */ -/** @} */ - diff --git a/src/gdisp/sys_rules.h b/src/gdisp/sys_rules.h deleted file mode 100644 index 79df2d88..00000000 --- a/src/gdisp/sys_rules.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gdisp/sys_rules.h - * @brief GDISP safety rules header file. - * - * @addtogroup GDISP - * @{ - */ - -#ifndef _GDISP_RULES_H -#define _GDISP_RULES_H - -#if GFX_USE_GDISP - #if !GFX_USE_GDRIVER - #if GFX_DISPLAY_RULE_WARNINGS - #warning "GDISP: GFX_USE_GDRIVER is required. GFX_USE_GDRIVER has turned on for you." - #endif - #undef GFX_USE_GDRIVER - #define GFX_USE_GDRIVER TRUE - #endif - #if defined(GDISP_DRIVER_LIST) - #if GDISP_TOTAL_DISPLAYS != 1 - #error "GDISP Multiple Drivers: You can't specify both GDISP_TOTAL_DISPLAYS and GDISP_DRIVER_LIST" - #endif - #ifndef GDISP_PIXELFORMAT - #error "GDISP Multiple Drivers: You must specify a value for GDISP_PIXELFORMAT when using GDISP_DRIVER_LIST" - #endif - #endif - #if GDISP_NEED_AUTOFLUSH && GDISP_NEED_TIMERFLUSH - #if GFX_DISPLAY_RULE_WARNINGS - #warning "GDISP: Both GDISP_NEED_AUTOFLUSH and GDISP_NEED_TIMERFLUSH has been set. GDISP_NEED_TIMERFLUSH has disabled for you." - #endif - #undef GDISP_NEED_TIMERFLUSH - #define GDISP_NEED_TIMERFLUSH FALSE - #endif - #if GDISP_NEED_TIMERFLUSH - #if GDISP_NEED_TIMERFLUSH < 50 || GDISP_NEED_TIMERFLUSH > 1200 - #error "GDISP: GDISP_NEED_TIMERFLUSH has been set to an invalid value (FALSE, 50-1200)." - #endif - #if !GFX_USE_GTIMER - #if GFX_DISPLAY_RULE_WARNINGS - #warning "GDISP: GDISP_NEED_TIMERFLUSH has been set but GFX_USE_GTIMER has not been set. It has been turned on for you." - #endif - #undef GFX_USE_GTIMER - #define GFX_USE_GTIMER TRUE - #undef GDISP_NEED_MULTITHREAD - #define GDISP_NEED_MULTITHREAD TRUE - #endif - #endif - #if GDISP_NEED_ANTIALIAS && !GDISP_NEED_PIXELREAD - #if GDISP_HARDWARE_PIXELREAD - #if GFX_DISPLAY_RULE_WARNINGS - #warning "GDISP: GDISP_NEED_ANTIALIAS has been set but GDISP_NEED_PIXELREAD has not. It has been turned on for you." - #endif - #undef GDISP_NEED_PIXELREAD - #define GDISP_NEED_PIXELREAD TRUE - #else - #if GFX_DISPLAY_RULE_WARNINGS - #warning "GDISP: GDISP_NEED_ANTIALIAS has been set but your hardware does not support reading back pixels. Anti-aliasing will only occur for filled characters." - #endif - #endif - #endif - #if (defined(GDISP_INCLUDE_FONT_SMALL) && GDISP_INCLUDE_FONT_SMALL) || (defined(GDISP_INCLUDE_FONT_LARGER) && GDISP_INCLUDE_FONT_LARGER) - #if GFX_DISPLAY_RULE_WARNINGS - #warning "GDISP: An old font (Small or Larger) has been defined. A single default font of UI2 has been added instead." - #warning "GDISP: Please see <$(GFXLIB)/include/gdisp/fonts/fonts.h> for a list of available font names." - #endif - #undef GDISP_INCLUDE_FONT_UI2 - #define GDISP_INCLUDE_FONT_UI2 TRUE - #endif - #if GDISP_NEED_IMAGE - #if !GFX_USE_GFILE - #if GFX_DISPLAY_RULE_WARNINGS - #warning "GDISP: GFX_USE_GFILE is required when GDISP_NEED_IMAGE is TRUE. It has been turned on for you." - #endif - #undef GFX_USE_GFILE - #define GFX_USE_GFILE TRUE - #endif - #endif -#endif - -#endif /* _GDISP_RULES_H */ -/** @} */ diff --git a/src/gdriver/gdriver.c b/src/gdriver/gdriver.c new file mode 100644 index 00000000..d0324639 --- /dev/null +++ b/src/gdriver/gdriver.c @@ -0,0 +1,148 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +#include "gfx.h" + +#if GFX_USE_GDRIVER + +#include "gdriver.h" + +#include <string.h> // For memset + +// Define the tables to hold the driver instances. +static GDriver *dhead; +static GDriver *dtail; + +// The system initialization. +void _gdriverInit(void) { +} + +// The system de-initialization. +void _gdriverDeinit(void) { + while(dhead) + gdriverUnRegister(dhead); +} + + +GDriver *gdriverRegister(const GDriverVMT *vmt, void *param) { + GDriver * pd; + unsigned dinstance, sinstance; + + // Loop to find the driver instance and the system instance numbers + dinstance = sinstance = 0; + for(pd = dhead; pd; pd = pd->driverchain) { + if (pd->vmt == vmt) + dinstance++; + if (pd->vmt->type == vmt->type) + sinstance++; + } + + // Get a new driver instance of the correct size and initialize it + pd = gfxAlloc(vmt->objsize); + if (!pd) + return 0; + memset(pd, 0, vmt->objsize); + pd->vmt = vmt; + if (vmt->init && !vmt->init(pd, param, dinstance, sinstance)) { + gfxFree(pd); + return 0; + } + + // Add it to the driver chain + if (dhead) + dtail->driverchain = pd; + else + dhead = dtail = pd; + + // Do the post init + if (vmt->postinit) + vmt->postinit(pd); + + return pd; +} + +void gdriverUnRegister(GDriver *driver) { + GDriver *pd; + + // Safety + if (!driver) + return; + + // Remove it from the list of drivers + if (dhead == driver) + dhead = driver->driverchain; + else { + for(pd = dhead; pd->driverchain; pd = pd->driverchain) { + if (pd->driverchain == driver) { + pd->driverchain = driver->driverchain; + break; + } + } + } + + // Call the deinit() + if (driver->vmt->deinit) + driver->vmt->deinit(driver); + + // Cleanup + gfxFree(driver); +} + +GDriver *gdriverGetInstance(uint16_t type, unsigned instance) { + GDriver *pd; + unsigned sinstance; + + // Loop to find the system instance + sinstance = 0; + for(pd = dhead; pd; pd = pd->driverchain) { + if (pd->vmt->type == type) { + if (sinstance == instance) + return pd; + sinstance++; + } + } + return 0; +} + +unsigned gdriverInstanceCount(uint16_t type) { + GDriver *pd; + unsigned sinstance; + + // Loop to count the system instances + sinstance = 0; + for(pd = dhead; pd; pd = pd->driverchain) { + if (pd->vmt->type == type) + sinstance++; + } + return sinstance; +} + +GDriver *gdriverGetNext(uint16_t type, GDriver *driver) { + driver = driver ? driver->driverchain : dhead; + + while(driver && driver->vmt->type != type) + driver = driver->driverchain; + + return driver; +} + +unsigned gdriverGetDriverInstanceNumber(GDriver *driver) { + GDriver *pd; + unsigned instance; + + // Loop to find the system instance + instance = 0; + for(pd = dhead; pd; pd = pd->driverchain) { + if (pd == driver) + return instance; + if (pd->vmt->type == driver->vmt->type) + instance++; + } + return (unsigned)-1; +} + +#endif /* GFX_USE_GDRIVER */ diff --git a/src/gdriver/gdriver.h b/src/gdriver/gdriver.h new file mode 100644 index 00000000..6f245ef1 --- /dev/null +++ b/src/gdriver/gdriver.h @@ -0,0 +1,159 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gdriver/gdriver.h + * + * @addtogroup GDRIVER + * + * @brief Module to support registering and unregistering of drivers + * + * @details GDRIVER provides a generalized way of defining and registering drivers. + * + * @note There are many different types of drivers and GDRIVER can handle any + * type of driver defined by the uGFX system. + * + * @note GDRIVER supports multiple drivers for one type of device. eg a SSD1289 LCD + * driver simultaneously with a framebuffer driver. + * @note GDRIVER supports multiple instances of a single driver. eg 2 SSD1289 LCD's. + * @note If there is only a single device of a particular type it will automatically + * register that device (it only needs to be included in the build, no special + * configuration is required) + * @note This module gdriver.h file is NOT included in the general gfx.h file. + * Instead it is included in each driver type's driver API. + * + * @pre GFX_USE_GDRIVER must be set to TRUE in your gfxconf.h + * + * @{ + */ + +#ifndef _GDRIVER_H +#define _GDRIVER_H + +#if GFX_USE_GDRIVER || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Type definitions */ +/*===========================================================================*/ + +#define GDRIVER_TYPE_DISPLAY 'g' // @< A graphics display +#define GDRIVER_TYPE_MOUSE 'm' // @< A mouse +#define GDRIVER_TYPE_TOUCH 'm' // @< A touch display (equivalent to a mouse) +#define GDRIVER_TYPE_TOGGLE 't' // @< A toggle device eg GPIO pins, switches etc +#define GDRIVER_TYPE_DIAL 'd' // @< A analog or digit dial (ranges in value from a minimum to a maximum) +#define GDRIVER_TYPE_KEYBOARD 'k' // @< A keyboard +#define GDRIVER_TYPE_BLOCK 'b' // @< A block device +#define GDRIVER_TYPE_STRING 's' // @< A device that returns strings of data + +/** + * @brief All runtime driver structures start with this structure + * + * @note This structure (and any additional structure memory) is allocated + * dynamically by the system for each driver instance. + */ +typedef struct GDriver { + struct GDriver * driverchain; + const struct GDriverVMT * vmt; +} GDriver; + +/** + * @brief All driver VMT's start with this structure. + */ +typedef struct GDriverVMT { + uint16_t type; // @< What type of driver this is + uint16_t flags; // @< Flags for the driver. Meaning is specific to each driver type. + uint32_t objsize; // @< How big the runtime driver structure is + bool_t (*init)(GDriver *driver, void *param, unsigned driverinstance, unsigned systeminstance); // @< Initialise the driver. Returns TRUE if OK. + // driverinstance is the instance 0..n of this driver. + // systeminstance is the instance 0..n of this type of device. + // The memory allocated is cleared before this call. + void (*postinit)(GDriver *driver); // @< Called once the driver is registered. + void (*deinit)(GDriver *driver); // @< De-initialise the driver +} GDriverVMT; + +/** + * @brief A definition that allows getting addresses of GDriverVMT structures to put into a list. + * @note eg. <code> + * const MyDriverVMTtype a[1] = {{...}}; + * const MyDriverVMTtype b[1] = {{...}}; + * ... + * \#define DRIVER_LIST a, b + * extern GDriverVMTList DRIVER_LIST; // Now treated as single element arrays of GDriverVMT + * const GDriverVMT const * mylist = { DRIVER_LIST }; + * </code> + * + */ +typedef const struct GDriverVMT const GDriverVMTList[1]; + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + + /** + * @brief Register a new driver instance. + * @return The runtime driver structure or NULL if it fails. + * + * @param[in] vmt The driver's vmt + * @param[in] param An arbitrary paramater passed to the driver init routine. + */ + GDriver *gdriverRegister(const GDriverVMT *vmt, void *param); + + /** + * @brief UnRegister a driver instance. + * + * @param[in] driver The driver instance's runtime structure + */ + void gdriverUnRegister(GDriver *driver); + + /** + * @brief Get the driver for a particular instance of a type of device + * @return The runtime driver structure or NULL if it fails. + * + * @param[in] type The type of driver to find + * @param[in] instance The instance (0..n) to find + */ + GDriver *gdriverGetInstance(uint16_t type, unsigned instance); + + /** + * @brief Get the count of instances of a type of device + * @return The instance count. + * + * @note Valid instance numbers are then 0 .. count-1 + * + * @param[in] type The type of driver to find + */ + unsigned gdriverInstanceCount(uint16_t type); + + /** + * @brief Get the instance number for a device + * @return The instance number or (unsigned)-1 if it fails. + * + * @param[in] driver The driver to find the instance number for + */ + unsigned gdriverGetDriverInstanceNumber(GDriver *driver); + + /** + * @brief Get the next driver for a type of device + * @return The runtime driver structure or NULL if there are no more. + * + * @param[in] type The type of driver to find + * @param[in] driver The last driver returned or NULL to start again + */ + GDriver *gdriverGetNext(uint16_t type, GDriver *driver); + +#ifdef __cplusplus +} +#endif + +#endif /* GFX_USE_GDRIVER */ + +#endif /* _GDRIVER_H */ +/** @} */ diff --git a/src/gdriver/gdriver.mk b/src/gdriver/gdriver.mk new file mode 100644 index 00000000..663042af --- /dev/null +++ b/src/gdriver/gdriver.mk @@ -0,0 +1 @@ +GFXSRC += $(GFXLIB)/src/gdriver/gdriver.c diff --git a/src/gdriver/gdriver_gdriver.c b/src/gdriver/gdriver_gdriver.c deleted file mode 100644 index 210840b1..00000000 --- a/src/gdriver/gdriver_gdriver.c +++ /dev/null @@ -1,148 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -#include "gfx.h" - -#if GFX_USE_GDRIVER - -#include "sys_defs.h" - -#include <string.h> // For memset - -// Define the tables to hold the driver instances. -static GDriver *dhead; -static GDriver *dtail; - -// The system initialization. -void _gdriverInit(void) { -} - -// The system de-initialization. -void _gdriverDeinit(void) { - while(dhead) - gdriverUnRegister(dhead); -} - - -GDriver *gdriverRegister(const GDriverVMT *vmt, void *param) { - GDriver * pd; - unsigned dinstance, sinstance; - - // Loop to find the driver instance and the system instance numbers - dinstance = sinstance = 0; - for(pd = dhead; pd; pd = pd->driverchain) { - if (pd->vmt == vmt) - dinstance++; - if (pd->vmt->type == vmt->type) - sinstance++; - } - - // Get a new driver instance of the correct size and initialize it - pd = gfxAlloc(vmt->objsize); - if (!pd) - return 0; - memset(pd, 0, vmt->objsize); - pd->vmt = vmt; - if (vmt->init && !vmt->init(pd, param, dinstance, sinstance)) { - gfxFree(pd); - return 0; - } - - // Add it to the driver chain - if (dhead) - dtail->driverchain = pd; - else - dhead = dtail = pd; - - // Do the post init - if (vmt->postinit) - vmt->postinit(pd); - - return pd; -} - -void gdriverUnRegister(GDriver *driver) { - GDriver *pd; - - // Safety - if (!driver) - return; - - // Remove it from the list of drivers - if (dhead == driver) - dhead = driver->driverchain; - else { - for(pd = dhead; pd->driverchain; pd = pd->driverchain) { - if (pd->driverchain == driver) { - pd->driverchain = driver->driverchain; - break; - } - } - } - - // Call the deinit() - if (driver->vmt->deinit) - driver->vmt->deinit(driver); - - // Cleanup - gfxFree(driver); -} - -GDriver *gdriverGetInstance(uint16_t type, unsigned instance) { - GDriver *pd; - unsigned sinstance; - - // Loop to find the system instance - sinstance = 0; - for(pd = dhead; pd; pd = pd->driverchain) { - if (pd->vmt->type == type) { - if (sinstance == instance) - return pd; - sinstance++; - } - } - return 0; -} - -unsigned gdriverInstanceCount(uint16_t type) { - GDriver *pd; - unsigned sinstance; - - // Loop to count the system instances - sinstance = 0; - for(pd = dhead; pd; pd = pd->driverchain) { - if (pd->vmt->type == type) - sinstance++; - } - return sinstance; -} - -GDriver *gdriverGetNext(uint16_t type, GDriver *driver) { - driver = driver ? driver->driverchain : dhead; - - while(driver && driver->vmt->type != type) - driver = driver->driverchain; - - return driver; -} - -unsigned gdriverGetDriverInstanceNumber(GDriver *driver) { - GDriver *pd; - unsigned instance; - - // Loop to find the system instance - instance = 0; - for(pd = dhead; pd; pd = pd->driverchain) { - if (pd == driver) - return instance; - if (pd->vmt->type == driver->vmt->type) - instance++; - } - return (unsigned)-1; -} - -#endif /* GFX_USE_GDRIVER */ diff --git a/src/gdriver/gdriver_options.h b/src/gdriver/gdriver_options.h new file mode 100644 index 00000000..ca3fe1f1 --- /dev/null +++ b/src/gdriver/gdriver_options.h @@ -0,0 +1,32 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gdriver/gdriver_options.h + * @brief GDRIVER - Driver options header file. + * + * @addtogroup GDRIVER + * @{ + */ + +#ifndef _GDRIVER_OPTIONS_H +#define _GDRIVER_OPTIONS_H + +/** + * @name GDRIVER Functionality to be included + * @{ + */ +/** + * @} + * + * @name GDRIVER Optional Parameters + * @{ + */ +/** @} */ + +#endif /* _GDRIVER_OPTIONS_H */ +/** @} */ diff --git a/src/gdriver/gdriver_rules.h b/src/gdriver/gdriver_rules.h new file mode 100644 index 00000000..2aaffa1b --- /dev/null +++ b/src/gdriver/gdriver_rules.h @@ -0,0 +1,23 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gdriver/gdriver_rules.h + * @brief GDRIVER safety rules header file. + * + * @addtogroup GFILE + * @{ + */ + +#ifndef _GDRIVER_RULES_H +#define _GDRIVER_RULES_H + +#if GFX_USE_GDRIVER +#endif + +#endif /* _GDRIVER_RULES_H */ +/** @} */ diff --git a/src/gdriver/sys_defs.h b/src/gdriver/sys_defs.h deleted file mode 100644 index 4ac20b19..00000000 --- a/src/gdriver/sys_defs.h +++ /dev/null @@ -1,159 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gdriver/sys_defs.h - * - * @addtogroup GDRIVER - * - * @brief Module to support registering and unregistering of drivers - * - * @details GDRIVER provides a generalized way of defining and registering drivers. - * - * @note There are many different types of drivers and GDRIVER can handle any - * type of driver defined by the uGFX system. - * - * @note GDRIVER supports multiple drivers for one type of device. eg a SSD1289 LCD - * driver simultaneously with a framebuffer driver. - * @note GDRIVER supports multiple instances of a single driver. eg 2 SSD1289 LCD's. - * @note If there is only a single device of a particular type it will automatically - * register that device (it only needs to be included in the build, no special - * configuration is required) - * @note This module sys_defs.h file is NOT included in the general gfx.h file. - * Instead it is included in each driver type's driver API. - * - * @pre GFX_USE_GDRIVER must be set to TRUE in your gfxconf.h - * - * @{ - */ - -#ifndef _GDRIVER_H -#define _GDRIVER_H - -#if GFX_USE_GDRIVER || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Type definitions */ -/*===========================================================================*/ - -#define GDRIVER_TYPE_DISPLAY 'g' // @< A graphics display -#define GDRIVER_TYPE_MOUSE 'm' // @< A mouse -#define GDRIVER_TYPE_TOUCH 'm' // @< A touch display (equivalent to a mouse) -#define GDRIVER_TYPE_TOGGLE 't' // @< A toggle device eg GPIO pins, switches etc -#define GDRIVER_TYPE_DIAL 'd' // @< A analog or digit dial (ranges in value from a minimum to a maximum) -#define GDRIVER_TYPE_KEYBOARD 'k' // @< A keyboard -#define GDRIVER_TYPE_BLOCK 'b' // @< A block device -#define GDRIVER_TYPE_STRING 's' // @< A device that returns strings of data - -/** - * @brief All runtime driver structures start with this structure - * - * @note This structure (and any additional structure memory) is allocated - * dynamically by the system for each driver instance. - */ -typedef struct GDriver { - struct GDriver * driverchain; - const struct GDriverVMT * vmt; -} GDriver; - -/** - * @brief All driver VMT's start with this structure. - */ -typedef struct GDriverVMT { - uint16_t type; // @< What type of driver this is - uint16_t flags; // @< Flags for the driver. Meaning is specific to each driver type. - uint32_t objsize; // @< How big the runtime driver structure is - bool_t (*init)(GDriver *driver, void *param, unsigned driverinstance, unsigned systeminstance); // @< Initialise the driver. Returns TRUE if OK. - // driverinstance is the instance 0..n of this driver. - // systeminstance is the instance 0..n of this type of device. - // The memory allocated is cleared before this call. - void (*postinit)(GDriver *driver); // @< Called once the driver is registered. - void (*deinit)(GDriver *driver); // @< De-initialise the driver -} GDriverVMT; - -/** - * @brief A definition that allows getting addresses of GDriverVMT structures to put into a list. - * @note eg. <code> - * const MyDriverVMTtype a[1] = {{...}}; - * const MyDriverVMTtype b[1] = {{...}}; - * ... - * \#define DRIVER_LIST a, b - * extern GDriverVMTList DRIVER_LIST; // Now treated as single element arrays of GDriverVMT - * const GDriverVMT const * mylist = { DRIVER_LIST }; - * </code> - * - */ -typedef const struct GDriverVMT const GDriverVMTList[1]; - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#ifdef __cplusplus -extern "C" { -#endif - - /** - * @brief Register a new driver instance. - * @return The runtime driver structure or NULL if it fails. - * - * @param[in] vmt The driver's vmt - * @param[in] param An arbitrary paramater passed to the driver init routine. - */ - GDriver *gdriverRegister(const GDriverVMT *vmt, void *param); - - /** - * @brief UnRegister a driver instance. - * - * @param[in] driver The driver instance's runtime structure - */ - void gdriverUnRegister(GDriver *driver); - - /** - * @brief Get the driver for a particular instance of a type of device - * @return The runtime driver structure or NULL if it fails. - * - * @param[in] type The type of driver to find - * @param[in] instance The instance (0..n) to find - */ - GDriver *gdriverGetInstance(uint16_t type, unsigned instance); - - /** - * @brief Get the count of instances of a type of device - * @return The instance count. - * - * @note Valid instance numbers are then 0 .. count-1 - * - * @param[in] type The type of driver to find - */ - unsigned gdriverInstanceCount(uint16_t type); - - /** - * @brief Get the instance number for a device - * @return The instance number or (unsigned)-1 if it fails. - * - * @param[in] driver The driver to find the instance number for - */ - unsigned gdriverGetDriverInstanceNumber(GDriver *driver); - - /** - * @brief Get the next driver for a type of device - * @return The runtime driver structure or NULL if there are no more. - * - * @param[in] type The type of driver to find - * @param[in] driver The last driver returned or NULL to start again - */ - GDriver *gdriverGetNext(uint16_t type, GDriver *driver); - -#ifdef __cplusplus -} -#endif - -#endif /* GFX_USE_GDRIVER */ - -#endif /* _GDRIVER_H */ -/** @} */ diff --git a/src/gdriver/sys_make.mk b/src/gdriver/sys_make.mk deleted file mode 100644 index 93810aa9..00000000 --- a/src/gdriver/sys_make.mk +++ /dev/null @@ -1 +0,0 @@ -GFXSRC += $(GFXLIB)/src/gdriver/gdriver_gdriver.c diff --git a/src/gdriver/sys_options.h b/src/gdriver/sys_options.h deleted file mode 100644 index bef0a95a..00000000 --- a/src/gdriver/sys_options.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gdriver/sys_options.h - * @brief GDRIVER - Driver options header file. - * - * @addtogroup GDRIVER - * @{ - */ - -#ifndef _GDRIVER_OPTIONS_H -#define _GDRIVER_OPTIONS_H - -/** - * @name GDRIVER Functionality to be included - * @{ - */ -/** - * @} - * - * @name GDRIVER Optional Parameters - * @{ - */ -/** @} */ - -#endif /* _GDRIVER_OPTIONS_H */ -/** @} */ diff --git a/src/gdriver/sys_rules.h b/src/gdriver/sys_rules.h deleted file mode 100644 index 596babba..00000000 --- a/src/gdriver/sys_rules.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gdriver/sys_rules.h - * @brief GDRIVER safety rules header file. - * - * @addtogroup GFILE - * @{ - */ - -#ifndef _GDRIVER_RULES_H -#define _GDRIVER_RULES_H - -#if GFX_USE_GDRIVER -#endif - -#endif /* _GDRIVER_RULES_H */ -/** @} */ diff --git a/src/gevent/gevent.c b/src/gevent/gevent.c new file mode 100644 index 00000000..bb57b3ad --- /dev/null +++ b/src/gevent/gevent.c @@ -0,0 +1,238 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +#include "gfx.h" + +#if GFX_USE_GEVENT || defined(__DOXYGEN__) + +#if GEVENT_ASSERT_NO_RESOURCE + #define GEVENT_ASSERT(x) assert(x) +#else + #define GEVENT_ASSERT(x) +#endif + +/* Flags in the listener structure */ +#define GLISTENER_EVENTBUSY 0x0001 // The event buffer is busy +#define GLISTENER_WAITING 0x0002 // The listener is waiting for a signal +#define GLISTENER_WITHSOURCE 0x0004 // The listener is being looked at by a source for a possible event +#define GLISTENER_PENDING 0x0008 // There is an event waiting ready to go without a current listener + +/* This mutex protects access to our tables */ +static gfxMutex geventMutex; + +/* Our table of listener/source pairs */ +static GSourceListener Assignments[GEVENT_MAX_SOURCE_LISTENERS]; + +/* Send an exit event if possible. */ +/* We already have the geventMutex */ +static void doExitEvent(GListener *pl) { + // Don't do the exit if someone else currently has the event lock + if ((pl->flags & (GLISTENER_WAITING|GLISTENER_EVENTBUSY)) == GLISTENER_WAITING) { + pl->flags |= GLISTENER_EVENTBUSY; // Event buffer is in use + pl->event.type = GEVENT_EXIT; // Set up the EXIT event + pl->flags &= ~GLISTENER_WAITING; // Wake up the listener (with data) + gfxSemSignal(&pl->waitqueue); + } +} + +/* Loop through the assignment table deleting this listener/source pair. */ +/* Null is treated as a wildcard. */ +/* We already have the geventMutex */ +static void deleteAssignments(GListener *pl, GSourceHandle gsh) { + GSourceListener *psl; + + for(psl = Assignments; psl < Assignments+GEVENT_MAX_SOURCE_LISTENERS; psl++) { + if ((!pl || psl->pListener == pl) && (!gsh || psl->pSource == gsh)) { + doExitEvent(psl->pListener); + psl->pListener = 0; + psl->pSource = 0; + } + } +} + +void _geventInit(void) +{ + gfxMutexInit(&geventMutex); +} + +void _geventDeinit(void) +{ + gfxMutexDestroy(&geventMutex); +} + +void geventListenerInit(GListener *pl) { + gfxSemInit(&pl->waitqueue, 0, MAX_SEMAPHORE_COUNT); // Next wait'er will block + pl->callback = 0; // No callback active + pl->event.type = GEVENT_NULL; // Always safety + pl->flags = 0; +} + +bool_t geventAttachSource(GListener *pl, GSourceHandle gsh, unsigned flags) { + GSourceListener *psl, *pslfree; + + // Safety first + if (!pl || !gsh) { + GEVENT_ASSERT(FALSE); + return FALSE; + } + + gfxMutexEnter(&geventMutex); + + // Check if this pair is already in the table (scan for a free slot at the same time) + pslfree = 0; + for(psl = Assignments; psl < Assignments+GEVENT_MAX_SOURCE_LISTENERS; psl++) { + + if (pl == psl->pListener && gsh == psl->pSource) { + // Just update the flags + psl->listenflags = flags; + gfxMutexExit(&geventMutex); + return TRUE; + } + if (!pslfree && !psl->pListener) + pslfree = psl; + } + + // A free slot was found - allocate it + if (pslfree) { + pslfree->pListener = pl; + pslfree->pSource = gsh; + pslfree->listenflags = flags; + pslfree->srcflags = 0; + } + gfxMutexExit(&geventMutex); + GEVENT_ASSERT(pslfree != 0); + return pslfree != 0; +} + +void geventDetachSource(GListener *pl, GSourceHandle gsh) { + if (pl) { + gfxMutexEnter(&geventMutex); + deleteAssignments(pl, gsh); + if (!gsh) + doExitEvent(pl); + gfxMutexExit(&geventMutex); + } +} + +GEvent *geventEventWait(GListener *pl, delaytime_t timeout) { + gfxMutexEnter(&geventMutex); + // Don't allow waiting if we are on callbacks or if there is already a thread waiting + if (pl->callback || (pl->flags & GLISTENER_WAITING)) { + gfxMutexExit(&geventMutex); + return 0; + } + + // Check to see if there is a pending event ready for us + if ((pl->flags & GLISTENER_PENDING)) { + pl->flags &= ~GLISTENER_PENDING; // We have now got this + pl->flags |= GLISTENER_EVENTBUSY; // Event buffer is definitely busy + gfxMutexExit(&geventMutex); + return &pl->event; + } + + // No - wait for one. + pl->flags &= ~GLISTENER_EVENTBUSY; // Event buffer is definitely not busy + pl->flags |= GLISTENER_WAITING; // We will now be waiting on the thread + gfxMutexExit(&geventMutex); + if (gfxSemWait(&pl->waitqueue, timeout)) + return &pl->event; + + // Timeout - clear the waiting flag. + // We know this is safe as any other thread will still think there is someone waiting. + gfxMutexEnter(&geventMutex); + pl->flags &= ~GLISTENER_WAITING; + gfxMutexExit(&geventMutex); + return 0; +} + +void geventEventComplete(GListener *pl) { + pl->flags &= ~GLISTENER_EVENTBUSY; +} + +void geventRegisterCallback(GListener *pl, GEventCallbackFn fn, void *param) { + if (pl) { + gfxMutexEnter(&geventMutex); + doExitEvent(pl); + pl->param = param; // Set the param + pl->callback = fn; // Set the callback function + if (fn) + pl->flags &= ~GLISTENER_EVENTBUSY; // The event buffer is immediately available + gfxMutexExit(&geventMutex); + } +} + +GSourceListener *geventGetSourceListener(GSourceHandle gsh, GSourceListener *lastlr) { + GSourceListener *psl; + + // Safety first + if (!gsh) + return 0; + + gfxMutexEnter(&geventMutex); + + // Unlock the last listener event buffer if it wasn't used. + if (lastlr && lastlr->pListener && (lastlr->pListener->flags & GLISTENER_WITHSOURCE)) + lastlr->pListener->flags &= ~(GLISTENER_WITHSOURCE|GLISTENER_EVENTBUSY); + + // Loop through the table looking for attachments to this source + for(psl = lastlr ? (lastlr+1) : Assignments; psl < Assignments+GEVENT_MAX_SOURCE_LISTENERS; psl++) { + if (gsh == psl->pSource) { + gfxMutexExit(&geventMutex); + return psl; + } + } + gfxMutexExit(&geventMutex); + return 0; +} + +GEvent *geventGetEventBuffer(GSourceListener *psl) { + gfxMutexEnter(&geventMutex); + if ((psl->pListener->flags & GLISTENER_EVENTBUSY)) { + // Oops - event buffer is still in use + gfxMutexExit(&geventMutex); + return 0; + } + + // Allocate the event buffer + psl->pListener->flags |= (GLISTENER_WITHSOURCE|GLISTENER_EVENTBUSY); + gfxMutexExit(&geventMutex); + return &psl->pListener->event; +} + +void geventSendEvent(GSourceListener *psl) { + gfxMutexEnter(&geventMutex); + if (psl->pListener->callback) { + + // Mark it back as free and as sent. This is early to be marking as free but it protects + // if the callback alters the listener in any way + psl->pListener->flags &= ~(GLISTENER_WITHSOURCE|GLISTENER_EVENTBUSY|GLISTENER_PENDING); + gfxMutexExit(&geventMutex); + + // Do the callback + psl->pListener->callback(psl->pListener->param, &psl->pListener->event); + + } else { + // Wake up the listener + psl->pListener->flags &= ~GLISTENER_WITHSOURCE; + if ((psl->pListener->flags & GLISTENER_WAITING)) { + psl->pListener->flags &= ~(GLISTENER_WAITING|GLISTENER_PENDING); + gfxSemSignal(&psl->pListener->waitqueue); + } else + psl->pListener->flags |= GLISTENER_PENDING; + + // The listener thread will free the event buffer when ready + gfxMutexExit(&geventMutex); + } +} + +void geventDetachSourceListeners(GSourceHandle gsh) { + gfxMutexEnter(&geventMutex); + deleteAssignments(0, gsh); + gfxMutexExit(&geventMutex); +} + +#endif /* GFX_USE_GEVENT */ diff --git a/src/gevent/gevent.h b/src/gevent/gevent.h new file mode 100644 index 00000000..a33fd84a --- /dev/null +++ b/src/gevent/gevent.h @@ -0,0 +1,246 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gevent/gevent.h + * + * @addtogroup GEVENT + * + * @brief Module to build a complete many-to-many event system + * + * @details GEVENT provides a simple to use but yet powerful event + * system. + * + * @pre GFX_USE_GEVENT must be set to TRUE in your gfxconf.h + * + * @{ + */ +#ifndef _GEVENT_H +#define _GEVENT_H + +#include "gfx.h" + +#if GFX_USE_GEVENT || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Type definitions */ +/*===========================================================================*/ + +typedef uint16_t GEventType; + #define GEVENT_NULL 0x0000 // Null Event - Do nothing + #define GEVENT_EXIT 0x0001 // The listener is being forced to exit (someone is destroying the listener) + + /* Other event types are allocated in ranges in their respective include files */ + #define GEVENT_GINPUT_FIRST 0x0100 // GINPUT events range from 0x0100 to 0x01FF + #define GEVENT_GWIN_FIRST 0x0200 // GWIN events range from 0x0200 to 0x02FF + #define GEVENT_GADC_FIRST 0x0300 // GADC events range from 0x0300 to 0x033F + #define GEVENT_GAUDIO_FIRST 0x0340 // GAUDIO events range from 0x0340 to 0x037F + #define GEVENT_USER_FIRST 0x8000 // Any application defined events start at 0x8000 + +// This object can be typecast to any GEventXxxxx type to allow any sub-system (or the application) to create events. +// The prerequisite is that the new status structure type starts with a field named 'type' of type 'GEventType'. +// The total status structure also must not exceed GEVENT_MAXIMUM_SIZE bytes. +// For example, this is used by GWIN button events, GINPUT data streams etc. +typedef union GEvent_u { + GEventType type; // The type of this event + char pad[GEVENT_MAXIMUM_SIZE]; // This is here to allow static initialisation of GEventObject's in the application. +} GEvent; + +// A special callback function +typedef void (*GEventCallbackFn)(void *param, GEvent *pe); + +// The Listener Object +typedef struct GListener { + gfxSem waitqueue; // Private: Semaphore for the listener to wait on. + uint16_t flags; // Private: Flags for operation + GEventCallbackFn callback; // Private: Call back Function + void *param; // Private: Parameter for the callback function. + GEvent event; // Public: The event object into which the event information is stored. + } GListener; + +// The Source Object +typedef struct GSource_t GSource, *GSourceHandle; + +// This structure is passed to a source to describe a contender listener for sending the current event. +typedef struct GSourceListener_t { + GListener *pListener; // The listener + GSource *pSource; // The source + unsigned listenflags; // The flags the listener passed when the source was assigned to it. + unsigned srcflags; // For the source's exclusive use. Initialised as 0 for a new listener source assignment. + } GSourceListener; + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/* How to listen for events (act as a Listener)... + 1. Get handles for all the event sources you are interested in. + 2. Initialise a listener + 3. Attach sources to your listener. + - Sources can be attached or detached from a listener at any time. + - A source can be attached to more than one listener. + 4. Loop on getting listener events + 5. When finished detach all sources from the listener + + How to create events (act as a Source)... + 1. Provide a funtion to the application that returns a GSourceHandle (which can be a pointer to whatever the source wants) + 2. Whenever a possible event occurs call geventGetSourceListener to get a pointer to a GSourceListener. + This will return NULL when there are no more listeners. + For each listener - check the flags to see if an event should be sent. + - use geventGetEvent() to get the event buffer supplied by the listener + and then call geventSendEvent to send the event. + - Note: geventGetEvent() may return FALSE to indicate the listener is currently not listening and + therefore no event should be sent. This situation enables the source to (optionally) flag + to the listener on its next wait that there have been missed events. + - Note: The GSourceListener pointer (and the GEvent buffer) are only valid between + the geventGetSourceListener call and either the geventSendEvent call or the next + geventGetSourceListener call. + - Note: All listeners must be processed for this event before anything else is processed. +*/ + +/*---------- Listener Functions --------------------------------------------*/ + +/** + * @brief Create a Listener + * @details If insufficient resources are available it will either assert or return NULL + * depending on the value of GEVENT_ASSERT_NO_RESOURCE. + * + * @param[in] pl A listener + */ +void geventListenerInit(GListener *pl); + +/** + * @brief Attach a source to a listener + * @details Flags are interpreted by the source when generating events for each listener. + * If this source is already assigned to the listener it will update the flags. + * If insufficient resources are available it will either assert or return FALSE + * depending on the value of GEVENT_ASSERT_NO_RESOURCE. + * + * @param[in] pl The listener + * @param[in] gsh The source which has to be attached to the listener + * @param[in] flags The flags + * + * @return TRUE if succeeded, FALSE otherwise + */ +bool_t geventAttachSource(GListener *pl, GSourceHandle gsh, unsigned flags); + +/** + * @brief Detach a source from a listener + * @details If gsh is NULL detach all sources from this listener and if there is still + * a thread waiting for events on this listener, it is sent the exit event. + * + * @param[in] pl The listener + * @param[in] gsh The source + */ +void geventDetachSource(GListener *pl, GSourceHandle gsh); + +/** + * @brief Wait for an event on a listener from an assigned source. + * @details The type of the event should be checked (pevent->type) and then pevent should + * be typecast to the actual event type if it needs to be processed. + * timeout specifies the time to wait in system ticks. + * TIME_INFINITE means no timeout - wait forever for an event. + * TIME_IMMEDIATE means return immediately + * @note The returned GEvent is released when this routine is called again + * or when optionally @p geventEventComplete() is called. Calling @p geventEventComplete() + * allows the GEvent object to be reused earlier which can reduce missed events. The GEvent + * object MUST NOT be used after this function is called (and is blocked waiting for the next + * event) or after geventEventComplete() is called. + * + * @param[in] pl The listener + * @param[in] timeout The timeout + * + * @return NULL on timeout + */ +GEvent *geventEventWait(GListener *pl, delaytime_t timeout); + +/** + * @brief Release the GEvent buffer associated with a listener. + * @details The GEvent returned by @p geventEventWait() is released. + * @note The GEvent pointer returned by @p geventEventWait() is released when @p geventEventWait() + * is called again or when this function is called. The GEvent + * object MUST NOT be used after this function is called. + * + * @param[in] pl The listener + */ +void geventEventComplete(GListener *pl); + +/* @brief Register a callback for an event on a listener from an assigned source. + * @details The type of the event should be checked (pevent->type) and then pevent should be typecast to the + * actual event type if it needs to be processed. + * + * @params[in] pl The Listener + * @params[in] fn The function to call back + * @params[in] param A parameter to pass the callback function + * + * @note The GEvent buffer is valid only during the time of the callback. The callback MUST NOT save + * a pointer to the buffer for use outside the callback. + * @note An existing callback function is de-registered by passing a NULL for 'fn'. Any existing + * callback function is replaced. Any thread currently waiting using geventEventWait will be sent the exit event. + * @note Callbacks occur in a thread context but stack space must be kept to a minumum and + * the callback must process quickly as all other events are performed on a single thread. + * @note In the callback function you should never call ANY event functions using your own GListener handle + * as it WILL create a deadlock and lock the system up. + * @note Applications should not use this call - geventEventWait() is the preferred mechanism for an + * application. This call is provided for GUI objects that may not have their own thread. + */ +void geventRegisterCallback(GListener *pl, GEventCallbackFn fn, void *param); + +/*---------- Source Functions --------------------------------------------*/ + +/** + * @brief Called by a source with a possible event to get a listener record. + * @details @p lastlr should be NULL on the first call and thereafter the result of the previous call. + * + * @param[in] gsh The source handler + * @param[in] lastlr The source listener + * + * @return NULL when there are no more listeners for this source + */ +GSourceListener *geventGetSourceListener(GSourceHandle gsh, GSourceListener *lastlr); + +/** + * @brief Get the event buffer from the GSourceListener. + * @details A NULL return allows the source to record (perhaps in glr->scrflags) that the listener + * has missed events. This can then be notified as part of the next event for the listener. + * The buffer can only be accessed untill the next call to geventGetSourceListener + * or geventSendEvent + * + * @param[in] psl The source listener + * + * @return NULL if the listener is not currently listening. + */ +GEvent *geventGetEventBuffer(GSourceListener *psl); + +/** + * @brief Called by a source to indicate the listener's event buffer has been filled. + * @details After calling this function the source must not reference in fields in the GSourceListener or the event buffer. + * + * @param[in] psl The source listener + */ +void geventSendEvent(GSourceListener *psl); + +/** + * @brief Detach any listener that has this source attached + * + * @param[in] gsh The source handle + */ +void geventDetachSourceListeners(GSourceHandle gsh); + +#ifdef __cplusplus +} +#endif + +#endif /* GFX_USE_GEVENT */ + +#endif /* _GEVENT_H */ +/** @} */ + diff --git a/src/gevent/gevent.mk b/src/gevent/gevent.mk new file mode 100644 index 00000000..5744ae46 --- /dev/null +++ b/src/gevent/gevent.mk @@ -0,0 +1 @@ +GFXSRC += $(GFXLIB)/src/gevent/gevent.c diff --git a/src/gevent/gevent_gevent.c b/src/gevent/gevent_gevent.c deleted file mode 100644 index fc45102e..00000000 --- a/src/gevent/gevent_gevent.c +++ /dev/null @@ -1,246 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gevent/gevent_gevent.c - * @brief GEVENT Driver code. - * - * @addtogroup GEVENT - * @{ - */ -#include "gfx.h" - -#if GFX_USE_GEVENT || defined(__DOXYGEN__) - -#if GEVENT_ASSERT_NO_RESOURCE - #define GEVENT_ASSERT(x) assert(x) -#else - #define GEVENT_ASSERT(x) -#endif - -/* Flags in the listener structure */ -#define GLISTENER_EVENTBUSY 0x0001 // The event buffer is busy -#define GLISTENER_WAITING 0x0002 // The listener is waiting for a signal -#define GLISTENER_WITHSOURCE 0x0004 // The listener is being looked at by a source for a possible event -#define GLISTENER_PENDING 0x0008 // There is an event waiting ready to go without a current listener - -/* This mutex protects access to our tables */ -static gfxMutex geventMutex; - -/* Our table of listener/source pairs */ -static GSourceListener Assignments[GEVENT_MAX_SOURCE_LISTENERS]; - -/* Send an exit event if possible. */ -/* We already have the geventMutex */ -static void doExitEvent(GListener *pl) { - // Don't do the exit if someone else currently has the event lock - if ((pl->flags & (GLISTENER_WAITING|GLISTENER_EVENTBUSY)) == GLISTENER_WAITING) { - pl->flags |= GLISTENER_EVENTBUSY; // Event buffer is in use - pl->event.type = GEVENT_EXIT; // Set up the EXIT event - pl->flags &= ~GLISTENER_WAITING; // Wake up the listener (with data) - gfxSemSignal(&pl->waitqueue); - } -} - -/* Loop through the assignment table deleting this listener/source pair. */ -/* Null is treated as a wildcard. */ -/* We already have the geventMutex */ -static void deleteAssignments(GListener *pl, GSourceHandle gsh) { - GSourceListener *psl; - - for(psl = Assignments; psl < Assignments+GEVENT_MAX_SOURCE_LISTENERS; psl++) { - if ((!pl || psl->pListener == pl) && (!gsh || psl->pSource == gsh)) { - doExitEvent(psl->pListener); - psl->pListener = 0; - psl->pSource = 0; - } - } -} - -void _geventInit(void) -{ - gfxMutexInit(&geventMutex); -} - -void _geventDeinit(void) -{ - gfxMutexDestroy(&geventMutex); -} - -void geventListenerInit(GListener *pl) { - gfxSemInit(&pl->waitqueue, 0, MAX_SEMAPHORE_COUNT); // Next wait'er will block - pl->callback = 0; // No callback active - pl->event.type = GEVENT_NULL; // Always safety - pl->flags = 0; -} - -bool_t geventAttachSource(GListener *pl, GSourceHandle gsh, unsigned flags) { - GSourceListener *psl, *pslfree; - - // Safety first - if (!pl || !gsh) { - GEVENT_ASSERT(FALSE); - return FALSE; - } - - gfxMutexEnter(&geventMutex); - - // Check if this pair is already in the table (scan for a free slot at the same time) - pslfree = 0; - for(psl = Assignments; psl < Assignments+GEVENT_MAX_SOURCE_LISTENERS; psl++) { - - if (pl == psl->pListener && gsh == psl->pSource) { - // Just update the flags - psl->listenflags = flags; - gfxMutexExit(&geventMutex); - return TRUE; - } - if (!pslfree && !psl->pListener) - pslfree = psl; - } - - // A free slot was found - allocate it - if (pslfree) { - pslfree->pListener = pl; - pslfree->pSource = gsh; - pslfree->listenflags = flags; - pslfree->srcflags = 0; - } - gfxMutexExit(&geventMutex); - GEVENT_ASSERT(pslfree != 0); - return pslfree != 0; -} - -void geventDetachSource(GListener *pl, GSourceHandle gsh) { - if (pl) { - gfxMutexEnter(&geventMutex); - deleteAssignments(pl, gsh); - if (!gsh) - doExitEvent(pl); - gfxMutexExit(&geventMutex); - } -} - -GEvent *geventEventWait(GListener *pl, delaytime_t timeout) { - gfxMutexEnter(&geventMutex); - // Don't allow waiting if we are on callbacks or if there is already a thread waiting - if (pl->callback || (pl->flags & GLISTENER_WAITING)) { - gfxMutexExit(&geventMutex); - return 0; - } - - // Check to see if there is a pending event ready for us - if ((pl->flags & GLISTENER_PENDING)) { - pl->flags &= ~GLISTENER_PENDING; // We have now got this - pl->flags |= GLISTENER_EVENTBUSY; // Event buffer is definitely busy - gfxMutexExit(&geventMutex); - return &pl->event; - } - - // No - wait for one. - pl->flags &= ~GLISTENER_EVENTBUSY; // Event buffer is definitely not busy - pl->flags |= GLISTENER_WAITING; // We will now be waiting on the thread - gfxMutexExit(&geventMutex); - if (gfxSemWait(&pl->waitqueue, timeout)) - return &pl->event; - - // Timeout - clear the waiting flag. - // We know this is safe as any other thread will still think there is someone waiting. - gfxMutexEnter(&geventMutex); - pl->flags &= ~GLISTENER_WAITING; - gfxMutexExit(&geventMutex); - return 0; -} - -void geventEventComplete(GListener *pl) { - pl->flags &= ~GLISTENER_EVENTBUSY; -} - -void geventRegisterCallback(GListener *pl, GEventCallbackFn fn, void *param) { - if (pl) { - gfxMutexEnter(&geventMutex); - doExitEvent(pl); - pl->param = param; // Set the param - pl->callback = fn; // Set the callback function - if (fn) - pl->flags &= ~GLISTENER_EVENTBUSY; // The event buffer is immediately available - gfxMutexExit(&geventMutex); - } -} - -GSourceListener *geventGetSourceListener(GSourceHandle gsh, GSourceListener *lastlr) { - GSourceListener *psl; - - // Safety first - if (!gsh) - return 0; - - gfxMutexEnter(&geventMutex); - - // Unlock the last listener event buffer if it wasn't used. - if (lastlr && lastlr->pListener && (lastlr->pListener->flags & GLISTENER_WITHSOURCE)) - lastlr->pListener->flags &= ~(GLISTENER_WITHSOURCE|GLISTENER_EVENTBUSY); - - // Loop through the table looking for attachments to this source - for(psl = lastlr ? (lastlr+1) : Assignments; psl < Assignments+GEVENT_MAX_SOURCE_LISTENERS; psl++) { - if (gsh == psl->pSource) { - gfxMutexExit(&geventMutex); - return psl; - } - } - gfxMutexExit(&geventMutex); - return 0; -} - -GEvent *geventGetEventBuffer(GSourceListener *psl) { - gfxMutexEnter(&geventMutex); - if ((psl->pListener->flags & GLISTENER_EVENTBUSY)) { - // Oops - event buffer is still in use - gfxMutexExit(&geventMutex); - return 0; - } - - // Allocate the event buffer - psl->pListener->flags |= (GLISTENER_WITHSOURCE|GLISTENER_EVENTBUSY); - gfxMutexExit(&geventMutex); - return &psl->pListener->event; -} - -void geventSendEvent(GSourceListener *psl) { - gfxMutexEnter(&geventMutex); - if (psl->pListener->callback) { - - // Mark it back as free and as sent. This is early to be marking as free but it protects - // if the callback alters the listener in any way - psl->pListener->flags &= ~(GLISTENER_WITHSOURCE|GLISTENER_EVENTBUSY|GLISTENER_PENDING); - gfxMutexExit(&geventMutex); - - // Do the callback - psl->pListener->callback(psl->pListener->param, &psl->pListener->event); - - } else { - // Wake up the listener - psl->pListener->flags &= ~GLISTENER_WITHSOURCE; - if ((psl->pListener->flags & GLISTENER_WAITING)) { - psl->pListener->flags &= ~(GLISTENER_WAITING|GLISTENER_PENDING); - gfxSemSignal(&psl->pListener->waitqueue); - } else - psl->pListener->flags |= GLISTENER_PENDING; - - // The listener thread will free the event buffer when ready - gfxMutexExit(&geventMutex); - } -} - -void geventDetachSourceListeners(GSourceHandle gsh) { - gfxMutexEnter(&geventMutex); - deleteAssignments(0, gsh); - gfxMutexExit(&geventMutex); -} - -#endif /* GFX_USE_GEVENT */ -/** @} */ diff --git a/src/gevent/gevent_options.h b/src/gevent/gevent_options.h new file mode 100644 index 00000000..7f68fd32 --- /dev/null +++ b/src/gevent/gevent_options.h @@ -0,0 +1,55 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gevent/gevent_options.h + * @brief GEVENT sub-system options header file. + * + * @addtogroup GEVENT + * @{ + */ + +#ifndef _GEVENT_OPTIONS_H +#define _GEVENT_OPTIONS_H + +/** + * @name GEVENT Functionality to be included + * @{ + */ + /** + * @brief Should routines assert() if they run out of resources. + * @details Defaults to FALSE. + * @details If FALSE the application must be prepared to handle these + * failures. + */ + #ifndef GEVENT_ASSERT_NO_RESOURCE + #define GEVENT_ASSERT_NO_RESOURCE FALSE + #endif +/** + * @} + * + * @name GEVENT Optional Sizing Parameters + * @{ + */ + /** + * @brief Defines the maximum size of an event status variable. + * @details Defaults to 32 bytes + */ + #ifndef GEVENT_MAXIMUM_SIZE + #define GEVENT_MAXIMUM_SIZE 32 + #endif + /** + * @brief Defines the maximum Source/Listener pairs in the system. + * @details Defaults to 32 + */ + #ifndef GEVENT_MAX_SOURCE_LISTENERS + #define GEVENT_MAX_SOURCE_LISTENERS 32 + #endif +/** @} */ + +#endif /* _GEVENT_OPTIONS_H */ +/** @} */ diff --git a/src/gevent/gevent_rules.h b/src/gevent/gevent_rules.h new file mode 100644 index 00000000..8dc12c8d --- /dev/null +++ b/src/gevent/gevent_rules.h @@ -0,0 +1,23 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gevent/gevent_rules.h + * @brief GEVENT safety rules header file. + * + * @addtogroup GEVENT + * @{ + */ + +#ifndef _GEVENT_RULES_H +#define _GEVENT_RULES_H + +#if GFX_USE_GEVENT +#endif + +#endif /* _GEVENT_RULES_H */ +/** @} */ diff --git a/src/gevent/sys_defs.h b/src/gevent/sys_defs.h deleted file mode 100644 index 9f1f4dde..00000000 --- a/src/gevent/sys_defs.h +++ /dev/null @@ -1,246 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gevent/sys_defs.h - * - * @addtogroup GEVENT - * - * @brief Module to build a complete many-to-many event system - * - * @details GEVENT provides a simple to use but yet powerful event - * system. - * - * @pre GFX_USE_GEVENT must be set to TRUE in your gfxconf.h - * - * @{ - */ -#ifndef _GEVENT_H -#define _GEVENT_H - -#include "gfx.h" - -#if GFX_USE_GEVENT || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Type definitions */ -/*===========================================================================*/ - -typedef uint16_t GEventType; - #define GEVENT_NULL 0x0000 // Null Event - Do nothing - #define GEVENT_EXIT 0x0001 // The listener is being forced to exit (someone is destroying the listener) - - /* Other event types are allocated in ranges in their respective include files */ - #define GEVENT_GINPUT_FIRST 0x0100 // GINPUT events range from 0x0100 to 0x01FF - #define GEVENT_GWIN_FIRST 0x0200 // GWIN events range from 0x0200 to 0x02FF - #define GEVENT_GADC_FIRST 0x0300 // GADC events range from 0x0300 to 0x033F - #define GEVENT_GAUDIO_FIRST 0x0340 // GAUDIO events range from 0x0340 to 0x037F - #define GEVENT_USER_FIRST 0x8000 // Any application defined events start at 0x8000 - -// This object can be typecast to any GEventXxxxx type to allow any sub-system (or the application) to create events. -// The prerequisite is that the new status structure type starts with a field named 'type' of type 'GEventType'. -// The total status structure also must not exceed GEVENT_MAXIMUM_SIZE bytes. -// For example, this is used by GWIN button events, GINPUT data streams etc. -typedef union GEvent_u { - GEventType type; // The type of this event - char pad[GEVENT_MAXIMUM_SIZE]; // This is here to allow static initialisation of GEventObject's in the application. -} GEvent; - -// A special callback function -typedef void (*GEventCallbackFn)(void *param, GEvent *pe); - -// The Listener Object -typedef struct GListener { - gfxSem waitqueue; // Private: Semaphore for the listener to wait on. - uint16_t flags; // Private: Flags for operation - GEventCallbackFn callback; // Private: Call back Function - void *param; // Private: Parameter for the callback function. - GEvent event; // Public: The event object into which the event information is stored. - } GListener; - -// The Source Object -typedef struct GSource_t GSource, *GSourceHandle; - -// This structure is passed to a source to describe a contender listener for sending the current event. -typedef struct GSourceListener_t { - GListener *pListener; // The listener - GSource *pSource; // The source - unsigned listenflags; // The flags the listener passed when the source was assigned to it. - unsigned srcflags; // For the source's exclusive use. Initialised as 0 for a new listener source assignment. - } GSourceListener; - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#ifdef __cplusplus -extern "C" { -#endif - -/* How to listen for events (act as a Listener)... - 1. Get handles for all the event sources you are interested in. - 2. Initialise a listener - 3. Attach sources to your listener. - - Sources can be attached or detached from a listener at any time. - - A source can be attached to more than one listener. - 4. Loop on getting listener events - 5. When finished detach all sources from the listener - - How to create events (act as a Source)... - 1. Provide a funtion to the application that returns a GSourceHandle (which can be a pointer to whatever the source wants) - 2. Whenever a possible event occurs call geventGetSourceListener to get a pointer to a GSourceListener. - This will return NULL when there are no more listeners. - For each listener - check the flags to see if an event should be sent. - - use geventGetEvent() to get the event buffer supplied by the listener - and then call geventSendEvent to send the event. - - Note: geventGetEvent() may return FALSE to indicate the listener is currently not listening and - therefore no event should be sent. This situation enables the source to (optionally) flag - to the listener on its next wait that there have been missed events. - - Note: The GSourceListener pointer (and the GEvent buffer) are only valid between - the geventGetSourceListener call and either the geventSendEvent call or the next - geventGetSourceListener call. - - Note: All listeners must be processed for this event before anything else is processed. -*/ - -/*---------- Listener Functions --------------------------------------------*/ - -/** - * @brief Create a Listener - * @details If insufficient resources are available it will either assert or return NULL - * depending on the value of GEVENT_ASSERT_NO_RESOURCE. - * - * @param[in] pl A listener - */ -void geventListenerInit(GListener *pl); - -/** - * @brief Attach a source to a listener - * @details Flags are interpreted by the source when generating events for each listener. - * If this source is already assigned to the listener it will update the flags. - * If insufficient resources are available it will either assert or return FALSE - * depending on the value of GEVENT_ASSERT_NO_RESOURCE. - * - * @param[in] pl The listener - * @param[in] gsh The source which has to be attached to the listener - * @param[in] flags The flags - * - * @return TRUE if succeeded, FALSE otherwise - */ -bool_t geventAttachSource(GListener *pl, GSourceHandle gsh, unsigned flags); - -/** - * @brief Detach a source from a listener - * @details If gsh is NULL detach all sources from this listener and if there is still - * a thread waiting for events on this listener, it is sent the exit event. - * - * @param[in] pl The listener - * @param[in] gsh The source - */ -void geventDetachSource(GListener *pl, GSourceHandle gsh); - -/** - * @brief Wait for an event on a listener from an assigned source. - * @details The type of the event should be checked (pevent->type) and then pevent should - * be typecast to the actual event type if it needs to be processed. - * timeout specifies the time to wait in system ticks. - * TIME_INFINITE means no timeout - wait forever for an event. - * TIME_IMMEDIATE means return immediately - * @note The returned GEvent is released when this routine is called again - * or when optionally @p geventEventComplete() is called. Calling @p geventEventComplete() - * allows the GEvent object to be reused earlier which can reduce missed events. The GEvent - * object MUST NOT be used after this function is called (and is blocked waiting for the next - * event) or after geventEventComplete() is called. - * - * @param[in] pl The listener - * @param[in] timeout The timeout - * - * @return NULL on timeout - */ -GEvent *geventEventWait(GListener *pl, delaytime_t timeout); - -/** - * @brief Release the GEvent buffer associated with a listener. - * @details The GEvent returned by @p geventEventWait() is released. - * @note The GEvent pointer returned by @p geventEventWait() is released when @p geventEventWait() - * is called again or when this function is called. The GEvent - * object MUST NOT be used after this function is called. - * - * @param[in] pl The listener - */ -void geventEventComplete(GListener *pl); - -/* @brief Register a callback for an event on a listener from an assigned source. - * @details The type of the event should be checked (pevent->type) and then pevent should be typecast to the - * actual event type if it needs to be processed. - * - * @params[in] pl The Listener - * @params[in] fn The function to call back - * @params[in] param A parameter to pass the callback function - * - * @note The GEvent buffer is valid only during the time of the callback. The callback MUST NOT save - * a pointer to the buffer for use outside the callback. - * @note An existing callback function is de-registered by passing a NULL for 'fn'. Any existing - * callback function is replaced. Any thread currently waiting using geventEventWait will be sent the exit event. - * @note Callbacks occur in a thread context but stack space must be kept to a minumum and - * the callback must process quickly as all other events are performed on a single thread. - * @note In the callback function you should never call ANY event functions using your own GListener handle - * as it WILL create a deadlock and lock the system up. - * @note Applications should not use this call - geventEventWait() is the preferred mechanism for an - * application. This call is provided for GUI objects that may not have their own thread. - */ -void geventRegisterCallback(GListener *pl, GEventCallbackFn fn, void *param); - -/*---------- Source Functions --------------------------------------------*/ - -/** - * @brief Called by a source with a possible event to get a listener record. - * @details @p lastlr should be NULL on the first call and thereafter the result of the previous call. - * - * @param[in] gsh The source handler - * @param[in] lastlr The source listener - * - * @return NULL when there are no more listeners for this source - */ -GSourceListener *geventGetSourceListener(GSourceHandle gsh, GSourceListener *lastlr); - -/** - * @brief Get the event buffer from the GSourceListener. - * @details A NULL return allows the source to record (perhaps in glr->scrflags) that the listener - * has missed events. This can then be notified as part of the next event for the listener. - * The buffer can only be accessed untill the next call to geventGetSourceListener - * or geventSendEvent - * - * @param[in] psl The source listener - * - * @return NULL if the listener is not currently listening. - */ -GEvent *geventGetEventBuffer(GSourceListener *psl); - -/** - * @brief Called by a source to indicate the listener's event buffer has been filled. - * @details After calling this function the source must not reference in fields in the GSourceListener or the event buffer. - * - * @param[in] psl The source listener - */ -void geventSendEvent(GSourceListener *psl); - -/** - * @brief Detach any listener that has this source attached - * - * @param[in] gsh The source handle - */ -void geventDetachSourceListeners(GSourceHandle gsh); - -#ifdef __cplusplus -} -#endif - -#endif /* GFX_USE_GEVENT */ - -#endif /* _GEVENT_H */ -/** @} */ - diff --git a/src/gevent/sys_make.mk b/src/gevent/sys_make.mk deleted file mode 100644 index f8b88e56..00000000 --- a/src/gevent/sys_make.mk +++ /dev/null @@ -1 +0,0 @@ -GFXSRC += $(GFXLIB)/src/gevent/gevent_gevent.c diff --git a/src/gevent/sys_options.h b/src/gevent/sys_options.h deleted file mode 100644 index 6a58103f..00000000 --- a/src/gevent/sys_options.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gevent/sys_options.h - * @brief GEVENT sub-system options header file. - * - * @addtogroup GEVENT - * @{ - */ - -#ifndef _GEVENT_OPTIONS_H -#define _GEVENT_OPTIONS_H - -/** - * @name GEVENT Functionality to be included - * @{ - */ - /** - * @brief Should routines assert() if they run out of resources. - * @details Defaults to FALSE. - * @details If FALSE the application must be prepared to handle these - * failures. - */ - #ifndef GEVENT_ASSERT_NO_RESOURCE - #define GEVENT_ASSERT_NO_RESOURCE FALSE - #endif -/** - * @} - * - * @name GEVENT Optional Sizing Parameters - * @{ - */ - /** - * @brief Defines the maximum size of an event status variable. - * @details Defaults to 32 bytes - */ - #ifndef GEVENT_MAXIMUM_SIZE - #define GEVENT_MAXIMUM_SIZE 32 - #endif - /** - * @brief Defines the maximum Source/Listener pairs in the system. - * @details Defaults to 32 - */ - #ifndef GEVENT_MAX_SOURCE_LISTENERS - #define GEVENT_MAX_SOURCE_LISTENERS 32 - #endif -/** @} */ - -#endif /* _GEVENT_OPTIONS_H */ -/** @} */ diff --git a/src/gevent/sys_rules.h b/src/gevent/sys_rules.h deleted file mode 100644 index 8cf7e9b4..00000000 --- a/src/gevent/sys_rules.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gevent/sys_rules.h - * @brief GEVENT safety rules header file. - * - * @addtogroup GEVENT - * @{ - */ - -#ifndef _GEVENT_RULES_H -#define _GEVENT_RULES_H - -#if GFX_USE_GEVENT -#endif - -#endif /* _GEVENT_RULES_H */ -/** @} */ diff --git a/src/gfile/gfile.c b/src/gfile/gfile.c new file mode 100644 index 00000000..4c22e6cc --- /dev/null +++ b/src/gfile/gfile.c @@ -0,0 +1,401 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +#include "gfx.h" + +#if GFX_USE_GFILE + +#include "gfile_fs.h" + +/** + * Define the VMT's for the file-systems we want to search for files. + * Virtual file-systems that have special open() calls do not need to + * be in this list. + */ +#if GFILE_NEED_ROMFS + extern const GFILEVMT FsROMVMT; +#endif +#if GFILE_NEED_NATIVEFS + extern const GFILEVMT FsNativeVMT; +#endif +#if GFILE_NEED_FATFS + extern const GFILEVMT FsFatFSVMT; +#endif +#if GFILE_NEED_RAMFS + extern const GFILEVMT FsRAMVMT; +#endif + + +/** + * The order of the file-systems below determines the order + * that they are searched to find a file. + */ +static const GFILEVMT const * FsArray[] = { + #if GFILE_NEED_ROMFS + &FsROMVMT, + #endif + #if GFILE_NEED_NATIVEFS + &FsNativeVMT, + #endif + #if GFILE_NEED_FATFS + &FsFatFSVMT, + #endif + #if GFILE_NEED_RAMFS + &FsRAMVMT, + #endif +}; + +/* + * The table of GFILE's + */ +static GFILE gfileArr[GFILE_MAX_GFILES]; +GFILE *gfileStdIn; +GFILE *gfileStdOut; +GFILE *gfileStdErr; + +/** + * The init routine + */ +void _gfileInit(void) { + #if GFILE_NEED_NATIVEFS + extern void _gfileNativeAssignStdio(void); + _gfileNativeAssignStdio(); + #endif +} + +void _gfileDeinit(void) +{ + /* ToDo */ +} + +/** + * Internal routine to find an empty GFILE slot and interpret flags. + */ +GFILE *_gfileFindSlot(const char *mode) { + GFILE * f; + + // First find an available GFILE slot. + for (f = gfileArr; f < &gfileArr[GFILE_MAX_GFILES]; f++) { + if (!(f->flags & GFILEFLG_OPEN)) { + // Get the flags + switch(mode[0]) { + case 'r': + f->flags = GFILEFLG_READ|GFILEFLG_MUSTEXIST; + while (*++mode) { + switch(mode[0]) { + case '+': f->flags |= GFILEFLG_WRITE; break; + case 'b': f->flags |= GFILEFLG_BINARY; break; + } + } + break; + case 'w': + f->flags = GFILEFLG_WRITE|GFILEFLG_TRUNC; + while (*++mode) { + switch(mode[0]) { + case '+': f->flags |= GFILEFLG_READ; break; + case 'b': f->flags |= GFILEFLG_BINARY; break; + case 'x': f->flags |= GFILEFLG_MUSTNOTEXIST; break; + } + } + break; + case 'a': + f->flags = GFILEFLG_WRITE|GFILEFLG_APPEND; + while (*++mode) { + switch(mode[0]) { + case '+': f->flags |= GFILEFLG_READ; break; + case 'b': f->flags |= GFILEFLG_BINARY; break; + case 'x': f->flags |= GFILEFLG_MUSTNOTEXIST; break; + } + } + break; + default: + return 0; + } + return f; + } + } + return 0; +} + +/******************************************************** + * IO routines + ********************************************************/ + +bool_t gfileExists(const char *fname) { + const GFILEVMT * const *p; + + #if GFILE_ALLOW_DEVICESPECIFIC + if (fname[0] && fname[1] == '|') { + for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) { + if (p[0]->prefix == fname[0]) + return p[0]->exists && p[0]->exists(fname+2); + } + return FALSE; + } + #endif + + for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) { + if (p[0]->exists && p[0]->exists(fname)) + return TRUE; + } + return FALSE; +} + +bool_t gfileDelete(const char *fname) { + const GFILEVMT **p; + + #if GFILE_ALLOW_DEVICESPECIFIC + if (fname[0] && fname[1] == '|') { + for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) { + if (p[0]->prefix == fname[0]) + return p[0]->del && p[0]->del(fname+2); + } + return FALSE; + } + #endif + + for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) { + if (p[0]->del && p[0]->del(fname)) + return TRUE; + } + return FALSE; +} + +long int gfileGetFilesize(const char *fname) { + const GFILEVMT * const *p; + long int res; + + #if GFILE_ALLOW_DEVICESPECIFIC + if (fname[0] && fname[1] == '|') { + for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) { + if (p[0]->prefix == fname[0]) + return p[0]->filesize ? p[0]->filesize(fname+2) : -1; + } + return -1; + } + #endif + + for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) { + if (p[0]->filesize && (res = p[0]->filesize(fname)) != -1) + return res; + } + return -1; +} + +bool_t gfileRename(const char *oldname, const char *newname) { + const GFILEVMT * const *p; + + #if GFILE_ALLOW_DEVICESPECIFIC + if ((oldname[0] && oldname[1] == '|') || (newname[0] && newname[1] == '|')) { + char ch; + + if (oldname[0] && oldname[1] == '|') { + ch = oldname[0]; + oldname += 2; + if (newname[0] && newname[1] == '|') { + if (newname[0] != ch) + // Both oldname and newname are fs specific but different ones. + return FALSE; + newname += 2; + } + } else { + ch = newname[0]; + newname += 2; + } + for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) { + if (p[0]->prefix == ch) + return p[0]->ren && p[0]->ren(oldname, newname); + } + return FALSE; + } + #endif + + for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) { + if (p[0]->ren && p[0]->ren(oldname,newname)) + return TRUE; + } + return FALSE; +} + +static bool_t testopen(const GFILEVMT *p, GFILE *f, const char *fname) { + // If we want write but the fs doesn't allow it then return + if ((f->flags & GFILEFLG_WRITE) && !(p->flags & GFSFLG_WRITEABLE)) + return FALSE; + + // Try to open + if (!p->open || !p->open(f, fname)) + return FALSE; + + // File is open - fill in all the details + f->vmt = p; + f->pos = 0; + f->flags |= GFILEFLG_OPEN; + if (p->flags & GFSFLG_SEEKABLE) + f->flags |= GFILEFLG_CANSEEK; + return TRUE; +} + +GFILE *gfileOpen(const char *fname, const char *mode) { + GFILE * f; + const GFILEVMT * const *p; + + // Get an empty file and set the flags + if (!(f = _gfileFindSlot(mode))) + return 0; + + #if GFILE_ALLOW_DEVICESPECIFIC + if (fname[0] && fname[1] == '|') { + for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) { + if (p[0]->prefix == fname[0]) + return testopen(p[0], f, fname+2) ? f : 0; + } + + // File not found + return 0; + } + #endif + + for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) { + if (testopen(p[0], f, fname)) + return f; + } + + // File not found + return 0; +} + +void gfileClose(GFILE *f) { + if (!f || !(f->flags & GFILEFLG_OPEN)) + return; + if (f->vmt->close) + f->vmt->close(f); + f->flags = 0; +} + +size_t gfileRead(GFILE *f, void *buf, size_t len) { + size_t res; + + if (!f || (f->flags & (GFILEFLG_OPEN|GFILEFLG_READ)) != (GFILEFLG_OPEN|GFILEFLG_READ)) + return 0; + if (!f->vmt->read) + return 0; + if ((res = f->vmt->read(f, buf, len)) <= 0) + return 0; + f->pos += res; + return res; +} + +size_t gfileWrite(GFILE *f, const void *buf, size_t len) { + size_t res; + + if (!f || (f->flags & (GFILEFLG_OPEN|GFILEFLG_WRITE)) != (GFILEFLG_OPEN|GFILEFLG_WRITE)) + return 0; + if (!f->vmt->write) + return 0; + if ((res = f->vmt->write(f, buf, len)) <= 0) + return 0; + f->pos += res; + return res; +} + +long int gfileGetPos(GFILE *f) { + if (!f || !(f->flags & GFILEFLG_OPEN)) + return 0; + return f->pos; +} + +bool_t gfileSetPos(GFILE *f, long int pos) { + if (!f || !(f->flags & GFILEFLG_OPEN)) + return FALSE; + if (!f->vmt->setpos || !f->vmt->setpos(f, pos)) + return FALSE; + f->pos = pos; + return TRUE; +} + +long int gfileGetSize(GFILE *f) { + if (!f || !(f->flags & GFILEFLG_OPEN)) + return 0; + if (!f->vmt->getsize) + return 0; + return f->vmt->getsize(f); +} + +bool_t gfileEOF(GFILE *f) { + if (!f || !(f->flags & GFILEFLG_OPEN)) + return TRUE; + if (!f->vmt->eof) + return FALSE; + return f->vmt->eof(f); +} + +bool_t gfileMount(char fs, const char* drive) { + const GFILEVMT * const *p; + + // Find the correct VMT + for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) { + if (p[0]->prefix == fs) { + if (!p[0]->mount) + return FALSE; + return p[0]->mount(drive); + } + } + return FALSE; +} + +bool_t gfileUnmount(char fs, const char* drive) { + const GFILEVMT * const *p; + + // Find the correct VMT + for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) { + if (p[0]->prefix == fs) { + if (!p[0]->mount) + return FALSE; + return p[0]->unmount(drive); + } + } + return FALSE; +} + +bool_t gfileSync(GFILE *f) { + if (!f->vmt->sync) + return FALSE; + return f->vmt->sync(f); +} + +#if GFILE_NEED_FILELISTS + gfileList *gfileOpenFileList(char fs, const char *path, bool_t dirs) { + const GFILEVMT * const *p; + gfileList * pfl; + + // Find the correct VMT + for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) { + if (p[0]->prefix == fs) { + if (!p[0]->flopen) + return 0; + pfl = p[0]->flopen(path, dirs); + if (pfl) { + pfl->vmt = p[0]; + pfl->dirs = dirs; + } + return pfl; + } + } + return 0; + } + + const char *gfileReadFileList(gfileList *pfl) { + return pfl->vmt->flread ? pfl->vmt->flread(pfl) : 0; + } + + void gfileCloseFileList(gfileList *pfl) { + if (pfl->vmt->flclose) + pfl->vmt->flclose(pfl); + } +#endif + +#endif /* GFX_USE_GFILE */ diff --git a/src/gfile/gfile.h b/src/gfile/gfile.h new file mode 100644 index 00000000..f332db4c --- /dev/null +++ b/src/gfile/gfile.h @@ -0,0 +1,470 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gfile/gfile.h + * @brief GFILE - File IO Routines header file. + * + * @addtogroup GFILE + * + * @brief Module which contains Operating system independent FILEIO + * + * @{ + */ + +#ifndef _GFILE_H +#define _GFILE_H + +#include "gfx.h" + +#if GFX_USE_GFILE || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Type definitions */ +/*===========================================================================*/ + +/** + * @brief A file pointer + */ + +typedef struct GFILE GFILE; +typedef struct gfileList gfileList; + +extern GFILE *gfileStdIn; +extern GFILE *gfileStdErr; +extern GFILE *gfileStdOut; + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + + /** + * @brief Check if file exists + * + * @param[in] fname The file name + * + * @return TRUE if file exists, FALSE otherwise + * + * @api + */ + bool_t gfileExists(const char *fname); + + /** + * @brief Delete file + * + * @param[in] fname The file name + * + * @return TRUE on success, FALSE otherwise + * + * @api + */ + bool_t gfileDelete(const char *fname); + + /** + * @brief Get the size of a file + * @note Please use @p gfileGetSize() if the file is opened + * + * @param[in] fname The file name + * + * @return File size on success, -1 on error + * + * @api + */ + long int gfileGetFilesize(const char *fname); + + /** + * @brief Rename file + * + * @param[in] oldname The current file name + * @param[in] newname The new name of the file + * + * @return TRUE on success, FALSE otherwise + * + * @api + */ + bool_t gfileRename(const char *oldname, const char *newname); + + /** + * @brief Open file + * @details A file must be opened before it can be accessed + * @details The resulting GFILE will be used for all functions that access the file. + * + * @param[in] fname The file name + * @param[in] mode The mode. + * + * @return Valid GFILE on success, 0 otherwise + * + * @note The modes follow the c library fopen() standard. + * The valid modes are: + * <ul><li>r - Open for read, the file must exist</li> + * <li>w - Open for write, the file is truncated if it exists</li> + * <li>wx - Open for write, the file must not exist</li> + * <li>a - Open for append, the file is truncated if it exists</li> + * <li>ax - Open for append, the file must not exists</li> + * </ul> + * The following flags can also be added to the above modes:<br/> + * <ul><li>+ - Open for both read and write</li> + * <li>b - Open as a binary file rather than a text file</li> + * </ul> + * @note Not all file-systems support all modes. For example, write + * is not available with the ROM file-system. Similarly few platforms + * distinguish between binary and text files. + * @note Even though binary vs. text is relevant only for a small number of platforms + * the "b" flag should always be specified for binary files such as images. + * This ensures portability to other platforms. The extra flag will be ignored + * on platforms where it is not relevant. + * + * @api + */ + GFILE * gfileOpen(const char *fname, const char *mode); + + /** + * @brief Close file + * @details Closes a file after is has been opened using @p gfileOpen() + * + * @param[in] f The file + * + * @api + */ + void gfileClose(GFILE *f); + + /** + * @brief Read from file + * @details Reads a given amount of bytes from the file + * @details The read/write cursor will not be reset when calling this function + * + * @param[in] f The file + * @param[out] buf The buffer in which to save the content that has been read from the file + * @param[in] len Amount of bytes to read + * + * @return Amount of bytes read + * + * @api + */ + size_t gfileRead(GFILE *f, void *buf, size_t len); + + /** + * @brief Write to file + * @details Write a given amount of bytes to the file + * @details The read/write cursor will not be reset when calling this function + * + * @param[in] f The file + * @param[in] buf The buffer which contains the content that will be written to the file + * @param[in] len Amount of bytes to write + * + * @return Amount of bytes written + * + * @api + */ + size_t gfileWrite(GFILE *f, const void *buf, size_t len); + + /** + * @brief Get the current position of the read/write cursor + * + * @param[in] f The file + * + * @return The current position in the file + * + * @api + */ + long int gfileGetPos(GFILE *f); + + /** + * @brief Set the position of the read/write cursor + * + * @param[in] f The file + * @param[in] pos The position to which the cursor will be set + * + * @return TRUE on success, FALSE otherwise + * + * @api + */ + bool_t gfileSetPos(GFILE *f, long int pos); + + /** + * @brief Get the size of file + * @note Please use @p gfileGetFilesize() if the file is not opened + * + * @param[in] f The file + * + * @return The size of the file + * + * @api + */ + long int gfileGetSize(GFILE *f); + + /** + * @brief Check for EOF + * @details Checks if the cursor is at the end of the file + * + * @param[in] f The file + * + * @return TRUE if EOF, FALSE otherwise + * + * @api + */ + bool_t gfileEOF(GFILE *f); + + /** + * @brief Mount a logical drive (aka partition) + * + * @details Not supported by every file system + * @details Currently just one drive at one is supported. + * + * @param[in] fs The file system (F for FatFS) + * @param[in] drive The logical drive prefix + * + * @return TRUE on success, FALSE otherwise + * + * @api + */ + bool_t gfileMount(char fs, const char *drive); + + /** + * @brief Unmount a logical drive (aka partition) + * + * @details Does have no effect if @p gfileMount() as been called before hand + * + * @param[in] fs The file system (F for FatFS) + * @param[in] drive The logical drive prefix + * + * @return TRUE on success, FALSE otherwise + * + * @api + */ + bool_t gfileUnmount(char fs, const char *drive); + + /** + * @brief Syncs the file object (flushes the buffer) + * + * @details Not supported by every file system + * + * @param[in] f The file + * + * @return TRUE on success, FALSE otherwise + * + * @api + */ + bool_t gfileSync(GFILE *f); + + #if GFILE_NEED_FILELISTS || defined(__DOXYGEN__) + /** + * @brief Open a file list + * + * @param[in] fs The file system (F for FatFS) + * @param[in] path Path information to pass to the file system + * @param[in] dirs Pass TRUE to get directories only, FALSE to get files only + * + * @return A pointer to a file list on success, NULL otherwise + * + * @note The path parameter is handled in a file-system specific way. It could be + * treated as a directory name, it may be treated as a file pattern, or it + * may be ignored. Passing NULL will always return the full list of files + * in at least the top level directory. + * @note For file systems that do not support directories, passing TRUE for dirs + * will return an error. + * @note You must call @p gfileCloseFileList() when you have finished with the + * file list in order to free resources. + * + * @api + */ + gfileList *gfileOpenFileList(char fs, const char *path, bool_t dirs); + + /** + * @brief Get the next file in a file list. + * + * @param[in] pfl Pointer to a file list returned by @p gfileOpenFileList() + * + * @return A pointer to a file (or directory) name. Returns NULL if there are no more. + * + * @note The file name may contain the full directory path or may not depending + * on how the file system treats directories. + * @note The returned buffer may be destroyed by the next call to any of + * @p gfileOpenFileList(), @p gfileReadFileList() or @p gfileCloseFileList(). + * Do not use this pointer after one of those calls. + * + * @api + */ + const char *gfileReadFileList(gfileList *pfl); + + /** + * @brief Close a file list. + * + * @param[in] pfl Pointer to a file list returned by @p gfileOpenFileList() + * + * @api + */ + void gfileCloseFileList(gfileList *pfl); + #endif + + #if (GFILE_NEED_CHIBIOSFS && GFX_USE_OS_CHIBIOS) || defined(__DOXYGEN__) + /** + * @brief Open file from a ChibiOS BaseFileStream + * + * @param[in] BaseFileStreamPtr The BaseFileStream to open as a GFILE + * @param[in] mode The mode. + * + * @return Valid GFILE on success, 0 otherwise + * + * @note The modes are the same modes as in @p gfileOpen(). The + * open mode is NOT compared against the BaseFileStream capabilities. + * @note Supported operations are: read, write, getpos, setpos, eof and getsize + * + * @api + */ + GFILE * gfileOpenBaseFileStream(void *BaseFileStreamPtr, const char *mode); + #endif + + #if GFILE_NEED_MEMFS || defined(__DOXYGEN__) + /** + * @brief Open file from a memory pointer + * + * @param[in] memptr The pointer to the memory + * @param[in] mode The mode. + * + * @return Valid GFILE on success, 0 otherwise + * + * @note The modes are the same modes as in @p gfileOpen(). Note there is + * no concept of file-size. Be careful not to overwrite other memory or + * to read from inaccessible sections of memory. + * @note Supported operations are: read, write, getpos, setpos + * + * @api + */ + GFILE * gfileOpenMemory(void *memptr, const char *mode); + #endif + + #if GFILE_NEED_STRINGS || defined(__DOXYGEN__) + /** + * @brief Open file from a null terminated C string + * + * @param[in] str The pointer to the string or string buffer + * @param[in] mode The mode + * + * @return Valid GFILE on success, 0 otherwise + * + * @note The modes are the same modes as in @p gfileOpen(). Note there is + * no concept of file-size. Be careful not to overwrite other memory or + * to read from inaccessible sections of memory. + * @note Reading will return EOF when the NULL character is reached. + * @note Writing will always place a NULL in the next character effectively terminating the + * string at the character just written. + * @note Supported operations are: read, write, append, getpos, setpos + * @note Be careful with setpos and getpos. They do not check for the end of the string. + * @note Reading and Writing will read/write a maximum of one character at a time. + * + * @api + */ + GFILE * gfileOpenString(char *str, const char *mode); + #endif + + #if GFILE_NEED_PRINTG || defined(__DOXYGEN__) + #include <stdarg.h> + + int vfnprintg(GFILE *f, int maxlen, const char *fmt, va_list arg); + int fnprintg(GFILE *f, int maxlen, const char *fmt, ...); + #define vfprintg(f,m,a) vfnprintg(f,0,m,a) + #define fprintg(f,m,...) fnprintg(f,0,m,...) + #define vprintg(m,a) vfnprintg(gfileStdOut,0,m,a) + #define printg(m,...) fnprintg(gfileStdOut,0,m,...) + + #if GFILE_NEED_STRINGS + int vsnprintg(char *buf, int maxlen, const char *fmt, va_list arg); + int snprintg(char *buf, int maxlen, const char *fmt, ...); + #define vsprintg(s,m,a) vsnprintg(s,0,m,a) + #define sprintg(s,m,...) snprintg(s,0,m,...) + #endif + #endif + + #if GFILE_NEED_SCANG || defined(__DOXYGEN__) + #include <stdarg.h> + + int vfscang(GFILE *f, const char *fmt, va_list arg); + int fscang(GFILE *f, const char *fmt, ...); + #define vscang(f,a) vfscang(gfileStdIn,f,a) + #define scang(f,...) fscang(gfileStdIn,f,...) + + #if GFILE_NEED_STRINGS + int vsscang(const char *buf, const char *fmt, va_list arg); + int sscang(const char *buf, const char *fmt, ...); + #endif + #endif + + #if GFILE_NEED_STDIO && !defined(GFILE_IMPLEMENTATION) + #define stdin gfileStdIn + #define stdout gfileStdOut + #define stderr gfileStdErr + #define FILENAME_MAX 256 // Use a relatively small number for an embedded platform + #define L_tmpnam FILENAME_MAX + #define FOPEN_MAX GFILE_MAX_GFILES + #define TMP_MAX GFILE_MAX_GFILES + #define P_tmpdir "/tmp/" + #define FILE GFILE + #define fopen(n,m) gfileOpen(n,m) + #define fclose(f) gfileClose(f) + size_t gstdioRead(void * ptr, size_t size, size_t count, FILE *f); + size_t gstdioWrite(const void * ptr, size_t size, size_t count, FILE *f); + #define fread(p,sz,cnt,f) gstdioRead(p,sz,cnt,f) + #define fwrite(p,sz,cnt,f) gstdioWrite(p,sz,cnt,f) + int gstdioSeek(FILE *f, size_t offset, int origin); + #define fseek(f,ofs,org) gstdioSeek(f,ofs,org) + #define SEEK_SET 0 + #define SEEK_CUR 1 + #define SEEK_END 2 + #define remove(n) (!gfileDelete(n)) + #define rename(o,n) (!gfileRename(o,n)) + #define fflush(f) (0) + #define ftell(f) gfileGetPos(f) + #define fpos_t long int + int gstdioGetpos(FILE *f, long int *pos); + #define fgetpos(f,pos) gstdioGetpos(f,pos) + #define fsetpos(f, pos) (!gfileSetPos(f, *pos)) + #define rewind(f) gfileSetPos(f, 0); + #define feof(f) gfileEOF(f) + + #define vfprintf(f,m,a) vfnprintg(f,0,m,a) + #define fprintf(f,m,...) fnprintg(f,0,m,...) + #define vprintf(m,a) vfnprintg(gfileStdOut,0,m,a) + #define printf(m,...) fnprintg(gfileStdOut,0,m,...) + #define vsnprintf(s,n,m,a) vsnprintg(s,n,m,a) + #define snprintf(s,n,m,...) snprintg(s,n,m,...) + #define vsprintf(s,m,a) vsnprintg(s,0,m,a) + #define sprintf(s,m,...) snprintg(s,0,m,...) + //TODO + //void clearerr ( FILE * stream ); + //int ferror ( FILE * stream ); + //FILE * tmpfile ( void ); // Auto-deleting + //char * tmpnam ( char * str ); + //char * mktemp (char *template); + //FILE * freopen ( const char * filename, const char * mode, FILE * stream ); + //setbuf + //setvbuf + //fflush + //fgetc + //fgets + //fputc + //fputs + //getc + //getchar + //puts + //ungetc + //void perror (const char * str); + #endif + +#ifdef __cplusplus +} +#endif + +#endif /* GFX_USE_GFILE */ + +#endif /* _GFILE_H */ +/** @} */ + diff --git a/src/gfile/gfile.mk b/src/gfile/gfile.mk new file mode 100644 index 00000000..8f20b5ce --- /dev/null +++ b/src/gfile/gfile.mk @@ -0,0 +1,18 @@ +GFXSRC += $(GFXLIB)/src/gfile/gfile.c \ + $(GFXLIB)/src/gfile/gfile_fs_native.c \ + $(GFXLIB)/src/gfile/gfile_fs_ram.c \ + $(GFXLIB)/src/gfile/gfile_fs_rom.c \ + $(GFXLIB)/src/gfile/gfile_fs_fatfs.c \ + $(GFXLIB)/src/gfile/gfile_fs_petitfs.c \ + $(GFXLIB)/src/gfile/gfile_fs_mem.c \ + $(GFXLIB)/src/gfile/gfile_fs_chibios.c \ + $(GFXLIB)/src/gfile/gfile_fs_strings.c \ + $(GFXLIB)/src/gfile/gfile_printg.c \ + $(GFXLIB)/src/gfile/gfile_scang.c \ + $(GFXLIB)/src/gfile/gfile_stdio.c \ + $(GFXLIB)/src/gfile/gfile_fatfs_wrapper.c \ + $(GFXLIB)/src/gfile/gfile_fatfs_diskio_chibios.c \ + $(GFXLIB)/src/gfile/gfile_petitfs_wrapper.c \ + $(GFXLIB)/src/gfile/gfile_petitfs_diskio_chibios.c \ + + \ No newline at end of file diff --git a/src/gfile/gfile_fatfs_diskio_chibios.c b/src/gfile/gfile_fatfs_diskio_chibios.c index 46ddbb7e..319d1a86 100644 --- a/src/gfile/gfile_fatfs_diskio_chibios.c +++ b/src/gfile/gfile_fatfs_diskio_chibios.c @@ -5,12 +5,6 @@ /* disk I/O modules and attach it to FatFs module with common interface. */ /*-----------------------------------------------------------------------*/ -/** - * @file src/gfile/gfile_fatfs_diskio_chibios.c - * @brief GFILE FATFS wrapper. - * - */ - #include "gfx.h" #if GFX_USE_GFILE && GFILE_NEED_FATFS && GFX_USE_OS_CHIBIOS diff --git a/src/gfile/gfile_fatfs_wrapper.c b/src/gfile/gfile_fatfs_wrapper.c index edcab056..fb5e6ec2 100644 --- a/src/gfile/gfile_fatfs_wrapper.c +++ b/src/gfile/gfile_fatfs_wrapper.c @@ -5,12 +5,6 @@ * http://ugfx.org/license.html */ -/** - * @file src/gfile/gfile_fatfs_wrapper.c - * @brief GFILE FATFS wrapper. - * - */ - #include "gfx.h" #if GFX_USE_GFILE && GFILE_NEED_FATFS diff --git a/src/gfile/gfile_gfile.c b/src/gfile/gfile_gfile.c deleted file mode 100644 index 3547f861..00000000 --- a/src/gfile/gfile_gfile.c +++ /dev/null @@ -1,407 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gfile/gfile_gfile.c - * @brief GFILE code. - * - */ - -#include "gfx.h" - -#if GFX_USE_GFILE - -#include "gfile_fs.h" - -/** - * Define the VMT's for the file-systems we want to search for files. - * Virtual file-systems that have special open() calls do not need to - * be in this list. - */ -#if GFILE_NEED_ROMFS - extern const GFILEVMT FsROMVMT; -#endif -#if GFILE_NEED_NATIVEFS - extern const GFILEVMT FsNativeVMT; -#endif -#if GFILE_NEED_FATFS - extern const GFILEVMT FsFatFSVMT; -#endif -#if GFILE_NEED_RAMFS - extern const GFILEVMT FsRAMVMT; -#endif - - -/** - * The order of the file-systems below determines the order - * that they are searched to find a file. - */ -static const GFILEVMT const * FsArray[] = { - #if GFILE_NEED_ROMFS - &FsROMVMT, - #endif - #if GFILE_NEED_NATIVEFS - &FsNativeVMT, - #endif - #if GFILE_NEED_FATFS - &FsFatFSVMT, - #endif - #if GFILE_NEED_RAMFS - &FsRAMVMT, - #endif -}; - -/* - * The table of GFILE's - */ -static GFILE gfileArr[GFILE_MAX_GFILES]; -GFILE *gfileStdIn; -GFILE *gfileStdOut; -GFILE *gfileStdErr; - -/** - * The init routine - */ -void _gfileInit(void) { - #if GFILE_NEED_NATIVEFS - extern void _gfileNativeAssignStdio(void); - _gfileNativeAssignStdio(); - #endif -} - -void _gfileDeinit(void) -{ - /* ToDo */ -} - -/** - * Internal routine to find an empty GFILE slot and interpret flags. - */ -GFILE *_gfileFindSlot(const char *mode) { - GFILE * f; - - // First find an available GFILE slot. - for (f = gfileArr; f < &gfileArr[GFILE_MAX_GFILES]; f++) { - if (!(f->flags & GFILEFLG_OPEN)) { - // Get the flags - switch(mode[0]) { - case 'r': - f->flags = GFILEFLG_READ|GFILEFLG_MUSTEXIST; - while (*++mode) { - switch(mode[0]) { - case '+': f->flags |= GFILEFLG_WRITE; break; - case 'b': f->flags |= GFILEFLG_BINARY; break; - } - } - break; - case 'w': - f->flags = GFILEFLG_WRITE|GFILEFLG_TRUNC; - while (*++mode) { - switch(mode[0]) { - case '+': f->flags |= GFILEFLG_READ; break; - case 'b': f->flags |= GFILEFLG_BINARY; break; - case 'x': f->flags |= GFILEFLG_MUSTNOTEXIST; break; - } - } - break; - case 'a': - f->flags = GFILEFLG_WRITE|GFILEFLG_APPEND; - while (*++mode) { - switch(mode[0]) { - case '+': f->flags |= GFILEFLG_READ; break; - case 'b': f->flags |= GFILEFLG_BINARY; break; - case 'x': f->flags |= GFILEFLG_MUSTNOTEXIST; break; - } - } - break; - default: - return 0; - } - return f; - } - } - return 0; -} - -/******************************************************** - * IO routines - ********************************************************/ - -bool_t gfileExists(const char *fname) { - const GFILEVMT * const *p; - - #if GFILE_ALLOW_DEVICESPECIFIC - if (fname[0] && fname[1] == '|') { - for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) { - if (p[0]->prefix == fname[0]) - return p[0]->exists && p[0]->exists(fname+2); - } - return FALSE; - } - #endif - - for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) { - if (p[0]->exists && p[0]->exists(fname)) - return TRUE; - } - return FALSE; -} - -bool_t gfileDelete(const char *fname) { - const GFILEVMT **p; - - #if GFILE_ALLOW_DEVICESPECIFIC - if (fname[0] && fname[1] == '|') { - for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) { - if (p[0]->prefix == fname[0]) - return p[0]->del && p[0]->del(fname+2); - } - return FALSE; - } - #endif - - for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) { - if (p[0]->del && p[0]->del(fname)) - return TRUE; - } - return FALSE; -} - -long int gfileGetFilesize(const char *fname) { - const GFILEVMT * const *p; - long int res; - - #if GFILE_ALLOW_DEVICESPECIFIC - if (fname[0] && fname[1] == '|') { - for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) { - if (p[0]->prefix == fname[0]) - return p[0]->filesize ? p[0]->filesize(fname+2) : -1; - } - return -1; - } - #endif - - for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) { - if (p[0]->filesize && (res = p[0]->filesize(fname)) != -1) - return res; - } - return -1; -} - -bool_t gfileRename(const char *oldname, const char *newname) { - const GFILEVMT * const *p; - - #if GFILE_ALLOW_DEVICESPECIFIC - if ((oldname[0] && oldname[1] == '|') || (newname[0] && newname[1] == '|')) { - char ch; - - if (oldname[0] && oldname[1] == '|') { - ch = oldname[0]; - oldname += 2; - if (newname[0] && newname[1] == '|') { - if (newname[0] != ch) - // Both oldname and newname are fs specific but different ones. - return FALSE; - newname += 2; - } - } else { - ch = newname[0]; - newname += 2; - } - for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) { - if (p[0]->prefix == ch) - return p[0]->ren && p[0]->ren(oldname, newname); - } - return FALSE; - } - #endif - - for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) { - if (p[0]->ren && p[0]->ren(oldname,newname)) - return TRUE; - } - return FALSE; -} - -static bool_t testopen(const GFILEVMT *p, GFILE *f, const char *fname) { - // If we want write but the fs doesn't allow it then return - if ((f->flags & GFILEFLG_WRITE) && !(p->flags & GFSFLG_WRITEABLE)) - return FALSE; - - // Try to open - if (!p->open || !p->open(f, fname)) - return FALSE; - - // File is open - fill in all the details - f->vmt = p; - f->pos = 0; - f->flags |= GFILEFLG_OPEN; - if (p->flags & GFSFLG_SEEKABLE) - f->flags |= GFILEFLG_CANSEEK; - return TRUE; -} - -GFILE *gfileOpen(const char *fname, const char *mode) { - GFILE * f; - const GFILEVMT * const *p; - - // Get an empty file and set the flags - if (!(f = _gfileFindSlot(mode))) - return 0; - - #if GFILE_ALLOW_DEVICESPECIFIC - if (fname[0] && fname[1] == '|') { - for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) { - if (p[0]->prefix == fname[0]) - return testopen(p[0], f, fname+2) ? f : 0; - } - - // File not found - return 0; - } - #endif - - for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) { - if (testopen(p[0], f, fname)) - return f; - } - - // File not found - return 0; -} - -void gfileClose(GFILE *f) { - if (!f || !(f->flags & GFILEFLG_OPEN)) - return; - if (f->vmt->close) - f->vmt->close(f); - f->flags = 0; -} - -size_t gfileRead(GFILE *f, void *buf, size_t len) { - size_t res; - - if (!f || (f->flags & (GFILEFLG_OPEN|GFILEFLG_READ)) != (GFILEFLG_OPEN|GFILEFLG_READ)) - return 0; - if (!f->vmt->read) - return 0; - if ((res = f->vmt->read(f, buf, len)) <= 0) - return 0; - f->pos += res; - return res; -} - -size_t gfileWrite(GFILE *f, const void *buf, size_t len) { - size_t res; - - if (!f || (f->flags & (GFILEFLG_OPEN|GFILEFLG_WRITE)) != (GFILEFLG_OPEN|GFILEFLG_WRITE)) - return 0; - if (!f->vmt->write) - return 0; - if ((res = f->vmt->write(f, buf, len)) <= 0) - return 0; - f->pos += res; - return res; -} - -long int gfileGetPos(GFILE *f) { - if (!f || !(f->flags & GFILEFLG_OPEN)) - return 0; - return f->pos; -} - -bool_t gfileSetPos(GFILE *f, long int pos) { - if (!f || !(f->flags & GFILEFLG_OPEN)) - return FALSE; - if (!f->vmt->setpos || !f->vmt->setpos(f, pos)) - return FALSE; - f->pos = pos; - return TRUE; -} - -long int gfileGetSize(GFILE *f) { - if (!f || !(f->flags & GFILEFLG_OPEN)) - return 0; - if (!f->vmt->getsize) - return 0; - return f->vmt->getsize(f); -} - -bool_t gfileEOF(GFILE *f) { - if (!f || !(f->flags & GFILEFLG_OPEN)) - return TRUE; - if (!f->vmt->eof) - return FALSE; - return f->vmt->eof(f); -} - -bool_t gfileMount(char fs, const char* drive) { - const GFILEVMT * const *p; - - // Find the correct VMT - for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) { - if (p[0]->prefix == fs) { - if (!p[0]->mount) - return FALSE; - return p[0]->mount(drive); - } - } - return FALSE; -} - -bool_t gfileUnmount(char fs, const char* drive) { - const GFILEVMT * const *p; - - // Find the correct VMT - for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) { - if (p[0]->prefix == fs) { - if (!p[0]->mount) - return FALSE; - return p[0]->unmount(drive); - } - } - return FALSE; -} - -bool_t gfileSync(GFILE *f) { - if (!f->vmt->sync) - return FALSE; - return f->vmt->sync(f); -} - -#if GFILE_NEED_FILELISTS - gfileList *gfileOpenFileList(char fs, const char *path, bool_t dirs) { - const GFILEVMT * const *p; - gfileList * pfl; - - // Find the correct VMT - for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) { - if (p[0]->prefix == fs) { - if (!p[0]->flopen) - return 0; - pfl = p[0]->flopen(path, dirs); - if (pfl) { - pfl->vmt = p[0]; - pfl->dirs = dirs; - } - return pfl; - } - } - return 0; - } - - const char *gfileReadFileList(gfileList *pfl) { - return pfl->vmt->flread ? pfl->vmt->flread(pfl) : 0; - } - - void gfileCloseFileList(gfileList *pfl) { - if (pfl->vmt->flclose) - pfl->vmt->flclose(pfl); - } -#endif - -#endif /* GFX_USE_GFILE */ diff --git a/src/gfile/gfile_options.h b/src/gfile/gfile_options.h new file mode 100644 index 00000000..06781f38 --- /dev/null +++ b/src/gfile/gfile_options.h @@ -0,0 +1,207 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gfile/gfile_options.h + * @brief GFILE - File IO options header file. + * + * @addtogroup GFILE + * @{ + */ + +#ifndef _GFILE_OPTIONS_H +#define _GFILE_OPTIONS_H + +/** + * @name GFILE Functionality to be included + * @{ + */ + /** + * @brief Should the filesystem not be mounted automatically + * @details The filesystem is normally mounted automatically if the + * user does not do it manually. This option turns that off + * so the user must manually mount the file-system first. + * @details Defaults to FALSE + */ + #ifndef GFILE_NEED_NOAUTOMOUNT + #define GFILE_NEED_NOAUTOMOUNT FALSE + #endif + /** + * @brief Should the filesystem be synced automatically + * @details The filesystem will automatically be synced after an open() or + * write() call unless this feature is disabled. + * @details If this feature is disabled, the user should sync the filesystem + * himself using @p gfileSync() + * @details Not all filesystems implement the syncing feature. This feature will + * have no effect in such a case. + * @details Defaults to FALSE + */ + #ifndef GFILE_NEED_NOAUTOSYNC + #define GFILE_NEED_NOAUTOSYNC FALSE + #endif + /** + * @brief Include printg, fprintg etc functions + * @details Defaults to FALSE + * @pre To get the string sprintg functions you also need to define @p GFILE_NEED_STRINGS + */ + #ifndef GFILE_NEED_PRINTG + #define GFILE_NEED_PRINTG FALSE + #endif + /** + * @brief Include scang, fscang etc functions + * @details Defaults to FALSE + * @pre To get the string sscang functions you also need to define @p GFILE_NEED_STRINGS + */ + #ifndef GFILE_NEED_SCANG + #define GFILE_NEED_SCANG FALSE + #endif + /** + * @brief Include the string based file functions + * @details Defaults to FALSE + */ + #ifndef GFILE_NEED_STRINGS + #define GFILE_NEED_STRINGS FALSE + #endif + /** + * @brief Map many stdio functions to their GFILE equivalent + * @details Defaults to FALSE + * @note This replaces the functions in stdio.h with equivalents + * - Do not include stdio.h as it has different conflicting definitions. + */ + #ifndef GFILE_NEED_STDIO + #define GFILE_NEED_STDIO FALSE + #endif + /** + * @brief Include the ROM file system + * @details Defaults to FALSE + * @note If GFILE_ALLOW_DEVICESPECIFIC is on then you can ensure that you are + * opening a file on the ROM file system by prefixing + * its name with "S|" (the letter 'S', followed by a vertical bar). + * @note This requires a file called romfs_files.h to be in the + * users project include path. This file should include all the files + * converted to .h files using the file2c utility (using flags "-dbcs"). + */ + #ifndef GFILE_NEED_ROMFS + #define GFILE_NEED_ROMFS FALSE + #endif + /** + * @brief Include the RAM file system + * @details Defaults to FALSE + * @note If GFILE_ALLOW_DEVICESPECIFIC is on then you can ensure that you are + * opening a file on the RAM file system by prefixing + * its name with "R|" (the letter 'R', followed by a vertical bar). + * @note You must also define GFILE_RAMFS_SIZE with the size of the file system + * to be allocated in RAM. + */ + #ifndef GFILE_NEED_RAMFS + #define GFILE_NEED_RAMFS FALSE + #endif + /** + * @brief Include the FAT file system driver based on the FATFS library + * @details Defaults to FALSE + * @note If GFILE_ALLOW_DEVICESPECIFIC is on then you can ensure that you are + * opening a file on the FAT file system by prefixing + * its name with "F|" (the letter 'F', followed by a vertical bar). + * @note FATFS and PETITFS offer the same FAT file system support. They just use + * different constraints. PETITFS is smaller but has less features. Only + * one can be used at a time. The block interfaces are also different. + */ + #ifndef GFILE_NEED_FATFS + #define GFILE_NEED_FATFS FALSE + #endif + /** + * @brief Include the FAT file system driver based on the PETITFS library + * @details Defaults to FALSE + * @note If GFILE_ALLOW_DEVICESPECIFIC is on then you can ensure that you are + * opening a file on the FAT file system by prefixing + * its name with "F|" (the letter 'F', followed by a vertical bar). + * @note FATFS and PETITFS offer the same FAT file system support. They just use + * different constraints. PETITFS is smaller but has less features. Only + * one can be used at a time. The block interfaces are also different. + * @note Due to the restrictions on the PETITFS library on writing, we do not implement + * writing. + * @note PETITFS can only have one file open at a time. + */ + #ifndef GFILE_NEED_PETITFS + #define GFILE_NEED_PETITFS FALSE + #endif + /** + * @brief Include the operating system's native file system + * @details Defaults to FALSE + * @note If GFILE_ALLOW_DEVICESPECIFIC is on then you can ensure that you are + * opening a file on the native file system by prefixing + * its name with "N|" (the letter 'N', followed by a vertical bar). + * @note If defined then the gfileStdOut and gfileStdErr handles + * use the operating system equivalent stdio and stderr. + * If it is not defined the gfileStdOut and gfileStdErr io is discarded. + */ + #ifndef GFILE_NEED_NATIVEFS + #define GFILE_NEED_NATIVEFS FALSE + #endif + /** + * @brief Include ChibiOS BaseFileStream support + * @details Defaults to FALSE + * @pre This is only relevant on the ChibiOS operating system. + * @note Use the @p gfileOpenBaseFileStream() call to open a GFILE based on a + * BaseFileStream. The BaseFileStream must already be open. + * @note A GFile of this type cannot be opened by filename. The BaseFileStream + * must be pre-opened using the operating system. + */ + #ifndef GFILE_NEED_CHIBIOSFS + #define GFILE_NEED_CHIBIOSFS FALSE + #endif + /** + * @brief Include raw memory pointer support + * @details Defaults to FALSE + * @note Use the @p gfileOpenMemory() call to open a GFILE based on a + * memory pointer. The GFILE opened appears to be of unlimited size. + * @note A GFile of this type cannot be opened by filename. + */ + #ifndef GFILE_NEED_MEMFS + #define GFILE_NEED_MEMFS FALSE + #endif + /** + * @brief Include support for file list functions + * @details Defaults to FALSE + * @note Adds support for @p gfileOpenFileList(), @p gfileReadFileList() and @p gfileCloseFileList(). + */ + #ifndef GFILE_NEED_FILELISTS + #define GFILE_NEED_FILELISTS FALSE + #endif +/** + * @} + * + * @name GFILE Optional Parameters + * @{ + */ + /** + * @brief Add floating point support to printg/scang etc. + */ + #ifndef GFILE_ALLOW_FLOATS + #define GFILE_ALLOW_FLOATS FALSE + #endif + /** + * @brief Can the device be specified as part of the file name. + * @note If this is on then a device letter and a vertical bar can be + * prefixed on a file name to specify that it must be on a + * specific device. + */ + #ifndef GFILE_ALLOW_DEVICESPECIFIC + #define GFILE_ALLOW_DEVICESPECIFIC FALSE + #endif + /** + * @brief The maximum number of open files + * @note This count excludes gfileStdIn, gfileStdOut and gfileStdErr + * (if open by default). + */ + #ifndef GFILE_MAX_GFILES + #define GFILE_MAX_GFILES 3 + #endif +/** @} */ + +#endif /* _GFILE_OPTIONS_H */ +/** @} */ diff --git a/src/gfile/gfile_petitfs_diskio_chibios.c b/src/gfile/gfile_petitfs_diskio_chibios.c index 90e709e4..7aa236ba 100644 --- a/src/gfile/gfile_petitfs_diskio_chibios.c +++ b/src/gfile/gfile_petitfs_diskio_chibios.c @@ -5,12 +5,6 @@ /* disk I/O modules and attach it to FatFs module with common interface. */ /*-----------------------------------------------------------------------*/ -/** - * @file src/gfile/gfile_petitfs_diskio_chibios.c - * @brief GFILE FATFS wrapper. - * - */ - #include "gfx.h" #if GFX_USE_GFILE && GFILE_NEED_PETITFS && GFX_USE_OS_CHIBIOS diff --git a/src/gfile/gfile_petitfs_wrapper.c b/src/gfile/gfile_petitfs_wrapper.c index 8efc7eb9..b7bc0ee1 100644 --- a/src/gfile/gfile_petitfs_wrapper.c +++ b/src/gfile/gfile_petitfs_wrapper.c @@ -5,12 +5,6 @@ * http://ugfx.org/license.html */ -/** - * @file src/gfile/gfile_petitfs_wrapper.c - * @brief GFILE PETITFS wrapper. - * - */ - #include "gfx.h" #if GFX_USE_GFILE && GFILE_NEED_PETITFS diff --git a/src/gfile/gfile_rules.h b/src/gfile/gfile_rules.h new file mode 100644 index 00000000..949a500c --- /dev/null +++ b/src/gfile/gfile_rules.h @@ -0,0 +1,26 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gfile/gfile_rules.h + * @brief GFILE safety rules header file. + * + * @addtogroup GFILE + * @{ + */ + +#ifndef _GFILE_RULES_H +#define _GFILE_RULES_H + +#if GFX_USE_GFILE + #if GFILE_NEED_PETITFS && GFILE_NEED_FATFS + #error "GFILE: Both GFILE_NEED_PETITFS and GFILE_NEED_FATFS cannot both be turned on at the same time." + #endif +#endif + +#endif /* _GFILE_RULES_H */ +/** @} */ diff --git a/src/gfile/sys_defs.h b/src/gfile/sys_defs.h deleted file mode 100644 index 2c475b40..00000000 --- a/src/gfile/sys_defs.h +++ /dev/null @@ -1,470 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gfile/sys_defs.h - * @brief GFILE - File IO Routines header file. - * - * @addtogroup GFILE - * - * @brief Module which contains Operating system independent FILEIO - * - * @{ - */ - -#ifndef _GFILE_H -#define _GFILE_H - -#include "gfx.h" - -#if GFX_USE_GFILE || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Type definitions */ -/*===========================================================================*/ - -/** - * @brief A file pointer - */ - -typedef struct GFILE GFILE; -typedef struct gfileList gfileList; - -extern GFILE *gfileStdIn; -extern GFILE *gfileStdErr; -extern GFILE *gfileStdOut; - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#ifdef __cplusplus -extern "C" { -#endif - - /** - * @brief Check if file exists - * - * @param[in] fname The file name - * - * @return TRUE if file exists, FALSE otherwise - * - * @api - */ - bool_t gfileExists(const char *fname); - - /** - * @brief Delete file - * - * @param[in] fname The file name - * - * @return TRUE on success, FALSE otherwise - * - * @api - */ - bool_t gfileDelete(const char *fname); - - /** - * @brief Get the size of a file - * @note Please use @p gfileGetSize() if the file is opened - * - * @param[in] fname The file name - * - * @return File size on success, -1 on error - * - * @api - */ - long int gfileGetFilesize(const char *fname); - - /** - * @brief Rename file - * - * @param[in] oldname The current file name - * @param[in] newname The new name of the file - * - * @return TRUE on success, FALSE otherwise - * - * @api - */ - bool_t gfileRename(const char *oldname, const char *newname); - - /** - * @brief Open file - * @details A file must be opened before it can be accessed - * @details The resulting GFILE will be used for all functions that access the file. - * - * @param[in] fname The file name - * @param[in] mode The mode. - * - * @return Valid GFILE on success, 0 otherwise - * - * @note The modes follow the c library fopen() standard. - * The valid modes are: - * <ul><li>r - Open for read, the file must exist</li> - * <li>w - Open for write, the file is truncated if it exists</li> - * <li>wx - Open for write, the file must not exist</li> - * <li>a - Open for append, the file is truncated if it exists</li> - * <li>ax - Open for append, the file must not exists</li> - * </ul> - * The following flags can also be added to the above modes:<br/> - * <ul><li>+ - Open for both read and write</li> - * <li>b - Open as a binary file rather than a text file</li> - * </ul> - * @note Not all file-systems support all modes. For example, write - * is not available with the ROM file-system. Similarly few platforms - * distinguish between binary and text files. - * @note Even though binary vs. text is relevant only for a small number of platforms - * the "b" flag should always be specified for binary files such as images. - * This ensures portability to other platforms. The extra flag will be ignored - * on platforms where it is not relevant. - * - * @api - */ - GFILE * gfileOpen(const char *fname, const char *mode); - - /** - * @brief Close file - * @details Closes a file after is has been opened using @p gfileOpen() - * - * @param[in] f The file - * - * @api - */ - void gfileClose(GFILE *f); - - /** - * @brief Read from file - * @details Reads a given amount of bytes from the file - * @details The read/write cursor will not be reset when calling this function - * - * @param[in] f The file - * @param[out] buf The buffer in which to save the content that has been read from the file - * @param[in] len Amount of bytes to read - * - * @return Amount of bytes read - * - * @api - */ - size_t gfileRead(GFILE *f, void *buf, size_t len); - - /** - * @brief Write to file - * @details Write a given amount of bytes to the file - * @details The read/write cursor will not be reset when calling this function - * - * @param[in] f The file - * @param[in] buf The buffer which contains the content that will be written to the file - * @param[in] len Amount of bytes to write - * - * @return Amount of bytes written - * - * @api - */ - size_t gfileWrite(GFILE *f, const void *buf, size_t len); - - /** - * @brief Get the current position of the read/write cursor - * - * @param[in] f The file - * - * @return The current position in the file - * - * @api - */ - long int gfileGetPos(GFILE *f); - - /** - * @brief Set the position of the read/write cursor - * - * @param[in] f The file - * @param[in] pos The position to which the cursor will be set - * - * @return TRUE on success, FALSE otherwise - * - * @api - */ - bool_t gfileSetPos(GFILE *f, long int pos); - - /** - * @brief Get the size of file - * @note Please use @p gfileGetFilesize() if the file is not opened - * - * @param[in] f The file - * - * @return The size of the file - * - * @api - */ - long int gfileGetSize(GFILE *f); - - /** - * @brief Check for EOF - * @details Checks if the cursor is at the end of the file - * - * @param[in] f The file - * - * @return TRUE if EOF, FALSE otherwise - * - * @api - */ - bool_t gfileEOF(GFILE *f); - - /** - * @brief Mount a logical drive (aka partition) - * - * @details Not supported by every file system - * @details Currently just one drive at one is supported. - * - * @param[in] fs The file system (F for FatFS) - * @param[in] drive The logical drive prefix - * - * @return TRUE on success, FALSE otherwise - * - * @api - */ - bool_t gfileMount(char fs, const char *drive); - - /** - * @brief Unmount a logical drive (aka partition) - * - * @details Does have no effect if @p gfileMount() as been called before hand - * - * @param[in] fs The file system (F for FatFS) - * @param[in] drive The logical drive prefix - * - * @return TRUE on success, FALSE otherwise - * - * @api - */ - bool_t gfileUnmount(char fs, const char *drive); - - /** - * @brief Syncs the file object (flushes the buffer) - * - * @details Not supported by every file system - * - * @param[in] f The file - * - * @return TRUE on success, FALSE otherwise - * - * @api - */ - bool_t gfileSync(GFILE *f); - - #if GFILE_NEED_FILELISTS || defined(__DOXYGEN__) - /** - * @brief Open a file list - * - * @param[in] fs The file system (F for FatFS) - * @param[in] path Path information to pass to the file system - * @param[in] dirs Pass TRUE to get directories only, FALSE to get files only - * - * @return A pointer to a file list on success, NULL otherwise - * - * @note The path parameter is handled in a file-system specific way. It could be - * treated as a directory name, it may be treated as a file pattern, or it - * may be ignored. Passing NULL will always return the full list of files - * in at least the top level directory. - * @note For file systems that do not support directories, passing TRUE for dirs - * will return an error. - * @note You must call @p gfileCloseFileList() when you have finished with the - * file list in order to free resources. - * - * @api - */ - gfileList *gfileOpenFileList(char fs, const char *path, bool_t dirs); - - /** - * @brief Get the next file in a file list. - * - * @param[in] pfl Pointer to a file list returned by @p gfileOpenFileList() - * - * @return A pointer to a file (or directory) name. Returns NULL if there are no more. - * - * @note The file name may contain the full directory path or may not depending - * on how the file system treats directories. - * @note The returned buffer may be destroyed by the next call to any of - * @p gfileOpenFileList(), @p gfileReadFileList() or @p gfileCloseFileList(). - * Do not use this pointer after one of those calls. - * - * @api - */ - const char *gfileReadFileList(gfileList *pfl); - - /** - * @brief Close a file list. - * - * @param[in] pfl Pointer to a file list returned by @p gfileOpenFileList() - * - * @api - */ - void gfileCloseFileList(gfileList *pfl); - #endif - - #if (GFILE_NEED_CHIBIOSFS && GFX_USE_OS_CHIBIOS) || defined(__DOXYGEN__) - /** - * @brief Open file from a ChibiOS BaseFileStream - * - * @param[in] BaseFileStreamPtr The BaseFileStream to open as a GFILE - * @param[in] mode The mode. - * - * @return Valid GFILE on success, 0 otherwise - * - * @note The modes are the same modes as in @p gfileOpen(). The - * open mode is NOT compared against the BaseFileStream capabilities. - * @note Supported operations are: read, write, getpos, setpos, eof and getsize - * - * @api - */ - GFILE * gfileOpenBaseFileStream(void *BaseFileStreamPtr, const char *mode); - #endif - - #if GFILE_NEED_MEMFS || defined(__DOXYGEN__) - /** - * @brief Open file from a memory pointer - * - * @param[in] memptr The pointer to the memory - * @param[in] mode The mode. - * - * @return Valid GFILE on success, 0 otherwise - * - * @note The modes are the same modes as in @p gfileOpen(). Note there is - * no concept of file-size. Be careful not to overwrite other memory or - * to read from inaccessible sections of memory. - * @note Supported operations are: read, write, getpos, setpos - * - * @api - */ - GFILE * gfileOpenMemory(void *memptr, const char *mode); - #endif - - #if GFILE_NEED_STRINGS || defined(__DOXYGEN__) - /** - * @brief Open file from a null terminated C string - * - * @param[in] str The pointer to the string or string buffer - * @param[in] mode The mode - * - * @return Valid GFILE on success, 0 otherwise - * - * @note The modes are the same modes as in @p gfileOpen(). Note there is - * no concept of file-size. Be careful not to overwrite other memory or - * to read from inaccessible sections of memory. - * @note Reading will return EOF when the NULL character is reached. - * @note Writing will always place a NULL in the next character effectively terminating the - * string at the character just written. - * @note Supported operations are: read, write, append, getpos, setpos - * @note Be careful with setpos and getpos. They do not check for the end of the string. - * @note Reading and Writing will read/write a maximum of one character at a time. - * - * @api - */ - GFILE * gfileOpenString(char *str, const char *mode); - #endif - - #if GFILE_NEED_PRINTG || defined(__DOXYGEN__) - #include <stdarg.h> - - int vfnprintg(GFILE *f, int maxlen, const char *fmt, va_list arg); - int fnprintg(GFILE *f, int maxlen, const char *fmt, ...); - #define vfprintg(f,m,a) vfnprintg(f,0,m,a) - #define fprintg(f,m,...) fnprintg(f,0,m,...) - #define vprintg(m,a) vfnprintg(gfileStdOut,0,m,a) - #define printg(m,...) fnprintg(gfileStdOut,0,m,...) - - #if GFILE_NEED_STRINGS - int vsnprintg(char *buf, int maxlen, const char *fmt, va_list arg); - int snprintg(char *buf, int maxlen, const char *fmt, ...); - #define vsprintg(s,m,a) vsnprintg(s,0,m,a) - #define sprintg(s,m,...) snprintg(s,0,m,...) - #endif - #endif - - #if GFILE_NEED_SCANG || defined(__DOXYGEN__) - #include <stdarg.h> - - int vfscang(GFILE *f, const char *fmt, va_list arg); - int fscang(GFILE *f, const char *fmt, ...); - #define vscang(f,a) vfscang(gfileStdIn,f,a) - #define scang(f,...) fscang(gfileStdIn,f,...) - - #if GFILE_NEED_STRINGS - int vsscang(const char *buf, const char *fmt, va_list arg); - int sscang(const char *buf, const char *fmt, ...); - #endif - #endif - - #if GFILE_NEED_STDIO && !defined(GFILE_IMPLEMENTATION) - #define stdin gfileStdIn - #define stdout gfileStdOut - #define stderr gfileStdErr - #define FILENAME_MAX 256 // Use a relatively small number for an embedded platform - #define L_tmpnam FILENAME_MAX - #define FOPEN_MAX GFILE_MAX_GFILES - #define TMP_MAX GFILE_MAX_GFILES - #define P_tmpdir "/tmp/" - #define FILE GFILE - #define fopen(n,m) gfileOpen(n,m) - #define fclose(f) gfileClose(f) - size_t gstdioRead(void * ptr, size_t size, size_t count, FILE *f); - size_t gstdioWrite(const void * ptr, size_t size, size_t count, FILE *f); - #define fread(p,sz,cnt,f) gstdioRead(p,sz,cnt,f) - #define fwrite(p,sz,cnt,f) gstdioWrite(p,sz,cnt,f) - int gstdioSeek(FILE *f, size_t offset, int origin); - #define fseek(f,ofs,org) gstdioSeek(f,ofs,org) - #define SEEK_SET 0 - #define SEEK_CUR 1 - #define SEEK_END 2 - #define remove(n) (!gfileDelete(n)) - #define rename(o,n) (!gfileRename(o,n)) - #define fflush(f) (0) - #define ftell(f) gfileGetPos(f) - #define fpos_t long int - int gstdioGetpos(FILE *f, long int *pos); - #define fgetpos(f,pos) gstdioGetpos(f,pos) - #define fsetpos(f, pos) (!gfileSetPos(f, *pos)) - #define rewind(f) gfileSetPos(f, 0); - #define feof(f) gfileEOF(f) - - #define vfprintf(f,m,a) vfnprintg(f,0,m,a) - #define fprintf(f,m,...) fnprintg(f,0,m,...) - #define vprintf(m,a) vfnprintg(gfileStdOut,0,m,a) - #define printf(m,...) fnprintg(gfileStdOut,0,m,...) - #define vsnprintf(s,n,m,a) vsnprintg(s,n,m,a) - #define snprintf(s,n,m,...) snprintg(s,n,m,...) - #define vsprintf(s,m,a) vsnprintg(s,0,m,a) - #define sprintf(s,m,...) snprintg(s,0,m,...) - //TODO - //void clearerr ( FILE * stream ); - //int ferror ( FILE * stream ); - //FILE * tmpfile ( void ); // Auto-deleting - //char * tmpnam ( char * str ); - //char * mktemp (char *template); - //FILE * freopen ( const char * filename, const char * mode, FILE * stream ); - //setbuf - //setvbuf - //fflush - //fgetc - //fgets - //fputc - //fputs - //getc - //getchar - //puts - //ungetc - //void perror (const char * str); - #endif - -#ifdef __cplusplus -} -#endif - -#endif /* GFX_USE_GFILE */ - -#endif /* _GFILE_H */ -/** @} */ - diff --git a/src/gfile/sys_make.mk b/src/gfile/sys_make.mk deleted file mode 100644 index 0a85d1ae..00000000 --- a/src/gfile/sys_make.mk +++ /dev/null @@ -1,18 +0,0 @@ -GFXSRC += $(GFXLIB)/src/gfile/gfile_gfile.c \ - $(GFXLIB)/src/gfile/gfile_fs_native.c \ - $(GFXLIB)/src/gfile/gfile_fs_ram.c \ - $(GFXLIB)/src/gfile/gfile_fs_rom.c \ - $(GFXLIB)/src/gfile/gfile_fs_fatfs.c \ - $(GFXLIB)/src/gfile/gfile_fs_petitfs.c \ - $(GFXLIB)/src/gfile/gfile_fs_mem.c \ - $(GFXLIB)/src/gfile/gfile_fs_chibios.c \ - $(GFXLIB)/src/gfile/gfile_fs_strings.c \ - $(GFXLIB)/src/gfile/gfile_printg.c \ - $(GFXLIB)/src/gfile/gfile_scang.c \ - $(GFXLIB)/src/gfile/gfile_stdio.c \ - $(GFXLIB)/src/gfile/gfile_fatfs_wrapper.c \ - $(GFXLIB)/src/gfile/gfile_fatfs_diskio_chibios.c \ - $(GFXLIB)/src/gfile/gfile_petitfs_wrapper.c \ - $(GFXLIB)/src/gfile/gfile_petitfs_diskio_chibios.c \ - - \ No newline at end of file diff --git a/src/gfile/sys_options.h b/src/gfile/sys_options.h deleted file mode 100644 index 5581b13b..00000000 --- a/src/gfile/sys_options.h +++ /dev/null @@ -1,207 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gfile/sys_options.h - * @brief GFILE - File IO options header file. - * - * @addtogroup GFILE - * @{ - */ - -#ifndef _GFILE_OPTIONS_H -#define _GFILE_OPTIONS_H - -/** - * @name GFILE Functionality to be included - * @{ - */ - /** - * @brief Should the filesystem not be mounted automatically - * @details The filesystem is normally mounted automatically if the - * user does not do it manually. This option turns that off - * so the user must manually mount the file-system first. - * @details Defaults to FALSE - */ - #ifndef GFILE_NEED_NOAUTOMOUNT - #define GFILE_NEED_NOAUTOMOUNT FALSE - #endif - /** - * @brief Should the filesystem be synced automatically - * @details The filesystem will automatically be synced after an open() or - * write() call unless this feature is disabled. - * @details If this feature is disabled, the user should sync the filesystem - * himself using @p gfileSync() - * @details Not all filesystems implement the syncing feature. This feature will - * have no effect in such a case. - * @details Defaults to FALSE - */ - #ifndef GFILE_NEED_NOAUTOSYNC - #define GFILE_NEED_NOAUTOSYNC FALSE - #endif - /** - * @brief Include printg, fprintg etc functions - * @details Defaults to FALSE - * @pre To get the string sprintg functions you also need to define @p GFILE_NEED_STRINGS - */ - #ifndef GFILE_NEED_PRINTG - #define GFILE_NEED_PRINTG FALSE - #endif - /** - * @brief Include scang, fscang etc functions - * @details Defaults to FALSE - * @pre To get the string sscang functions you also need to define @p GFILE_NEED_STRINGS - */ - #ifndef GFILE_NEED_SCANG - #define GFILE_NEED_SCANG FALSE - #endif - /** - * @brief Include the string based file functions - * @details Defaults to FALSE - */ - #ifndef GFILE_NEED_STRINGS - #define GFILE_NEED_STRINGS FALSE - #endif - /** - * @brief Map many stdio functions to their GFILE equivalent - * @details Defaults to FALSE - * @note This replaces the functions in stdio.h with equivalents - * - Do not include stdio.h as it has different conflicting definitions. - */ - #ifndef GFILE_NEED_STDIO - #define GFILE_NEED_STDIO FALSE - #endif - /** - * @brief Include the ROM file system - * @details Defaults to FALSE - * @note If GFILE_ALLOW_DEVICESPECIFIC is on then you can ensure that you are - * opening a file on the ROM file system by prefixing - * its name with "S|" (the letter 'S', followed by a vertical bar). - * @note This requires a file called romfs_files.h to be in the - * users project include path. This file should include all the files - * converted to .h files using the file2c utility (using flags "-dbcs"). - */ - #ifndef GFILE_NEED_ROMFS - #define GFILE_NEED_ROMFS FALSE - #endif - /** - * @brief Include the RAM file system - * @details Defaults to FALSE - * @note If GFILE_ALLOW_DEVICESPECIFIC is on then you can ensure that you are - * opening a file on the RAM file system by prefixing - * its name with "R|" (the letter 'R', followed by a vertical bar). - * @note You must also define GFILE_RAMFS_SIZE with the size of the file system - * to be allocated in RAM. - */ - #ifndef GFILE_NEED_RAMFS - #define GFILE_NEED_RAMFS FALSE - #endif - /** - * @brief Include the FAT file system driver based on the FATFS library - * @details Defaults to FALSE - * @note If GFILE_ALLOW_DEVICESPECIFIC is on then you can ensure that you are - * opening a file on the FAT file system by prefixing - * its name with "F|" (the letter 'F', followed by a vertical bar). - * @note FATFS and PETITFS offer the same FAT file system support. They just use - * different constraints. PETITFS is smaller but has less features. Only - * one can be used at a time. The block interfaces are also different. - */ - #ifndef GFILE_NEED_FATFS - #define GFILE_NEED_FATFS FALSE - #endif - /** - * @brief Include the FAT file system driver based on the PETITFS library - * @details Defaults to FALSE - * @note If GFILE_ALLOW_DEVICESPECIFIC is on then you can ensure that you are - * opening a file on the FAT file system by prefixing - * its name with "F|" (the letter 'F', followed by a vertical bar). - * @note FATFS and PETITFS offer the same FAT file system support. They just use - * different constraints. PETITFS is smaller but has less features. Only - * one can be used at a time. The block interfaces are also different. - * @note Due to the restrictions on the PETITFS library on writing, we do not implement - * writing. - * @note PETITFS can only have one file open at a time. - */ - #ifndef GFILE_NEED_PETITFS - #define GFILE_NEED_PETITFS FALSE - #endif - /** - * @brief Include the operating system's native file system - * @details Defaults to FALSE - * @note If GFILE_ALLOW_DEVICESPECIFIC is on then you can ensure that you are - * opening a file on the native file system by prefixing - * its name with "N|" (the letter 'N', followed by a vertical bar). - * @note If defined then the gfileStdOut and gfileStdErr handles - * use the operating system equivalent stdio and stderr. - * If it is not defined the gfileStdOut and gfileStdErr io is discarded. - */ - #ifndef GFILE_NEED_NATIVEFS - #define GFILE_NEED_NATIVEFS FALSE - #endif - /** - * @brief Include ChibiOS BaseFileStream support - * @details Defaults to FALSE - * @pre This is only relevant on the ChibiOS operating system. - * @note Use the @p gfileOpenBaseFileStream() call to open a GFILE based on a - * BaseFileStream. The BaseFileStream must already be open. - * @note A GFile of this type cannot be opened by filename. The BaseFileStream - * must be pre-opened using the operating system. - */ - #ifndef GFILE_NEED_CHIBIOSFS - #define GFILE_NEED_CHIBIOSFS FALSE - #endif - /** - * @brief Include raw memory pointer support - * @details Defaults to FALSE - * @note Use the @p gfileOpenMemory() call to open a GFILE based on a - * memory pointer. The GFILE opened appears to be of unlimited size. - * @note A GFile of this type cannot be opened by filename. - */ - #ifndef GFILE_NEED_MEMFS - #define GFILE_NEED_MEMFS FALSE - #endif - /** - * @brief Include support for file list functions - * @details Defaults to FALSE - * @note Adds support for @p gfileOpenFileList(), @p gfileReadFileList() and @p gfileCloseFileList(). - */ - #ifndef GFILE_NEED_FILELISTS - #define GFILE_NEED_FILELISTS FALSE - #endif -/** - * @} - * - * @name GFILE Optional Parameters - * @{ - */ - /** - * @brief Add floating point support to printg/scang etc. - */ - #ifndef GFILE_ALLOW_FLOATS - #define GFILE_ALLOW_FLOATS FALSE - #endif - /** - * @brief Can the device be specified as part of the file name. - * @note If this is on then a device letter and a vertical bar can be - * prefixed on a file name to specify that it must be on a - * specific device. - */ - #ifndef GFILE_ALLOW_DEVICESPECIFIC - #define GFILE_ALLOW_DEVICESPECIFIC FALSE - #endif - /** - * @brief The maximum number of open files - * @note This count excludes gfileStdIn, gfileStdOut and gfileStdErr - * (if open by default). - */ - #ifndef GFILE_MAX_GFILES - #define GFILE_MAX_GFILES 3 - #endif -/** @} */ - -#endif /* _GFILE_OPTIONS_H */ -/** @} */ diff --git a/src/gfile/sys_rules.h b/src/gfile/sys_rules.h deleted file mode 100644 index d13041f2..00000000 --- a/src/gfile/sys_rules.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gfile/sys_rules.h - * @brief GFILE safety rules header file. - * - * @addtogroup GFILE - * @{ - */ - -#ifndef _GFILE_RULES_H -#define _GFILE_RULES_H - -#if GFX_USE_GFILE - #if GFILE_NEED_PETITFS && GFILE_NEED_FATFS - #error "GFILE: Both GFILE_NEED_PETITFS and GFILE_NEED_FATFS cannot both be turned on at the same time." - #endif -#endif - -#endif /* _GFILE_RULES_H */ -/** @} */ diff --git a/src/ginput/driver_dial.h b/src/ginput/driver_dial.h deleted file mode 100644 index bf01da20..00000000 --- a/src/ginput/driver_dial.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/ginput/driver_dial.h - * @brief GINPUT header file for dial drivers. - * - * @defgroup Dial Dial - * @ingroup GINPUT - * @{ - */ - -#ifndef _LLD_GINPUT_DIAL_H -#define _LLD_GINPUT_DIAL_H - -#if GINPUT_NEED_DIAL || defined(__DOXYGEN__) - -#include "ginput_lld_dial_config.h" - -typedef void (*DialCallbackFn)(uint16_t instance, uint16_t rawvalue); - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#ifdef __cplusplus -extern "C" { -#endif - - void ginput_lld_dial_init(void); - void ginput_lld_dial_poll(DialCallbackFn fn); - -#ifdef __cplusplus -} -#endif - -#endif /* GFX_USE_GINPUT && GINPUT_NEED_TOGGLE */ - -#endif /* _LLD_GINPUT_TOGGLE_H */ -/** @} */ - diff --git a/src/ginput/driver_keyboard.h b/src/ginput/driver_keyboard.h deleted file mode 100644 index 329df97a..00000000 --- a/src/ginput/driver_keyboard.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/ginput/driver_keyboard.h - * @brief GINPUT LLD header file for keyboard drivers. - * - * @defgroup Keyboard Keyboard - * @ingroup GINPUT - * @{ - */ - -#ifndef _LLD_GINPUT_KEYBOARD_H -#define _LLD_GINPUT_KEYBOARD_H - -#if GINPUT_NEED_KEYBOARD //|| defined(__DOXYGEN__) - -// Include the GDRIVER infrastructure -#include "src/gdriver/sys_defs.h" - -typedef struct GKeyboard { - GDriver d; // The driver overheads and vmt - uint16_t cntc; // The byte count in c - uint16_t cntsc; // The byte count in sc - char c[8]; // The utf8 code for the current key - char sc[8]; // The scancode for the current key - uint32_t keystate; // The keyboard state. - uint16_t flags; - #define GKEYBOARD_FLG_NEEDREAD 0x0001 - uint16_t laystate; // The layout state. - const uint8_t * pLayout; // The current keyboard layout - // Other driver specific fields may follow. -} GKeyboard; - -typedef struct GKeyboardVMT { - GDriverVMT d; // Device flags are part of the general vmt - #define GKEYBOARD_VFLG_NOPOLL 0x0001 // Do not poll this device - it is purely interrupt driven - #define GKEYBOARD_VFLG_DYNAMICONLY 0x8000 // This keyboard driver should not be statically initialized eg Win32 - const uint8_t * defLayout; // The default keyboard layout - bool_t (*init)(GKeyboard *m, unsigned driverinstance); // Required - void (*deinit)(GKeyboard *m); // Optional - int (*getdata)(GKeyboard *k, uint8_t *pch, int sz); // Required. Get zero or more scancode bytes. Returns the number of scancode bytes returns - void (*putdata)(GKeyboard *k, char ch); // Optional. Send a single byte to the keyboard. -} GKeyboardVMT; - -#define gkvmt(m) ((const GKeyboardVMT const *)((m)->d.vmt)) - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -// If we are not using multiple keyboards then hard-code the VMT name -#if !defined(GINPUT_KEYBOARD_DRIVER_LIST) - #undef GKEYBOARD_DRIVER_VMT - #define GKEYBOARD_DRIVER_VMT GKEYBOARDVMT_OnlyOne -#endif - -#ifdef __cplusplus -extern "C" { -#endif - /** - * @brief Initialize a keyboard driver - * - * @param[in] g The keyboard driver - * @param[in] param Unused by keyboard - * @param[in] driverinstance The driver instance ToDo: Add some more details - * @param[in] systeminstance The mouse instance ToDo: Add some more details - * - * @return TRUE on success, FALSE otherwise - * @note This routine is provided by the high level code for - * use in the driver VMT's GMouseVMT.d structure. - * - * @notapi - */ - bool_t _gkeyboardInitDriver(GDriver *g, void *param, unsigned driverinstance, unsigned systeminstance); - - /** - * @brief Routine that is called after initialization - * - * @param[in] g The keyboard driver - * @note This routine is provided by the high level code for - * use in the driver VMT's GKeyboardVMT.d structure. - * - * @notapi - */ - void _gkeyboardPostInitDriver(GDriver *g); - - /** - * @brief Deinitialize a keyboard driver - * - * @param[in] g The kerboard driver - * @note This routine is provided by the high level code for - * use in the driver VMT's GKeyboardVMT.d structure. - * - * @notapi - */ - void _gkeyboardDeInitDriver(GDriver *g); - - /** - * @brief Wakeup the high level code so that it attempts another read - * - * @note This routine is provided to low level drivers by the high level code - * - * @notapi - */ - void _gkeyboardWakeup(GKeyboard *k); - - /** - * @brief Wakeup the high level code so that it attempts another read - * - * @note This routine is provided to low level drivers by the high level code - * - * @iclass - * @notapi - */ - void _gkeyboardWakeupI(GKeyboard *k); - -#ifdef __cplusplus -} -#endif - -#endif /* GINPUT_NEED_KEYBOARD */ - -#endif /* _LLD_GINPUT_KEYBOARD_H */ -/** @} */ diff --git a/src/ginput/driver_mouse.h b/src/ginput/driver_mouse.h deleted file mode 100644 index 5f948458..00000000 --- a/src/ginput/driver_mouse.h +++ /dev/null @@ -1,174 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/ginput/driver_mouse.h - * @brief GINPUT LLD header file for mouse/touch drivers. - * - * @defgroup Mouse Mouse - * @ingroup GINPUT - * @{ - */ - -#ifndef _LLD_GINPUT_MOUSE_H -#define _LLD_GINPUT_MOUSE_H - -#if GINPUT_NEED_MOUSE //|| defined(__DOXYGEN__) - -// Include the GDRIVER infrastructure -#include "src/gdriver/sys_defs.h" - -typedef struct GMouseReading { - coord_t x, y, z; - uint16_t buttons; - } GMouseReading; - -#if !GINPUT_TOUCH_NOCALIBRATE - typedef struct GMouseCalibration { - float ax; - float bx; - float cx; - float ay; - float by; - float cy; - } GMouseCalibration; -#endif - -typedef struct GMouse { - GDriver d; // The driver overheads and vmt - GMouseReading r; // The current position and state - uint16_t flags; // Flags - #define GMOUSE_FLG_CLICK_TIMER 0x0001 // Currently timing a click - #define GMOUSE_FLG_INDELTA 0x0002 // Currently in a up/down transition test - #define GMOUSE_FLG_CLIP 0x0004 // Clip reading to the display - #define GMOUSE_FLG_CALIBRATE 0x0008 // Calibrate readings - #define GMOUSE_FLG_IN_CAL 0x0010 // Currently in calibration routine - #define GMOUSE_FLG_FINGERMODE 0x0020 // Mouse is currently in finger mode - #define GMOUSE_FLG_NEEDREAD 0x0040 // The mouse needs reading - #define GMOUSE_FLG_DRIVER_FIRST 0x0100 // The first flag available for the driver - point clickpos; // The position of the last click event - systemticks_t clicktime; // The time of the last click event - GDisplay * display; // The display the mouse is associated with - #if !GINPUT_TOUCH_NOCALIBRATE - GMouseCalibration caldata; // The calibration data - #endif - // Other driver specific fields may follow. -} GMouse; - -typedef struct GMouseJitter { - coord_t calibrate; // Maximum error for a calibration to succeed - coord_t click; // Movement allowed without discarding the CLICK or CLICKCXT event - coord_t move; // Movement allowed without discarding the MOVE event -} GMouseJitter; - -typedef struct GMouseVMT { - GDriverVMT d; // Device flags are part of the general vmt - #define GMOUSE_VFLG_TOUCH 0x0001 // This is a touch device (rather than a mouse). Button 1 is calculated from z value. - #define GMOUSE_VFLG_NOPOLL 0x0002 // Do not poll this device - it is purely interrupt driven - #define GMOUSE_VFLG_SELFROTATION 0x0004 // This device returns readings that are aligned with the display orientation - #define GMOUSE_VFLG_DEFAULTFINGER 0x0008 // Default to finger mode - #define GMOUSE_VFLG_CALIBRATE 0x0010 // This device requires calibration - #define GMOUSE_VFLG_CAL_EXTREMES 0x0020 // Use edge to edge calibration - #define GMOUSE_VFLG_CAL_TEST 0x0040 // Test the results of the calibration - #define GMOUSE_VFLG_ONLY_DOWN 0x0100 // This device returns a valid position only when the mouse is down - #define GMOUSE_VFLG_POORUPDOWN 0x0200 // Position readings during up/down are unreliable - #define GMOUSE_VFLG_DYNAMICONLY 0x8000 // This mouse driver should not be statically initialized eg Win32 - coord_t z_max; // TOUCH: Maximum possible z value (fully touched) - coord_t z_min; // TOUCH: Minimum possible z value (touch off screen). Note may also be > z_max - coord_t z_touchon; // TOUCH: z values between z_max and this are a solid touch on - coord_t z_touchoff; // TOUCH: z values between z_min and this are a solid touch off - - GMouseJitter pen_jitter; // PEN MODE: Jitter settings - GMouseJitter finger_jitter; // FINGER MODE: Jitter settings - - bool_t (*init)(GMouse *m, unsigned driverinstance); // Required - void (*deinit)(GMouse *m); // Optional - bool_t (*get)(GMouse *m, GMouseReading *prd); // Required - void (*calsave)(GMouse *m, const void *buf, size_t sz); // Optional - bool_t (*calload)(GMouse *m, void *buf, size_t sz); // Optional -} GMouseVMT; - -#define gmvmt(m) ((const GMouseVMT const *)((m)->d.vmt)) - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -// If we are not using multiple mice then hard-code the VMT name -#if !defined(GINPUT_MOUSE_DRIVER_LIST) - #undef GMOUSE_DRIVER_VMT - #define GMOUSE_DRIVER_VMT GMOUSEVMT_OnlyOne -#endif - -#ifdef __cplusplus -extern "C" { -#endif - /** - * @brief Initialize a mouse driver - * - * @param[in] g The mouse driver - * @param[in] display The display to which the mouse shall be assigned - * @param[in] driverinstance The driver instance ToDo: Add some more details - * @param[in] systeminstance The mouse instance ToDo: Add some more details - * - * @return TRUE on success, FALSE otherwise - * @note This routine is provided by the high level code for - * use in the driver VMT's GMouseVMT.d structure. - * - * @notapi - */ - bool_t _gmouseInitDriver(GDriver *g, void *display, unsigned driverinstance, unsigned systeminstance); - - /** - * @brief Routine that is called after initialization - * - * @param[in] g The mouse driver - * @note This routine is provided by the high level code for - * use in the driver VMT's GMouseVMT.d structure. - * - * @notapi - */ - void _gmousePostInitDriver(GDriver *g); - - /** - * @brief Deinitialize a mouse driver - * - * @param[in] g The mouse driver - * @note This routine is provided by the high level code for - * use in the driver VMT's GMouseVMT.d structure. - * - * @notapi - */ - void _gmouseDeInitDriver(GDriver *g); - - /** - * @brief Wakeup the high level code so that it attempts another read - * - * @note This routine is provided to low level drivers by the high level code - * - * @notapi - */ - void _gmouseWakeup(GMouse *m); - - /** - * @brief Wakeup the high level code so that it attempts another read - * - * @note This routine is provided to low level drivers by the high level code - * - * @iclass - * @notapi - */ - void _gmouseWakeupI(GMouse *m); - -#ifdef __cplusplus -} -#endif - -#endif /* GINPUT_NEED_MOUSE */ - -#endif /* _LLD_GINPUT_MOUSE_H */ -/** @} */ diff --git a/src/ginput/driver_toggle.h b/src/ginput/driver_toggle.h deleted file mode 100644 index 6d672c91..00000000 --- a/src/ginput/driver_toggle.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/ginput/driver_toggle.h - * @brief GINPUT header file for toggle drivers. - * - * @defgroup Toggle Toggle - * @ingroup GINPUT - * @{ - */ - -#ifndef _LLD_GINPUT_TOGGLE_H -#define _LLD_GINPUT_TOGGLE_H - -#if GINPUT_NEED_TOGGLE || defined(__DOXYGEN__) - -// Describes how the toggle bits are obtained -typedef struct GToggleConfig_t { - void *id; - unsigned mask; - unsigned invert; - unsigned mode; -} GToggleConfig; - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#ifdef __cplusplus -extern "C" { -#endif - - extern const GToggleConfig GInputToggleConfigTable[GINPUT_TOGGLE_CONFIG_ENTRIES]; - - void ginput_lld_toggle_init(const GToggleConfig *ptc); - unsigned ginput_lld_toggle_getbits(const GToggleConfig *ptc); - - /* This routine is provided to low level drivers to wakeup a value read from a thread context. - * Particularly useful if GINPUT_TOGGLE_POLL_PERIOD = TIME_INFINITE - */ - void ginputToggleWakeup(void); - - /* This routine is provided to low level drivers to wakeup a value read from an ISR - * Particularly useful if GINPUT_TOGGLE_POLL_PERIOD = TIME_INFINITE - */ - void ginputToggleWakeupI(void); - -#ifdef __cplusplus -} -#endif - -#endif /* GFX_USE_GINPUT && GINPUT_NEED_TOGGLE */ - -#endif /* _LLD_GINPUT_TOGGLE_H */ -/** @} */ - diff --git a/src/ginput/ginput.c b/src/ginput/ginput.c new file mode 100644 index 00000000..becefc19 --- /dev/null +++ b/src/ginput/ginput.c @@ -0,0 +1,54 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/ginput/ginput_ginput.c + * @brief GINPUT subsystem common code. + * + * @addtogroup GINPUT + * @{ + */ +#include "gfx.h" + +#if GFX_USE_GINPUT + +#if GINPUT_NEED_MOUSE + extern void _gmouseInit(void); + extern void _gmouseDeinit(void); +#endif +#if GINPUT_NEED_KEYBOARD + extern void _gkeyboardInit(void); + extern void _gkeyboardDeinit(void); +#endif + +void _ginputInit(void) +{ + #if GINPUT_NEED_MOUSE + _gmouseInit(); + #endif + #if GINPUT_NEED_KEYBOARD + _gkeyboardInit(); + #endif + /** + * This should really call an init routine for each ginput sub-system. + * Maybe we'll do this later. + */ +} + +void _ginputDeinit(void) +{ + #if GINPUT_NEED_KEYBOARD + _gkeyboardDeinit(); + #endif + #if GINPUT_NEED_MOUSE + _gmouseDeinit(); + #endif +} + +#endif /* GFX_USE_GINPUT */ +/** @} */ + diff --git a/src/ginput/ginput.h b/src/ginput/ginput.h new file mode 100644 index 00000000..469a10d1 --- /dev/null +++ b/src/ginput/ginput.h @@ -0,0 +1,50 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/ginput/ginput.h + * + * @addtogroup GINPUT + * + * @brief Module to interface different hardware input sources such as touchscreens + * + * @details GINPUT provides an easy and common interface to use different input devices + * such as touchscreens and mices. + * + * @pre GFX_USE_GINPUT must be set to TRUE in your gfxconf.h + * + * @{ + */ +#ifndef _GINPUT_H +#define _GINPUT_H + +#include "gfx.h" + +#if GFX_USE_GINPUT || defined(__DOXYGEN__) + +/* How to use... + + 1. Get source handles for all the inputs you are interested in. + - Attempting to get a handle for one instance of an input more than once will return the same handle + 2. Create a listener + 3. Assign inputs to your listener. + - Inputs can be assigned or released from a listener at any time. + - An input can be assigned to more than one listener. + 4. Loop on getting listener events + 5. When complete destroy the listener +*/ + +// Include various ginput types +#include "ginput_mouse.h" +#include "ginput_keyboard.h" +#include "ginput_toggle.h" +#include "ginput_dial.h" + +#endif /* GFX_USE_GINPUT */ + +#endif /* _GINPUT_H */ +/** @} */ diff --git a/src/ginput/ginput.mk b/src/ginput/ginput.mk new file mode 100644 index 00000000..c814afa2 --- /dev/null +++ b/src/ginput/ginput.mk @@ -0,0 +1,6 @@ +GFXSRC += $(GFXLIB)/src/ginput/ginput.c \ + $(GFXLIB)/src/ginput/ginput_mouse.c \ + $(GFXLIB)/src/ginput/ginput_keyboard.c \ + $(GFXLIB)/src/ginput/ginput_keyboard_microcode.c \ + $(GFXLIB)/src/ginput/ginput_toggle.c \ + $(GFXLIB)/src/ginput/ginput_dial.c diff --git a/src/ginput/ginput_dial.c b/src/ginput/ginput_dial.c index 6af89b31..6c4f872b 100644 --- a/src/ginput/ginput_dial.c +++ b/src/ginput/ginput_dial.c @@ -17,7 +17,7 @@ #if GFX_USE_GINPUT && GINPUT_NEED_DIAL -#include "driver_dial.h" +#include "ginput_driver_dial.h" static GTIMER_DECL(DialTimer); static struct DialStatus_t { diff --git a/src/ginput/ginput_driver_dial.h b/src/ginput/ginput_driver_dial.h new file mode 100644 index 00000000..146ffc8d --- /dev/null +++ b/src/ginput/ginput_driver_dial.h @@ -0,0 +1,45 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/ginput/ginput_driver_dial.h + * @brief GINPUT header file for dial drivers. + * + * @defgroup Dial Dial + * @ingroup GINPUT + * @{ + */ + +#ifndef _LLD_GINPUT_DIAL_H +#define _LLD_GINPUT_DIAL_H + +#if GINPUT_NEED_DIAL || defined(__DOXYGEN__) + +#include "ginput_lld_dial_config.h" + +typedef void (*DialCallbackFn)(uint16_t instance, uint16_t rawvalue); + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + + void ginput_lld_dial_init(void); + void ginput_lld_dial_poll(DialCallbackFn fn); + +#ifdef __cplusplus +} +#endif + +#endif /* GFX_USE_GINPUT && GINPUT_NEED_TOGGLE */ + +#endif /* _LLD_GINPUT_TOGGLE_H */ +/** @} */ + diff --git a/src/ginput/ginput_driver_keyboard.h b/src/ginput/ginput_driver_keyboard.h new file mode 100644 index 00000000..62a00339 --- /dev/null +++ b/src/ginput/ginput_driver_keyboard.h @@ -0,0 +1,129 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/ginput/ginput_driver_keyboard.h + * @brief GINPUT LLD header file for keyboard drivers. + * + * @defgroup Keyboard Keyboard + * @ingroup GINPUT + * @{ + */ + +#ifndef _LLD_GINPUT_KEYBOARD_H +#define _LLD_GINPUT_KEYBOARD_H + +#if GINPUT_NEED_KEYBOARD //|| defined(__DOXYGEN__) + +// Include the GDRIVER infrastructure +#include "src/gdriver/gdriver.h" + +typedef struct GKeyboard { + GDriver d; // The driver overheads and vmt + uint16_t cntc; // The byte count in c + uint16_t cntsc; // The byte count in sc + char c[8]; // The utf8 code for the current key + char sc[8]; // The scancode for the current key + uint32_t keystate; // The keyboard state. + uint16_t flags; + #define GKEYBOARD_FLG_NEEDREAD 0x0001 + uint16_t laystate; // The layout state. + const uint8_t * pLayout; // The current keyboard layout + // Other driver specific fields may follow. +} GKeyboard; + +typedef struct GKeyboardVMT { + GDriverVMT d; // Device flags are part of the general vmt + #define GKEYBOARD_VFLG_NOPOLL 0x0001 // Do not poll this device - it is purely interrupt driven + #define GKEYBOARD_VFLG_DYNAMICONLY 0x8000 // This keyboard driver should not be statically initialized eg Win32 + const uint8_t * defLayout; // The default keyboard layout + bool_t (*init)(GKeyboard *m, unsigned driverinstance); // Required + void (*deinit)(GKeyboard *m); // Optional + int (*getdata)(GKeyboard *k, uint8_t *pch, int sz); // Required. Get zero or more scancode bytes. Returns the number of scancode bytes returns + void (*putdata)(GKeyboard *k, char ch); // Optional. Send a single byte to the keyboard. +} GKeyboardVMT; + +#define gkvmt(m) ((const GKeyboardVMT const *)((m)->d.vmt)) + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +// If we are not using multiple keyboards then hard-code the VMT name +#if !defined(GINPUT_KEYBOARD_DRIVER_LIST) + #undef GKEYBOARD_DRIVER_VMT + #define GKEYBOARD_DRIVER_VMT GKEYBOARDVMT_OnlyOne +#endif + +#ifdef __cplusplus +extern "C" { +#endif + /** + * @brief Initialize a keyboard driver + * + * @param[in] g The keyboard driver + * @param[in] param Unused by keyboard + * @param[in] driverinstance The driver instance ToDo: Add some more details + * @param[in] systeminstance The mouse instance ToDo: Add some more details + * + * @return TRUE on success, FALSE otherwise + * @note This routine is provided by the high level code for + * use in the driver VMT's GMouseVMT.d structure. + * + * @notapi + */ + bool_t _gkeyboardInitDriver(GDriver *g, void *param, unsigned driverinstance, unsigned systeminstance); + + /** + * @brief Routine that is called after initialization + * + * @param[in] g The keyboard driver + * @note This routine is provided by the high level code for + * use in the driver VMT's GKeyboardVMT.d structure. + * + * @notapi + */ + void _gkeyboardPostInitDriver(GDriver *g); + + /** + * @brief Deinitialize a keyboard driver + * + * @param[in] g The kerboard driver + * @note This routine is provided by the high level code for + * use in the driver VMT's GKeyboardVMT.d structure. + * + * @notapi + */ + void _gkeyboardDeInitDriver(GDriver *g); + + /** + * @brief Wakeup the high level code so that it attempts another read + * + * @note This routine is provided to low level drivers by the high level code + * + * @notapi + */ + void _gkeyboardWakeup(GKeyboard *k); + + /** + * @brief Wakeup the high level code so that it attempts another read + * + * @note This routine is provided to low level drivers by the high level code + * + * @iclass + * @notapi + */ + void _gkeyboardWakeupI(GKeyboard *k); + +#ifdef __cplusplus +} +#endif + +#endif /* GINPUT_NEED_KEYBOARD */ + +#endif /* _LLD_GINPUT_KEYBOARD_H */ +/** @} */ diff --git a/src/ginput/ginput_driver_mouse.h b/src/ginput/ginput_driver_mouse.h new file mode 100644 index 00000000..93d01124 --- /dev/null +++ b/src/ginput/ginput_driver_mouse.h @@ -0,0 +1,174 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/ginput/ginput_driver_mouse.h + * @brief GINPUT LLD header file for mouse/touch drivers. + * + * @defgroup Mouse Mouse + * @ingroup GINPUT + * @{ + */ + +#ifndef _LLD_GINPUT_MOUSE_H +#define _LLD_GINPUT_MOUSE_H + +#if GINPUT_NEED_MOUSE //|| defined(__DOXYGEN__) + +// Include the GDRIVER infrastructure +#include "src/gdriver/gdriver.h" + +typedef struct GMouseReading { + coord_t x, y, z; + uint16_t buttons; + } GMouseReading; + +#if !GINPUT_TOUCH_NOCALIBRATE + typedef struct GMouseCalibration { + float ax; + float bx; + float cx; + float ay; + float by; + float cy; + } GMouseCalibration; +#endif + +typedef struct GMouse { + GDriver d; // The driver overheads and vmt + GMouseReading r; // The current position and state + uint16_t flags; // Flags + #define GMOUSE_FLG_CLICK_TIMER 0x0001 // Currently timing a click + #define GMOUSE_FLG_INDELTA 0x0002 // Currently in a up/down transition test + #define GMOUSE_FLG_CLIP 0x0004 // Clip reading to the display + #define GMOUSE_FLG_CALIBRATE 0x0008 // Calibrate readings + #define GMOUSE_FLG_IN_CAL 0x0010 // Currently in calibration routine + #define GMOUSE_FLG_FINGERMODE 0x0020 // Mouse is currently in finger mode + #define GMOUSE_FLG_NEEDREAD 0x0040 // The mouse needs reading + #define GMOUSE_FLG_DRIVER_FIRST 0x0100 // The first flag available for the driver + point clickpos; // The position of the last click event + systemticks_t clicktime; // The time of the last click event + GDisplay * display; // The display the mouse is associated with + #if !GINPUT_TOUCH_NOCALIBRATE + GMouseCalibration caldata; // The calibration data + #endif + // Other driver specific fields may follow. +} GMouse; + +typedef struct GMouseJitter { + coord_t calibrate; // Maximum error for a calibration to succeed + coord_t click; // Movement allowed without discarding the CLICK or CLICKCXT event + coord_t move; // Movement allowed without discarding the MOVE event +} GMouseJitter; + +typedef struct GMouseVMT { + GDriverVMT d; // Device flags are part of the general vmt + #define GMOUSE_VFLG_TOUCH 0x0001 // This is a touch device (rather than a mouse). Button 1 is calculated from z value. + #define GMOUSE_VFLG_NOPOLL 0x0002 // Do not poll this device - it is purely interrupt driven + #define GMOUSE_VFLG_SELFROTATION 0x0004 // This device returns readings that are aligned with the display orientation + #define GMOUSE_VFLG_DEFAULTFINGER 0x0008 // Default to finger mode + #define GMOUSE_VFLG_CALIBRATE 0x0010 // This device requires calibration + #define GMOUSE_VFLG_CAL_EXTREMES 0x0020 // Use edge to edge calibration + #define GMOUSE_VFLG_CAL_TEST 0x0040 // Test the results of the calibration + #define GMOUSE_VFLG_ONLY_DOWN 0x0100 // This device returns a valid position only when the mouse is down + #define GMOUSE_VFLG_POORUPDOWN 0x0200 // Position readings during up/down are unreliable + #define GMOUSE_VFLG_DYNAMICONLY 0x8000 // This mouse driver should not be statically initialized eg Win32 + coord_t z_max; // TOUCH: Maximum possible z value (fully touched) + coord_t z_min; // TOUCH: Minimum possible z value (touch off screen). Note may also be > z_max + coord_t z_touchon; // TOUCH: z values between z_max and this are a solid touch on + coord_t z_touchoff; // TOUCH: z values between z_min and this are a solid touch off + + GMouseJitter pen_jitter; // PEN MODE: Jitter settings + GMouseJitter finger_jitter; // FINGER MODE: Jitter settings + + bool_t (*init)(GMouse *m, unsigned driverinstance); // Required + void (*deinit)(GMouse *m); // Optional + bool_t (*get)(GMouse *m, GMouseReading *prd); // Required + void (*calsave)(GMouse *m, const void *buf, size_t sz); // Optional + bool_t (*calload)(GMouse *m, void *buf, size_t sz); // Optional +} GMouseVMT; + +#define gmvmt(m) ((const GMouseVMT const *)((m)->d.vmt)) + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +// If we are not using multiple mice then hard-code the VMT name +#if !defined(GINPUT_MOUSE_DRIVER_LIST) + #undef GMOUSE_DRIVER_VMT + #define GMOUSE_DRIVER_VMT GMOUSEVMT_OnlyOne +#endif + +#ifdef __cplusplus +extern "C" { +#endif + /** + * @brief Initialize a mouse driver + * + * @param[in] g The mouse driver + * @param[in] display The display to which the mouse shall be assigned + * @param[in] driverinstance The driver instance ToDo: Add some more details + * @param[in] systeminstance The mouse instance ToDo: Add some more details + * + * @return TRUE on success, FALSE otherwise + * @note This routine is provided by the high level code for + * use in the driver VMT's GMouseVMT.d structure. + * + * @notapi + */ + bool_t _gmouseInitDriver(GDriver *g, void *display, unsigned driverinstance, unsigned systeminstance); + + /** + * @brief Routine that is called after initialization + * + * @param[in] g The mouse driver + * @note This routine is provided by the high level code for + * use in the driver VMT's GMouseVMT.d structure. + * + * @notapi + */ + void _gmousePostInitDriver(GDriver *g); + + /** + * @brief Deinitialize a mouse driver + * + * @param[in] g The mouse driver + * @note This routine is provided by the high level code for + * use in the driver VMT's GMouseVMT.d structure. + * + * @notapi + */ + void _gmouseDeInitDriver(GDriver *g); + + /** + * @brief Wakeup the high level code so that it attempts another read + * + * @note This routine is provided to low level drivers by the high level code + * + * @notapi + */ + void _gmouseWakeup(GMouse *m); + + /** + * @brief Wakeup the high level code so that it attempts another read + * + * @note This routine is provided to low level drivers by the high level code + * + * @iclass + * @notapi + */ + void _gmouseWakeupI(GMouse *m); + +#ifdef __cplusplus +} +#endif + +#endif /* GINPUT_NEED_MOUSE */ + +#endif /* _LLD_GINPUT_MOUSE_H */ +/** @} */ diff --git a/src/ginput/ginput_driver_toggle.h b/src/ginput/ginput_driver_toggle.h new file mode 100644 index 00000000..0dced07b --- /dev/null +++ b/src/ginput/ginput_driver_toggle.h @@ -0,0 +1,61 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/ginput/ginput_driver_toggle.h + * @brief GINPUT header file for toggle drivers. + * + * @defgroup Toggle Toggle + * @ingroup GINPUT + * @{ + */ + +#ifndef _LLD_GINPUT_TOGGLE_H +#define _LLD_GINPUT_TOGGLE_H + +#if GINPUT_NEED_TOGGLE || defined(__DOXYGEN__) + +// Describes how the toggle bits are obtained +typedef struct GToggleConfig_t { + void *id; + unsigned mask; + unsigned invert; + unsigned mode; +} GToggleConfig; + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + + extern const GToggleConfig GInputToggleConfigTable[GINPUT_TOGGLE_CONFIG_ENTRIES]; + + void ginput_lld_toggle_init(const GToggleConfig *ptc); + unsigned ginput_lld_toggle_getbits(const GToggleConfig *ptc); + + /* This routine is provided to low level drivers to wakeup a value read from a thread context. + * Particularly useful if GINPUT_TOGGLE_POLL_PERIOD = TIME_INFINITE + */ + void ginputToggleWakeup(void); + + /* This routine is provided to low level drivers to wakeup a value read from an ISR + * Particularly useful if GINPUT_TOGGLE_POLL_PERIOD = TIME_INFINITE + */ + void ginputToggleWakeupI(void); + +#ifdef __cplusplus +} +#endif + +#endif /* GFX_USE_GINPUT && GINPUT_NEED_TOGGLE */ + +#endif /* _LLD_GINPUT_TOGGLE_H */ +/** @} */ + diff --git a/src/ginput/ginput_ginput.c b/src/ginput/ginput_ginput.c deleted file mode 100644 index becefc19..00000000 --- a/src/ginput/ginput_ginput.c +++ /dev/null @@ -1,54 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/ginput/ginput_ginput.c - * @brief GINPUT subsystem common code. - * - * @addtogroup GINPUT - * @{ - */ -#include "gfx.h" - -#if GFX_USE_GINPUT - -#if GINPUT_NEED_MOUSE - extern void _gmouseInit(void); - extern void _gmouseDeinit(void); -#endif -#if GINPUT_NEED_KEYBOARD - extern void _gkeyboardInit(void); - extern void _gkeyboardDeinit(void); -#endif - -void _ginputInit(void) -{ - #if GINPUT_NEED_MOUSE - _gmouseInit(); - #endif - #if GINPUT_NEED_KEYBOARD - _gkeyboardInit(); - #endif - /** - * This should really call an init routine for each ginput sub-system. - * Maybe we'll do this later. - */ -} - -void _ginputDeinit(void) -{ - #if GINPUT_NEED_KEYBOARD - _gkeyboardDeinit(); - #endif - #if GINPUT_NEED_MOUSE - _gmouseDeinit(); - #endif -} - -#endif /* GFX_USE_GINPUT */ -/** @} */ - diff --git a/src/ginput/ginput_keyboard.c b/src/ginput/ginput_keyboard.c index 2d284eaa..eac2e9e0 100644 --- a/src/ginput/ginput_keyboard.c +++ b/src/ginput/ginput_keyboard.c @@ -21,8 +21,8 @@ #endif // Get the keyboard driver interface -#include "driver_keyboard.h" -#include "keyboard_microcode.h" +#include "ginput_driver_keyboard.h" +#include "ginput_keyboard_microcode.h" // The keyboard poll timer static GTIMER_DECL(KeyboardTimer); diff --git a/src/ginput/ginput_keyboard_microcode.c b/src/ginput/ginput_keyboard_microcode.c new file mode 100644 index 00000000..e3c04d5f --- /dev/null +++ b/src/ginput/ginput_keyboard_microcode.c @@ -0,0 +1,73 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/ginput/keyboard_microcode.c + * @brief GINPUT keyboard standard microcode definitions. + */ + +#include "gfx.h" + +#if GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD && !GKEYBOARD_LAYOUT_OFF + +#include "keyboard_microcode.h" + +#if GKEYBOARD_LAYOUT_SCANCODE2_US + + #error "Keyboard Layout SCANCODE2_US is not fully implemented yet" + + uint8_t KeyboardLayout_ScancodeSet2_US[] = { + KMC_HEADERSTART, KMC_HEADER_ID1, KMC_HEADER_ID2, KMC_HEADER_VER_1, + + KMC_RECORDSTART, 0x03, // Handle E0 codes (ignore for now assuming a single character) + KMC_TEST_LASTCODE, 0xE0, + KMC_ACT_DONE, + KMC_RECORDSTART, 0x03, + KMC_TEST_CODE, 0xE0, + KMC_ACT_STOP, + + KMC_RECORDSTART, 0x03, // Handle E1 codes (ignore for now assuming a single character) + KMC_TEST_LASTCODE, 0xE1, + KMC_ACT_DONE, + KMC_RECORDSTART, 0x03, + KMC_TEST_CODE, 0xE1, + KMC_ACT_STOP, + + KMC_RECORDSTART, 0x03, // Handle E2 codes (ignore for now assuming a single character) + KMC_TEST_LASTCODE, 0xE2, + KMC_ACT_DONE, + KMC_RECORDSTART, 0x03, + KMC_TEST_CODE, 0xE2, + KMC_ACT_STOP, + + KMC_RECORDSTART, 0x06, // KeyUp + KMC_TEST_CODEBIT, 0x80, + KMC_ACT_STATEBIT, GKEYSTATE_KEYUP_BIT, + KMC_ACT_CODEBIT, 0x80 | KMC_BIT_CLEAR, + + KMC_RECORDSTART, 0x05, // CapsLock (on keyup to avoid repeats) + KMC_TEST_CODE, 0x58, + KMC_TEST_STATEBIT, GKEYSTATE_KEYUP_BIT | KMC_BIT_CLEAR, + KMC_ACT_DONE, + KMC_RECORDSTART, 0x05, + KMC_TEST_CODE, 0x58, + KMC_ACT_STATEBIT, GKEYSTATE_CAPSLOCK_BIT | KMC_BIT_INVERT, + KMC_ACT_DONE, + + KMC_RECORDSTART, 0x05, // Detect Shift Keys + //KMC_ACT_LAYOUTBIT, SCANCODESET2_LAYOUT_E0_BIT | KMC_BIT_CLEAR, + KMC_ACT_STOP, + + KMC_RECORDSTART, 0x03, + KMC_ACT_CHARRANGE, 0x00, + KMC_ACT_DONE, + + KMC_RECORDSTART, 0x00, + }; +#endif // GKEYBOARD_LAYOUT_SCANCODE2_US + +#endif // GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD && !GKEYBOARD_LAYOUT_OFF diff --git a/src/ginput/ginput_keyboard_microcode.h b/src/ginput/ginput_keyboard_microcode.h new file mode 100644 index 00000000..c18e94e5 --- /dev/null +++ b/src/ginput/ginput_keyboard_microcode.h @@ -0,0 +1,107 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/ginput/keyboard_microcode.h + * @brief GINPUT keyboard layout microcode definition. + */ + +#ifndef _KEYBOARD_MICROCODE_H +#define _KEYBOARD_MICROCODE_H + +/* + * Keyboard Layout Microcode Definition + * + * Each time a byte is received from the keyboard it is processed through the layout microcode engine. This enables conversion from + * scancodes to ascii, internationalization, and various other tricky keyboard behavior. + * Note a "code" is defined as a single byte of data from the keyboard, a "scancode" is one or more "codes" that are sent in response to one keyboard press or release. + * + * The layout microcode can even be switched on the fly by the application to effect such changes as changing from US English to Russian layouts. + * They could conceivably even be loaded from disk at run-time. + * + * In the interest of efficiency there is very little error checking. Make sure your layout microcode has been debugged properly before releasing + * to production code. + * + * Layout microcode consists of a header followed by 1 or more records. + * + * The header consists of a KMC_HEADERSTART and associated bytes. The is used only to check it looks like it might be layout microcode and to specify the + * version of microcode. Future versions of layout microcode will always have the same header at least to the version number. + * + * Each record is delimited by a KMC_RECORDSTART. Each record can contain a maximum of 255 bytes. + * A record length of zero indicates the end of the layout microcode. + * + * A record consists a mixture of tests and actions (normally the tests are first). If a test fails the rest of this record is skipped and the next + * record is processed. This has the effect of AND'ing multiple tests that occur together. + * KMC_TEST_INIT and KMC_TEST_ERROR are special. These tests must be the first byte in their respective record as without this test being there, there + * is an implicit test that a code has actually been received. + * If no records have successful tests for this code then by extension no actions are executed. That is, the code is ignored. + * After fully processing a record, the next record is processed. This can be prevented by using the KMC_ACT_STOP action. When encountered all processing + * on this code stops. + * + * Some tests set a pseudo variable called "diff". This is then used by some actions. At the start of a new record "diff" is set to "code" (or 0 for the init and + * error conditions). + * Some tests and actions do bit operations on either the saved key-state or on the code itself. Bit numbers (which can range from 0 to 31) test or affect the + * "set" state of the bit. OR'ing KMC_BIT_CLEAR with the bit number test or affect the "clear" state of the bit. For example, KMC_ACT_STATEBIT with a parameter + * of 10 will set bit 10 of the key-state. KMC_ACT_STATEBIT with a parameter of (10 | KMC_BIT_CLEAR) will clear bit 10 of the key-state. + * + */ + +#define KMC_HEADERSTART 0x00 // Followed by: ID1 ID2 VER - This is the start of layout microcode. + #define KMC_HEADER_ID1 'L' + #define KMC_HEADER_ID2 'M' + #define KMC_HEADER_VER_1 0x01 + + #define KMC_HEADER_VER_CURRENT KMC_HEADER_VER_1 // The current version number + #define KMC_HEADER_VER_MIN KMC_HEADER_VER_1 // The minimum version number accepted + #define KMC_HEADER_VER_MAX KMC_HEADER_VER_1 // The maximum version number accepted + +#define KMC_RECORDSTART 0x01 // Followed by: nn b0 b1 ... b(nn-1) - nn bytes of test and action follow, nn = 00 means end of all tests + +#define KMC_TEST_INIT 0x10 // Followed by: nothing - The layout is initializing +#define KMC_TEST_ERROR 0x11 // Followed by: nothing - The keyboard has signaled an error +#define KMC_TEST_CODE 0x12 // Followed by: aa - Code must equal aa. Diff is set to 0 +#define KMC_TEST_CODERANGE 0x13 // Followed by: aa bb - Code must be between aa and bb (inclusive). Diff is set to (code - aa) +#define KMC_TEST_CODETABLE 0x14 // Followed by: n m1 m2 ... - Code must equal an m value. There are n possible m values. Diff is set to 0 to n-1 (the match position) +#define KMC_TEST_STATEBIT 0x15 // Followed by: b - Test if a key-state bit is set/clear. b = 0 to 31 or b = (0 | KMC_BIT_CLEAR) to (31 | KMC_BIT_CLEAR) +#define KMC_TEST_STATEOR 0x16 // Followed by: b1 b2 - Test two key-state bits and OR the result +#define KMC_TEST_STATEAND 0x17 // Followed by: b1 b2 - Test two key-state bits and AND the result +#define KMC_TEST_LAYOUTBIT 0x18 // Followed by: b - Test if a layout bit is set/clear. b = 0 to 15 or b = (0 | KMC_BIT_CLEAR) to (15 | KMC_BIT_CLEAR) +#define KMC_TEST_LAYOUTOR 0x19 // Followed by: b1 b2 - Test two layout bits and OR the result +#define KMC_TEST_LAYOUTAND 0x1A // Followed by: b1 b2 - Test two layout bits and AND the result +#define KMC_TEST_CODEBIT 0x1B // Followed by: b - Test if a code bit is set/clear. b = 0 to 7 or b = (0 | KMC_BIT_CLEAR) to (7 | KMC_BIT_CLEAR) +#define KMC_TEST_CODEOR 0x1C // Followed by: b1 b2 - Test two code bits and OR the result +#define KMC_TEST_CODEAND 0x1D // Followed by: b1 b2 - Test two code bits and AND the result +#define KMC_TEST_LASTCODE 0x1E // Followed by: aa - Test if the last scancode was aa +#define KMC_TEST_SHIFT 0x20 // Followed by: nothing - Test if a shift key is down +#define KMC_TEST_NOSHIFT 0x21 // Followed by: nothing - Test if a shift key is not down +#define KMC_TEST_CTRL 0x22 // Followed by: nothing - Test if a control key is down +#define KMC_TEST_NOCTRL 0x23 // Followed by: nothing - Test if a control key is not down +#define KMC_TEST_ALT 0x24 // Followed by: nothing - Test if an alt key is down +#define KMC_TEST_NOALT 0x25 // Followed by: nothing - Test if an alt key is not down +#define KMC_TEST_CAPS 0x26 // Followed by: nothing - Test if capslock as modified by shift is active +#define KMC_TEST_NOCAPS 0x27 // Followed by: nothing - Test if capslock as modified by shift is not active +#define KMC_TEST_NUMLOCK 0x28 // Followed by: nothing - Test if numlock is active +#define KMC_TEST_NONUMLOCK 0x29 // Followed by: nothing - Test if numlock is not active + +#define KMC_ACT_STOP 0xFF // Followed by: nothing - Stop processing this code +#define KMC_ACT_DONE 0xFE // Followed by: nothing - Finished processing this scancode sequence. (also implies STOP) +#define KMC_ACT_RESET 0xFD // Followed by: nothing - Empty all buffers +#define KMC_ACT_STATEBIT 0x80 // Followed by: b - Set or clear bit b in key-state. b = 0 to 31 or b = (0 | KMC_BIT_CLEAR) to (31 | KMC_BIT_CLEAR) +#define KMC_ACT_LAYOUTBIT 0x81 // Followed by: b - Set or clear bit b in layout bits. b = 0 to 15 or b = (0 | KMC_BIT_CLEAR) to (15 | KMC_BIT_CLEAR) +#define KMC_ACT_CODEBIT 0x82 // Followed by: b - Set or clear bit b in code. b = 0 to 7 or b = (0 | KMC_BIT_CLEAR) to (7 | KMC_BIT_CLEAR) +#define KMC_ACT_CHAR 0x83 // Followed by: nn - Append char nn to output buffer +#define KMC_ACT_CHARCODE 0x84 // Followed by: nothing - Append the code to output buffer +#define KMC_ACT_CHARRANGE 0x85 // Followed by: nn - Append char nn + Diff to output +#define KMC_ACT_CHARTABLE 0x86 // Followed by: n c1 c2 ... - Append char to output based on c[Diff]. If Diff is greater than n then nothing is appended. +#define KMC_ACT_CLEAR 0x87 // Followed by: nothing - Clear the output buffer +#define KMC_ACT_CHARADD 0x88 // Followed by: nn - Multiple the last char in output buffer by nn (assume 0 if none) and add Diff +#define KMC_ACT_DATA 0x89 // Followed by: nn - Send nn back to the keyboard + +#define KMC_BIT_CLEAR 0x80 // The bit number is being used for a bit clear operation rather than a bit set operation. +#define KMC_BIT_INVERT 0x40 // Invert the bit rather than setting or clearing it. + +#endif /* _KEYBOARD_MICROCODE_H */ diff --git a/src/ginput/ginput_mouse.c b/src/ginput/ginput_mouse.c index 9ecf7dc7..f88c2ded 100644 --- a/src/ginput/ginput_mouse.c +++ b/src/ginput/ginput_mouse.c @@ -46,7 +46,7 @@ #define CALIBRATION_ERROR_HEIGHT 40 // Get the mouse driver interface -#include "driver_mouse.h" +#include "ginput_driver_mouse.h" // The mouse poll timer static GTIMER_DECL(MouseTimer); diff --git a/src/ginput/ginput_options.h b/src/ginput/ginput_options.h new file mode 100644 index 00000000..3d3478d2 --- /dev/null +++ b/src/ginput/ginput_options.h @@ -0,0 +1,221 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/ginput/ginput_options.h + * @brief GINPUT sub-system options header file. + * + * @addtogroup GINPUT + * @{ + */ + +#ifndef _GINPUT_OPTIONS_H +#define _GINPUT_OPTIONS_H + +/** + * @name GINPUT Functionality to be included + * @{ + */ + /** + * @brief Should mouse/touch functions be included. + * @details Defaults to FALSE + * @note Also add a mouse/touch hardware driver to your makefile. + * Eg. + * include $(GFXLIB)/drivers/ginput/touch/MCU/driver.mk + */ + #ifndef GINPUT_NEED_MOUSE + #define GINPUT_NEED_MOUSE FALSE + #endif + /** + * @brief Should keyboard functions be included. + * @details Defaults to FALSE + * @note Also add a keyboard hardware driver to your makefile. + * Eg. + * include $(GFXLIB)/drivers/ginput/keyboard/XXXX/driver.mk + */ + #ifndef GINPUT_NEED_KEYBOARD + #define GINPUT_NEED_KEYBOARD FALSE + #endif + /** + * @brief Should hardware toggle/switch/button functions be included. + * @details Defaults to FALSE + * @note Also add a toggle hardware driver to your makefile. + * Eg. + * include $(GFXLIB)/drivers/ginput/toggle/Pal/driver.mk + */ + #ifndef GINPUT_NEED_TOGGLE + #define GINPUT_NEED_TOGGLE FALSE + #endif + /** + * @brief Should analog dial functions be included. + * @details Defaults to FALSE + * @note Also add a dial hardware driver to your makefile. + * Eg. + * include $(GFXLIB)/drivers/ginput/dial/analog/driver.mk + */ + #ifndef GINPUT_NEED_DIAL + #define GINPUT_NEED_DIAL FALSE + #endif +/** + * @} + * + * @name GINPUT Optional Sizing Parameters + * @{ + */ +/** + * @} + * + * @name GINPUT Optional Low Level Driver Defines + * @{ + */ + /** + * @brief Start touch devices without loading or running calibration. + * @details Defaults to FALSE + * @note This is used if you want to manually control the initial calibration + * process. In practice this is only useful for a touch driver test program. + */ + #ifndef GINPUT_TOUCH_STARTRAW + #define GINPUT_TOUCH_STARTRAW FALSE + #endif + /** + * @brief Turn off the touch calibration GUI. + * @details Defaults to FALSE + * @note Turning off the calibration GUI just turns off the manual calibration + * process. Readings may still be calibrated if calibration data + * can be loaded. + * @note Calibration requires a lot of code. If your device doesn't require it + * using this option can save a lot of space. + */ + #ifndef GINPUT_TOUCH_NOCALIBRATE_GUI + #define GINPUT_TOUCH_NOCALIBRATE_GUI FALSE + #endif + /** + * @brief Turn off all touch calibration support. + * @details Defaults to FALSE + * @note With this set to TRUE touch readings will not be calibrated. + * @note This automatically turns off the calibration GUI too! + * @note Calibration requires a lot of code. If your device doesn't require it + * using this option can save a lot of space. + */ + #ifndef GINPUT_TOUCH_NOCALIBRATE + #define GINPUT_TOUCH_NOCALIBRATE FALSE + #endif + /** + * @brief Turn off all touch support. + * @details Defaults to FALSE + * @note This automatically turns off all calibration and the calibration GUI too! + * @note Touch device handling requires a lot of code. If your device doesn't require it + * using this option can save a lot of space. + */ + #ifndef GINPUT_TOUCH_NOTOUCH + #define GINPUT_TOUCH_NOTOUCH FALSE + #endif + /** + * @brief Milliseconds between mouse polls. + * @details Defaults to 25 milliseconds + * @note How often mice should be polled. More often leads to smoother mouse movement + * but increases CPU usage. + */ + #ifndef GINPUT_MOUSE_POLL_PERIOD + #define GINPUT_MOUSE_POLL_PERIOD 25 + #endif + + /** + * @brief Maximum length of CLICK in milliseconds + * @details Defaults to 300 milliseconds + * @note Mouse down to Mouse up times greater than this are not clicks. + */ + #ifndef GINPUT_MOUSE_CLICK_TIME + #define GINPUT_MOUSE_CLICK_TIME 300 + #endif + /** + * @brief Milliseconds to generate a CXTCLICK on a touch device. + * @details Defaults to 500 milliseconds + * @note If you hold the touch down for longer than this a CXTCLICK is generated + * but only on a touch device. + */ + #ifndef GINPUT_TOUCH_CXTCLICK_TIME + #define GINPUT_TOUCH_CXTCLICK_TIME 500 + #endif + /** + * @brief There is a user supplied routine to load mouse calibration data + * @details Defaults to FALSE + * @note If TRUE the user must supply the @p LoadMouseCalibration() routine. + */ + #ifndef GINPUT_TOUCH_USER_CALIBRATION_LOAD + #define GINPUT_TOUCH_USER_CALIBRATION_LOAD FALSE + #endif + /** + * @brief There is a user supplied routine to save mouse calibration data + * @details Defaults to FALSE + * @note If TRUE the user must supply the @p SaveMouseCalibration() routine. + */ + #ifndef GINPUT_TOUCH_USER_CALIBRATION_SAVE + #define GINPUT_TOUCH_USER_CALIBRATION_SAVE FALSE + #endif + /** + * @brief Define multiple static mice + * @details When not defined the system automatically detects a single linked mouse driver + * @note The references to GMOUSEVMT_Win32 in the definition would be replaced + * by the names of the VMT for each of the static mice you want to + * include. + * @note Dynamic mice associated automatically with a display eg Win32, X or GFXnet + * do not need to be specified in this list as the associated display driver will register + * them automatically as the display is created. + */ + #if defined(__DOXYGEN__) + #define GMOUSE_DRIVER_LIST GMOUSEVMT_Win32, GMOUSEVMT_Win32 + #endif + /** + * @brief Milliseconds between keyboard polls. + * @details Defaults to 200 milliseconds + * @note How often keyboards should be polled. + */ + #ifndef GINPUT_KEYBOARD_POLL_PERIOD + #define GINPUT_KEYBOARD_POLL_PERIOD 200 + #endif + /** + * @brief Define multiple static keyboards + * @details When not defined the system automatically detects a single linked keyboard driver + * @note The references to GKEYBOARDVMT_Win32 in the definition would be replaced + * by the names of the VMT for each of the static keyboards you want to + * include. + * @note Dynamic keyboards associated automatically with a display eg Win32, X or GFXnet + * do not need to be specified in this list as the display driver will register + * them automatically as the display is created. + */ + #if defined(__DOXYGEN__) + #define GKEYBOARD_DRIVER_LIST GMOUSEVMT_Win32, GMOUSEVMT_Win32 + #endif + /** + * @brief Turn off the layout engine. + * @details When defined the layout engine is removed from the code and characters + * are passed directly from the keyboard driver to the application. + * @note Turning off the layout engine just saves code if it is not needed. + */ + #ifndef GKEYBOARD_LAYOUT_OFF + #define GKEYBOARD_LAYOUT_OFF FALSE + #endif + /** + * @brief Various Keyboard Layouts that can be included. + * @details A keyboard layout controls conversion of scancodes to characters + * and enables one keyboard to have multiple language mappings. + * @note Defining a layout does not make it active. The keyboard driver + * must have it active as the default or the application must + * use @p ginputSetKeyboardLayout() to set the active layout. + * @note Multiple layouts can be included but only one will be active + * at a time (per keyboard). + * @{ + */ + #ifndef GKEYBOARD_LAYOUT_SCANCODE2_US + #define GKEYBOARD_LAYOUT_SCANCODE2_US FALSE // US Keyboard using the ScanCode 2 set. + #endif + /** @} */ +/** @} */ + +#endif /* _GINPUT_OPTIONS_H */ +/** @} */ diff --git a/src/ginput/ginput_rules.h b/src/ginput/ginput_rules.h new file mode 100644 index 00000000..6d997f90 --- /dev/null +++ b/src/ginput/ginput_rules.h @@ -0,0 +1,52 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/ginput/ginput_rules.h + * @brief GINPUT safety rules header file. + * + * @addtogroup GINPUT + * @{ + */ + +#ifndef _GINPUT_RULES_H +#define _GINPUT_RULES_H + +#if GFX_USE_GINPUT + #if !GFX_USE_GEVENT + #if GFX_DISPLAY_RULE_WARNINGS + #warning "GINPUT: GFX_USE_GEVENT is required if GFX_USE_GINPUT is TRUE. It has been turned on for you." + #endif + #undef GFX_USE_GEVENT + #define GFX_USE_GEVENT TRUE + #endif + #if !GFX_USE_GTIMER + #if GFX_DISPLAY_RULE_WARNINGS + #warning "GINPUT: GFX_USE_GTIMER is required if GFX_USE_GINPUT is TRUE. It has been turned on for you." + #endif + #undef GFX_USE_GTIMER + #define GFX_USE_GTIMER TRUE + #endif + #if GINPUT_NEED_MOUSE + #if GINPUT_TOUCH_NOTOUCH + // No warning needed for this + #undef GINPUT_TOUCH_NOCALIBRATE + #define GINPUT_TOUCH_NOCALIBRATE TRUE + #endif + #if GINPUT_TOUCH_NOCALIBRATE + // No warning needed for this + #undef GINPUT_TOUCH_NOCALIBRATE_GUI + #define GINPUT_TOUCH_NOCALIBRATE_GUI TRUE + #endif + #if !GINPUT_TOUCH_NOTOUCH && GINPUT_MOUSE_CLICK_TIME > GINPUT_TOUCH_CXTCLICK_TIME + #error "GINPUT MOUSE: The GINPUT_MOUSE_CLICK_TIME must be <= GINPUT_TOUCH_CXTCLICK_TIME" + #endif + #endif +#endif + +#endif /* _GINPUT_RULES_H */ +/** @} */ diff --git a/src/ginput/ginput_toggle.c b/src/ginput/ginput_toggle.c index 4c6dc9ae..9ef945fa 100644 --- a/src/ginput/ginput_toggle.c +++ b/src/ginput/ginput_toggle.c @@ -17,7 +17,7 @@ #if (GFX_USE_GINPUT && GINPUT_NEED_TOGGLE) || defined(__DOXYGEN__) -#include "driver_toggle.h" +#include "ginput_driver_toggle.h" #define GINPUT_TOGGLE_ISON 0x01 #define GINPUT_TOGGLE_INVERT 0x02 diff --git a/src/ginput/keyboard_microcode.c b/src/ginput/keyboard_microcode.c deleted file mode 100644 index e3c04d5f..00000000 --- a/src/ginput/keyboard_microcode.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/ginput/keyboard_microcode.c - * @brief GINPUT keyboard standard microcode definitions. - */ - -#include "gfx.h" - -#if GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD && !GKEYBOARD_LAYOUT_OFF - -#include "keyboard_microcode.h" - -#if GKEYBOARD_LAYOUT_SCANCODE2_US - - #error "Keyboard Layout SCANCODE2_US is not fully implemented yet" - - uint8_t KeyboardLayout_ScancodeSet2_US[] = { - KMC_HEADERSTART, KMC_HEADER_ID1, KMC_HEADER_ID2, KMC_HEADER_VER_1, - - KMC_RECORDSTART, 0x03, // Handle E0 codes (ignore for now assuming a single character) - KMC_TEST_LASTCODE, 0xE0, - KMC_ACT_DONE, - KMC_RECORDSTART, 0x03, - KMC_TEST_CODE, 0xE0, - KMC_ACT_STOP, - - KMC_RECORDSTART, 0x03, // Handle E1 codes (ignore for now assuming a single character) - KMC_TEST_LASTCODE, 0xE1, - KMC_ACT_DONE, - KMC_RECORDSTART, 0x03, - KMC_TEST_CODE, 0xE1, - KMC_ACT_STOP, - - KMC_RECORDSTART, 0x03, // Handle E2 codes (ignore for now assuming a single character) - KMC_TEST_LASTCODE, 0xE2, - KMC_ACT_DONE, - KMC_RECORDSTART, 0x03, - KMC_TEST_CODE, 0xE2, - KMC_ACT_STOP, - - KMC_RECORDSTART, 0x06, // KeyUp - KMC_TEST_CODEBIT, 0x80, - KMC_ACT_STATEBIT, GKEYSTATE_KEYUP_BIT, - KMC_ACT_CODEBIT, 0x80 | KMC_BIT_CLEAR, - - KMC_RECORDSTART, 0x05, // CapsLock (on keyup to avoid repeats) - KMC_TEST_CODE, 0x58, - KMC_TEST_STATEBIT, GKEYSTATE_KEYUP_BIT | KMC_BIT_CLEAR, - KMC_ACT_DONE, - KMC_RECORDSTART, 0x05, - KMC_TEST_CODE, 0x58, - KMC_ACT_STATEBIT, GKEYSTATE_CAPSLOCK_BIT | KMC_BIT_INVERT, - KMC_ACT_DONE, - - KMC_RECORDSTART, 0x05, // Detect Shift Keys - //KMC_ACT_LAYOUTBIT, SCANCODESET2_LAYOUT_E0_BIT | KMC_BIT_CLEAR, - KMC_ACT_STOP, - - KMC_RECORDSTART, 0x03, - KMC_ACT_CHARRANGE, 0x00, - KMC_ACT_DONE, - - KMC_RECORDSTART, 0x00, - }; -#endif // GKEYBOARD_LAYOUT_SCANCODE2_US - -#endif // GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD && !GKEYBOARD_LAYOUT_OFF diff --git a/src/ginput/keyboard_microcode.h b/src/ginput/keyboard_microcode.h deleted file mode 100644 index c18e94e5..00000000 --- a/src/ginput/keyboard_microcode.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/ginput/keyboard_microcode.h - * @brief GINPUT keyboard layout microcode definition. - */ - -#ifndef _KEYBOARD_MICROCODE_H -#define _KEYBOARD_MICROCODE_H - -/* - * Keyboard Layout Microcode Definition - * - * Each time a byte is received from the keyboard it is processed through the layout microcode engine. This enables conversion from - * scancodes to ascii, internationalization, and various other tricky keyboard behavior. - * Note a "code" is defined as a single byte of data from the keyboard, a "scancode" is one or more "codes" that are sent in response to one keyboard press or release. - * - * The layout microcode can even be switched on the fly by the application to effect such changes as changing from US English to Russian layouts. - * They could conceivably even be loaded from disk at run-time. - * - * In the interest of efficiency there is very little error checking. Make sure your layout microcode has been debugged properly before releasing - * to production code. - * - * Layout microcode consists of a header followed by 1 or more records. - * - * The header consists of a KMC_HEADERSTART and associated bytes. The is used only to check it looks like it might be layout microcode and to specify the - * version of microcode. Future versions of layout microcode will always have the same header at least to the version number. - * - * Each record is delimited by a KMC_RECORDSTART. Each record can contain a maximum of 255 bytes. - * A record length of zero indicates the end of the layout microcode. - * - * A record consists a mixture of tests and actions (normally the tests are first). If a test fails the rest of this record is skipped and the next - * record is processed. This has the effect of AND'ing multiple tests that occur together. - * KMC_TEST_INIT and KMC_TEST_ERROR are special. These tests must be the first byte in their respective record as without this test being there, there - * is an implicit test that a code has actually been received. - * If no records have successful tests for this code then by extension no actions are executed. That is, the code is ignored. - * After fully processing a record, the next record is processed. This can be prevented by using the KMC_ACT_STOP action. When encountered all processing - * on this code stops. - * - * Some tests set a pseudo variable called "diff". This is then used by some actions. At the start of a new record "diff" is set to "code" (or 0 for the init and - * error conditions). - * Some tests and actions do bit operations on either the saved key-state or on the code itself. Bit numbers (which can range from 0 to 31) test or affect the - * "set" state of the bit. OR'ing KMC_BIT_CLEAR with the bit number test or affect the "clear" state of the bit. For example, KMC_ACT_STATEBIT with a parameter - * of 10 will set bit 10 of the key-state. KMC_ACT_STATEBIT with a parameter of (10 | KMC_BIT_CLEAR) will clear bit 10 of the key-state. - * - */ - -#define KMC_HEADERSTART 0x00 // Followed by: ID1 ID2 VER - This is the start of layout microcode. - #define KMC_HEADER_ID1 'L' - #define KMC_HEADER_ID2 'M' - #define KMC_HEADER_VER_1 0x01 - - #define KMC_HEADER_VER_CURRENT KMC_HEADER_VER_1 // The current version number - #define KMC_HEADER_VER_MIN KMC_HEADER_VER_1 // The minimum version number accepted - #define KMC_HEADER_VER_MAX KMC_HEADER_VER_1 // The maximum version number accepted - -#define KMC_RECORDSTART 0x01 // Followed by: nn b0 b1 ... b(nn-1) - nn bytes of test and action follow, nn = 00 means end of all tests - -#define KMC_TEST_INIT 0x10 // Followed by: nothing - The layout is initializing -#define KMC_TEST_ERROR 0x11 // Followed by: nothing - The keyboard has signaled an error -#define KMC_TEST_CODE 0x12 // Followed by: aa - Code must equal aa. Diff is set to 0 -#define KMC_TEST_CODERANGE 0x13 // Followed by: aa bb - Code must be between aa and bb (inclusive). Diff is set to (code - aa) -#define KMC_TEST_CODETABLE 0x14 // Followed by: n m1 m2 ... - Code must equal an m value. There are n possible m values. Diff is set to 0 to n-1 (the match position) -#define KMC_TEST_STATEBIT 0x15 // Followed by: b - Test if a key-state bit is set/clear. b = 0 to 31 or b = (0 | KMC_BIT_CLEAR) to (31 | KMC_BIT_CLEAR) -#define KMC_TEST_STATEOR 0x16 // Followed by: b1 b2 - Test two key-state bits and OR the result -#define KMC_TEST_STATEAND 0x17 // Followed by: b1 b2 - Test two key-state bits and AND the result -#define KMC_TEST_LAYOUTBIT 0x18 // Followed by: b - Test if a layout bit is set/clear. b = 0 to 15 or b = (0 | KMC_BIT_CLEAR) to (15 | KMC_BIT_CLEAR) -#define KMC_TEST_LAYOUTOR 0x19 // Followed by: b1 b2 - Test two layout bits and OR the result -#define KMC_TEST_LAYOUTAND 0x1A // Followed by: b1 b2 - Test two layout bits and AND the result -#define KMC_TEST_CODEBIT 0x1B // Followed by: b - Test if a code bit is set/clear. b = 0 to 7 or b = (0 | KMC_BIT_CLEAR) to (7 | KMC_BIT_CLEAR) -#define KMC_TEST_CODEOR 0x1C // Followed by: b1 b2 - Test two code bits and OR the result -#define KMC_TEST_CODEAND 0x1D // Followed by: b1 b2 - Test two code bits and AND the result -#define KMC_TEST_LASTCODE 0x1E // Followed by: aa - Test if the last scancode was aa -#define KMC_TEST_SHIFT 0x20 // Followed by: nothing - Test if a shift key is down -#define KMC_TEST_NOSHIFT 0x21 // Followed by: nothing - Test if a shift key is not down -#define KMC_TEST_CTRL 0x22 // Followed by: nothing - Test if a control key is down -#define KMC_TEST_NOCTRL 0x23 // Followed by: nothing - Test if a control key is not down -#define KMC_TEST_ALT 0x24 // Followed by: nothing - Test if an alt key is down -#define KMC_TEST_NOALT 0x25 // Followed by: nothing - Test if an alt key is not down -#define KMC_TEST_CAPS 0x26 // Followed by: nothing - Test if capslock as modified by shift is active -#define KMC_TEST_NOCAPS 0x27 // Followed by: nothing - Test if capslock as modified by shift is not active -#define KMC_TEST_NUMLOCK 0x28 // Followed by: nothing - Test if numlock is active -#define KMC_TEST_NONUMLOCK 0x29 // Followed by: nothing - Test if numlock is not active - -#define KMC_ACT_STOP 0xFF // Followed by: nothing - Stop processing this code -#define KMC_ACT_DONE 0xFE // Followed by: nothing - Finished processing this scancode sequence. (also implies STOP) -#define KMC_ACT_RESET 0xFD // Followed by: nothing - Empty all buffers -#define KMC_ACT_STATEBIT 0x80 // Followed by: b - Set or clear bit b in key-state. b = 0 to 31 or b = (0 | KMC_BIT_CLEAR) to (31 | KMC_BIT_CLEAR) -#define KMC_ACT_LAYOUTBIT 0x81 // Followed by: b - Set or clear bit b in layout bits. b = 0 to 15 or b = (0 | KMC_BIT_CLEAR) to (15 | KMC_BIT_CLEAR) -#define KMC_ACT_CODEBIT 0x82 // Followed by: b - Set or clear bit b in code. b = 0 to 7 or b = (0 | KMC_BIT_CLEAR) to (7 | KMC_BIT_CLEAR) -#define KMC_ACT_CHAR 0x83 // Followed by: nn - Append char nn to output buffer -#define KMC_ACT_CHARCODE 0x84 // Followed by: nothing - Append the code to output buffer -#define KMC_ACT_CHARRANGE 0x85 // Followed by: nn - Append char nn + Diff to output -#define KMC_ACT_CHARTABLE 0x86 // Followed by: n c1 c2 ... - Append char to output based on c[Diff]. If Diff is greater than n then nothing is appended. -#define KMC_ACT_CLEAR 0x87 // Followed by: nothing - Clear the output buffer -#define KMC_ACT_CHARADD 0x88 // Followed by: nn - Multiple the last char in output buffer by nn (assume 0 if none) and add Diff -#define KMC_ACT_DATA 0x89 // Followed by: nn - Send nn back to the keyboard - -#define KMC_BIT_CLEAR 0x80 // The bit number is being used for a bit clear operation rather than a bit set operation. -#define KMC_BIT_INVERT 0x40 // Invert the bit rather than setting or clearing it. - -#endif /* _KEYBOARD_MICROCODE_H */ diff --git a/src/ginput/sys_defs.h b/src/ginput/sys_defs.h deleted file mode 100644 index cab1e15d..00000000 --- a/src/ginput/sys_defs.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/ginput/sys_defs.h - * - * @addtogroup GINPUT - * - * @brief Module to interface different hardware input sources such as touchscreens - * - * @details GINPUT provides an easy and common interface to use different input devices - * such as touchscreens and mices. - * - * @pre GFX_USE_GINPUT must be set to TRUE in your gfxconf.h - * - * @{ - */ -#ifndef _GINPUT_H -#define _GINPUT_H - -#include "gfx.h" - -#if GFX_USE_GINPUT || defined(__DOXYGEN__) - -/* How to use... - - 1. Get source handles for all the inputs you are interested in. - - Attempting to get a handle for one instance of an input more than once will return the same handle - 2. Create a listener - 3. Assign inputs to your listener. - - Inputs can be assigned or released from a listener at any time. - - An input can be assigned to more than one listener. - 4. Loop on getting listener events - 5. When complete destroy the listener -*/ - -// Include various ginput types -#include "ginput_mouse.h" -#include "ginput_keyboard.h" -#include "ginput_toggle.h" -#include "ginput_dial.h" - -#endif /* GFX_USE_GINPUT */ - -#endif /* _GINPUT_H */ -/** @} */ diff --git a/src/ginput/sys_make.mk b/src/ginput/sys_make.mk deleted file mode 100644 index 6adb2e4c..00000000 --- a/src/ginput/sys_make.mk +++ /dev/null @@ -1,6 +0,0 @@ -GFXSRC += $(GFXLIB)/src/ginput/ginput_ginput.c \ - $(GFXLIB)/src/ginput/ginput_mouse.c \ - $(GFXLIB)/src/ginput/ginput_keyboard.c \ - $(GFXLIB)/src/ginput/keyboard_microcode.c \ - $(GFXLIB)/src/ginput/ginput_toggle.c \ - $(GFXLIB)/src/ginput/ginput_dial.c diff --git a/src/ginput/sys_options.h b/src/ginput/sys_options.h deleted file mode 100644 index 9c1b0d30..00000000 --- a/src/ginput/sys_options.h +++ /dev/null @@ -1,221 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/ginput/sys_options.h - * @brief GINPUT sub-system options header file. - * - * @addtogroup GINPUT - * @{ - */ - -#ifndef _GINPUT_OPTIONS_H -#define _GINPUT_OPTIONS_H - -/** - * @name GINPUT Functionality to be included - * @{ - */ - /** - * @brief Should mouse/touch functions be included. - * @details Defaults to FALSE - * @note Also add a mouse/touch hardware driver to your makefile. - * Eg. - * include $(GFXLIB)/drivers/ginput/touch/MCU/driver.mk - */ - #ifndef GINPUT_NEED_MOUSE - #define GINPUT_NEED_MOUSE FALSE - #endif - /** - * @brief Should keyboard functions be included. - * @details Defaults to FALSE - * @note Also add a keyboard hardware driver to your makefile. - * Eg. - * include $(GFXLIB)/drivers/ginput/keyboard/XXXX/driver.mk - */ - #ifndef GINPUT_NEED_KEYBOARD - #define GINPUT_NEED_KEYBOARD FALSE - #endif - /** - * @brief Should hardware toggle/switch/button functions be included. - * @details Defaults to FALSE - * @note Also add a toggle hardware driver to your makefile. - * Eg. - * include $(GFXLIB)/drivers/ginput/toggle/Pal/driver.mk - */ - #ifndef GINPUT_NEED_TOGGLE - #define GINPUT_NEED_TOGGLE FALSE - #endif - /** - * @brief Should analog dial functions be included. - * @details Defaults to FALSE - * @note Also add a dial hardware driver to your makefile. - * Eg. - * include $(GFXLIB)/drivers/ginput/dial/analog/driver.mk - */ - #ifndef GINPUT_NEED_DIAL - #define GINPUT_NEED_DIAL FALSE - #endif -/** - * @} - * - * @name GINPUT Optional Sizing Parameters - * @{ - */ -/** - * @} - * - * @name GINPUT Optional Low Level Driver Defines - * @{ - */ - /** - * @brief Start touch devices without loading or running calibration. - * @details Defaults to FALSE - * @note This is used if you want to manually control the initial calibration - * process. In practice this is only useful for a touch driver test program. - */ - #ifndef GINPUT_TOUCH_STARTRAW - #define GINPUT_TOUCH_STARTRAW FALSE - #endif - /** - * @brief Turn off the touch calibration GUI. - * @details Defaults to FALSE - * @note Turning off the calibration GUI just turns off the manual calibration - * process. Readings may still be calibrated if calibration data - * can be loaded. - * @note Calibration requires a lot of code. If your device doesn't require it - * using this option can save a lot of space. - */ - #ifndef GINPUT_TOUCH_NOCALIBRATE_GUI - #define GINPUT_TOUCH_NOCALIBRATE_GUI FALSE - #endif - /** - * @brief Turn off all touch calibration support. - * @details Defaults to FALSE - * @note With this set to TRUE touch readings will not be calibrated. - * @note This automatically turns off the calibration GUI too! - * @note Calibration requires a lot of code. If your device doesn't require it - * using this option can save a lot of space. - */ - #ifndef GINPUT_TOUCH_NOCALIBRATE - #define GINPUT_TOUCH_NOCALIBRATE FALSE - #endif - /** - * @brief Turn off all touch support. - * @details Defaults to FALSE - * @note This automatically turns off all calibration and the calibration GUI too! - * @note Touch device handling requires a lot of code. If your device doesn't require it - * using this option can save a lot of space. - */ - #ifndef GINPUT_TOUCH_NOTOUCH - #define GINPUT_TOUCH_NOTOUCH FALSE - #endif - /** - * @brief Milliseconds between mouse polls. - * @details Defaults to 25 milliseconds - * @note How often mice should be polled. More often leads to smoother mouse movement - * but increases CPU usage. - */ - #ifndef GINPUT_MOUSE_POLL_PERIOD - #define GINPUT_MOUSE_POLL_PERIOD 25 - #endif - - /** - * @brief Maximum length of CLICK in milliseconds - * @details Defaults to 300 milliseconds - * @note Mouse down to Mouse up times greater than this are not clicks. - */ - #ifndef GINPUT_MOUSE_CLICK_TIME - #define GINPUT_MOUSE_CLICK_TIME 300 - #endif - /** - * @brief Milliseconds to generate a CXTCLICK on a touch device. - * @details Defaults to 500 milliseconds - * @note If you hold the touch down for longer than this a CXTCLICK is generated - * but only on a touch device. - */ - #ifndef GINPUT_TOUCH_CXTCLICK_TIME - #define GINPUT_TOUCH_CXTCLICK_TIME 500 - #endif - /** - * @brief There is a user supplied routine to load mouse calibration data - * @details Defaults to FALSE - * @note If TRUE the user must supply the @p LoadMouseCalibration() routine. - */ - #ifndef GINPUT_TOUCH_USER_CALIBRATION_LOAD - #define GINPUT_TOUCH_USER_CALIBRATION_LOAD FALSE - #endif - /** - * @brief There is a user supplied routine to save mouse calibration data - * @details Defaults to FALSE - * @note If TRUE the user must supply the @p SaveMouseCalibration() routine. - */ - #ifndef GINPUT_TOUCH_USER_CALIBRATION_SAVE - #define GINPUT_TOUCH_USER_CALIBRATION_SAVE FALSE - #endif - /** - * @brief Define multiple static mice - * @details When not defined the system automatically detects a single linked mouse driver - * @note The references to GMOUSEVMT_Win32 in the definition would be replaced - * by the names of the VMT for each of the static mice you want to - * include. - * @note Dynamic mice associated automatically with a display eg Win32, X or GFXnet - * do not need to be specified in this list as the associated display driver will register - * them automatically as the display is created. - */ - #if defined(__DOXYGEN__) - #define GMOUSE_DRIVER_LIST GMOUSEVMT_Win32, GMOUSEVMT_Win32 - #endif - /** - * @brief Milliseconds between keyboard polls. - * @details Defaults to 200 milliseconds - * @note How often keyboards should be polled. - */ - #ifndef GINPUT_KEYBOARD_POLL_PERIOD - #define GINPUT_KEYBOARD_POLL_PERIOD 200 - #endif - /** - * @brief Define multiple static keyboards - * @details When not defined the system automatically detects a single linked keyboard driver - * @note The references to GKEYBOARDVMT_Win32 in the definition would be replaced - * by the names of the VMT for each of the static keyboards you want to - * include. - * @note Dynamic keyboards associated automatically with a display eg Win32, X or GFXnet - * do not need to be specified in this list as the display driver will register - * them automatically as the display is created. - */ - #if defined(__DOXYGEN__) - #define GKEYBOARD_DRIVER_LIST GMOUSEVMT_Win32, GMOUSEVMT_Win32 - #endif - /** - * @brief Turn off the layout engine. - * @details When defined the layout engine is removed from the code and characters - * are passed directly from the keyboard driver to the application. - * @note Turning off the layout engine just saves code if it is not needed. - */ - #ifndef GKEYBOARD_LAYOUT_OFF - #define GKEYBOARD_LAYOUT_OFF FALSE - #endif - /** - * @brief Various Keyboard Layouts that can be included. - * @details A keyboard layout controls conversion of scancodes to characters - * and enables one keyboard to have multiple language mappings. - * @note Defining a layout does not make it active. The keyboard driver - * must have it active as the default or the application must - * use @p ginputSetKeyboardLayout() to set the active layout. - * @note Multiple layouts can be included but only one will be active - * at a time (per keyboard). - * @{ - */ - #ifndef GKEYBOARD_LAYOUT_SCANCODE2_US - #define GKEYBOARD_LAYOUT_SCANCODE2_US FALSE // US Keyboard using the ScanCode 2 set. - #endif - /** @} */ -/** @} */ - -#endif /* _GINPUT_OPTIONS_H */ -/** @} */ diff --git a/src/ginput/sys_rules.h b/src/ginput/sys_rules.h deleted file mode 100644 index d9a367ce..00000000 --- a/src/ginput/sys_rules.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/ginput/sys_rules.h - * @brief GINPUT safety rules header file. - * - * @addtogroup GINPUT - * @{ - */ - -#ifndef _GINPUT_RULES_H -#define _GINPUT_RULES_H - -#if GFX_USE_GINPUT - #if !GFX_USE_GEVENT - #if GFX_DISPLAY_RULE_WARNINGS - #warning "GINPUT: GFX_USE_GEVENT is required if GFX_USE_GINPUT is TRUE. It has been turned on for you." - #endif - #undef GFX_USE_GEVENT - #define GFX_USE_GEVENT TRUE - #endif - #if !GFX_USE_GTIMER - #if GFX_DISPLAY_RULE_WARNINGS - #warning "GINPUT: GFX_USE_GTIMER is required if GFX_USE_GINPUT is TRUE. It has been turned on for you." - #endif - #undef GFX_USE_GTIMER - #define GFX_USE_GTIMER TRUE - #endif - #if GINPUT_NEED_MOUSE - #if GINPUT_TOUCH_NOTOUCH - // No warning needed for this - #undef GINPUT_TOUCH_NOCALIBRATE - #define GINPUT_TOUCH_NOCALIBRATE TRUE - #endif - #if GINPUT_TOUCH_NOCALIBRATE - // No warning needed for this - #undef GINPUT_TOUCH_NOCALIBRATE_GUI - #define GINPUT_TOUCH_NOCALIBRATE_GUI TRUE - #endif - #if !GINPUT_TOUCH_NOTOUCH && GINPUT_MOUSE_CLICK_TIME > GINPUT_TOUCH_CXTCLICK_TIME - #error "GINPUT MOUSE: The GINPUT_MOUSE_CLICK_TIME must be <= GINPUT_TOUCH_CXTCLICK_TIME" - #endif - #endif -#endif - -#endif /* _GINPUT_RULES_H */ -/** @} */ diff --git a/src/gmisc/gmisc.c b/src/gmisc/gmisc.c new file mode 100644 index 00000000..9bcc7bbe --- /dev/null +++ b/src/gmisc/gmisc.c @@ -0,0 +1,22 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +#include "gfx.h" + +#if GFX_USE_GMISC + +void _gmiscInit(void) +{ + +} + +void _gmiscDeinit(void) +{ + +} + +#endif /* GFX_USE_GMISC */ diff --git a/src/gmisc/gmisc.h b/src/gmisc/gmisc.h new file mode 100644 index 00000000..8f858682 --- /dev/null +++ b/src/gmisc/gmisc.h @@ -0,0 +1,471 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gmisc/gmisc.h + * @brief GMISC - Miscellaneous Routines header file. + * + * @addtogroup GMISC + * + * @brief Module which contains different features such as array conversions + * + * @{ + */ + +#ifndef _GMISC_H +#define _GMISC_H + +#include "gfx.h" + +/*===========================================================================*/ +/* Type definitions */ +/*===========================================================================*/ + +// Forward definition +typedef struct point point; + +/** + * @brief Sample data formats + * @note These are defined regardless of whether you use the GMISC module + * or not as they are used in lots of places. + */ +typedef enum ArrayDataFormat_e { + ARRAY_DATA_UNKNOWN = 0, + ARRAY_DATA_4BITUNSIGNED = 4, ARRAY_DATA_4BITSIGNED = 5, + ARRAY_DATA_8BITUNSIGNED = 8, ARRAY_DATA_8BITSIGNED = 9, + ARRAY_DATA_10BITUNSIGNED = 10, ARRAY_DATA_10BITSIGNED = 11, + ARRAY_DATA_12BITUNSIGNED = 12, ARRAY_DATA_12BITSIGNED = 13, + ARRAY_DATA_14BITUNSIGNED = 14, ARRAY_DATA_14BITSIGNED = 15, + ARRAY_DATA_16BITUNSIGNED = 16, ARRAY_DATA_16BITSIGNED = 17, + } ArrayDataFormat; + +/** + * @brief Is the sample data format a "signed" data format? + */ +#define gfxSampleFormatIsSigned(fmt) ((fmt) & 1) + +/** + * @brief How many bits are in the sample data format + */ +#define gfxSampleFormatBits(fmt) ((fmt) & ~1) + +/** + * @brief The type for a fixed point type. + * @details The top 16 bits are the integer component, the bottom 16 bits are the real component. + */ +typedef int32_t fixed; + +/** + * @brief Macros to convert to and from a fixed point. + * @{ + */ +#define FIXED(x) ((fixed)(x)<<16) /* @< integer to fixed */ +#define NONFIXED(x) ((x)>>16) /* @< fixed to integer */ +#define FIXED0_5 32768 /* @< 0.5 as a fixed (used for rounding) */ +#define FP2FIXED(x) ((fixed)((x)*65536.0)) /* @< floating point to fixed */ +#define FIXED2FP(x) ((double)(x)/65536.0) /* @< fixed to floating point */ +#define FIXEDMUL(a,b) ((fixed)((((long long)(a))*(b))>>16)) /* @< fixed,fixed multiplication */ +#define FIXEDMULINT(a,b) ((a)*(b)) /* @< integer,fixed multiplication */ +/** @} */ + +/** + * @brief The famous number pi + */ +#define PI 3.1415926535897932384626433832795028841971693993751 + +/** + * @brief pi as a fixed point + */ +#define FIXED_PI FP2FIXED(PI) + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if GFX_USE_GMISC || defined(__DOXYGEN__) + +#ifdef __cplusplus +extern "C" { +#endif + +#if GMISC_NEED_ARRAYOPS || defined(__DOXYGEN__) + /** + * @brief Convert from one array format to another array format. + * @pre Requires GFX_USE_GMISC and GMISC_NEED_ARRAYOPS + * + * @param[in] srcfmt The format of the source array + * @param[in] src The source array + * @param[in] dstfmt The format of the destination array + * @param[in] dst The dstination array + * @param[in] cnt The number of array elements to convert + * + * @note Assumes the destination buffer is large enough for the resultant data. + * @note This routine is optimised to perform as fast as possible. + * @note No type checking is performed on the source format. It is assumed to + * have only valid values eg. ARRAY_DATA_4BITSIGNED will have values + * 0000 -> 0111 for positive numbers and 1111 -> 1000 for negative numbers + * Bits 5 -> 8 in the storage byte are treated in an undefined manner. + * @note If srcfmt or dstfmt is an unknown format, this routine does nothing + * with no warning that something is wrong + * + * @api + */ + void gmiscArrayConvert(ArrayDataFormat srcfmt, void *src, ArrayDataFormat dstfmt, void *dst, size_t cnt); + + #if 0 + void gmiscArrayTranslate(ArrayDataFormat fmt, void *src, void *dst, size_t cnt, int trans); + + void gmiscArrayMultiply(ArrayDataFormat fmt, void *src, void *dst, size_t cnt, int mult); + + void gmiscArrayDivide(ArrayDataFormat fmt, void *src, void *dst, size_t cnt, int mdiv); + + void gmiscArrayMultDiv(ArrayDataFormat fmt, void *src, void *dst, size_t cnt, int mult, int div); + + void gmiscArrayAdd(ArrayDataFormat fmt, void *src1, void *src2, void *dst, size_t cnt); + + void gmiscArrayAddNoOverflow(ArrayDataFormat fmt, void *src1, void *src2, void *dst, size_t cnt); + #endif +#endif + +#if GMISC_NEED_FASTTRIG || defined(__DOXYGEN__) + extern const double sintabledouble[]; + + /** + * @brief Fast Table Based Trig functions + * @return A double in the range -1.0 .. 0.0 .. 1.0 + * @pre Requires GFX_USE_GMISC and GMISC_NEED_FASTTRIG + * + * @param[in] degrees The angle in degrees (not radians) + * + * @note These functions use degrees rather than radians to describe the angle. + * + * @api + * @{ + */ + double fsin(int degrees); + double fcos(int degrees); + /** @} + * + * @brief Fast Table Based Trig functions + * @return A double in the range -1.0 .. 0.0 .. 1.0 + * @pre Requires GFX_USE_GMISC and GMISC_NEED_FASTTRIG + * + * @param[in] degrees The angle in degrees 0 .. 359 + * + * @note These functions use degrees rather than radians to describe the angle. + * @note These functions are super fast but require the parameter to be in range. + * Use the lowercase functions if the parameter may not be in range or if a + * required trig function is not supported in this form. + * + * @api + * @{ + */ + #define FSIN(degrees) sintabledouble[degrees]; + /** @} */ +#endif + +#if GMISC_NEED_FIXEDTRIG || defined(__DOXYGEN__) + extern const fixed sintablefixed[]; + + /** + * @brief Fast Table Based Trig functions + * @return A fixed point in the range -1.0 .. 0.0 .. 1.0 + * @pre Requires GFX_USE_GMISC and GMISC_NEED_FIXEDTRIG + * + * @param[in] degrees The angle in degrees (not radians) + * + * @note These functions use degrees rather than radians to describe the angle. + * + * @api + * @{ + */ + fixed ffsin(int degrees); + fixed ffcos(int degrees); + /** @} + * + * @brief Fast Table Based Trig functions + * @return A fixed point in the range -1.0 .. 0.0 .. 1.0 + * @pre Requires GFX_USE_GMISC and GMISC_NEED_FIXEDTRIG + * + * @param[in] degrees The angle in degrees 0 .. 359 + * + * @note These functions use degrees rather than radians to describe the angle. + * @note These functions are super fast but require the parameter to be in range. + * Use the lowercase functions if the parameter may not be in range or if a + * required trig function is not supported in this form. + * + * @api + * @{ + */ + #define FFSIN(degrees) sintablefixed[degrees]; + /** @} */ +#endif + +#if GMISC_NEED_INVSQRT || defined(__DOXYGEN__) + /** + * @brief Fast inverse square root function (x^-1/2) + * @return The approximate inverse square root + * @pre Requires GFX_USE_GMISC and GMISC_NEED_INVSQRT + * + * @param[in] n The number to find the inverse square root of + * + * @note This function generates an approximate result. Higher accuracy (at the expense + * of speed) can be obtained by modifying the source code (the necessary line + * is already there - just commented out). + * @note This function relies on the internal machine format of a float and a long. + * If your machine architecture is very unusual this function may not work. + * + * @api + */ + float invsqrt(float n); +#endif + +#if GMISC_NEED_MATRIXFLOAT2D || defined(__DOXYGEN__) + + /** + * @brief A matrix for doing 2D graphics using floats + * @pre Requires GFX_USE_GMISC and GMISC_NEED_MATRIXFLOAT2D + */ + typedef struct MatrixFloat2D { + float a00, a01, a02; + float a10, a11, a12; + float a20, a21, a22; + } MatrixFloat2D; + + /** + * @brief Apply the matrix to a set of points + * @pre Requires GFX_USE_GMISC and GMISC_NEED_MATRIXFLOAT2D + * + * @param[in] dst The destination array of points + * @param[in] src The source array of points + * @param[in] m The matrix to apply + * @param[in] cnt How many points are in the array + * + * @note In-place matrix application is allowed ie. dst = src + * + * @api + */ + void gmiscMatrixFloat2DApplyToPoints(point *dst, const point *src, const MatrixFloat2D *m, int cnt); + + /** + * @brief Set the 2D matrix to the identity matrix + * @pre Requires GFX_USE_GMISC and GMISC_NEED_MATRIXFLOAT2D + * + * @param[in] m The matrix to set to identity + * + * @api + */ + void gmiscMatrixFloat2DSetIdentity(MatrixFloat2D *m); + + /** + * @brief Multiple two 2D matrixes together + * @pre Requires GFX_USE_GMISC and GMISC_NEED_MATRIXFLOAT2D + * + * @param[in] dst The destination matrix + * @param[in] src1 The first source matrix + * @param[in] src2 The second source matrix + * + * @note In-place matrix application is NOT allowed ie. dst != src1, dst != src2 + * + * @api + */ + void gmiscMatrixFloat2DMultiply(MatrixFloat2D *dst, const MatrixFloat2D *src1, const MatrixFloat2D *src2); + + /** + * @brief Add an x,y translation to a matrix + * @pre Requires GFX_USE_GMISC and GMISC_NEED_MATRIXFLOAT2D + * + * @param[in] dst The destination matrix + * @param[in] src The source matrix. Can be NULL + * @param[in] tx, ty The x and y translation to apply + * + * @note In-place matrix operation is NOT allowed ie. dst != src + * @note If no source matrix is provided, it is equivalent to applying the operation + * to an identity matrix. It also is a much simpler operation requiring no multiplication. + * + * @api + */ + void gmiscMatrixFloat2DApplyTranslation(MatrixFloat2D *dst, const MatrixFloat2D *src, float tx, float ty); + + /** + * @brief Add x,y scaling to a matrix + * @pre Requires GFX_USE_GMISC and GMISC_NEED_MATRIXFLOAT2D + * + * @param[in] dst The destination matrix + * @param[in] src The source matrix. Can be NULL + * @param[in] sx, sy The scaling to apply in the x and y direction. Negative numbers give reflection. + * + * @note In-place matrix operation is NOT allowed ie. dst != src + * @note If no source matrix is provided, it is equivalent to applying the operation + * to an identity matrix. It also is a much simpler operation requiring no multiplication. + * + * @api + */ + void gmiscMatrixFloat2DApplyScale(MatrixFloat2D *dst, const MatrixFloat2D *src, float sx, float sy); + + /** + * @brief Add x,y shear to a matrix + * @pre Requires GFX_USE_GMISC and GMISC_NEED_MATRIXFLOAT2D + * + * @param[in] dst The destination matrix + * @param[in] src The source matrix. Can be NULL + * @param[in] sx, sy The shear to apply in the x and y direction. + * + * @note In-place matrix operation is NOT allowed ie. dst != src + * @note If no source matrix is provided, it is equivalent to applying the operation + * to an identity matrix. It also is a much simpler operation requiring no multiplication. + * + * @api + */ + void gmiscMatrixFloat2DApplyShear(MatrixFloat2D *dst, const MatrixFloat2D *src, float sx, float sy); + + /** + * @brief Add rotation to a matrix + * @pre Requires GFX_USE_GMISC and GMISC_NEED_MATRIXFLOAT2D + * + * @param[in] dst The destination matrix + * @param[in] src The source matrix. Can be NULL + * @param[in] angle The angle to apply in degrees (not radians). + * + * @note In-place matrix operation is NOT allowed ie. dst != src + * @note If no source matrix is provided, it is equivalent to applying the operation + * to an identity matrix. It also is a much simpler operation. + * @note If GMISC_NEED_FASTTRIG is defined then the fast table sin and cos lookup's will be used + * rather than the C library versions. + * + * @api + */ + void gmiscMatrixFloat2DApplyRotation(MatrixFloat2D *dst, const MatrixFloat2D *src, int angle); +#endif + +#if GMISC_NEED_MATRIXFIXED2D || defined(__DOXYGEN__) + + /** + * @brief A matrix for doing 2D graphics using fixed point maths + * @pre Requires GFX_USE_GMISC and GMISC_NEED_MATRIXFIXED2D + */ + typedef struct MatrixFixed2D { + fixed a00, a01, a02; + fixed a10, a11, a12; + fixed a20, a21, a22; + } MatrixFixed2D; + + /** + * @brief Apply the matrix to a set of points + * @pre Requires GFX_USE_GMISC and GMISC_NEED_MATRIXFIXED2D + * + * @param[in] dst The destination array of points + * @param[in] src The source array of points + * @param[in] m The matrix to apply + * @param[in] cnt How many points are in the array + * + * @note In-place matrix application is allowed ie. dst = src + * + * @api + */ + void gmiscMatrixFixed2DApplyToPoints(point *dst, const point *src, const MatrixFixed2D *m, int cnt); + + /** + * @brief Set the 2D matrix to the identity matrix + * @pre Requires GFX_USE_GMISC and GMISC_NEED_MATRIXFIXED2D + * + * @param[in] m The matrix to set to identity + * + * @api + */ + void gmiscMatrixFixed2DSetIdentity(MatrixFixed2D *m); + + /** + * @brief Multiple two 2D matrixes together + * @pre Requires GFX_USE_GMISC and GMISC_NEED_MATRIXFIXED2D + * + * @param[in] dst The destination matrix + * @param[in] src1 The first source matrix + * @param[in] src2 The second source matrix + * + * @note In-place matrix application is NOT allowed ie. dst != src1, dst != src2 + * + * @api + */ + void gmiscMatrixFixed2DMultiply(MatrixFixed2D *dst, const MatrixFixed2D *src1, const MatrixFixed2D *src2); + + /** + * @brief Add an x,y translation to a matrix + * @pre Requires GFX_USE_GMISC and GMISC_NEED_MATRIXFIXED2D + * + * @param[in] dst The destination matrix + * @param[in] src The source matrix. Can be NULL + * @param[in] tx, ty The x and y translation to apply + * + * @note In-place matrix operation is NOT allowed ie. dst != src + * @note If no source matrix is provided, it is equivalent to applying the operation + * to an identity matrix. It also is a much simpler operation requiring no multiplication. + * + * @api + */ + void gmiscMatrixFixed2DApplyTranslation(MatrixFixed2D *dst, const MatrixFixed2D *src, fixed tx, fixed ty); + + /** + * @brief Add x,y scaling to a matrix + * @pre Requires GFX_USE_GMISC and GMISC_NEED_MATRIXFIXED2D + * + * @param[in] dst The destination matrix + * @param[in] src The source matrix. Can be NULL + * @param[in] sx, sy The scaling to apply in the x and y direction. Negative numbers give reflection. + * + * @note In-place matrix operation is NOT allowed ie. dst != src + * @note If no source matrix is provided, it is equivalent to applying the operation + * to an identity matrix. It also is a much simpler operation requiring no multiplication. + * + * @api + */ + void gmiscMatrixFixed2DApplyScale(MatrixFixed2D *dst, const MatrixFixed2D *src, fixed sx, fixed sy); + + /** + * @brief Add x,y shear to a matrix + * @pre Requires GFX_USE_GMISC and GMISC_NEED_MATRIXFIXED2D + * + * @param[in] dst The destination matrix + * @param[in] src The source matrix. Can be NULL + * @param[in] sx, sy The shear to apply in the x and y direction. + * + * @note In-place matrix operation is NOT allowed ie. dst != src + * @note If no source matrix is provided, it is equivalent to applying the operation + * to an identity matrix. It also is a much simpler operation requiring no multiplication. + * + * @api + */ + void gmiscMatrixFixed2DApplyShear(MatrixFixed2D *dst, const MatrixFixed2D *src, fixed sx, fixed sy); + + #if GMISC_NEED_FIXEDTRIG || defined(__DOXYGEN__) + /** + * @brief Add rotation to a matrix + * @pre Requires GFX_USE_GMISC and GMISC_NEED_MATRIXFIXED2D and GMISC_NEED_FIXEDTRIG + * + * @param[in] dst The destination matrix + * @param[in] src The source matrix. Can be NULL + * @param[in] angle The angle to apply in degrees (not radians). + * + * @note In-place matrix operation is NOT allowed ie. dst != src + * @note If no source matrix is provided, it is equivalent to applying the operation + * to an identity matrix. It also is a much simpler operation requiring no multiplication. + * + * @api + */ + void gmiscMatrixFixed2DApplyRotation(MatrixFixed2D *dst, const MatrixFixed2D *src, int angle); + #endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* GFX_USE_MISC */ + +#endif /* _GMISC_H */ +/** @} */ + diff --git a/src/gmisc/gmisc.mk b/src/gmisc/gmisc.mk new file mode 100644 index 00000000..fa2e382f --- /dev/null +++ b/src/gmisc/gmisc.mk @@ -0,0 +1,4 @@ +GFXSRC += $(GFXLIB)/src/gmisc/gmisc.c \ + $(GFXLIB)/src/gmisc/gmisc_arrayops.c \ + $(GFXLIB)/src/gmisc/gmisc_matrix2d.c \ + $(GFXLIB)/src/gmisc/gmisc_trig.c diff --git a/src/gmisc/gmisc_arrayops.c b/src/gmisc/gmisc_arrayops.c index 6e5442cd..350a787c 100644 --- a/src/gmisc/gmisc_arrayops.c +++ b/src/gmisc/gmisc_arrayops.c @@ -5,13 +5,6 @@ * http://ugfx.org/license.html */ -/** - * @file src/gmisc/gmisc_arrayops.c - * @brief GMISC Array Operations code. - * - * @addtogroup GMISC - * @{ - */ #include "gfx.h" #if GFX_USE_GMISC && GMISC_NEED_ARRAYOPS @@ -223,4 +216,3 @@ void gmiscArrayConvert(ArrayDataFormat srcfmt, void *src, ArrayDataFormat dstfmt } #endif /* GFX_USE_GMISC && GMISC_NEED_ARRAYOPS */ -/** @} */ diff --git a/src/gmisc/gmisc_gmisc.c b/src/gmisc/gmisc_gmisc.c deleted file mode 100644 index 3121182f..00000000 --- a/src/gmisc/gmisc_gmisc.c +++ /dev/null @@ -1,27 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gmisc/gmisc_gmisc.c - * @brief GMISC Functions. - * - */ -#include "gfx.h" - -#if GFX_USE_GMISC - -void _gmiscInit(void) -{ - -} - -void _gmiscDeinit(void) -{ - -} - -#endif /* GFX_USE_GMISC */ diff --git a/src/gmisc/gmisc_matrix2d.c b/src/gmisc/gmisc_matrix2d.c index dd13603e..84b90c23 100644 --- a/src/gmisc/gmisc_matrix2d.c +++ b/src/gmisc/gmisc_matrix2d.c @@ -5,10 +5,6 @@ * http://ugfx.org/license.html */ -/** - * @file src/gmisc/gmisc_matrix2d.c - * @brief GMISC 2D matrix operations code. - */ #include "gfx.h" #if GFX_USE_GMISC diff --git a/src/gmisc/gmisc_options.h b/src/gmisc/gmisc_options.h new file mode 100644 index 00000000..7f175fd7 --- /dev/null +++ b/src/gmisc/gmisc_options.h @@ -0,0 +1,85 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gmisc/gmisc_options.h + * @brief GMISC - Miscellaneous Routines options header file. + * + * @addtogroup GMISC + * @{ + */ + +#ifndef _GMISC_OPTIONS_H +#define _GMISC_OPTIONS_H + +/** + * @name GMISC Functionality to be included + * @{ + */ + /** + * @brief Include array operation functions + * @details Defaults to FALSE + */ + #ifndef GMISC_NEED_ARRAYOPS + #define GMISC_NEED_ARRAYOPS FALSE + #endif + /** + * @brief Include fast floating point trig functions (fsin, fcos) + * @details Defaults to FALSE + */ + #ifndef GMISC_NEED_FASTTRIG + #define GMISC_NEED_FASTTRIG FALSE + #endif + /** + * @brief Include fast fixed point trig functions (ffsin, ffcos) + * @details Defaults to FALSE + */ + #ifndef GMISC_NEED_FIXEDTRIG + #define GMISC_NEED_FIXEDTRIG FALSE + #endif + /** + * @brief Include fast inverse square root (x^-1/2) + * @details Defaults to FALSE + */ + #ifndef GMISC_NEED_INVSQRT + #define GMISC_NEED_INVSQRT FALSE + #endif +/** + * @} + * + * @name GMISC Optional Parameters + * @{ + */ + /** + * @brief Modifies the @p invsqrt() function to assume a different integer to floating point endianness. + * @note Normally the floating point format and the integer format have + * the same endianness. Unfortunately there are some strange + * processors that don't eg. some very early ARM devices. + * For those where the endianness doesn't match you can fix it by + * defining GMISC_INVSQRT_MIXED_ENDIAN. + * @note This still assumes the processor is using an ieee floating point format. + * + * If you have a software floating point that uses a non-standard + * floating point format (or very strange hardware) then define + * GMISC_INVSQRT_REAL_SLOW and it will do it the hard way. + */ + #ifndef GMISC_INVSQRT_MIXED_ENDIAN + #define GMISC_INVSQRT_MIXED_ENDIAN FALSE + #endif + /** + * @brief Modifies the @p invsqrt() function to do things the long slow way. + * @note This causes the @p invsqrt() function to work regardless of the + * processor floating point format. + * @note This makes the @p invsqrt() function very slow. + */ + #ifndef GMISC_INVSQRT_REAL_SLOW + #define GMISC_INVSQRT_REAL_SLOW FALSE + #endif +/** @} */ + +#endif /* _GMISC_OPTIONS_H */ +/** @} */ diff --git a/src/gmisc/gmisc_rules.h b/src/gmisc/gmisc_rules.h new file mode 100644 index 00000000..44a19846 --- /dev/null +++ b/src/gmisc/gmisc_rules.h @@ -0,0 +1,23 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gmisc/gmisc_rules.h + * @brief GMISC safety rules header file. + * + * @addtogroup GMISC + * @{ + */ + +#ifndef _GMISC_RULES_H +#define _GMISC_RULES_H + +#if GFX_USE_GMISC +#endif + +#endif /* _GMISC_RULES_H */ +/** @} */ diff --git a/src/gmisc/gmisc_trig.c b/src/gmisc/gmisc_trig.c index 7314f389..12d06be2 100644 --- a/src/gmisc/gmisc_trig.c +++ b/src/gmisc/gmisc_trig.c @@ -5,13 +5,6 @@ * http://ugfx.org/license.html */ -/** - * @file src/gmisc/gmisc_trig.c - * @brief GMISC Trig Functions. - * - * @addtogroup GMISC - * @{ - */ #include "gfx.h" #if GFX_USE_GMISC @@ -187,4 +180,3 @@ #endif #endif /* GFX_USE_GMISC */ -/** @} */ diff --git a/src/gmisc/sys_defs.h b/src/gmisc/sys_defs.h deleted file mode 100644 index 813fa07e..00000000 --- a/src/gmisc/sys_defs.h +++ /dev/null @@ -1,471 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gmisc/sys_defs.h - * @brief GMISC - Miscellaneous Routines header file. - * - * @addtogroup GMISC - * - * @brief Module which contains different features such as array conversions - * - * @{ - */ - -#ifndef _GMISC_H -#define _GMISC_H - -#include "gfx.h" - -/*===========================================================================*/ -/* Type definitions */ -/*===========================================================================*/ - -// Forward definition -typedef struct point point; - -/** - * @brief Sample data formats - * @note These are defined regardless of whether you use the GMISC module - * or not as they are used in lots of places. - */ -typedef enum ArrayDataFormat_e { - ARRAY_DATA_UNKNOWN = 0, - ARRAY_DATA_4BITUNSIGNED = 4, ARRAY_DATA_4BITSIGNED = 5, - ARRAY_DATA_8BITUNSIGNED = 8, ARRAY_DATA_8BITSIGNED = 9, - ARRAY_DATA_10BITUNSIGNED = 10, ARRAY_DATA_10BITSIGNED = 11, - ARRAY_DATA_12BITUNSIGNED = 12, ARRAY_DATA_12BITSIGNED = 13, - ARRAY_DATA_14BITUNSIGNED = 14, ARRAY_DATA_14BITSIGNED = 15, - ARRAY_DATA_16BITUNSIGNED = 16, ARRAY_DATA_16BITSIGNED = 17, - } ArrayDataFormat; - -/** - * @brief Is the sample data format a "signed" data format? - */ -#define gfxSampleFormatIsSigned(fmt) ((fmt) & 1) - -/** - * @brief How many bits are in the sample data format - */ -#define gfxSampleFormatBits(fmt) ((fmt) & ~1) - -/** - * @brief The type for a fixed point type. - * @details The top 16 bits are the integer component, the bottom 16 bits are the real component. - */ -typedef int32_t fixed; - -/** - * @brief Macros to convert to and from a fixed point. - * @{ - */ -#define FIXED(x) ((fixed)(x)<<16) /* @< integer to fixed */ -#define NONFIXED(x) ((x)>>16) /* @< fixed to integer */ -#define FIXED0_5 32768 /* @< 0.5 as a fixed (used for rounding) */ -#define FP2FIXED(x) ((fixed)((x)*65536.0)) /* @< floating point to fixed */ -#define FIXED2FP(x) ((double)(x)/65536.0) /* @< fixed to floating point */ -#define FIXEDMUL(a,b) ((fixed)((((long long)(a))*(b))>>16)) /* @< fixed,fixed multiplication */ -#define FIXEDMULINT(a,b) ((a)*(b)) /* @< integer,fixed multiplication */ -/** @} */ - -/** - * @brief The famous number pi - */ -#define PI 3.1415926535897932384626433832795028841971693993751 - -/** - * @brief pi as a fixed point - */ -#define FIXED_PI FP2FIXED(PI) - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if GFX_USE_GMISC || defined(__DOXYGEN__) - -#ifdef __cplusplus -extern "C" { -#endif - -#if GMISC_NEED_ARRAYOPS || defined(__DOXYGEN__) - /** - * @brief Convert from one array format to another array format. - * @pre Requires GFX_USE_GMISC and GMISC_NEED_ARRAYOPS - * - * @param[in] srcfmt The format of the source array - * @param[in] src The source array - * @param[in] dstfmt The format of the destination array - * @param[in] dst The dstination array - * @param[in] cnt The number of array elements to convert - * - * @note Assumes the destination buffer is large enough for the resultant data. - * @note This routine is optimised to perform as fast as possible. - * @note No type checking is performed on the source format. It is assumed to - * have only valid values eg. ARRAY_DATA_4BITSIGNED will have values - * 0000 -> 0111 for positive numbers and 1111 -> 1000 for negative numbers - * Bits 5 -> 8 in the storage byte are treated in an undefined manner. - * @note If srcfmt or dstfmt is an unknown format, this routine does nothing - * with no warning that something is wrong - * - * @api - */ - void gmiscArrayConvert(ArrayDataFormat srcfmt, void *src, ArrayDataFormat dstfmt, void *dst, size_t cnt); - - #if 0 - void gmiscArrayTranslate(ArrayDataFormat fmt, void *src, void *dst, size_t cnt, int trans); - - void gmiscArrayMultiply(ArrayDataFormat fmt, void *src, void *dst, size_t cnt, int mult); - - void gmiscArrayDivide(ArrayDataFormat fmt, void *src, void *dst, size_t cnt, int mdiv); - - void gmiscArrayMultDiv(ArrayDataFormat fmt, void *src, void *dst, size_t cnt, int mult, int div); - - void gmiscArrayAdd(ArrayDataFormat fmt, void *src1, void *src2, void *dst, size_t cnt); - - void gmiscArrayAddNoOverflow(ArrayDataFormat fmt, void *src1, void *src2, void *dst, size_t cnt); - #endif -#endif - -#if GMISC_NEED_FASTTRIG || defined(__DOXYGEN__) - extern const double sintabledouble[]; - - /** - * @brief Fast Table Based Trig functions - * @return A double in the range -1.0 .. 0.0 .. 1.0 - * @pre Requires GFX_USE_GMISC and GMISC_NEED_FASTTRIG - * - * @param[in] degrees The angle in degrees (not radians) - * - * @note These functions use degrees rather than radians to describe the angle. - * - * @api - * @{ - */ - double fsin(int degrees); - double fcos(int degrees); - /** @} - * - * @brief Fast Table Based Trig functions - * @return A double in the range -1.0 .. 0.0 .. 1.0 - * @pre Requires GFX_USE_GMISC and GMISC_NEED_FASTTRIG - * - * @param[in] degrees The angle in degrees 0 .. 359 - * - * @note These functions use degrees rather than radians to describe the angle. - * @note These functions are super fast but require the parameter to be in range. - * Use the lowercase functions if the parameter may not be in range or if a - * required trig function is not supported in this form. - * - * @api - * @{ - */ - #define FSIN(degrees) sintabledouble[degrees]; - /** @} */ -#endif - -#if GMISC_NEED_FIXEDTRIG || defined(__DOXYGEN__) - extern const fixed sintablefixed[]; - - /** - * @brief Fast Table Based Trig functions - * @return A fixed point in the range -1.0 .. 0.0 .. 1.0 - * @pre Requires GFX_USE_GMISC and GMISC_NEED_FIXEDTRIG - * - * @param[in] degrees The angle in degrees (not radians) - * - * @note These functions use degrees rather than radians to describe the angle. - * - * @api - * @{ - */ - fixed ffsin(int degrees); - fixed ffcos(int degrees); - /** @} - * - * @brief Fast Table Based Trig functions - * @return A fixed point in the range -1.0 .. 0.0 .. 1.0 - * @pre Requires GFX_USE_GMISC and GMISC_NEED_FIXEDTRIG - * - * @param[in] degrees The angle in degrees 0 .. 359 - * - * @note These functions use degrees rather than radians to describe the angle. - * @note These functions are super fast but require the parameter to be in range. - * Use the lowercase functions if the parameter may not be in range or if a - * required trig function is not supported in this form. - * - * @api - * @{ - */ - #define FFSIN(degrees) sintablefixed[degrees]; - /** @} */ -#endif - -#if GMISC_NEED_INVSQRT || defined(__DOXYGEN__) - /** - * @brief Fast inverse square root function (x^-1/2) - * @return The approximate inverse square root - * @pre Requires GFX_USE_GMISC and GMISC_NEED_INVSQRT - * - * @param[in] n The number to find the inverse square root of - * - * @note This function generates an approximate result. Higher accuracy (at the expense - * of speed) can be obtained by modifying the source code (the necessary line - * is already there - just commented out). - * @note This function relies on the internal machine format of a float and a long. - * If your machine architecture is very unusual this function may not work. - * - * @api - */ - float invsqrt(float n); -#endif - -#if GMISC_NEED_MATRIXFLOAT2D || defined(__DOXYGEN__) - - /** - * @brief A matrix for doing 2D graphics using floats - * @pre Requires GFX_USE_GMISC and GMISC_NEED_MATRIXFLOAT2D - */ - typedef struct MatrixFloat2D { - float a00, a01, a02; - float a10, a11, a12; - float a20, a21, a22; - } MatrixFloat2D; - - /** - * @brief Apply the matrix to a set of points - * @pre Requires GFX_USE_GMISC and GMISC_NEED_MATRIXFLOAT2D - * - * @param[in] dst The destination array of points - * @param[in] src The source array of points - * @param[in] m The matrix to apply - * @param[in] cnt How many points are in the array - * - * @note In-place matrix application is allowed ie. dst = src - * - * @api - */ - void gmiscMatrixFloat2DApplyToPoints(point *dst, const point *src, const MatrixFloat2D *m, int cnt); - - /** - * @brief Set the 2D matrix to the identity matrix - * @pre Requires GFX_USE_GMISC and GMISC_NEED_MATRIXFLOAT2D - * - * @param[in] m The matrix to set to identity - * - * @api - */ - void gmiscMatrixFloat2DSetIdentity(MatrixFloat2D *m); - - /** - * @brief Multiple two 2D matrixes together - * @pre Requires GFX_USE_GMISC and GMISC_NEED_MATRIXFLOAT2D - * - * @param[in] dst The destination matrix - * @param[in] src1 The first source matrix - * @param[in] src2 The second source matrix - * - * @note In-place matrix application is NOT allowed ie. dst != src1, dst != src2 - * - * @api - */ - void gmiscMatrixFloat2DMultiply(MatrixFloat2D *dst, const MatrixFloat2D *src1, const MatrixFloat2D *src2); - - /** - * @brief Add an x,y translation to a matrix - * @pre Requires GFX_USE_GMISC and GMISC_NEED_MATRIXFLOAT2D - * - * @param[in] dst The destination matrix - * @param[in] src The source matrix. Can be NULL - * @param[in] tx, ty The x and y translation to apply - * - * @note In-place matrix operation is NOT allowed ie. dst != src - * @note If no source matrix is provided, it is equivalent to applying the operation - * to an identity matrix. It also is a much simpler operation requiring no multiplication. - * - * @api - */ - void gmiscMatrixFloat2DApplyTranslation(MatrixFloat2D *dst, const MatrixFloat2D *src, float tx, float ty); - - /** - * @brief Add x,y scaling to a matrix - * @pre Requires GFX_USE_GMISC and GMISC_NEED_MATRIXFLOAT2D - * - * @param[in] dst The destination matrix - * @param[in] src The source matrix. Can be NULL - * @param[in] sx, sy The scaling to apply in the x and y direction. Negative numbers give reflection. - * - * @note In-place matrix operation is NOT allowed ie. dst != src - * @note If no source matrix is provided, it is equivalent to applying the operation - * to an identity matrix. It also is a much simpler operation requiring no multiplication. - * - * @api - */ - void gmiscMatrixFloat2DApplyScale(MatrixFloat2D *dst, const MatrixFloat2D *src, float sx, float sy); - - /** - * @brief Add x,y shear to a matrix - * @pre Requires GFX_USE_GMISC and GMISC_NEED_MATRIXFLOAT2D - * - * @param[in] dst The destination matrix - * @param[in] src The source matrix. Can be NULL - * @param[in] sx, sy The shear to apply in the x and y direction. - * - * @note In-place matrix operation is NOT allowed ie. dst != src - * @note If no source matrix is provided, it is equivalent to applying the operation - * to an identity matrix. It also is a much simpler operation requiring no multiplication. - * - * @api - */ - void gmiscMatrixFloat2DApplyShear(MatrixFloat2D *dst, const MatrixFloat2D *src, float sx, float sy); - - /** - * @brief Add rotation to a matrix - * @pre Requires GFX_USE_GMISC and GMISC_NEED_MATRIXFLOAT2D - * - * @param[in] dst The destination matrix - * @param[in] src The source matrix. Can be NULL - * @param[in] angle The angle to apply in degrees (not radians). - * - * @note In-place matrix operation is NOT allowed ie. dst != src - * @note If no source matrix is provided, it is equivalent to applying the operation - * to an identity matrix. It also is a much simpler operation. - * @note If GMISC_NEED_FASTTRIG is defined then the fast table sin and cos lookup's will be used - * rather than the C library versions. - * - * @api - */ - void gmiscMatrixFloat2DApplyRotation(MatrixFloat2D *dst, const MatrixFloat2D *src, int angle); -#endif - -#if GMISC_NEED_MATRIXFIXED2D || defined(__DOXYGEN__) - - /** - * @brief A matrix for doing 2D graphics using fixed point maths - * @pre Requires GFX_USE_GMISC and GMISC_NEED_MATRIXFIXED2D - */ - typedef struct MatrixFixed2D { - fixed a00, a01, a02; - fixed a10, a11, a12; - fixed a20, a21, a22; - } MatrixFixed2D; - - /** - * @brief Apply the matrix to a set of points - * @pre Requires GFX_USE_GMISC and GMISC_NEED_MATRIXFIXED2D - * - * @param[in] dst The destination array of points - * @param[in] src The source array of points - * @param[in] m The matrix to apply - * @param[in] cnt How many points are in the array - * - * @note In-place matrix application is allowed ie. dst = src - * - * @api - */ - void gmiscMatrixFixed2DApplyToPoints(point *dst, const point *src, const MatrixFixed2D *m, int cnt); - - /** - * @brief Set the 2D matrix to the identity matrix - * @pre Requires GFX_USE_GMISC and GMISC_NEED_MATRIXFIXED2D - * - * @param[in] m The matrix to set to identity - * - * @api - */ - void gmiscMatrixFixed2DSetIdentity(MatrixFixed2D *m); - - /** - * @brief Multiple two 2D matrixes together - * @pre Requires GFX_USE_GMISC and GMISC_NEED_MATRIXFIXED2D - * - * @param[in] dst The destination matrix - * @param[in] src1 The first source matrix - * @param[in] src2 The second source matrix - * - * @note In-place matrix application is NOT allowed ie. dst != src1, dst != src2 - * - * @api - */ - void gmiscMatrixFixed2DMultiply(MatrixFixed2D *dst, const MatrixFixed2D *src1, const MatrixFixed2D *src2); - - /** - * @brief Add an x,y translation to a matrix - * @pre Requires GFX_USE_GMISC and GMISC_NEED_MATRIXFIXED2D - * - * @param[in] dst The destination matrix - * @param[in] src The source matrix. Can be NULL - * @param[in] tx, ty The x and y translation to apply - * - * @note In-place matrix operation is NOT allowed ie. dst != src - * @note If no source matrix is provided, it is equivalent to applying the operation - * to an identity matrix. It also is a much simpler operation requiring no multiplication. - * - * @api - */ - void gmiscMatrixFixed2DApplyTranslation(MatrixFixed2D *dst, const MatrixFixed2D *src, fixed tx, fixed ty); - - /** - * @brief Add x,y scaling to a matrix - * @pre Requires GFX_USE_GMISC and GMISC_NEED_MATRIXFIXED2D - * - * @param[in] dst The destination matrix - * @param[in] src The source matrix. Can be NULL - * @param[in] sx, sy The scaling to apply in the x and y direction. Negative numbers give reflection. - * - * @note In-place matrix operation is NOT allowed ie. dst != src - * @note If no source matrix is provided, it is equivalent to applying the operation - * to an identity matrix. It also is a much simpler operation requiring no multiplication. - * - * @api - */ - void gmiscMatrixFixed2DApplyScale(MatrixFixed2D *dst, const MatrixFixed2D *src, fixed sx, fixed sy); - - /** - * @brief Add x,y shear to a matrix - * @pre Requires GFX_USE_GMISC and GMISC_NEED_MATRIXFIXED2D - * - * @param[in] dst The destination matrix - * @param[in] src The source matrix. Can be NULL - * @param[in] sx, sy The shear to apply in the x and y direction. - * - * @note In-place matrix operation is NOT allowed ie. dst != src - * @note If no source matrix is provided, it is equivalent to applying the operation - * to an identity matrix. It also is a much simpler operation requiring no multiplication. - * - * @api - */ - void gmiscMatrixFixed2DApplyShear(MatrixFixed2D *dst, const MatrixFixed2D *src, fixed sx, fixed sy); - - #if GMISC_NEED_FIXEDTRIG || defined(__DOXYGEN__) - /** - * @brief Add rotation to a matrix - * @pre Requires GFX_USE_GMISC and GMISC_NEED_MATRIXFIXED2D and GMISC_NEED_FIXEDTRIG - * - * @param[in] dst The destination matrix - * @param[in] src The source matrix. Can be NULL - * @param[in] angle The angle to apply in degrees (not radians). - * - * @note In-place matrix operation is NOT allowed ie. dst != src - * @note If no source matrix is provided, it is equivalent to applying the operation - * to an identity matrix. It also is a much simpler operation requiring no multiplication. - * - * @api - */ - void gmiscMatrixFixed2DApplyRotation(MatrixFixed2D *dst, const MatrixFixed2D *src, int angle); - #endif -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* GFX_USE_MISC */ - -#endif /* _GMISC_H */ -/** @} */ - diff --git a/src/gmisc/sys_make.mk b/src/gmisc/sys_make.mk deleted file mode 100644 index 203910e6..00000000 --- a/src/gmisc/sys_make.mk +++ /dev/null @@ -1,4 +0,0 @@ -GFXSRC += $(GFXLIB)/src/gmisc/gmisc_gmisc.c \ - $(GFXLIB)/src/gmisc/gmisc_arrayops.c \ - $(GFXLIB)/src/gmisc/gmisc_matrix2d.c \ - $(GFXLIB)/src/gmisc/gmisc_trig.c diff --git a/src/gmisc/sys_options.h b/src/gmisc/sys_options.h deleted file mode 100644 index 9cdf37f6..00000000 --- a/src/gmisc/sys_options.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gmisc/sys_options.h - * @brief GMISC - Miscellaneous Routines options header file. - * - * @addtogroup GMISC - * @{ - */ - -#ifndef _GMISC_OPTIONS_H -#define _GMISC_OPTIONS_H - -/** - * @name GMISC Functionality to be included - * @{ - */ - /** - * @brief Include array operation functions - * @details Defaults to FALSE - */ - #ifndef GMISC_NEED_ARRAYOPS - #define GMISC_NEED_ARRAYOPS FALSE - #endif - /** - * @brief Include fast floating point trig functions (fsin, fcos) - * @details Defaults to FALSE - */ - #ifndef GMISC_NEED_FASTTRIG - #define GMISC_NEED_FASTTRIG FALSE - #endif - /** - * @brief Include fast fixed point trig functions (ffsin, ffcos) - * @details Defaults to FALSE - */ - #ifndef GMISC_NEED_FIXEDTRIG - #define GMISC_NEED_FIXEDTRIG FALSE - #endif - /** - * @brief Include fast inverse square root (x^-1/2) - * @details Defaults to FALSE - */ - #ifndef GMISC_NEED_INVSQRT - #define GMISC_NEED_INVSQRT FALSE - #endif -/** - * @} - * - * @name GMISC Optional Parameters - * @{ - */ - /** - * @brief Modifies the @p invsqrt() function to assume a different integer to floating point endianness. - * @note Normally the floating point format and the integer format have - * the same endianness. Unfortunately there are some strange - * processors that don't eg. some very early ARM devices. - * For those where the endianness doesn't match you can fix it by - * defining GMISC_INVSQRT_MIXED_ENDIAN. - * @note This still assumes the processor is using an ieee floating point format. - * - * If you have a software floating point that uses a non-standard - * floating point format (or very strange hardware) then define - * GMISC_INVSQRT_REAL_SLOW and it will do it the hard way. - */ - #ifndef GMISC_INVSQRT_MIXED_ENDIAN - #define GMISC_INVSQRT_MIXED_ENDIAN FALSE - #endif - /** - * @brief Modifies the @p invsqrt() function to do things the long slow way. - * @note This causes the @p invsqrt() function to work regardless of the - * processor floating point format. - * @note This makes the @p invsqrt() function very slow. - */ - #ifndef GMISC_INVSQRT_REAL_SLOW - #define GMISC_INVSQRT_REAL_SLOW FALSE - #endif -/** @} */ - -#endif /* _GMISC_OPTIONS_H */ -/** @} */ diff --git a/src/gmisc/sys_rules.h b/src/gmisc/sys_rules.h deleted file mode 100644 index 6c66907a..00000000 --- a/src/gmisc/sys_rules.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gmisc/sys_rules.h - * @brief GMISC safety rules header file. - * - * @addtogroup GMISC - * @{ - */ - -#ifndef _GMISC_RULES_H -#define _GMISC_RULES_H - -#if GFX_USE_GMISC -#endif - -#endif /* _GMISC_RULES_H */ -/** @} */ diff --git a/src/gos/gos.h b/src/gos/gos.h new file mode 100644 index 00000000..f1def55a --- /dev/null +++ b/src/gos/gos.h @@ -0,0 +1,470 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gos/gos.h + * @brief GOS - Operating System Support header file + * + * @addtogroup GOS + * + * @brief Module to build a uniform abstraction layer between uGFX and the underlying system + * + * @note Some of the routines specified below may be implemented simply as + * a macro to the real operating system call. + * @{ + */ + +#ifndef _GOS_H +#define _GOS_H + +#if defined(__DOXYGEN__) + /*===========================================================================*/ + /* Type definitions */ + /*===========================================================================*/ + + /** + * @name Various integer sizes + * @note Your platform may use slightly different definitions to these + * @{ + */ + typedef unsigned char bool_t; + typedef char int8_t; + typedef unsigned char uint8_t; + typedef short int16_t; + typedef unsigned short uint16_t; + typedef long int32_t; + typedef unsigned long uint32_t; + /** @} */ + + /** + * @name Various platform (and operating system) dependent types + * @note Your platform may use slightly different definitions to these + * @{ + */ + typedef unsigned long size_t; + typedef unsigned long delaytime_t; + typedef unsigned long systemticks_t; + typedef short semcount_t; + typedef int threadreturn_t; + typedef int threadpriority_t; + /** @} */ + + /** + * @brief Declare a thread function + * + * @param[in] fnName The name of the function + * @param[in] param A custom parameter that is passed to the function + */ + #define DECLARE_THREAD_FUNCTION(fnName, param) threadreturn_t fnName(void *param) + + /** + * @brief Declare a thread stack + * + * @param[in] name The name of the stack + * @param[in] sz The size of the stack + */ + #define DECLARE_THREAD_STACK(name, sz) uint8_t name[sz]; + + /** + * @name Various platform (and operating system) constants + * @note Your platform may use slightly different definitions to these + * @{ + */ + #define FALSE 0 + #define TRUE 1 + #define TIME_IMMEDIATE 0 + #define TIME_INFINITE ((delaytime_t)-1) + #define MAX_SEMAPHORE_COUNT ((semcount_t)(((unsigned long)((semcount_t)(-1))) >> 1)) + #define LOW_PRIORITY 0 + #define NORMAL_PRIORITY 1 + #define HIGH_PRIORITY 2 + /** @} */ + + /** + * @brief A semaphore + * @note Your operating system will have a proper definition for this structure + */ + typedef struct {} gfxSem; + + /** + * @brief A mutex + * @note Your operating system will have a proper definition for this structure + */ + typedef struct {} gfxMutex; + + /** + * @brief A thread handle + * @note Your operating system will have a proper definition for this. + */ + typedef void * gfxThreadHandle; + + /*===========================================================================*/ + /* Function declarations. */ + /*===========================================================================*/ + + #ifdef __cplusplus + extern "C" { + #endif + + /** + * @brief Halt the GFX application due to an error. + * + * @param[in] msg An optional debug message to show (Can be NULL) + * + * @api + */ + void gfxHalt(const char *msg); + + /** + * @brief Exit the GFX application. + * + * @api + */ + void gfxExit(void); + + /** + * @brief Allocate memory + * @return A pointer to the memory allocated or NULL if there is no more memory available + * + * @param[in] sz The size in bytes of the area to allocate + * + * @api + */ + void *gfxAlloc(size_t sz); + + /** + * @brief Re-allocate memory + * @return A pointer to the new memory area or NULL if there is no more memory available + * + * @param[in] ptr The old memory area to be increased/decreased in size + * @param[in] oldsz The size in bytes of the old memory area + * @param[in] newsz The size in bytes of the new memory area + * + * @note Some operating systems don't use the oldsz parameter as they implicitly know the size of + * old memory area. The parameter must always be supplied however for API compatibility. + * @note gfxRealloc() can make the area smaller or larger but may have to return a different pointer. + * If this occurs the new area contains a copy of the data from the old area. The old memory + * pointer should not be used after this routine as the original area may have been freed. + * @note If there is insufficient memory to create the new memory region, NULL is returned and the + * old memory area is left unchanged. + * + * @api + */ + void *gfxRealloc(void *ptr, size_t oldsz, size_t newsz); + + /** + * @brief Free memory + * + * @param[in] ptr The memory to free + * + * @api + */ + void gfxFree(void *ptr); + + /** + * @brief Yield the current thread + * @details Give up the rest of the current time slice for this thread in order to give other threads + * a chance to run. + * + * @api + */ + void gfxYield(void); + + /** + * @brief Put the current thread to sleep for the specified period in milliseconds + * + * @param[in] ms The number milliseconds to sleep + * + * @note Specifying TIME_IMMEDIATE will yield the current thread but return + * on the next time slice. + * @note Specifying TIME_INFINITE will sleep forever. + * + * @api + */ + void gfxSleepMilliseconds(delaytime_t ms); + + /** + * @brief Put the current thread to sleep for the specified period in microseconds + * + * @param[in] us The number microseconds to sleep + * + * @note Specifying TIME_IMMEDIATE will return immediately (no sleeping) + * @note Specifying TIME_INFINITE will sleep forever. + * + * @api + */ + void gfxSleepMicroseconds(delaytime_t us); + + /** + * @brief Get the current operating system tick time + * @return The current tick time + * + * @note A "tick" is an arbitrary period of time that the operating + * system uses to mark time. + * @note The absolute value of this call is relatively meaningless. Its usefulness + * is in calculating periods between two calls to this function. + * @note As the value from this function can wrap it is important that any periods are calculated + * as t2 - t1 and then compared to the desired period rather than comparing + * t1 + period to t2 + * + * @api + */ + systemticks_t gfxSystemTicks(void); + + /** + * @brief Convert a given number of millseconds to a number of operating system ticks + * @return The period in system ticks. + * + * @note A "tick" is an arbitrary period of time that the operating + * system uses to mark time. + * + * @param[in] ms The number of millseconds + * + * @api + */ + systemticks_t gfxMillisecondsToTicks(delaytime_t ms); + + /** + * @brief Lock the operating system to protect a sequence of code + * + * @note Calling this will lock out all other threads from executing even at interrupt level + * within the GFX system. On hardware this may be implemented as a disabling of interrupts, + * however in an operating system which hides real interrupt level code it may simply use a + * mutex lock. + * @note The thread MUST NOT block whilst the system is locked. It must execute in this state for + * as short a period as possible as this can seriously affect interrupt latency on some + * platforms. + * @note While locked only interrupt level (iclass) GFX routines may be called. + * + * @api + */ + void gfxSystemLock(void); + + /** + * @brief Unlock the operating system previous locked by gfxSystemLock() + * + * @api + */ + void gfxSystemUnlock(void); + + /** + * @brief Initialise a mutex to protect a region of code from other threads. + * + * @param[in] pmutex A pointer to the mutex + * + * @note Whilst a counting semaphore with a limit of 1 can be used for similiar purposes + * on many operating systems using a seperate mutex structure is more efficient. + * + * @api + */ + void gfxMutexInit(gfxMutex *pmutex); + + /** + * @brief Destroy a Mutex. + * + * @param[in] pmutex A pointer to the mutex + * + * @api + */ + void gfxMutexDestroy(gfxMutex *pmutex); + + /** + * @brief Enter the critical code region protected by the mutex. + * @details Blocks until there is no other thread in the critical region. + * + * @param[in] pmutex A pointer to the mutex + * + * @api + */ + void gfxMutexEnter(gfxMutex *pmutex); + + /** + * @brief Exit the critical code region protected by the mutex. + * @details May cause another thread waiting on the mutex to now be placed into the run queue. + * + * @param[in] pmutex A pointer to the mutex + * + * @api + */ + void gfxMutexExit(gfxMutex *pmutex); + + /** + * @brief Initialise a Counted Semaphore + * + * @param[in] psem A pointer to the semaphore + * @param[in] val The initial value of the semaphore + * @param[in] limit The maxmimum value of the semaphore + * + * @note Operations defined for counted semaphores: + * Signal: The semaphore counter is increased and if the result is non-positive then a waiting thread + * is queued for execution. Note that once the thread reaches "limit", further signals are + * ignored. + * Wait: The semaphore counter is decreased and if the result becomes negative the thread is queued + * in the semaphore and suspended. + * + * @api + */ + void gfxSemInit(gfxSem *psem, semcount_t val, semcount_t limit); + + /** + * @brief Destroy a Counted Semaphore + * + * @param[in] psem A pointer to the semaphore + * + * @note Any threads waiting on the semaphore will be released + * + * @api + */ + void gfxSemDestroy(gfxSem *psem); + + /** + * @brief Wait on a semaphore + * @details The semaphore counter is decreased and if the result becomes negative the thread waits for it to become + * non-negative again + * @return FALSE if the wait timeout occurred otherwise TRUE + * + * @param[in] psem A pointer to the semaphore + * @param[in] ms The maximum time to wait for the semaphore + * + * @api + */ + bool_t gfxSemWait(gfxSem *psem, delaytime_t ms); + + /** + * @brief Test if a wait on a semaphore can be satisfied immediately + * @details Equivalent to @p gfxSemWait(psem, TIME_IMMEDIATE) except it can be called at interrupt level + * @return FALSE if the wait would occur occurred otherwise TRUE + * + * @param[in] psem A pointer to the semaphore + * + * @iclass + * @api + */ + bool_t gfxSemWaitI(gfxSem *psem); + + /** + * @brief Signal a semaphore + * @details The semaphore counter is increased and if the result is non-positive then a waiting thread + * is queued for execution. Note that once the thread reaches "limit", further signals are + * ignored. + * + * @param[in] psem A pointer to the semaphore + * + * @api + */ + void gfxSemSignal(gfxSem *psem); + + /** + * @brief Signal a semaphore + * @details The semaphore counter is increased and if the result is non-positive then a waiting thread + * is queued for execution. Note that once the thread reaches "limit", further signals are + * ignored. + * + * @param[in] psem A pointer to the semaphore + * + * @iclass + * @api + */ + void gfxSemSignalI(gfxSem *psem); + + /** + * @brief Get the current semaphore count + * @return The current semaphore count + * + * @param[in] psem A pointer to the semaphore + * + * @api + */ + semcount_t gfxSemCounter(gfxSem *psem); + + /** + * @brief Get the current semaphore count + * @return The current semaphore count + * + * @param[in] psem A pointer to the semaphore + * + * @iclass + * @api + */ + semcount_t gfxSemCounterI(gfxSem *psem); + + /** + * @brief Start a new thread. + * @return Returns a thread handle if the thread was started, NULL on an error + * + * @param[in] stackarea A pointer to the area for the new threads stack or NULL to dynamically allocate it + * @param[in] stacksz The size of the thread stack. 0 means the default operating system size although this + * is only valid when stackarea is dynamically allocated. + * @param[in] prio The priority of the new thread + * @param[in] fn The function the new thread will run + * @param[in] param A parameter to pass the thread function. + * + * @api + */ + gfxThreadHandle gfxThreadCreate(void *stackarea, size_t stacksz, threadpriority_t prio, DECLARE_THREAD_FUNCTION((*fn),p), void *param); + + /** + * @brief Wait for a thread to finish. + * @return Returns the thread exit code. + * + * @param[in] thread The Thread Handle + * + * @note This will also close the thread handle as it is no longer useful + * once the thread has ended. + * @api + */ + threadreturn_t gfxThreadWait(gfxThreadHandle thread); + + /** + * @brief Get the current thread handle. + * @return A thread handle + * + * @api + */ + gfxThreadHandle gfxThreadMe(void); + + /** + * @brief Close the thread handle. + * + * @param[in] thread The Thread Handle + * + * @note This does not affect the thread, it just closes our handle to the thread. + * + * @api + */ + void gfxThreadClose(gfxThreadHandle thread); + + #ifdef __cplusplus + } + #endif + +/** + * All the above was just for the doxygen documentation. All the implementation of the above + * (without any of the documentation overheads) is in the files below. + */ +#elif GFX_USE_OS_RAWRTOS + #include "src/gos/gos_rawrtos.h" +#elif GFX_USE_OS_CHIBIOS + #include "src/gos/gos_chibios.h" +#elif GFX_USE_OS_FREERTOS + #include "src/gos/gos_freertos.h" +#elif GFX_USE_OS_WIN32 + #include "src/gos/gos_win32.h" +#elif GFX_USE_OS_LINUX + #include "src/gos/gos_linux.h" +#elif GFX_USE_OS_OSX + #include "src/gos/gos_osx.h" +#elif GFX_USE_OS_RAW32 + #include "src/gos/gos_raw32.h" +#elif GFX_USE_OS_ECOS + #include "src/gos/gos_ecos.h" +#else + #error "Your operating system is not supported yet" +#endif + +#endif /* _GOS_H */ +/** @} */ diff --git a/src/gos/gos.mk b/src/gos/gos.mk new file mode 100644 index 00000000..a7b3dec6 --- /dev/null +++ b/src/gos/gos.mk @@ -0,0 +1,9 @@ +GFXSRC += $(GFXLIB)/src/gos/gos_chibios.c \ + $(GFXLIB)/src/gos/gos_freertos.c \ + $(GFXLIB)/src/gos/gos_win32.c \ + $(GFXLIB)/src/gos/gos_linux.c \ + $(GFXLIB)/src/gos/gos_osx.c \ + $(GFXLIB)/src/gos/gos_raw32.c \ + $(GFXLIB)/src/gos/gos_ecos.c \ + $(GFXLIB)/src/gos/gos_rawrtos.c + diff --git a/src/gos/gos_options.h b/src/gos/gos_options.h new file mode 100644 index 00000000..611acfb7 --- /dev/null +++ b/src/gos/gos_options.h @@ -0,0 +1,115 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gos/gos_options.h + * @brief GOS - Operating System options header file. + * + * @addtogroup GOS + * @{ + */ + +#ifndef _GOS_OPTIONS_H +#define _GOS_OPTIONS_H + +/** + * @name The operating system to use. One (and only one) of these must be defined. + * @{ + */ + /** + * @brief Use ChibiOS + * @details Defaults to FALSE + */ + #ifndef GFX_USE_OS_CHIBIOS + #define GFX_USE_OS_CHIBIOS FALSE + #endif + /** + * @brief Use FreeRTOS + * @details Defaults to FALSE + */ + #ifndef GFX_USE_OS_FREERTOS + #define GFX_USE_OS_FREERTOS FALSE + #endif + /** + * @brief Use Win32 + * @details Defaults to FALSE + */ + #ifndef GFX_USE_OS_WIN32 + #define GFX_USE_OS_WIN32 FALSE + #endif + /** + * @brief Use a linux based system running X11 + * @details Defaults to FALSE + */ + #ifndef GFX_USE_OS_LINUX + #define GFX_USE_OS_LINUX FALSE + #endif + /** + * @brief Use a Mac OS-X based system + * @details Defaults to FALSE + */ + #ifndef GFX_USE_OS_OSX + #define GFX_USE_OS_OSX FALSE + #endif + /** + * @brief Use a Raw 32 bit CPU based system + * @details Defaults to FALSE + */ + #ifndef GFX_USE_OS_RAW32 + #define GFX_USE_OS_RAW32 FALSE + #endif + /** + * @brief Use a eCos + * @details Defaults to FALSE + */ + #ifndef GFX_USE_OS_ECOS + #define GFX_USE_OS_ECOS FALSE + #endif +/** + * @} + * + * @name GOS Optional Parameters + * @{ + */ + /** + * @brief Should uGFX avoid initializing the operating system + * @details Defaults to FALSE + * @note This is not relevant to all operating systems eg Win32 never initializes the + * operating system as uGFX runs as an application outside the boot process. + * @note Operating system initialization is not necessarily implemented for all + * operating systems yet even when it is relevant. These operating systems + * will display a compile warning reminding you to initialize the operating + * system in your application code. Note that on these operating systems the + * demo applications will not work without modification. + */ + #ifndef GFX_NO_OS_INIT + #define GFX_NO_OS_INIT FALSE + #endif + /** + * @brief Should uGFX stuff be added to the FreeRTOS+Tracer + * @details Defaults to FALSE + */ + #ifndef GFX_FREERTOS_USE_TRACE + #define GFX_FREERTOS_USE_TRACE FALSE + #endif + /** + * @brief How much RAM should uGFX use for the heap + * @details Defaults to 0. Only valid with GFX_USE_OS_RAW32 + * @note If 0 then the standard C runtime malloc(), free() and realloc() + * are used. + * @note If it is non-zero then this is the number of bytes of RAM + * to use for the heap (gfxAlloc() and gfxFree()). No C + * runtime routines will be used and a new routine @p gfxAddHeapBlock() + * is added allowing the user to add extra memory blocks to the heap. + */ + #ifndef GOS_RAW_HEAP_SIZE + #define GOS_RAW_HEAP_SIZE 0 + #endif +/** @} */ + +#endif /* _GOS_OPTIONS_H */ +/** @} */ diff --git a/src/gos/gos_rawrtos.c b/src/gos/gos_rawrtos.c index cd684208..c47c85bf 100644 --- a/src/gos/gos_rawrtos.c +++ b/src/gos/gos_rawrtos.c @@ -1,3 +1,10 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + #include "gfx.h" #if GFX_USE_OS_RAWRTOS diff --git a/src/gos/gos_rules.h b/src/gos/gos_rules.h new file mode 100644 index 00000000..0a86f86e --- /dev/null +++ b/src/gos/gos_rules.h @@ -0,0 +1,36 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gos/gos_rules.h + * @brief GOS safety rules header file. + * + * @addtogroup GOS + * @{ + */ + +#ifndef _GOS_RULES_H +#define _GOS_RULES_H + +#if !GFX_USE_OS_CHIBIOS && !GFX_USE_OS_WIN32 && !GFX_USE_OS_LINUX && !GFX_USE_OS_OSX && !GFX_USE_OS_RAW32 && !GFX_USE_OS_FREERTOS && !GFX_USE_OS_ECOS && !GFX_USE_OS_RAWRTOS + #if GFX_DISPLAY_RULE_WARNINGS + #warning "GOS: No Operating System has been defined. ChibiOS (GFX_USE_OS_CHIBIOS) has been turned on for you." + #endif + #undef GFX_USE_OS_CHIBIOS + #define GFX_USE_OS_CHIBIOS TRUE +#endif + +#if GFX_USE_OS_CHIBIOS + GFX_USE_OS_WIN32 + GFX_USE_OS_LINUX + GFX_USE_OS_OSX + GFX_USE_OS_RAW32 + GFX_USE_OS_FREERTOS + GFX_USE_OS_ECOS + GFX_USE_OS_RAWRTOS != 1 * TRUE + #error "GOS: More than one operation system has been defined as TRUE." +#endif + +#if GFX_FREERTOS_USE_TRACE && !GFX_USE_OS_FREERTOS + #error "GOS: GFX_FREERTOS_USE_TRACE is only available for the FreeRTOS port." +#endif + +#endif /* _GOS_RULES_H */ +/** @} */ diff --git a/src/gos/gos_win32.c b/src/gos/gos_win32.c index 0ae9e618..a7e0943b 100644 --- a/src/gos/gos_win32.c +++ b/src/gos/gos_win32.c @@ -5,10 +5,6 @@ * http://ugfx.org/license.html */ -/** - * @file src/gos/gos_win32.c - * @brief GOS Win32 Operating System support. - */ #include "gfx.h" #if GFX_USE_OS_WIN32 diff --git a/src/gos/sys_defs.h b/src/gos/sys_defs.h deleted file mode 100644 index 7e1348bb..00000000 --- a/src/gos/sys_defs.h +++ /dev/null @@ -1,470 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gos/sys_defs.h - * @brief GOS - Operating System Support header file - * - * @addtogroup GOS - * - * @brief Module to build a uniform abstraction layer between uGFX and the underlying system - * - * @note Some of the routines specified below may be implemented simply as - * a macro to the real operating system call. - * @{ - */ - -#ifndef _GOS_H -#define _GOS_H - -#if defined(__DOXYGEN__) - /*===========================================================================*/ - /* Type definitions */ - /*===========================================================================*/ - - /** - * @name Various integer sizes - * @note Your platform may use slightly different definitions to these - * @{ - */ - typedef unsigned char bool_t; - typedef char int8_t; - typedef unsigned char uint8_t; - typedef short int16_t; - typedef unsigned short uint16_t; - typedef long int32_t; - typedef unsigned long uint32_t; - /** @} */ - - /** - * @name Various platform (and operating system) dependent types - * @note Your platform may use slightly different definitions to these - * @{ - */ - typedef unsigned long size_t; - typedef unsigned long delaytime_t; - typedef unsigned long systemticks_t; - typedef short semcount_t; - typedef int threadreturn_t; - typedef int threadpriority_t; - /** @} */ - - /** - * @brief Declare a thread function - * - * @param[in] fnName The name of the function - * @param[in] param A custom parameter that is passed to the function - */ - #define DECLARE_THREAD_FUNCTION(fnName, param) threadreturn_t fnName(void *param) - - /** - * @brief Declare a thread stack - * - * @param[in] name The name of the stack - * @param[in] sz The size of the stack - */ - #define DECLARE_THREAD_STACK(name, sz) uint8_t name[sz]; - - /** - * @name Various platform (and operating system) constants - * @note Your platform may use slightly different definitions to these - * @{ - */ - #define FALSE 0 - #define TRUE 1 - #define TIME_IMMEDIATE 0 - #define TIME_INFINITE ((delaytime_t)-1) - #define MAX_SEMAPHORE_COUNT ((semcount_t)(((unsigned long)((semcount_t)(-1))) >> 1)) - #define LOW_PRIORITY 0 - #define NORMAL_PRIORITY 1 - #define HIGH_PRIORITY 2 - /** @} */ - - /** - * @brief A semaphore - * @note Your operating system will have a proper definition for this structure - */ - typedef struct {} gfxSem; - - /** - * @brief A mutex - * @note Your operating system will have a proper definition for this structure - */ - typedef struct {} gfxMutex; - - /** - * @brief A thread handle - * @note Your operating system will have a proper definition for this. - */ - typedef void * gfxThreadHandle; - - /*===========================================================================*/ - /* Function declarations. */ - /*===========================================================================*/ - - #ifdef __cplusplus - extern "C" { - #endif - - /** - * @brief Halt the GFX application due to an error. - * - * @param[in] msg An optional debug message to show (Can be NULL) - * - * @api - */ - void gfxHalt(const char *msg); - - /** - * @brief Exit the GFX application. - * - * @api - */ - void gfxExit(void); - - /** - * @brief Allocate memory - * @return A pointer to the memory allocated or NULL if there is no more memory available - * - * @param[in] sz The size in bytes of the area to allocate - * - * @api - */ - void *gfxAlloc(size_t sz); - - /** - * @brief Re-allocate memory - * @return A pointer to the new memory area or NULL if there is no more memory available - * - * @param[in] ptr The old memory area to be increased/decreased in size - * @param[in] oldsz The size in bytes of the old memory area - * @param[in] newsz The size in bytes of the new memory area - * - * @note Some operating systems don't use the oldsz parameter as they implicitly know the size of - * old memory area. The parameter must always be supplied however for API compatibility. - * @note gfxRealloc() can make the area smaller or larger but may have to return a different pointer. - * If this occurs the new area contains a copy of the data from the old area. The old memory - * pointer should not be used after this routine as the original area may have been freed. - * @note If there is insufficient memory to create the new memory region, NULL is returned and the - * old memory area is left unchanged. - * - * @api - */ - void *gfxRealloc(void *ptr, size_t oldsz, size_t newsz); - - /** - * @brief Free memory - * - * @param[in] ptr The memory to free - * - * @api - */ - void gfxFree(void *ptr); - - /** - * @brief Yield the current thread - * @details Give up the rest of the current time slice for this thread in order to give other threads - * a chance to run. - * - * @api - */ - void gfxYield(void); - - /** - * @brief Put the current thread to sleep for the specified period in milliseconds - * - * @param[in] ms The number milliseconds to sleep - * - * @note Specifying TIME_IMMEDIATE will yield the current thread but return - * on the next time slice. - * @note Specifying TIME_INFINITE will sleep forever. - * - * @api - */ - void gfxSleepMilliseconds(delaytime_t ms); - - /** - * @brief Put the current thread to sleep for the specified period in microseconds - * - * @param[in] us The number microseconds to sleep - * - * @note Specifying TIME_IMMEDIATE will return immediately (no sleeping) - * @note Specifying TIME_INFINITE will sleep forever. - * - * @api - */ - void gfxSleepMicroseconds(delaytime_t us); - - /** - * @brief Get the current operating system tick time - * @return The current tick time - * - * @note A "tick" is an arbitrary period of time that the operating - * system uses to mark time. - * @note The absolute value of this call is relatively meaningless. Its usefulness - * is in calculating periods between two calls to this function. - * @note As the value from this function can wrap it is important that any periods are calculated - * as t2 - t1 and then compared to the desired period rather than comparing - * t1 + period to t2 - * - * @api - */ - systemticks_t gfxSystemTicks(void); - - /** - * @brief Convert a given number of millseconds to a number of operating system ticks - * @return The period in system ticks. - * - * @note A "tick" is an arbitrary period of time that the operating - * system uses to mark time. - * - * @param[in] ms The number of millseconds - * - * @api - */ - systemticks_t gfxMillisecondsToTicks(delaytime_t ms); - - /** - * @brief Lock the operating system to protect a sequence of code - * - * @note Calling this will lock out all other threads from executing even at interrupt level - * within the GFX system. On hardware this may be implemented as a disabling of interrupts, - * however in an operating system which hides real interrupt level code it may simply use a - * mutex lock. - * @note The thread MUST NOT block whilst the system is locked. It must execute in this state for - * as short a period as possible as this can seriously affect interrupt latency on some - * platforms. - * @note While locked only interrupt level (iclass) GFX routines may be called. - * - * @api - */ - void gfxSystemLock(void); - - /** - * @brief Unlock the operating system previous locked by gfxSystemLock() - * - * @api - */ - void gfxSystemUnlock(void); - - /** - * @brief Initialise a mutex to protect a region of code from other threads. - * - * @param[in] pmutex A pointer to the mutex - * - * @note Whilst a counting semaphore with a limit of 1 can be used for similiar purposes - * on many operating systems using a seperate mutex structure is more efficient. - * - * @api - */ - void gfxMutexInit(gfxMutex *pmutex); - - /** - * @brief Destroy a Mutex. - * - * @param[in] pmutex A pointer to the mutex - * - * @api - */ - void gfxMutexDestroy(gfxMutex *pmutex); - - /** - * @brief Enter the critical code region protected by the mutex. - * @details Blocks until there is no other thread in the critical region. - * - * @param[in] pmutex A pointer to the mutex - * - * @api - */ - void gfxMutexEnter(gfxMutex *pmutex); - - /** - * @brief Exit the critical code region protected by the mutex. - * @details May cause another thread waiting on the mutex to now be placed into the run queue. - * - * @param[in] pmutex A pointer to the mutex - * - * @api - */ - void gfxMutexExit(gfxMutex *pmutex); - - /** - * @brief Initialise a Counted Semaphore - * - * @param[in] psem A pointer to the semaphore - * @param[in] val The initial value of the semaphore - * @param[in] limit The maxmimum value of the semaphore - * - * @note Operations defined for counted semaphores: - * Signal: The semaphore counter is increased and if the result is non-positive then a waiting thread - * is queued for execution. Note that once the thread reaches "limit", further signals are - * ignored. - * Wait: The semaphore counter is decreased and if the result becomes negative the thread is queued - * in the semaphore and suspended. - * - * @api - */ - void gfxSemInit(gfxSem *psem, semcount_t val, semcount_t limit); - - /** - * @brief Destroy a Counted Semaphore - * - * @param[in] psem A pointer to the semaphore - * - * @note Any threads waiting on the semaphore will be released - * - * @api - */ - void gfxSemDestroy(gfxSem *psem); - - /** - * @brief Wait on a semaphore - * @details The semaphore counter is decreased and if the result becomes negative the thread waits for it to become - * non-negative again - * @return FALSE if the wait timeout occurred otherwise TRUE - * - * @param[in] psem A pointer to the semaphore - * @param[in] ms The maximum time to wait for the semaphore - * - * @api - */ - bool_t gfxSemWait(gfxSem *psem, delaytime_t ms); - - /** - * @brief Test if a wait on a semaphore can be satisfied immediately - * @details Equivalent to @p gfxSemWait(psem, TIME_IMMEDIATE) except it can be called at interrupt level - * @return FALSE if the wait would occur occurred otherwise TRUE - * - * @param[in] psem A pointer to the semaphore - * - * @iclass - * @api - */ - bool_t gfxSemWaitI(gfxSem *psem); - - /** - * @brief Signal a semaphore - * @details The semaphore counter is increased and if the result is non-positive then a waiting thread - * is queued for execution. Note that once the thread reaches "limit", further signals are - * ignored. - * - * @param[in] psem A pointer to the semaphore - * - * @api - */ - void gfxSemSignal(gfxSem *psem); - - /** - * @brief Signal a semaphore - * @details The semaphore counter is increased and if the result is non-positive then a waiting thread - * is queued for execution. Note that once the thread reaches "limit", further signals are - * ignored. - * - * @param[in] psem A pointer to the semaphore - * - * @iclass - * @api - */ - void gfxSemSignalI(gfxSem *psem); - - /** - * @brief Get the current semaphore count - * @return The current semaphore count - * - * @param[in] psem A pointer to the semaphore - * - * @api - */ - semcount_t gfxSemCounter(gfxSem *psem); - - /** - * @brief Get the current semaphore count - * @return The current semaphore count - * - * @param[in] psem A pointer to the semaphore - * - * @iclass - * @api - */ - semcount_t gfxSemCounterI(gfxSem *psem); - - /** - * @brief Start a new thread. - * @return Returns a thread handle if the thread was started, NULL on an error - * - * @param[in] stackarea A pointer to the area for the new threads stack or NULL to dynamically allocate it - * @param[in] stacksz The size of the thread stack. 0 means the default operating system size although this - * is only valid when stackarea is dynamically allocated. - * @param[in] prio The priority of the new thread - * @param[in] fn The function the new thread will run - * @param[in] param A parameter to pass the thread function. - * - * @api - */ - gfxThreadHandle gfxThreadCreate(void *stackarea, size_t stacksz, threadpriority_t prio, DECLARE_THREAD_FUNCTION((*fn),p), void *param); - - /** - * @brief Wait for a thread to finish. - * @return Returns the thread exit code. - * - * @param[in] thread The Thread Handle - * - * @note This will also close the thread handle as it is no longer useful - * once the thread has ended. - * @api - */ - threadreturn_t gfxThreadWait(gfxThreadHandle thread); - - /** - * @brief Get the current thread handle. - * @return A thread handle - * - * @api - */ - gfxThreadHandle gfxThreadMe(void); - - /** - * @brief Close the thread handle. - * - * @param[in] thread The Thread Handle - * - * @note This does not affect the thread, it just closes our handle to the thread. - * - * @api - */ - void gfxThreadClose(gfxThreadHandle thread); - - #ifdef __cplusplus - } - #endif - -/** - * All the above was just for the doxygen documentation. All the implementation of the above - * (without any of the documentation overheads) is in the files below. - */ -#elif GFX_USE_OS_RAWRTOS - #include "src/gos/gos_rawrtos.h" -#elif GFX_USE_OS_CHIBIOS - #include "src/gos/gos_chibios.h" -#elif GFX_USE_OS_FREERTOS - #include "src/gos/gos_freertos.h" -#elif GFX_USE_OS_WIN32 - #include "src/gos/gos_win32.h" -#elif GFX_USE_OS_LINUX - #include "src/gos/gos_linux.h" -#elif GFX_USE_OS_OSX - #include "src/gos/gos_osx.h" -#elif GFX_USE_OS_RAW32 - #include "src/gos/gos_raw32.h" -#elif GFX_USE_OS_ECOS - #include "src/gos/gos_ecos.h" -#else - #error "Your operating system is not supported yet" -#endif - -#endif /* _GOS_H */ -/** @} */ diff --git a/src/gos/sys_make.mk b/src/gos/sys_make.mk deleted file mode 100644 index a7b3dec6..00000000 --- a/src/gos/sys_make.mk +++ /dev/null @@ -1,9 +0,0 @@ -GFXSRC += $(GFXLIB)/src/gos/gos_chibios.c \ - $(GFXLIB)/src/gos/gos_freertos.c \ - $(GFXLIB)/src/gos/gos_win32.c \ - $(GFXLIB)/src/gos/gos_linux.c \ - $(GFXLIB)/src/gos/gos_osx.c \ - $(GFXLIB)/src/gos/gos_raw32.c \ - $(GFXLIB)/src/gos/gos_ecos.c \ - $(GFXLIB)/src/gos/gos_rawrtos.c - diff --git a/src/gos/sys_options.h b/src/gos/sys_options.h deleted file mode 100644 index 45e398dd..00000000 --- a/src/gos/sys_options.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gos/sys_options.h - * @brief GOS - Operating System options header file. - * - * @addtogroup GOS - * @{ - */ - -#ifndef _GOS_OPTIONS_H -#define _GOS_OPTIONS_H - -/** - * @name The operating system to use. One (and only one) of these must be defined. - * @{ - */ - /** - * @brief Use ChibiOS - * @details Defaults to FALSE - */ - #ifndef GFX_USE_OS_CHIBIOS - #define GFX_USE_OS_CHIBIOS FALSE - #endif - /** - * @brief Use FreeRTOS - * @details Defaults to FALSE - */ - #ifndef GFX_USE_OS_FREERTOS - #define GFX_USE_OS_FREERTOS FALSE - #endif - /** - * @brief Use Win32 - * @details Defaults to FALSE - */ - #ifndef GFX_USE_OS_WIN32 - #define GFX_USE_OS_WIN32 FALSE - #endif - /** - * @brief Use a linux based system running X11 - * @details Defaults to FALSE - */ - #ifndef GFX_USE_OS_LINUX - #define GFX_USE_OS_LINUX FALSE - #endif - /** - * @brief Use a Mac OS-X based system - * @details Defaults to FALSE - */ - #ifndef GFX_USE_OS_OSX - #define GFX_USE_OS_OSX FALSE - #endif - /** - * @brief Use a Raw 32 bit CPU based system - * @details Defaults to FALSE - */ - #ifndef GFX_USE_OS_RAW32 - #define GFX_USE_OS_RAW32 FALSE - #endif - /** - * @brief Use a eCos - * @details Defaults to FALSE - */ - #ifndef GFX_USE_OS_ECOS - #define GFX_USE_OS_ECOS FALSE - #endif -/** - * @} - * - * @name GOS Optional Parameters - * @{ - */ - /** - * @brief Should uGFX avoid initializing the operating system - * @details Defaults to FALSE - * @note This is not relevant to all operating systems eg Win32 never initializes the - * operating system as uGFX runs as an application outside the boot process. - * @note Operating system initialization is not necessarily implemented for all - * operating systems yet even when it is relevant. These operating systems - * will display a compile warning reminding you to initialize the operating - * system in your application code. Note that on these operating systems the - * demo applications will not work without modification. - */ - #ifndef GFX_NO_OS_INIT - #define GFX_NO_OS_INIT FALSE - #endif - /** - * @brief Should uGFX stuff be added to the FreeRTOS+Tracer - * @details Defaults to FALSE - */ - #ifndef GFX_FREERTOS_USE_TRACE - #define GFX_FREERTOS_USE_TRACE FALSE - #endif - /** - * @brief How much RAM should uGFX use for the heap - * @details Defaults to 0. Only valid with GFX_USE_OS_RAW32 - * @note If 0 then the standard C runtime malloc(), free() and realloc() - * are used. - * @note If it is non-zero then this is the number of bytes of RAM - * to use for the heap (gfxAlloc() and gfxFree()). No C - * runtime routines will be used and a new routine @p gfxAddHeapBlock() - * is added allowing the user to add extra memory blocks to the heap. - */ - #ifndef GOS_RAW_HEAP_SIZE - #define GOS_RAW_HEAP_SIZE 0 - #endif -/** @} */ - -#endif /* _GOS_OPTIONS_H */ -/** @} */ diff --git a/src/gos/sys_rules.h b/src/gos/sys_rules.h deleted file mode 100644 index 6d6c7845..00000000 --- a/src/gos/sys_rules.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gos/sys_rules.h - * @brief GOS safety rules header file. - * - * @addtogroup GOS - * @{ - */ - -#ifndef _GOS_RULES_H -#define _GOS_RULES_H - -#if !GFX_USE_OS_CHIBIOS && !GFX_USE_OS_WIN32 && !GFX_USE_OS_LINUX && !GFX_USE_OS_OSX && !GFX_USE_OS_RAW32 && !GFX_USE_OS_FREERTOS && !GFX_USE_OS_ECOS && !GFX_USE_OS_RAWRTOS - #if GFX_DISPLAY_RULE_WARNINGS - #warning "GOS: No Operating System has been defined. ChibiOS (GFX_USE_OS_CHIBIOS) has been turned on for you." - #endif - #undef GFX_USE_OS_CHIBIOS - #define GFX_USE_OS_CHIBIOS TRUE -#endif - -#if GFX_USE_OS_CHIBIOS + GFX_USE_OS_WIN32 + GFX_USE_OS_LINUX + GFX_USE_OS_OSX + GFX_USE_OS_RAW32 + GFX_USE_OS_FREERTOS + GFX_USE_OS_ECOS + GFX_USE_OS_RAWRTOS != 1 * TRUE - #error "GOS: More than one operation system has been defined as TRUE." -#endif - -#if GFX_FREERTOS_USE_TRACE && !GFX_USE_OS_FREERTOS - #error "GOS: GFX_FREERTOS_USE_TRACE is only available for the FreeRTOS port." -#endif - -#endif /* _GOS_RULES_H */ -/** @} */ diff --git a/src/gqueue/gqueue.c b/src/gqueue/gqueue.c new file mode 100644 index 00000000..1205143f --- /dev/null +++ b/src/gqueue/gqueue.c @@ -0,0 +1,464 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +#include "gfx.h" + +#if GFX_USE_GQUEUE + +#if GQUEUE_NEED_BUFFERS + static gfxQueueGSync bufferFreeList; +#endif + +void _gqueueInit(void) +{ + #if GQUEUE_NEED_BUFFERS + gfxQueueGSyncInit(&bufferFreeList); + #endif +} + +void _gqueueDeinit(void) +{ +} + +#if GQUEUE_NEED_ASYNC + void gfxQueueASyncInit(gfxQueueASync *pqueue) { + pqueue->head = pqueue->tail = 0; + } + + gfxQueueASyncItem *gfxQueueASyncGet(gfxQueueASync *pqueue) { + gfxQueueASyncItem *pi; + + // This is just a shortcut to speed execution + if (!pqueue->head) + return 0; + + gfxSystemLock(); + pi = gfxQueueASyncGetI(pqueue); + gfxSystemUnlock(); + + return pi; + } + gfxQueueASyncItem *gfxQueueASyncGetI(gfxQueueASync *pqueue) { + gfxQueueASyncItem *pi; + + if ((pi = pqueue->head)) { + pqueue->head = pi->next; + pi->next = 0; + } + + return pi; + } + + void gfxQueueASyncPut(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem) { + gfxSystemLock(); + gfxQueueASyncPutI(pqueue, pitem); + gfxSystemUnlock(); + } + void gfxQueueASyncPutI(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem) { + if (!pitem) return; // Safety + pitem->next = 0; + if (!pqueue->head) { + pqueue->head = pqueue->tail = pitem; + } else { + pqueue->tail->next = pitem; + pqueue->tail = pitem; + } + } + + void gfxQueueASyncPush(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem) { + gfxSystemLock(); + gfxQueueASyncPushI(pqueue, pitem); + gfxSystemUnlock(); + } + void gfxQueueASyncPushI(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem) { + if (!pitem) return; // Safety + pitem->next = pqueue->head; + pqueue->head = pitem; + if (!pitem->next) + pqueue->tail = pitem; + } + + void gfxQueueASyncInsert(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem, gfxQueueASyncItem *pafter) { + gfxSystemLock(); + gfxQueueASyncInsertI(pqueue, pitem, pafter); + gfxSystemUnlock(); + } + void gfxQueueASyncInsertI(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem, gfxQueueASyncItem *pafter) { + if (!pitem) return; // Safety + + if (pafter && gfxQueueASyncIsInI(pqueue, pafter)) { + pitem->next = pafter->next; + pafter->next = pitem; + if (pqueue->tail == pafter) + pqueue->tail = pitem; + } else { + pitem->next = 0; + if (!pqueue->head) { + pqueue->head = pqueue->tail = pitem; + } else { + pqueue->tail->next = pitem; + pqueue->tail = pitem; + } + } + } + + void gfxQueueASyncRemove(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem) { + gfxSystemLock(); + gfxQueueASyncRemoveI(pqueue, pitem); + gfxSystemUnlock(); + } + void gfxQueueASyncRemoveI(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem) { + gfxQueueASyncItem *pi; + + if (!pitem) return; // Safety + if (pqueue->head) { + if (pqueue->head == pitem) { + pqueue->head = pitem->next; + pitem->next = 0; + } else { + for(pi = pqueue->head; pi->next; pi = pi->next) { + if (pi->next == pitem) { + pi->next = pitem->next; + if (pqueue->tail == pitem) + pqueue->tail = pi; + pitem->next = 0; + break; + } + } + } + } + } + + bool_t gfxQueueASyncIsIn(gfxQueueASync *pqueue, const gfxQueueASyncItem *pitem) { + bool_t res; + + gfxSystemLock(); + res = gfxQueueASyncIsInI(pqueue, pitem); + gfxSystemUnlock(); + + return res; + } + bool_t gfxQueueASyncIsInI(gfxQueueASync *pqueue, const gfxQueueASyncItem *pitem) { + gfxQueueASyncItem *pi; + + for(pi = pqueue->head; pi; pi = pi->next) { + if (pi == pitem) + return TRUE; + } + return FALSE; + } +#endif + +#if GQUEUE_NEED_GSYNC + void gfxQueueGSyncInit(gfxQueueGSync *pqueue) { + pqueue->head = pqueue->tail = 0; + gfxSemInit(&pqueue->sem, 0, MAX_SEMAPHORE_COUNT); + } + void gfxQueueGSyncDeinit(gfxQueueGSync *pqueue) { + pqueue->head = pqueue->tail = 0; + gfxSemDestroy(&pqueue->sem); + } + + gfxQueueGSyncItem *gfxQueueGSyncGet(gfxQueueGSync *pqueue, delaytime_t ms) { + gfxQueueGSyncItem *pi; + + if (!gfxSemWait(&pqueue->sem, ms)) + return 0; + + gfxSystemLock(); + pi = pqueue->head; + pqueue->head = pi->next; + pi->next = 0; + gfxSystemUnlock(); + + return pi; + } + gfxQueueGSyncItem *gfxQueueGSyncGetI(gfxQueueGSync *pqueue) { + gfxQueueGSyncItem *pi; + + if (!gfxSemWaitI(&pqueue->sem)) + return 0; + + pi = pqueue->head; + pqueue->head = pi->next; + pi->next = 0; + return pi; + } + + void gfxQueueGSyncPut(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem) { + gfxSystemLock(); + gfxQueueGSyncPutI(pqueue, pitem); + gfxSystemUnlock(); + } + void gfxQueueGSyncPutI(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem) { + if (!pitem) return; // Safety + pitem->next = 0; + if (!pqueue->head) { + pqueue->head = pqueue->tail = pitem; + } else { + pqueue->tail->next = pitem; + pqueue->tail = pitem; + } + gfxSemSignalI(&pqueue->sem); + } + + void gfxQueueGSyncPush(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem) { + gfxSystemLock(); + gfxQueueGSyncPushI(pqueue, pitem); + gfxSystemUnlock(); + } + void gfxQueueGSyncPushI(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem) { + if (!pitem) return; // Safety + pitem->next = pqueue->head; + pqueue->head = pitem; + if (!pitem->next) + pqueue->tail = pitem; + gfxSemSignalI(&pqueue->sem); + } + + void gfxQueueGSyncInsert(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem, gfxQueueASyncItem *pafter) { + gfxSystemLock(); + gfxQueueGSyncInsertI(pqueue, pitem, pafter); + gfxSystemUnlock(); + } + void gfxQueueGSyncInsertI(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem, gfxQueueASyncItem *pafter) { + if (!pitem) return; // Safety + + if (pafter && gfxQueueGSyncIsInI(pqueue, pafter)) { + pitem->next = pafter->next; + pafter->next = pitem; + if (pqueue->tail == pafter) + pqueue->tail = pitem; + } else { + pitem->next = 0; + if (!pqueue->head) { + pqueue->head = pqueue->tail = pitem; + } else { + pqueue->tail->next = pitem; + pqueue->tail = pitem; + } + } + } + + void gfxQueueGSyncRemove(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem) { + gfxSystemLock(); + gfxQueueGSyncRemoveI(pqueue, pitem); + gfxSystemUnlock(); + } + void gfxQueueGSyncRemoveI(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem) { + gfxQueueGSyncItem *pi; + + if (!pitem) return; // Safety + if (pqueue->head) { + if (pqueue->head == pitem) { + pqueue->head = pitem->next; + pitem->next = 0; + } else { + for(pi = pqueue->head; pi->next; pi = pi->next) { + if (pi->next == pitem) { + pi->next = pitem->next; + if (pqueue->tail == pitem) + pqueue->tail = pi; + pitem->next = 0; + break; + } + } + } + } + } + + bool_t gfxQueueGSyncIsIn(gfxQueueGSync *pqueue, const gfxQueueGSyncItem *pitem) { + bool_t res; + + gfxSystemLock(); + res = gfxQueueGSyncIsInI(pqueue, pitem); + gfxSystemUnlock(); + + return res; + } + bool_t gfxQueueGSyncIsInI(gfxQueueGSync *pqueue, const gfxQueueGSyncItem *pitem) { + gfxQueueGSyncItem *pi; + + for(pi = pqueue->head; pi; pi = pi->next) { + if (pi == pitem) + return TRUE; + } + return FALSE; + } +#endif + +#if GQUEUE_NEED_FSYNC + void gfxQueueFSyncInit(gfxQueueFSync *pqueue) { + pqueue->head = pqueue->tail = 0; + gfxSemInit(&pqueue->sem, 0, MAX_SEMAPHORE_COUNT); + } + void gfxQueueFSyncDeinit(gfxQueueGSync *pqueue) { + while(gfxQueueFSyncGet(pqueue, TIME_IMMEDIATE)); + pqueue->head = pqueue->tail = 0; + gfxSemDestroy(&pqueue->sem); + } + + gfxQueueFSyncItem *gfxQueueFSyncGet(gfxQueueFSync *pqueue, delaytime_t ms) { + gfxQueueFSyncItem *pi; + + if (!gfxSemWait(&pqueue->sem, ms)) + return 0; + + gfxSystemLock(); + pi = pqueue->head; + pqueue->head = pi->next; + pi->next = 0; + gfxSystemUnlock(); + + gfxSemSignal(&pi->sem); + gfxSemDestroy(&pi->sem); + + return pi; + } + + bool_t gfxQueueFSyncPut(gfxQueueFSync *pqueue, gfxQueueFSyncItem *pitem, delaytime_t ms) { + if (!pitem) return; // Safety + gfxSemInit(&pitem->sem, 0, 1); + pitem->next = 0; + + gfxSystemLock(); + if (!pqueue->head) { + pqueue->head = pqueue->tail = pitem; + } else { + pqueue->tail->next = pitem; + pqueue->tail = pitem; + } + gfxSystemUnlock(); + + gfxSemSignal(&pqueue->sem); + + return gfxSemWait(&pitem->sem, ms); + } + + bool_t gfxQueueFSyncPush(gfxQueueFSync *pqueue, gfxQueueFSyncItem *pitem, delaytime_t ms) { + if (!pitem) return; // Safety + gfxSemInit(&pitem->sem, 0, 1); + + gfxSystemLock(); + pitem->next = pqueue->head; + pqueue->head = pitem; + if (!pitem->next) + pqueue->tail = pitem; + gfxSystemUnlock(); + + gfxSemSignal(&pqueue->sem); + + return gfxSemWait(&pitem->sem, ms); + } + + bool_t gfxQueueFSyncInsert(gfxQueueFSync *pqueue, gfxQueueFSyncItem *pitem, gfxQueueASyncItem *pafter, delaytime_t ms) { + if (!pitem) return; // Safety + gfxSemInit(&pitem->sem, 0, 1); + + gfxSystemLock(); + if (pafter && gfxQueueGSyncIsInI(pqueue, pafter)) { + pitem->next = pafter->next; + pafter->next = pitem; + if (pqueue->tail == pafter) + pqueue->tail = pitem; + } else { + pitem->next = 0; + if (!pqueue->head) { + pqueue->head = pqueue->tail = pitem; + } else { + pqueue->tail->next = pitem; + pqueue->tail = pitem; + } + } + gfxSystemUnlock(); + + gfxSemSignal(&pqueue->sem); + + return gfxSemWait(&pitem->sem, ms); + + } + + void gfxQueueFSyncRemove(gfxQueueFSync *pqueue, gfxQueueFSyncItem *pitem) { + gfxQueueFSyncItem *pi; + + if (!pitem) return; // Safety + gfxSystemLock(); + if (pqueue->head) { + if (pqueue->head == pitem) { + pqueue->head = pitem->next; + found: + pitem->next = 0; + gfxSystemUnlock(); + gfxSemSignal(&pitem->sem); + gfxSemDestroy(&pitem->sem); + return; + } + for(pi = pqueue->head; pi->next; pi = pi->next) { + if (pi->next == pitem) { + pi->next = pitem->next; + if (pqueue->tail == pitem) + pqueue->tail = pi; + goto found; + } + } + } + gfxSystemUnlock(); + } + + bool_t gfxQueueFSyncIsIn(gfxQueueFSync *pqueue, const gfxQueueFSyncItem *pitem) { + bool_t res; + + gfxSystemLock(); + res = gfxQueueFSyncIsInI(pqueue, pitem); + gfxSystemUnlock(); + + return res; + } + bool_t gfxQueueFSyncIsInI(gfxQueueFSync *pqueue, const gfxQueueFSyncItem *pitem) { + gfxQueueASyncItem *pi; + + for(pi = pqueue->head; pi; pi = pi->next) { + if (pi == pitem) + return TRUE; + } + return FALSE; + } +#endif + +#if GQUEUE_NEED_BUFFERS + bool_t gfxBufferAlloc(unsigned num, size_t size) { + GDataBuffer *pd; + + if (num < 1) + return FALSE; + + // Round up to a multiple of 4 to prevent problems with structure alignment + size = (size + 3) & ~0x03; + + // Allocate the memory + if (!(pd = gfxAlloc((size+sizeof(GDataBuffer)) * num))) + return FALSE; + + // Add each of them to our free list + for(;num--; pd = (GDataBuffer *)((char *)(pd+1)+size)) { + pd->size = size; + gfxBufferRelease(pd); + } + + return TRUE; + } + + void gfxBufferRelease(GDataBuffer *pd) { gfxQueueGSyncPut(&bufferFreeList, (gfxQueueGSyncItem *)pd); } + void gfxBufferReleaseI(GDataBuffer *pd) { gfxQueueGSyncPutI(&bufferFreeList, (gfxQueueGSyncItem *)pd); } + GDataBuffer *gfxBufferGet(delaytime_t ms) { return (GDataBuffer *)gfxQueueGSyncGet(&bufferFreeList, ms); } + GDataBuffer *gfxBufferGetI(void) { return (GDataBuffer *)gfxQueueGSyncGetI(&bufferFreeList); } + bool_t gfxBufferIsAvailable(void) { return bufferFreeList.head != 0; } + +#endif + + +#endif /* GFX_USE_GQUEUE */ diff --git a/src/gqueue/gqueue.h b/src/gqueue/gqueue.h new file mode 100644 index 00000000..924024b7 --- /dev/null +++ b/src/gqueue/gqueue.h @@ -0,0 +1,412 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gqueue/gqueue.h + * @brief GQUEUE header file. + * + * @addtogroup GQUEUE + * + * @brief Module which provides queue management (only internally used) + * + * @details There are 3 types of queues: + * <ul><li><b>Asynchronous Queues (ASync) </b> - Queue operations never block</li> + * <li><b>Get Synchronous Queues (GSync) </b> - Queue Get operations block until something is placed in the Queue</li> + * <li><b>Put Synchronous Queues (PSync)</b> - Queue Put operations block until the element is removed from the Queue</li> + * <li><b>Fully Synchronous Queues (FSync)</b> - Queue GET and Put operations block</li> + * </ul> + * We need 4 types of queues even though fully synchronous queues support all operations including asynchronous + * operations because fully synchronous queues have the highest storage requirements. The other queue types are + * optimizations. Efficiency IS important to use (particularly RAM efficiency). + * In practice we only implement ASync, GSync and FSync queues as PSync queues are of dubious value. + * <br> + * We also define GDataBuffer which is a data buffer that supports being queued. + * @{ + */ + +#ifndef _GQUEUE_H +#define _GQUEUE_H + +#if GFX_USE_GQUEUE || defined(__DOXYGEN__) + +/** + * @brief A queue item + * @{ + */ +typedef struct gfxQueueASyncItem { + struct gfxQueueASyncItem *next; +} gfxQueueASyncItem, gfxQueueGSyncItem; + +typedef struct gfxQueueFSyncItem { + struct gfxQueueFSyncItem *next; + gfxSem sem; +} gfxQueueFSyncItem; +/** @} */ + +/** + * @brief A queue + * @{ + */ +typedef struct gfxQueueASync { + gfxQueueASyncItem *head; + gfxQueueASyncItem *tail; +} gfxQueueASync; + +typedef struct gfxQueueGSync { + gfxQueueGSyncItem *head; + gfxQueueGSyncItem *tail; + gfxSem sem; +} gfxQueueGSync; + +typedef struct gfxQueueFSync { + gfxQueueFSyncItem *head; + gfxQueueFSyncItem *tail; + gfxSem sem; +} gfxQueueFSync; +/** @} */ + +/** + * @brief A Data Buffer Queue + * @note This structure is followed immediately by the data itself. + * When allocating the buffers for the data put this structure + * at the beginning of the buffer. + */ +typedef struct GDataBuffer { + gfxQueueGSyncItem next; // @< Used for queueing the buffers + size_t size; // @< The size of the buffer area following this structure (in bytes) + size_t len; // @< The length of the data in the buffer area (in bytes) +} GDataBuffer; + +/*===========================================================================*/ +/* Function declarations. */ +/*===========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name Initialisation functions + * @brief Initialise a queue. + * + * @param[in] pqueue A pointer to the queue + * + * @note Whilst queues are normally FIFO, a GFX queue also supports push and pop operations. + * A pop operation is the same as normal get from the queue but a push places the item + * at the head of the queue instead of the tail (as a put would). + * + * @api + * @{ + */ +void gfxQueueASyncInit(gfxQueueASync *pqueue); +void gfxQueueGSyncInit(gfxQueueGSync *pqueue); +void gfxQueueFSyncInit(gfxQueueFSync *pqueue); +/** @} */ + +/** + * @name Deinitialisation functions + * @brief De-Initialise a queue. + * + * @param[in] pqueue A pointer to the queue + * + * @api + * @{ + */ +#define gfxQueueASyncDeinit(pqueue) +void gfxQueueGSyncDeinit(gfxQueueGSync *pqueue); +void gfxQueueFSyncDeinit(gfxQueueFSync *pqueue); +/** @} */ + +/** + * @name Get() Functions + * @brief Get an item from the head of the queue (and remove it from the queue). + * @return NULL if the timeout expires before an item is available + * + * @param[in] pqueue A pointer to the queue + * @param[in] ms The maxmimum time to wait for an item. For ASync queues this parameter is + * not specified as TIME_IMMEDIATE is assumed. + * + * @note The routines ending in "I" are interrupt/system/iclass level routines. + * + * @api + * @{ + */ +gfxQueueASyncItem *gfxQueueASyncGet(gfxQueueASync *pqueue); +gfxQueueASyncItem *gfxQueueASyncGetI(gfxQueueASync *pqueue); +gfxQueueGSyncItem *gfxQueueGSyncGet(gfxQueueGSync *pqueue, delaytime_t ms); +gfxQueueGSyncItem *gfxQueueGSyncGetI(gfxQueueGSync *pqueue); +gfxQueueFSyncItem *gfxQueueFSyncGet(gfxQueueFSync *pqueue, delaytime_t ms); +/** @} */ + +/** + * @name Put() Functions + * @brief Put an item on the end of the queue. + * @return none for ASync and GSync queues; For FSync queues - FALSE on timeout, otherwise TRUE + * + * @param[in] pqueue A pointer to the queue + * @param[in] pitem A pointer to the queue item + * @param[in] ms The maxmimum time to wait for an item to be removed from the queue (only for FSync queues) + * + * @note FSync: Use a delay time of TIME_IMMEDIATE if you don't want to wait until the + * item is removed from the queue. Note that even if the timeout occurs - the item + * remains in the queue. + * @note The routines ending in "I" are interrupt/system/iclass level routines. + * + * @api + * @{ + */ +void gfxQueueASyncPut(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem); +void gfxQueueASyncPutI(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem); +void gfxQueueGSyncPut(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem); +void gfxQueueGSyncPutI(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem); +bool_t gfxQueueFSyncPut(gfxQueueFSync *pqueue, gfxQueueFSyncItem *pitem, delaytime_t ms); +/** @} */ + +/** + * @name Pop() Functions + * @brief Pop an item from the head of the queue (and remove it from the queue). + * @details This is exactly the same as the Get operation above. + * + * @api + * @{ + */ +#define gfxQueueASyncPop(pqueue) gfxQueueASyncGet(pqueue) +#define gfxQueueASyncPopI(pqueue) gfxQueueASyncGetI(pqueue) +#define gfxQueueGSyncPop(pqueue, ms) gfxQueueGSyncGet(pqueue, ms) +#define gfxQueueFSyncPop(pqueue, ms) gfxQueueFSyncGet(pqueue, ms) +/** @} */ + +/** + * @name Push() Functions + * @brief Push an item into the start of the queue. + * @return none for ASync and GSync queues; For FSync queues - FALSE on timeout, otherwise TRUE + * + * @param[in] pqueue A pointer to the queue + * @param[in] pitem A pointer to the queue item + * @param[in] ms The maxmimum time to wait for an item to be popped (only for FSync queues) + * + * @note FSync: Use a delay time of TIME_IMMEDIATE if you don't want to wait until the + * item is removed from the queue. Note that even if the timeout occurs - the item + * remains in the queue. + * @note The routines ending in "I" are interrupt/system/iclass level routines. + * + * @api + * @{ + */ +void gfxQueueASyncPush(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem); +void gfxQueueASyncPushI(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem); +void gfxQueueGSyncPush(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem); +void gfxQueueGSyncPushI(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem); +bool_t gfxQueueFSyncPush(gfxQueueFSync *pqueue, gfxQueueFSyncItem *pitem, delaytime_t ms); +/** @} */ + +/** + * @name Insert() Functions + * @brief Insert an item on the queue after the specified item. + * @return none for ASync and GSync queues; For FSync queues - FALSE on timeout, otherwise TRUE + * + * @param[in] pqueue A pointer to the queue + * @param[in] pitem A pointer to the queue item + * @param[in] pafter A pointer to the queue item this new item must be inserted after. If NULL or + * pafter can't be found in the queue, it puts the new item at the end of the queue. + * @param[in] ms The maxmimum time to wait for an item to be removed from the queue (only for FSync queues) + * + * @note FSync: Use a delay time of TIME_IMMEDIATE if you don't want to wait until the + * item is removed from the queue. Note that even if the timeout occurs - the item + * remains in the queue. + * @note The routines ending in "I" are interrupt/system/iclass level routines. + * + * @api + * @{ + */ +void gfxQueueASyncInsert(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem, gfxQueueASyncItem *pafter); +void gfxQueueASyncInsertI(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem, gfxQueueASyncItem *pafter); +void gfxQueueGSyncInsert(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem, gfxQueueASyncItem *pafter); +void gfxQueueGSyncInsertI(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem, gfxQueueASyncItem *pafter); +bool_t gfxQueueFSyncInsert(gfxQueueFSync *pqueue, gfxQueueFSyncItem *pitem, gfxQueueASyncItem *pafter, delaytime_t ms); +/** @} */ + +/** + * @name Remove() Functions + * @brief Remove an item from the queue. + * @note Removes the specified item from the queue where-ever it is in the queue + * + * @param[in] pqueue A pointer to the queue + * @param[in] pitem A pointer to the queue item + * + * @note If the item isn't in the queue the routine just returns. + * @note If a process is waiting on the Put/Push operation for the item, that process + * will be signaled. + * @note The routines ending in "I" are interrupt/system/iclass level routines. + * + * @api + * @{ + */ +void gfxQueueASyncRemove(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem); +void gfxQueueASyncRemoveI(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem); +void gfxQueueGSyncRemove(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem); +void gfxQueueGSyncRemoveI(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem); +void gfxQueueFSyncRemove(gfxQueueFSync *pqueue, gfxQueueFSyncItem *pitem); +/** @} */ + +/** + * @name isEmpty() Functions + * @brief Is the queue empty? + * @return TRUE if the queue is empty + * + * @param[in] pqueue A pointer to the queue + * + * @note The routines ending in "I" are interrupt/system/iclass level routines. + * + * @api + * @{ + */ +#define gfxQueueASyncIsEmpty(pqueue) ((pqueue)->head == 0) +#define gfxQueueASyncIsEmptyI(pqueue) ((pqueue)->head == 0) +#define gfxQueueGSyncIsEmpty(pqueue) ((pqueue)->head == 0) +#define gfxQueueGSyncIsEmptyI(pqueue) ((pqueue)->head == 0) +#define gfxQueueFSyncIsEmpty(pqueue) ((pqueue)->head == 0) +#define gfxQueueFSyncIsEmptyI(pqueue) ((pqueue)->head == 0) +/** @} */ + +/** + * @name IsInQueue() Functions + * @brief Is an item in the queue? + * @return TRUE if the item is in the queue? + * + * @param[in] pqueue A pointer to the queue + * @param[in] pitem A pointer to the queue item + * + * @note This operation may be expensive. + * @note The routines ending in "I" are interrupt/system/iclass level routines. + * + * @api + * @{ + */ +bool_t gfxQueueASyncIsIn(gfxQueueASync *pqueue, const gfxQueueASyncItem *pitem); +bool_t gfxQueueASyncIsInI(gfxQueueASync *pqueue, const gfxQueueASyncItem *pitem); +bool_t gfxQueueGSyncIsIn(gfxQueueGSync *pqueue, const gfxQueueGSyncItem *pitem); +bool_t gfxQueueGSyncIsInI(gfxQueueGSync *pqueue, const gfxQueueGSyncItem *pitem); +bool_t gfxQueueFSyncIsIn(gfxQueueFSync *pqueue, const gfxQueueFSyncItem *pitem); +bool_t gfxQueueFSyncIsInI(gfxQueueFSync *pqueue, const gfxQueueFSyncItem *pitem); +/** @} */ + +/** + * @name Peek() Functions + * @brief Get the first item from the head of the queue but do not remove it from the queue. + * @return NULL if no item is available. + * + * @param[in] pqueue A pointer to the queue + * + * @note This call does not block. + * @note This can be used as the first call to iterate all the elements in the queue. + * @note As that item is still on the queue, it should be treated as read-only. It could + * also be removed from the queue at any time by another thread (thereby altering the + * queue item). + * @note The routines ending in "I" are interrupt/system/iclass level routines. + * + * @api + * @{ + */ +#define gfxQueueASyncPeek(pqueue) ((const gfxQueueASyncItem *)((pqueue)->head)) +#define gfxQueueASyncPeekI(pqueue) ((const gfxQueueASyncItem *)((pqueue)->head)) +#define gfxQueueGSyncPeek(pqueue) ((const gfxQueueGSyncItem *)((pqueue)->head)) +#define gfxQueueGSyncPeekI(pqueue) ((const gfxQueueGSyncItem *)((pqueue)->head)) +#define gfxQueueFSyncPeek(pqueue) ((const gfxQueueFSyncItem *)((pqueue)->head)) +#define gfxQueueFSyncPeekI(pqueue) ((const gfxQueueFSyncItem *)((pqueue)->head)) +/** @} */ + +/** + * @name Next() Functions + * @brief Get the next item in the queue (but do not remove it from the queue). + * @return NULL if no item is available. + * + * @param[in] pitem The previous item in the queue + * + * @note This call does not block. + * @note This can be used as subsequent calls to iterate all the elements in the queue. + * @note As that item is still on the queue, it should be treated as read-only. It could + * also be removed from the queue at any time by another thread (thereby altering the + * queue item). + * @note The routines ending in "I" are interrupt/system/iclass level routines. + * + * @api + * @{ + */ +#define gfxQueueASyncNext(pitem) ((const gfxQueueASyncItem *)((pitem)->next)) +#define gfxQueueASyncNextI(pitem) ((const gfxQueueASyncItem *)((pitem)->next)) +#define gfxQueueGSyncNext(pitem) ((const gfxQueueGSyncItem *)((pitem)->next)) +#define gfxQueueGSyncNextI(pitem) ((const gfxQueueGSyncItem *)((pitem)->next)) +#define gfxQueueFSyncNext(pitem) ((const gfxQueueFSyncItem *)((pitem)->next)) +#define gfxQueueFSyncNextI(pitem) ((const gfxQueueFSyncItem *)((pitem)->next)) +/** @} */ + +/** + * @name BufferAlloc() Functions + * @brief Allocate some buffers and put them on the free list + * @return TRUE is it succeeded. FALSE on allocation failure. + * + * @param[in] num The number of buffers to allocate + * @param[in] size The size (in bytes) of each buffer + * + * @api + * @{ + */ +bool_t gfxBufferAlloc(unsigned num, size_t size); +/** @} */ + +/** + * @name BufferIsAvailable() Functions + * @brief Is there one or more buffers currently available on the free list + * @return TRUE if there are buffers in the free list + * + * @api + * @{ + */ +bool_t gfxBufferIsAvailable(void); +/** @} */ + +/** + * @name BufferGet() Functions + * @brief Get a buffer from the free list + * @return A GDataBuffer pointer or NULL if the timeout is exceeded + * + * @param[in] ms The maximum amount of time in milliseconds to wait for a buffer if one is not available. + * + * @api + * @{ + */ +GDataBuffer *gfxBufferGet(delaytime_t ms); +GDataBuffer *gfxBufferGetI(void); +/** @} */ + +/** + * @name BufferRelease) Functions + * @brief Release a buffer back to the free list + * + * @param[in] pd The buffer to put (back) on the free-list. + * + * @note This call should be used to return any buffers that were taken from + * the free-list once they have been finished with. It can also be used + * to put new buffers onto the free-list. Just make sure the "size" field + * of the GDataBuffer structure has been filled in first. + * + * @api + * @{ + */ +void gfxBufferRelease(GDataBuffer *pd); +void gfxBufferReleaseI(GDataBuffer *pd); +/** @} */ + + +#ifdef __cplusplus +} +#endif + +#endif /* GFX_USE_GQUEUE */ +#endif /* _GQUEUE_H */ +/** @} */ diff --git a/src/gqueue/gqueue.mk b/src/gqueue/gqueue.mk new file mode 100644 index 00000000..ab8a0423 --- /dev/null +++ b/src/gqueue/gqueue.mk @@ -0,0 +1 @@ +GFXSRC += $(GFXLIB)/src/gqueue/gqueue.c diff --git a/src/gqueue/gqueue_gqueue.c b/src/gqueue/gqueue_gqueue.c deleted file mode 100644 index 5a50c64b..00000000 --- a/src/gqueue/gqueue_gqueue.c +++ /dev/null @@ -1,469 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gqueue/gqueue_gqueue.c - * @brief GQUEUE source file. - */ - -#include "gfx.h" - -#if GFX_USE_GQUEUE - -#if GQUEUE_NEED_BUFFERS - static gfxQueueGSync bufferFreeList; -#endif - -void _gqueueInit(void) -{ - #if GQUEUE_NEED_BUFFERS - gfxQueueGSyncInit(&bufferFreeList); - #endif -} - -void _gqueueDeinit(void) -{ -} - -#if GQUEUE_NEED_ASYNC - void gfxQueueASyncInit(gfxQueueASync *pqueue) { - pqueue->head = pqueue->tail = 0; - } - - gfxQueueASyncItem *gfxQueueASyncGet(gfxQueueASync *pqueue) { - gfxQueueASyncItem *pi; - - // This is just a shortcut to speed execution - if (!pqueue->head) - return 0; - - gfxSystemLock(); - pi = gfxQueueASyncGetI(pqueue); - gfxSystemUnlock(); - - return pi; - } - gfxQueueASyncItem *gfxQueueASyncGetI(gfxQueueASync *pqueue) { - gfxQueueASyncItem *pi; - - if ((pi = pqueue->head)) { - pqueue->head = pi->next; - pi->next = 0; - } - - return pi; - } - - void gfxQueueASyncPut(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem) { - gfxSystemLock(); - gfxQueueASyncPutI(pqueue, pitem); - gfxSystemUnlock(); - } - void gfxQueueASyncPutI(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem) { - if (!pitem) return; // Safety - pitem->next = 0; - if (!pqueue->head) { - pqueue->head = pqueue->tail = pitem; - } else { - pqueue->tail->next = pitem; - pqueue->tail = pitem; - } - } - - void gfxQueueASyncPush(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem) { - gfxSystemLock(); - gfxQueueASyncPushI(pqueue, pitem); - gfxSystemUnlock(); - } - void gfxQueueASyncPushI(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem) { - if (!pitem) return; // Safety - pitem->next = pqueue->head; - pqueue->head = pitem; - if (!pitem->next) - pqueue->tail = pitem; - } - - void gfxQueueASyncInsert(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem, gfxQueueASyncItem *pafter) { - gfxSystemLock(); - gfxQueueASyncInsertI(pqueue, pitem, pafter); - gfxSystemUnlock(); - } - void gfxQueueASyncInsertI(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem, gfxQueueASyncItem *pafter) { - if (!pitem) return; // Safety - - if (pafter && gfxQueueASyncIsInI(pqueue, pafter)) { - pitem->next = pafter->next; - pafter->next = pitem; - if (pqueue->tail == pafter) - pqueue->tail = pitem; - } else { - pitem->next = 0; - if (!pqueue->head) { - pqueue->head = pqueue->tail = pitem; - } else { - pqueue->tail->next = pitem; - pqueue->tail = pitem; - } - } - } - - void gfxQueueASyncRemove(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem) { - gfxSystemLock(); - gfxQueueASyncRemoveI(pqueue, pitem); - gfxSystemUnlock(); - } - void gfxQueueASyncRemoveI(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem) { - gfxQueueASyncItem *pi; - - if (!pitem) return; // Safety - if (pqueue->head) { - if (pqueue->head == pitem) { - pqueue->head = pitem->next; - pitem->next = 0; - } else { - for(pi = pqueue->head; pi->next; pi = pi->next) { - if (pi->next == pitem) { - pi->next = pitem->next; - if (pqueue->tail == pitem) - pqueue->tail = pi; - pitem->next = 0; - break; - } - } - } - } - } - - bool_t gfxQueueASyncIsIn(gfxQueueASync *pqueue, const gfxQueueASyncItem *pitem) { - bool_t res; - - gfxSystemLock(); - res = gfxQueueASyncIsInI(pqueue, pitem); - gfxSystemUnlock(); - - return res; - } - bool_t gfxQueueASyncIsInI(gfxQueueASync *pqueue, const gfxQueueASyncItem *pitem) { - gfxQueueASyncItem *pi; - - for(pi = pqueue->head; pi; pi = pi->next) { - if (pi == pitem) - return TRUE; - } - return FALSE; - } -#endif - -#if GQUEUE_NEED_GSYNC - void gfxQueueGSyncInit(gfxQueueGSync *pqueue) { - pqueue->head = pqueue->tail = 0; - gfxSemInit(&pqueue->sem, 0, MAX_SEMAPHORE_COUNT); - } - void gfxQueueGSyncDeinit(gfxQueueGSync *pqueue) { - pqueue->head = pqueue->tail = 0; - gfxSemDestroy(&pqueue->sem); - } - - gfxQueueGSyncItem *gfxQueueGSyncGet(gfxQueueGSync *pqueue, delaytime_t ms) { - gfxQueueGSyncItem *pi; - - if (!gfxSemWait(&pqueue->sem, ms)) - return 0; - - gfxSystemLock(); - pi = pqueue->head; - pqueue->head = pi->next; - pi->next = 0; - gfxSystemUnlock(); - - return pi; - } - gfxQueueGSyncItem *gfxQueueGSyncGetI(gfxQueueGSync *pqueue) { - gfxQueueGSyncItem *pi; - - if (!gfxSemWaitI(&pqueue->sem)) - return 0; - - pi = pqueue->head; - pqueue->head = pi->next; - pi->next = 0; - return pi; - } - - void gfxQueueGSyncPut(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem) { - gfxSystemLock(); - gfxQueueGSyncPutI(pqueue, pitem); - gfxSystemUnlock(); - } - void gfxQueueGSyncPutI(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem) { - if (!pitem) return; // Safety - pitem->next = 0; - if (!pqueue->head) { - pqueue->head = pqueue->tail = pitem; - } else { - pqueue->tail->next = pitem; - pqueue->tail = pitem; - } - gfxSemSignalI(&pqueue->sem); - } - - void gfxQueueGSyncPush(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem) { - gfxSystemLock(); - gfxQueueGSyncPushI(pqueue, pitem); - gfxSystemUnlock(); - } - void gfxQueueGSyncPushI(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem) { - if (!pitem) return; // Safety - pitem->next = pqueue->head; - pqueue->head = pitem; - if (!pitem->next) - pqueue->tail = pitem; - gfxSemSignalI(&pqueue->sem); - } - - void gfxQueueGSyncInsert(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem, gfxQueueASyncItem *pafter) { - gfxSystemLock(); - gfxQueueGSyncInsertI(pqueue, pitem, pafter); - gfxSystemUnlock(); - } - void gfxQueueGSyncInsertI(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem, gfxQueueASyncItem *pafter) { - if (!pitem) return; // Safety - - if (pafter && gfxQueueGSyncIsInI(pqueue, pafter)) { - pitem->next = pafter->next; - pafter->next = pitem; - if (pqueue->tail == pafter) - pqueue->tail = pitem; - } else { - pitem->next = 0; - if (!pqueue->head) { - pqueue->head = pqueue->tail = pitem; - } else { - pqueue->tail->next = pitem; - pqueue->tail = pitem; - } - } - } - - void gfxQueueGSyncRemove(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem) { - gfxSystemLock(); - gfxQueueGSyncRemoveI(pqueue, pitem); - gfxSystemUnlock(); - } - void gfxQueueGSyncRemoveI(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem) { - gfxQueueGSyncItem *pi; - - if (!pitem) return; // Safety - if (pqueue->head) { - if (pqueue->head == pitem) { - pqueue->head = pitem->next; - pitem->next = 0; - } else { - for(pi = pqueue->head; pi->next; pi = pi->next) { - if (pi->next == pitem) { - pi->next = pitem->next; - if (pqueue->tail == pitem) - pqueue->tail = pi; - pitem->next = 0; - break; - } - } - } - } - } - - bool_t gfxQueueGSyncIsIn(gfxQueueGSync *pqueue, const gfxQueueGSyncItem *pitem) { - bool_t res; - - gfxSystemLock(); - res = gfxQueueGSyncIsInI(pqueue, pitem); - gfxSystemUnlock(); - - return res; - } - bool_t gfxQueueGSyncIsInI(gfxQueueGSync *pqueue, const gfxQueueGSyncItem *pitem) { - gfxQueueGSyncItem *pi; - - for(pi = pqueue->head; pi; pi = pi->next) { - if (pi == pitem) - return TRUE; - } - return FALSE; - } -#endif - -#if GQUEUE_NEED_FSYNC - void gfxQueueFSyncInit(gfxQueueFSync *pqueue) { - pqueue->head = pqueue->tail = 0; - gfxSemInit(&pqueue->sem, 0, MAX_SEMAPHORE_COUNT); - } - void gfxQueueFSyncDeinit(gfxQueueGSync *pqueue) { - while(gfxQueueFSyncGet(pqueue, TIME_IMMEDIATE)); - pqueue->head = pqueue->tail = 0; - gfxSemDestroy(&pqueue->sem); - } - - gfxQueueFSyncItem *gfxQueueFSyncGet(gfxQueueFSync *pqueue, delaytime_t ms) { - gfxQueueFSyncItem *pi; - - if (!gfxSemWait(&pqueue->sem, ms)) - return 0; - - gfxSystemLock(); - pi = pqueue->head; - pqueue->head = pi->next; - pi->next = 0; - gfxSystemUnlock(); - - gfxSemSignal(&pi->sem); - gfxSemDestroy(&pi->sem); - - return pi; - } - - bool_t gfxQueueFSyncPut(gfxQueueFSync *pqueue, gfxQueueFSyncItem *pitem, delaytime_t ms) { - if (!pitem) return; // Safety - gfxSemInit(&pitem->sem, 0, 1); - pitem->next = 0; - - gfxSystemLock(); - if (!pqueue->head) { - pqueue->head = pqueue->tail = pitem; - } else { - pqueue->tail->next = pitem; - pqueue->tail = pitem; - } - gfxSystemUnlock(); - - gfxSemSignal(&pqueue->sem); - - return gfxSemWait(&pitem->sem, ms); - } - - bool_t gfxQueueFSyncPush(gfxQueueFSync *pqueue, gfxQueueFSyncItem *pitem, delaytime_t ms) { - if (!pitem) return; // Safety - gfxSemInit(&pitem->sem, 0, 1); - - gfxSystemLock(); - pitem->next = pqueue->head; - pqueue->head = pitem; - if (!pitem->next) - pqueue->tail = pitem; - gfxSystemUnlock(); - - gfxSemSignal(&pqueue->sem); - - return gfxSemWait(&pitem->sem, ms); - } - - bool_t gfxQueueFSyncInsert(gfxQueueFSync *pqueue, gfxQueueFSyncItem *pitem, gfxQueueASyncItem *pafter, delaytime_t ms) { - if (!pitem) return; // Safety - gfxSemInit(&pitem->sem, 0, 1); - - gfxSystemLock(); - if (pafter && gfxQueueGSyncIsInI(pqueue, pafter)) { - pitem->next = pafter->next; - pafter->next = pitem; - if (pqueue->tail == pafter) - pqueue->tail = pitem; - } else { - pitem->next = 0; - if (!pqueue->head) { - pqueue->head = pqueue->tail = pitem; - } else { - pqueue->tail->next = pitem; - pqueue->tail = pitem; - } - } - gfxSystemUnlock(); - - gfxSemSignal(&pqueue->sem); - - return gfxSemWait(&pitem->sem, ms); - - } - - void gfxQueueFSyncRemove(gfxQueueFSync *pqueue, gfxQueueFSyncItem *pitem) { - gfxQueueFSyncItem *pi; - - if (!pitem) return; // Safety - gfxSystemLock(); - if (pqueue->head) { - if (pqueue->head == pitem) { - pqueue->head = pitem->next; - found: - pitem->next = 0; - gfxSystemUnlock(); - gfxSemSignal(&pitem->sem); - gfxSemDestroy(&pitem->sem); - return; - } - for(pi = pqueue->head; pi->next; pi = pi->next) { - if (pi->next == pitem) { - pi->next = pitem->next; - if (pqueue->tail == pitem) - pqueue->tail = pi; - goto found; - } - } - } - gfxSystemUnlock(); - } - - bool_t gfxQueueFSyncIsIn(gfxQueueFSync *pqueue, const gfxQueueFSyncItem *pitem) { - bool_t res; - - gfxSystemLock(); - res = gfxQueueFSyncIsInI(pqueue, pitem); - gfxSystemUnlock(); - - return res; - } - bool_t gfxQueueFSyncIsInI(gfxQueueFSync *pqueue, const gfxQueueFSyncItem *pitem) { - gfxQueueASyncItem *pi; - - for(pi = pqueue->head; pi; pi = pi->next) { - if (pi == pitem) - return TRUE; - } - return FALSE; - } -#endif - -#if GQUEUE_NEED_BUFFERS - bool_t gfxBufferAlloc(unsigned num, size_t size) { - GDataBuffer *pd; - - if (num < 1) - return FALSE; - - // Round up to a multiple of 4 to prevent problems with structure alignment - size = (size + 3) & ~0x03; - - // Allocate the memory - if (!(pd = gfxAlloc((size+sizeof(GDataBuffer)) * num))) - return FALSE; - - // Add each of them to our free list - for(;num--; pd = (GDataBuffer *)((char *)(pd+1)+size)) { - pd->size = size; - gfxBufferRelease(pd); - } - - return TRUE; - } - - void gfxBufferRelease(GDataBuffer *pd) { gfxQueueGSyncPut(&bufferFreeList, (gfxQueueGSyncItem *)pd); } - void gfxBufferReleaseI(GDataBuffer *pd) { gfxQueueGSyncPutI(&bufferFreeList, (gfxQueueGSyncItem *)pd); } - GDataBuffer *gfxBufferGet(delaytime_t ms) { return (GDataBuffer *)gfxQueueGSyncGet(&bufferFreeList, ms); } - GDataBuffer *gfxBufferGetI(void) { return (GDataBuffer *)gfxQueueGSyncGetI(&bufferFreeList); } - bool_t gfxBufferIsAvailable(void) { return bufferFreeList.head != 0; } - -#endif - - -#endif /* GFX_USE_GQUEUE */ diff --git a/src/gqueue/gqueue_options.h b/src/gqueue/gqueue_options.h new file mode 100644 index 00000000..a9f7302f --- /dev/null +++ b/src/gqueue/gqueue_options.h @@ -0,0 +1,59 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gqueue/gqueue_options.h + * @brief GQUEUE - Queue options header file. + * + * @addtogroup GQUEUE + * @{ + */ + +#ifndef _GQUEUE_OPTIONS_H +#define _GQUEUE_OPTIONS_H + +/** + * @name GQUEUE Functions to include. + * @{ + */ + /** + * @brief Enable Asynchronous Queues + * @details Defaults to FALSE + */ + #ifndef GQUEUE_NEED_ASYNC + #define GQUEUE_NEED_ASYNC FALSE + #endif + /** + * @brief Enable Get-Synchronous Queues + * @details Defaults to FALSE + */ + #ifndef GQUEUE_NEED_GSYNC + #define GQUEUE_NEED_GSYNC FALSE + #endif + /** + * @brief Enable Fully Synchronous Queues + * @details Defaults to FALSE + */ + #ifndef GQUEUE_NEED_FSYNC + #define GQUEUE_NEED_FSYNC FALSE + #endif + /** + * @brief Enable Queue-able Data Buffers + */ + #ifndef GQUEUE_NEED_BUFFERS + #define GQUEUE_NEED_BUFFERS FALSE + #endif +/** + * @} + * + * @name GQUEUE Optional Sizing Parameters + * @{ + */ +/** @} */ + +#endif /* _GQUEUE_OPTIONS_H */ +/** @} */ diff --git a/src/gqueue/gqueue_rules.h b/src/gqueue/gqueue_rules.h new file mode 100644 index 00000000..0bccb196 --- /dev/null +++ b/src/gqueue/gqueue_rules.h @@ -0,0 +1,30 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gqueue/gqueue_rules.h + * @brief GQUEUE safety rules header file. + * + * @addtogroup GQUEUE + * @{ + */ + +#ifndef _GQUEUE_RULES_H +#define _GQUEUE_RULES_H + +#if GFX_USE_GQUEUE + #if GQUEUE_NEED_BUFFERS && !GQUEUE_NEED_GSYNC + #if GFX_DISPLAY_RULE_WARNINGS + #warning "GQUEUE: GQUEUE_NEED_GSYNC is required if GQUEUE_NEED_BUFFERS is TRUE. It has been turned on for you." + #endif + #undef GQUEUE_NEED_GSYNC + #define GQUEUE_NEED_GSYNC TRUE + #endif +#endif + +#endif /* _GQUEUE_RULES_H */ +/** @} */ diff --git a/src/gqueue/sys_defs.h b/src/gqueue/sys_defs.h deleted file mode 100644 index cba22588..00000000 --- a/src/gqueue/sys_defs.h +++ /dev/null @@ -1,412 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gqueue/sys_defs.h - * @brief GQUEUE header file. - * - * @addtogroup GQUEUE - * - * @brief Module which provides queue management (only internally used) - * - * @details There are 3 types of queues: - * <ul><li><b>Asynchronous Queues (ASync) </b> - Queue operations never block</li> - * <li><b>Get Synchronous Queues (GSync) </b> - Queue Get operations block until something is placed in the Queue</li> - * <li><b>Put Synchronous Queues (PSync)</b> - Queue Put operations block until the element is removed from the Queue</li> - * <li><b>Fully Synchronous Queues (FSync)</b> - Queue GET and Put operations block</li> - * </ul> - * We need 4 types of queues even though fully synchronous queues support all operations including asynchronous - * operations because fully synchronous queues have the highest storage requirements. The other queue types are - * optimizations. Efficiency IS important to use (particularly RAM efficiency). - * In practice we only implement ASync, GSync and FSync queues as PSync queues are of dubious value. - * <br> - * We also define GDataBuffer which is a data buffer that supports being queued. - * @{ - */ - -#ifndef _GQUEUE_H -#define _GQUEUE_H - -#if GFX_USE_GQUEUE || defined(__DOXYGEN__) - -/** - * @brief A queue item - * @{ - */ -typedef struct gfxQueueASyncItem { - struct gfxQueueASyncItem *next; -} gfxQueueASyncItem, gfxQueueGSyncItem; - -typedef struct gfxQueueFSyncItem { - struct gfxQueueFSyncItem *next; - gfxSem sem; -} gfxQueueFSyncItem; -/** @} */ - -/** - * @brief A queue - * @{ - */ -typedef struct gfxQueueASync { - gfxQueueASyncItem *head; - gfxQueueASyncItem *tail; -} gfxQueueASync; - -typedef struct gfxQueueGSync { - gfxQueueGSyncItem *head; - gfxQueueGSyncItem *tail; - gfxSem sem; -} gfxQueueGSync; - -typedef struct gfxQueueFSync { - gfxQueueFSyncItem *head; - gfxQueueFSyncItem *tail; - gfxSem sem; -} gfxQueueFSync; -/** @} */ - -/** - * @brief A Data Buffer Queue - * @note This structure is followed immediately by the data itself. - * When allocating the buffers for the data put this structure - * at the beginning of the buffer. - */ -typedef struct GDataBuffer { - gfxQueueGSyncItem next; // @< Used for queueing the buffers - size_t size; // @< The size of the buffer area following this structure (in bytes) - size_t len; // @< The length of the data in the buffer area (in bytes) -} GDataBuffer; - -/*===========================================================================*/ -/* Function declarations. */ -/*===========================================================================*/ - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @name Initialisation functions - * @brief Initialise a queue. - * - * @param[in] pqueue A pointer to the queue - * - * @note Whilst queues are normally FIFO, a GFX queue also supports push and pop operations. - * A pop operation is the same as normal get from the queue but a push places the item - * at the head of the queue instead of the tail (as a put would). - * - * @api - * @{ - */ -void gfxQueueASyncInit(gfxQueueASync *pqueue); -void gfxQueueGSyncInit(gfxQueueGSync *pqueue); -void gfxQueueFSyncInit(gfxQueueFSync *pqueue); -/** @} */ - -/** - * @name Deinitialisation functions - * @brief De-Initialise a queue. - * - * @param[in] pqueue A pointer to the queue - * - * @api - * @{ - */ -#define gfxQueueASyncDeinit(pqueue) -void gfxQueueGSyncDeinit(gfxQueueGSync *pqueue); -void gfxQueueFSyncDeinit(gfxQueueFSync *pqueue); -/** @} */ - -/** - * @name Get() Functions - * @brief Get an item from the head of the queue (and remove it from the queue). - * @return NULL if the timeout expires before an item is available - * - * @param[in] pqueue A pointer to the queue - * @param[in] ms The maxmimum time to wait for an item. For ASync queues this parameter is - * not specified as TIME_IMMEDIATE is assumed. - * - * @note The routines ending in "I" are interrupt/system/iclass level routines. - * - * @api - * @{ - */ -gfxQueueASyncItem *gfxQueueASyncGet(gfxQueueASync *pqueue); -gfxQueueASyncItem *gfxQueueASyncGetI(gfxQueueASync *pqueue); -gfxQueueGSyncItem *gfxQueueGSyncGet(gfxQueueGSync *pqueue, delaytime_t ms); -gfxQueueGSyncItem *gfxQueueGSyncGetI(gfxQueueGSync *pqueue); -gfxQueueFSyncItem *gfxQueueFSyncGet(gfxQueueFSync *pqueue, delaytime_t ms); -/** @} */ - -/** - * @name Put() Functions - * @brief Put an item on the end of the queue. - * @return none for ASync and GSync queues; For FSync queues - FALSE on timeout, otherwise TRUE - * - * @param[in] pqueue A pointer to the queue - * @param[in] pitem A pointer to the queue item - * @param[in] ms The maxmimum time to wait for an item to be removed from the queue (only for FSync queues) - * - * @note FSync: Use a delay time of TIME_IMMEDIATE if you don't want to wait until the - * item is removed from the queue. Note that even if the timeout occurs - the item - * remains in the queue. - * @note The routines ending in "I" are interrupt/system/iclass level routines. - * - * @api - * @{ - */ -void gfxQueueASyncPut(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem); -void gfxQueueASyncPutI(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem); -void gfxQueueGSyncPut(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem); -void gfxQueueGSyncPutI(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem); -bool_t gfxQueueFSyncPut(gfxQueueFSync *pqueue, gfxQueueFSyncItem *pitem, delaytime_t ms); -/** @} */ - -/** - * @name Pop() Functions - * @brief Pop an item from the head of the queue (and remove it from the queue). - * @details This is exactly the same as the Get operation above. - * - * @api - * @{ - */ -#define gfxQueueASyncPop(pqueue) gfxQueueASyncGet(pqueue) -#define gfxQueueASyncPopI(pqueue) gfxQueueASyncGetI(pqueue) -#define gfxQueueGSyncPop(pqueue, ms) gfxQueueGSyncGet(pqueue, ms) -#define gfxQueueFSyncPop(pqueue, ms) gfxQueueFSyncGet(pqueue, ms) -/** @} */ - -/** - * @name Push() Functions - * @brief Push an item into the start of the queue. - * @return none for ASync and GSync queues; For FSync queues - FALSE on timeout, otherwise TRUE - * - * @param[in] pqueue A pointer to the queue - * @param[in] pitem A pointer to the queue item - * @param[in] ms The maxmimum time to wait for an item to be popped (only for FSync queues) - * - * @note FSync: Use a delay time of TIME_IMMEDIATE if you don't want to wait until the - * item is removed from the queue. Note that even if the timeout occurs - the item - * remains in the queue. - * @note The routines ending in "I" are interrupt/system/iclass level routines. - * - * @api - * @{ - */ -void gfxQueueASyncPush(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem); -void gfxQueueASyncPushI(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem); -void gfxQueueGSyncPush(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem); -void gfxQueueGSyncPushI(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem); -bool_t gfxQueueFSyncPush(gfxQueueFSync *pqueue, gfxQueueFSyncItem *pitem, delaytime_t ms); -/** @} */ - -/** - * @name Insert() Functions - * @brief Insert an item on the queue after the specified item. - * @return none for ASync and GSync queues; For FSync queues - FALSE on timeout, otherwise TRUE - * - * @param[in] pqueue A pointer to the queue - * @param[in] pitem A pointer to the queue item - * @param[in] pafter A pointer to the queue item this new item must be inserted after. If NULL or - * pafter can't be found in the queue, it puts the new item at the end of the queue. - * @param[in] ms The maxmimum time to wait for an item to be removed from the queue (only for FSync queues) - * - * @note FSync: Use a delay time of TIME_IMMEDIATE if you don't want to wait until the - * item is removed from the queue. Note that even if the timeout occurs - the item - * remains in the queue. - * @note The routines ending in "I" are interrupt/system/iclass level routines. - * - * @api - * @{ - */ -void gfxQueueASyncInsert(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem, gfxQueueASyncItem *pafter); -void gfxQueueASyncInsertI(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem, gfxQueueASyncItem *pafter); -void gfxQueueGSyncInsert(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem, gfxQueueASyncItem *pafter); -void gfxQueueGSyncInsertI(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem, gfxQueueASyncItem *pafter); -bool_t gfxQueueFSyncInsert(gfxQueueFSync *pqueue, gfxQueueFSyncItem *pitem, gfxQueueASyncItem *pafter, delaytime_t ms); -/** @} */ - -/** - * @name Remove() Functions - * @brief Remove an item from the queue. - * @note Removes the specified item from the queue where-ever it is in the queue - * - * @param[in] pqueue A pointer to the queue - * @param[in] pitem A pointer to the queue item - * - * @note If the item isn't in the queue the routine just returns. - * @note If a process is waiting on the Put/Push operation for the item, that process - * will be signaled. - * @note The routines ending in "I" are interrupt/system/iclass level routines. - * - * @api - * @{ - */ -void gfxQueueASyncRemove(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem); -void gfxQueueASyncRemoveI(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem); -void gfxQueueGSyncRemove(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem); -void gfxQueueGSyncRemoveI(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem); -void gfxQueueFSyncRemove(gfxQueueFSync *pqueue, gfxQueueFSyncItem *pitem); -/** @} */ - -/** - * @name isEmpty() Functions - * @brief Is the queue empty? - * @return TRUE if the queue is empty - * - * @param[in] pqueue A pointer to the queue - * - * @note The routines ending in "I" are interrupt/system/iclass level routines. - * - * @api - * @{ - */ -#define gfxQueueASyncIsEmpty(pqueue) ((pqueue)->head == 0) -#define gfxQueueASyncIsEmptyI(pqueue) ((pqueue)->head == 0) -#define gfxQueueGSyncIsEmpty(pqueue) ((pqueue)->head == 0) -#define gfxQueueGSyncIsEmptyI(pqueue) ((pqueue)->head == 0) -#define gfxQueueFSyncIsEmpty(pqueue) ((pqueue)->head == 0) -#define gfxQueueFSyncIsEmptyI(pqueue) ((pqueue)->head == 0) -/** @} */ - -/** - * @name IsInQueue() Functions - * @brief Is an item in the queue? - * @return TRUE if the item is in the queue? - * - * @param[in] pqueue A pointer to the queue - * @param[in] pitem A pointer to the queue item - * - * @note This operation may be expensive. - * @note The routines ending in "I" are interrupt/system/iclass level routines. - * - * @api - * @{ - */ -bool_t gfxQueueASyncIsIn(gfxQueueASync *pqueue, const gfxQueueASyncItem *pitem); -bool_t gfxQueueASyncIsInI(gfxQueueASync *pqueue, const gfxQueueASyncItem *pitem); -bool_t gfxQueueGSyncIsIn(gfxQueueGSync *pqueue, const gfxQueueGSyncItem *pitem); -bool_t gfxQueueGSyncIsInI(gfxQueueGSync *pqueue, const gfxQueueGSyncItem *pitem); -bool_t gfxQueueFSyncIsIn(gfxQueueFSync *pqueue, const gfxQueueFSyncItem *pitem); -bool_t gfxQueueFSyncIsInI(gfxQueueFSync *pqueue, const gfxQueueFSyncItem *pitem); -/** @} */ - -/** - * @name Peek() Functions - * @brief Get the first item from the head of the queue but do not remove it from the queue. - * @return NULL if no item is available. - * - * @param[in] pqueue A pointer to the queue - * - * @note This call does not block. - * @note This can be used as the first call to iterate all the elements in the queue. - * @note As that item is still on the queue, it should be treated as read-only. It could - * also be removed from the queue at any time by another thread (thereby altering the - * queue item). - * @note The routines ending in "I" are interrupt/system/iclass level routines. - * - * @api - * @{ - */ -#define gfxQueueASyncPeek(pqueue) ((const gfxQueueASyncItem *)((pqueue)->head)) -#define gfxQueueASyncPeekI(pqueue) ((const gfxQueueASyncItem *)((pqueue)->head)) -#define gfxQueueGSyncPeek(pqueue) ((const gfxQueueGSyncItem *)((pqueue)->head)) -#define gfxQueueGSyncPeekI(pqueue) ((const gfxQueueGSyncItem *)((pqueue)->head)) -#define gfxQueueFSyncPeek(pqueue) ((const gfxQueueFSyncItem *)((pqueue)->head)) -#define gfxQueueFSyncPeekI(pqueue) ((const gfxQueueFSyncItem *)((pqueue)->head)) -/** @} */ - -/** - * @name Next() Functions - * @brief Get the next item in the queue (but do not remove it from the queue). - * @return NULL if no item is available. - * - * @param[in] pitem The previous item in the queue - * - * @note This call does not block. - * @note This can be used as subsequent calls to iterate all the elements in the queue. - * @note As that item is still on the queue, it should be treated as read-only. It could - * also be removed from the queue at any time by another thread (thereby altering the - * queue item). - * @note The routines ending in "I" are interrupt/system/iclass level routines. - * - * @api - * @{ - */ -#define gfxQueueASyncNext(pitem) ((const gfxQueueASyncItem *)((pitem)->next)) -#define gfxQueueASyncNextI(pitem) ((const gfxQueueASyncItem *)((pitem)->next)) -#define gfxQueueGSyncNext(pitem) ((const gfxQueueGSyncItem *)((pitem)->next)) -#define gfxQueueGSyncNextI(pitem) ((const gfxQueueGSyncItem *)((pitem)->next)) -#define gfxQueueFSyncNext(pitem) ((const gfxQueueFSyncItem *)((pitem)->next)) -#define gfxQueueFSyncNextI(pitem) ((const gfxQueueFSyncItem *)((pitem)->next)) -/** @} */ - -/** - * @name BufferAlloc() Functions - * @brief Allocate some buffers and put them on the free list - * @return TRUE is it succeeded. FALSE on allocation failure. - * - * @param[in] num The number of buffers to allocate - * @param[in] size The size (in bytes) of each buffer - * - * @api - * @{ - */ -bool_t gfxBufferAlloc(unsigned num, size_t size); -/** @} */ - -/** - * @name BufferIsAvailable() Functions - * @brief Is there one or more buffers currently available on the free list - * @return TRUE if there are buffers in the free list - * - * @api - * @{ - */ -bool_t gfxBufferIsAvailable(void); -/** @} */ - -/** - * @name BufferGet() Functions - * @brief Get a buffer from the free list - * @return A GDataBuffer pointer or NULL if the timeout is exceeded - * - * @param[in] ms The maximum amount of time in milliseconds to wait for a buffer if one is not available. - * - * @api - * @{ - */ -GDataBuffer *gfxBufferGet(delaytime_t ms); -GDataBuffer *gfxBufferGetI(void); -/** @} */ - -/** - * @name BufferRelease) Functions - * @brief Release a buffer back to the free list - * - * @param[in] pd The buffer to put (back) on the free-list. - * - * @note This call should be used to return any buffers that were taken from - * the free-list once they have been finished with. It can also be used - * to put new buffers onto the free-list. Just make sure the "size" field - * of the GDataBuffer structure has been filled in first. - * - * @api - * @{ - */ -void gfxBufferRelease(GDataBuffer *pd); -void gfxBufferReleaseI(GDataBuffer *pd); -/** @} */ - - -#ifdef __cplusplus -} -#endif - -#endif /* GFX_USE_GQUEUE */ -#endif /* _GQUEUE_H */ -/** @} */ diff --git a/src/gqueue/sys_make.mk b/src/gqueue/sys_make.mk deleted file mode 100644 index f8a542c3..00000000 --- a/src/gqueue/sys_make.mk +++ /dev/null @@ -1 +0,0 @@ -GFXSRC += $(GFXLIB)/src/gqueue/gqueue_gqueue.c diff --git a/src/gqueue/sys_options.h b/src/gqueue/sys_options.h deleted file mode 100644 index 169cf116..00000000 --- a/src/gqueue/sys_options.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gqueue/sys_options.h - * @brief GQUEUE - Queue options header file. - * - * @addtogroup GQUEUE - * @{ - */ - -#ifndef _GQUEUE_OPTIONS_H -#define _GQUEUE_OPTIONS_H - -/** - * @name GQUEUE Functions to include. - * @{ - */ - /** - * @brief Enable Asynchronous Queues - * @details Defaults to FALSE - */ - #ifndef GQUEUE_NEED_ASYNC - #define GQUEUE_NEED_ASYNC FALSE - #endif - /** - * @brief Enable Get-Synchronous Queues - * @details Defaults to FALSE - */ - #ifndef GQUEUE_NEED_GSYNC - #define GQUEUE_NEED_GSYNC FALSE - #endif - /** - * @brief Enable Fully Synchronous Queues - * @details Defaults to FALSE - */ - #ifndef GQUEUE_NEED_FSYNC - #define GQUEUE_NEED_FSYNC FALSE - #endif - /** - * @brief Enable Queue-able Data Buffers - */ - #ifndef GQUEUE_NEED_BUFFERS - #define GQUEUE_NEED_BUFFERS FALSE - #endif -/** - * @} - * - * @name GQUEUE Optional Sizing Parameters - * @{ - */ -/** @} */ - -#endif /* _GQUEUE_OPTIONS_H */ -/** @} */ diff --git a/src/gqueue/sys_rules.h b/src/gqueue/sys_rules.h deleted file mode 100644 index 831952d8..00000000 --- a/src/gqueue/sys_rules.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gqueue/sys_rules.h - * @brief GQUEUE safety rules header file. - * - * @addtogroup GQUEUE - * @{ - */ - -#ifndef _GQUEUE_RULES_H -#define _GQUEUE_RULES_H - -#if GFX_USE_GQUEUE - #if GQUEUE_NEED_BUFFERS && !GQUEUE_NEED_GSYNC - #if GFX_DISPLAY_RULE_WARNINGS - #warning "GQUEUE: GQUEUE_NEED_GSYNC is required if GQUEUE_NEED_BUFFERS is TRUE. It has been turned on for you." - #endif - #undef GQUEUE_NEED_GSYNC - #define GQUEUE_NEED_GSYNC TRUE - #endif -#endif - -#endif /* _GQUEUE_RULES_H */ -/** @} */ diff --git a/src/gtimer/gtimer.c b/src/gtimer/gtimer.c new file mode 100644 index 00000000..f84cad22 --- /dev/null +++ b/src/gtimer/gtimer.c @@ -0,0 +1,229 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +#include "gfx.h" + +#if GFX_USE_GTIMER || defined(__DOXYGEN__) + +#define GTIMER_FLG_PERIODIC 0x0001 +#define GTIMER_FLG_INFINITE 0x0002 +#define GTIMER_FLG_JABBED 0x0004 +#define GTIMER_FLG_SCHEDULED 0x0008 + +/* Don't rework this macro to use a ternary operator - the gcc compiler stuffs it up */ +#define TimeIsWithin(x, start, end) ((end >= start && x >= start && x <= end) || (end < start && (x >= start || x <= end))) + +/* This mutex protects access to our tables */ +static gfxMutex mutex; +static gfxThreadHandle hThread = 0; +static GTimer *pTimerHead = 0; +static gfxSem waitsem; +static DECLARE_THREAD_STACK(waTimerThread, GTIMER_THREAD_WORKAREA_SIZE); + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +static DECLARE_THREAD_FUNCTION(GTimerThreadHandler, arg) { + (void)arg; + GTimer *pt; + systemticks_t tm; + systemticks_t nxtTimeout; + systemticks_t lastTime; + GTimerFunction fn; + void *param; + + nxtTimeout = TIME_INFINITE; + lastTime = 0; + while(1) { + /* Wait for work to do. */ + gfxYield(); // Give someone else a go no matter how busy we are + gfxSemWait(&waitsem, nxtTimeout); + + restartTimerChecks: + + // Our reference time + tm = gfxSystemTicks(); + nxtTimeout = TIME_INFINITE; + + /* We need to obtain the mutex */ + gfxMutexEnter(&mutex); + + if (pTimerHead) { + pt = pTimerHead; + do { + // Do we have something to do for this timer? + if ((pt->flags & GTIMER_FLG_JABBED) || (!(pt->flags & GTIMER_FLG_INFINITE) && TimeIsWithin(pt->when, lastTime, tm))) { + + // Is this timer periodic? + if ((pt->flags & GTIMER_FLG_PERIODIC) && pt->period != TIME_IMMEDIATE) { + // Yes - Update ready for the next period + if (!(pt->flags & GTIMER_FLG_INFINITE)) { + // We may have skipped a period. + // We use this complicated formulae rather than a loop + // because the gcc compiler stuffs up the loop so that it + // either loops forever or doesn't get executed at all. + pt->when += ((tm + pt->period - pt->when) / pt->period) * pt->period; + } + + // We are definitely no longer jabbed + pt->flags &= ~GTIMER_FLG_JABBED; + + } else { + // No - get us off the timers list + if (pt->next == pt->prev) + pTimerHead = 0; + else { + pt->next->prev = pt->prev; + pt->prev->next = pt->next; + if (pTimerHead == pt) + pTimerHead = pt->next; + } + pt->flags = 0; + } + + // Call the callback function + fn = pt->fn; + param = pt->param; + gfxMutexExit(&mutex); + fn(param); + + // We no longer hold the mutex, the callback function may have taken a while + // and our list may have been altered so start again! + goto restartTimerChecks; + } + + // Find when we next need to wake up + if (!(pt->flags & GTIMER_FLG_INFINITE) && pt->when - tm < nxtTimeout) + nxtTimeout = pt->when - tm; + pt = pt->next; + } while(pt != pTimerHead); + } + + // Ready for the next loop + lastTime = tm; + gfxMutexExit(&mutex); + } + return 0; +} + +void _gtimerInit(void) +{ + gfxSemInit(&waitsem, 0, 1); + gfxMutexInit(&mutex); +} + +void _gtimerDeinit(void) +{ + gfxSemDestroy(&waitsem); + gfxMutexDestroy(&mutex); + // Need to destroy GTimer thread here +} + +void gtimerInit(GTimer* pt) +{ + pt->flags = 0; +} + +void gtimerDeinit(GTimer* pt) +{ + gtimerStop(pt); +} + +void gtimerStart(GTimer *pt, GTimerFunction fn, void *param, bool_t periodic, delaytime_t millisec) { + gfxMutexEnter(&mutex); + + // Start our thread if not already going + if (!hThread) { + hThread = gfxThreadCreate(waTimerThread, sizeof(waTimerThread), GTIMER_THREAD_PRIORITY, GTimerThreadHandler, 0); + if (hThread) {gfxThreadClose(hThread);} // We never really need the handle again + } + + // Is this already scheduled? + if (pt->flags & GTIMER_FLG_SCHEDULED) { + // Cancel it! + if (pt->next == pt->prev) + pTimerHead = 0; + else { + pt->next->prev = pt->prev; + pt->prev->next = pt->next; + if (pTimerHead == pt) + pTimerHead = pt->next; + } + } + + // Set up the timer structure + pt->fn = fn; + pt->param = param; + pt->flags = GTIMER_FLG_SCHEDULED; + if (periodic) + pt->flags |= GTIMER_FLG_PERIODIC; + if (millisec == TIME_INFINITE) { + pt->flags |= GTIMER_FLG_INFINITE; + pt->period = TIME_INFINITE; + } else { + pt->period = gfxMillisecondsToTicks(millisec); + pt->when = gfxSystemTicks() + pt->period; + } + + // Just pop it on the end of the queue + if (pTimerHead) { + pt->next = pTimerHead; + pt->prev = pTimerHead->prev; + pt->prev->next = pt; + pt->next->prev = pt; + } else + pt->next = pt->prev = pTimerHead = pt; + + // Bump the thread + if (!(pt->flags & GTIMER_FLG_INFINITE)) + gfxSemSignal(&waitsem); + gfxMutexExit(&mutex); +} + +void gtimerStop(GTimer *pt) { + gfxMutexEnter(&mutex); + if (pt->flags & GTIMER_FLG_SCHEDULED) { + // Cancel it! + if (pt->next == pt) + pTimerHead = 0; + else { + pt->next->prev = pt->prev; + pt->prev->next = pt->next; + if (pTimerHead == pt) + pTimerHead = pt->next; + } + // Make sure we know the structure is dead! + pt->flags = 0; + } + gfxMutexExit(&mutex); +} + +bool_t gtimerIsActive(GTimer *pt) { + return (pt->flags & GTIMER_FLG_SCHEDULED) ? TRUE : FALSE; +} + +void gtimerJab(GTimer *pt) { + gfxMutexEnter(&mutex); + + // Jab it! + pt->flags |= GTIMER_FLG_JABBED; + + // Bump the thread + gfxSemSignal(&waitsem); + gfxMutexExit(&mutex); +} + +void gtimerJabI(GTimer *pt) { + // Jab it! + pt->flags |= GTIMER_FLG_JABBED; + + // Bump the thread + gfxSemSignalI(&waitsem); +} + +#endif /* GFX_USE_GTIMER */ diff --git a/src/gtimer/gtimer.h b/src/gtimer/gtimer.h new file mode 100644 index 00000000..6803ac66 --- /dev/null +++ b/src/gtimer/gtimer.h @@ -0,0 +1,180 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gtimer/gtimer.h + * + * @addtogroup GTIMER + * + * @brief Module which provides software based timers for user-space applications + * + * @details The reason why ChibiOS/GFX has it's own timer abstraction is because + * virtual timers provided by ChibiOS/RT are interrupt context only. + * While great for what they are designed for, they make coding of the input + * drivers much more complex. + * For non-performance critical drivers like these input drivers, it would also + * hog an in-ordinate amount of critical (interrupt locked) system time. + * This contrary to the goals of a real-time operating system. So a user-land + * (thread based) timer mechanism is also required. + * + * @pre GFX_USE_GTIMER must be set to TRUE in your gfxconf.h + * + * @{ + */ + +#ifndef _GTIMER_H +#define _GTIMER_H + +#include "gfx.h" + +#if GFX_USE_GTIMER || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Type definitions */ +/*===========================================================================*/ + +/* Data part of a static GTimer initialiser */ +#define _GTIMER_DATA() {0,0,0,0,0,0,0} + +/* Static GTimer initialiser */ +#define GTIMER_DECL(name) GTimer name = _GTIMER_DATA() + +/* A callback function (executed in a thread context) */ +typedef void (*GTimerFunction)(void *param); + +/** + * @brief A GTimer structure + */ +typedef struct GTimer_t { + GTimerFunction fn; + void *param; + systemticks_t when; + systemticks_t period; + uint16_t flags; + struct GTimer_t *next; + struct GTimer_t *prev; +} GTimer; + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initialise a timer + * + * @param[in] pt Pointer to a GTimer structure + * + * @api + */ +void gtimerInit(GTimer* pt); + +/** + * @brief Deinitialise a timer + * + * @param[in] pt Pointer to a GTimer structure + * + * @api + */ +void gtimerDeinit(GTimer* pt); + +/** + * @brief Set a timer going or alter its properties if it is already going. + * + * @param[in] pt Pointer to a GTimer structure + * @param[in] fn The callback function + * @param[in] param The parameter to pass to the callback function + * @param[in] periodic Is the timer a periodic timer? FALSE is a once-only timer. + * @param[in] millisec The timer period. The following special values are allowed: + * TIME_IMMEDIATE causes the callback function to be called asap. + * A periodic timer with this value will fire once only. + * TIME_INFINITE never timeout (unless triggered by gtimerJab or gtimerJabI) + * + * @note If the timer is already active its properties are updated with the new parameters. + * The current period will be immediately canceled (without the callback function being + * called) and the timer will be restart with the new timer properties. + * @note The callback function should be careful not to over-run the thread stack. + * Define a new value for the macro GTIME_THREAD_STACK_SIZE if you want to + * change the default size. + * @note The callback function should return as quickly as possible as all + * timer callbacks are performed by a single thread. If a callback function + * takes too long it could affect the timer response for other timers. + * @note A timer callback function is not a replacement for a dedicated thread if the + * function wants to perform computationally expensive stuff. + * @note As the callback function is called on GTIMER's thread, the function must make sure it uses + * appropriate synchronisation controls such as semaphores or mutexes around any data + * structures it shares with other threads such as the main application thread. + * + * @api + */ +void gtimerStart(GTimer *pt, GTimerFunction fn, void *param, bool_t periodic, delaytime_t millisec); + +/** + * @brief Stop a timer (periodic or otherwise) + * + * @param[in] pt Pointer to a GTimer structure + * + * @note If the timer is not active this does nothing. + * + * @api + */ +void gtimerStop(GTimer *pt); + +/** + * @brief Test if a timer is currently active + * + * @param[in] pt Pointer to a GTimer structure + * + * @return TRUE if active, FALSE otherwise + * + * @api + */ +bool_t gtimerIsActive(GTimer *pt); + +/** + * @brief Jab a timer causing the current period to immediate expire + * @details The callback function will be called as soon as possible. + * + * @pre Use from a normal thread context. + * + * @param[in] pt Pointer to a GTimer structure + * + * @note If the timer is not active this does nothing. + * @note Repeated Jabs before the callback function actually happens are ignored. + * + * @api + */ +void gtimerJab(GTimer *pt); + +/** + * @brief Jab a timer causing the current period to immediate expire + * @details The callback function will be called as soon as possible. + * + * @pre Use from an interrupt routine context. + * + * @param[in] pt Pointer to a GTimer structure + * + * @note If the timer is not active this does nothing. + * @note Repeated Jabs before the callback function actually happens are ignored. + * + * @iclass + * @api + */ +void gtimerJabI(GTimer *pt); + +#ifdef __cplusplus +} +#endif + +#endif /* GFX_USE_GTIMER */ + +#endif /* _GTIMER_H */ +/** @} */ + diff --git a/src/gtimer/gtimer.mk b/src/gtimer/gtimer.mk new file mode 100644 index 00000000..801c31a6 --- /dev/null +++ b/src/gtimer/gtimer.mk @@ -0,0 +1 @@ +GFXSRC += $(GFXLIB)/src/gtimer/gtimer.c diff --git a/src/gtimer/gtimer_gtimer.c b/src/gtimer/gtimer_gtimer.c deleted file mode 100644 index 38dceca5..00000000 --- a/src/gtimer/gtimer_gtimer.c +++ /dev/null @@ -1,238 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gtimer/gtimer_gtimer.c - * @brief GTIMER sub-system code. - * - * @addtogroup GTIMER - * @{ - */ -#include "gfx.h" - -#if GFX_USE_GTIMER || defined(__DOXYGEN__) - -#define GTIMER_FLG_PERIODIC 0x0001 -#define GTIMER_FLG_INFINITE 0x0002 -#define GTIMER_FLG_JABBED 0x0004 -#define GTIMER_FLG_SCHEDULED 0x0008 - -/* Don't rework this macro to use a ternary operator - the gcc compiler stuffs it up */ -#define TimeIsWithin(x, start, end) ((end >= start && x >= start && x <= end) || (end < start && (x >= start || x <= end))) - -/* This mutex protects access to our tables */ -static gfxMutex mutex; -static gfxThreadHandle hThread = 0; -static GTimer *pTimerHead = 0; -static gfxSem waitsem; -static DECLARE_THREAD_STACK(waTimerThread, GTIMER_THREAD_WORKAREA_SIZE); - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -static DECLARE_THREAD_FUNCTION(GTimerThreadHandler, arg) { - (void)arg; - GTimer *pt; - systemticks_t tm; - systemticks_t nxtTimeout; - systemticks_t lastTime; - GTimerFunction fn; - void *param; - - nxtTimeout = TIME_INFINITE; - lastTime = 0; - while(1) { - /* Wait for work to do. */ - gfxYield(); // Give someone else a go no matter how busy we are - gfxSemWait(&waitsem, nxtTimeout); - - restartTimerChecks: - - // Our reference time - tm = gfxSystemTicks(); - nxtTimeout = TIME_INFINITE; - - /* We need to obtain the mutex */ - gfxMutexEnter(&mutex); - - if (pTimerHead) { - pt = pTimerHead; - do { - // Do we have something to do for this timer? - if ((pt->flags & GTIMER_FLG_JABBED) || (!(pt->flags & GTIMER_FLG_INFINITE) && TimeIsWithin(pt->when, lastTime, tm))) { - - // Is this timer periodic? - if ((pt->flags & GTIMER_FLG_PERIODIC) && pt->period != TIME_IMMEDIATE) { - // Yes - Update ready for the next period - if (!(pt->flags & GTIMER_FLG_INFINITE)) { - // We may have skipped a period. - // We use this complicated formulae rather than a loop - // because the gcc compiler stuffs up the loop so that it - // either loops forever or doesn't get executed at all. - pt->when += ((tm + pt->period - pt->when) / pt->period) * pt->period; - } - - // We are definitely no longer jabbed - pt->flags &= ~GTIMER_FLG_JABBED; - - } else { - // No - get us off the timers list - if (pt->next == pt->prev) - pTimerHead = 0; - else { - pt->next->prev = pt->prev; - pt->prev->next = pt->next; - if (pTimerHead == pt) - pTimerHead = pt->next; - } - pt->flags = 0; - } - - // Call the callback function - fn = pt->fn; - param = pt->param; - gfxMutexExit(&mutex); - fn(param); - - // We no longer hold the mutex, the callback function may have taken a while - // and our list may have been altered so start again! - goto restartTimerChecks; - } - - // Find when we next need to wake up - if (!(pt->flags & GTIMER_FLG_INFINITE) && pt->when - tm < nxtTimeout) - nxtTimeout = pt->when - tm; - pt = pt->next; - } while(pt != pTimerHead); - } - - // Ready for the next loop - lastTime = tm; - gfxMutexExit(&mutex); - } - return 0; -} - -void _gtimerInit(void) -{ - gfxSemInit(&waitsem, 0, 1); - gfxMutexInit(&mutex); -} - -void _gtimerDeinit(void) -{ - gfxSemDestroy(&waitsem); - gfxMutexDestroy(&mutex); - // Need to destroy GTimer thread here -} - -void gtimerInit(GTimer* pt) -{ - pt->flags = 0; -} - -void gtimerDeinit(GTimer* pt) -{ - gtimerStop(pt); -} - -void gtimerStart(GTimer *pt, GTimerFunction fn, void *param, bool_t periodic, delaytime_t millisec) { - gfxMutexEnter(&mutex); - - // Start our thread if not already going - if (!hThread) { - hThread = gfxThreadCreate(waTimerThread, sizeof(waTimerThread), GTIMER_THREAD_PRIORITY, GTimerThreadHandler, 0); - if (hThread) {gfxThreadClose(hThread);} // We never really need the handle again - } - - // Is this already scheduled? - if (pt->flags & GTIMER_FLG_SCHEDULED) { - // Cancel it! - if (pt->next == pt->prev) - pTimerHead = 0; - else { - pt->next->prev = pt->prev; - pt->prev->next = pt->next; - if (pTimerHead == pt) - pTimerHead = pt->next; - } - } - - // Set up the timer structure - pt->fn = fn; - pt->param = param; - pt->flags = GTIMER_FLG_SCHEDULED; - if (periodic) - pt->flags |= GTIMER_FLG_PERIODIC; - if (millisec == TIME_INFINITE) { - pt->flags |= GTIMER_FLG_INFINITE; - pt->period = TIME_INFINITE; - } else { - pt->period = gfxMillisecondsToTicks(millisec); - pt->when = gfxSystemTicks() + pt->period; - } - - // Just pop it on the end of the queue - if (pTimerHead) { - pt->next = pTimerHead; - pt->prev = pTimerHead->prev; - pt->prev->next = pt; - pt->next->prev = pt; - } else - pt->next = pt->prev = pTimerHead = pt; - - // Bump the thread - if (!(pt->flags & GTIMER_FLG_INFINITE)) - gfxSemSignal(&waitsem); - gfxMutexExit(&mutex); -} - -void gtimerStop(GTimer *pt) { - gfxMutexEnter(&mutex); - if (pt->flags & GTIMER_FLG_SCHEDULED) { - // Cancel it! - if (pt->next == pt) - pTimerHead = 0; - else { - pt->next->prev = pt->prev; - pt->prev->next = pt->next; - if (pTimerHead == pt) - pTimerHead = pt->next; - } - // Make sure we know the structure is dead! - pt->flags = 0; - } - gfxMutexExit(&mutex); -} - -bool_t gtimerIsActive(GTimer *pt) { - return (pt->flags & GTIMER_FLG_SCHEDULED) ? TRUE : FALSE; -} - -void gtimerJab(GTimer *pt) { - gfxMutexEnter(&mutex); - - // Jab it! - pt->flags |= GTIMER_FLG_JABBED; - - // Bump the thread - gfxSemSignal(&waitsem); - gfxMutexExit(&mutex); -} - -void gtimerJabI(GTimer *pt) { - // Jab it! - pt->flags |= GTIMER_FLG_JABBED; - - // Bump the thread - gfxSemSignalI(&waitsem); -} - -#endif /* GFX_USE_GTIMER */ -/** @} */ - diff --git a/src/gtimer/gtimer_options.h b/src/gtimer/gtimer_options.h new file mode 100644 index 00000000..dc0c6141 --- /dev/null +++ b/src/gtimer/gtimer_options.h @@ -0,0 +1,46 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gtimer/gtimer_options.h + * @brief GTIMER sub-system options header file. + * + * @addtogroup GTIMER + * @{ + */ + +#ifndef _GTIMER_OPTIONS_H +#define _GTIMER_OPTIONS_H + +/** + * @name GTIMER Functionality to be included + * @{ + */ +/** + * @} + * + * @name GTIMER Optional Sizing Parameters + * @{ + */ + /** + * @brief Defines the GTIMER thread priority + * @details Defaults to HIGH_PRIORITY + */ + #ifndef GTIMER_THREAD_PRIORITY + #define GTIMER_THREAD_PRIORITY HIGH_PRIORITY + #endif + /** + * @brief Defines the size of the timer threads work area (stack+structures). + * @details Defaults to 2048 bytes + */ + #ifndef GTIMER_THREAD_WORKAREA_SIZE + #define GTIMER_THREAD_WORKAREA_SIZE 2048 + #endif +/** @} */ + +#endif /* _GTIMER_OPTIONS_H */ +/** @} */ diff --git a/src/gtimer/gtimer_rules.h b/src/gtimer/gtimer_rules.h new file mode 100644 index 00000000..ff966d38 --- /dev/null +++ b/src/gtimer/gtimer_rules.h @@ -0,0 +1,29 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gtimer/gtimer_rules.h + * @brief GTIMER safety rules header file. + * + * @addtogroup GTIMER + * @{ + */ + +#ifndef _GTIMER_RULES_H +#define _GTIMER_RULES_H + +#if GFX_USE_GTIMER + #if GFX_USE_GDISP && !GDISP_NEED_MULTITHREAD + #if GFX_DISPLAY_RULE_WARNINGS + #warning "GTIMER: GDISP_NEED_MULTITHREAD has not been specified." + #warning "GTIMER: Make sure you are not performing any GDISP/GWIN drawing operations in the timer callback!" + #endif + #endif +#endif + +#endif /* _GTIMER_RULES_H */ +/** @} */ diff --git a/src/gtimer/sys_defs.h b/src/gtimer/sys_defs.h deleted file mode 100644 index 45b12162..00000000 --- a/src/gtimer/sys_defs.h +++ /dev/null @@ -1,180 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gtimer/sys_defs.h - * - * @addtogroup GTIMER - * - * @brief Module which provides software based timers for user-space applications - * - * @details The reason why ChibiOS/GFX has it's own timer abstraction is because - * virtual timers provided by ChibiOS/RT are interrupt context only. - * While great for what they are designed for, they make coding of the input - * drivers much more complex. - * For non-performance critical drivers like these input drivers, it would also - * hog an in-ordinate amount of critical (interrupt locked) system time. - * This contrary to the goals of a real-time operating system. So a user-land - * (thread based) timer mechanism is also required. - * - * @pre GFX_USE_GTIMER must be set to TRUE in your gfxconf.h - * - * @{ - */ - -#ifndef _GTIMER_H -#define _GTIMER_H - -#include "gfx.h" - -#if GFX_USE_GTIMER || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Type definitions */ -/*===========================================================================*/ - -/* Data part of a static GTimer initialiser */ -#define _GTIMER_DATA() {0,0,0,0,0,0,0} - -/* Static GTimer initialiser */ -#define GTIMER_DECL(name) GTimer name = _GTIMER_DATA() - -/* A callback function (executed in a thread context) */ -typedef void (*GTimerFunction)(void *param); - -/** - * @brief A GTimer structure - */ -typedef struct GTimer_t { - GTimerFunction fn; - void *param; - systemticks_t when; - systemticks_t period; - uint16_t flags; - struct GTimer_t *next; - struct GTimer_t *prev; -} GTimer; - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Initialise a timer - * - * @param[in] pt Pointer to a GTimer structure - * - * @api - */ -void gtimerInit(GTimer* pt); - -/** - * @brief Deinitialise a timer - * - * @param[in] pt Pointer to a GTimer structure - * - * @api - */ -void gtimerDeinit(GTimer* pt); - -/** - * @brief Set a timer going or alter its properties if it is already going. - * - * @param[in] pt Pointer to a GTimer structure - * @param[in] fn The callback function - * @param[in] param The parameter to pass to the callback function - * @param[in] periodic Is the timer a periodic timer? FALSE is a once-only timer. - * @param[in] millisec The timer period. The following special values are allowed: - * TIME_IMMEDIATE causes the callback function to be called asap. - * A periodic timer with this value will fire once only. - * TIME_INFINITE never timeout (unless triggered by gtimerJab or gtimerJabI) - * - * @note If the timer is already active its properties are updated with the new parameters. - * The current period will be immediately canceled (without the callback function being - * called) and the timer will be restart with the new timer properties. - * @note The callback function should be careful not to over-run the thread stack. - * Define a new value for the macro GTIME_THREAD_STACK_SIZE if you want to - * change the default size. - * @note The callback function should return as quickly as possible as all - * timer callbacks are performed by a single thread. If a callback function - * takes too long it could affect the timer response for other timers. - * @note A timer callback function is not a replacement for a dedicated thread if the - * function wants to perform computationally expensive stuff. - * @note As the callback function is called on GTIMER's thread, the function must make sure it uses - * appropriate synchronisation controls such as semaphores or mutexes around any data - * structures it shares with other threads such as the main application thread. - * - * @api - */ -void gtimerStart(GTimer *pt, GTimerFunction fn, void *param, bool_t periodic, delaytime_t millisec); - -/** - * @brief Stop a timer (periodic or otherwise) - * - * @param[in] pt Pointer to a GTimer structure - * - * @note If the timer is not active this does nothing. - * - * @api - */ -void gtimerStop(GTimer *pt); - -/** - * @brief Test if a timer is currently active - * - * @param[in] pt Pointer to a GTimer structure - * - * @return TRUE if active, FALSE otherwise - * - * @api - */ -bool_t gtimerIsActive(GTimer *pt); - -/** - * @brief Jab a timer causing the current period to immediate expire - * @details The callback function will be called as soon as possible. - * - * @pre Use from a normal thread context. - * - * @param[in] pt Pointer to a GTimer structure - * - * @note If the timer is not active this does nothing. - * @note Repeated Jabs before the callback function actually happens are ignored. - * - * @api - */ -void gtimerJab(GTimer *pt); - -/** - * @brief Jab a timer causing the current period to immediate expire - * @details The callback function will be called as soon as possible. - * - * @pre Use from an interrupt routine context. - * - * @param[in] pt Pointer to a GTimer structure - * - * @note If the timer is not active this does nothing. - * @note Repeated Jabs before the callback function actually happens are ignored. - * - * @iclass - * @api - */ -void gtimerJabI(GTimer *pt); - -#ifdef __cplusplus -} -#endif - -#endif /* GFX_USE_GTIMER */ - -#endif /* _GTIMER_H */ -/** @} */ - diff --git a/src/gtimer/sys_make.mk b/src/gtimer/sys_make.mk deleted file mode 100644 index e48dc9f5..00000000 --- a/src/gtimer/sys_make.mk +++ /dev/null @@ -1 +0,0 @@ -GFXSRC += $(GFXLIB)/src/gtimer/gtimer_gtimer.c diff --git a/src/gtimer/sys_options.h b/src/gtimer/sys_options.h deleted file mode 100644 index 8dca0b82..00000000 --- a/src/gtimer/sys_options.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gtimer/sys_options.h - * @brief GTIMER sub-system options header file. - * - * @addtogroup GTIMER - * @{ - */ - -#ifndef _GTIMER_OPTIONS_H -#define _GTIMER_OPTIONS_H - -/** - * @name GTIMER Functionality to be included - * @{ - */ -/** - * @} - * - * @name GTIMER Optional Sizing Parameters - * @{ - */ - /** - * @brief Defines the GTIMER thread priority - * @details Defaults to HIGH_PRIORITY - */ - #ifndef GTIMER_THREAD_PRIORITY - #define GTIMER_THREAD_PRIORITY HIGH_PRIORITY - #endif - /** - * @brief Defines the size of the timer threads work area (stack+structures). - * @details Defaults to 2048 bytes - */ - #ifndef GTIMER_THREAD_WORKAREA_SIZE - #define GTIMER_THREAD_WORKAREA_SIZE 2048 - #endif -/** @} */ - -#endif /* _GTIMER_OPTIONS_H */ -/** @} */ diff --git a/src/gtimer/sys_rules.h b/src/gtimer/sys_rules.h deleted file mode 100644 index 6f277965..00000000 --- a/src/gtimer/sys_rules.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gtimer/sys_rules.h - * @brief GTIMER safety rules header file. - * - * @addtogroup GTIMER - * @{ - */ - -#ifndef _GTIMER_RULES_H -#define _GTIMER_RULES_H - -#if GFX_USE_GTIMER - #if GFX_USE_GDISP && !GDISP_NEED_MULTITHREAD - #if GFX_DISPLAY_RULE_WARNINGS - #warning "GTIMER: GDISP_NEED_MULTITHREAD has not been specified." - #warning "GTIMER: Make sure you are not performing any GDISP/GWIN drawing operations in the timer callback!" - #endif - #endif -#endif - -#endif /* _GTIMER_RULES_H */ -/** @} */ diff --git a/src/gwin/gwin.c b/src/gwin/gwin.c new file mode 100644 index 00000000..eb51b89a --- /dev/null +++ b/src/gwin/gwin.c @@ -0,0 +1,400 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gwin/gwin_gwin.c + * @brief GWIN sub-system code + */ + +#include "gfx.h" + +#if GFX_USE_GWIN + +#include "gwin_class.h" + +#include <string.h> + +/*----------------------------------------------- + * Data + *-----------------------------------------------*/ + +static const gwinVMT basegwinVMT = { + "GWIN", // The classname + sizeof(GWindowObject), // The object size + 0, // The destroy routine + 0, // The redraw routine + 0, // The after-clear routine +}; + +static color_t defaultFgColor = White; +static color_t defaultBgColor = Black; +#if GDISP_NEED_TEXT + static font_t defaultFont; +#endif + +/*----------------------------------------------- + * Helper Routines + *-----------------------------------------------*/ + +/*----------------------------------------------- + * Class Routines + *-----------------------------------------------*/ + +void _gwinInit(void) +{ + extern void _gwmInit(void); + + _gwmInit(); + #if GWIN_NEED_WIDGET + extern void _gwidgetInit(void); + + _gwidgetInit(); + #endif + #if GWIN_NEED_CONTAINERS + extern void _gcontainerInit(void); + + _gcontainerInit(); + #endif +} + +void _gwinDeinit(void) +{ + extern void _gwmDeinit(void); + + #if GWIN_NEED_CONTAINERS + extern void _gcontainerDeinit(void); + + _gcontainerDeinit(); + #endif + #if GWIN_NEED_WIDGET + extern void _gwidgetDeinit(void); + + _gwidgetDeinit(); + #endif + + _gwmDeinit(); +} + +// Internal routine for use by GWIN components only +// Initialise a window creating it dynamically if required. +GHandle _gwindowCreate(GDisplay *g, GWindowObject *pgw, const GWindowInit *pInit, const gwinVMT *vmt, uint32_t flags) { + // Allocate the structure if necessary + if (!pgw) { + if (!(pgw = gfxAlloc(vmt->size))) + return 0; + pgw->flags = flags|GWIN_FLG_DYNAMIC; + } else + pgw->flags = flags; + + // Initialise all basic fields + pgw->display = g; + pgw->vmt = vmt; + pgw->color = defaultFgColor; + pgw->bgcolor = defaultBgColor; + #if GDISP_NEED_TEXT + pgw->font = defaultFont; + #endif + + // Make sure we don't create nasty problems for ourselves + if (vmt->size > sizeof(GWindowObject)) + memset(pgw+1, 0, vmt->size - sizeof(GWindowObject)); + + if (!_gwinWMAdd(pgw, pInit)) { + if ((pgw->flags & GWIN_FLG_DYNAMIC)) + gfxFree(pgw); + return 0; + } + + return (GHandle)pgw; +} + +// Internal routine for use by GWIN components only +void _gwinDestroy(GHandle gh, GRedrawMethod how) { + if (!gh) + return; + + // Make the window invisible + gwinSetVisible(gh, FALSE); + + // Make sure it is flushed first - must be REDRAW_WAIT or REDRAW_INSESSION + _gwinFlushRedraws(how); + + #if GWIN_NEED_CONTAINERS + // Notify the parent it is about to be deleted + if (gh->parent && ((gcontainerVMT *)gh->parent->vmt)->NotifyDelete) + ((gcontainerVMT *)gh->parent->vmt)->NotifyDelete(gh->parent, gh); + #endif + + // Remove from the window manager + #if GWIN_NEED_WINDOWMANAGER + _GWINwm->vmt->Delete(gh); + #endif + + // Class destroy routine + if (gh->vmt->Destroy) + gh->vmt->Destroy(gh); + + // Clean up the structure + if (gh->flags & GWIN_FLG_DYNAMIC) { + gh->flags = 0; // To be sure, to be sure + gfxFree((void *)gh); + } else + gh->flags = 0; // To be sure, to be sure +} + +/*----------------------------------------------- + * Routines that affect all windows + *-----------------------------------------------*/ + +void gwinClearInit(GWindowInit *pwi) { + char *p; + unsigned len; + + for(p = (char *)pwi, len = sizeof(GWindowInit); len; len--) + *p++ = 0; +} + +void gwinSetDefaultColor(color_t clr) { + defaultFgColor = clr; +} + +color_t gwinGetDefaultColor(void) { + return defaultFgColor; +} + +void gwinSetDefaultBgColor(color_t bgclr) { + defaultBgColor = bgclr; +} + +color_t gwinGetDefaultBgColor(void) { + return defaultBgColor; +} + +#if GDISP_NEED_TEXT + void gwinSetDefaultFont(font_t font) { + defaultFont = font; + } + + font_t gwinGetDefaultFont(void) { + return defaultFont; + } +#endif + +/*----------------------------------------------- + * The GWindow Routines + *-----------------------------------------------*/ + +GHandle gwinGWindowCreate(GDisplay *g, GWindowObject *pgw, const GWindowInit *pInit) { + if (!(pgw = _gwindowCreate(g, pgw, pInit, &basegwinVMT, 0))) + return 0; + + gwinSetVisible(pgw, pInit->show); + + return pgw; +} + +void gwinDestroy(GHandle gh) { + _gwinDestroy(gh, REDRAW_WAIT); +} + +const char *gwinGetClassName(GHandle gh) { + return gh->vmt->classname; +} + +bool_t gwinGetVisible(GHandle gh) { + return (gh->flags & GWIN_FLG_SYSVISIBLE) ? TRUE : FALSE; +} + +bool_t gwinGetEnabled(GHandle gh) { + return (gh->flags & GWIN_FLG_SYSENABLED) ? TRUE : FALSE; +} + +#if GDISP_NEED_TEXT + void gwinSetFont(GHandle gh, font_t font) { + gh->font = font; + } +#endif + +void gwinClear(GHandle gh) { + /* + * Don't render anything when the window is not visible but + * still call the AfterClear() routine as some widgets will + * need this to clear internal buffers or similar + */ + if (_gwinDrawStart(gh)) { + gdispGFillArea(gh->display, gh->x, gh->y, gh->width, gh->height, gh->bgcolor); + _gwinDrawEnd(gh); + } + if (gh->vmt->AfterClear) + gh->vmt->AfterClear(gh); +} + +void gwinDrawPixel(GHandle gh, coord_t x, coord_t y) { + if (!_gwinDrawStart(gh)) return; + gdispGDrawPixel(gh->display, gh->x+x, gh->y+y, gh->color); + _gwinDrawEnd(gh); +} + +void gwinDrawLine(GHandle gh, coord_t x0, coord_t y0, coord_t x1, coord_t y1) { + if (!_gwinDrawStart(gh)) return; + gdispGDrawLine(gh->display, gh->x+x0, gh->y+y0, gh->x+x1, gh->y+y1, gh->color); + _gwinDrawEnd(gh); +} + +void gwinDrawBox(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy) { + if (!_gwinDrawStart(gh)) return; + gdispGDrawBox(gh->display, gh->x+x, gh->y+y, cx, cy, gh->color); + _gwinDrawEnd(gh); +} + +void gwinFillArea(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy) { + if (!_gwinDrawStart(gh)) return; + gdispGFillArea(gh->display, gh->x+x, gh->y+y, cx, cy, gh->color); + _gwinDrawEnd(gh); +} + +void gwinBlitArea(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t srcx, coord_t srcy, coord_t srccx, const pixel_t *buffer) { + if (!_gwinDrawStart(gh)) return; + gdispGBlitArea(gh->display, gh->x+x, gh->y+y, cx, cy, srcx, srcy, srccx, buffer); + _gwinDrawEnd(gh); +} + +#if GDISP_NEED_CIRCLE + void gwinDrawCircle(GHandle gh, coord_t x, coord_t y, coord_t radius) { + if (!_gwinDrawStart(gh)) return; + gdispGDrawCircle(gh->display, gh->x+x, gh->y+y, radius, gh->color); + _gwinDrawEnd(gh); + } + + void gwinFillCircle(GHandle gh, coord_t x, coord_t y, coord_t radius) { + if (!_gwinDrawStart(gh)) return; + gdispGFillCircle(gh->display, gh->x+x, gh->y+y, radius, gh->color); + _gwinDrawEnd(gh); + } +#endif + +#if GDISP_NEED_ELLIPSE + void gwinDrawEllipse(GHandle gh, coord_t x, coord_t y, coord_t a, coord_t b) { + if (!_gwinDrawStart(gh)) return; + gdispGDrawEllipse(gh->display, gh->x+x, gh->y+y, a, b, gh->color); + _gwinDrawEnd(gh); + } + + void gwinFillEllipse(GHandle gh, coord_t x, coord_t y, coord_t a, coord_t b) { + if (!_gwinDrawStart(gh)) return; + gdispGFillEllipse(gh->display, gh->x+x, gh->y+y, a, b, gh->color); + _gwinDrawEnd(gh); + } +#endif + +#if GDISP_NEED_ARC + void gwinDrawArc(GHandle gh, coord_t x, coord_t y, coord_t radius, coord_t startangle, coord_t endangle) { + if (!_gwinDrawStart(gh)) return; + gdispGDrawArc(gh->display, gh->x+x, gh->y+y, radius, startangle, endangle, gh->color); + _gwinDrawEnd(gh); + } + + void gwinFillArc(GHandle gh, coord_t x, coord_t y, coord_t radius, coord_t startangle, coord_t endangle) { + if (!_gwinDrawStart(gh)) return; + gdispGFillArc(gh->display, gh->x+x, gh->y+y, radius, startangle, endangle, gh->color); + _gwinDrawEnd(gh); + } +#endif + +#if GDISP_NEED_ARCSECTORS + void gwinDrawArcSectors(GHandle gh, coord_t x, coord_t y, coord_t radius, uint8_t sectors) { + if (!_gwinDrawStart(gh)) return; + gdispGDrawArcSectors(gh->display, gh->x+x, gh->y+y, radius, sectors, gh->color); + _gwinDrawEnd(gh); + } + + void gwinFillArcSectors(GHandle gh, coord_t x, coord_t y, coord_t radius, uint8_t sectors) { + if (!_gwinDrawStart(gh)) return; + gdispGFillArcSectors(gh->display, gh->x+x, gh->y+y, radius, sectors, gh->color); + _gwinDrawEnd(gh); + } +#endif + +#if GDISP_NEED_PIXELREAD + color_t gwinGetPixelColor(GHandle gh, coord_t x, coord_t y) { + if (!_gwinDrawStart(gh)) return (color_t)0; + return gdispGGetPixelColor(gh->display, gh->x+x, gh->y+y); + _gwinDrawEnd(gh); + } +#endif + +#if GDISP_NEED_TEXT + void gwinDrawChar(GHandle gh, coord_t x, coord_t y, char c) { + if (!gh->font || !_gwinDrawStart(gh)) return; + gdispGDrawChar(gh->display, gh->x+x, gh->y+y, c, gh->font, gh->color); + _gwinDrawEnd(gh); + } + + void gwinFillChar(GHandle gh, coord_t x, coord_t y, char c) { + if (!gh->font || !_gwinDrawStart(gh)) return; + gdispGFillChar(gh->display, gh->x+x, gh->y+y, c, gh->font, gh->color, gh->bgcolor); + _gwinDrawEnd(gh); + } + + void gwinDrawString(GHandle gh, coord_t x, coord_t y, const char *str) { + if (!gh->font || !_gwinDrawStart(gh)) return; + gdispGDrawString(gh->display, gh->x+x, gh->y+y, str, gh->font, gh->color); + _gwinDrawEnd(gh); + } + + void gwinFillString(GHandle gh, coord_t x, coord_t y, const char *str) { + if (!gh->font || !_gwinDrawStart(gh)) return; + gdispGFillString(gh->display, gh->x+x, gh->y+y, str, gh->font, gh->color, gh->bgcolor); + _gwinDrawEnd(gh); + } + + void gwinDrawStringBox(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy, const char* str, justify_t justify) { + if (!gh->font || !_gwinDrawStart(gh)) return; + gdispGDrawStringBox(gh->display, gh->x+x, gh->y+y, cx, cy, str, gh->font, gh->color, justify); + _gwinDrawEnd(gh); + } + + void gwinFillStringBox(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy, const char* str, justify_t justify) { + if (!gh->font || !_gwinDrawStart(gh)) return; + gdispGFillStringBox(gh->display, gh->x+x, gh->y+y, cx, cy, str, gh->font, gh->color, gh->bgcolor, justify); + _gwinDrawEnd(gh); + } +#endif + +#if GDISP_NEED_CONVEX_POLYGON + void gwinDrawPoly(GHandle gh, coord_t tx, coord_t ty, const point *pntarray, unsigned cnt) { + if (!_gwinDrawStart(gh)) return; + gdispGDrawPoly(gh->display, tx+gh->x, ty+gh->y, pntarray, cnt, gh->color); + _gwinDrawEnd(gh); + } + + void gwinFillConvexPoly(GHandle gh, coord_t tx, coord_t ty, const point *pntarray, unsigned cnt) { + if (!_gwinDrawStart(gh)) return; + gdispGFillConvexPoly(gh->display, tx+gh->x, ty+gh->y, pntarray, cnt, gh->color); + _gwinDrawEnd(gh); + } + void gwinDrawThickLine(GHandle gh, coord_t x0, coord_t y0, coord_t x1, coord_t y1, coord_t width, bool_t round) { + if (!_gwinDrawStart(gh)) return; + gdispGDrawThickLine(gh->display, gh->x+x0, gh->y+y0, gh->x+x1, gh->y+y1, gh->color, width, round); + _gwinDrawEnd(gh); + } +#endif + +#if GDISP_NEED_IMAGE + gdispImageError gwinDrawImage(GHandle gh, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy) { + gdispImageError ret; + + if (!_gwinDrawStart(gh)) return GDISP_IMAGE_ERR_OK; + ret = gdispGImageDraw(gh->display, img, gh->x+x, gh->y+y, cx, cy, sx, sy); + _gwinDrawEnd(gh); + return ret; + } +#endif + +#endif /* GFX_USE_GWIN */ +/** @} */ + diff --git a/src/gwin/gwin.h b/src/gwin/gwin.h new file mode 100644 index 00000000..81bf38e5 --- /dev/null +++ b/src/gwin/gwin.h @@ -0,0 +1,1026 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gwin/gwin.h + * + * @defgroup Window Window + * @ingroup Windows + * + * @details GWIN provides a basic window manager which allows it to easily + * create and destroy different windows at runtime. Each window + * will have it's own properties such as colors as well as + * it's own drawing origin. + * + * @pre GFX_USE_GWIN must be set to TRUE in your gfxconf.h + * @{ + */ + +#ifndef _GWIN_H +#define _GWIN_H + +#include "gfx.h" + +#if GFX_USE_GWIN || defined(__DOXYGEN__) + +/* Forward declaration */ +typedef struct GWindowObject *GHandle; + +/** + * @brief A window object structure + * @note Do not access the members directly. Treat it as a black-box and use the method functions. + * @{ + */ +typedef struct GWindowObject { + #if GWIN_NEED_WINDOWMANAGER + // This MUST be the first member of the structure + gfxQueueASyncItem wmq; // @< The next window (for the window manager) + #endif + const struct gwinVMT *vmt; // @< The VMT for this GWIN + GDisplay * display; // @< The display this window is on. + coord_t x, y; // @< Screen relative position + coord_t width, height; // @< Dimensions of this window + color_t color, bgcolor; // @< The current drawing colors + uint32_t flags; // @< Window flags (the meaning is private to the GWIN class) + #if GDISP_NEED_TEXT + font_t font; // @< The current font + #endif + #if GWIN_NEED_CONTAINERS + GHandle parent; // @< The parent window + #endif +} GWindowObject, * GHandle; +/** @} */ + +/** + * @brief The structure to initialise a GWIN. + * + * @note Some gwin's will need extra parameters. + * @note The dimensions and position may be changed to fit on the real screen. + * @note If you create this structure on the stack, you should always memset + * it to all zero's first in case a future version of the software + * add's extra fields. Alternatively you can use @p gwinClearInit() + * to clear it. + * + * @{ + */ +typedef struct GWindowInit { + coord_t x, y; // @< The initial position relative to its parent + coord_t width, height; // @< The initial dimension + bool_t show; // @< Should the window be visible initially + #if GWIN_NEED_CONTAINERS + GHandle parent; // @< The parent - must be a container or NULL + #endif +} GWindowInit; +/** @} */ + +/** + * @brief A window's minimized, maximized or normal size + */ +typedef enum { GWIN_NORMAL, GWIN_MAXIMIZE, GWIN_MINIMIZE } GWindowMinMax; + +#ifdef __cplusplus +extern "C" { +#endif + +/*------------------------------------------------- + * Window Manager functions + *-------------------------------------------------*/ + +#if GWIN_NEED_WINDOWMANAGER || defined(__DOXYGEN__) + // Forward definition + struct GWindowManager; + + /** + * @brief Set the window manager for the GWIN system. + * + * @param[in] gwm The window manager to use. Can be NULL to turn off the existing window manager. + * + * @note A window manager is responsible for handling when window visibility is changed or + * a window is resized for moved. Note that only saved window states will be redrawn. Each + * window type can save different information (or none at all). See the documentation on each window + * type to see which information it saves (and can therefore be automatically redrawn). + * For window types that do not save any state information, the window manager determines what to do. + * Generally it will just clear the window to its background color. + * + * @api + */ + void gwinSetWindowManager(struct GWindowManager *gwm); +#endif + +/*------------------------------------------------- + * Functions that affect all windows + *-------------------------------------------------*/ + + /** + * @brief Clear a GWindowInit structure to all zero's + * @note This function is provided just to prevent problems + * on operating systems where using memset() causes issues + * in the users application. + * + * @param[in] pwi The GWindowInit structure to clear + * + * @api + */ + void gwinClearInit(GWindowInit *pwi); + + /** + * @brief Set the default foreground color for all new GWIN windows + * + * @param[in] clr The color to be set + * + * @api + */ + void gwinSetDefaultColor(color_t clr); + + /** + * @brief Get the default foreground color for all new GWIN windows + * + * @return The current default color for all new GWIN windows + * + * @api + */ + color_t gwinGetDefaultColor(void); + + /** + * @brief Set the default background color for all new GWIN windows + * + * @param[in] bgclr The background color + * + * @api + */ + void gwinSetDefaultBgColor(color_t bgclr); + + /** + * @brief Get the default background color for all new GWIN windows + * + * @return The current default background color for all new GWIN windows + * + * @api + */ + color_t gwinGetDefaultBgColor(void); + + #if GDISP_NEED_TEXT || defined(__DOXYGEN__) + /** + * @brief Set the default font for all new GWIN windows + * + * @param[in] font The new font to be set + * + * @api + */ + void gwinSetDefaultFont(font_t font); + + /** + * @brief Get the current default font + * + * @return The current default font + * + * @api + */ + font_t gwinGetDefaultFont(void); + #endif + +/*------------------------------------------------- + * Base functions + *-------------------------------------------------*/ + + /** + * @brief Create a basic window. + * @return NULL if there is no resultant drawing area, otherwise a window handle. + * + * @param[in] g The GDisplay to display this window on + * @param[in] pgw The window structure to initialize. If this is NULL the structure is dynamically allocated. + * @param[in] pInit How to initialise the window + * + * @note The drawing color and the background color get set to the current defaults. If you haven't called + * @p gwinSetDefaultColor() or @p gwinSetDefaultBgColor() then these are White and Black respectively. + * @note The font gets set to the current default font. If you haven't called @p gwinSetDefaultFont() then there + * is no default font and text drawing operations will no nothing. + * @note A basic window does not save the drawing state. It is not automatically redrawn if the window is moved or + * its visibility state is changed. + * + * @api + */ + GHandle gwinGWindowCreate(GDisplay *g, GWindowObject *pgw, const GWindowInit *pInit); + #define gwinWindowCreate(pgw, pInit) gwinGWindowCreate(GDISP, pgw, pInit); + + /** + * @brief Destroy a window (of any type). Releases any dynamically allocated memory. + * + * @param[in] gh The window handle + * + * @api + */ + void gwinDestroy(GHandle gh); + + /** + * @brief Get the real class name of the GHandle + * @details Returns a string describing the object class. + * + * @param[in] gh The window + * + * @return A string describing the object class. + * + * @api + */ + const char* gwinGetClassName(GHandle gh); + + /** + * @brief Get an ID that uniquely describes the class of the GHandle + * + * @param[in] gh The window + * + * @api + */ + #define gwinGetClassID(gh) ((void *)((gh)->vmt)) + + /** + * @brief Get the X coordinate of the window + * @details Returns the X coordinate of the origin of the window. + * The coordinate is relative to the physical screen zero point. + * + * @param[in] gh The window + * + * @api + */ + #define gwinGetScreenX(gh) ((gh)->x) + + /** + * @brief Get the Y coordinate of the window + * @details Returns the Y coordinate of the origin of the window. + * The coordinate is relative to the physical screen zero point. + * + * @param[in] gh The window + * + * @api + */ + #define gwinGetScreenY(gh) ((gh)->y) + + /** + * @brief Get the width of the window + * + * @param[in] gh The window + * + * @api + */ + #define gwinGetWidth(gh) ((gh)->width) + + /** + * @brief Get the height of the window + * + * @param[in] gh The window + * + * @api + */ + #define gwinGetHeight(gh) ((gh)->height) + + /** + * @brief Set foreground color + * @details Set the color which will be used to draw + * + * @param[in] gh The window + * @param[in] clr The color to be set + * + * @api + */ + #define gwinSetColor(gh, clr) (gh)->color = (clr) + + /** + * @brief Set background color + * @details Set the color which will be used as background + * @note gwinClear() must be called to set the background color + * + * @param[in] gh The window + * @param[in] bgclr The background color + * + * @api + */ + #define gwinSetBgColor(gh, bgclr) (gh)->bgcolor = (bgclr) + + /** + * @brief Get the foreground color of a window + * + * @param[in] gh The window + * + * @api + */ + #define gwinGetColor(gh) (gh)->color + + /** + * @brief Get the background color of a window + * + * @param[in] gh The window + * + * @api + */ + #define gwinGetBgColor(gh) (gh)->bgcolor + + /** + * @brief Sets whether a window is visible or not + * + * @param[in] gh The window + * @param[in] visible Whether the window should be visible or not + * + * @note When a window is marked as not visible, drawing operations + * on the window do nothing. + * @note When a window is marked as visible, it is not automatically + * redrawn as many window types don't remember their drawing state. + * Widgets such as Buttons, Sliders etc will be redrawn. + * @note If there is no window manager in use, when a window is marked + * as not visible, nothing is done to remove the window from the screen. + * When there is a window manager, it is up to the window manager to + * handle what happens. + * @note Even when you mark a window as visible, it may still not be displayed + * if it's parent is invisible. When the parent becomes visible this child + * will automatically be shown because it is already marked as visible. + * + * @api + */ + void gwinSetVisible(GHandle gh, bool_t visible); + + /** + * @brief Makes a widget become visible + * + * @param[in] gh The window handle + * + * @api + */ + #define gwinShow(gh) gwinSetVisible(gh, TRUE) + + /** + * @brief Makes a widget become invisible + * + * @param[in] gh The window handle + * + * @api + */ + #define gwinHide(gh) gwinSetVisible(gh, FALSE) + + /** + * @brief Gets the visibility of a window + * @return TRUE if visible + * + * @note It is possible for a child to be marked as visible by @p gwinSetVisible() + * but for this call to return FALSE if one of its parents are not visible. + * + * @param[in] gh The window + * + * @api + */ + bool_t gwinGetVisible(GHandle gh); + + /** + * @brief Enable or disable a window + * + * @param[in] gh The window handle + * @param[in] enabled Enable or disable the window + * + * @note The window is automatically redrawn if it supports self-redrawing. + * @note Even when you mark a window as enabled, it may still remain disabled + * if it's parent is disabled. When the parent becomes enabled this child + * will automatically be enabled because it is already marked as enabled. + * + * @api + */ + void gwinSetEnabled(GHandle gh, bool_t enabled); + + /** + * @brief Enables a widget + * + * @param[in] gh The window handle + * + * @api + */ + #define gwinEnable(gh) gwinSetEnabled(gh, TRUE) + + /** + * @brief Disables a widget + * + * @param[in] gh The window handle + * + * @api + */ + #define gwinDisable(gh) gwinSetEnabled(gh, FALSE) + + /** + * @brief Gets the enabled state of a window + * @return TRUE if enabled + * + * @note It is possible for a child to be marked as enabled by @p gwinSetEnabled() + * but for this call to return FALSE if one of its parents are not enabled. + * + * @param[in] gh The window + * + * @api + */ + bool_t gwinGetEnabled(GHandle gh); + + /** + * @brief Move a window + * + * @param[in] gh The window + * @param[in] x, y The new position (screen relative) for this window + * + * @note The final window position may not be the requested position. Windows + * are clipped to the screen area and the window manager may also affect the position. + * @note The window is redrawn if it is visible. See the comments in @p gwinSetVisible() + * with regard to what can be redrawn and what can't. + * @note It is up to the window manager to determine what happens with the screen area + * uncovered by moving the window. When there is no window manager, nothing + * is done with the uncovered area. + * + * @api + */ + void gwinMove(GHandle gh, coord_t x, coord_t y); + + /** + * @brief Resize a window + * + * @param[in] gh The window + * @param[in] width, height The new size of the window + * + * @note The final window size may not be the requested size. Windows + * are clipped to the screen area and the window manager may also affect the size. + * @note The window is redrawn if it is visible. See the comments in @p gwinSetVisible() + * with regard to what can be redrawn and what can't. + * @note It is up to the window manager to determine what happens with any screen area + * uncovered by resizing the window. When there is no window manager, nothing + * is done with the uncovered area. + * + * @api + */ + void gwinResize(GHandle gh, coord_t width, coord_t height); + + /** + * @brief Redraw a window + * + * @param[in] gh The window + * + * @note This is normally never required as windows and widgets will redraw as required. + * Note that some windows are incapable of redrawing themselves as they don't save + * their drawing state. + * + * @api + */ + void gwinRedraw(GHandle gh); + + #if GWIN_NEED_WINDOWMANAGER || defined (__DOXYGEN__) + /** + * @brief Redraw a window + * + * @param[in] g The display to redraw. Passing NULL will redraw all displays. + * @param[in] preserve Should the redraw try to preserve existing screen data for those + * windows that can't redraw themselves? + * + * @note This is normally never required as windows and widgets will redraw as required. + * @note Some windows are incapable of redrawing themselves as they don't save + * their drawing state. + * @note This does not clear the background - just redraws the gwin windows (where possible) + * + * @api + */ + void gwinRedrawDisplay(GDisplay *g, bool_t preserve); + + /** + * @brief Minimize, Maximize or Restore a window + * @pre GWIN_NEED_WINDOWMANAGER must be TRUE + * + * @param[in] gh The window + * @param[in] minmax The new minimized/maximized state + * + * @note The final window state may not be the requested state. Window Managers + * do not need to implement changing the minmax state. If there is no + * window manager this call is ignored. + * @note The window is redrawn if it is changed. See the comments in @p gwinSetVisible() + * with regard to what can be redrawn and what can't. + * @note It is up to the window manager to determine what happens with any screen area + * uncovered by resizing the window. + * @note When a window is minimised it may be asked to draw the window or the window + * manager may draw the minimised window. + * + * @api + */ + void gwinSetMinMax(GHandle gh, GWindowMinMax minmax); + + /** + * @brief Get the Minimized/Maximized state of a window + * @pre GWIN_NEED_WINDOWMANAGER must be TRUE + * + * @param[in] gh The window + * + * @return GWIN_NORMAL, GWIN_MAXIMIZE or GWIN_MINIMIZE + * + * @api + */ + GWindowMinMax gwinGetMinMax(GHandle gh); + + /** + * @brief Raise a window to the top of the z-order + * @pre GWIN_NEED_WINDOWMANAGER must be TRUE + * + * @param[in] gh The window + * + * @note The window z-order is only supported by some window managers. See the comments + * in @p gwinSetVisible() with regard to what can be redrawn and what can't. + * + * @api + */ + void gwinRaise(GHandle gh); + + /** + * @brief Get the next window in the z-order + * @return The next window or NULL if no more windows + * + * @param[in] gh The previous window or NULL to get the first window + * + * @note This returns the next window in the system from top to bottom. + * @note Where there are parent child relationships, this ignores them + * and will list all windows in the system. There is no defined + * order between children of siblings and they can in fact be mixed + * in order. The only relationship honored is that parents will be + * listed before their children. + * + * @api + */ + GHandle gwinGetNextWindow(GHandle gh); + + #endif + + #if GDISP_NEED_TEXT || defined(__DOXYGEN__) + /** + * @brief Set the current font for this window. + * + * @param[in] gh The window handle + * @param[in] font The font to use for text functions + * + * @api + */ + void gwinSetFont(GHandle gh, font_t font); + #endif + +/*------------------------------------------------- + * Drawing functions + *-------------------------------------------------*/ + + /** + * @brief Clear the window + * @note Uses the current background color to clear the window + * + * @param[in] gh The window handle + * + * @api + */ + void gwinClear(GHandle gh); + + /** + * @brief Set a pixel in the window + * @note Uses the current foreground color to set the pixel + * @note May leave GDISP clipping to this window's dimensions + * + * @param[in] gh The window handle + * @param[in] x,y The coordinates of the pixel + * + * @api + */ + void gwinDrawPixel(GHandle gh, coord_t x, coord_t y); + + /** + * @brief Draw a line in the window + * @note Uses the current foreground color to draw the line + * @note May leave GDISP clipping to this window's dimensions + * + * @param[in] gh The window handle + * @param[in] x0,y0 The start position + * @param[in] x1,y1 The end position + * + * @api + */ + void gwinDrawLine(GHandle gh, coord_t x0, coord_t y0, coord_t x1, coord_t y1); + + /** + * @brief Draw a box in the window + * @note Uses the current foreground color to draw the box + * @note May leave GDISP clipping to this window's dimensions + * + * @param[in] gh The window handle + * @param[in] x,y The start position + * @param[in] cx,cy The size of the box (outside dimensions) + * + * @api + */ + void gwinDrawBox(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy); + + /** + * @brief Fill an rectangular area in the window + * @note Uses the current foreground color to fill the box + * @note May leave GDISP clipping to this window's dimensions + * + * @param[in] gh The window handle + * @param[in] x,y The start position + * @param[in] cx,cy The size of the box (outside dimensions) + * + * @api + */ + void gwinFillArea(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy); + + /** + * @brief Fill an area in the window using the supplied bitmap. + * @details The bitmap is in the pixel format specified by the low level driver + * @note If GDISP_NEED_ASYNC is defined then the buffer must be static + * or at least retained until this call has finished the blit. You can + * tell when all graphics drawing is finished by @p gdispIsBusy() going FALSE. + * @note May leave GDISP clipping to this window's dimensions + * + * @param[in] gh The window handle + * @param[in] x, y The start filled area + * @param[in] cx, cy The width and height to be filled + * @param[in] srcx, srcy The bitmap position to start the fill from + * @param[in] srccx The width of a line in the bitmap. + * @param[in] buffer The pixels to use to fill the area. + * + * @api + */ + void gwinBlitArea(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t srcx, coord_t srcy, coord_t srccx, const pixel_t *buffer); + +/*------------------------------------------------- + * Circle, ellipse, arc and arc-sectors functions + *-------------------------------------------------*/ + + #if GDISP_NEED_CIRCLE || defined(__DOXYGEN__) + /** + * @brief Draw a circle in the window. + * @note Uses the current foreground color to draw the circle + * @note May leave GDISP clipping to this window's dimensions + * + * @param[in] gh The window handle + * @param[in] x, y The center of the circle + * @param[in] radius The radius of the circle + * + * @api + */ + void gwinDrawCircle(GHandle gh, coord_t x, coord_t y, coord_t radius); + + /** + * @brief Draw a filled circle in the window. + * @note Uses the current foreground color to draw the filled circle + * @note May leave GDISP clipping to this window's dimensions + * + * @param[in] gh The window handle + * @param[in] x, y The center of the circle + * @param[in] radius The radius of the circle + * + * @api + */ + void gwinFillCircle(GHandle gh, coord_t x, coord_t y, coord_t radius); + #endif + + #if GDISP_NEED_ELLIPSE || defined(__DOXYGEN__) + /** + * @brief Draw an ellipse. + * @note Uses the current foreground color to draw the ellipse + * @note May leave GDISP clipping to this window's dimensions + * + * @param[in] gh The window handle + * @param[in] x,y The center of the ellipse + * @param[in] a,b The dimensions of the ellipse + * + * @api + */ + void gwinDrawEllipse(GHandle gh, coord_t x, coord_t y, coord_t a, coord_t b); + + /** + * @brief Draw an filled ellipse. + * @note Uses the current foreground color to draw the filled ellipse + * @note May leave GDISP clipping to this window's dimensions + * + * @param[in] gh The window handle + * @param[in] x,y The center of the ellipse + * @param[in] a,b The dimensions of the ellipse + * + * @api + */ + void gwinFillEllipse(GHandle gh, coord_t x, coord_t y, coord_t a, coord_t b); + #endif + + #if GDISP_NEED_ARC || defined(__DOXYGEN__) + /* + * @brief Draw an arc in the window. + * @note Uses the current foreground color to draw the arc + * @note May leave GDISP clipping to this window's dimensions + * + * @param[in] gh The window handle + * @param[in] x,y The center point + * @param[in] radius The radius of the arc + * @param[in] start The start angle (0 to 360) + * @param[in] end The end angle (0 to 360) + * + * @api + */ + void gwinDrawArc(GHandle gh, coord_t x, coord_t y, coord_t radius, coord_t startangle, coord_t endangle); + + /* + * @brief Draw a filled arc in the window. + * @note Uses the current foreground color to draw the filled arc + * @note May leave GDISP clipping to this window's dimensions + * + * @param[in] gh The window handle + * @param[in] x,y The center point + * @param[in] radius The radius of the arc + * @param[in] start The start angle (0 to 360) + * @param[in] end The end angle (0 to 360) + * + * @api + */ + void gwinFillArc(GHandle gh, coord_t x, coord_t y, coord_t radius, coord_t startangle, coord_t endangle); + #endif + + #if GDISP_NEED_ARCSECTORS || defined(__DOXYGEN__) + /* + * @brief Draw a selection of 45 degree arcs of a circle in the window. + * @note Uses the current foreground color to draw the arc sector + * @note May leave GDISP clipping to this window's dimensions + * + * @param[in] gh The window handle + * @param[in] x,y The center of the circle + * @param[in] radius The radius of the circle + * @param[in] sectors Bits determine which sectors are drawn. + * Bits go anti-clockwise from the 0 degree mark (y = 0, x is positive), as follows: + * bit 0 - upper right right ----- + * bit 1 - upper upper right /2 1\ + * bit 2 - upper upper left /3 0\ + * bit 3 - upper left left \4 7/ + * bit 4 - lower left left \5 6/ + * bit 5 - lower lower left ----- + * bit 6 - lower lower right + * bit 7 - lower left left + * + * @api + */ + void gwinDrawArcSectors(GHandle gh, coord_t x, coord_t y, coord_t radius, uint8_t sectors); + + /* + * @brief Draw a filled selection of 45 degree arcs of a circle in the window. + * @note Uses the current foreground color to draw the arc sector + * @note May leave GDISP clipping to this window's dimensions + * + * @param[in] gh The window handle + * @param[in] x,y The center of the circle + * @param[in] radius The radius of the circle + * @param[in] sectors Bits determine which sectors are drawn. + * Bits go anti-clockwise from the 0 degree mark (y = 0, x is positive), as follows: + * bit 0 - upper right right ----- + * bit 1 - upper upper right /2 1\ + * bit 2 - upper upper left /3 0\ + * bit 3 - upper left left \4 7/ + * bit 4 - lower left left \5 6/ + * bit 5 - lower lower left ----- + * bit 6 - lower lower right + * bit 7 - lower left left + * + * @api + */ + void gwinFillArcSectors(GHandle gh, coord_t x, coord_t y, coord_t radius, uint8_t sectors); + #endif + +/*------------------------------------------------- + * Pixel read-back functions + *-------------------------------------------------*/ + + #if GDISP_NEED_PIXELREAD || defined(__DOXYGEN__) + /** + * @brief Get the color of a pixel in the window. + * @return The color of the pixel. + * @note May leave GDISP clipping to this window's dimensions + * + * @param[in] gh The window handle + * @param[in] x,y The position in the window + * + * @api + */ + color_t gwinGetPixelColor(GHandle gh, coord_t x, coord_t y); + #endif + +/*------------------------------------------------- + * Text functions + *-------------------------------------------------*/ + + #if GDISP_NEED_TEXT || defined(__DOXYGEN__) + /** + * @brief Draw a text character at the specified position in the window. + * @pre The font must have been set. + * @note Uses the current foreground color to draw the character + * @note May leave GDISP clipping to this window's dimensions + * + * @param[in] gh The window handle + * @param[in] x,y The position for the text + * @param[in] c The character to draw + * + * @api + */ + void gwinDrawChar(GHandle gh, coord_t x, coord_t y, char c); + + /** + * @brief Draw a text character with a filled background at the specified position in the window. + * @pre The font must have been set. + * @note Uses the current foreground color to draw the character and fills the background using the background drawing color + * @note May leave GDISP clipping to this window's dimensions + * + * @param[in] gh The window handle + * @param[in] x,y The position for the text + * @param[in] c The character to draw + * + * @api + */ + void gwinFillChar(GHandle gh, coord_t x, coord_t y, char c); + + /** + * @brief Draw a text string in the window + * @pre The font must have been set. + * @note Uses the current foreground color to draw the character + * @note May leave GDISP clipping to this window's dimensions + * + * @param[in] gh The window handle + * @param[in] x,y The position for the text + * @param[in] str The string to draw + * + * @api + */ + void gwinDrawString(GHandle gh, coord_t x, coord_t y, const char *str); + + /** + * @brief Draw a text string with a filled background in the window + * @pre The font must have been set. + * @note Uses the current foreground color to draw the character and fills the background using the background drawing color + * @note May leave GDISP clipping to this window's dimensions + * + * @param[in] gh The window handle + * @param[in] x,y The position for the text + * @param[in] str The string to draw + * + * @api + */ + void gwinFillString(GHandle gh, coord_t x, coord_t y, const char *str); + + /** + * @brief Draw a text string verticly centered within the specified box. + * @pre The font must have been set. + * @note Uses the current foreground color to draw the character. + * @note The specified box does not need to align with the window box + * @note May leave GDISP clipping to this window's dimensions + * + * @param[in] gh The window handle + * @param[in] x,y The position for the text (need to define top-right or base-line - check code) + * @param[in] cx,cy The width and height of the box + * @param[in] str The string to draw + * @param[in] justify Justify the text left, center or right within the box + * + * @api + */ + void gwinDrawStringBox(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy, const char* str, justify_t justify); + + /** + * @brief Draw a text string verticly centered within the specified filled box. + * @pre The font must have been set. + * @note Uses the current foreground color to draw the character and fills the background using the background drawing color + * @note The entire box is filled. Note this box does not need to align with the window box + * @note May leave GDISP clipping to this window's dimensions + * + * @param[in] gh The window handle + * @param[in] x,y The position for the text (need to define top-right or base-line - check code) + * @param[in] cx,cy The width and height of the box + * @param[in] str The string to draw + * @param[in] justify Justify the text left, center or right within the box + * + * @api + */ + void gwinFillStringBox(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy, const char* str, justify_t justify); + #endif + +/*------------------------------------------------- + * Polygon functions + *-------------------------------------------------*/ + + #if GDISP_NEED_CONVEX_POLYGON || defined(__DOXYGEN__) + /** + * @brief Draw an enclosed polygon (convex, non-convex or complex). + * + * @note Uses the current foreground color. + * + * @param[in] gh The window handle + * @param[in] tx, ty Transform all points in pntarray by tx, ty + * @param[in] pntarray An array of points + * @param[in] cnt The number of points in the array + * + * @api + */ + void gwinDrawPoly(GHandle gh, coord_t tx, coord_t ty, const point *pntarray, unsigned cnt); + + /** + * @brief Fill a convex polygon + * @details Doesn't handle non-convex or complex polygons. + * + * @note Uses the current foreground color. + * + * @param[in] gh The window handle + * @param[in] tx, ty Transform all points in pntarray by tx, ty + * @param[in] pntarray An array of points + * @param[in] cnt The number of points in the array + * + * @note Convex polygons are those that have no internal angles. That is; + * you can draw a line from any point on the polygon to any other point + * on the polygon without it going outside the polygon. In our case we generalise + * this a little by saying that an infinite horizontal line (at any y value) will cross + * no more than two edges on the polygon. Some non-convex polygons do fit this criteria + * and can therefore be drawn. + * @note This routine is designed to be very efficient with even simple display hardware. + * + * @api + */ + void gwinFillConvexPoly(GHandle gh, coord_t tx, coord_t ty, const point *pntarray, unsigned cnt); + + /** + * @brief Draw a thick line in the window + * @details The line thickness is specified in pixels. The line ends can + * be selected to be either flat or round. + * @note Uses gdispGFillConvexPoly() internally to perform the drawing. + * @note Uses the current foreground color to draw the line + * + * @param[in] gh The window handle + * @param[in] x0,y0 The start position + * @param[in] x1,y1 The end position + * @param[in] width The width of the line + * @param[in] round Use round ends for the line + * + * @api + */ + void gwinDrawThickLine(GHandle gh, coord_t x0, coord_t y0, coord_t x1, coord_t y1, coord_t width, bool_t round); + #endif + +/*------------------------------------------------- + * Image functions + *-------------------------------------------------*/ + + #if GDISP_NEED_IMAGE || defined(__DOXYGEN__) + /** + * @brief Draw the image + * @return GDISP_IMAGE_ERR_OK (0) on success or an error code. + * + * @param[in] gh The window handle + * @param[in] img The image structure + * @param[in] x,y The window location to draw the image + * @param[in] cx,cy The area on the screen to draw + * @param[in] sx,sy The image position to start drawing at + * + * @pre gdispImageOpen() must have returned successfully. + * + * @note If sx,sy + cx,cy is outside the image boundaries the area outside the image + * is simply not drawn. + * @note If @p gdispImageCache() has been called first for this frame, this routine will draw using a + * fast blit from the cached frame. If not, it reads the input and decodes it as it + * is drawing. This may be significantly slower than if the image has been cached (but + * uses a lot less RAM) + * + * @api + */ + gdispImageError gwinDrawImage(GHandle gh, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); + #endif + +#ifdef __cplusplus +} +#endif + +/*------------------------------------------------- + * Additional functionality + *-------------------------------------------------*/ + + /* Include widgets */ + #if GWIN_NEED_WIDGET || defined(__DOXYGEN__) + #include "gwin_widget.h" + #endif + + /* Include containers */ + #if GWIN_NEED_CONTAINERS || defined(__DOXYGEN__) + #include "gwin_container.h" + #endif + + /* Include vanilla window objects */ + #if GWIN_NEED_CONSOLE || defined(__DOXYGEN__) + #include "gwin_console.h" + #endif + #if GWIN_NEED_GRAPH || defined(__DOXYGEN__) + #include "gwin_graph.h" + #endif + #if GWIN_NEED_IMAGE || defined(__DOXYGEN__) + #include "gwin_image.h" + #endif + #if GWIN_NEED_GL3D || defined(__DOXYGEN__) + #include "gwin_gl3d.h" + #endif + +#endif /* GFX_USE_GWIN */ + +#endif /* _GWIN_H */ +/** @} */ diff --git a/src/gwin/gwin.mk b/src/gwin/gwin.mk new file mode 100644 index 00000000..23993346 --- /dev/null +++ b/src/gwin/gwin.mk @@ -0,0 +1,19 @@ +GFXSRC += $(GFXLIB)/src/gwin/gwin.c \ + $(GFXLIB)/src/gwin/gwin_widget.c \ + $(GFXLIB)/src/gwin/gwin_wm.c \ + $(GFXLIB)/src/gwin/gwin_console.c \ + $(GFXLIB)/src/gwin/gwin_graph.c \ + $(GFXLIB)/src/gwin/gwin_button.c \ + $(GFXLIB)/src/gwin/gwin_slider.c \ + $(GFXLIB)/src/gwin/gwin_checkbox.c \ + $(GFXLIB)/src/gwin/gwin_image.c \ + $(GFXLIB)/src/gwin/gwin_label.c \ + $(GFXLIB)/src/gwin/gwin_radio.c \ + $(GFXLIB)/src/gwin/gwin_list.c \ + $(GFXLIB)/src/gwin/gwin_progressbar.c \ + $(GFXLIB)/src/gwin/gwin_container.c \ + $(GFXLIB)/src/gwin/gwin_frame.c \ + $(GFXLIB)/src/gwin/gwin_tabset.c \ + $(GFXLIB)/src/gwin/gwin_gl3d.c \ + +GFXINC += $(GFXLIB)/3rdparty/tinygl-0.4-ugfx/include diff --git a/src/gwin/gwin_console.h b/src/gwin/gwin_console.h index 77e623eb..a21642aa 100644 --- a/src/gwin/gwin_console.h +++ b/src/gwin/gwin_console.h @@ -24,7 +24,7 @@ #ifndef _GWIN_CONSOLE_H #define _GWIN_CONSOLE_H -/* This file is included within "src/gwin/sys_defs.h" */ +/* This file is included within "src/gwin/gwin.h" */ // A console window. Supports wrapped text writing and a cursor. typedef struct GConsoleObject { diff --git a/src/gwin/gwin_container.h b/src/gwin/gwin_container.h index 19562a7d..c40683db 100644 --- a/src/gwin/gwin_container.h +++ b/src/gwin/gwin_container.h @@ -21,7 +21,7 @@ #ifndef _GCONTAINER_H #define _GCONTAINER_H -/* This file is included within "src/gwin/sys_defs.h" */ +/* This file is included within "src/gwin/gwin.h" */ // Forward definition struct GContainerObject; diff --git a/src/gwin/gwin_gl3d.h b/src/gwin/gwin_gl3d.h index 644f45bf..84ead5d9 100644 --- a/src/gwin/gwin_gl3d.h +++ b/src/gwin/gwin_gl3d.h @@ -22,7 +22,7 @@ #ifndef _GWIN_GL3D_H #define _GWIN_GL3D_H -/* This file is included within "src/gwin/sys_defs.h" */ +/* This file is included within "src/gwin/gwin.h" */ // A gl3d window diff --git a/src/gwin/gwin_graph.h b/src/gwin/gwin_graph.h index eea80679..8355f494 100644 --- a/src/gwin/gwin_graph.h +++ b/src/gwin/gwin_graph.h @@ -22,7 +22,7 @@ #ifndef _GWIN_GRAPH_H #define _GWIN_GRAPH_H -/* This file is included within "src/gwin/sys_defs.h" */ +/* This file is included within "src/gwin/gwin.h" */ typedef enum GGraphPointType_e { GGRAPH_POINT_NONE, GGRAPH_POINT_DOT, GGRAPH_POINT_SQUARE, GGRAPH_POINT_CIRCLE diff --git a/src/gwin/gwin_gwin.c b/src/gwin/gwin_gwin.c deleted file mode 100644 index eb51b89a..00000000 --- a/src/gwin/gwin_gwin.c +++ /dev/null @@ -1,400 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gwin/gwin_gwin.c - * @brief GWIN sub-system code - */ - -#include "gfx.h" - -#if GFX_USE_GWIN - -#include "gwin_class.h" - -#include <string.h> - -/*----------------------------------------------- - * Data - *-----------------------------------------------*/ - -static const gwinVMT basegwinVMT = { - "GWIN", // The classname - sizeof(GWindowObject), // The object size - 0, // The destroy routine - 0, // The redraw routine - 0, // The after-clear routine -}; - -static color_t defaultFgColor = White; -static color_t defaultBgColor = Black; -#if GDISP_NEED_TEXT - static font_t defaultFont; -#endif - -/*----------------------------------------------- - * Helper Routines - *-----------------------------------------------*/ - -/*----------------------------------------------- - * Class Routines - *-----------------------------------------------*/ - -void _gwinInit(void) -{ - extern void _gwmInit(void); - - _gwmInit(); - #if GWIN_NEED_WIDGET - extern void _gwidgetInit(void); - - _gwidgetInit(); - #endif - #if GWIN_NEED_CONTAINERS - extern void _gcontainerInit(void); - - _gcontainerInit(); - #endif -} - -void _gwinDeinit(void) -{ - extern void _gwmDeinit(void); - - #if GWIN_NEED_CONTAINERS - extern void _gcontainerDeinit(void); - - _gcontainerDeinit(); - #endif - #if GWIN_NEED_WIDGET - extern void _gwidgetDeinit(void); - - _gwidgetDeinit(); - #endif - - _gwmDeinit(); -} - -// Internal routine for use by GWIN components only -// Initialise a window creating it dynamically if required. -GHandle _gwindowCreate(GDisplay *g, GWindowObject *pgw, const GWindowInit *pInit, const gwinVMT *vmt, uint32_t flags) { - // Allocate the structure if necessary - if (!pgw) { - if (!(pgw = gfxAlloc(vmt->size))) - return 0; - pgw->flags = flags|GWIN_FLG_DYNAMIC; - } else - pgw->flags = flags; - - // Initialise all basic fields - pgw->display = g; - pgw->vmt = vmt; - pgw->color = defaultFgColor; - pgw->bgcolor = defaultBgColor; - #if GDISP_NEED_TEXT - pgw->font = defaultFont; - #endif - - // Make sure we don't create nasty problems for ourselves - if (vmt->size > sizeof(GWindowObject)) - memset(pgw+1, 0, vmt->size - sizeof(GWindowObject)); - - if (!_gwinWMAdd(pgw, pInit)) { - if ((pgw->flags & GWIN_FLG_DYNAMIC)) - gfxFree(pgw); - return 0; - } - - return (GHandle)pgw; -} - -// Internal routine for use by GWIN components only -void _gwinDestroy(GHandle gh, GRedrawMethod how) { - if (!gh) - return; - - // Make the window invisible - gwinSetVisible(gh, FALSE); - - // Make sure it is flushed first - must be REDRAW_WAIT or REDRAW_INSESSION - _gwinFlushRedraws(how); - - #if GWIN_NEED_CONTAINERS - // Notify the parent it is about to be deleted - if (gh->parent && ((gcontainerVMT *)gh->parent->vmt)->NotifyDelete) - ((gcontainerVMT *)gh->parent->vmt)->NotifyDelete(gh->parent, gh); - #endif - - // Remove from the window manager - #if GWIN_NEED_WINDOWMANAGER - _GWINwm->vmt->Delete(gh); - #endif - - // Class destroy routine - if (gh->vmt->Destroy) - gh->vmt->Destroy(gh); - - // Clean up the structure - if (gh->flags & GWIN_FLG_DYNAMIC) { - gh->flags = 0; // To be sure, to be sure - gfxFree((void *)gh); - } else - gh->flags = 0; // To be sure, to be sure -} - -/*----------------------------------------------- - * Routines that affect all windows - *-----------------------------------------------*/ - -void gwinClearInit(GWindowInit *pwi) { - char *p; - unsigned len; - - for(p = (char *)pwi, len = sizeof(GWindowInit); len; len--) - *p++ = 0; -} - -void gwinSetDefaultColor(color_t clr) { - defaultFgColor = clr; -} - -color_t gwinGetDefaultColor(void) { - return defaultFgColor; -} - -void gwinSetDefaultBgColor(color_t bgclr) { - defaultBgColor = bgclr; -} - -color_t gwinGetDefaultBgColor(void) { - return defaultBgColor; -} - -#if GDISP_NEED_TEXT - void gwinSetDefaultFont(font_t font) { - defaultFont = font; - } - - font_t gwinGetDefaultFont(void) { - return defaultFont; - } -#endif - -/*----------------------------------------------- - * The GWindow Routines - *-----------------------------------------------*/ - -GHandle gwinGWindowCreate(GDisplay *g, GWindowObject *pgw, const GWindowInit *pInit) { - if (!(pgw = _gwindowCreate(g, pgw, pInit, &basegwinVMT, 0))) - return 0; - - gwinSetVisible(pgw, pInit->show); - - return pgw; -} - -void gwinDestroy(GHandle gh) { - _gwinDestroy(gh, REDRAW_WAIT); -} - -const char *gwinGetClassName(GHandle gh) { - return gh->vmt->classname; -} - -bool_t gwinGetVisible(GHandle gh) { - return (gh->flags & GWIN_FLG_SYSVISIBLE) ? TRUE : FALSE; -} - -bool_t gwinGetEnabled(GHandle gh) { - return (gh->flags & GWIN_FLG_SYSENABLED) ? TRUE : FALSE; -} - -#if GDISP_NEED_TEXT - void gwinSetFont(GHandle gh, font_t font) { - gh->font = font; - } -#endif - -void gwinClear(GHandle gh) { - /* - * Don't render anything when the window is not visible but - * still call the AfterClear() routine as some widgets will - * need this to clear internal buffers or similar - */ - if (_gwinDrawStart(gh)) { - gdispGFillArea(gh->display, gh->x, gh->y, gh->width, gh->height, gh->bgcolor); - _gwinDrawEnd(gh); - } - if (gh->vmt->AfterClear) - gh->vmt->AfterClear(gh); -} - -void gwinDrawPixel(GHandle gh, coord_t x, coord_t y) { - if (!_gwinDrawStart(gh)) return; - gdispGDrawPixel(gh->display, gh->x+x, gh->y+y, gh->color); - _gwinDrawEnd(gh); -} - -void gwinDrawLine(GHandle gh, coord_t x0, coord_t y0, coord_t x1, coord_t y1) { - if (!_gwinDrawStart(gh)) return; - gdispGDrawLine(gh->display, gh->x+x0, gh->y+y0, gh->x+x1, gh->y+y1, gh->color); - _gwinDrawEnd(gh); -} - -void gwinDrawBox(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy) { - if (!_gwinDrawStart(gh)) return; - gdispGDrawBox(gh->display, gh->x+x, gh->y+y, cx, cy, gh->color); - _gwinDrawEnd(gh); -} - -void gwinFillArea(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy) { - if (!_gwinDrawStart(gh)) return; - gdispGFillArea(gh->display, gh->x+x, gh->y+y, cx, cy, gh->color); - _gwinDrawEnd(gh); -} - -void gwinBlitArea(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t srcx, coord_t srcy, coord_t srccx, const pixel_t *buffer) { - if (!_gwinDrawStart(gh)) return; - gdispGBlitArea(gh->display, gh->x+x, gh->y+y, cx, cy, srcx, srcy, srccx, buffer); - _gwinDrawEnd(gh); -} - -#if GDISP_NEED_CIRCLE - void gwinDrawCircle(GHandle gh, coord_t x, coord_t y, coord_t radius) { - if (!_gwinDrawStart(gh)) return; - gdispGDrawCircle(gh->display, gh->x+x, gh->y+y, radius, gh->color); - _gwinDrawEnd(gh); - } - - void gwinFillCircle(GHandle gh, coord_t x, coord_t y, coord_t radius) { - if (!_gwinDrawStart(gh)) return; - gdispGFillCircle(gh->display, gh->x+x, gh->y+y, radius, gh->color); - _gwinDrawEnd(gh); - } -#endif - -#if GDISP_NEED_ELLIPSE - void gwinDrawEllipse(GHandle gh, coord_t x, coord_t y, coord_t a, coord_t b) { - if (!_gwinDrawStart(gh)) return; - gdispGDrawEllipse(gh->display, gh->x+x, gh->y+y, a, b, gh->color); - _gwinDrawEnd(gh); - } - - void gwinFillEllipse(GHandle gh, coord_t x, coord_t y, coord_t a, coord_t b) { - if (!_gwinDrawStart(gh)) return; - gdispGFillEllipse(gh->display, gh->x+x, gh->y+y, a, b, gh->color); - _gwinDrawEnd(gh); - } -#endif - -#if GDISP_NEED_ARC - void gwinDrawArc(GHandle gh, coord_t x, coord_t y, coord_t radius, coord_t startangle, coord_t endangle) { - if (!_gwinDrawStart(gh)) return; - gdispGDrawArc(gh->display, gh->x+x, gh->y+y, radius, startangle, endangle, gh->color); - _gwinDrawEnd(gh); - } - - void gwinFillArc(GHandle gh, coord_t x, coord_t y, coord_t radius, coord_t startangle, coord_t endangle) { - if (!_gwinDrawStart(gh)) return; - gdispGFillArc(gh->display, gh->x+x, gh->y+y, radius, startangle, endangle, gh->color); - _gwinDrawEnd(gh); - } -#endif - -#if GDISP_NEED_ARCSECTORS - void gwinDrawArcSectors(GHandle gh, coord_t x, coord_t y, coord_t radius, uint8_t sectors) { - if (!_gwinDrawStart(gh)) return; - gdispGDrawArcSectors(gh->display, gh->x+x, gh->y+y, radius, sectors, gh->color); - _gwinDrawEnd(gh); - } - - void gwinFillArcSectors(GHandle gh, coord_t x, coord_t y, coord_t radius, uint8_t sectors) { - if (!_gwinDrawStart(gh)) return; - gdispGFillArcSectors(gh->display, gh->x+x, gh->y+y, radius, sectors, gh->color); - _gwinDrawEnd(gh); - } -#endif - -#if GDISP_NEED_PIXELREAD - color_t gwinGetPixelColor(GHandle gh, coord_t x, coord_t y) { - if (!_gwinDrawStart(gh)) return (color_t)0; - return gdispGGetPixelColor(gh->display, gh->x+x, gh->y+y); - _gwinDrawEnd(gh); - } -#endif - -#if GDISP_NEED_TEXT - void gwinDrawChar(GHandle gh, coord_t x, coord_t y, char c) { - if (!gh->font || !_gwinDrawStart(gh)) return; - gdispGDrawChar(gh->display, gh->x+x, gh->y+y, c, gh->font, gh->color); - _gwinDrawEnd(gh); - } - - void gwinFillChar(GHandle gh, coord_t x, coord_t y, char c) { - if (!gh->font || !_gwinDrawStart(gh)) return; - gdispGFillChar(gh->display, gh->x+x, gh->y+y, c, gh->font, gh->color, gh->bgcolor); - _gwinDrawEnd(gh); - } - - void gwinDrawString(GHandle gh, coord_t x, coord_t y, const char *str) { - if (!gh->font || !_gwinDrawStart(gh)) return; - gdispGDrawString(gh->display, gh->x+x, gh->y+y, str, gh->font, gh->color); - _gwinDrawEnd(gh); - } - - void gwinFillString(GHandle gh, coord_t x, coord_t y, const char *str) { - if (!gh->font || !_gwinDrawStart(gh)) return; - gdispGFillString(gh->display, gh->x+x, gh->y+y, str, gh->font, gh->color, gh->bgcolor); - _gwinDrawEnd(gh); - } - - void gwinDrawStringBox(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy, const char* str, justify_t justify) { - if (!gh->font || !_gwinDrawStart(gh)) return; - gdispGDrawStringBox(gh->display, gh->x+x, gh->y+y, cx, cy, str, gh->font, gh->color, justify); - _gwinDrawEnd(gh); - } - - void gwinFillStringBox(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy, const char* str, justify_t justify) { - if (!gh->font || !_gwinDrawStart(gh)) return; - gdispGFillStringBox(gh->display, gh->x+x, gh->y+y, cx, cy, str, gh->font, gh->color, gh->bgcolor, justify); - _gwinDrawEnd(gh); - } -#endif - -#if GDISP_NEED_CONVEX_POLYGON - void gwinDrawPoly(GHandle gh, coord_t tx, coord_t ty, const point *pntarray, unsigned cnt) { - if (!_gwinDrawStart(gh)) return; - gdispGDrawPoly(gh->display, tx+gh->x, ty+gh->y, pntarray, cnt, gh->color); - _gwinDrawEnd(gh); - } - - void gwinFillConvexPoly(GHandle gh, coord_t tx, coord_t ty, const point *pntarray, unsigned cnt) { - if (!_gwinDrawStart(gh)) return; - gdispGFillConvexPoly(gh->display, tx+gh->x, ty+gh->y, pntarray, cnt, gh->color); - _gwinDrawEnd(gh); - } - void gwinDrawThickLine(GHandle gh, coord_t x0, coord_t y0, coord_t x1, coord_t y1, coord_t width, bool_t round) { - if (!_gwinDrawStart(gh)) return; - gdispGDrawThickLine(gh->display, gh->x+x0, gh->y+y0, gh->x+x1, gh->y+y1, gh->color, width, round); - _gwinDrawEnd(gh); - } -#endif - -#if GDISP_NEED_IMAGE - gdispImageError gwinDrawImage(GHandle gh, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy) { - gdispImageError ret; - - if (!_gwinDrawStart(gh)) return GDISP_IMAGE_ERR_OK; - ret = gdispGImageDraw(gh->display, img, gh->x+x, gh->y+y, cx, cy, sx, sy); - _gwinDrawEnd(gh); - return ret; - } -#endif - -#endif /* GFX_USE_GWIN */ -/** @} */ - diff --git a/src/gwin/gwin_image.h b/src/gwin/gwin_image.h index 2e0a3218..5def4037 100644 --- a/src/gwin/gwin_image.h +++ b/src/gwin/gwin_image.h @@ -27,7 +27,7 @@ #ifndef _GWIN_IMAGE_H #define _GWIN_IMAGE_H -// This file is included within "src/gwin/sys_defs.h" +// This file is included within "src/gwin/gwin.h" // An image window typedef struct GImageObject { diff --git a/src/gwin/gwin_options.h b/src/gwin/gwin_options.h new file mode 100644 index 00000000..9252e67a --- /dev/null +++ b/src/gwin/gwin_options.h @@ -0,0 +1,334 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gwin/gwin_options.h + * @brief GWIN sub-system options header file. + * + * @addtogroup GWIN + * @brief The GWIN module uses all the other modules (GDISP, GINPUT, GTIMER...) to + * form a complete GUI toolkit. + * + * @{ + */ + +#ifndef _GWIN_OPTIONS_H +#define _GWIN_OPTIONS_H + +/** + * @name GWIN Functionality to be included + * @{ + */ + /** + * @brief Should window manager support be included + * @details Defaults to FALSE + */ + #ifndef GWIN_NEED_WINDOWMANAGER + #define GWIN_NEED_WIDGET FALSE + #endif + /** + * @brief Should the widget hierarchy be included. This provides parent-child features. + * @details Defaults to FALSE + */ + #ifndef GWIN_NEED_CONTAINERS + #define GWIN_NEED_CONTAINERS FALSE + #endif + /** + * @brief Should widget functions be included. Needed for any widget (eg Buttons, Sliders etc) + * @details Defaults to FALSE + */ + #ifndef GWIN_NEED_WIDGET + #define GWIN_NEED_WIDGET FALSE + #endif + /** + * @brief Should the simple container be included. + * @details Defaults to FALSE + */ + #ifndef GWIN_NEED_CONTAINER + #define GWIN_NEED_CONTAINER FALSE + #endif + /** + * @brief Should the frame widget be included. + * @details Defaults to FALSE + */ + #ifndef GWIN_NEED_FRAME + #define GWIN_NEED_FRAME FALSE + #endif + /** + * @brief Should console functions be included. + * @details Defaults to FALSE + */ + #ifndef GWIN_NEED_CONSOLE + #define GWIN_NEED_CONSOLE FALSE + #endif + /** + * @brief Should graph functions be included. + * @details Defaults to FALSE + */ + #ifndef GWIN_NEED_GRAPH + #define GWIN_NEED_GRAPH FALSE + #endif + /** + * @brief Should gl3d functions be included. + * @details Defaults to FALSE + */ + #ifndef GWIN_NEED_GL3D + #define GWIN_NEED_GL3D FALSE + #endif + /** + * @brief Should button functions be included. + * @details Defaults to FALSE + */ + #ifndef GWIN_NEED_BUTTON + #define GWIN_NEED_BUTTON FALSE + #endif + /** + * @brief Should progressbar functions be included. + * @details Defaults to FALSE + */ + #ifndef GWIN_NEED_PROGRESSBAR + #define GWIN_NEED_PROGRESSBAR FALSE + #endif + /** + * @brief Should slider functions be included. + * @details Defaults to FALSE + */ + #ifndef GWIN_NEED_SLIDER + #define GWIN_NEED_SLIDER FALSE + #endif + /** + * @brief Should checkbox functions be included. + * @details Defaults to FALSE + */ + #ifndef GWIN_NEED_CHECKBOX + #define GWIN_NEED_CHECKBOX FALSE + #endif + /** + * @brief Should image functions be included. + * @details Defaults to FALSE + */ + #ifndef GWIN_NEED_IMAGE + #define GWIN_NEED_IMAGE FALSE + #endif + /** + * @brief Should label functions be included. + * @details Defaults to FALSE + */ + #ifndef GWIN_NEED_LABEL + #define GWIN_NEED_LABEL FALSE + #endif + /** + * @brief Should radio button functions be included. + * @details Defaults to FALSE + */ + #ifndef GWIN_NEED_RADIO + #define GWIN_NEED_RADIO FALSE + #endif + /** + * @brief Should tabset functions be included. + * @details Defaults to FALSE + */ + #ifndef GWIN_NEED_TABSET + #define GWIN_NEED_TABSET FALSE + #endif +/** + * @} + * + * @name GWIN Optional Parameters + * @{ + */ + /** + * @brief Add a tag to each widget + * @details Defaults to FALSE + * @note Adds a tag member to each widget. Any events created include this tag. + * The enables switch based application logic to detect the event source. + */ + #ifndef GWIN_WIDGET_TAGS + #define GWIN_WIDGET_TAGS FALSE + #endif + /** + * @brief Use flat styling for controls rather than a 3D look + * @details Defaults to FALSE + * @note This may appear better on color-restricted displays + * @note Flat styling is less graphics and cpu intensive (marginally) than the default 3D look. + */ + #ifndef GWIN_FLAT_STYLING + #define GWIN_FLAT_STYLING FALSE + #endif + /** + * @brief Don't use a timer for redrawing windows + * @details Defaults to FALSE + * @note Normally windows and widgets are redrawn on a timer. Setting this + * option causes them to be redrawn immediately. Note that this can + * cause extended blocking times on events and saves little code. + * @note If GWIN_NEED_WINDOWMANAGER is FALSE then this setting is ignored + * as redrawing always occurs immediately. + */ + #ifndef GWIN_REDRAW_IMMEDIATE + #define GWIN_REDRAW_IMMEDIATE FALSE + #endif + /** + * @brief Redraw all windows in a single operation + * @details Defaults to FALSE + * @note Windows are normally redraw one per gtimer cycle. + * Setting this option causes all windows to be redrawn in + * a single gtimer cycle. Note that this can + * cause extended blocking times on the timer thread but may + * speed up redraw slightly. + * @note This is only relevant if GWIN_REDRAW_IMMEDIATE is FALSE. + * Everything always gets redrawn in a single operation if + * GWIN_REDRAW_IMMEDIATE is TRUE. + */ + #ifndef GWIN_REDRAW_SINGLEOP + #define GWIN_REDRAW_SINGLEOP FALSE + #endif + /** + * @brief Buttons should not insist the mouse is over the button on mouse release + * @details Defaults to FALSE + */ + #ifndef GWIN_BUTTON_LAZY_RELEASE + #define GWIN_BUTTON_LAZY_RELEASE FALSE + #endif + /** + * @brief Should the content of the console be saved for redrawing. + * @details Defaults to FALSE + * @details If this feature is enabled, the contents of the console will be saved + * as it is written. If a redraw is required it will be redrawn from the + * history. Scrolling will also use the history buffer if it is turned on. + * @note Using this option allocates the amount of memory to store the + * history based on the minimum character width in the current font + * at the time the history is turned on. Using a fixed width font is a good + * idea to minimize memory usage. + * @note If you change the size of the window or you change the font being displayed + * you should turn off the history and then turn it back on in order to get + * a new buffer of the correct size for the window/font combination. Strange + * redrawing and scrolling effects can occur if the buffer is too small to + * save a complete screen of data. Note the system tries to optimize storage + * so this may only be evident in very limited situations eg with a console + * with many characters in it. + * @note @p gwinConsoleSetBuffer() can be used to turn the history buffer off and on. + */ + #ifndef GWIN_CONSOLE_USE_HISTORY + #define GWIN_CONSOLE_USE_HISTORY FALSE + #endif + /** + * @brief Use font width averaging for the history buffer allocation. + * @details Defaults to FALSE + * @details If this feature is enabled, the width one third of the way between + * the font's character width minimum and maximum will be used instead + * of the font's minimum width. + * @note This option reduces the memory allocation for a variable width font's + * history buffer. Note that strange + * redrawing and scrolling effects can occur if the buffer is too small to + * save a complete screen of data. The system tries to optimize storage + * so this may only be evident in very limited situations eg with a console + * with many characters in it. + */ + #ifndef GWIN_CONSOLE_HISTORY_AVERAGING + #define GWIN_CONSOLE_HISTORY_AVERAGING FALSE + #endif + /** + * @brief Should the history be turned on for all console windows when they are first created. + * @details Defaults to FALSE + * @note @p gwinConsoleSetBuffer() can be used to turn the history buffer off and on at + * any time. + */ + #ifndef GWIN_CONSOLE_HISTORY_ATCREATE + #define GWIN_CONSOLE_HISTORY_ATCREATE FALSE + #endif + /** + * @brief Console Windows need floating point support in @p gwinPrintf + * @details Defaults to FALSE + */ + #ifndef GWIN_CONSOLE_USE_FLOAT + #define GWIN_CONSOLE_USE_FLOAT FALSE + #endif + /** + * @brief Console windows support escape sequences to control display + * @details Defaults to FALSE + * + * @note + * Currently supported: + * ESC color Change subsequent text color + * color: "0" = black, "1" = red, "2" = green, "3" = yellow, "4" = blue, + * "5" = magenta, "6" = cyan, "7" = white + * ESC C Revert subsequent text color to the window default + * ESC u Turn on underline + * ESC U Turn off underline + * ESC b Turn on bold + * ESC B Turn off bold + * ESC J Clear the window + */ + #ifndef GWIN_CONSOLE_ESCSEQ + #define GWIN_CONSOLE_ESCSEQ FALSE + #endif + /** + * @brief Console Windows need BaseStreamSequential support (ChibiOS only) + * @details Defaults to FALSE + * @note To use the ChibiOS basestream functions such as chprintf() + * for printing in a console window you need to set this option to + * TRUE in your gfxconf.h and include in your application source file... + * \#include "chprintf.h" + * In your makefile, as part of your list of C source files, include + * ${CHIBIOS}/os/various/chprintf.c + */ + #ifndef GWIN_CONSOLE_USE_BASESTREAM + #define GWIN_CONSOLE_USE_BASESTREAM FALSE + #endif + /** + * @brief Image windows can optionally support animated images + * @details Defaults to FALSE + */ + #ifndef GWIN_NEED_IMAGE_ANIMATION + #define GWIN_NEED_IMAGE_ANIMATION FALSE + #endif + /** + * @brief Enable the API to automatically increment the progressbar over time + * @details Defaults to FALSE + */ + #ifndef GWIN_PROGRESSBAR_AUTO + #define GWIN_PROGRESSBAR_AUTO FALSE + #endif + /** + * @brief Should the slider avoid snapping to a fixed position when the mouse is released + * @details Defaults to FALSE + * @note If FALSE the slider will snap to the closest set-able position when the + * mouse is released. If TRUE it will maintain the position the + * mouse was released at, except when at the minimum and maximum slider values. + */ + #ifndef GWIN_SLIDER_NOSNAP + #define GWIN_SLIDER_NOSNAP FALSE + #endif + /** + * @brief The number of pixels of dead-band at each end of the slider + * @details Defaults to 5 + * @note A dead-band is required because fingers can often cannot + * accurately control the slider peg at the edges of the slider + */ + #ifndef GWIN_SLIDER_DEAD_BAND + #define GWIN_SLIDER_DEAD_BAND 5 + #endif + /** + * @brief How many toggles it takes to go from minimum to maximum value on a slider + * @details Defaults to 20 + * @note When the slider is being operated by a toggle device this setting describes + * how many toggles are required to go from end to end. + */ + #ifndef GWIN_SLIDER_TOGGLE_INC + #define GWIN_SLIDER_TOGGLE_INC 20 + #endif + /** + * @brief The height in pixels of a row of tabs in a tabset + * @details Defaults to 18 + */ + #ifndef GWIN_TABSET_TABHEIGHT + #define GWIN_TABSET_TABHEIGHT 18 + #endif +/** @} */ + +#endif /* _GWIN_OPTIONS_H */ +/** @} */ diff --git a/src/gwin/gwin_rules.h b/src/gwin/gwin_rules.h new file mode 100644 index 00000000..8d8ef2cc --- /dev/null +++ b/src/gwin/gwin_rules.h @@ -0,0 +1,129 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gwin/gwin_rules.h + * @brief GWIN safety rules header file. + * + * @addtogroup GWIN + * @{ + */ + +#ifndef _GWIN_RULES_H +#define _GWIN_RULES_H + +#if GFX_USE_GWIN + // Sub-system rules + #if !GFX_USE_GDISP + #error "GWIN: GFX_USE_GDISP must be TRUE when using GWIN" + #endif + #if !GDISP_NEED_CLIP + #if GFX_DISPLAY_RULE_WARNINGS + #warning "GWIN: Drawing can occur outside the defined windows as GDISP_NEED_CLIP is FALSE" + #endif + #endif + + // Objects require their super-class + #if GWIN_NEED_TABSET || GWIN_NEED_FRAME || GWIN_NEED_CONTAINER + #if !GWIN_NEED_CONTAINERS + #if GFX_DISPLAY_RULE_WARNINGS + #warning "GWIN: GWIN_NEED_CONTAINERS is required when a container is enabled. It has been turned on for you." + #endif + #undef GWIN_NEED_CONTAINERS + #define GWIN_NEED_CONTAINERS TRUE + #endif + #endif + #if GWIN_NEED_BUTTON || GWIN_NEED_SLIDER || GWIN_NEED_CHECKBOX || GWIN_NEED_LABEL || GWIN_NEED_RADIO || GWIN_NEED_LIST || \ + GWIN_NEED_IMAGE || GWIN_NEED_CHECKBOX || GWIN_NEED_PROGRESSBAR + #if !GWIN_NEED_WIDGET + #if GFX_DISPLAY_RULE_WARNINGS + #warning "GWIN: GWIN_NEED_WIDGET is required when a widget is used. It has been turned on for you." + #endif + #undef GWIN_NEED_WIDGET + #define GWIN_NEED_WIDGET TRUE + #endif + #endif + + // Rules for the super-classes + #if GWIN_NEED_CONTAINERS + #if !GWIN_NEED_WIDGET + #if GFX_DISPLAY_RULE_WARNINGS + #warning "GWIN: GWIN_NEED_WIDGET is required when GWIN_NEED_CONTAINERS is enabled. It has been turned on for you." + #endif + #undef GWIN_NEED_WIDGET + #define GWIN_NEED_WIDGET TRUE + #endif + #endif + #if GWIN_NEED_WIDGET + #if !GDISP_NEED_TEXT + #error "GWIN: GDISP_NEED_TEXT is required if GWIN_NEED_WIDGET is TRUE." + #endif + #if !GFX_USE_GINPUT + // This test also ensures that GFX_USE_GEVENT is set + #error "GWIN: GFX_USE_GINPUT (and one or more input sources) is required if GWIN_NEED_WIDGET is TRUE" + #endif + #if !GWIN_NEED_WINDOWMANAGER + #if GFX_DISPLAY_RULE_WARNINGS + #warning "GWIN: GWIN_NEED_WINDOWMANAGER is required if GWIN_NEED_WIDGET is TRUE. It has been turned on for you." + #endif + #undef GWIN_NEED_WINDOWMANAGER + #define GWIN_NEED_WINDOWMANAGER TRUE + #endif + #if !GDISP_NEED_MULTITHREAD + #if GFX_DISPLAY_RULE_WARNINGS + #warning "GWIN: GDISP_NEED_MULTITHREAD is required if GWIN_NEED_WIDGET is TRUE. It has been turned on for you" + #endif + #undef GDISP_NEED_MULTITHREAD + #define GDISP_NEED_MULTITHREAD TRUE + #endif + #endif + #if GWIN_NEED_WINDOWMANAGER + #if !GFX_USE_GQUEUE || !GQUEUE_NEED_ASYNC + #if GFX_DISPLAY_RULE_WARNINGS + #warning "GWIN: GFX_USE_GQUEUE and GQUEUE_NEED_ASYNC is required if GWIN_NEED_WINDOWMANAGER is TRUE. It has been turned on for you." + #endif + #undef GFX_USE_GQUEUE + #undef GQUEUE_NEED_ASYNC + #define GFX_USE_GQUEUE TRUE + #define GQUEUE_NEED_ASYNC TRUE + #endif + #if !GFX_USE_GTIMER + #if GFX_DISPLAY_RULE_WARNINGS + #warning "GWIN: GFX_USE_GTIMER is required if GWIN_NEED_WINDOWMANAGER is TRUE. It has been turned on for you." + #endif + #undef GFX_USE_GTIMER + #define GFX_USE_GTIMER TRUE + #endif + #endif + + // Rules for individual objects + #if GWIN_NEED_LIST + #if !GDISP_NEED_TEXT + #error "GWIN: GDISP_NEED_TEXT is required when GWIN_NEED_LIST is TRUE." + #endif + #endif + #if GWIN_NEED_RADIO + #if !GDISP_NEED_CIRCLE + #if GFX_DISPLAY_RULE_WARNINGS + #warning "GWIN: GDISP_NEED_CIRCLE should be set to TRUE for much nicer radio button widgets." + #endif + #endif + #endif + #if GWIN_NEED_IMAGE + #if !GDISP_NEED_IMAGE + #error "GWIN: GDISP_NEED_IMAGE is required when GWIN_NEED_IMAGE is TRUE." + #endif + #endif + #if GWIN_NEED_CONSOLE + #if !GDISP_NEED_TEXT + #error "GWIN: GDISP_NEED_TEXT is required if GWIN_NEED_CONSOLE is TRUE." + #endif + #endif +#endif + +#endif /* _GWIN_RULES_H */ +/** @} */ diff --git a/src/gwin/gwin_widget.h b/src/gwin/gwin_widget.h index c6bc6d4c..63d73e77 100644 --- a/src/gwin/gwin_widget.h +++ b/src/gwin/gwin_widget.h @@ -23,7 +23,7 @@ #ifndef _GWIDGET_H #define _GWIDGET_H -/* This file is included within "src/gwin/sys_defs.h" */ +/* This file is included within "src/gwin/gwin.h" */ // Forward definition struct GWidgetObject; diff --git a/src/gwin/sys_defs.h b/src/gwin/sys_defs.h deleted file mode 100644 index afab138c..00000000 --- a/src/gwin/sys_defs.h +++ /dev/null @@ -1,1026 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gwin/sys_defs.h - * - * @defgroup Window Window - * @ingroup Windows - * - * @details GWIN provides a basic window manager which allows it to easily - * create and destroy different windows at runtime. Each window - * will have it's own properties such as colors as well as - * it's own drawing origin. - * - * @pre GFX_USE_GWIN must be set to TRUE in your gfxconf.h - * @{ - */ - -#ifndef _GWIN_H -#define _GWIN_H - -#include "gfx.h" - -#if GFX_USE_GWIN || defined(__DOXYGEN__) - -/* Forward declaration */ -typedef struct GWindowObject *GHandle; - -/** - * @brief A window object structure - * @note Do not access the members directly. Treat it as a black-box and use the method functions. - * @{ - */ -typedef struct GWindowObject { - #if GWIN_NEED_WINDOWMANAGER - // This MUST be the first member of the structure - gfxQueueASyncItem wmq; // @< The next window (for the window manager) - #endif - const struct gwinVMT *vmt; // @< The VMT for this GWIN - GDisplay * display; // @< The display this window is on. - coord_t x, y; // @< Screen relative position - coord_t width, height; // @< Dimensions of this window - color_t color, bgcolor; // @< The current drawing colors - uint32_t flags; // @< Window flags (the meaning is private to the GWIN class) - #if GDISP_NEED_TEXT - font_t font; // @< The current font - #endif - #if GWIN_NEED_CONTAINERS - GHandle parent; // @< The parent window - #endif -} GWindowObject, * GHandle; -/** @} */ - -/** - * @brief The structure to initialise a GWIN. - * - * @note Some gwin's will need extra parameters. - * @note The dimensions and position may be changed to fit on the real screen. - * @note If you create this structure on the stack, you should always memset - * it to all zero's first in case a future version of the software - * add's extra fields. Alternatively you can use @p gwinClearInit() - * to clear it. - * - * @{ - */ -typedef struct GWindowInit { - coord_t x, y; // @< The initial position relative to its parent - coord_t width, height; // @< The initial dimension - bool_t show; // @< Should the window be visible initially - #if GWIN_NEED_CONTAINERS - GHandle parent; // @< The parent - must be a container or NULL - #endif -} GWindowInit; -/** @} */ - -/** - * @brief A window's minimized, maximized or normal size - */ -typedef enum { GWIN_NORMAL, GWIN_MAXIMIZE, GWIN_MINIMIZE } GWindowMinMax; - -#ifdef __cplusplus -extern "C" { -#endif - -/*------------------------------------------------- - * Window Manager functions - *-------------------------------------------------*/ - -#if GWIN_NEED_WINDOWMANAGER || defined(__DOXYGEN__) - // Forward definition - struct GWindowManager; - - /** - * @brief Set the window manager for the GWIN system. - * - * @param[in] gwm The window manager to use. Can be NULL to turn off the existing window manager. - * - * @note A window manager is responsible for handling when window visibility is changed or - * a window is resized for moved. Note that only saved window states will be redrawn. Each - * window type can save different information (or none at all). See the documentation on each window - * type to see which information it saves (and can therefore be automatically redrawn). - * For window types that do not save any state information, the window manager determines what to do. - * Generally it will just clear the window to its background color. - * - * @api - */ - void gwinSetWindowManager(struct GWindowManager *gwm); -#endif - -/*------------------------------------------------- - * Functions that affect all windows - *-------------------------------------------------*/ - - /** - * @brief Clear a GWindowInit structure to all zero's - * @note This function is provided just to prevent problems - * on operating systems where using memset() causes issues - * in the users application. - * - * @param[in] pwi The GWindowInit structure to clear - * - * @api - */ - void gwinClearInit(GWindowInit *pwi); - - /** - * @brief Set the default foreground color for all new GWIN windows - * - * @param[in] clr The color to be set - * - * @api - */ - void gwinSetDefaultColor(color_t clr); - - /** - * @brief Get the default foreground color for all new GWIN windows - * - * @return The current default color for all new GWIN windows - * - * @api - */ - color_t gwinGetDefaultColor(void); - - /** - * @brief Set the default background color for all new GWIN windows - * - * @param[in] bgclr The background color - * - * @api - */ - void gwinSetDefaultBgColor(color_t bgclr); - - /** - * @brief Get the default background color for all new GWIN windows - * - * @return The current default background color for all new GWIN windows - * - * @api - */ - color_t gwinGetDefaultBgColor(void); - - #if GDISP_NEED_TEXT || defined(__DOXYGEN__) - /** - * @brief Set the default font for all new GWIN windows - * - * @param[in] font The new font to be set - * - * @api - */ - void gwinSetDefaultFont(font_t font); - - /** - * @brief Get the current default font - * - * @return The current default font - * - * @api - */ - font_t gwinGetDefaultFont(void); - #endif - -/*------------------------------------------------- - * Base functions - *-------------------------------------------------*/ - - /** - * @brief Create a basic window. - * @return NULL if there is no resultant drawing area, otherwise a window handle. - * - * @param[in] g The GDisplay to display this window on - * @param[in] pgw The window structure to initialize. If this is NULL the structure is dynamically allocated. - * @param[in] pInit How to initialise the window - * - * @note The drawing color and the background color get set to the current defaults. If you haven't called - * @p gwinSetDefaultColor() or @p gwinSetDefaultBgColor() then these are White and Black respectively. - * @note The font gets set to the current default font. If you haven't called @p gwinSetDefaultFont() then there - * is no default font and text drawing operations will no nothing. - * @note A basic window does not save the drawing state. It is not automatically redrawn if the window is moved or - * its visibility state is changed. - * - * @api - */ - GHandle gwinGWindowCreate(GDisplay *g, GWindowObject *pgw, const GWindowInit *pInit); - #define gwinWindowCreate(pgw, pInit) gwinGWindowCreate(GDISP, pgw, pInit); - - /** - * @brief Destroy a window (of any type). Releases any dynamically allocated memory. - * - * @param[in] gh The window handle - * - * @api - */ - void gwinDestroy(GHandle gh); - - /** - * @brief Get the real class name of the GHandle - * @details Returns a string describing the object class. - * - * @param[in] gh The window - * - * @return A string describing the object class. - * - * @api - */ - const char* gwinGetClassName(GHandle gh); - - /** - * @brief Get an ID that uniquely describes the class of the GHandle - * - * @param[in] gh The window - * - * @api - */ - #define gwinGetClassID(gh) ((void *)((gh)->vmt)) - - /** - * @brief Get the X coordinate of the window - * @details Returns the X coordinate of the origin of the window. - * The coordinate is relative to the physical screen zero point. - * - * @param[in] gh The window - * - * @api - */ - #define gwinGetScreenX(gh) ((gh)->x) - - /** - * @brief Get the Y coordinate of the window - * @details Returns the Y coordinate of the origin of the window. - * The coordinate is relative to the physical screen zero point. - * - * @param[in] gh The window - * - * @api - */ - #define gwinGetScreenY(gh) ((gh)->y) - - /** - * @brief Get the width of the window - * - * @param[in] gh The window - * - * @api - */ - #define gwinGetWidth(gh) ((gh)->width) - - /** - * @brief Get the height of the window - * - * @param[in] gh The window - * - * @api - */ - #define gwinGetHeight(gh) ((gh)->height) - - /** - * @brief Set foreground color - * @details Set the color which will be used to draw - * - * @param[in] gh The window - * @param[in] clr The color to be set - * - * @api - */ - #define gwinSetColor(gh, clr) (gh)->color = (clr) - - /** - * @brief Set background color - * @details Set the color which will be used as background - * @note gwinClear() must be called to set the background color - * - * @param[in] gh The window - * @param[in] bgclr The background color - * - * @api - */ - #define gwinSetBgColor(gh, bgclr) (gh)->bgcolor = (bgclr) - - /** - * @brief Get the foreground color of a window - * - * @param[in] gh The window - * - * @api - */ - #define gwinGetColor(gh) (gh)->color - - /** - * @brief Get the background color of a window - * - * @param[in] gh The window - * - * @api - */ - #define gwinGetBgColor(gh) (gh)->bgcolor - - /** - * @brief Sets whether a window is visible or not - * - * @param[in] gh The window - * @param[in] visible Whether the window should be visible or not - * - * @note When a window is marked as not visible, drawing operations - * on the window do nothing. - * @note When a window is marked as visible, it is not automatically - * redrawn as many window types don't remember their drawing state. - * Widgets such as Buttons, Sliders etc will be redrawn. - * @note If there is no window manager in use, when a window is marked - * as not visible, nothing is done to remove the window from the screen. - * When there is a window manager, it is up to the window manager to - * handle what happens. - * @note Even when you mark a window as visible, it may still not be displayed - * if it's parent is invisible. When the parent becomes visible this child - * will automatically be shown because it is already marked as visible. - * - * @api - */ - void gwinSetVisible(GHandle gh, bool_t visible); - - /** - * @brief Makes a widget become visible - * - * @param[in] gh The window handle - * - * @api - */ - #define gwinShow(gh) gwinSetVisible(gh, TRUE) - - /** - * @brief Makes a widget become invisible - * - * @param[in] gh The window handle - * - * @api - */ - #define gwinHide(gh) gwinSetVisible(gh, FALSE) - - /** - * @brief Gets the visibility of a window - * @return TRUE if visible - * - * @note It is possible for a child to be marked as visible by @p gwinSetVisible() - * but for this call to return FALSE if one of its parents are not visible. - * - * @param[in] gh The window - * - * @api - */ - bool_t gwinGetVisible(GHandle gh); - - /** - * @brief Enable or disable a window - * - * @param[in] gh The window handle - * @param[in] enabled Enable or disable the window - * - * @note The window is automatically redrawn if it supports self-redrawing. - * @note Even when you mark a window as enabled, it may still remain disabled - * if it's parent is disabled. When the parent becomes enabled this child - * will automatically be enabled because it is already marked as enabled. - * - * @api - */ - void gwinSetEnabled(GHandle gh, bool_t enabled); - - /** - * @brief Enables a widget - * - * @param[in] gh The window handle - * - * @api - */ - #define gwinEnable(gh) gwinSetEnabled(gh, TRUE) - - /** - * @brief Disables a widget - * - * @param[in] gh The window handle - * - * @api - */ - #define gwinDisable(gh) gwinSetEnabled(gh, FALSE) - - /** - * @brief Gets the enabled state of a window - * @return TRUE if enabled - * - * @note It is possible for a child to be marked as enabled by @p gwinSetEnabled() - * but for this call to return FALSE if one of its parents are not enabled. - * - * @param[in] gh The window - * - * @api - */ - bool_t gwinGetEnabled(GHandle gh); - - /** - * @brief Move a window - * - * @param[in] gh The window - * @param[in] x, y The new position (screen relative) for this window - * - * @note The final window position may not be the requested position. Windows - * are clipped to the screen area and the window manager may also affect the position. - * @note The window is redrawn if it is visible. See the comments in @p gwinSetVisible() - * with regard to what can be redrawn and what can't. - * @note It is up to the window manager to determine what happens with the screen area - * uncovered by moving the window. When there is no window manager, nothing - * is done with the uncovered area. - * - * @api - */ - void gwinMove(GHandle gh, coord_t x, coord_t y); - - /** - * @brief Resize a window - * - * @param[in] gh The window - * @param[in] width, height The new size of the window - * - * @note The final window size may not be the requested size. Windows - * are clipped to the screen area and the window manager may also affect the size. - * @note The window is redrawn if it is visible. See the comments in @p gwinSetVisible() - * with regard to what can be redrawn and what can't. - * @note It is up to the window manager to determine what happens with any screen area - * uncovered by resizing the window. When there is no window manager, nothing - * is done with the uncovered area. - * - * @api - */ - void gwinResize(GHandle gh, coord_t width, coord_t height); - - /** - * @brief Redraw a window - * - * @param[in] gh The window - * - * @note This is normally never required as windows and widgets will redraw as required. - * Note that some windows are incapable of redrawing themselves as they don't save - * their drawing state. - * - * @api - */ - void gwinRedraw(GHandle gh); - - #if GWIN_NEED_WINDOWMANAGER || defined (__DOXYGEN__) - /** - * @brief Redraw a window - * - * @param[in] g The display to redraw. Passing NULL will redraw all displays. - * @param[in] preserve Should the redraw try to preserve existing screen data for those - * windows that can't redraw themselves? - * - * @note This is normally never required as windows and widgets will redraw as required. - * @note Some windows are incapable of redrawing themselves as they don't save - * their drawing state. - * @note This does not clear the background - just redraws the gwin windows (where possible) - * - * @api - */ - void gwinRedrawDisplay(GDisplay *g, bool_t preserve); - - /** - * @brief Minimize, Maximize or Restore a window - * @pre GWIN_NEED_WINDOWMANAGER must be TRUE - * - * @param[in] gh The window - * @param[in] minmax The new minimized/maximized state - * - * @note The final window state may not be the requested state. Window Managers - * do not need to implement changing the minmax state. If there is no - * window manager this call is ignored. - * @note The window is redrawn if it is changed. See the comments in @p gwinSetVisible() - * with regard to what can be redrawn and what can't. - * @note It is up to the window manager to determine what happens with any screen area - * uncovered by resizing the window. - * @note When a window is minimised it may be asked to draw the window or the window - * manager may draw the minimised window. - * - * @api - */ - void gwinSetMinMax(GHandle gh, GWindowMinMax minmax); - - /** - * @brief Get the Minimized/Maximized state of a window - * @pre GWIN_NEED_WINDOWMANAGER must be TRUE - * - * @param[in] gh The window - * - * @return GWIN_NORMAL, GWIN_MAXIMIZE or GWIN_MINIMIZE - * - * @api - */ - GWindowMinMax gwinGetMinMax(GHandle gh); - - /** - * @brief Raise a window to the top of the z-order - * @pre GWIN_NEED_WINDOWMANAGER must be TRUE - * - * @param[in] gh The window - * - * @note The window z-order is only supported by some window managers. See the comments - * in @p gwinSetVisible() with regard to what can be redrawn and what can't. - * - * @api - */ - void gwinRaise(GHandle gh); - - /** - * @brief Get the next window in the z-order - * @return The next window or NULL if no more windows - * - * @param[in] gh The previous window or NULL to get the first window - * - * @note This returns the next window in the system from top to bottom. - * @note Where there are parent child relationships, this ignores them - * and will list all windows in the system. There is no defined - * order between children of siblings and they can in fact be mixed - * in order. The only relationship honored is that parents will be - * listed before their children. - * - * @api - */ - GHandle gwinGetNextWindow(GHandle gh); - - #endif - - #if GDISP_NEED_TEXT || defined(__DOXYGEN__) - /** - * @brief Set the current font for this window. - * - * @param[in] gh The window handle - * @param[in] font The font to use for text functions - * - * @api - */ - void gwinSetFont(GHandle gh, font_t font); - #endif - -/*------------------------------------------------- - * Drawing functions - *-------------------------------------------------*/ - - /** - * @brief Clear the window - * @note Uses the current background color to clear the window - * - * @param[in] gh The window handle - * - * @api - */ - void gwinClear(GHandle gh); - - /** - * @brief Set a pixel in the window - * @note Uses the current foreground color to set the pixel - * @note May leave GDISP clipping to this window's dimensions - * - * @param[in] gh The window handle - * @param[in] x,y The coordinates of the pixel - * - * @api - */ - void gwinDrawPixel(GHandle gh, coord_t x, coord_t y); - - /** - * @brief Draw a line in the window - * @note Uses the current foreground color to draw the line - * @note May leave GDISP clipping to this window's dimensions - * - * @param[in] gh The window handle - * @param[in] x0,y0 The start position - * @param[in] x1,y1 The end position - * - * @api - */ - void gwinDrawLine(GHandle gh, coord_t x0, coord_t y0, coord_t x1, coord_t y1); - - /** - * @brief Draw a box in the window - * @note Uses the current foreground color to draw the box - * @note May leave GDISP clipping to this window's dimensions - * - * @param[in] gh The window handle - * @param[in] x,y The start position - * @param[in] cx,cy The size of the box (outside dimensions) - * - * @api - */ - void gwinDrawBox(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy); - - /** - * @brief Fill an rectangular area in the window - * @note Uses the current foreground color to fill the box - * @note May leave GDISP clipping to this window's dimensions - * - * @param[in] gh The window handle - * @param[in] x,y The start position - * @param[in] cx,cy The size of the box (outside dimensions) - * - * @api - */ - void gwinFillArea(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy); - - /** - * @brief Fill an area in the window using the supplied bitmap. - * @details The bitmap is in the pixel format specified by the low level driver - * @note If GDISP_NEED_ASYNC is defined then the buffer must be static - * or at least retained until this call has finished the blit. You can - * tell when all graphics drawing is finished by @p gdispIsBusy() going FALSE. - * @note May leave GDISP clipping to this window's dimensions - * - * @param[in] gh The window handle - * @param[in] x, y The start filled area - * @param[in] cx, cy The width and height to be filled - * @param[in] srcx, srcy The bitmap position to start the fill from - * @param[in] srccx The width of a line in the bitmap. - * @param[in] buffer The pixels to use to fill the area. - * - * @api - */ - void gwinBlitArea(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t srcx, coord_t srcy, coord_t srccx, const pixel_t *buffer); - -/*------------------------------------------------- - * Circle, ellipse, arc and arc-sectors functions - *-------------------------------------------------*/ - - #if GDISP_NEED_CIRCLE || defined(__DOXYGEN__) - /** - * @brief Draw a circle in the window. - * @note Uses the current foreground color to draw the circle - * @note May leave GDISP clipping to this window's dimensions - * - * @param[in] gh The window handle - * @param[in] x, y The center of the circle - * @param[in] radius The radius of the circle - * - * @api - */ - void gwinDrawCircle(GHandle gh, coord_t x, coord_t y, coord_t radius); - - /** - * @brief Draw a filled circle in the window. - * @note Uses the current foreground color to draw the filled circle - * @note May leave GDISP clipping to this window's dimensions - * - * @param[in] gh The window handle - * @param[in] x, y The center of the circle - * @param[in] radius The radius of the circle - * - * @api - */ - void gwinFillCircle(GHandle gh, coord_t x, coord_t y, coord_t radius); - #endif - - #if GDISP_NEED_ELLIPSE || defined(__DOXYGEN__) - /** - * @brief Draw an ellipse. - * @note Uses the current foreground color to draw the ellipse - * @note May leave GDISP clipping to this window's dimensions - * - * @param[in] gh The window handle - * @param[in] x,y The center of the ellipse - * @param[in] a,b The dimensions of the ellipse - * - * @api - */ - void gwinDrawEllipse(GHandle gh, coord_t x, coord_t y, coord_t a, coord_t b); - - /** - * @brief Draw an filled ellipse. - * @note Uses the current foreground color to draw the filled ellipse - * @note May leave GDISP clipping to this window's dimensions - * - * @param[in] gh The window handle - * @param[in] x,y The center of the ellipse - * @param[in] a,b The dimensions of the ellipse - * - * @api - */ - void gwinFillEllipse(GHandle gh, coord_t x, coord_t y, coord_t a, coord_t b); - #endif - - #if GDISP_NEED_ARC || defined(__DOXYGEN__) - /* - * @brief Draw an arc in the window. - * @note Uses the current foreground color to draw the arc - * @note May leave GDISP clipping to this window's dimensions - * - * @param[in] gh The window handle - * @param[in] x,y The center point - * @param[in] radius The radius of the arc - * @param[in] start The start angle (0 to 360) - * @param[in] end The end angle (0 to 360) - * - * @api - */ - void gwinDrawArc(GHandle gh, coord_t x, coord_t y, coord_t radius, coord_t startangle, coord_t endangle); - - /* - * @brief Draw a filled arc in the window. - * @note Uses the current foreground color to draw the filled arc - * @note May leave GDISP clipping to this window's dimensions - * - * @param[in] gh The window handle - * @param[in] x,y The center point - * @param[in] radius The radius of the arc - * @param[in] start The start angle (0 to 360) - * @param[in] end The end angle (0 to 360) - * - * @api - */ - void gwinFillArc(GHandle gh, coord_t x, coord_t y, coord_t radius, coord_t startangle, coord_t endangle); - #endif - - #if GDISP_NEED_ARCSECTORS || defined(__DOXYGEN__) - /* - * @brief Draw a selection of 45 degree arcs of a circle in the window. - * @note Uses the current foreground color to draw the arc sector - * @note May leave GDISP clipping to this window's dimensions - * - * @param[in] gh The window handle - * @param[in] x,y The center of the circle - * @param[in] radius The radius of the circle - * @param[in] sectors Bits determine which sectors are drawn. - * Bits go anti-clockwise from the 0 degree mark (y = 0, x is positive), as follows: - * bit 0 - upper right right ----- - * bit 1 - upper upper right /2 1\ - * bit 2 - upper upper left /3 0\ - * bit 3 - upper left left \4 7/ - * bit 4 - lower left left \5 6/ - * bit 5 - lower lower left ----- - * bit 6 - lower lower right - * bit 7 - lower left left - * - * @api - */ - void gwinDrawArcSectors(GHandle gh, coord_t x, coord_t y, coord_t radius, uint8_t sectors); - - /* - * @brief Draw a filled selection of 45 degree arcs of a circle in the window. - * @note Uses the current foreground color to draw the arc sector - * @note May leave GDISP clipping to this window's dimensions - * - * @param[in] gh The window handle - * @param[in] x,y The center of the circle - * @param[in] radius The radius of the circle - * @param[in] sectors Bits determine which sectors are drawn. - * Bits go anti-clockwise from the 0 degree mark (y = 0, x is positive), as follows: - * bit 0 - upper right right ----- - * bit 1 - upper upper right /2 1\ - * bit 2 - upper upper left /3 0\ - * bit 3 - upper left left \4 7/ - * bit 4 - lower left left \5 6/ - * bit 5 - lower lower left ----- - * bit 6 - lower lower right - * bit 7 - lower left left - * - * @api - */ - void gwinFillArcSectors(GHandle gh, coord_t x, coord_t y, coord_t radius, uint8_t sectors); - #endif - -/*------------------------------------------------- - * Pixel read-back functions - *-------------------------------------------------*/ - - #if GDISP_NEED_PIXELREAD || defined(__DOXYGEN__) - /** - * @brief Get the color of a pixel in the window. - * @return The color of the pixel. - * @note May leave GDISP clipping to this window's dimensions - * - * @param[in] gh The window handle - * @param[in] x,y The position in the window - * - * @api - */ - color_t gwinGetPixelColor(GHandle gh, coord_t x, coord_t y); - #endif - -/*------------------------------------------------- - * Text functions - *-------------------------------------------------*/ - - #if GDISP_NEED_TEXT || defined(__DOXYGEN__) - /** - * @brief Draw a text character at the specified position in the window. - * @pre The font must have been set. - * @note Uses the current foreground color to draw the character - * @note May leave GDISP clipping to this window's dimensions - * - * @param[in] gh The window handle - * @param[in] x,y The position for the text - * @param[in] c The character to draw - * - * @api - */ - void gwinDrawChar(GHandle gh, coord_t x, coord_t y, char c); - - /** - * @brief Draw a text character with a filled background at the specified position in the window. - * @pre The font must have been set. - * @note Uses the current foreground color to draw the character and fills the background using the background drawing color - * @note May leave GDISP clipping to this window's dimensions - * - * @param[in] gh The window handle - * @param[in] x,y The position for the text - * @param[in] c The character to draw - * - * @api - */ - void gwinFillChar(GHandle gh, coord_t x, coord_t y, char c); - - /** - * @brief Draw a text string in the window - * @pre The font must have been set. - * @note Uses the current foreground color to draw the character - * @note May leave GDISP clipping to this window's dimensions - * - * @param[in] gh The window handle - * @param[in] x,y The position for the text - * @param[in] str The string to draw - * - * @api - */ - void gwinDrawString(GHandle gh, coord_t x, coord_t y, const char *str); - - /** - * @brief Draw a text string with a filled background in the window - * @pre The font must have been set. - * @note Uses the current foreground color to draw the character and fills the background using the background drawing color - * @note May leave GDISP clipping to this window's dimensions - * - * @param[in] gh The window handle - * @param[in] x,y The position for the text - * @param[in] str The string to draw - * - * @api - */ - void gwinFillString(GHandle gh, coord_t x, coord_t y, const char *str); - - /** - * @brief Draw a text string verticly centered within the specified box. - * @pre The font must have been set. - * @note Uses the current foreground color to draw the character. - * @note The specified box does not need to align with the window box - * @note May leave GDISP clipping to this window's dimensions - * - * @param[in] gh The window handle - * @param[in] x,y The position for the text (need to define top-right or base-line - check code) - * @param[in] cx,cy The width and height of the box - * @param[in] str The string to draw - * @param[in] justify Justify the text left, center or right within the box - * - * @api - */ - void gwinDrawStringBox(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy, const char* str, justify_t justify); - - /** - * @brief Draw a text string verticly centered within the specified filled box. - * @pre The font must have been set. - * @note Uses the current foreground color to draw the character and fills the background using the background drawing color - * @note The entire box is filled. Note this box does not need to align with the window box - * @note May leave GDISP clipping to this window's dimensions - * - * @param[in] gh The window handle - * @param[in] x,y The position for the text (need to define top-right or base-line - check code) - * @param[in] cx,cy The width and height of the box - * @param[in] str The string to draw - * @param[in] justify Justify the text left, center or right within the box - * - * @api - */ - void gwinFillStringBox(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy, const char* str, justify_t justify); - #endif - -/*------------------------------------------------- - * Polygon functions - *-------------------------------------------------*/ - - #if GDISP_NEED_CONVEX_POLYGON || defined(__DOXYGEN__) - /** - * @brief Draw an enclosed polygon (convex, non-convex or complex). - * - * @note Uses the current foreground color. - * - * @param[in] gh The window handle - * @param[in] tx, ty Transform all points in pntarray by tx, ty - * @param[in] pntarray An array of points - * @param[in] cnt The number of points in the array - * - * @api - */ - void gwinDrawPoly(GHandle gh, coord_t tx, coord_t ty, const point *pntarray, unsigned cnt); - - /** - * @brief Fill a convex polygon - * @details Doesn't handle non-convex or complex polygons. - * - * @note Uses the current foreground color. - * - * @param[in] gh The window handle - * @param[in] tx, ty Transform all points in pntarray by tx, ty - * @param[in] pntarray An array of points - * @param[in] cnt The number of points in the array - * - * @note Convex polygons are those that have no internal angles. That is; - * you can draw a line from any point on the polygon to any other point - * on the polygon without it going outside the polygon. In our case we generalise - * this a little by saying that an infinite horizontal line (at any y value) will cross - * no more than two edges on the polygon. Some non-convex polygons do fit this criteria - * and can therefore be drawn. - * @note This routine is designed to be very efficient with even simple display hardware. - * - * @api - */ - void gwinFillConvexPoly(GHandle gh, coord_t tx, coord_t ty, const point *pntarray, unsigned cnt); - - /** - * @brief Draw a thick line in the window - * @details The line thickness is specified in pixels. The line ends can - * be selected to be either flat or round. - * @note Uses gdispGFillConvexPoly() internally to perform the drawing. - * @note Uses the current foreground color to draw the line - * - * @param[in] gh The window handle - * @param[in] x0,y0 The start position - * @param[in] x1,y1 The end position - * @param[in] width The width of the line - * @param[in] round Use round ends for the line - * - * @api - */ - void gwinDrawThickLine(GHandle gh, coord_t x0, coord_t y0, coord_t x1, coord_t y1, coord_t width, bool_t round); - #endif - -/*------------------------------------------------- - * Image functions - *-------------------------------------------------*/ - - #if GDISP_NEED_IMAGE || defined(__DOXYGEN__) - /** - * @brief Draw the image - * @return GDISP_IMAGE_ERR_OK (0) on success or an error code. - * - * @param[in] gh The window handle - * @param[in] img The image structure - * @param[in] x,y The window location to draw the image - * @param[in] cx,cy The area on the screen to draw - * @param[in] sx,sy The image position to start drawing at - * - * @pre gdispImageOpen() must have returned successfully. - * - * @note If sx,sy + cx,cy is outside the image boundaries the area outside the image - * is simply not drawn. - * @note If @p gdispImageCache() has been called first for this frame, this routine will draw using a - * fast blit from the cached frame. If not, it reads the input and decodes it as it - * is drawing. This may be significantly slower than if the image has been cached (but - * uses a lot less RAM) - * - * @api - */ - gdispImageError gwinDrawImage(GHandle gh, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); - #endif - -#ifdef __cplusplus -} -#endif - -/*------------------------------------------------- - * Additional functionality - *-------------------------------------------------*/ - - /* Include widgets */ - #if GWIN_NEED_WIDGET || defined(__DOXYGEN__) - #include "gwin_widget.h" - #endif - - /* Include containers */ - #if GWIN_NEED_CONTAINERS || defined(__DOXYGEN__) - #include "gwin_container.h" - #endif - - /* Include vanilla window objects */ - #if GWIN_NEED_CONSOLE || defined(__DOXYGEN__) - #include "gwin_console.h" - #endif - #if GWIN_NEED_GRAPH || defined(__DOXYGEN__) - #include "gwin_graph.h" - #endif - #if GWIN_NEED_IMAGE || defined(__DOXYGEN__) - #include "gwin_image.h" - #endif - #if GWIN_NEED_GL3D || defined(__DOXYGEN__) - #include "gwin_gl3d.h" - #endif - -#endif /* GFX_USE_GWIN */ - -#endif /* _GWIN_H */ -/** @} */ diff --git a/src/gwin/sys_make.mk b/src/gwin/sys_make.mk deleted file mode 100644 index c619466e..00000000 --- a/src/gwin/sys_make.mk +++ /dev/null @@ -1,19 +0,0 @@ -GFXSRC += $(GFXLIB)/src/gwin/gwin_gwin.c \ - $(GFXLIB)/src/gwin/gwin_widget.c \ - $(GFXLIB)/src/gwin/gwin_wm.c \ - $(GFXLIB)/src/gwin/gwin_console.c \ - $(GFXLIB)/src/gwin/gwin_graph.c \ - $(GFXLIB)/src/gwin/gwin_button.c \ - $(GFXLIB)/src/gwin/gwin_slider.c \ - $(GFXLIB)/src/gwin/gwin_checkbox.c \ - $(GFXLIB)/src/gwin/gwin_image.c \ - $(GFXLIB)/src/gwin/gwin_label.c \ - $(GFXLIB)/src/gwin/gwin_radio.c \ - $(GFXLIB)/src/gwin/gwin_list.c \ - $(GFXLIB)/src/gwin/gwin_progressbar.c \ - $(GFXLIB)/src/gwin/gwin_container.c \ - $(GFXLIB)/src/gwin/gwin_frame.c \ - $(GFXLIB)/src/gwin/gwin_tabset.c \ - $(GFXLIB)/src/gwin/gwin_gl3d.c \ - -GFXINC += $(GFXLIB)/3rdparty/tinygl-0.4-ugfx/include diff --git a/src/gwin/sys_options.h b/src/gwin/sys_options.h deleted file mode 100644 index df8f497e..00000000 --- a/src/gwin/sys_options.h +++ /dev/null @@ -1,334 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gwin/sys_options.h - * @brief GWIN sub-system options header file. - * - * @addtogroup GWIN - * @brief The GWIN module uses all the other modules (GDISP, GINPUT, GTIMER...) to - * form a complete GUI toolkit. - * - * @{ - */ - -#ifndef _GWIN_OPTIONS_H -#define _GWIN_OPTIONS_H - -/** - * @name GWIN Functionality to be included - * @{ - */ - /** - * @brief Should window manager support be included - * @details Defaults to FALSE - */ - #ifndef GWIN_NEED_WINDOWMANAGER - #define GWIN_NEED_WIDGET FALSE - #endif - /** - * @brief Should the widget hierarchy be included. This provides parent-child features. - * @details Defaults to FALSE - */ - #ifndef GWIN_NEED_CONTAINERS - #define GWIN_NEED_CONTAINERS FALSE - #endif - /** - * @brief Should widget functions be included. Needed for any widget (eg Buttons, Sliders etc) - * @details Defaults to FALSE - */ - #ifndef GWIN_NEED_WIDGET - #define GWIN_NEED_WIDGET FALSE - #endif - /** - * @brief Should the simple container be included. - * @details Defaults to FALSE - */ - #ifndef GWIN_NEED_CONTAINER - #define GWIN_NEED_CONTAINER FALSE - #endif - /** - * @brief Should the frame widget be included. - * @details Defaults to FALSE - */ - #ifndef GWIN_NEED_FRAME - #define GWIN_NEED_FRAME FALSE - #endif - /** - * @brief Should console functions be included. - * @details Defaults to FALSE - */ - #ifndef GWIN_NEED_CONSOLE - #define GWIN_NEED_CONSOLE FALSE - #endif - /** - * @brief Should graph functions be included. - * @details Defaults to FALSE - */ - #ifndef GWIN_NEED_GRAPH - #define GWIN_NEED_GRAPH FALSE - #endif - /** - * @brief Should gl3d functions be included. - * @details Defaults to FALSE - */ - #ifndef GWIN_NEED_GL3D - #define GWIN_NEED_GL3D FALSE - #endif - /** - * @brief Should button functions be included. - * @details Defaults to FALSE - */ - #ifndef GWIN_NEED_BUTTON - #define GWIN_NEED_BUTTON FALSE - #endif - /** - * @brief Should progressbar functions be included. - * @details Defaults to FALSE - */ - #ifndef GWIN_NEED_PROGRESSBAR - #define GWIN_NEED_PROGRESSBAR FALSE - #endif - /** - * @brief Should slider functions be included. - * @details Defaults to FALSE - */ - #ifndef GWIN_NEED_SLIDER - #define GWIN_NEED_SLIDER FALSE - #endif - /** - * @brief Should checkbox functions be included. - * @details Defaults to FALSE - */ - #ifndef GWIN_NEED_CHECKBOX - #define GWIN_NEED_CHECKBOX FALSE - #endif - /** - * @brief Should image functions be included. - * @details Defaults to FALSE - */ - #ifndef GWIN_NEED_IMAGE - #define GWIN_NEED_IMAGE FALSE - #endif - /** - * @brief Should label functions be included. - * @details Defaults to FALSE - */ - #ifndef GWIN_NEED_LABEL - #define GWIN_NEED_LABEL FALSE - #endif - /** - * @brief Should radio button functions be included. - * @details Defaults to FALSE - */ - #ifndef GWIN_NEED_RADIO - #define GWIN_NEED_RADIO FALSE - #endif - /** - * @brief Should tabset functions be included. - * @details Defaults to FALSE - */ - #ifndef GWIN_NEED_TABSET - #define GWIN_NEED_TABSET FALSE - #endif -/** - * @} - * - * @name GWIN Optional Parameters - * @{ - */ - /** - * @brief Add a tag to each widget - * @details Defaults to FALSE - * @note Adds a tag member to each widget. Any events created include this tag. - * The enables switch based application logic to detect the event source. - */ - #ifndef GWIN_WIDGET_TAGS - #define GWIN_WIDGET_TAGS FALSE - #endif - /** - * @brief Use flat styling for controls rather than a 3D look - * @details Defaults to FALSE - * @note This may appear better on color-restricted displays - * @note Flat styling is less graphics and cpu intensive (marginally) than the default 3D look. - */ - #ifndef GWIN_FLAT_STYLING - #define GWIN_FLAT_STYLING FALSE - #endif - /** - * @brief Don't use a timer for redrawing windows - * @details Defaults to FALSE - * @note Normally windows and widgets are redrawn on a timer. Setting this - * option causes them to be redrawn immediately. Note that this can - * cause extended blocking times on events and saves little code. - * @note If GWIN_NEED_WINDOWMANAGER is FALSE then this setting is ignored - * as redrawing always occurs immediately. - */ - #ifndef GWIN_REDRAW_IMMEDIATE - #define GWIN_REDRAW_IMMEDIATE FALSE - #endif - /** - * @brief Redraw all windows in a single operation - * @details Defaults to FALSE - * @note Windows are normally redraw one per gtimer cycle. - * Setting this option causes all windows to be redrawn in - * a single gtimer cycle. Note that this can - * cause extended blocking times on the timer thread but may - * speed up redraw slightly. - * @note This is only relevant if GWIN_REDRAW_IMMEDIATE is FALSE. - * Everything always gets redrawn in a single operation if - * GWIN_REDRAW_IMMEDIATE is TRUE. - */ - #ifndef GWIN_REDRAW_SINGLEOP - #define GWIN_REDRAW_SINGLEOP FALSE - #endif - /** - * @brief Buttons should not insist the mouse is over the button on mouse release - * @details Defaults to FALSE - */ - #ifndef GWIN_BUTTON_LAZY_RELEASE - #define GWIN_BUTTON_LAZY_RELEASE FALSE - #endif - /** - * @brief Should the content of the console be saved for redrawing. - * @details Defaults to FALSE - * @details If this feature is enabled, the contents of the console will be saved - * as it is written. If a redraw is required it will be redrawn from the - * history. Scrolling will also use the history buffer if it is turned on. - * @note Using this option allocates the amount of memory to store the - * history based on the minimum character width in the current font - * at the time the history is turned on. Using a fixed width font is a good - * idea to minimize memory usage. - * @note If you change the size of the window or you change the font being displayed - * you should turn off the history and then turn it back on in order to get - * a new buffer of the correct size for the window/font combination. Strange - * redrawing and scrolling effects can occur if the buffer is too small to - * save a complete screen of data. Note the system tries to optimize storage - * so this may only be evident in very limited situations eg with a console - * with many characters in it. - * @note @p gwinConsoleSetBuffer() can be used to turn the history buffer off and on. - */ - #ifndef GWIN_CONSOLE_USE_HISTORY - #define GWIN_CONSOLE_USE_HISTORY FALSE - #endif - /** - * @brief Use font width averaging for the history buffer allocation. - * @details Defaults to FALSE - * @details If this feature is enabled, the width one third of the way between - * the font's character width minimum and maximum will be used instead - * of the font's minimum width. - * @note This option reduces the memory allocation for a variable width font's - * history buffer. Note that strange - * redrawing and scrolling effects can occur if the buffer is too small to - * save a complete screen of data. The system tries to optimize storage - * so this may only be evident in very limited situations eg with a console - * with many characters in it. - */ - #ifndef GWIN_CONSOLE_HISTORY_AVERAGING - #define GWIN_CONSOLE_HISTORY_AVERAGING FALSE - #endif - /** - * @brief Should the history be turned on for all console windows when they are first created. - * @details Defaults to FALSE - * @note @p gwinConsoleSetBuffer() can be used to turn the history buffer off and on at - * any time. - */ - #ifndef GWIN_CONSOLE_HISTORY_ATCREATE - #define GWIN_CONSOLE_HISTORY_ATCREATE FALSE - #endif - /** - * @brief Console Windows need floating point support in @p gwinPrintf - * @details Defaults to FALSE - */ - #ifndef GWIN_CONSOLE_USE_FLOAT - #define GWIN_CONSOLE_USE_FLOAT FALSE - #endif - /** - * @brief Console windows support escape sequences to control display - * @details Defaults to FALSE - * - * @note - * Currently supported: - * ESC color Change subsequent text color - * color: "0" = black, "1" = red, "2" = green, "3" = yellow, "4" = blue, - * "5" = magenta, "6" = cyan, "7" = white - * ESC C Revert subsequent text color to the window default - * ESC u Turn on underline - * ESC U Turn off underline - * ESC b Turn on bold - * ESC B Turn off bold - * ESC J Clear the window - */ - #ifndef GWIN_CONSOLE_ESCSEQ - #define GWIN_CONSOLE_ESCSEQ FALSE - #endif - /** - * @brief Console Windows need BaseStreamSequential support (ChibiOS only) - * @details Defaults to FALSE - * @note To use the ChibiOS basestream functions such as chprintf() - * for printing in a console window you need to set this option to - * TRUE in your gfxconf.h and include in your application source file... - * \#include "chprintf.h" - * In your makefile, as part of your list of C source files, include - * ${CHIBIOS}/os/various/chprintf.c - */ - #ifndef GWIN_CONSOLE_USE_BASESTREAM - #define GWIN_CONSOLE_USE_BASESTREAM FALSE - #endif - /** - * @brief Image windows can optionally support animated images - * @details Defaults to FALSE - */ - #ifndef GWIN_NEED_IMAGE_ANIMATION - #define GWIN_NEED_IMAGE_ANIMATION FALSE - #endif - /** - * @brief Enable the API to automatically increment the progressbar over time - * @details Defaults to FALSE - */ - #ifndef GWIN_PROGRESSBAR_AUTO - #define GWIN_PROGRESSBAR_AUTO FALSE - #endif - /** - * @brief Should the slider avoid snapping to a fixed position when the mouse is released - * @details Defaults to FALSE - * @note If FALSE the slider will snap to the closest set-able position when the - * mouse is released. If TRUE it will maintain the position the - * mouse was released at, except when at the minimum and maximum slider values. - */ - #ifndef GWIN_SLIDER_NOSNAP - #define GWIN_SLIDER_NOSNAP FALSE - #endif - /** - * @brief The number of pixels of dead-band at each end of the slider - * @details Defaults to 5 - * @note A dead-band is required because fingers can often cannot - * accurately control the slider peg at the edges of the slider - */ - #ifndef GWIN_SLIDER_DEAD_BAND - #define GWIN_SLIDER_DEAD_BAND 5 - #endif - /** - * @brief How many toggles it takes to go from minimum to maximum value on a slider - * @details Defaults to 20 - * @note When the slider is being operated by a toggle device this setting describes - * how many toggles are required to go from end to end. - */ - #ifndef GWIN_SLIDER_TOGGLE_INC - #define GWIN_SLIDER_TOGGLE_INC 20 - #endif - /** - * @brief The height in pixels of a row of tabs in a tabset - * @details Defaults to 18 - */ - #ifndef GWIN_TABSET_TABHEIGHT - #define GWIN_TABSET_TABHEIGHT 18 - #endif -/** @} */ - -#endif /* _GWIN_OPTIONS_H */ -/** @} */ diff --git a/src/gwin/sys_rules.h b/src/gwin/sys_rules.h deleted file mode 100644 index 39864901..00000000 --- a/src/gwin/sys_rules.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gwin/sys_rules.h - * @brief GWIN safety rules header file. - * - * @addtogroup GWIN - * @{ - */ - -#ifndef _GWIN_RULES_H -#define _GWIN_RULES_H - -#if GFX_USE_GWIN - // Sub-system rules - #if !GFX_USE_GDISP - #error "GWIN: GFX_USE_GDISP must be TRUE when using GWIN" - #endif - #if !GDISP_NEED_CLIP - #if GFX_DISPLAY_RULE_WARNINGS - #warning "GWIN: Drawing can occur outside the defined windows as GDISP_NEED_CLIP is FALSE" - #endif - #endif - - // Objects require their super-class - #if GWIN_NEED_TABSET || GWIN_NEED_FRAME || GWIN_NEED_CONTAINER - #if !GWIN_NEED_CONTAINERS - #if GFX_DISPLAY_RULE_WARNINGS - #warning "GWIN: GWIN_NEED_CONTAINERS is required when a container is enabled. It has been turned on for you." - #endif - #undef GWIN_NEED_CONTAINERS - #define GWIN_NEED_CONTAINERS TRUE - #endif - #endif - #if GWIN_NEED_BUTTON || GWIN_NEED_SLIDER || GWIN_NEED_CHECKBOX || GWIN_NEED_LABEL || GWIN_NEED_RADIO || GWIN_NEED_LIST || \ - GWIN_NEED_IMAGE || GWIN_NEED_CHECKBOX || GWIN_NEED_PROGRESSBAR - #if !GWIN_NEED_WIDGET - #if GFX_DISPLAY_RULE_WARNINGS - #warning "GWIN: GWIN_NEED_WIDGET is required when a widget is used. It has been turned on for you." - #endif - #undef GWIN_NEED_WIDGET - #define GWIN_NEED_WIDGET TRUE - #endif - #endif - - // Rules for the super-classes - #if GWIN_NEED_CONTAINERS - #if !GWIN_NEED_WIDGET - #if GFX_DISPLAY_RULE_WARNINGS - #warning "GWIN: GWIN_NEED_WIDGET is required when GWIN_NEED_CONTAINERS is enabled. It has been turned on for you." - #endif - #undef GWIN_NEED_WIDGET - #define GWIN_NEED_WIDGET TRUE - #endif - #endif - #if GWIN_NEED_WIDGET - #if !GDISP_NEED_TEXT - #error "GWIN: GDISP_NEED_TEXT is required if GWIN_NEED_WIDGET is TRUE." - #endif - #if !GFX_USE_GINPUT - // This test also ensures that GFX_USE_GEVENT is set - #error "GWIN: GFX_USE_GINPUT (and one or more input sources) is required if GWIN_NEED_WIDGET is TRUE" - #endif - #if !GWIN_NEED_WINDOWMANAGER - #if GFX_DISPLAY_RULE_WARNINGS - #warning "GWIN: GWIN_NEED_WINDOWMANAGER is required if GWIN_NEED_WIDGET is TRUE. It has been turned on for you." - #endif - #undef GWIN_NEED_WINDOWMANAGER - #define GWIN_NEED_WINDOWMANAGER TRUE - #endif - #if !GDISP_NEED_MULTITHREAD - #if GFX_DISPLAY_RULE_WARNINGS - #warning "GWIN: GDISP_NEED_MULTITHREAD is required if GWIN_NEED_WIDGET is TRUE. It has been turned on for you" - #endif - #undef GDISP_NEED_MULTITHREAD - #define GDISP_NEED_MULTITHREAD TRUE - #endif - #endif - #if GWIN_NEED_WINDOWMANAGER - #if !GFX_USE_GQUEUE || !GQUEUE_NEED_ASYNC - #if GFX_DISPLAY_RULE_WARNINGS - #warning "GWIN: GFX_USE_GQUEUE and GQUEUE_NEED_ASYNC is required if GWIN_NEED_WINDOWMANAGER is TRUE. It has been turned on for you." - #endif - #undef GFX_USE_GQUEUE - #undef GQUEUE_NEED_ASYNC - #define GFX_USE_GQUEUE TRUE - #define GQUEUE_NEED_ASYNC TRUE - #endif - #if !GFX_USE_GTIMER - #if GFX_DISPLAY_RULE_WARNINGS - #warning "GWIN: GFX_USE_GTIMER is required if GWIN_NEED_WINDOWMANAGER is TRUE. It has been turned on for you." - #endif - #undef GFX_USE_GTIMER - #define GFX_USE_GTIMER TRUE - #endif - #endif - - // Rules for individual objects - #if GWIN_NEED_LIST - #if !GDISP_NEED_TEXT - #error "GWIN: GDISP_NEED_TEXT is required when GWIN_NEED_LIST is TRUE." - #endif - #endif - #if GWIN_NEED_RADIO - #if !GDISP_NEED_CIRCLE - #if GFX_DISPLAY_RULE_WARNINGS - #warning "GWIN: GDISP_NEED_CIRCLE should be set to TRUE for much nicer radio button widgets." - #endif - #endif - #endif - #if GWIN_NEED_IMAGE - #if !GDISP_NEED_IMAGE - #error "GWIN: GDISP_NEED_IMAGE is required when GWIN_NEED_IMAGE is TRUE." - #endif - #endif - #if GWIN_NEED_CONSOLE - #if !GDISP_NEED_TEXT - #error "GWIN: GDISP_NEED_TEXT is required if GWIN_NEED_CONSOLE is TRUE." - #endif - #endif -#endif - -#endif /* _GWIN_RULES_H */ -/** @} */ -- cgit v1.2.3